Merge "Ignore signature stripping protection for preinstalled APKs." into nyc-dev

am: e60d72f

* commit 'e60d72f49a10ba8d51fd184ed5604ca4d8225552':
  Ignore signature stripping protection for preinstalled APKs.
This commit is contained in:
Alex Klyubin
2016-03-24 19:51:30 +00:00
committed by android-build-merger
3 changed files with 69 additions and 39 deletions

View File

@@ -1158,10 +1158,15 @@ public class PackageParser {
StrictJarFile jarFile = null; StrictJarFile jarFile = null;
try { try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor"); 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( jarFile = new StrictJarFile(
apkPath, apkPath,
!verified // whether to verify JAR signature !verified, // whether to verify JAR signature
); signatureSchemeRollbackProtectionsEnforced);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Always verify manifest, regardless of source // Always verify manifest, regardless of source

View File

@@ -58,11 +58,22 @@ public final class StrictJarFile {
public StrictJarFile(String fileName) public StrictJarFile(String fileName)
throws IOException, SecurityException { 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.nativeHandle = nativeOpenJarFile(fileName);
this.raf = new RandomAccessFile(fileName, "r"); this.raf = new RandomAccessFile(fileName, "r");
@@ -73,7 +84,12 @@ public final class StrictJarFile {
if (verify) { if (verify) {
HashMap<String, byte[]> metaEntries = getMetaEntries(); HashMap<String, byte[]> metaEntries = getMetaEntries();
this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true); 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<String> files = manifest.getEntries().keySet(); Set<String> files = manifest.getEntries().keySet();
for (String file : files) { for (String file : files) {
if (findEntry(file) == null) { if (findEntry(file) == null) {

View File

@@ -72,6 +72,7 @@ class StrictJarVerifier {
private final StrictJarManifest manifest; private final StrictJarManifest manifest;
private final HashMap<String, byte[]> metaEntries; private final HashMap<String, byte[]> metaEntries;
private final int mainAttributesEnd; private final int mainAttributesEnd;
private final boolean signatureSchemeRollbackProtectionsEnforced;
private final Hashtable<String, HashMap<String, Attributes>> signatures = private final Hashtable<String, HashMap<String, Attributes>> signatures =
new Hashtable<String, HashMap<String, Attributes>>(5); new Hashtable<String, HashMap<String, Attributes>>(5);
@@ -164,13 +165,19 @@ class StrictJarVerifier {
* *
* @param name * @param name
* the name of the JAR file being verified. * 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, StrictJarVerifier(String name, StrictJarManifest manifest,
HashMap<String, byte[]> metaEntries) { HashMap<String, byte[]> metaEntries, boolean signatureSchemeRollbackProtectionsEnforced) {
jarName = name; jarName = name;
this.manifest = manifest; this.manifest = manifest;
this.metaEntries = metaEntries; this.metaEntries = metaEntries;
this.mainAttributesEnd = manifest.getMainAttributesEnd(); this.mainAttributesEnd = manifest.getMainAttributesEnd();
this.signatureSchemeRollbackProtectionsEnforced =
signatureSchemeRollbackProtectionsEnforced;
} }
/** /**
@@ -357,40 +364,42 @@ class StrictJarVerifier {
return; return;
} }
// Check whether APK Signature Scheme v2 signature was stripped. // If requested, check whether APK Signature Scheme v2 signature was stripped.
String apkSignatureSchemeIdList = if (signatureSchemeRollbackProtectionsEnforced) {
attributes.getValue( String apkSignatureSchemeIdList =
ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME); attributes.getValue(
if (apkSignatureSchemeIdList != null) { ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
// This field contains a comma-separated list of APK signature scheme IDs which were if (apkSignatureSchemeIdList != null) {
// used to sign this APK. If an ID is known to us, it means signatures of that scheme // This field contains a comma-separated list of APK signature scheme IDs which
// were stripped from the APK because otherwise we wouldn't have fallen back to // were used to sign this APK. If an ID is known to us, it means signatures of that
// verifying the APK using the JAR signature scheme. // scheme were stripped from the APK because otherwise we wouldn't have fallen back
boolean v2SignatureGenerated = false; // to verifying the APK using the JAR signature scheme.
StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ","); boolean v2SignatureGenerated = false;
while (tokenizer.hasMoreTokens()) { StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
String idText = tokenizer.nextToken().trim(); while (tokenizer.hasMoreTokens()) {
if (idText.isEmpty()) { String idText = tokenizer.nextToken().trim();
continue; 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) { if (v2SignatureGenerated) {
throw new SecurityException(signatureFile + " indicates " + jarName + " is signed" throw new SecurityException(signatureFile + " indicates " + jarName
+ " using APK Signature Scheme v2, but no such signature was found." + " is signed using APK Signature Scheme v2, but no such signature was"
+ " Signature stripped?"); + " found. Signature stripped?");
}
} }
} }