Loading core/java/android/content/pm/PackageInstaller.java +31 −7 Original line number Diff line number Diff line Loading @@ -36,7 +36,9 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.RemoteException; import android.annotation.IntRange; import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; import android.util.ExceptionUtils; import com.android.internal.util.IndentingPrintWriter; Loading Loading @@ -79,6 +81,10 @@ import java.util.List; public class PackageInstaller { private static final String TAG = "PackageInstaller"; /** {@hide} */ public static final boolean ENABLE_REVOCABLE_FD = SystemProperties.getBoolean("fw.revocable_fd", false); /** * Activity Action: Show details about a particular install session. This * may surface actions such as pause, resume, or cancel. Loading Loading @@ -753,15 +759,21 @@ public class PackageInstaller { public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes, long lengthBytes) throws IOException { try { if (ENABLE_REVOCABLE_FD) { return new ParcelFileDescriptor.AutoCloseOutputStream( mSession.openWrite(name, offsetBytes, lengthBytes)); } else { final ParcelFileDescriptor clientSocket = mSession.openWrite(name, offsetBytes, lengthBytes); return new FileBridge.FileBridgeOutputStream(clientSocket); } } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Loading @@ -770,12 +782,24 @@ public class PackageInstaller { * {@link #openWrite(String, long, long)}. */ public void fsync(@NonNull OutputStream out) throws IOException { if (ENABLE_REVOCABLE_FD) { if (out instanceof ParcelFileDescriptor.AutoCloseOutputStream) { try { Os.fsync(((ParcelFileDescriptor.AutoCloseOutputStream) out).getFD()); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } } else { throw new IllegalArgumentException("Unrecognized stream"); } } else { if (out instanceof FileBridge.FileBridgeOutputStream) { ((FileBridge.FileBridgeOutputStream) out).fsync(); } else { throw new IllegalArgumentException("Unrecognized stream"); } } } /** * Return all APK names contained in this session. Loading core/java/android/os/FileBridge.java +2 −0 Original line number Diff line number Diff line Loading @@ -41,7 +41,9 @@ import java.util.Arrays; * hands-off. * * @hide * @deprecated replaced by {@link RevocableFileDescriptor} */ @Deprecated public class FileBridge extends Thread { private static final String TAG = "FileBridge"; Loading core/java/android/os/RevocableFileDescriptor.java 0 → 100644 +161 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; import android.content.Context; import android.os.storage.StorageManager; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.Slog; import libcore.io.IoUtils; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.InterruptedIOException; /** * Variant of {@link FileDescriptor} that allows its creator to revoke all * access to the underlying resource. * <p> * This is useful when the code that originally opened a file needs to strongly * assert that any clients are completely hands-off for security purposes. * * @hide */ public class RevocableFileDescriptor { private static final String TAG = "RevocableFileDescriptor"; private static final boolean DEBUG = true; private FileDescriptor mInner; private ParcelFileDescriptor mOuter; private volatile boolean mRevoked; /** {@hide} */ public RevocableFileDescriptor() { } /** * Create an instance that references the given {@link File}. */ public RevocableFileDescriptor(Context context, File file) throws IOException { try { init(context, Os.open(file.getAbsolutePath(), OsConstants.O_CREAT | OsConstants.O_RDWR, 0700)); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } } /** * Create an instance that references the given {@link FileDescriptor}. */ public RevocableFileDescriptor(Context context, FileDescriptor fd) throws IOException { init(context, fd); } /** {@hide} */ public void init(Context context, FileDescriptor fd) throws IOException { mInner = fd; mOuter = context.getSystemService(StorageManager.class) .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback); } /** * Return a {@link ParcelFileDescriptor} which can safely be passed to an * untrusted process. After {@link #revoke()} is called, all operations will * fail with {@link OsConstants#EPERM}. */ public ParcelFileDescriptor getRevocableFileDescriptor() { return mOuter; } /** * Revoke all future access to the {@link ParcelFileDescriptor} returned by * {@link #getRevocableFileDescriptor()}. From this point forward, all * operations will fail with {@link OsConstants#EPERM}. */ public void revoke() { mRevoked = true; IoUtils.closeQuietly(mInner); } public boolean isRevoked() { return mRevoked; } private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() { private void checkRevoked() throws ErrnoException { if (mRevoked) { throw new ErrnoException(TAG, OsConstants.EPERM); } } @Override public long onGetSize() throws ErrnoException { checkRevoked(); return Os.fstat(mInner).st_size; } @Override public int onRead(long offset, int size, byte[] data) throws ErrnoException { checkRevoked(); int n = 0; while (n < size) { try { n += Os.pread(mInner, data, n, size - n, offset + n); break; } catch (InterruptedIOException e) { n += e.bytesTransferred; } } return n; } @Override public int onWrite(long offset, int size, byte[] data) throws ErrnoException { checkRevoked(); int n = 0; while (n < size) { try { n += Os.pwrite(mInner, data, n, size - n, offset + n); break; } catch (InterruptedIOException e) { n += e.bytesTransferred; } } return n; } @Override public void onFsync() throws ErrnoException { if (DEBUG) Slog.v(TAG, "onFsync()"); checkRevoked(); Os.fsync(mInner); } @Override public void onRelease() { if (DEBUG) Slog.v(TAG, "onRelease()"); mRevoked = true; IoUtils.closeQuietly(mInner); } }; } services/core/java/com/android/server/pm/PackageInstallerSession.java +32 −6 Original line number Diff line number Diff line Loading @@ -56,7 +56,9 @@ import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.ProxyFileDescriptorCallback; import android.os.RemoteException; import android.os.RevocableFileDescriptor; import android.os.UserHandle; import android.os.storage.StorageManager; import android.system.ErrnoException; Loading Loading @@ -148,7 +150,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private String mFinalMessage; @GuardedBy("mLock") private ArrayList<FileBridge> mBridges = new ArrayList<>(); private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>(); @GuardedBy("mLock") private final ArrayList<FileBridge> mBridges = new ArrayList<>(); @GuardedBy("mLock") private IPackageInstallObserver2 mRemoteObserver; Loading Loading @@ -430,13 +434,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Quick sanity check of state, and allocate a pipe for ourselves. We // then do heavy disk allocation outside the lock, but this open pipe // will block any attempted install transitions. final RevocableFileDescriptor fd; final FileBridge bridge; synchronized (mLock) { assertPreparedAndNotSealed("openWrite"); if (PackageInstaller.ENABLE_REVOCABLE_FD) { fd = new RevocableFileDescriptor(); bridge = null; mFds.add(fd); } else { fd = null; bridge = new FileBridge(); mBridges.add(bridge); } } try { // Use installer provided name for now; we always rename later Loading Loading @@ -468,9 +480,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); } if (PackageInstaller.ENABLE_REVOCABLE_FD) { fd.init(mContext, targetFd); return fd.getRevocableFileDescriptor(); } else { bridge.setTargetFile(targetFd); bridge.start(); return new ParcelFileDescriptor(bridge.getClientSocket()); } } catch (ErrnoException e) { throw e.rethrowAsIOException(); Loading Loading @@ -512,6 +529,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { wasSealed = mSealed; if (!mSealed) { // Verify that all writers are hands-off for (RevocableFileDescriptor fd : mFds) { if (!fd.isRevoked()) { throw new SecurityException("Files still open"); } } for (FileBridge bridge : mBridges) { if (!bridge.isClosed()) { throw new SecurityException("Files still open"); Loading Loading @@ -1170,6 +1192,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mDestroyed = true; // Force shut down all bridges for (RevocableFileDescriptor fd : mFds) { fd.revoke(); } for (FileBridge bridge : mBridges) { bridge.forceClose(); } Loading Loading @@ -1211,6 +1236,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { pw.printPair("mPermissionsAccepted", mPermissionsAccepted); pw.printPair("mRelinquished", mRelinquished); pw.printPair("mDestroyed", mDestroyed); pw.printPair("mFds", mFds.size()); pw.printPair("mBridges", mBridges.size()); pw.printPair("mFinalStatus", mFinalStatus); pw.printPair("mFinalMessage", mFinalMessage); Loading Loading
core/java/android/content/pm/PackageInstaller.java +31 −7 Original line number Diff line number Diff line Loading @@ -36,7 +36,9 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.RemoteException; import android.annotation.IntRange; import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; import android.util.ExceptionUtils; import com.android.internal.util.IndentingPrintWriter; Loading Loading @@ -79,6 +81,10 @@ import java.util.List; public class PackageInstaller { private static final String TAG = "PackageInstaller"; /** {@hide} */ public static final boolean ENABLE_REVOCABLE_FD = SystemProperties.getBoolean("fw.revocable_fd", false); /** * Activity Action: Show details about a particular install session. This * may surface actions such as pause, resume, or cancel. Loading Loading @@ -753,15 +759,21 @@ public class PackageInstaller { public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes, long lengthBytes) throws IOException { try { if (ENABLE_REVOCABLE_FD) { return new ParcelFileDescriptor.AutoCloseOutputStream( mSession.openWrite(name, offsetBytes, lengthBytes)); } else { final ParcelFileDescriptor clientSocket = mSession.openWrite(name, offsetBytes, lengthBytes); return new FileBridge.FileBridgeOutputStream(clientSocket); } } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Loading @@ -770,12 +782,24 @@ public class PackageInstaller { * {@link #openWrite(String, long, long)}. */ public void fsync(@NonNull OutputStream out) throws IOException { if (ENABLE_REVOCABLE_FD) { if (out instanceof ParcelFileDescriptor.AutoCloseOutputStream) { try { Os.fsync(((ParcelFileDescriptor.AutoCloseOutputStream) out).getFD()); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } } else { throw new IllegalArgumentException("Unrecognized stream"); } } else { if (out instanceof FileBridge.FileBridgeOutputStream) { ((FileBridge.FileBridgeOutputStream) out).fsync(); } else { throw new IllegalArgumentException("Unrecognized stream"); } } } /** * Return all APK names contained in this session. Loading
core/java/android/os/FileBridge.java +2 −0 Original line number Diff line number Diff line Loading @@ -41,7 +41,9 @@ import java.util.Arrays; * hands-off. * * @hide * @deprecated replaced by {@link RevocableFileDescriptor} */ @Deprecated public class FileBridge extends Thread { private static final String TAG = "FileBridge"; Loading
core/java/android/os/RevocableFileDescriptor.java 0 → 100644 +161 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; import android.content.Context; import android.os.storage.StorageManager; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.Slog; import libcore.io.IoUtils; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.InterruptedIOException; /** * Variant of {@link FileDescriptor} that allows its creator to revoke all * access to the underlying resource. * <p> * This is useful when the code that originally opened a file needs to strongly * assert that any clients are completely hands-off for security purposes. * * @hide */ public class RevocableFileDescriptor { private static final String TAG = "RevocableFileDescriptor"; private static final boolean DEBUG = true; private FileDescriptor mInner; private ParcelFileDescriptor mOuter; private volatile boolean mRevoked; /** {@hide} */ public RevocableFileDescriptor() { } /** * Create an instance that references the given {@link File}. */ public RevocableFileDescriptor(Context context, File file) throws IOException { try { init(context, Os.open(file.getAbsolutePath(), OsConstants.O_CREAT | OsConstants.O_RDWR, 0700)); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } } /** * Create an instance that references the given {@link FileDescriptor}. */ public RevocableFileDescriptor(Context context, FileDescriptor fd) throws IOException { init(context, fd); } /** {@hide} */ public void init(Context context, FileDescriptor fd) throws IOException { mInner = fd; mOuter = context.getSystemService(StorageManager.class) .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback); } /** * Return a {@link ParcelFileDescriptor} which can safely be passed to an * untrusted process. After {@link #revoke()} is called, all operations will * fail with {@link OsConstants#EPERM}. */ public ParcelFileDescriptor getRevocableFileDescriptor() { return mOuter; } /** * Revoke all future access to the {@link ParcelFileDescriptor} returned by * {@link #getRevocableFileDescriptor()}. From this point forward, all * operations will fail with {@link OsConstants#EPERM}. */ public void revoke() { mRevoked = true; IoUtils.closeQuietly(mInner); } public boolean isRevoked() { return mRevoked; } private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() { private void checkRevoked() throws ErrnoException { if (mRevoked) { throw new ErrnoException(TAG, OsConstants.EPERM); } } @Override public long onGetSize() throws ErrnoException { checkRevoked(); return Os.fstat(mInner).st_size; } @Override public int onRead(long offset, int size, byte[] data) throws ErrnoException { checkRevoked(); int n = 0; while (n < size) { try { n += Os.pread(mInner, data, n, size - n, offset + n); break; } catch (InterruptedIOException e) { n += e.bytesTransferred; } } return n; } @Override public int onWrite(long offset, int size, byte[] data) throws ErrnoException { checkRevoked(); int n = 0; while (n < size) { try { n += Os.pwrite(mInner, data, n, size - n, offset + n); break; } catch (InterruptedIOException e) { n += e.bytesTransferred; } } return n; } @Override public void onFsync() throws ErrnoException { if (DEBUG) Slog.v(TAG, "onFsync()"); checkRevoked(); Os.fsync(mInner); } @Override public void onRelease() { if (DEBUG) Slog.v(TAG, "onRelease()"); mRevoked = true; IoUtils.closeQuietly(mInner); } }; }
services/core/java/com/android/server/pm/PackageInstallerSession.java +32 −6 Original line number Diff line number Diff line Loading @@ -56,7 +56,9 @@ import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.ProxyFileDescriptorCallback; import android.os.RemoteException; import android.os.RevocableFileDescriptor; import android.os.UserHandle; import android.os.storage.StorageManager; import android.system.ErrnoException; Loading Loading @@ -148,7 +150,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private String mFinalMessage; @GuardedBy("mLock") private ArrayList<FileBridge> mBridges = new ArrayList<>(); private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>(); @GuardedBy("mLock") private final ArrayList<FileBridge> mBridges = new ArrayList<>(); @GuardedBy("mLock") private IPackageInstallObserver2 mRemoteObserver; Loading Loading @@ -430,13 +434,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Quick sanity check of state, and allocate a pipe for ourselves. We // then do heavy disk allocation outside the lock, but this open pipe // will block any attempted install transitions. final RevocableFileDescriptor fd; final FileBridge bridge; synchronized (mLock) { assertPreparedAndNotSealed("openWrite"); if (PackageInstaller.ENABLE_REVOCABLE_FD) { fd = new RevocableFileDescriptor(); bridge = null; mFds.add(fd); } else { fd = null; bridge = new FileBridge(); mBridges.add(bridge); } } try { // Use installer provided name for now; we always rename later Loading Loading @@ -468,9 +480,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); } if (PackageInstaller.ENABLE_REVOCABLE_FD) { fd.init(mContext, targetFd); return fd.getRevocableFileDescriptor(); } else { bridge.setTargetFile(targetFd); bridge.start(); return new ParcelFileDescriptor(bridge.getClientSocket()); } } catch (ErrnoException e) { throw e.rethrowAsIOException(); Loading Loading @@ -512,6 +529,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { wasSealed = mSealed; if (!mSealed) { // Verify that all writers are hands-off for (RevocableFileDescriptor fd : mFds) { if (!fd.isRevoked()) { throw new SecurityException("Files still open"); } } for (FileBridge bridge : mBridges) { if (!bridge.isClosed()) { throw new SecurityException("Files still open"); Loading Loading @@ -1170,6 +1192,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mDestroyed = true; // Force shut down all bridges for (RevocableFileDescriptor fd : mFds) { fd.revoke(); } for (FileBridge bridge : mBridges) { bridge.forceClose(); } Loading Loading @@ -1211,6 +1236,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { pw.printPair("mPermissionsAccepted", mPermissionsAccepted); pw.printPair("mRelinquished", mRelinquished); pw.printPair("mDestroyed", mDestroyed); pw.printPair("mFds", mFds.size()); pw.printPair("mBridges", mBridges.size()); pw.printPair("mFinalStatus", mFinalStatus); pw.printPair("mFinalMessage", mFinalMessage); Loading