• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.os;
18 
19 import android.content.Context;
20 import android.os.storage.StorageManager;
21 import android.system.ErrnoException;
22 import android.system.Os;
23 import android.system.OsConstants;
24 import android.util.Slog;
25 
26 import libcore.io.IoUtils;
27 
28 import java.io.File;
29 import java.io.FileDescriptor;
30 import java.io.IOException;
31 import java.io.InterruptedIOException;
32 
33 /**
34  * Variant of {@link FileDescriptor} that allows its creator to revoke all
35  * access to the underlying resource.
36  * <p>
37  * This is useful when the code that originally opened a file needs to strongly
38  * assert that any clients are completely hands-off for security purposes.
39  *
40  * @hide
41  */
42 public class RevocableFileDescriptor {
43     private static final String TAG = "RevocableFileDescriptor";
44     private static final boolean DEBUG = true;
45 
46     private FileDescriptor mInner;
47     private ParcelFileDescriptor mOuter;
48 
49     private volatile boolean mRevoked;
50 
51     private ParcelFileDescriptor.OnCloseListener mOnCloseListener;
52 
53     /** {@hide} */
RevocableFileDescriptor()54     public RevocableFileDescriptor() {
55     }
56 
57     /**
58      * Create an instance that references the given {@link File}.
59      */
RevocableFileDescriptor(Context context, File file)60     public RevocableFileDescriptor(Context context, File file) throws IOException {
61         try {
62             init(context, Os.open(file.getAbsolutePath(),
63                     OsConstants.O_CREAT | OsConstants.O_RDWR, 0700));
64         } catch (ErrnoException e) {
65             throw e.rethrowAsIOException();
66         }
67     }
68 
69     /**
70      * Create an instance that references the given {@link FileDescriptor}.
71      */
RevocableFileDescriptor(Context context, FileDescriptor fd)72     public RevocableFileDescriptor(Context context, FileDescriptor fd) throws IOException {
73         init(context, fd);
74     }
75 
76     /** {@hide} */
init(Context context, FileDescriptor fd)77     public void init(Context context, FileDescriptor fd) throws IOException {
78         mInner = fd;
79         mOuter = context.getSystemService(StorageManager.class)
80                 .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback);
81     }
82 
83     /**
84      * Return a {@link ParcelFileDescriptor} which can safely be passed to an
85      * untrusted process. After {@link #revoke()} is called, all operations will
86      * fail with {@link OsConstants#EPERM}.
87      */
getRevocableFileDescriptor()88     public ParcelFileDescriptor getRevocableFileDescriptor() {
89         return mOuter;
90     }
91 
92     /**
93      * Revoke all future access to the {@link ParcelFileDescriptor} returned by
94      * {@link #getRevocableFileDescriptor()}. From this point forward, all
95      * operations will fail with {@link OsConstants#EPERM}.
96      */
revoke()97     public void revoke() {
98         mRevoked = true;
99         IoUtils.closeQuietly(mInner);
100     }
101 
102     /**
103      * Callback for indicating that {@link ParcelFileDescriptor} passed to the client
104      * process ({@link #getRevocableFileDescriptor()}) has been closed.
105      */
addOnCloseListener(ParcelFileDescriptor.OnCloseListener onCloseListener)106     public void addOnCloseListener(ParcelFileDescriptor.OnCloseListener onCloseListener) {
107         mOnCloseListener = onCloseListener;
108     }
109 
isRevoked()110     public boolean isRevoked() {
111         return mRevoked;
112     }
113 
114     private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
115         private void checkRevoked() throws ErrnoException {
116             if (mRevoked) {
117                 throw new ErrnoException(TAG, OsConstants.EPERM);
118             }
119         }
120 
121         @Override
122         public long onGetSize() throws ErrnoException {
123             checkRevoked();
124             return Os.fstat(mInner).st_size;
125         }
126 
127         @Override
128         public int onRead(long offset, int size, byte[] data) throws ErrnoException {
129             checkRevoked();
130             int n = 0;
131             while (n < size) {
132                 try {
133                     n += Os.pread(mInner, data, n, size - n, offset + n);
134                     break;
135                 } catch (InterruptedIOException e) {
136                     n += e.bytesTransferred;
137                 }
138             }
139             return n;
140         }
141 
142         @Override
143         public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
144             checkRevoked();
145             int n = 0;
146             while (n < size) {
147                 try {
148                     n += Os.pwrite(mInner, data, n, size - n, offset + n);
149                     break;
150                 } catch (InterruptedIOException e) {
151                     n += e.bytesTransferred;
152                 }
153             }
154             return n;
155         }
156 
157         @Override
158         public void onFsync() throws ErrnoException {
159             if (DEBUG) Slog.v(TAG, "onFsync()");
160             checkRevoked();
161             Os.fsync(mInner);
162         }
163 
164         @Override
165         public void onRelease() {
166             if (DEBUG) Slog.v(TAG, "onRelease()");
167             mRevoked = true;
168             IoUtils.closeQuietly(mInner);
169             if (mOnCloseListener != null) {
170                 mOnCloseListener.onClose(null);
171             }
172         }
173     };
174 }
175