Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SecurityPolicy factory method for apps signed with the platform certificate #11239

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
41 changes: 41 additions & 0 deletions binder/src/main/java/io/grpc/binder/SecurityPolicies.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,6 +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.errorprone.annotations.CheckReturnValue;
import io.grpc.ExperimentalApi;
import io.grpc.Status;
Expand All @@ -40,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
Expand Down Expand Up @@ -97,6 +102,42 @@ 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.
* @param backgroundExecutor an executor suitable for blocking disk I/O.
mateusazis marked this conversation as resolved.
Show resolved Hide resolved
* @throws NullPointerException if any of the inputs are {@code null}.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/11238")
public static AsyncSecurityPolicy hasSameSignatureAsPlatform(
PackageManager packageManager, String packageName, Executor backgroundExecutor) {
return new AsyncSecurityPolicy() {
@Override
public ListenableFuture<Status> 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.
}
return oneOfSignatures(
packageManager,
packageName,
ImmutableList.copyOf(platformPackageInfo.signatures))
.checkAuthorization(uid);
},
backgroundExecutor);
}
};
}

/**
* Creates {@link SecurityPolicy} which checks if the SHA-256 hash of the package signature
* matches {@code requiredSignatureSha256Hash}.
Expand Down
65 changes: 65 additions & 0 deletions binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.hash.Hashing;
import com.google.common.util.concurrent.MoreExecutors;
import io.grpc.Status;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
Expand Down Expand Up @@ -84,6 +85,12 @@ private void installPackages(int uid, PackageInfo... packageInfo) {
shadowOf(packageManager).setPackagesForUid(uid, packageNames);
}

private void setupPlatformSignature(Signature... signatures) {
PackageInfo platformPackageInfo =
newBuilder().setPackageName("android").setSignatures(signatures).build();
installPackages(Process.SYSTEM_UID, platformPackageInfo);
}

@Test
public void testInternalOnly() throws Exception {
policy = SecurityPolicies.internalOnly();
Expand Down Expand Up @@ -151,6 +158,64 @@ public void testHasSignature_failsIfSignatureDoesNotMatch() throws Exception {
.isEqualTo(Status.PERMISSION_DENIED.getCode());
}

@Test
public void testHasSameSignatureAsPlatform_succeedsIfSignaturesMatch() {
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, MoreExecutors.newDirectExecutorService());

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(), MoreExecutors.newDirectExecutorService());

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, MoreExecutors.newDirectExecutorService());

assertThat(policy.checkAuthorization(OTHER_UID).getCode())
.isEqualTo(Status.PERMISSION_DENIED.getCode());
}

@Test
public void testHasSameSignatureAsPlatform_failsIfUidUnknown() {
setupPlatformSignature(SIG1);
policy =
SecurityPolicies.hasSameSignatureAsPlatform(
packageManager, appContext.getPackageName(), MoreExecutors.newDirectExecutorService());

assertThat(policy.checkAuthorization(OTHER_UID_UNKNOWN).getCode())
.isEqualTo(Status.UNAUTHENTICATED.getCode());
}

@Test
public void testOneOfSignatures_succeedsIfPackageNameAndSignaturesMatch()
throws Exception {
Expand Down
Loading