diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index db7ca2af80e39..84605bb14d7da 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1158,10 +1158,15 @@ public class PackageParser { StrictJarFile jarFile = null; try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor"); + // Ignore signature stripping protections when verifying APKs from system partition. + // For those APKs we only care about extracting signer certificates, and don't care + // about verifying integrity. + boolean signatureSchemeRollbackProtectionsEnforced = + (parseFlags & PARSE_IS_SYSTEM) == 0; jarFile = new StrictJarFile( apkPath, - !verified // whether to verify JAR signature - ); + !verified, // whether to verify JAR signature + signatureSchemeRollbackProtectionsEnforced); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Always verify manifest, regardless of source diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java index 302a08d58dd3a..5d94b06df9fae 100644 --- a/core/java/android/util/jar/StrictJarFile.java +++ b/core/java/android/util/jar/StrictJarFile.java @@ -58,11 +58,22 @@ public final class StrictJarFile { public StrictJarFile(String fileName) throws IOException, SecurityException { - this(fileName, true); + this(fileName, true, true); } - public StrictJarFile(String fileName, boolean verify) - throws IOException, SecurityException { + /** + * + * @param verify whether to verify the file's JAR signatures and collect the corresponding + * signer certificates. + * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against + * stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or + * {@code false} to ignore any such protections. This parameter is ignored when + * {@code verify} is {@code false}. + */ + public StrictJarFile(String fileName, + boolean verify, + boolean signatureSchemeRollbackProtectionsEnforced) + throws IOException, SecurityException { this.nativeHandle = nativeOpenJarFile(fileName); this.raf = new RandomAccessFile(fileName, "r"); @@ -73,7 +84,12 @@ public final class StrictJarFile { if (verify) { HashMap metaEntries = getMetaEntries(); this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true); - this.verifier = new StrictJarVerifier(fileName, manifest, metaEntries); + this.verifier = + new StrictJarVerifier( + fileName, + manifest, + metaEntries, + signatureSchemeRollbackProtectionsEnforced); Set files = manifest.getEntries().keySet(); for (String file : files) { if (findEntry(file) == null) { diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java index 0546a5f724d68..6da50ba85b37d 100644 --- a/core/java/android/util/jar/StrictJarVerifier.java +++ b/core/java/android/util/jar/StrictJarVerifier.java @@ -72,6 +72,7 @@ class StrictJarVerifier { private final StrictJarManifest manifest; private final HashMap metaEntries; private final int mainAttributesEnd; + private final boolean signatureSchemeRollbackProtectionsEnforced; private final Hashtable> signatures = new Hashtable>(5); @@ -164,13 +165,19 @@ class StrictJarVerifier { * * @param name * the name of the JAR file being verified. + * + * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against + * stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or + * {@code false} to ignore any such protections. */ StrictJarVerifier(String name, StrictJarManifest manifest, - HashMap metaEntries) { + HashMap metaEntries, boolean signatureSchemeRollbackProtectionsEnforced) { jarName = name; this.manifest = manifest; this.metaEntries = metaEntries; this.mainAttributesEnd = manifest.getMainAttributesEnd(); + this.signatureSchemeRollbackProtectionsEnforced = + signatureSchemeRollbackProtectionsEnforced; } /** @@ -357,40 +364,42 @@ class StrictJarVerifier { return; } - // Check whether APK Signature Scheme v2 signature was stripped. - String apkSignatureSchemeIdList = - attributes.getValue( - ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME); - if (apkSignatureSchemeIdList != null) { - // This field contains a comma-separated list of APK signature scheme IDs which were - // used to sign this APK. If an ID is known to us, it means signatures of that scheme - // were stripped from the APK because otherwise we wouldn't have fallen back to - // verifying the APK using the JAR signature scheme. - boolean v2SignatureGenerated = false; - StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ","); - while (tokenizer.hasMoreTokens()) { - String idText = tokenizer.nextToken().trim(); - if (idText.isEmpty()) { - continue; + // If requested, check whether APK Signature Scheme v2 signature was stripped. + if (signatureSchemeRollbackProtectionsEnforced) { + String apkSignatureSchemeIdList = + attributes.getValue( + ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME); + if (apkSignatureSchemeIdList != null) { + // This field contains a comma-separated list of APK signature scheme IDs which + // were used to sign this APK. If an ID is known to us, it means signatures of that + // scheme were stripped from the APK because otherwise we wouldn't have fallen back + // to verifying the APK using the JAR signature scheme. + boolean v2SignatureGenerated = false; + StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ","); + while (tokenizer.hasMoreTokens()) { + String idText = tokenizer.nextToken().trim(); + if (idText.isEmpty()) { + continue; + } + int id; + try { + id = Integer.parseInt(idText); + } catch (Exception ignored) { + continue; + } + if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) { + // This APK was supposed to be signed with APK Signature Scheme v2 but no + // such signature was found. + v2SignatureGenerated = true; + break; + } } - int id; - try { - id = Integer.parseInt(idText); - } catch (Exception ignored) { - continue; - } - if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) { - // This APK was supposed to be signed with APK Signature Scheme v2 but no such - // signature was found. - v2SignatureGenerated = true; - break; - } - } - if (v2SignatureGenerated) { - throw new SecurityException(signatureFile + " indicates " + jarName + " is signed" - + " using APK Signature Scheme v2, but no such signature was found." - + " Signature stripped?"); + if (v2SignatureGenerated) { + throw new SecurityException(signatureFile + " indicates " + jarName + + " is signed using APK Signature Scheme v2, but no such signature was" + + " found. Signature stripped?"); + } } }