Skip to content

Commit

Permalink
Add security policies for checking device owner/profile owner. (#9428)
Browse files Browse the repository at this point in the history
  • Loading branch information
prateek-0 committed Sep 7, 2022
1 parent bf692c0 commit 0fd5751
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 2 deletions.
4 changes: 2 additions & 2 deletions binder/build.gradle
Expand Up @@ -23,14 +23,14 @@ android {
}
}
}
compileSdkVersion 29
compileSdkVersion 30
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
defaultConfig {
minSdkVersion 19
targetSdkVersion 29
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down
70 changes: 70 additions & 0 deletions binder/src/main/java/io/grpc/binder/SecurityPolicies.java
Expand Up @@ -17,11 +17,14 @@
package io.grpc.binder;

import android.annotation.SuppressLint;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Process;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
Expand Down Expand Up @@ -173,6 +176,50 @@ public Status checkAuthorization(int uid) {
};
}

/**
* Creates {@link SecurityPolicy} which checks if the app is a device owner app. See
* {@link DevicePolicyManager}.
*/
public static SecurityPolicy isDeviceOwner(Context applicationContext) {
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
return anyPackageWithUidSatisfies(
applicationContext,
pkg -> VERSION.SDK_INT >= 18 && devicePolicyManager.isDeviceOwnerApp(pkg),
"Rejected by device owner policy. No packages found for UID.",
"Rejected by device owner policy");
}

/**
* Creates {@link SecurityPolicy} which checks if the app is a profile owner app. See
* {@link DevicePolicyManager}.
*/
public static SecurityPolicy isProfileOwner(Context applicationContext) {
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
return anyPackageWithUidSatisfies(
applicationContext,
pkg -> VERSION.SDK_INT >= 21 && devicePolicyManager.isProfileOwnerApp(pkg),
"Rejected by profile owner policy. No packages found for UID.",
"Rejected by profile owner policy");
}

/**
* Creates {@link SecurityPolicy} which checks if the app is a profile owner app on an
* organization-owned device. See {@link DevicePolicyManager}.
*/
public static SecurityPolicy isProfileOwnerOnOrganizationOwnedDevice(Context applicationContext) {
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
return anyPackageWithUidSatisfies(
applicationContext,
pkg -> VERSION.SDK_INT >= 30
&& devicePolicyManager.isProfileOwnerApp(pkg)
&& devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile(),
"Rejected by profile owner on organization-owned device policy. No packages found for UID.",
"Rejected by profile owner on organization-owned device policy");
}

private static Status checkUidSignature(
PackageManager packageManager,
int uid,
Expand Down Expand Up @@ -406,6 +453,29 @@ private static Status checkPermissions(
return Status.OK;
}

private static SecurityPolicy anyPackageWithUidSatisfies(
Context applicationContext,
Predicate<String> condition,
String errorMessageForNoPackages,
String errorMessageForDenied) {
return new SecurityPolicy() {
@Override
public Status checkAuthorization(int uid) {
String[] packages = applicationContext.getPackageManager().getPackagesForUid(uid);
if (packages == null || packages.length == 0) {
return Status.UNAUTHENTICATED.withDescription(errorMessageForNoPackages);
}

for (String pkg : packages) {
if (condition.apply(pkg)) {
return Status.OK;
}
}
return Status.PERMISSION_DENIED.withDescription(errorMessageForDenied);
}
};
}

/**
* Checks if the SHA-256 hash of the {@code signature} matches one of the {@code
* expectedSignatureSha256Hashes}.
Expand Down
174 changes: 174 additions & 0 deletions binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java
Expand Up @@ -24,10 +24,15 @@
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.Shadows.shadowOf;

import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Process;
import androidx.test.core.app.ApplicationProvider;
import com.google.common.collect.ImmutableList;
Expand All @@ -40,6 +45,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

@RunWith(RobolectricTestRunner.class)
public final class SecurityPoliciesTest {
Expand All @@ -59,13 +65,16 @@ public final class SecurityPoliciesTest {

private Context appContext;
private PackageManager packageManager;
private DevicePolicyManager devicePolicyManager;

private SecurityPolicy policy;

@Before
public void setUp() {
appContext = ApplicationProvider.getApplicationContext();
packageManager = appContext.getPackageManager();
devicePolicyManager =
(DevicePolicyManager) appContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
}

@SuppressWarnings("deprecation")
Expand Down Expand Up @@ -323,6 +332,171 @@ public void testHasPermissions_failsIfPackageDoesNotHavePermissions() throws Exc
.contains(OTHER_UID_PACKAGE_NAME);
}

@Test
@Config(sdk = 18)
public void testIsDeviceOwner_succeedsForDeviceOwner() throws Exception {
PackageInfo info =
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();

installPackages(OTHER_UID, info);
shadowOf(devicePolicyManager)
.setDeviceOwner(new ComponentName(OTHER_UID_PACKAGE_NAME, "foo"));

policy = SecurityPolicies.isDeviceOwner(appContext);

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

@Test
@Config(sdk = 18)
public void testIsDeviceOwner_failsForNotDeviceOwner() throws Exception {
PackageInfo info =
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();

installPackages(OTHER_UID, info);

policy = SecurityPolicies.isDeviceOwner(appContext);

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

@Test
@Config(sdk = 18)
public void testIsDeviceOwner_failsWhenNoPackagesForUid() throws Exception {
policy = SecurityPolicies.isDeviceOwner(appContext);

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

@Test
@Config(sdk = 17)
public void testIsDeviceOwner_failsForSdkLevelTooLow() throws Exception {
PackageInfo info =
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();

installPackages(OTHER_UID, info);

policy = SecurityPolicies.isDeviceOwner(appContext);

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

@Test
@Config(sdk = 21)
public void testIsProfileOwner_succeedsForProfileOwner() throws Exception {
PackageInfo info =
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();

installPackages(OTHER_UID, info);
shadowOf(devicePolicyManager)
.setProfileOwner(new ComponentName(OTHER_UID_PACKAGE_NAME, "foo"));

policy = SecurityPolicies.isProfileOwner(appContext);

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

@Test
@Config(sdk = 21)
public void testIsProfileOwner_failsForNotProfileOwner() throws Exception {
PackageInfo info =
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();

installPackages(OTHER_UID, info);

policy = SecurityPolicies.isProfileOwner(appContext);

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

@Test
@Config(sdk = 21)
public void testIsProfileOwner_failsWhenNoPackagesForUid() throws Exception {
policy = SecurityPolicies.isProfileOwner(appContext);

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

@Test
@Config(sdk = 19)
public void testIsProfileOwner_failsForSdkLevelTooLow() throws Exception {
PackageInfo info =
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();

installPackages(OTHER_UID, info);

policy = SecurityPolicies.isProfileOwner(appContext);

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

@Test
@Config(sdk = 30)
public void testIsProfileOwnerOnOrgOwned_succeedsForProfileOwnerOnOrgOwned() throws Exception {
PackageInfo info =
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();

installPackages(OTHER_UID, info);
shadowOf(devicePolicyManager)
.setProfileOwner(new ComponentName(OTHER_UID_PACKAGE_NAME, "foo"));
shadowOf(devicePolicyManager).setOrganizationOwnedDeviceWithManagedProfile(true);

policy = SecurityPolicies.isProfileOwnerOnOrganizationOwnedDevice(appContext);

assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode());

}

@Test
@Config(sdk = 30)
public void testIsProfileOwnerOnOrgOwned_failsForProfileOwnerOnNonOrgOwned() throws Exception {
PackageInfo info =
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();

installPackages(OTHER_UID, info);
shadowOf(devicePolicyManager)
.setProfileOwner(new ComponentName(OTHER_UID_PACKAGE_NAME, "foo"));
shadowOf(devicePolicyManager).setOrganizationOwnedDeviceWithManagedProfile(false);

policy = SecurityPolicies.isProfileOwnerOnOrganizationOwnedDevice(appContext);

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

@Test
@Config(sdk = 21)
public void testIsProfileOwnerOnOrgOwned_failsForNotProfileOwner() throws Exception {
PackageInfo info =
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();

installPackages(OTHER_UID, info);

policy = SecurityPolicies.isProfileOwnerOnOrganizationOwnedDevice(appContext);

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

@Test
@Config(sdk = 21)
public void testIsProfileOwnerOnOrgOwned_failsWhenNoPackagesForUid() throws Exception {
policy = SecurityPolicies.isProfileOwnerOnOrganizationOwnedDevice(appContext);

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

@Test
@Config(sdk = 29)
public void testIsProfileOwnerOnOrgOwned_failsForSdkLevelTooLow() throws Exception {
PackageInfo info =
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();

installPackages(OTHER_UID, info);

policy = SecurityPolicies.isProfileOwner(appContext);

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

private static PackageInfoBuilder newBuilder() {
return new PackageInfoBuilder();
}
Expand Down

0 comments on commit 0fd5751

Please sign in to comment.