Skip to content

Commit 0fd5751

Browse files
authored
Add security policies for checking device owner/profile owner. (#9428)
1 parent bf692c0 commit 0fd5751

File tree

3 files changed

+246
-2
lines changed

3 files changed

+246
-2
lines changed

binder/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ android {
2323
}
2424
}
2525
}
26-
compileSdkVersion 29
26+
compileSdkVersion 30
2727
compileOptions {
2828
sourceCompatibility 1.8
2929
targetCompatibility 1.8
3030
}
3131
defaultConfig {
3232
minSdkVersion 19
33-
targetSdkVersion 29
33+
targetSdkVersion 30
3434
versionCode 1
3535
versionName "1.0"
3636
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

binder/src/main/java/io/grpc/binder/SecurityPolicies.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
package io.grpc.binder;
1818

1919
import android.annotation.SuppressLint;
20+
import android.app.admin.DevicePolicyManager;
21+
import android.content.Context;
2022
import android.content.pm.PackageInfo;
2123
import android.content.pm.PackageManager;
2224
import android.content.pm.PackageManager.NameNotFoundException;
2325
import android.content.pm.Signature;
2426
import android.os.Build;
27+
import android.os.Build.VERSION;
2528
import android.os.Process;
2629
import com.google.common.base.Preconditions;
2730
import com.google.common.base.Predicate;
@@ -173,6 +176,50 @@ public Status checkAuthorization(int uid) {
173176
};
174177
}
175178

179+
/**
180+
* Creates {@link SecurityPolicy} which checks if the app is a device owner app. See
181+
* {@link DevicePolicyManager}.
182+
*/
183+
public static SecurityPolicy isDeviceOwner(Context applicationContext) {
184+
DevicePolicyManager devicePolicyManager =
185+
(DevicePolicyManager) applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
186+
return anyPackageWithUidSatisfies(
187+
applicationContext,
188+
pkg -> VERSION.SDK_INT >= 18 && devicePolicyManager.isDeviceOwnerApp(pkg),
189+
"Rejected by device owner policy. No packages found for UID.",
190+
"Rejected by device owner policy");
191+
}
192+
193+
/**
194+
* Creates {@link SecurityPolicy} which checks if the app is a profile owner app. See
195+
* {@link DevicePolicyManager}.
196+
*/
197+
public static SecurityPolicy isProfileOwner(Context applicationContext) {
198+
DevicePolicyManager devicePolicyManager =
199+
(DevicePolicyManager) applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
200+
return anyPackageWithUidSatisfies(
201+
applicationContext,
202+
pkg -> VERSION.SDK_INT >= 21 && devicePolicyManager.isProfileOwnerApp(pkg),
203+
"Rejected by profile owner policy. No packages found for UID.",
204+
"Rejected by profile owner policy");
205+
}
206+
207+
/**
208+
* Creates {@link SecurityPolicy} which checks if the app is a profile owner app on an
209+
* organization-owned device. See {@link DevicePolicyManager}.
210+
*/
211+
public static SecurityPolicy isProfileOwnerOnOrganizationOwnedDevice(Context applicationContext) {
212+
DevicePolicyManager devicePolicyManager =
213+
(DevicePolicyManager) applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
214+
return anyPackageWithUidSatisfies(
215+
applicationContext,
216+
pkg -> VERSION.SDK_INT >= 30
217+
&& devicePolicyManager.isProfileOwnerApp(pkg)
218+
&& devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile(),
219+
"Rejected by profile owner on organization-owned device policy. No packages found for UID.",
220+
"Rejected by profile owner on organization-owned device policy");
221+
}
222+
176223
private static Status checkUidSignature(
177224
PackageManager packageManager,
178225
int uid,
@@ -406,6 +453,29 @@ private static Status checkPermissions(
406453
return Status.OK;
407454
}
408455

456+
private static SecurityPolicy anyPackageWithUidSatisfies(
457+
Context applicationContext,
458+
Predicate<String> condition,
459+
String errorMessageForNoPackages,
460+
String errorMessageForDenied) {
461+
return new SecurityPolicy() {
462+
@Override
463+
public Status checkAuthorization(int uid) {
464+
String[] packages = applicationContext.getPackageManager().getPackagesForUid(uid);
465+
if (packages == null || packages.length == 0) {
466+
return Status.UNAUTHENTICATED.withDescription(errorMessageForNoPackages);
467+
}
468+
469+
for (String pkg : packages) {
470+
if (condition.apply(pkg)) {
471+
return Status.OK;
472+
}
473+
}
474+
return Status.PERMISSION_DENIED.withDescription(errorMessageForDenied);
475+
}
476+
};
477+
}
478+
409479
/**
410480
* Checks if the SHA-256 hash of the {@code signature} matches one of the {@code
411481
* expectedSignatureSha256Hashes}.

binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,15 @@
2424
import static com.google.common.truth.Truth.assertThat;
2525
import static org.robolectric.Shadows.shadowOf;
2626

27+
import android.app.admin.DevicePolicyManager;
28+
import android.content.ComponentName;
2729
import android.content.Context;
2830
import android.content.pm.PackageInfo;
2931
import android.content.pm.PackageManager;
3032
import android.content.pm.Signature;
33+
import android.os.Build;
34+
import android.os.Build.VERSION;
35+
import android.os.Build.VERSION_CODES;
3136
import android.os.Process;
3237
import androidx.test.core.app.ApplicationProvider;
3338
import com.google.common.collect.ImmutableList;
@@ -40,6 +45,7 @@
4045
import org.junit.Test;
4146
import org.junit.runner.RunWith;
4247
import org.robolectric.RobolectricTestRunner;
48+
import org.robolectric.annotation.Config;
4349

4450
@RunWith(RobolectricTestRunner.class)
4551
public final class SecurityPoliciesTest {
@@ -59,13 +65,16 @@ public final class SecurityPoliciesTest {
5965

6066
private Context appContext;
6167
private PackageManager packageManager;
68+
private DevicePolicyManager devicePolicyManager;
6269

6370
private SecurityPolicy policy;
6471

6572
@Before
6673
public void setUp() {
6774
appContext = ApplicationProvider.getApplicationContext();
6875
packageManager = appContext.getPackageManager();
76+
devicePolicyManager =
77+
(DevicePolicyManager) appContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
6978
}
7079

7180
@SuppressWarnings("deprecation")
@@ -323,6 +332,171 @@ public void testHasPermissions_failsIfPackageDoesNotHavePermissions() throws Exc
323332
.contains(OTHER_UID_PACKAGE_NAME);
324333
}
325334

335+
@Test
336+
@Config(sdk = 18)
337+
public void testIsDeviceOwner_succeedsForDeviceOwner() throws Exception {
338+
PackageInfo info =
339+
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();
340+
341+
installPackages(OTHER_UID, info);
342+
shadowOf(devicePolicyManager)
343+
.setDeviceOwner(new ComponentName(OTHER_UID_PACKAGE_NAME, "foo"));
344+
345+
policy = SecurityPolicies.isDeviceOwner(appContext);
346+
347+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode());
348+
}
349+
350+
@Test
351+
@Config(sdk = 18)
352+
public void testIsDeviceOwner_failsForNotDeviceOwner() throws Exception {
353+
PackageInfo info =
354+
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();
355+
356+
installPackages(OTHER_UID, info);
357+
358+
policy = SecurityPolicies.isDeviceOwner(appContext);
359+
360+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.PERMISSION_DENIED.getCode());
361+
}
362+
363+
@Test
364+
@Config(sdk = 18)
365+
public void testIsDeviceOwner_failsWhenNoPackagesForUid() throws Exception {
366+
policy = SecurityPolicies.isDeviceOwner(appContext);
367+
368+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.UNAUTHENTICATED.getCode());
369+
}
370+
371+
@Test
372+
@Config(sdk = 17)
373+
public void testIsDeviceOwner_failsForSdkLevelTooLow() throws Exception {
374+
PackageInfo info =
375+
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();
376+
377+
installPackages(OTHER_UID, info);
378+
379+
policy = SecurityPolicies.isDeviceOwner(appContext);
380+
381+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.PERMISSION_DENIED.getCode());
382+
}
383+
384+
@Test
385+
@Config(sdk = 21)
386+
public void testIsProfileOwner_succeedsForProfileOwner() throws Exception {
387+
PackageInfo info =
388+
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();
389+
390+
installPackages(OTHER_UID, info);
391+
shadowOf(devicePolicyManager)
392+
.setProfileOwner(new ComponentName(OTHER_UID_PACKAGE_NAME, "foo"));
393+
394+
policy = SecurityPolicies.isProfileOwner(appContext);
395+
396+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode());
397+
}
398+
399+
@Test
400+
@Config(sdk = 21)
401+
public void testIsProfileOwner_failsForNotProfileOwner() throws Exception {
402+
PackageInfo info =
403+
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();
404+
405+
installPackages(OTHER_UID, info);
406+
407+
policy = SecurityPolicies.isProfileOwner(appContext);
408+
409+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.PERMISSION_DENIED.getCode());
410+
}
411+
412+
@Test
413+
@Config(sdk = 21)
414+
public void testIsProfileOwner_failsWhenNoPackagesForUid() throws Exception {
415+
policy = SecurityPolicies.isProfileOwner(appContext);
416+
417+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.UNAUTHENTICATED.getCode());
418+
}
419+
420+
@Test
421+
@Config(sdk = 19)
422+
public void testIsProfileOwner_failsForSdkLevelTooLow() throws Exception {
423+
PackageInfo info =
424+
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();
425+
426+
installPackages(OTHER_UID, info);
427+
428+
policy = SecurityPolicies.isProfileOwner(appContext);
429+
430+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.PERMISSION_DENIED.getCode());
431+
}
432+
433+
@Test
434+
@Config(sdk = 30)
435+
public void testIsProfileOwnerOnOrgOwned_succeedsForProfileOwnerOnOrgOwned() throws Exception {
436+
PackageInfo info =
437+
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();
438+
439+
installPackages(OTHER_UID, info);
440+
shadowOf(devicePolicyManager)
441+
.setProfileOwner(new ComponentName(OTHER_UID_PACKAGE_NAME, "foo"));
442+
shadowOf(devicePolicyManager).setOrganizationOwnedDeviceWithManagedProfile(true);
443+
444+
policy = SecurityPolicies.isProfileOwnerOnOrganizationOwnedDevice(appContext);
445+
446+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode());
447+
448+
}
449+
450+
@Test
451+
@Config(sdk = 30)
452+
public void testIsProfileOwnerOnOrgOwned_failsForProfileOwnerOnNonOrgOwned() throws Exception {
453+
PackageInfo info =
454+
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();
455+
456+
installPackages(OTHER_UID, info);
457+
shadowOf(devicePolicyManager)
458+
.setProfileOwner(new ComponentName(OTHER_UID_PACKAGE_NAME, "foo"));
459+
shadowOf(devicePolicyManager).setOrganizationOwnedDeviceWithManagedProfile(false);
460+
461+
policy = SecurityPolicies.isProfileOwnerOnOrganizationOwnedDevice(appContext);
462+
463+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode());
464+
}
465+
466+
@Test
467+
@Config(sdk = 21)
468+
public void testIsProfileOwnerOnOrgOwned_failsForNotProfileOwner() throws Exception {
469+
PackageInfo info =
470+
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();
471+
472+
installPackages(OTHER_UID, info);
473+
474+
policy = SecurityPolicies.isProfileOwnerOnOrganizationOwnedDevice(appContext);
475+
476+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.PERMISSION_DENIED.getCode());
477+
}
478+
479+
@Test
480+
@Config(sdk = 21)
481+
public void testIsProfileOwnerOnOrgOwned_failsWhenNoPackagesForUid() throws Exception {
482+
policy = SecurityPolicies.isProfileOwnerOnOrganizationOwnedDevice(appContext);
483+
484+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.UNAUTHENTICATED.getCode());
485+
}
486+
487+
@Test
488+
@Config(sdk = 29)
489+
public void testIsProfileOwnerOnOrgOwned_failsForSdkLevelTooLow() throws Exception {
490+
PackageInfo info =
491+
newBuilder().setPackageName(OTHER_UID_PACKAGE_NAME).setSignatures(SIG2).build();
492+
493+
installPackages(OTHER_UID, info);
494+
495+
policy = SecurityPolicies.isProfileOwner(appContext);
496+
497+
assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.PERMISSION_DENIED.getCode());
498+
}
499+
326500
private static PackageInfoBuilder newBuilder() {
327501
return new PackageInfoBuilder();
328502
}

0 commit comments

Comments
 (0)