Home | History | Annotate | Download | only in os
      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     /** {@hide} */
     52     public RevocableFileDescriptor() {
     53     }
     54 
     55     /**
     56      * Create an instance that references the given {@link File}.
     57      */
     58     public RevocableFileDescriptor(Context context, File file) throws IOException {
     59         try {
     60             init(context, Os.open(file.getAbsolutePath(),
     61                     OsConstants.O_CREAT | OsConstants.O_RDWR, 0700));
     62         } catch (ErrnoException e) {
     63             throw e.rethrowAsIOException();
     64         }
     65     }
     66 
     67     /**
     68      * Create an instance that references the given {@link FileDescriptor}.
     69      */
     70     public RevocableFileDescriptor(Context context, FileDescriptor fd) throws IOException {
     71         init(context, fd);
     72     }
     73 
     74     /** {@hide} */
     75     public void init(Context context, FileDescriptor fd) throws IOException {
     76         mInner = fd;
     77         mOuter = context.getSystemService(StorageManager.class)
     78                 .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback);
     79     }
     80 
     81     /**
     82      * Return a {@link ParcelFileDescriptor} which can safely be passed to an
     83      * untrusted process. After {@link #revoke()} is called, all operations will
     84      * fail with {@link OsConstants#EPERM}.
     85      */
     86     public ParcelFileDescriptor getRevocableFileDescriptor() {
     87         return mOuter;
     88     }
     89 
     90     /**
     91      * Revoke all future access to the {@link ParcelFileDescriptor} returned by
     92      * {@link #getRevocableFileDescriptor()}. From this point forward, all
     93      * operations will fail with {@link OsConstants#EPERM}.
     94      */
     95     public void revoke() {
     96         mRevoked = true;
     97         IoUtils.closeQuietly(mInner);
     98     }
     99 
    100     public boolean isRevoked() {
    101         return mRevoked;
    102     }
    103 
    104     private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
    105         private void checkRevoked() throws ErrnoException {
    106             if (mRevoked) {
    107                 throw new ErrnoException(TAG, OsConstants.EPERM);
    108             }
    109         }
    110 
    111         @Override
    112         public long onGetSize() throws ErrnoException {
    113             checkRevoked();
    114             return Os.fstat(mInner).st_size;
    115         }
    116 
    117         @Override
    118         public int onRead(long offset, int size, byte[] data) throws ErrnoException {
    119             checkRevoked();
    120             int n = 0;
    121             while (n < size) {
    122                 try {
    123                     n += Os.pread(mInner, data, n, size - n, offset + n);
    124                     break;
    125                 } catch (InterruptedIOException e) {
    126                     n += e.bytesTransferred;
    127                 }
    128             }
    129             return n;
    130         }
    131 
    132         @Override
    133         public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
    134             checkRevoked();
    135             int n = 0;
    136             while (n < size) {
    137                 try {
    138                     n += Os.pwrite(mInner, data, n, size - n, offset + n);
    139                     break;
    140                 } catch (InterruptedIOException e) {
    141                     n += e.bytesTransferred;
    142                 }
    143             }
    144             return n;
    145         }
    146 
    147         @Override
    148         public void onFsync() throws ErrnoException {
    149             if (DEBUG) Slog.v(TAG, "onFsync()");
    150             checkRevoked();
    151             Os.fsync(mInner);
    152         }
    153 
    154         @Override
    155         public void onRelease() {
    156             if (DEBUG) Slog.v(TAG, "onRelease()");
    157             mRevoked = true;
    158             IoUtils.closeQuietly(mInner);
    159         }
    160     };
    161 }
    162