diff options
| author | 2018-01-23 23:19:59 +0000 | |
|---|---|---|
| committer | 2018-01-23 23:19:59 +0000 | |
| commit | 4ec3efce54b14157ce9084faed51be07cbbbe274 (patch) | |
| tree | e3fac3fe3d7f69492b5374dff66657b3fda2221d | |
| parent | 555c6fcd96cfd00bfa7f8d26de0faca41d103675 (diff) | |
| parent | 77029c5b16351775cb2333369ef9a4bc1d9acf58 (diff) | |
Merge "Add proof-of-rotation information to PackageParser.SigningDetails"
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"; |