diff --git a/core/java/android/util/apk/SourceStampVerifier.java b/core/java/android/util/apk/SourceStampVerifier.java index c78baf5b7c530..a7ae32d1baa2d 100644 --- a/core/java/android/util/apk/SourceStampVerifier.java +++ b/core/java/android/util/apk/SourceStampVerifier.java @@ -24,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorith import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import android.util.Pair; +import android.util.Slog; import android.util.jar.StrictJarFile; import libcore.io.IoUtils; @@ -69,6 +70,8 @@ import java.util.zip.ZipEntry; */ public abstract class SourceStampVerifier { + private static final String TAG = "SourceStampVerifier"; + private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a; private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; private static final int SOURCE_STAMP_BLOCK_ID = 0x2b09189e; @@ -99,28 +102,31 @@ public abstract class SourceStampVerifier { /** Verifies SourceStamp present in the provided APK. */ public static SourceStampVerificationResult verify(String apkFile) { + StrictJarFile apkJar = null; try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { - return verify(apk); - } catch (IOException e) { - // Any exception in reading the APK returns a non-present SourceStamp outcome - // without affecting the outcome of any of the other signature schemes. - return SourceStampVerificationResult.notPresent(); - } - } - - private static SourceStampVerificationResult verify(RandomAccessFile apk) { - byte[] sourceStampCertificateDigest; - try { - sourceStampCertificateDigest = getSourceStampCertificateDigest(apk); + apkJar = + new StrictJarFile( + apkFile, + /* verify= */ false, + /* signatureSchemeRollbackProtectionsEnforced= */ false); + byte[] sourceStampCertificateDigest = getSourceStampCertificateDigest(apkJar); if (sourceStampCertificateDigest == null) { // SourceStamp certificate hash file not found, which means that there is not // SourceStamp present. return SourceStampVerificationResult.notPresent(); } + return verify(apk, sourceStampCertificateDigest); } catch (IOException e) { + // Any exception in reading the APK returns a non-present SourceStamp outcome + // without affecting the outcome of any of the other signature schemes. return SourceStampVerificationResult.notPresent(); + } finally { + closeApkJar(apkJar); } + } + private static SourceStampVerificationResult verify( + RandomAccessFile apk, byte[] sourceStampCertificateDigest) { try { SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID); @@ -283,22 +289,17 @@ public abstract class SourceStampVerifier { return apkContentDigests; } - private static byte[] getSourceStampCertificateDigest(RandomAccessFile apk) throws IOException { - StrictJarFile apkJar = - new StrictJarFile( - apk.getFD(), - /* verify= */ false, - /* signatureSchemeRollbackProtectionsEnforced= */ false); - ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME); - if (zipEntry == null) { - // SourceStamp certificate hash file not found, which means that there is not - // SourceStamp present. - return null; - } + private static byte[] getSourceStampCertificateDigest(StrictJarFile apkJar) throws IOException { InputStream inputStream = null; - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try { + ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME); + if (zipEntry == null) { + // SourceStamp certificate hash file not found, which means that there is not + // SourceStamp present. + return null; + } inputStream = apkJar.getInputStream(zipEntry); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Trying to read the certificate digest, which should be less than 1024 bytes. byte[] buffer = new byte[1024]; @@ -327,4 +328,15 @@ public abstract class SourceStampVerifier { } return result.array(); } + + private static void closeApkJar(StrictJarFile apkJar) { + try { + if (apkJar == null) { + return; + } + apkJar.close(); + } catch (IOException e) { + Slog.e(TAG, "Could not close APK jar", e); + } + } } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index c2d1364c9a01d..aa12bbd6c662a 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -73,6 +73,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; @@ -88,6 +89,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; /** Implementation of {@link AppIntegrityManagerService}. */ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { @@ -473,9 +475,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { SourceStampVerificationResult sourceStampVerificationResult; if (installationPath.isDirectory()) { - try { + try (Stream filesList = Files.list(installationPath.toPath())) { List apkFiles = - Files.list(installationPath.toPath()) + filesList .map(path -> path.toAbsolutePath().toString()) .collect(Collectors.toList()); sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles);