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:
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user