summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2018-01-23 23:19:59 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-01-23 23:19:59 +0000
commit4ec3efce54b14157ce9084faed51be07cbbbe274 (patch)
treee3fac3fe3d7f69492b5374dff66657b3fda2221d
parent555c6fcd96cfd00bfa7f8d26de0faca41d103675 (diff)
parent77029c5b16351775cb2333369ef9a4bc1d9acf58 (diff)
Merge "Add proof-of-rotation information to PackageParser.SigningDetails"
-rw-r--r--core/java/android/content/pm/PackageParser.java149
-rw-r--r--core/java/android/util/apk/ApkSignatureVerifier.java44
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java63
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java26
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageSignatures.java264
-rw-r--r--services/core/java/com/android/server/pm/Settings.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java4
8 files changed, 422 insertions, 138 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4a71467f4b36..4efd08134065 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -5684,23 +5684,74 @@ public class PackageParser {
@Nullable
public final ArraySet<PublicKey> publicKeys;
+ /**
+ * Collection of {@code Signature} objects, each of which is formed from a former signing
+ * certificate of this APK before it was changed by signing certificate rotation.
+ */
+ @Nullable
+ public final Signature[] pastSigningCertificates;
+
+ /**
+ * Flags for the {@code pastSigningCertificates} collection, which indicate the capabilities
+ * the including APK wishes to grant to its past signing certificates.
+ */
+ @Nullable
+ public final int[] pastSigningCertificatesFlags;
+
/** A representation of unknown signing details. Use instead of null. */
public static final SigningDetails UNKNOWN =
- new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null);
+ new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null, null);
@VisibleForTesting
public SigningDetails(Signature[] signatures,
@SignatureSchemeVersion int signatureSchemeVersion,
- ArraySet<PublicKey> keys) {
+ ArraySet<PublicKey> keys, Signature[] pastSigningCertificates,
+ int[] pastSigningCertificatesFlags) {
this.signatures = signatures;
this.signatureSchemeVersion = signatureSchemeVersion;
this.publicKeys = keys;
+ this.pastSigningCertificates = pastSigningCertificates;
+ this.pastSigningCertificatesFlags = pastSigningCertificatesFlags;
+ }
+
+ public SigningDetails(Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ Signature[] pastSigningCertificates, int[] pastSigningCertificatesFlags)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
+ pastSigningCertificates, pastSigningCertificatesFlags);
}
public SigningDetails(Signature[] signatures,
@SignatureSchemeVersion int signatureSchemeVersion)
throws CertificateException {
- this(signatures, signatureSchemeVersion, toSigningKeys(signatures));
+ this(signatures, signatureSchemeVersion,
+ null, null);
+ }
+
+ public SigningDetails(SigningDetails orig) {
+ if (orig != null) {
+ if (orig.signatures != null) {
+ this.signatures = orig.signatures.clone();
+ } else {
+ this.signatures = null;
+ }
+ this.signatureSchemeVersion = orig.signatureSchemeVersion;
+ this.publicKeys = new ArraySet<>(orig.publicKeys);
+ if (orig.pastSigningCertificates != null) {
+ this.pastSigningCertificates = orig.pastSigningCertificates.clone();
+ this.pastSigningCertificatesFlags = orig.pastSigningCertificatesFlags.clone();
+ } else {
+ this.pastSigningCertificates = null;
+ this.pastSigningCertificatesFlags = null;
+ }
+ } else {
+ this.signatures = null;
+ this.signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ this.publicKeys = null;
+ this.pastSigningCertificates = null;
+ this.pastSigningCertificatesFlags = null;
+ }
}
/** Returns true if the signing details have one or more signatures. */
@@ -5728,6 +5779,8 @@ public class PackageParser {
dest.writeTypedArray(this.signatures, flags);
dest.writeInt(this.signatureSchemeVersion);
dest.writeArraySet(this.publicKeys);
+ dest.writeTypedArray(this.pastSigningCertificates, flags);
+ dest.writeIntArray(this.pastSigningCertificatesFlags);
}
protected SigningDetails(Parcel in) {
@@ -5735,6 +5788,8 @@ public class PackageParser {
this.signatures = in.createTypedArray(Signature.CREATOR);
this.signatureSchemeVersion = in.readInt();
this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
+ this.pastSigningCertificates = in.createTypedArray(Signature.CREATOR);
+ this.pastSigningCertificatesFlags = in.createIntArray();
}
public static final Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() {
@@ -5761,8 +5816,23 @@ public class PackageParser {
if (signatureSchemeVersion != that.signatureSchemeVersion) return false;
if (!Signature.areExactMatch(signatures, that.signatures)) return false;
- return publicKeys != null ? publicKeys.equals(that.publicKeys)
- : that.publicKeys == null;
+ if (publicKeys != null) {
+ if (!publicKeys.equals((that.publicKeys))) {
+ return false;
+ }
+ } else if (that.publicKeys != null) {
+ return false;
+ }
+
+ // can't use Signature.areExactMatch() because order matters with the past signing certs
+ if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
+ return false;
+ }
+ if (!Arrays.equals(pastSigningCertificatesFlags, that.pastSigningCertificatesFlags)) {
+ return false;
+ }
+
+ return true;
}
@Override
@@ -5770,8 +5840,77 @@ public class PackageParser {
int result = +Arrays.hashCode(signatures);
result = 31 * result + signatureSchemeVersion;
result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
+ result = 31 * result + Arrays.hashCode(pastSigningCertificates);
+ result = 31 * result + Arrays.hashCode(pastSigningCertificatesFlags);
return result;
}
+
+ /**
+ * Builder of {@code SigningDetails} instances.
+ */
+ public static class Builder {
+ private Signature[] mSignatures;
+ private int mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ private Signature[] mPastSigningCertificates;
+ private int[] mPastSigningCertificatesFlags;
+
+ public Builder() {
+ }
+
+ /** get signing certificates used to sign the current APK */
+ public Builder setSignatures(Signature[] signatures) {
+ mSignatures = signatures;
+ return this;
+ }
+
+ /** set the signature scheme version used to sign the APK */
+ public Builder setSignatureSchemeVersion(int signatureSchemeVersion) {
+ mSignatureSchemeVersion = signatureSchemeVersion;
+ return this;
+ }
+
+ /** set the signing certificates by which the APK proved it can be authenticated */
+ public Builder setPastSigningCertificates(Signature[] pastSigningCertificates) {
+ mPastSigningCertificates = pastSigningCertificates;
+ return this;
+ }
+
+ /** set the flags for the {@code pastSigningCertificates} */
+ public Builder setPastSigningCertificatesFlags(int[] pastSigningCertificatesFlags) {
+ mPastSigningCertificatesFlags = pastSigningCertificatesFlags;
+ return this;
+ }
+
+ private void checkInvariants() {
+ // must have signatures and scheme version set
+ if (mSignatures == null) {
+ throw new IllegalStateException("SigningDetails requires the current signing"
+ + " certificates.");
+ }
+
+ // pastSigningCerts and flags must match up
+ boolean pastMismatch = false;
+ if (mPastSigningCertificates != null && mPastSigningCertificatesFlags != null) {
+ if (mPastSigningCertificates.length != mPastSigningCertificatesFlags.length) {
+ pastMismatch = true;
+ }
+ } else if (!(mPastSigningCertificates == null
+ && mPastSigningCertificatesFlags == null)) {
+ pastMismatch = true;
+ }
+ if (pastMismatch) {
+ throw new IllegalStateException("SigningDetails must have a one to one mapping "
+ + "between pastSigningCertificates and pastSigningCertificatesFlags");
+ }
+ }
+ /** build a {@code SigningDetails} object */
+ public SigningDetails build()
+ throws CertificateException {
+ checkInvariants();
+ return new SigningDetails(mSignatures, mSignatureSchemeVersion,
+ mPastSigningCertificates, mPastSigningCertificatesFlags);
+ }
+ }
}
/**
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index a2a76169c83a..87943725ba21 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -80,10 +80,22 @@ public class ApkSignatureVerifier {
ApkSignatureSchemeV3Verifier.verify(apkPath);
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new PackageParser.SigningDetails(signerSigs,
- SignatureSchemeVersion.SIGNING_BLOCK_V3);
+ Signature[] pastSignerSigs = null;
+ int[] pastSignerSigsFlags = null;
+ if (vSigner.por != null) {
+ // populate proof-of-rotation information
+ pastSignerSigs = new Signature[vSigner.por.certs.size()];
+ pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
+ for (int i = 0; i < pastSignerSigs.length; i++) {
+ pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
+ pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+ }
+ }
+ return new PackageParser.SigningDetails(
+ signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ pastSignerSigs, pastSignerSigsFlags);
} catch (SignatureNotFoundException e) {
- // not signed with v2, try older if allowed
+ // not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v3 signature in package " + apkPath, e);
@@ -92,7 +104,7 @@ public class ApkSignatureVerifier {
// APK Signature Scheme v2 signature found but did not verify
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath
- + " using APK Signature Scheme v2", e);
+ + " using APK Signature Scheme v3", e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -304,25 +316,37 @@ public class ApkSignatureVerifier {
}
// first try v3
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV3");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV3");
try {
ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
ApkSignatureSchemeV3Verifier.plsCertsNoVerifyOnlyCerts(apkPath);
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new PackageParser.SigningDetails(signerSigs,
- SignatureSchemeVersion.SIGNING_BLOCK_V3);
+ Signature[] pastSignerSigs = null;
+ int[] pastSignerSigsFlags = null;
+ if (vSigner.por != null) {
+ // populate proof-of-rotation information
+ pastSignerSigs = new Signature[vSigner.por.certs.size()];
+ pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
+ for (int i = 0; i < pastSignerSigs.length; i++) {
+ pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
+ pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+ }
+ }
+ return new PackageParser.SigningDetails(
+ signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ pastSignerSigs, pastSignerSigsFlags);
} catch (SignatureNotFoundException e) {
- // not signed with v2, try older if allowed
+ // not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v3 signature in package " + apkPath, e);
}
} catch (Exception e) {
- // APK Signature Scheme v2 signature found but did not verify
+ // APK Signature Scheme v3 signature found but did not verify
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath
- + " using APK Signature Scheme v2", e);
+ + " using APK Signature Scheme v3", e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index faf6114237cd..da1bdc7bb15a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5421,13 +5421,13 @@ Slog.e("TODD",
if (isCallerInstantApp) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- s1 = ((SharedUserSetting)obj).signatures.mSignatures;
+ s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- s1 = ps.signatures.mSignatures;
+ s1 = ps.signatures.mSigningDetails.signatures;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
@@ -5440,13 +5440,13 @@ Slog.e("TODD",
if (isCallerInstantApp) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- s2 = ((SharedUserSetting)obj).signatures.mSignatures;
+ s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- s2 = ps.signatures.mSignatures;
+ s2 = ps.signatures.mSigningDetails.signatures;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
@@ -8233,19 +8233,15 @@ Slog.e("TODD",
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(pkg)
&& !isRecoverSignatureUpdateNeeded(pkg)) {
- if (ps.signatures.mSignatures != null
- && ps.signatures.mSignatures.length != 0
- && ps.signatures.mSignatureSchemeVersion != SignatureSchemeVersion.UNKNOWN) {
+ if (ps.signatures.mSigningDetails.signatures != null
+ && ps.signatures.mSigningDetails.signatures.length != 0
+ && ps.signatures.mSigningDetails.signatureSchemeVersion
+ != SignatureSchemeVersion.UNKNOWN) {
// Optimization: reuse the existing cached signing data
// if the package appears to be unchanged.
- try {
- pkg.mSigningDetails = new PackageParser.SigningDetails(ps.signatures.mSignatures,
- ps.signatures.mSignatureSchemeVersion);
- return;
- } catch (CertificateException e) {
- Slog.e(TAG, "Attempt to read public keys from persisted signatures failed for "
- + ps.name, e);
- }
+ pkg.mSigningDetails =
+ new PackageParser.SigningDetails(ps.signatures.mSigningDetails);
+ return;
}
Slog.w(TAG, "PackageSetting for " + ps.name
@@ -8573,8 +8569,9 @@ Slog.e("TODD",
if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
&& !pkgSetting.isSystem()) {
// if the signatures don't match, wipe the installed application and its data
- if (compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSigningDetails.signatures)
- != PackageManager.SIGNATURE_MATCH) {
+ if (compareSignatures(pkgSetting.signatures.mSigningDetails.signatures,
+ pkg.mSigningDetails.signatures)
+ != PackageManager.SIGNATURE_MATCH) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
@@ -9936,14 +9933,14 @@ Slog.e("TODD",
if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
// We just determined the app is signed correctly, so bring
// over the latest parsed certs.
- pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+ pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
} else {
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Package " + pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
} else {
- pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+ pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
String msg = "System package " + pkg.packageName
+ " signature changed; retaining data.";
reportSettingsProblem(Log.WARN, msg);
@@ -9963,21 +9960,22 @@ Slog.e("TODD",
}
// We just determined the app is signed correctly, so bring
// over the latest parsed certs.
- pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+ pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
} catch (PackageManagerException e) {
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
throw e;
}
// The signature has changed, but this package is in the system
// image... let's recover!
- pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+ pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
// However... if this package is part of a shared user, but it
// doesn't match the signature of the shared user, let's fail.
// What this means is that you can't change the signatures
// associated with an overall shared user, which doesn't seem all
// that unreasonable.
if (signatureCheckPs.sharedUser != null) {
- if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
+ if (compareSignatures(
+ signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
throw new PackageManagerException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
@@ -10804,9 +10802,12 @@ Slog.e("TODD",
if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
// Exempt SharedUsers signed with the platform key.
PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
- if ((platformPkgSetting.signatures.mSignatures != null) &&
- (compareSignatures(platformPkgSetting.signatures.mSignatures,
- pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) {
+ if ((platformPkgSetting.signatures.mSigningDetails
+ != PackageParser.SigningDetails.UNKNOWN)
+ && (compareSignatures(
+ platformPkgSetting.signatures.mSigningDetails.signatures,
+ pkg.mSigningDetails.signatures)
+ != PackageManager.SIGNATURE_MATCH)) {
throw new PackageManagerException("Apps that share a user with a " +
"privileged app must themselves be marked as privileged. " +
pkg.packageName + " shares privileged user " +
@@ -14248,9 +14249,10 @@ Slog.e("TODD",
Object obj = mSettings.getUserIdLPr(callingUid);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
- callerSignature = ((SharedUserSetting)obj).signatures.mSignatures;
+ callerSignature =
+ ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
} else if (obj instanceof PackageSetting) {
- callerSignature = ((PackageSetting)obj).signatures.mSignatures;
+ callerSignature = ((PackageSetting)obj).signatures.mSigningDetails.signatures;
} else {
throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
}
@@ -14262,7 +14264,7 @@ Slog.e("TODD",
// not signed with the same cert as the caller.
if (installerPackageSetting != null) {
if (compareSignatures(callerSignature,
- installerPackageSetting.signatures.mSignatures)
+ installerPackageSetting.signatures.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as new installer package "
@@ -14279,7 +14281,7 @@ Slog.e("TODD",
// okay to change it.
if (setting != null) {
if (compareSignatures(callerSignature,
- setting.signatures.mSignatures)
+ setting.signatures.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as old installer package "
@@ -16787,7 +16789,8 @@ Slog.e("TODD",
sourcePackageSetting, scanFlags))) {
sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
} else {
- sigsOk = compareSignatures(sourcePackageSetting.signatures.mSignatures,
+ sigsOk = compareSignatures(
+ sourcePackageSetting.signatures.mSigningDetails.signatures,
pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH;
}
if (!sigsOk) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 7b96ca67d38f..cfc12de02267 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -509,7 +509,7 @@ public class PackageManagerServiceUtils {
private static boolean matchSignaturesCompat(String packageName,
PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
ArraySet<Signature> existingSet = new ArraySet<Signature>();
- for (Signature sig : packageSignatures.mSignatures) {
+ for (Signature sig : packageSignatures.mSigningDetails.signatures) {
existingSet.add(sig);
}
ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
@@ -526,7 +526,7 @@ public class PackageManagerServiceUtils {
// 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);
+ packageSignatures.mSigningDetails = parsedSignatures;
return true;
}
return false;
@@ -561,8 +561,8 @@ public class PackageManagerServiceUtils {
try {
PackageParser.collectCertificates(disabledPkgSetting.pkg,
PackageParser.PARSE_IS_SYSTEM_DIR);
- if (compareSignatures(pkgSetting.signatures.mSignatures,
- disabledPkgSetting.signatures.mSignatures)
+ if (compareSignatures(pkgSetting.signatures.mSigningDetails.signatures,
+ disabledPkgSetting.signatures.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH) {
logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
pkgSetting.name);
@@ -593,9 +593,9 @@ public class PackageManagerServiceUtils {
throws PackageManagerException {
final String packageName = pkgSetting.name;
boolean compatMatch = false;
- if (pkgSetting.signatures.mSignatures != null) {
+ if (pkgSetting.signatures.mSigningDetails.signatures != null) {
// Already existing package. Make sure signatures match
- boolean match = compareSignatures(pkgSetting.signatures.mSignatures,
+ boolean match = compareSignatures(pkgSetting.signatures.mSigningDetails.signatures,
parsedSignatures.signatures)
== PackageManager.SIGNATURE_MATCH;
if (!match && compareCompat) {
@@ -605,7 +605,7 @@ public class PackageManagerServiceUtils {
}
if (!match && compareRecover) {
match = matchSignaturesRecover(
- packageName, pkgSetting.signatures.mSignatures,
+ packageName, pkgSetting.signatures.mSigningDetails.signatures,
parsedSignatures.signatures);
}
@@ -620,17 +620,21 @@ public class PackageManagerServiceUtils {
}
}
// Check for shared user signatures
- if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
+ if (pkgSetting.sharedUser != null
+ && pkgSetting.sharedUser.signatures.mSigningDetails.signatures != null) {
// Already existing package. Make sure signatures match
- boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
- parsedSignatures.signatures) == PackageManager.SIGNATURE_MATCH;
+ boolean match =
+ compareSignatures(
+ pkgSetting.sharedUser.signatures.mSigningDetails.signatures,
+ parsedSignatures.signatures) == PackageManager.SIGNATURE_MATCH;
if (!match && compareCompat) {
match = matchSignaturesCompat(
packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
}
if (!match && compareRecover) {
match = matchSignaturesRecover(packageName,
- pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures.signatures);
+ pkgSetting.sharedUser.signatures.mSigningDetails.signatures,
+ parsedSignatures.signatures);
compatMatch |= match;
}
if (!match) {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index e3c4c4358e10..18356c570d01 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -233,7 +233,7 @@ public abstract class PackageSettingBase extends SettingBase {
}
public Signature[] getSignatures() {
- return signatures.mSignatures;
+ return signatures.mSigningDetails.signatures;
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index d567d5c11cb2..d471fc837334 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -22,91 +22,148 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.annotation.NonNull;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
import android.util.Log;
import java.io.IOException;
+import java.security.cert.CertificateException;
import java.util.ArrayList;
class PackageSignatures {
- Signature[] mSignatures;
- @SignatureSchemeVersion int mSignatureSchemeVersion;
+
+ @NonNull PackageParser.SigningDetails mSigningDetails;
PackageSignatures(PackageSignatures orig) {
- if (orig != null && orig.mSignatures != null) {
- mSignatures = orig.mSignatures.clone();
- mSignatureSchemeVersion = orig.mSignatureSchemeVersion;
+ if (orig != null && orig.mSigningDetails != PackageParser.SigningDetails.UNKNOWN) {
+ mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+ } else {
+ mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
}
}
PackageSignatures(PackageParser.SigningDetails signingDetails) {
- assignSignatures(signingDetails);
+ mSigningDetails = signingDetails;
}
PackageSignatures() {
+ mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
}
void writeXml(XmlSerializer serializer, String tagName,
- ArrayList<Signature> pastSignatures) throws IOException {
- if (mSignatures == null) {
+ ArrayList<Signature> writtenSignatures) throws IOException {
+ if (mSigningDetails.signatures == null) {
return;
}
serializer.startTag(null, tagName);
- serializer.attribute(null, "count",
- Integer.toString(mSignatures.length));
- serializer.attribute(null, "schemeVersion", Integer.toString(mSignatureSchemeVersion));
- for (int i=0; i<mSignatures.length; i++) {
+ serializer.attribute(null, "count", Integer.toString(mSigningDetails.signatures.length));
+ serializer.attribute(null, "schemeVersion",
+ Integer.toString(mSigningDetails.signatureSchemeVersion));
+ writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, null);
+
+ // if we have past signer certificate information, write it out
+ if (mSigningDetails.pastSigningCertificates != null) {
+ serializer.startTag(null, "pastSigs");
+ serializer.attribute(null, "count",
+ Integer.toString(mSigningDetails.pastSigningCertificates.length));
+ writeCertsListXml(
+ serializer, writtenSignatures, mSigningDetails.pastSigningCertificates,
+ mSigningDetails.pastSigningCertificatesFlags);
+ serializer.endTag(null, "pastSigs");
+ }
+ serializer.endTag(null, tagName);
+ }
+
+ private void writeCertsListXml(XmlSerializer serializer, ArrayList<Signature> writtenSignatures,
+ Signature[] signatures, int[] flags) throws IOException {
+ for (int i=0; i<signatures.length; i++) {
serializer.startTag(null, "cert");
- final Signature sig = mSignatures[i];
+ final Signature sig = signatures[i];
final int sigHash = sig.hashCode();
- final int numPast = pastSignatures.size();
+ final int numWritten = writtenSignatures.size();
int j;
- for (j=0; j<numPast; j++) {
- Signature pastSig = pastSignatures.get(j);
- if (pastSig.hashCode() == sigHash && pastSig.equals(sig)) {
+ for (j=0; j<numWritten; j++) {
+ Signature writtenSig = writtenSignatures.get(j);
+ if (writtenSig.hashCode() == sigHash && writtenSig.equals(sig)) {
serializer.attribute(null, "index", Integer.toString(j));
break;
}
}
- if (j >= numPast) {
- pastSignatures.add(sig);
- serializer.attribute(null, "index", Integer.toString(numPast));
+ if (j >= numWritten) {
+ writtenSignatures.add(sig);
+ serializer.attribute(null, "index", Integer.toString(numWritten));
serializer.attribute(null, "key", sig.toCharsString());
}
+ if (flags != null) {
+ serializer.attribute(null, "flags", Integer.toString(flags[i]));
+ }
serializer.endTag(null, "cert");
}
- serializer.endTag(null, tagName);
}
- void readXml(XmlPullParser parser, ArrayList<Signature> pastSignatures)
+ void readXml(XmlPullParser parser, ArrayList<Signature> readSignatures)
throws IOException, XmlPullParserException {
+ PackageParser.SigningDetails.Builder builder =
+ new PackageParser.SigningDetails.Builder();
+
String countStr = parser.getAttributeValue(null, "count");
if (countStr == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <signatures> has"
+ "Error in package manager settings: <sigs> has"
+ " no count at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
}
+ final int count = Integer.parseInt(countStr);
+
String schemeVersionStr = parser.getAttributeValue(null, "schemeVersion");
+ int signatureSchemeVersion;
if (schemeVersionStr == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <signatures> has no schemeVersion at "
+ "Error in package manager settings: <sigs> has no schemeVersion at "
+ parser.getPositionDescription());
- mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
} else {
- mSignatureSchemeVersion = Integer.parseInt(countStr);
+ signatureSchemeVersion = Integer.parseInt(schemeVersionStr);
}
- final int count = Integer.parseInt(countStr);
- mSignatures = new Signature[count];
+ builder.setSignatureSchemeVersion(signatureSchemeVersion);
+ Signature[] signatures = new Signature[count];
+ int pos = readCertsListXml(parser, readSignatures, signatures, null, builder);
+ builder.setSignatures(signatures);
+ if (pos < count) {
+ // Should never happen -- there is an error in the written
+ // settings -- but if it does we don't want to generate
+ // a bad array.
+ Signature[] newSigs = new Signature[pos];
+ System.arraycopy(signatures, 0, newSigs, 0, pos);
+ builder = builder.setSignatures(newSigs);
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <sigs> count does not match number of "
+ + " <cert> entries" + parser.getPositionDescription());
+ }
+
+ try {
+ mSigningDetails = builder.build();
+ } catch (CertificateException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <sigs> "
+ + "unable to convert certificate(s) to public key(s).");
+ mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ }
+ }
+
+ private int readCertsListXml(XmlPullParser parser, ArrayList<Signature> readSignatures,
+ Signature[] signatures, int[] flags, PackageParser.SigningDetails.Builder builder)
+ throws IOException, XmlPullParserException {
+ int count = signatures.length;
int pos = 0;
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG
|| type == XmlPullParser.TEXT) {
continue;
@@ -121,83 +178,128 @@ class PackageSignatures {
int idx = Integer.parseInt(index);
String key = parser.getAttributeValue(null, "key");
if (key == null) {
- if (idx >= 0 && idx < pastSignatures.size()) {
- Signature sig = pastSignatures.get(idx);
+ if (idx >= 0 && idx < readSignatures.size()) {
+ Signature sig = readSignatures.get(idx);
if (sig != null) {
- mSignatures[pos] = pastSignatures.get(idx);
+ signatures[pos] = readSignatures.get(idx);
pos++;
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <cert> "
- + "index " + index + " is not defined at "
- + parser.getPositionDescription());
+ + "index " + index + " is not defined at "
+ + parser.getPositionDescription());
}
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <cert> "
- + "index " + index + " is out of bounds at "
- + parser.getPositionDescription());
+ + "index " + index + " is out of bounds at "
+ + parser.getPositionDescription());
}
} else {
- while (pastSignatures.size() <= idx) {
- pastSignatures.add(null);
+ while (readSignatures.size() <= idx) {
+ readSignatures.add(null);
}
Signature sig = new Signature(key);
- pastSignatures.set(idx, sig);
- mSignatures[pos] = sig;
+ readSignatures.set(idx, sig);
+ signatures[pos] = sig;
pos++;
}
} catch (NumberFormatException e) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <cert> "
- + "index " + index + " is not a number at "
- + parser.getPositionDescription());
+ + "index " + index + " is not a number at "
+ + parser.getPositionDescription());
} catch (IllegalArgumentException e) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <cert> "
- + "index " + index + " has an invalid signature at "
- + parser.getPositionDescription() + ": "
- + e.getMessage());
+ + "index " + index + " has an invalid signature at "
+ + parser.getPositionDescription() + ": "
+ + e.getMessage());
+ }
+
+ if (flags != null) {
+ String flagsStr = parser.getAttributeValue(null, "flags");
+ if (flagsStr != null) {
+ try {
+ flags[pos] = Integer.parseInt(flagsStr);
+ } catch (NumberFormatException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <cert> "
+ + "flags " + flagsStr + " is not a number at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <cert> has no"
+ + " flags at " + parser.getPositionDescription());
+ }
}
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <cert> has"
- + " no index at " + parser.getPositionDescription());
+ + " no index at " + parser.getPositionDescription());
}
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: too "
- + "many <cert> tags, expected " + count
- + " at " + parser.getPositionDescription());
+ + "many <cert> tags, expected " + count
+ + " at " + parser.getPositionDescription());
+ }
+ } else if (tagName.equals("pastSigs")) {
+ if (flags == null) {
+ // we haven't encountered pastSigs yet, go ahead
+ String countStr = parser.getAttributeValue(null, "count");
+ if (countStr == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <pastSigs> has"
+ + " no count at " + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ try {
+ final int pastSigsCount = Integer.parseInt(countStr);
+ Signature[] pastSignatures = new Signature[pastSigsCount];
+ int[] pastSignaturesFlags = new int[pastSigsCount];
+ int pastSigsPos = readCertsListXml(parser, readSignatures, pastSignatures,
+ pastSignaturesFlags, builder);
+ builder = builder
+ .setPastSigningCertificates(pastSignatures)
+ .setPastSigningCertificatesFlags(pastSignaturesFlags);
+
+ if (pastSigsPos < pastSigsCount) {
+ // Should never happen -- there is an error in the written
+ // settings -- but if it does we don't want to generate
+ // a bad array.
+ Signature[] newSigs = new Signature[pastSigsPos];
+ System.arraycopy(pastSignatures, 0, newSigs, 0, pastSigsPos);
+ int[] newFlags = new int[pastSigsPos];
+ System.arraycopy(pastSignaturesFlags, 0, newFlags, 0, pastSigsPos);
+ builder = builder
+ .setPastSigningCertificates(newSigs)
+ .setPastSigningCertificatesFlags(newFlags);
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <pastSigs> count does not "
+ + "match number of <cert> entries "
+ + parser.getPositionDescription());
+ }
+ } catch (NumberFormatException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <pastSigs> "
+ + "count " + countStr + " is not a number at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "<pastSigs> encountered multiple times under the same <sigs> at "
+ + parser.getPositionDescription());
}
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
- "Unknown element under <cert>: "
- + parser.getName());
+ "Unknown element under <sigs>: "
+ + parser.getName());
}
XmlUtils.skipCurrentTag(parser);
}
-
- if (pos < count) {
- // Should never happen -- there is an error in the written
- // settings -- but if it does we don't want to generate
- // a bad array.
- Signature[] newSigs = new Signature[pos];
- System.arraycopy(mSignatures, 0, newSigs, 0, pos);
- mSignatures = newSigs;
- }
- }
-
- void assignSignatures(PackageParser.SigningDetails signingDetails) {
- mSignatureSchemeVersion = signingDetails.signatureSchemeVersion;
- if (!signingDetails.hasSignatures()) {
- mSignatures = null;
- return;
- }
- mSignatures = new Signature[signingDetails.signatures.length];
- for (int i=0; i<signingDetails.signatures.length; i++) {
- mSignatures[i] = signingDetails.signatures[i];
- }
+ return pos;
}
@Override
@@ -206,16 +308,26 @@ class PackageSignatures {
buf.append("PackageSignatures{");
buf.append(Integer.toHexString(System.identityHashCode(this)));
buf.append(" version:");
- buf.append(mSignatureSchemeVersion);
+ buf.append(mSigningDetails.signatureSchemeVersion);
buf.append(", signatures:[");
- if (mSignatures != null) {
- for (int i=0; i<mSignatures.length; i++) {
+ if (mSigningDetails.signatures != null) {
+ for (int i = 0; i < mSigningDetails.signatures.length; i++) {
if (i > 0) buf.append(", ");
buf.append(Integer.toHexString(
- mSignatures[i].hashCode()));
+ mSigningDetails.signatures[i].hashCode()));
}
}
buf.append("]}");
+ buf.append(", past signatures:[");
+ if (mSigningDetails.pastSigningCertificates != null) {
+ for (int i = 0; i < mSigningDetails.pastSigningCertificates.length; i++) {
+ if (i > 0) buf.append(", ");
+ buf.append(Integer.toHexString(
+ mSigningDetails.pastSigningCertificates[i].hashCode()));
+ buf.append(" flags: ");
+ buf.append(Integer.toHexString(mSigningDetails.pastSigningCertificatesFlags[i]));
+ }
+ }
return buf.toString();
}
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ecbc45217dbb..8ce412e5783a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -920,13 +920,13 @@ public final class Settings {
// by that time.
void insertPackageSettingLPw(PackageSetting p, PackageParser.Package pkg) {
// Update signatures if needed.
- if (p.signatures.mSignatures == null) {
- p.signatures.assignSignatures(pkg.mSigningDetails);
+ if (p.signatures.mSigningDetails.signatures == null) {
+ p.signatures.mSigningDetails = pkg.mSigningDetails;
}
// If this app defines a shared user id initialize
// the shared user signatures as well.
- if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
- p.sharedUser.signatures.assignSignatures(pkg.mSigningDetails);
+ if (p.sharedUser != null && p.sharedUser.signatures.mSigningDetails.signatures == null) {
+ p.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
}
addPackageSettingLPw(p, p.sharedUser);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 49601c32cdc2..5c7348c68240 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -497,7 +497,9 @@ public class PackageParserTest {
new PackageParser.SigningDetails(
new Signature[] { new Signature(new byte[16]) },
2,
- new ArraySet<>());
+ new ArraySet<>(),
+ null,
+ null);
pkg.mExtras = new Bundle();
pkg.mRestrictedAccountType = "foo19";
pkg.mRequiredAccountType = "foo20";