Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +23 −15 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import android.content.pm.IPackageInstallerSession; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode; import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser; Loading Loading @@ -982,9 +983,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSealed = true; // Read transfers from the original owner stay open, but as the session's data // cannot be modified anymore, there is no leak of information. // For staged sessions, the validation is performed by StagingManager. if (!params.isMultiPackage && !params.isStaged) { // cannot be modified anymore, there is no leak of information. For staged sessions, // further validation may be performed by the staging manager. if (!params.isMultiPackage) { final PackageInfo pkgInfo = mPm.getPackageInfo( params.appPackageName, PackageManager.GET_SIGNATURES | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); Loading Loading @@ -1328,18 +1329,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSigningDetails = apk.signingDetails; mResolvedBaseFile = addedFile; assertApkConsistentLocked(String.valueOf(addedFile), apk); if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { try { // STOPSHIP: For APEX we should also implement proper APK Signature verification. mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts( pkgInfo.applicationInfo.sourceDir, PackageParser.SigningDetails.SignatureSchemeVersion.JAR); } catch (PackageParserException e) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Couldn't obtain signatures from base APK"); } // STOPSHIP: Ensure that we remove the non-staged version of APEX installs in production // because we currently do not verify that signatures are consistent with the previously // installed version in that case. // // When that happens, this hack can be reverted and we can rely on APEXd to map between // APEX files and their package names instead of parsing it out of the AndroidManifest // such as here. if (params.appPackageName == null) { params.appPackageName = mPackageName; } } Loading Loading @@ -2012,6 +2010,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } /** {@hide} */ void setStagedSessionFailed(@StagedSessionErrorCode int errorCode) { synchronized (mLock) { mStagedSessionReady = false; mStagedSessionApplied = false; mStagedSessionFailed = true; mStagedSessionErrorCode = errorCode; } } private void destroyInternal() { synchronized (mLock) { mSealed = true; Loading services/core/java/com/android/server/pm/StagingManager.java +72 −3 Original line number Diff line number Diff line Loading @@ -17,10 +17,23 @@ package com.android.server.pm; import android.annotation.NonNull; import android.apex.ApexInfo; import android.apex.IApexService; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.PackageParser.SigningDetails; import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion; import android.content.pm.ParceledListSlice; import android.content.pm.Signature; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.util.apk.ApkSignatureVerifier; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; Loading Loading @@ -71,14 +84,70 @@ public class StagingManager { return new ParceledListSlice<>(result); } private static boolean validateApexSignatureLocked(String apexPath, String packageName) { final SigningDetails signingDetails; try { signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR); } catch (PackageParserException e) { Slog.e(TAG, "Unable to parse APEX package: " + apexPath, e); return false; } final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); final ApexInfo apexInfo; try { apexInfo = apex.getActivePackage(packageName); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact APEXD", re); return false; } if (apexInfo == null || TextUtils.isEmpty(apexInfo.packageName)) { // TODO: What is the right thing to do here ? This implies there's no active package // with the given name. This should never be the case in production (where we only // accept updates to existing APEXes) but may be required for testing. return true; } final SigningDetails existingSigningDetails; try { existingSigningDetails = ApkSignatureVerifier.verify( apexInfo.packagePath, SignatureSchemeVersion.JAR); } catch (PackageParserException e) { Slog.e(TAG, "Unable to parse APEX package: " + apexInfo.packagePath, e); return false; } // Now that we have both sets of signatures, demand that they're an exact match. if (Signature.areExactMatch(existingSigningDetails.signatures, signingDetails.signatures)) { return true; } return false; } void commitSession(@NonNull PackageInstallerSession sessionInfo) { updateStoredSession(sessionInfo); mBgHandler.post(() -> { // TODO(b/118865310): Dispatch the session to apexd/PackageManager for verification. For // now we directly mark it as ready. sessionInfo.setStagedSessionReady(); mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(), sessionInfo.userId); SessionInfo session = sessionInfo.generateInfo(false); // For APEXes, we validate the signature here before we write the package to the // staging directory. For APKs, the signature verification will be done by the package // manager at the point at which it applies the staged install. // // TODO: Decide whether we want to fail fast by detecting signature mismatches right // away. if ((sessionInfo.params.installFlags & PackageManager.INSTALL_APEX) != 0) { if (!validateApexSignatureLocked(session.resolvedBaseCodePath, session.appPackageName)) { sessionInfo.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED); } } mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(false), sessionInfo.userId); }); } Loading Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +23 −15 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import android.content.pm.IPackageInstallerSession; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode; import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser; Loading Loading @@ -982,9 +983,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSealed = true; // Read transfers from the original owner stay open, but as the session's data // cannot be modified anymore, there is no leak of information. // For staged sessions, the validation is performed by StagingManager. if (!params.isMultiPackage && !params.isStaged) { // cannot be modified anymore, there is no leak of information. For staged sessions, // further validation may be performed by the staging manager. if (!params.isMultiPackage) { final PackageInfo pkgInfo = mPm.getPackageInfo( params.appPackageName, PackageManager.GET_SIGNATURES | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); Loading Loading @@ -1328,18 +1329,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSigningDetails = apk.signingDetails; mResolvedBaseFile = addedFile; assertApkConsistentLocked(String.valueOf(addedFile), apk); if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { try { // STOPSHIP: For APEX we should also implement proper APK Signature verification. mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts( pkgInfo.applicationInfo.sourceDir, PackageParser.SigningDetails.SignatureSchemeVersion.JAR); } catch (PackageParserException e) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Couldn't obtain signatures from base APK"); } // STOPSHIP: Ensure that we remove the non-staged version of APEX installs in production // because we currently do not verify that signatures are consistent with the previously // installed version in that case. // // When that happens, this hack can be reverted and we can rely on APEXd to map between // APEX files and their package names instead of parsing it out of the AndroidManifest // such as here. if (params.appPackageName == null) { params.appPackageName = mPackageName; } } Loading Loading @@ -2012,6 +2010,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } /** {@hide} */ void setStagedSessionFailed(@StagedSessionErrorCode int errorCode) { synchronized (mLock) { mStagedSessionReady = false; mStagedSessionApplied = false; mStagedSessionFailed = true; mStagedSessionErrorCode = errorCode; } } private void destroyInternal() { synchronized (mLock) { mSealed = true; Loading
services/core/java/com/android/server/pm/StagingManager.java +72 −3 Original line number Diff line number Diff line Loading @@ -17,10 +17,23 @@ package com.android.server.pm; import android.annotation.NonNull; import android.apex.ApexInfo; import android.apex.IApexService; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.PackageParser.SigningDetails; import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion; import android.content.pm.ParceledListSlice; import android.content.pm.Signature; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.util.apk.ApkSignatureVerifier; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; Loading Loading @@ -71,14 +84,70 @@ public class StagingManager { return new ParceledListSlice<>(result); } private static boolean validateApexSignatureLocked(String apexPath, String packageName) { final SigningDetails signingDetails; try { signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR); } catch (PackageParserException e) { Slog.e(TAG, "Unable to parse APEX package: " + apexPath, e); return false; } final IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); final ApexInfo apexInfo; try { apexInfo = apex.getActivePackage(packageName); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact APEXD", re); return false; } if (apexInfo == null || TextUtils.isEmpty(apexInfo.packageName)) { // TODO: What is the right thing to do here ? This implies there's no active package // with the given name. This should never be the case in production (where we only // accept updates to existing APEXes) but may be required for testing. return true; } final SigningDetails existingSigningDetails; try { existingSigningDetails = ApkSignatureVerifier.verify( apexInfo.packagePath, SignatureSchemeVersion.JAR); } catch (PackageParserException e) { Slog.e(TAG, "Unable to parse APEX package: " + apexInfo.packagePath, e); return false; } // Now that we have both sets of signatures, demand that they're an exact match. if (Signature.areExactMatch(existingSigningDetails.signatures, signingDetails.signatures)) { return true; } return false; } void commitSession(@NonNull PackageInstallerSession sessionInfo) { updateStoredSession(sessionInfo); mBgHandler.post(() -> { // TODO(b/118865310): Dispatch the session to apexd/PackageManager for verification. For // now we directly mark it as ready. sessionInfo.setStagedSessionReady(); mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(), sessionInfo.userId); SessionInfo session = sessionInfo.generateInfo(false); // For APEXes, we validate the signature here before we write the package to the // staging directory. For APKs, the signature verification will be done by the package // manager at the point at which it applies the staged install. // // TODO: Decide whether we want to fail fast by detecting signature mismatches right // away. if ((sessionInfo.params.installFlags & PackageManager.INSTALL_APEX) != 0) { if (!validateApexSignatureLocked(session.resolvedBaseCodePath, session.appPackageName)) { sessionInfo.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED); } } mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(false), sessionInfo.userId); }); } Loading