Backend attestation support: API.

Bug: 160605420
Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest PackageManagerServiceTest ChecksumsTest
Change-Id: I5734e3dd0cc272cb95244182fee337f1c7564c79
This commit is contained in:
Alex Buynytskyy
2021-01-14 11:03:18 -08:00
parent f98870a5c6
commit f1cbd3e110
4 changed files with 112 additions and 31 deletions

View File

@@ -12050,7 +12050,6 @@ package android.content.pm {
public static class PackageInstaller.Session implements java.io.Closeable {
method public void abandon();
method @Deprecated public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
method public void addChildSessionId(int);
method public void close();
method public void commit(@NonNull android.content.IntentSender);
@@ -12064,6 +12063,7 @@ package android.content.pm {
method @NonNull public java.io.OutputStream openWrite(@NonNull String, long, long) throws java.io.IOException;
method public void removeChildSessionId(int);
method public void removeSplit(@NonNull String) throws java.io.IOException;
method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException;
method public void setStagingProgress(float);
method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
}

View File

@@ -35,7 +35,7 @@ interface IPackageInstallerSession {
void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd);
void stageViaHardLink(String target);
void addChecksums(String name, in Checksum[] checksums);
void setChecksums(String name, in Checksum[] checksums, in byte[] signature);
void removeSplit(String splitName);

View File

@@ -1247,12 +1247,15 @@ public class PackageInstaller {
}
/**
* Adds installer-provided checksums for the APK file in session.
* Sets installer-provided checksums for the APK file in session.
*
* @param name previously written as part of this session.
* {@link #openWrite}
* @param checksums installer intends to make available via
* {@link PackageManager#requestChecksums}.
* @param signature PKCS#7 detached signature bytes over serialized checksums to enable
* fs-verity for the checksums or null if fs-verity should not be enabled.
* @see <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#built-in-signature-verification">fs-verity</a>
* @throws SecurityException if called after the session has been
* committed or abandoned.
* @throws IllegalStateException if checksums for this file have already been added.
@@ -1262,13 +1265,14 @@ public class PackageInstaller {
* in {@link PackageManager#requestChecksums}.
*/
@Deprecated
public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums)
throws IOException {
public void setChecksums(@NonNull String name, @NonNull List<Checksum> checksums,
@Nullable byte[] signature) throws IOException {
Objects.requireNonNull(name);
Objects.requireNonNull(checksums);
try {
mSession.addChecksums(name, checksums.toArray(new Checksum[checksums.size()]));
mSession.setChecksums(name, checksums.toArray(new Checksum[checksums.size()]),
signature);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;

View File

@@ -186,6 +186,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
static final String TAG_CHILD_SESSION = "childSession";
static final String TAG_SESSION_FILE = "sessionFile";
static final String TAG_SESSION_CHECKSUM = "sessionChecksum";
static final String TAG_SESSION_CHECKSUM_SIGNATURE = "sessionChecksumSignature";
private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
"whitelisted-restricted-permission";
@@ -397,8 +398,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private ArraySet<FileEntry> mFiles = new ArraySet<>();
static class PerFileChecksum {
private final Checksum[] mChecksums;
private final byte[] mSignature;
PerFileChecksum(Checksum[] checksums, byte[] signature) {
mChecksums = checksums;
mSignature = signature;
}
Checksum[] getChecksums() {
return this.mChecksums;
}
byte[] getSignature() {
return this.mSignature;
}
}
@GuardedBy("mLock")
private ArrayMap<String, Checksum[]> mChecksums = new ArrayMap<>();
private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>();
@Nullable
final StagedSession mStagedSession;
@@ -921,7 +940,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
SessionParams params, long createdMillis, long committedMillis,
File stageDir, String stageCid, InstallationFile[] files,
ArrayMap<String, List<Checksum>> checksums,
ArrayMap<String, PerFileChecksum> checksums,
boolean prepared, boolean committed, boolean destroyed, boolean sealed,
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
@@ -967,11 +986,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
if (checksums != null) {
for (int i = 0, isize = checksums.size(); i < isize; ++i) {
final String fileName = checksums.keyAt(i);
final List<Checksum> fileChecksums = checksums.valueAt(i);
mChecksums.put(fileName, fileChecksums.toArray(new Checksum[fileChecksums.size()]));
}
mChecksums.putAll(checksums);
}
if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) {
@@ -1253,7 +1268,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
@Override
public void addChecksums(String name, @NonNull Checksum[] checksums) {
public void setChecksums(String name, @NonNull Checksum[] checksums,
@Nullable byte[] signature) {
if (checksums.length == 0) {
return;
}
@@ -1269,6 +1285,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new IllegalStateException("Can't obtain calling installer's package.");
}
if (signature != null && signature.length != 0) {
final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
if (!standardMode || legacyMode) {
Slog.e(TAG,
"Can't enforce checksum's signature: Apk-Verity is disabled or in legacy "
+ "mode.");
signature = null;
}
}
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums");
@@ -1277,7 +1304,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new IllegalStateException("Duplicate checksums.");
}
mChecksums.put(name, checksums);
mChecksums.put(name, new PerFileChecksum(checksums, signature));
}
}
@@ -3032,15 +3059,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile);
}
private void storeBytesToInstallationFile(final String localPath, final String absolutePath,
final byte[] bytes) throws IOException {
if (!isIncrementalInstallation() || mIncrementalFileStorages == null) {
FileUtils.bytesToFile(absolutePath, bytes);
} else {
mIncrementalFileStorages.makeFile(localPath, bytes);
}
}
@GuardedBy("mLock")
private void maybeStageDigestsLocked(File origFile, File targetFile, String splitName)
throws PackageManagerException {
final Checksum[] checksums = mChecksums.get(origFile.getName());
if (checksums == null) {
final PerFileChecksum perFileChecksum = mChecksums.get(origFile.getName());
if (perFileChecksum == null) {
return;
}
mChecksums.remove(origFile.getName());
final Checksum[] checksums = perFileChecksum.getChecksums();
if (checksums.length == 0) {
return;
}
@@ -3048,14 +3085,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName());
final File targetDigestsFile = new File(stageDir, targetDigestsPath);
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
// Storing and staging checksums.
ApkChecksums.writeChecksums(os, checksums);
final byte[] checksumsBytes = os.toByteArray();
storeBytesToInstallationFile(targetDigestsPath, targetDigestsFile.getAbsolutePath(),
os.toByteArray());
stageFileLocked(targetDigestsFile, targetDigestsFile);
if (!isIncrementalInstallation() || mIncrementalFileStorages == null) {
FileUtils.bytesToFile(targetDigestsFile.getAbsolutePath(), checksumsBytes);
} else {
mIncrementalFileStorages.makeFile(targetDigestsPath, checksumsBytes);
final byte[] signature = perFileChecksum.getSignature();
if (signature == null || signature.length == 0) {
return;
}
// Storing and staging signature.
final String targetDigestsSignaturePath = VerityUtils.getFsveritySignatureFilePath(
targetDigestsPath);
final File targetDigestsSignatureFile = new File(stageDir, targetDigestsSignaturePath);
storeBytesToInstallationFile(targetDigestsSignaturePath,
targetDigestsSignatureFile.getAbsolutePath(), signature);
stageFileLocked(targetDigestsSignatureFile, targetDigestsSignatureFile);
} catch (CertificateException e) {
throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
"Failed to encode certificate for " + mPackageName, e);
@@ -3063,8 +3110,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Failed to store digests for " + mPackageName, e);
}
stageFileLocked(targetDigestsFile, targetDigestsFile);
}
@GuardedBy("mLock")
@@ -4277,7 +4322,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
for (int i = 0, isize = mChecksums.size(); i < isize; ++i) {
final String fileName = mChecksums.keyAt(i);
final Checksum[] checksums = mChecksums.valueAt(i);
final PerFileChecksum perFileChecksum = mChecksums.valueAt(i);
final Checksum[] checksums = perFileChecksum.getChecksums();
for (Checksum checksum : checksums) {
out.startTag(null, TAG_SESSION_CHECKSUM);
writeStringAttribute(out, ATTR_NAME, fileName);
@@ -4286,6 +4332,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
out.endTag(null, TAG_SESSION_CHECKSUM);
}
}
for (int i = 0, isize = mChecksums.size(); i < isize; ++i) {
final String fileName = mChecksums.keyAt(i);
final PerFileChecksum perFileChecksum = mChecksums.valueAt(i);
final byte[] signature = perFileChecksum.getSignature();
if (signature == null || signature.length == 0) {
continue;
}
out.startTag(null, TAG_SESSION_CHECKSUM_SIGNATURE);
writeStringAttribute(out, ATTR_NAME, fileName);
writeByteArrayAttribute(out, ATTR_SIGNATURE, signature);
out.endTag(null, TAG_SESSION_CHECKSUM_SIGNATURE);
}
}
out.endTag(null, TAG_SESSION);
@@ -4401,6 +4460,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
List<Integer> childSessionIds = new ArrayList<>();
List<InstallationFile> files = new ArrayList<>();
ArrayMap<String, List<Checksum>> checksums = new ArrayMap<>();
ArrayMap<String, byte[]> signatures = new ArrayMap<>();
int outerDepth = in.getDepth();
int type;
while ((type = in.next()) != XmlPullParser.END_DOCUMENT
@@ -4443,6 +4503,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
fileChecksums.add(checksum);
}
if (TAG_SESSION_CHECKSUM_SIGNATURE.equals(in.getName())) {
final String fileName = readStringAttribute(in, ATTR_NAME);
final byte[] signature = readByteArrayAttribute(in, ATTR_SIGNATURE);
signatures.put(fileName, signature);
}
}
if (grantedRuntimePermissions.size() > 0) {
@@ -4471,13 +4536,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY);
}
ArrayMap<String, PerFileChecksum> checksumsMap = null;
if (!checksums.isEmpty()) {
checksumsMap = new ArrayMap<>(checksums.size());
for (int i = 0, isize = checksums.size(); i < isize; ++i) {
final String fileName = checksums.keyAt(i);
final List<Checksum> perFileChecksum = checksums.valueAt(i);
final byte[] perFileSignature = signatures.get(fileName);
checksumsMap.put(fileName, new PerFileChecksum(
perFileChecksum.toArray(new Checksum[perFileChecksum.size()]),
perFileSignature));
}
}
InstallSource installSource = InstallSource.create(installInitiatingPackageName,
installOriginatingPackageName, installerPackageName, installerAttributionTag);
return new PackageInstallerSession(callback, context, pm, sessionProvider,
installerThread, stagingManager, sessionId, userId, installerUid,
installSource, params, createdMillis, committedMillis, stageDir, stageCid,
fileArray, checksums, prepared, committed, destroyed, sealed, childSessionIdsArray,
parentSessionId, isReady, isFailed, isApplied, stagedSessionErrorCode,
stagedSessionErrorMessage);
return new PackageInstallerSession(callback, context, pm, sessionProvider, installerThread,
stagingManager, sessionId, userId, installerUid, installSource, params,
createdMillis, committedMillis, stageDir, stageCid, fileArray, checksumsMap,
prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId,
isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
}
}