Merge "Add proof-of-rotation information to PackageParser.SigningDetails"

This commit is contained in:
TreeHugger Robot
2018-01-23 23:19:59 +00:00
committed by Android (Google) Code Review
8 changed files with 431 additions and 147 deletions

View File

@@ -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);
}
}
}
/**

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -233,7 +233,7 @@ public abstract class PackageSettingBase extends SettingBase {
}
public Signature[] getSignatures() {
return signatures.mSignatures;
return signatures.mSigningDetails.signatures;
}
/**

View File

@@ -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.startTag(null, "cert");
final Signature sig = mSignatures[i];
final int sigHash = sig.hashCode();
final int numPast = pastSignatures.size();
int j;
for (j=0; j<numPast; j++) {
Signature pastSig = pastSignatures.get(j);
if (pastSig.hashCode() == sigHash && pastSig.equals(sig)) {
serializer.attribute(null, "index", Integer.toString(j));
break;
}
}
if (j >= numPast) {
pastSignatures.add(sig);
serializer.attribute(null, "index", Integer.toString(numPast));
serializer.attribute(null, "key", sig.toCharsString());
}
serializer.endTag(null, "cert");
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);
}
void readXml(XmlPullParser parser, ArrayList<Signature> pastSignatures)
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 = signatures[i];
final int sigHash = sig.hashCode();
final int numWritten = writtenSignatures.size();
int j;
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 >= 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");
}
}
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();
}
}

View File

@@ -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);
}

View File

@@ -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";