diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index 1924a861e002a..a9f190c84cca2 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -16,6 +16,8 @@ package com.android.server; +import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; + import android.content.ContentResolver; import android.content.Context; import android.os.Build; @@ -146,7 +148,7 @@ public class RescueParty { SystemProperties.set(PROP_RESCUE_LEVEL, Integer.toString(level)); EventLogTags.writeRescueLevel(level, triggerUid); - PackageManagerService.logCriticalInfo(Log.WARN, "Incremented rescue level to " + logCriticalInfo(Log.WARN, "Incremented rescue level to " + levelToString(level) + " triggered by UID " + triggerUid); } @@ -166,12 +168,12 @@ public class RescueParty { try { executeRescueLevelInternal(context, level); EventLogTags.writeRescueSuccess(level); - PackageManagerService.logCriticalInfo(Log.DEBUG, + logCriticalInfo(Log.DEBUG, "Finished rescue level " + levelToString(level)); } catch (Throwable t) { final String msg = ExceptionUtils.getCompleteMessage(t); EventLogTags.writeRescueFailure(level, msg); - PackageManagerService.logCriticalInfo(Log.ERROR, + logCriticalInfo(Log.ERROR, "Failed rescue level " + levelToString(level) + ": " + msg); } } diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java index 357446623a08e..fca95857c7e00 100644 --- a/services/core/java/com/android/server/pm/KeySetManagerService.java +++ b/services/core/java/com/android/server/pm/KeySetManagerService.java @@ -18,6 +18,8 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; +import static com.android.server.pm.PackageManagerService.SCAN_INITIAL; + import com.android.internal.util.Preconditions; import android.content.pm.PackageParser; import android.util.ArrayMap; @@ -341,6 +343,41 @@ public class KeySetManagerService { return mKeySets.get(id) != null; } + public boolean shouldCheckUpgradeKeySetLocked(PackageSettingBase oldPs, int scanFlags) { + // Can't rotate keys during boot or if sharedUser. + if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser() + || !oldPs.keySetData.isUsingUpgradeKeySets()) { + return false; + } + // app is using upgradeKeySets; make sure all are valid + long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets(); + for (int i = 0; i < upgradeKeySets.length; i++) { + if (!isIdValidKeySetId(upgradeKeySets[i])) { + Slog.wtf(TAG, "Package " + + (oldPs.name != null ? oldPs.name : "") + + " contains upgrade-key-set reference to unknown key-set: " + + upgradeKeySets[i] + + " reverting to signatures check."); + return false; + } + } + return true; + } + + public boolean checkUpgradeKeySetLocked(PackageSettingBase oldPS, + PackageParser.Package newPkg) { + // Upgrade keysets are being used. Determine if new package has a superset of the + // required keys. + long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets(); + for (int i = 0; i < upgradeKeySets.length; i++) { + Set upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]); + if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) { + return true; + } + } + return false; + } + /** * Fetches the {@link PublicKey public keys} which belong to the specified * KeySet id. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6c42f4fc8a359..6a0ea4ee82b69 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -102,6 +102,15 @@ import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter; +import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures; +import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists; +import static com.android.server.pm.PackageManagerServiceUtils.decompressFile; +import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride; +import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo; +import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles; +import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime; +import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; +import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures; import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE; import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS; import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; @@ -408,7 +417,7 @@ public class PackageManagerService extends IPackageManager.Stub private static final boolean DEBUG_FILTERS = false; public static final boolean DEBUG_PERMISSIONS = false; private static final boolean DEBUG_SHARED_LIBRARIES = false; - private static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE; + public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE; // Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService // and PackageDexOptimizer. All these classes have their own flag to allow switching a single @@ -462,9 +471,9 @@ public class PackageManagerService extends IPackageManager.Stub private static final String STATIC_SHARED_LIB_DELIMITER = "_"; /** Extension of the compressed packages */ - private final static String COMPRESSED_EXTENSION = ".gz"; + public final static String COMPRESSED_EXTENSION = ".gz"; /** Suffix of stub packages on the system partition */ - private final static String STUB_SUFFIX = "-Stub"; + public final static String STUB_SUFFIX = "-Stub"; private static final int[] EMPTY_INT_ARRAY = new int[0]; @@ -3076,75 +3085,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private int decompressFile(File srcFile, File dstFile) throws ErrnoException { - if (DEBUG_COMPRESSION) { - Slog.i(TAG, "Decompress file" - + "; src: " + srcFile.getAbsolutePath() - + ", dst: " + dstFile.getAbsolutePath()); - } - try ( - InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile)); - OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/); - ) { - Streams.copy(fileIn, fileOut); - Os.chmod(dstFile.getAbsolutePath(), 0644); - return PackageManager.INSTALL_SUCCEEDED; - } catch (IOException e) { - logCriticalInfo(Log.ERROR, "Failed to decompress file" - + "; src: " + srcFile.getAbsolutePath() - + ", dst: " + dstFile.getAbsolutePath()); - } - return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - } - - private File[] getCompressedFiles(String codePath) { - final File stubCodePath = new File(codePath); - final String stubName = stubCodePath.getName(); - - // The layout of a compressed package on a given partition is as follows : - // - // Compressed artifacts: - // - // /partition/ModuleName/foo.gz - // /partation/ModuleName/bar.gz - // - // Stub artifact: - // - // /partition/ModuleName-Stub/ModuleName-Stub.apk - // - // In other words, stub is on the same partition as the compressed artifacts - // and in a directory that's suffixed with "-Stub". - int idx = stubName.lastIndexOf(STUB_SUFFIX); - if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) { - return null; - } - - final File stubParentDir = stubCodePath.getParentFile(); - if (stubParentDir == null) { - Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath); - return null; - } - - final File compressedPath = new File(stubParentDir, stubName.substring(0, idx)); - final File[] files = compressedPath.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.toLowerCase().endsWith(COMPRESSED_EXTENSION); - } - }); - - if (DEBUG_COMPRESSION && files != null && files.length > 0) { - Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files)); - } - - return files; - } - - private boolean compressedFileExists(String codePath) { - final File[] compressedFiles = getCompressedFiles(codePath); - return compressedFiles != null && compressedFiles.length > 0; - } - /** * Decompresses the given package on the system image onto * the /data partition. @@ -5384,56 +5324,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - /** - * Compares two sets of signatures. Returns: - *
- * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null, - *
- * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null, - *
- * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null, - *
- * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical, - *
- * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ. - */ - public static int compareSignatures(Signature[] s1, Signature[] s2) { - if (s1 == null) { - return s2 == null - ? PackageManager.SIGNATURE_NEITHER_SIGNED - : PackageManager.SIGNATURE_FIRST_NOT_SIGNED; - } - - if (s2 == null) { - return PackageManager.SIGNATURE_SECOND_NOT_SIGNED; - } - - if (s1.length != s2.length) { - return PackageManager.SIGNATURE_NO_MATCH; - } - - // Since both signature sets are of size 1, we can compare without HashSets. - if (s1.length == 1) { - return s1[0].equals(s2[0]) ? - PackageManager.SIGNATURE_MATCH : - PackageManager.SIGNATURE_NO_MATCH; - } - - ArraySet set1 = new ArraySet(); - for (Signature sig : s1) { - set1.add(sig); - } - ArraySet set2 = new ArraySet(); - for (Signature sig : s2) { - set2.add(sig); - } - // Make sure s2 contains all signatures in s1. - if (set1.equals(set2)) { - return PackageManager.SIGNATURE_MATCH; - } - return PackageManager.SIGNATURE_NO_MATCH; - } - /** * If the database version for this type of package (internal storage or * external storage) is less than the version where package signatures @@ -5444,76 +5334,11 @@ public class PackageManagerService extends IPackageManager.Stub return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY; } - /** - * Used for backward compatibility to make sure any packages with - * certificate chains get upgraded to the new style. {@code existingSigs} - * will be in the old format (since they were stored on disk from before the - * system upgrade) and {@code scannedSigs} will be in the newer format. - */ - private int compareSignaturesCompat(PackageSignatures existingSigs, - PackageParser.Package scannedPkg) { - if (!isCompatSignatureUpdateNeeded(scannedPkg)) { - return PackageManager.SIGNATURE_NO_MATCH; - } - - ArraySet existingSet = new ArraySet(); - for (Signature sig : existingSigs.mSignatures) { - existingSet.add(sig); - } - ArraySet scannedCompatSet = new ArraySet(); - for (Signature sig : scannedPkg.mSignatures) { - try { - Signature[] chainSignatures = sig.getChainSignatures(); - for (Signature chainSig : chainSignatures) { - scannedCompatSet.add(chainSig); - } - } catch (CertificateEncodingException e) { - scannedCompatSet.add(sig); - } - } - /* - * Make sure the expanded scanned set contains all signatures in the - * existing one. - */ - if (scannedCompatSet.equals(existingSet)) { - // Migrate the old signatures to the new scheme. - existingSigs.assignSignatures(scannedPkg.mSignatures); - // The new KeySets will be re-added later in the scanning process. - synchronized (mPackages) { - mSettings.mKeySetManagerService.removeAppKeySetDataLPw(scannedPkg.packageName); - } - return PackageManager.SIGNATURE_MATCH; - } - return PackageManager.SIGNATURE_NO_MATCH; - } - private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) { final VersionInfo ver = getSettingsVersionForPackage(scannedPkg); return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER; } - private int compareSignaturesRecover(PackageSignatures existingSigs, - PackageParser.Package scannedPkg) { - if (!isRecoverSignatureUpdateNeeded(scannedPkg)) { - return PackageManager.SIGNATURE_NO_MATCH; - } - - String msg = null; - try { - if (Signature.areEffectiveMatch(existingSigs.mSignatures, scannedPkg.mSignatures)) { - logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for " - + scannedPkg.packageName); - return PackageManager.SIGNATURE_MATCH; - } - } catch (CertificateException e) { - msg = e.getMessage(); - } - - logCriticalInfo(Log.INFO, - "Failed to recover certificates for " + scannedPkg.packageName + ": " + msg); - return PackageManager.SIGNATURE_NO_MATCH; - } - @Override public List getAllPackages() { final int callingUid = Binder.getCallingUid(); @@ -8237,51 +8062,10 @@ public class PackageManagerService extends IPackageManager.Stub parallelPackageParser.close(); } - private static File getSettingsProblemFile() { - File dataDir = Environment.getDataDirectory(); - File systemDir = new File(dataDir, "system"); - File fname = new File(systemDir, "uiderrors.txt"); - return fname; - } - public static void reportSettingsProblem(int priority, String msg) { logCriticalInfo(priority, msg); } - public static void logCriticalInfo(int priority, String msg) { - Slog.println(priority, TAG, msg); - EventLogTags.writePmCriticalInfo(msg); - try { - File fname = getSettingsProblemFile(); - FileOutputStream out = new FileOutputStream(fname, true); - PrintWriter pw = new FastPrintWriter(out); - SimpleDateFormat formatter = new SimpleDateFormat(); - String dateString = formatter.format(new Date(System.currentTimeMillis())); - pw.println(dateString + ": " + msg); - pw.close(); - FileUtils.setPermissions( - fname.toString(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH, - -1, -1); - } catch (java.io.IOException e) { - } - } - - private long getLastModifiedTime(PackageParser.Package pkg, File srcFile) { - if (srcFile.isDirectory()) { - final File baseFile = new File(pkg.baseCodePath); - long maxModifiedTime = baseFile.lastModified(); - if (pkg.splitCodePaths != null) { - for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) { - final File splitFile = new File(pkg.splitCodePaths[i]); - maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified()); - } - } - return maxModifiedTime; - } - return srcFile.lastModified(); - } - private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, File srcFile, final int policyFlags) throws PackageManagerException { // When upgrading from pre-N MR1, verify the package time stamp using the package @@ -8294,7 +8078,7 @@ public class PackageManagerService extends IPackageManager.Stub && !isCompatSignatureUpdateNeeded(pkg) && !isRecoverSignatureUpdateNeeded(pkg)) { long mSigningKeySetId = ps.keySetData.getProperSigningKeySet(); - KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.mKeySetManagerService; ArraySet signingKs; synchronized (mPackages) { signingKs = ksms.getPublicKeysFromKeySetLPr(mSigningKeySetId); @@ -8710,7 +8494,7 @@ public class PackageManagerService extends IPackageManager.Stub return scannedPkg; } - private void renameStaticSharedLibraryPackage(PackageParser.Package pkg) { + private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) { // Derive the new package synthetic package name pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER + pkg.staticSharedLibVersion); @@ -8724,49 +8508,6 @@ public class PackageManagerService extends IPackageManager.Stub return processName; } - private void verifySignaturesLP(PackageSetting pkgSetting, PackageParser.Package pkg) - throws PackageManagerException { - if (pkgSetting.signatures.mSignatures != null) { - // Already existing package. Make sure signatures match - boolean match = compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures) - == PackageManager.SIGNATURE_MATCH; - if (!match) { - match = compareSignaturesCompat(pkgSetting.signatures, pkg) - == PackageManager.SIGNATURE_MATCH; - } - if (!match) { - match = compareSignaturesRecover(pkgSetting.signatures, pkg) - == PackageManager.SIGNATURE_MATCH; - } - if (!match) { - throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " - + pkg.packageName + " signatures do not match the " - + "previously installed version; ignoring!"); - } - } - - // Check for shared user signatures - if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) { - // Already existing package. Make sure signatures match - boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, - pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; - if (!match) { - match = compareSignaturesCompat(pkgSetting.sharedUser.signatures, pkg) - == PackageManager.SIGNATURE_MATCH; - } - if (!match) { - match = compareSignaturesRecover(pkgSetting.sharedUser.signatures, pkg) - == PackageManager.SIGNATURE_MATCH; - } - if (!match) { - throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, - "Package " + pkg.packageName - + " has no signatures that match those in shared user " - + pkgSetting.sharedUser.name + "; ignoring!"); - } - } - } - /** * Enforces that only the system UID or root's UID can call a method exposed * via Binder. @@ -9737,24 +9478,6 @@ public class PackageManagerService extends IPackageManager.Stub return res; } - /** - * Derive the value of the {@code cpuAbiOverride} based on the provided - * value and an optional stored value from the package settings. - */ - private static String deriveAbiOverride(String abiOverride, PackageSetting settings) { - String cpuAbiOverride = null; - - if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) { - cpuAbiOverride = null; - } else if (abiOverride != null) { - cpuAbiOverride = abiOverride; - } else if (settings != null) { - cpuAbiOverride = settings.cpuAbiOverrideString; - } - - return cpuAbiOverride; - } - private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg, final int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { @@ -10102,8 +9825,9 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) { - if (checkUpgradeKeySetLP(signatureCheckPs, pkg)) { + final KeySetManagerService ksms = mSettings.mKeySetManagerService; + if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) { + if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) { // We just determined the app is signed correctly, so bring // over the latest parsed certs. pkgSetting.signatures.mSignatures = pkg.mSignatures; @@ -10121,8 +9845,16 @@ public class PackageManagerService extends IPackageManager.Stub } } else { try { - // SIDE EFFECTS; compareSignaturesCompat() changes KeysetManagerService - verifySignaturesLP(signatureCheckPs, pkg); + final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg); + final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg); + final boolean compatMatch = verifySignatures(signatureCheckPs, pkg.mSignatures, + compareCompat, compareRecover); + // The new KeySets will be re-added later in the scanning process. + if (compatMatch) { + synchronized (mPackages) { + ksms.removeAppKeySetDataLPw(pkg.packageName); + } + } // We just determined the app is signed correctly, so bring // over the latest parsed certs. pkgSetting.signatures.mSignatures = pkg.mSignatures; @@ -10410,7 +10142,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Make sure we're not adding any bogus keyset info - KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.mKeySetManagerService; ksms.assertScannedPackageValid(pkg); synchronized (mPackages) { @@ -15566,42 +15298,6 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - private boolean shouldCheckUpgradeKeySetLP(PackageSettingBase oldPs, int scanFlags) { - // Can't rotate keys during boot or if sharedUser. - if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser() - || !oldPs.keySetData.isUsingUpgradeKeySets()) { - return false; - } - // app is using upgradeKeySets; make sure all are valid - KeySetManagerService ksms = mSettings.mKeySetManagerService; - long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets(); - for (int i = 0; i < upgradeKeySets.length; i++) { - if (!ksms.isIdValidKeySetId(upgradeKeySets[i])) { - Slog.wtf(TAG, "Package " - + (oldPs.name != null ? oldPs.name : "") - + " contains upgrade-key-set reference to unknown key-set: " - + upgradeKeySets[i] - + " reverting to signatures check."); - return false; - } - } - return true; - } - - private boolean checkUpgradeKeySetLP(PackageSettingBase oldPS, PackageParser.Package newPkg) { - // Upgrade keysets are being used. Determine if new package has a superset of the - // required keys. - long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets(); - KeySetManagerService ksms = mSettings.mKeySetManagerService; - for (int i = 0; i < upgradeKeySets.length; i++) { - Set upgradeSet = ksms.getPublicKeysFromKeySetLPr(upgradeKeySets[i]); - if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) { - return true; - } - } - return false; - } - private static void updateDigest(MessageDigest digest, File file) throws IOException { try (DigestInputStream digestStream = new DigestInputStream(new FileInputStream(file), digest)) { @@ -15640,8 +15336,9 @@ public class PackageManagerService extends IPackageManager.Stub ps = mSettings.mPackages.get(pkgName); // verify signatures are valid - if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) { - if (!checkUpgradeKeySetLP(ps, pkg)) { + final KeySetManagerService ksms = mSettings.mKeySetManagerService; + if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) { + if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) { res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "New package not signed by keys specified by upgrade-keysets: " + pkgName); @@ -16542,8 +16239,9 @@ public class PackageManagerService extends IPackageManager.Stub // Quick sanity check that we're signed correctly if updating; // we'll check this again later when scanning, but we want to // bail early here before tripping over redefined permissions. - if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) { - if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) { + final KeySetManagerService ksms = mSettings.mKeySetManagerService; + if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) { + if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) { res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + pkg.packageName + " upgrade keys do not match the " + "previously installed version"); @@ -16551,7 +16249,16 @@ public class PackageManagerService extends IPackageManager.Stub } } else { try { - verifySignaturesLP(signatureCheckPs, pkg); + final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg); + final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg); + final boolean compatMatch = verifySignatures( + signatureCheckPs, pkg.mSignatures, compareCompat, compareRecover); + // The new KeySets will be re-added later in the scanning process. + if (compatMatch) { + synchronized (mPackages) { + ksms.removeAppKeySetDataLPw(pkg.packageName); + } + } } catch (PackageManagerException e) { res.setError(e.error, e.getMessage()); return; @@ -16589,10 +16296,11 @@ public class PackageManagerService extends IPackageManager.Stub final boolean sigsOk; final String sourcePackageName = bp.getSourcePackageName(); final PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting(); + final KeySetManagerService ksms = mSettings.mKeySetManagerService; if (sourcePackageName.equals(pkg.packageName) - && (shouldCheckUpgradeKeySetLP(sourcePackageSetting, - scanFlags))) { - sigsOk = checkUpgradeKeySetLP(sourcePackageSetting, pkg); + && (ksms.shouldCheckUpgradeKeySetLocked( + sourcePackageSetting, scanFlags))) { + sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg); } else { sigsOk = compareSignatures(sourcePackageSetting.signatures.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; @@ -20938,34 +20646,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); pw.println(); pw.println("Package warning messages:"); - BufferedReader in = null; - String line = null; - try { - in = new BufferedReader(new FileReader(getSettingsProblemFile())); - while ((line = in.readLine()) != null) { - if (line.contains("ignored: updated version")) continue; - pw.println(line); - } - } catch (IOException ignored) { - } finally { - IoUtils.closeQuietly(in); - } + dumpCriticalInfo(pw, null); } if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) { - BufferedReader in = null; - String line = null; - try { - in = new BufferedReader(new FileReader(getSettingsProblemFile())); - while ((line = in.readLine()) != null) { - if (line.contains("ignored: updated version")) continue; - pw.print("msg,"); - pw.println(line); - } - } catch (IOException ignored) { - } finally { - IoUtils.closeQuietly(in); - } + dumpCriticalInfo(pw, "msg,"); } } @@ -21011,26 +20696,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); dumpFeaturesProto(proto); mSettings.dumpPackagesProto(proto); mSettings.dumpSharedUsersProto(proto); - dumpMessagesProto(proto); + dumpCriticalInfo(proto); } proto.flush(); } - private void dumpMessagesProto(ProtoOutputStream proto) { - BufferedReader in = null; - String line = null; - try { - in = new BufferedReader(new FileReader(getSettingsProblemFile())); - while ((line = in.readLine()) != null) { - if (line.contains("ignored: updated version")) continue; - proto.write(PackageServiceDumpProto.MESSAGES, line); - } - } catch (IOException ignored) { - } finally { - IoUtils.closeQuietly(in); - } - } - private void dumpFeaturesProto(ProtoOutputStream proto) { synchronized (mAvailableFeatures) { final int count = mAvailableFeatures.size(); @@ -22439,7 +22109,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); Slog.w(TAG, "KeySet requested for filtered package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } - KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.mKeySetManagerService; return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias)); } } @@ -22468,7 +22138,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); && Process.SYSTEM_UID != callingUid) { throw new SecurityException("May not access signing KeySet of other apps."); } - KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.mKeySetManagerService; return new KeySet(ksms.getSigningKeySetByPackageNameLPr(packageName)); } } @@ -22492,7 +22162,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } IBinder ksh = ks.getToken(); if (ksh instanceof KeySetHandle) { - KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.mKeySetManagerService; return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ksh); } return false; @@ -22518,7 +22188,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } IBinder ksh = ks.getToken(); if (ksh instanceof KeySetHandle) { - KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.mKeySetManagerService; return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ksh); } return false; diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 67e06dda044fd..758abd76950b2 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -16,42 +16,74 @@ package com.android.server.pm; +import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; +import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; +import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION; +import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; +import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; +import static com.android.server.pm.PackageManagerService.STUB_SUFFIX; +import static com.android.server.pm.PackageManagerService.TAG; +import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; + +import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastPrintWriter; +import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; -import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; -import static com.android.server.pm.PackageManagerService.TAG; - -import com.android.internal.util.ArrayUtils; - import android.annotation.NonNull; import android.app.AppGlobals; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.ResolveInfo; +import android.content.pm.Signature; import android.os.Build; import android.os.Debug; +import android.os.Environment; +import android.os.FileUtils; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.service.pm.PackageServiceDumpProto; import android.system.ErrnoException; import android.system.Os; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.jar.StrictJarFile; -import dalvik.system.VMRuntime; -import libcore.io.Libcore; +import android.util.proto.ProtoOutputStream; +import dalvik.system.VMRuntime; + +import libcore.io.IoUtils; +import libcore.io.Libcore; +import libcore.io.Streams; + +import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FilenameFilter; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.function.Predicate; +import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; /** @@ -200,7 +232,7 @@ public class PackageManagerServiceUtils { * * If it doesn't have sufficient information about the package, it return false. */ - static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, + public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo, long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) { @@ -263,6 +295,21 @@ public class PackageManagerServiceUtils { return false; } + public static long getLastModifiedTime(PackageParser.Package pkg, File srcFile) { + if (srcFile.isDirectory()) { + final File baseFile = new File(pkg.baseCodePath); + long maxModifiedTime = baseFile.lastModified(); + if (pkg.splitCodePaths != null) { + for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) { + final File splitFile = new File(pkg.splitCodePaths[i]); + maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified()); + } + } + return maxModifiedTime; + } + return srcFile.lastModified(); + } + /** * Checks that the archive located at {@code fileName} has uncompressed dex file and so * files that can be direclty mapped. @@ -318,6 +365,57 @@ public class PackageManagerServiceUtils { } } + private static File getSettingsProblemFile() { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + File fname = new File(systemDir, "uiderrors.txt"); + return fname; + } + + public static void dumpCriticalInfo(ProtoOutputStream proto) { + try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) { + String line = null; + while ((line = in.readLine()) != null) { + if (line.contains("ignored: updated version")) continue; + proto.write(PackageServiceDumpProto.MESSAGES, line); + } + } catch (IOException ignored) { + } + } + + public static void dumpCriticalInfo(PrintWriter pw, String msg) { + try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) { + String line = null; + while ((line = in.readLine()) != null) { + if (line.contains("ignored: updated version")) continue; + if (msg != null) { + pw.print(msg); + } + pw.println(line); + } + } catch (IOException ignored) { + } + } + + public static void logCriticalInfo(int priority, String msg) { + Slog.println(priority, TAG, msg); + EventLogTags.writePmCriticalInfo(msg); + try { + File fname = getSettingsProblemFile(); + FileOutputStream out = new FileOutputStream(fname, true); + PrintWriter pw = new FastPrintWriter(out); + SimpleDateFormat formatter = new SimpleDateFormat(); + String dateString = formatter.format(new Date(System.currentTimeMillis())); + pw.println(dateString + ": " + msg); + pw.close(); + FileUtils.setPermissions( + fname.toString(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH, + -1, -1); + } catch (java.io.IOException e) { + } + } + public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) { if (callingUid == Process.SHELL_UID) { if (userHandle >= 0 @@ -331,4 +429,240 @@ public class PackageManagerServiceUtils { } } } + + /** + * Derive the value of the {@code cpuAbiOverride} based on the provided + * value and an optional stored value from the package settings. + */ + public static String deriveAbiOverride(String abiOverride, PackageSetting settings) { + String cpuAbiOverride = null; + if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) { + cpuAbiOverride = null; + } else if (abiOverride != null) { + cpuAbiOverride = abiOverride; + } else if (settings != null) { + cpuAbiOverride = settings.cpuAbiOverrideString; + } + return cpuAbiOverride; + } + + /** + * Compares two sets of signatures. Returns: + *
+ * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null, + *
+ * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null, + *
+ * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null, + *
+ * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical, + *
+ * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ. + */ + public static int compareSignatures(Signature[] s1, Signature[] s2) { + if (s1 == null) { + return s2 == null + ? PackageManager.SIGNATURE_NEITHER_SIGNED + : PackageManager.SIGNATURE_FIRST_NOT_SIGNED; + } + + if (s2 == null) { + return PackageManager.SIGNATURE_SECOND_NOT_SIGNED; + } + + if (s1.length != s2.length) { + return PackageManager.SIGNATURE_NO_MATCH; + } + + // Since both signature sets are of size 1, we can compare without HashSets. + if (s1.length == 1) { + return s1[0].equals(s2[0]) ? + PackageManager.SIGNATURE_MATCH : + PackageManager.SIGNATURE_NO_MATCH; + } + + ArraySet set1 = new ArraySet(); + for (Signature sig : s1) { + set1.add(sig); + } + ArraySet set2 = new ArraySet(); + for (Signature sig : s2) { + set2.add(sig); + } + // Make sure s2 contains all signatures in s1. + if (set1.equals(set2)) { + return PackageManager.SIGNATURE_MATCH; + } + return PackageManager.SIGNATURE_NO_MATCH; + } + + /** + * Used for backward compatibility to make sure any packages with + * certificate chains get upgraded to the new style. {@code existingSigs} + * will be in the old format (since they were stored on disk from before the + * system upgrade) and {@code scannedSigs} will be in the newer format. + */ + private static boolean matchSignaturesCompat(String packageName, + PackageSignatures packageSignatures, Signature[] parsedSignatures) { + ArraySet existingSet = new ArraySet(); + for (Signature sig : packageSignatures.mSignatures) { + existingSet.add(sig); + } + ArraySet scannedCompatSet = new ArraySet(); + for (Signature sig : parsedSignatures) { + try { + Signature[] chainSignatures = sig.getChainSignatures(); + for (Signature chainSig : chainSignatures) { + scannedCompatSet.add(chainSig); + } + } catch (CertificateEncodingException e) { + scannedCompatSet.add(sig); + } + } + // make sure the expanded scanned set contains all signatures in the existing one + if (scannedCompatSet.equals(existingSet)) { + // migrate the old signatures to the new scheme + packageSignatures.assignSignatures(parsedSignatures); + return true; + } + return false; + } + + private static boolean matchSignaturesRecover(String packageName, + Signature[] existingSignatures, Signature[] parsedSignatures) { + String msg = null; + try { + if (Signature.areEffectiveMatch(existingSignatures, parsedSignatures)) { + logCriticalInfo(Log.INFO, + "Recovered effectively matching certificates for " + packageName); + return true; + } + } catch (CertificateException e) { + msg = e.getMessage(); + } + logCriticalInfo(Log.INFO, + "Failed to recover certificates for " + packageName + ": " + msg); + return false; + } + + /** + * Verifies that signatures match. + * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}. + * @throws PackageManagerException if the signatures did not match. + */ + public static boolean verifySignatures(PackageSetting pkgSetting, + Signature[] parsedSignatures, boolean compareCompat, boolean compareRecover) + throws PackageManagerException { + final String packageName = pkgSetting.name; + boolean compatMatch = false; + if (pkgSetting.signatures.mSignatures != null) { + // Already existing package. Make sure signatures match + boolean match = compareSignatures(pkgSetting.signatures.mSignatures, parsedSignatures) + == PackageManager.SIGNATURE_MATCH; + if (!match && compareCompat) { + match = matchSignaturesCompat(packageName, pkgSetting.signatures, parsedSignatures); + compatMatch = match; + } + if (!match && compareRecover) { + match = matchSignaturesRecover( + packageName, pkgSetting.signatures.mSignatures, parsedSignatures); + } + if (!match) { + throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "Package " + packageName + + " signatures don't match previously installed version; ignoring!"); + } + } + // Check for shared user signatures + if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) { + // Already existing package. Make sure signatures match + boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, + parsedSignatures) == PackageManager.SIGNATURE_MATCH; + if (!match) { + match = matchSignaturesCompat( + packageName, pkgSetting.sharedUser.signatures, parsedSignatures); + } + if (!match && compareCompat) { + match = matchSignaturesRecover( + packageName, pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures); + compatMatch |= match; + } + if (!match && compareRecover) { + throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, + "Package " + packageName + + " has no signatures that match those in shared user " + + pkgSetting.sharedUser.name + "; ignoring!"); + } + } + return compatMatch; + } + + public static int decompressFile(File srcFile, File dstFile) throws ErrnoException { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "Decompress file" + + "; src: " + srcFile.getAbsolutePath() + + ", dst: " + dstFile.getAbsolutePath()); + } + try ( + InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile)); + OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/); + ) { + Streams.copy(fileIn, fileOut); + Os.chmod(dstFile.getAbsolutePath(), 0644); + return PackageManager.INSTALL_SUCCEEDED; + } catch (IOException e) { + logCriticalInfo(Log.ERROR, "Failed to decompress file" + + "; src: " + srcFile.getAbsolutePath() + + ", dst: " + dstFile.getAbsolutePath()); + } + return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + } + + public static File[] getCompressedFiles(String codePath) { + final File stubCodePath = new File(codePath); + final String stubName = stubCodePath.getName(); + + // The layout of a compressed package on a given partition is as follows : + // + // Compressed artifacts: + // + // /partition/ModuleName/foo.gz + // /partation/ModuleName/bar.gz + // + // Stub artifact: + // + // /partition/ModuleName-Stub/ModuleName-Stub.apk + // + // In other words, stub is on the same partition as the compressed artifacts + // and in a directory that's suffixed with "-Stub". + int idx = stubName.lastIndexOf(STUB_SUFFIX); + if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) { + return null; + } + + final File stubParentDir = stubCodePath.getParentFile(); + if (stubParentDir == null) { + Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath); + return null; + } + + final File compressedPath = new File(stubParentDir, stubName.substring(0, idx)); + final File[] files = compressedPath.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(COMPRESSED_EXTENSION); + } + }); + + if (DEBUG_COMPRESSION && files != null && files.length > 0) { + Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files)); + } + + return files; + } + + public static boolean compressedFileExists(String codePath) { + final File[] compressedFiles = getCompressedFiles(codePath); + return compressedFiles != null && compressedFiles.length > 0; + } } diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java index bfe09b8ecbce7..96c102b50a125 100644 --- a/services/core/java/com/android/server/pm/UserDataPreparer.java +++ b/services/core/java/com/android/server/pm/UserDataPreparer.java @@ -16,6 +16,8 @@ package com.android.server.pm; +import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; + import android.content.Context; import android.content.pm.UserInfo; import android.os.Environment; @@ -40,8 +42,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import static com.android.server.pm.PackageManagerService.logCriticalInfo; - /** * Helper class for preparing and destroying user storage */ diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 8014acf653eeb..c40d1fad9931a 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -16,6 +16,8 @@ package com.android.server.pm.permission; +import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1110,8 +1112,8 @@ public final class DefaultPermissionGrantPolicy { final String systemPackageName = mServiceInternal.getKnownPackageName( PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM); final PackageParser.Package systemPackage = getPackage(systemPackageName); - return PackageManagerService.compareSignatures(systemPackage.mSignatures, - pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; + return compareSignatures(systemPackage.mSignatures, pkg.mSignatures) + == PackageManager.SIGNATURE_MATCH; } private void grantDefaultPermissionExceptions(int userId) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 6d2051f28253f..7d8e20696affe 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -1009,10 +1009,10 @@ Slog.e(TAG, "TODD: Packages: " + Arrays.toString(packages)); PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM); final PackageParser.Package systemPackage = mPackageManagerInt.getPackage(systemPackageName); - boolean allowed = (PackageManagerService.compareSignatures( + boolean allowed = (PackageManagerServiceUtils.compareSignatures( bp.getSourceSignatures(), pkg.mSignatures) == PackageManager.SIGNATURE_MATCH) - || (PackageManagerService.compareSignatures( + || (PackageManagerServiceUtils.compareSignatures( systemPackage.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH); if (!allowed && (privilegedPermission || oemPermission)) {