From 2be59906d885bfcf9a9b92645b550d08ec76cd2b Mon Sep 17 00:00:00 2001 From: Mateus Azis Date: Sat, 25 May 2024 16:31:11 +0200 Subject: [PATCH 1/9] Add SecurityPolicy factory method for apps signed with the platform certificate. Addresses: #11238 --- .../java/io/grpc/binder/SecurityPolicies.java | 21 +++++++ .../io/grpc/binder/SecurityPoliciesTest.java | 60 +++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index fc65fb11428..222a177e8af 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -97,6 +97,27 @@ public static SecurityPolicy hasSignature( packageManager, packageName, ImmutableList.of(requiredSignature)); } + /** + * Creates a {@link SecurityPolicy} which checks if the given package is signed with the same + * certificate that the current Android platform was signed with. + * + * @param packageName the package name of the allowed package. + * @throws NullPointerException if any of the inputs are {@code null}. + */ + @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11238") + public static SecurityPolicy hasSameSignatureAsPlatform( + PackageManager packageManager, String packageName) { + PackageInfo platformPackageInfo; + try { + platformPackageInfo = + packageManager.getPackageInfo("android", PackageManager.GET_SIGNING_CERTIFICATES); + } catch (PackageManager.NameNotFoundException e) { + throw new AssertionError(e); // impossible; the platform package is always available. + } + Signature platformSignature = platformPackageInfo.signatures[0]; + return hasSignature(packageManager, packageName, platformSignature); + } + /** * Creates {@link SecurityPolicy} which checks if the SHA-256 hash of the package signature * matches {@code requiredSignatureSha256Hash}. diff --git a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java index 02ff5e059e1..382e0cc4ed8 100644 --- a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java +++ b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java @@ -84,6 +84,12 @@ private void installPackages(int uid, PackageInfo... packageInfo) { shadowOf(packageManager).setPackagesForUid(uid, packageNames); } + private void setupPlatformSignature(Signature signature) { + PackageInfo platformPackageInfo = + newBuilder().setPackageName("android").setSignatures(signature).build(); + installPackages(Process.SYSTEM_UID, platformPackageInfo); + } + @Test public void testInternalOnly() throws Exception { policy = SecurityPolicies.internalOnly(); @@ -151,6 +157,60 @@ public void testHasSignature_failsIfSignatureDoesNotMatch() throws Exception { .isEqualTo(Status.PERMISSION_DENIED.getCode()); } + @Test + public void testHasSameSignatureAsPlatform_succeedsIfSignaturesMatch() { + setupPlatformSignature(SIG1); + PackageInfo info = + newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG1).build(); + installPackages(OTHER_UID, info); + + policy = SecurityPolicies.hasSameSignatureAsPlatform(packageManager, OTHER_UID_PACKAGE_NAME); + + assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); + } + + @Test + public void testHasSameSignatureAsPlatform_failsIfPackageNameDoesNotMatch() { + setupPlatformSignature(SIG1); + PackageInfo info = + newBuilder() + .setPackageName(OTHER_UID_SAME_SIGNATURE_PACKAGE_NAME) + .setSignatures(SIG1) + .build(); + installPackages(OTHER_UID_SAME_SIGNATURE, info); + + policy = + SecurityPolicies.hasSameSignatureAsPlatform(packageManager, appContext.getPackageName()); + + assertThat(policy.checkAuthorization(OTHER_UID_SAME_SIGNATURE).getCode()) + .isEqualTo(Status.PERMISSION_DENIED.getCode()); + } + + @Test + public void testHasSameSignatureAsPlatform_failsIfSignatureDoesNotMatch() { + setupPlatformSignature(SIG1); + PackageInfo info = + newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build(); + installPackages(OTHER_UID, info); + + policy = SecurityPolicies.hasSameSignatureAsPlatform(packageManager, OTHER_UID_PACKAGE_NAME); + + assertThat(policy.checkAuthorization(OTHER_UID).getCode()) + .isEqualTo(Status.PERMISSION_DENIED.getCode()); + } + + @Test + public void testHasSameSignatureAsPlatform_failsIfUidUnknown() { + setupPlatformSignature(SIG1); + policy = + SecurityPolicies.hasSameSignatureAsPlatform( + packageManager, + appContext.getPackageName()); + + assertThat(policy.checkAuthorization(OTHER_UID_UNKNOWN).getCode()) + .isEqualTo(Status.UNAUTHENTICATED.getCode()); + } + @Test public void testOneOfSignatures_succeedsIfPackageNameAndSignaturesMatch() throws Exception { From c052f7991a6206b952bab25c86b8dd8c0b9c7625 Mon Sep 17 00:00:00 2001 From: Mateus Azis Date: Thu, 30 May 2024 14:55:59 -0700 Subject: [PATCH 2/9] I/O executor offloading; checking all signatures. --- .../java/io/grpc/binder/SecurityPolicies.java | 55 +++++++++++++++---- .../io/grpc/binder/SecurityPoliciesTest.java | 28 +++++++--- 2 files changed, 66 insertions(+), 17 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index 222a177e8af..64af16f6fea 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -16,6 +16,8 @@ package io.grpc.binder; +import static com.google.common.base.Preconditions.checkState; + import android.annotation.SuppressLint; import android.app.admin.DevicePolicyManager; import android.content.Context; @@ -26,12 +28,16 @@ import android.os.Build; import android.os.Build.VERSION; import android.os.Process; +import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.hash.Hashing; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import com.google.errorprone.annotations.CheckReturnValue; import io.grpc.ExperimentalApi; import io.grpc.Status; @@ -40,6 +46,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.concurrent.Executor; /** Static factory methods for creating standard security policies. */ @CheckReturnValue @@ -102,20 +109,48 @@ public static SecurityPolicy hasSignature( * certificate that the current Android platform was signed with. * * @param packageName the package name of the allowed package. + * @param backgroundExecutor an executor suitable for blocking disk I/O. * @throws NullPointerException if any of the inputs are {@code null}. */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11238") - public static SecurityPolicy hasSameSignatureAsPlatform( - PackageManager packageManager, String packageName) { - PackageInfo platformPackageInfo; - try { - platformPackageInfo = - packageManager.getPackageInfo("android", PackageManager.GET_SIGNING_CERTIFICATES); - } catch (PackageManager.NameNotFoundException e) { - throw new AssertionError(e); // impossible; the platform package is always available. + public static AsyncSecurityPolicy hasSameSignatureAsPlatform( + PackageManager packageManager, String packageName, + ListeningExecutorService backgroundExecutor) { + return new AsyncSecurityPolicy() { + @Override + public ListenableFuture checkAuthorizationAsync(int uid) { + return backgroundExecutor.submit(() -> { + PackageInfo platformPackageInfo; + try { + platformPackageInfo = + packageManager.getPackageInfo("android", packageManager.GET_SIGNING_CERTIFICATES); + } catch (PackageManager.NameNotFoundException e) { + throw new AssertionError(e); // impossible; the platform package is always available. + } + ImmutableList policies = + createSignaturePolicies(packageManager, packageName, platformPackageInfo); + return anyOf(policies.toArray(new SecurityPolicy[0])).checkAuthorization(uid); + }); + } + }; + } + + private static ImmutableList createSignaturePolicies( + PackageManager packageManager, String packageName, PackageInfo packageInfo) { + Signature[] signatures = packageInfo.signatures; + checkState( + signatures.length > 0, "There should be at least one platform signature"); + ImmutableList.Builder policies = + ImmutableList.builderWithExpectedSize(signatures.length); + for (Signature signature : signatures) { + policies.add(new SecurityPolicy() { + @Override + public Status checkAuthorization(int uid) { + return hasSignature(packageManager, packageName, signature).checkAuthorization(uid); + } + }); } - Signature platformSignature = platformPackageInfo.signatures[0]; - return hasSignature(packageManager, packageName, platformSignature); + return policies.build(); } /** diff --git a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java index 382e0cc4ed8..0b23f902d53 100644 --- a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java +++ b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java @@ -35,6 +35,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.hash.Hashing; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import io.grpc.Status; import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -84,9 +86,9 @@ private void installPackages(int uid, PackageInfo... packageInfo) { shadowOf(packageManager).setPackagesForUid(uid, packageNames); } - private void setupPlatformSignature(Signature signature) { + private void setupPlatformSignature(Signature...signatures) { PackageInfo platformPackageInfo = - newBuilder().setPackageName("android").setSignatures(signature).build(); + newBuilder().setPackageName("android").setSignatures(signatures).build(); installPackages(Process.SYSTEM_UID, platformPackageInfo); } @@ -159,12 +161,16 @@ public void testHasSignature_failsIfSignatureDoesNotMatch() throws Exception { @Test public void testHasSameSignatureAsPlatform_succeedsIfSignaturesMatch() { - setupPlatformSignature(SIG1); + setupPlatformSignature(SIG2, SIG1); PackageInfo info = newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG1).build(); installPackages(OTHER_UID, info); - policy = SecurityPolicies.hasSameSignatureAsPlatform(packageManager, OTHER_UID_PACKAGE_NAME); + policy = + SecurityPolicies.hasSameSignatureAsPlatform( + packageManager, + OTHER_UID_PACKAGE_NAME, + MoreExecutors.newDirectExecutorService()); assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); } @@ -180,7 +186,10 @@ public void testHasSameSignatureAsPlatform_failsIfPackageNameDoesNotMatch() { installPackages(OTHER_UID_SAME_SIGNATURE, info); policy = - SecurityPolicies.hasSameSignatureAsPlatform(packageManager, appContext.getPackageName()); + SecurityPolicies.hasSameSignatureAsPlatform( + packageManager, + appContext.getPackageName(), + MoreExecutors.newDirectExecutorService()); assertThat(policy.checkAuthorization(OTHER_UID_SAME_SIGNATURE).getCode()) .isEqualTo(Status.PERMISSION_DENIED.getCode()); @@ -193,7 +202,11 @@ public void testHasSameSignatureAsPlatform_failsIfSignatureDoesNotMatch() { newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build(); installPackages(OTHER_UID, info); - policy = SecurityPolicies.hasSameSignatureAsPlatform(packageManager, OTHER_UID_PACKAGE_NAME); + policy = + SecurityPolicies.hasSameSignatureAsPlatform( + packageManager, + OTHER_UID_PACKAGE_NAME, + MoreExecutors.newDirectExecutorService()); assertThat(policy.checkAuthorization(OTHER_UID).getCode()) .isEqualTo(Status.PERMISSION_DENIED.getCode()); @@ -205,7 +218,8 @@ public void testHasSameSignatureAsPlatform_failsIfUidUnknown() { policy = SecurityPolicies.hasSameSignatureAsPlatform( packageManager, - appContext.getPackageName()); + appContext.getPackageName(), + MoreExecutors.newDirectExecutorService()); assertThat(policy.checkAuthorization(OTHER_UID_UNKNOWN).getCode()) .isEqualTo(Status.UNAUTHENTICATED.getCode()); From 4055cf227a25d96040b8c076157cf62bd91344ad Mon Sep 17 00:00:00 2001 From: Mateus Azis Date: Thu, 30 May 2024 14:59:47 -0700 Subject: [PATCH 3/9] removed unused imports --- binder/src/main/java/io/grpc/binder/SecurityPolicies.java | 3 --- binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java | 1 - 2 files changed, 4 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index 64af16f6fea..c61d218048f 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -28,7 +28,6 @@ import android.os.Build; import android.os.Build.VERSION; import android.os.Process; -import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; @@ -37,7 +36,6 @@ import com.google.common.hash.Hashing; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; import com.google.errorprone.annotations.CheckReturnValue; import io.grpc.ExperimentalApi; import io.grpc.Status; @@ -46,7 +44,6 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; -import java.util.concurrent.Executor; /** Static factory methods for creating standard security policies. */ @CheckReturnValue diff --git a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java index 0b23f902d53..6e78f0251ef 100644 --- a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java +++ b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java @@ -35,7 +35,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.hash.Hashing; -import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import io.grpc.Status; import java.util.HashMap; From fe9d9d765ac20cccb3ce790c31632ef2b3d15b49 Mon Sep 17 00:00:00 2001 From: Mateus Azis Date: Fri, 31 May 2024 13:46:38 -0700 Subject: [PATCH 4/9] switch from ListeningExecutorService to Executor+Futures.submit --- .../src/main/java/io/grpc/binder/SecurityPolicies.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index c61d218048f..5c5c1121b76 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -34,8 +34,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.hash.Hashing; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; import com.google.errorprone.annotations.CheckReturnValue; import io.grpc.ExperimentalApi; import io.grpc.Status; @@ -44,6 +44,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.concurrent.Executor; /** Static factory methods for creating standard security policies. */ @CheckReturnValue @@ -112,11 +113,11 @@ public static SecurityPolicy hasSignature( @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11238") public static AsyncSecurityPolicy hasSameSignatureAsPlatform( PackageManager packageManager, String packageName, - ListeningExecutorService backgroundExecutor) { + Executor backgroundExecutor) { return new AsyncSecurityPolicy() { @Override public ListenableFuture checkAuthorizationAsync(int uid) { - return backgroundExecutor.submit(() -> { + return Futures.submit(() -> { PackageInfo platformPackageInfo; try { platformPackageInfo = @@ -127,7 +128,7 @@ public ListenableFuture checkAuthorizationAsync(int uid) { ImmutableList policies = createSignaturePolicies(packageManager, packageName, platformPackageInfo); return anyOf(policies.toArray(new SecurityPolicy[0])).checkAuthorization(uid); - }); + }, backgroundExecutor); } }; } From 3b715d3cc50f00c04c2bb32de4f86c3234a83a3b Mon Sep 17 00:00:00 2001 From: Mateus Azis Date: Fri, 31 May 2024 13:52:41 -0700 Subject: [PATCH 5/9] run google-java-format --- .../java/io/grpc/binder/SecurityPolicies.java | 47 ++++++++++--------- .../io/grpc/binder/SecurityPoliciesTest.java | 21 +++------ 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index 5c5c1121b76..bf3ec28e12d 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -112,23 +112,26 @@ public static SecurityPolicy hasSignature( */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11238") public static AsyncSecurityPolicy hasSameSignatureAsPlatform( - PackageManager packageManager, String packageName, - Executor backgroundExecutor) { + PackageManager packageManager, String packageName, Executor backgroundExecutor) { return new AsyncSecurityPolicy() { @Override public ListenableFuture checkAuthorizationAsync(int uid) { - return Futures.submit(() -> { - PackageInfo platformPackageInfo; - try { - platformPackageInfo = - packageManager.getPackageInfo("android", packageManager.GET_SIGNING_CERTIFICATES); - } catch (PackageManager.NameNotFoundException e) { - throw new AssertionError(e); // impossible; the platform package is always available. - } - ImmutableList policies = - createSignaturePolicies(packageManager, packageName, platformPackageInfo); - return anyOf(policies.toArray(new SecurityPolicy[0])).checkAuthorization(uid); - }, backgroundExecutor); + return Futures.submit( + () -> { + PackageInfo platformPackageInfo; + try { + platformPackageInfo = + packageManager.getPackageInfo( + "android", packageManager.GET_SIGNING_CERTIFICATES); + } catch (PackageManager.NameNotFoundException e) { + throw new AssertionError( + e); // impossible; the platform package is always available. + } + ImmutableList policies = + createSignaturePolicies(packageManager, packageName, platformPackageInfo); + return anyOf(policies.toArray(new SecurityPolicy[0])).checkAuthorization(uid); + }, + backgroundExecutor); } }; } @@ -136,17 +139,17 @@ public ListenableFuture checkAuthorizationAsync(int uid) { private static ImmutableList createSignaturePolicies( PackageManager packageManager, String packageName, PackageInfo packageInfo) { Signature[] signatures = packageInfo.signatures; - checkState( - signatures.length > 0, "There should be at least one platform signature"); + checkState(signatures.length > 0, "There should be at least one platform signature"); ImmutableList.Builder policies = ImmutableList.builderWithExpectedSize(signatures.length); for (Signature signature : signatures) { - policies.add(new SecurityPolicy() { - @Override - public Status checkAuthorization(int uid) { - return hasSignature(packageManager, packageName, signature).checkAuthorization(uid); - } - }); + policies.add( + new SecurityPolicy() { + @Override + public Status checkAuthorization(int uid) { + return hasSignature(packageManager, packageName, signature).checkAuthorization(uid); + } + }); } return policies.build(); } diff --git a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java index 6e78f0251ef..69c29f86d73 100644 --- a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java +++ b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java @@ -85,7 +85,7 @@ private void installPackages(int uid, PackageInfo... packageInfo) { shadowOf(packageManager).setPackagesForUid(uid, packageNames); } - private void setupPlatformSignature(Signature...signatures) { + private void setupPlatformSignature(Signature... signatures) { PackageInfo platformPackageInfo = newBuilder().setPackageName("android").setSignatures(signatures).build(); installPackages(Process.SYSTEM_UID, platformPackageInfo); @@ -167,9 +167,7 @@ public void testHasSameSignatureAsPlatform_succeedsIfSignaturesMatch() { policy = SecurityPolicies.hasSameSignatureAsPlatform( - packageManager, - OTHER_UID_PACKAGE_NAME, - MoreExecutors.newDirectExecutorService()); + packageManager, OTHER_UID_PACKAGE_NAME, MoreExecutors.newDirectExecutorService()); assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); } @@ -186,9 +184,7 @@ public void testHasSameSignatureAsPlatform_failsIfPackageNameDoesNotMatch() { policy = SecurityPolicies.hasSameSignatureAsPlatform( - packageManager, - appContext.getPackageName(), - MoreExecutors.newDirectExecutorService()); + packageManager, appContext.getPackageName(), MoreExecutors.newDirectExecutorService()); assertThat(policy.checkAuthorization(OTHER_UID_SAME_SIGNATURE).getCode()) .isEqualTo(Status.PERMISSION_DENIED.getCode()); @@ -203,9 +199,7 @@ public void testHasSameSignatureAsPlatform_failsIfSignatureDoesNotMatch() { policy = SecurityPolicies.hasSameSignatureAsPlatform( - packageManager, - OTHER_UID_PACKAGE_NAME, - MoreExecutors.newDirectExecutorService()); + packageManager, OTHER_UID_PACKAGE_NAME, MoreExecutors.newDirectExecutorService()); assertThat(policy.checkAuthorization(OTHER_UID).getCode()) .isEqualTo(Status.PERMISSION_DENIED.getCode()); @@ -216,17 +210,14 @@ public void testHasSameSignatureAsPlatform_failsIfUidUnknown() { setupPlatformSignature(SIG1); policy = SecurityPolicies.hasSameSignatureAsPlatform( - packageManager, - appContext.getPackageName(), - MoreExecutors.newDirectExecutorService()); + packageManager, appContext.getPackageName(), MoreExecutors.newDirectExecutorService()); assertThat(policy.checkAuthorization(OTHER_UID_UNKNOWN).getCode()) .isEqualTo(Status.UNAUTHENTICATED.getCode()); } @Test - public void testOneOfSignatures_succeedsIfPackageNameAndSignaturesMatch() - throws Exception { + public void testOneOfSignatures_succeedsIfPackageNameAndSignaturesMatch() throws Exception { PackageInfo info = newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build(); From 857106ba6751c736c7061f25ff1b98e6ca3fc7a8 Mon Sep 17 00:00:00 2001 From: Mateus Azis Date: Fri, 31 May 2024 13:53:40 -0700 Subject: [PATCH 6/9] undo unrelated formattting change --- binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java index 69c29f86d73..fded38685c4 100644 --- a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java +++ b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java @@ -217,7 +217,8 @@ public void testHasSameSignatureAsPlatform_failsIfUidUnknown() { } @Test - public void testOneOfSignatures_succeedsIfPackageNameAndSignaturesMatch() throws Exception { + public void testOneOfSignatures_succeedsIfPackageNameAndSignaturesMatch() + throws Exception { PackageInfo info = newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build(); From ac26e364eeb8cb807d44ad0ab47875e5666b8887 Mon Sep 17 00:00:00 2001 From: Mateus Azis Date: Mon, 3 Jun 2024 11:20:56 -0700 Subject: [PATCH 7/9] reuse oneOfSignatures --- .../java/io/grpc/binder/SecurityPolicies.java | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index bf3ec28e12d..c85154a285f 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -127,33 +127,17 @@ public ListenableFuture checkAuthorizationAsync(int uid) { throw new AssertionError( e); // impossible; the platform package is always available. } - ImmutableList policies = - createSignaturePolicies(packageManager, packageName, platformPackageInfo); - return anyOf(policies.toArray(new SecurityPolicy[0])).checkAuthorization(uid); + return oneOfSignatures( + packageManager, + packageName, + ImmutableList.copyOf(platformPackageInfo.signatures)) + .checkAuthorization(uid); }, backgroundExecutor); } }; } - private static ImmutableList createSignaturePolicies( - PackageManager packageManager, String packageName, PackageInfo packageInfo) { - Signature[] signatures = packageInfo.signatures; - checkState(signatures.length > 0, "There should be at least one platform signature"); - ImmutableList.Builder policies = - ImmutableList.builderWithExpectedSize(signatures.length); - for (Signature signature : signatures) { - policies.add( - new SecurityPolicy() { - @Override - public Status checkAuthorization(int uid) { - return hasSignature(packageManager, packageName, signature).checkAuthorization(uid); - } - }); - } - return policies.build(); - } - /** * Creates {@link SecurityPolicy} which checks if the SHA-256 hash of the package signature * matches {@code requiredSignatureSha256Hash}. From f59e1a3df73bc19878a2787612f94e6b5c166e49 Mon Sep 17 00:00:00 2001 From: Mateus Azis Date: Mon, 3 Jun 2024 11:35:45 -0700 Subject: [PATCH 8/9] omit mention to 'disk I/O' --- binder/src/main/java/io/grpc/binder/SecurityPolicies.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index c85154a285f..54f261c8ec4 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -107,7 +107,7 @@ public static SecurityPolicy hasSignature( * certificate that the current Android platform was signed with. * * @param packageName the package name of the allowed package. - * @param backgroundExecutor an executor suitable for blocking disk I/O. + * @param backgroundExecutor an executor suitable for blocking I/O. * @throws NullPointerException if any of the inputs are {@code null}. */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11238") From f29dc7df0cbfaf7c9fe8a8a5e59ff5660f6be405 Mon Sep 17 00:00:00 2001 From: Mateus Azis Date: Wed, 5 Jun 2024 14:55:30 -0700 Subject: [PATCH 9/9] get signatures in 27- vs 28+ SDKs --- .../java/io/grpc/binder/SecurityPolicies.java | 34 +++++++++++++------ .../io/grpc/binder/SecurityPoliciesTest.java | 14 ++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index 54f261c8ec4..c9fc4d83f29 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -16,6 +16,7 @@ package io.grpc.binder; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import android.annotation.SuppressLint; @@ -25,6 +26,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; +import android.content.pm.SigningInfo; import android.os.Build; import android.os.Build.VERSION; import android.os.Process; @@ -118,19 +120,12 @@ public static AsyncSecurityPolicy hasSameSignatureAsPlatform( public ListenableFuture checkAuthorizationAsync(int uid) { return Futures.submit( () -> { - PackageInfo platformPackageInfo; - try { - platformPackageInfo = - packageManager.getPackageInfo( - "android", packageManager.GET_SIGNING_CERTIFICATES); - } catch (PackageManager.NameNotFoundException e) { - throw new AssertionError( - e); // impossible; the platform package is always available. - } + Signature[] platformSignatures = getPlatformSignatures(packageManager); + return oneOfSignatures( packageManager, packageName, - ImmutableList.copyOf(platformPackageInfo.signatures)) + ImmutableList.copyOf(platformSignatures)) .checkAuthorization(uid); }, backgroundExecutor); @@ -138,6 +133,25 @@ public ListenableFuture checkAuthorizationAsync(int uid) { }; } + private static Signature[] getPlatformSignatures(PackageManager packageManager) { + try { + PackageInfo packageInfo; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + packageInfo = + packageManager.getPackageInfo("android", PackageManager.GET_SIGNING_CERTIFICATES); + SigningInfo signingInfo = checkNotNull(packageInfo.signingInfo); + return signingInfo.hasMultipleSigners() + ? signingInfo.getApkContentsSigners() + : signingInfo.getSigningCertificateHistory(); + } else { + packageInfo = packageManager.getPackageInfo("android", PackageManager.GET_SIGNATURES); + return checkNotNull(packageInfo.signatures); + } + } catch (NameNotFoundException nnfe) { + throw new AssertionError(nnfe); // impossible: the "android" package is always present + } + } + /** * Creates {@link SecurityPolicy} which checks if the SHA-256 hash of the package signature * matches {@code requiredSignatureSha256Hash}. diff --git a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java index fded38685c4..c606eab2fc5 100644 --- a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java +++ b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java @@ -30,6 +30,8 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; +import android.content.pm.SigningInfo; +import android.os.Build; import android.os.Process; import androidx.test.core.app.ApplicationProvider; import com.google.common.collect.ImmutableList; @@ -43,6 +45,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowSigningInfo; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @@ -159,6 +162,7 @@ public void testHasSignature_failsIfSignatureDoesNotMatch() throws Exception { } @Test + @Config(sdk = {Build.VERSION_CODES.O_MR1, Build.VERSION_CODES.P}) public void testHasSameSignatureAsPlatform_succeedsIfSignaturesMatch() { setupPlatformSignature(SIG2, SIG1); PackageInfo info = @@ -173,6 +177,7 @@ public void testHasSameSignatureAsPlatform_succeedsIfSignaturesMatch() { } @Test + @Config(sdk = {Build.VERSION_CODES.O_MR1, Build.VERSION_CODES.P}) public void testHasSameSignatureAsPlatform_failsIfPackageNameDoesNotMatch() { setupPlatformSignature(SIG1); PackageInfo info = @@ -191,6 +196,7 @@ public void testHasSameSignatureAsPlatform_failsIfPackageNameDoesNotMatch() { } @Test + @Config(sdk = {Build.VERSION_CODES.O_MR1, Build.VERSION_CODES.P}) public void testHasSameSignatureAsPlatform_failsIfSignatureDoesNotMatch() { setupPlatformSignature(SIG1); PackageInfo info = @@ -206,6 +212,7 @@ public void testHasSameSignatureAsPlatform_failsIfSignatureDoesNotMatch() { } @Test + @Config(sdk = {Build.VERSION_CODES.O_MR1, Build.VERSION_CODES.P}) public void testHasSameSignatureAsPlatform_failsIfUidUnknown() { setupPlatformSignature(SIG1); policy = @@ -565,6 +572,13 @@ public PackageInfo build() { if (this.signatures != null) { packageInfo.signatures = this.signatures; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + SigningInfo signingInfo = new SigningInfo(); + ShadowSigningInfo shadowSigningInfo = shadowOf(signingInfo); + shadowSigningInfo.setSignatures(this.signatures); + packageInfo.signingInfo = signingInfo; + } } if (!this.permissions.isEmpty()) {