diff options
author | Benedict Wong <benedictwong@google.com> | 2020-12-18 20:55:46 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-12-18 20:55:46 +0000 |
commit | d9ed2cd26a34e105e8c5709d90076fdeeb13a119 (patch) | |
tree | 73698d0163d51096884412e9b0417d2ac9baf322 | |
parent | 10a3c4bad0458042debc53309eedfc35d0415d93 (diff) | |
parent | e54bf052769b96da0bb6b10bdd9bba95c669f3ab (diff) |
Merge "Enforce carrier privileges when setting/clearing VCN configs" am: 066429ddbe am: e54bf05276
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1486653
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: If9d92d219a1eefccf3cceed9e9063e8da715ee24
-rw-r--r-- | services/core/java/com/android/server/VcnManagementService.java | 73 | ||||
-rw-r--r-- | tests/vcn/java/com/android/server/VcnManagementServiceTest.java | 135 |
2 files changed, 203 insertions, 5 deletions
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 165b6a1b08f1..e9f17fff5a61 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -25,13 +25,22 @@ import android.net.NetworkProvider; import android.net.NetworkRequest; import android.net.vcn.IVcnManagementService; import android.net.vcn.VcnConfig; +import android.os.Binder; import android.os.HandlerThread; import android.os.Looper; import android.os.ParcelUuid; +import android.os.Process; +import android.os.UserHandle; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; +import java.util.ArrayList; +import java.util.List; + /** * VcnManagementService manages Virtual Carrier Network profiles and lifecycles. * @@ -130,6 +139,18 @@ public class VcnManagementService extends IVcnManagementService.Stub { } return mHandlerThread.getLooper(); } + + /** + * Retrieves the caller's UID + * + * <p>This call MUST be made before calling {@link Binder#clearCallingIdentity}, otherwise + * this will not work properly. + * + * @return + */ + public int getBinderCallingUid() { + return Binder.getCallingUid(); + } } /** Notifies the VcnManagementService that external dependencies can be set up. */ @@ -140,6 +161,50 @@ public class VcnManagementService extends IVcnManagementService.Stub { .registerNetworkProvider(mNetworkProvider); } + private void enforcePrimaryUser() { + final int uid = mDeps.getBinderCallingUid(); + if (uid == Process.SYSTEM_UID) { + throw new IllegalStateException( + "Calling identity was System Server. Was Binder calling identity cleared?"); + } + + if (!UserHandle.getUserHandleForUid(uid).isSystem()) { + throw new SecurityException( + "VcnManagementService can only be used by callers running as the primary user"); + } + } + + private void enforceCallingUserAndCarrierPrivilege(ParcelUuid subscriptionGroup) { + // Only apps running in the primary (system) user are allowed to configure the VCN. This is + // in line with Telephony's behavior with regards to binding to a Carrier App provided + // CarrierConfigService. + enforcePrimaryUser(); + + // TODO (b/172619301): Check based on events propagated from CarrierPrivilegesTracker + final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class); + final List<SubscriptionInfo> subscriptionInfos = new ArrayList<>(); + Binder.withCleanCallingIdentity( + () -> { + subscriptionInfos.addAll(subMgr.getSubscriptionsInGroup(subscriptionGroup)); + }); + + final TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class); + for (SubscriptionInfo info : subscriptionInfos) { + // Check subscription is active first; much cheaper/faster check, and an app (currently) + // cannot be carrier privileged for inactive subscriptions. + if (subMgr.isValidSlotIndex(info.getSimSlotIndex()) + && telMgr.hasCarrierPrivileges(info.getSubscriptionId())) { + // TODO (b/173717728): Allow configuration for inactive, but manageable + // subscriptions. + // TODO (b/173718661): Check for whole subscription groups at a time. + return; + } + } + + throw new SecurityException( + "Carrier privilege required for subscription group to set VCN Config"); + } + /** * Sets a VCN config for a given subscription group. * @@ -150,6 +215,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); requireNonNull(config, "config was null"); + enforceCallingUserAndCarrierPrivilege(subscriptionGroup); + + // TODO: Clear Binder calling identity + // TODO: Store VCN configuration, trigger startup as necessary } @@ -162,6 +231,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); + enforceCallingUserAndCarrierPrivilege(subscriptionGroup); + + // TODO: Clear Binder calling identity + // TODO: Clear VCN configuration, trigger teardown as necessary } diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index c91fdbffd760..633cf64bc274 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -16,14 +16,23 @@ package com.android.server; +import static org.junit.Assert.fail; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import android.content.Context; import android.net.ConnectivityManager; +import android.net.vcn.VcnConfig; +import android.os.ParcelUuid; +import android.os.Process; +import android.os.UserHandle; import android.os.test.TestLooper; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -31,27 +40,73 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Collections; +import java.util.UUID; + /** Tests for {@link VcnManagementService}. */ @RunWith(AndroidJUnit4.class) @SmallTest public class VcnManagementServiceTest { + private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0)); + private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO = + new SubscriptionInfo( + 1 /* id */, + "" /* iccId */, + 0 /* simSlotIndex */, + "Carrier" /* displayName */, + "Carrier" /* carrierName */, + 0 /* nameSource */, + 255 /* iconTint */, + "12345" /* number */, + 0 /* roaming */, + null /* icon */, + "0" /* mcc */, + "0" /* mnc */, + "0" /* countryIso */, + false /* isEmbedded */, + null /* nativeAccessRules */, + null /* cardString */, + false /* isOpportunistic */, + TEST_UUID_1.toString() /* groupUUID */, + 0 /* carrierId */, + 0 /* profileClass */); + private final Context mMockContext = mock(Context.class); private final VcnManagementService.Dependencies mMockDeps = mock(VcnManagementService.Dependencies.class); private final TestLooper mTestLooper = new TestLooper(); private final ConnectivityManager mConnMgr = mock(ConnectivityManager.class); + private final TelephonyManager mTelMgr = mock(TelephonyManager.class); + private final SubscriptionManager mSubMgr = mock(SubscriptionManager.class); private final VcnManagementService mVcnMgmtSvc; - public VcnManagementServiceTest() { - doReturn(Context.CONNECTIVITY_SERVICE) - .when(mMockContext) - .getSystemServiceName(ConnectivityManager.class); - doReturn(mConnMgr).when(mMockContext).getSystemService(Context.CONNECTIVITY_SERVICE); + public VcnManagementServiceTest() throws Exception { + setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class); + setupSystemService( + mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class); doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper(); + doReturn(Process.FIRST_APPLICATION_UID).when(mMockDeps).getBinderCallingUid(); + + setupMockedCarrierPrivilege(true); mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps); } + private void setupSystemService(Object service, String name, Class<?> serviceClass) { + doReturn(name).when(mMockContext).getSystemServiceName(serviceClass); + doReturn(service).when(mMockContext).getSystemService(name); + } + + private void setupMockedCarrierPrivilege(boolean isPrivileged) { + doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO)) + .when(mSubMgr) + .getSubscriptionsInGroup(any()); + doReturn(isPrivileged) + .when(mTelMgr) + .hasCarrierPrivileges(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId())); + } + @Test public void testSystemReady() throws Exception { mVcnMgmtSvc.systemReady(); @@ -59,4 +114,74 @@ public class VcnManagementServiceTest { verify(mConnMgr) .registerNetworkProvider(any(VcnManagementService.VcnNetworkProvider.class)); } + + @Test + public void testSetVcnConfigRequiresNonSystemServer() throws Exception { + doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid(); + + try { + mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, new VcnConfig.Builder().build()); + fail("Expected IllegalStateException exception for system server"); + } catch (IllegalStateException expected) { + } + } + + @Test + public void testSetVcnConfigRequiresSystemUser() throws Exception { + doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID)) + .when(mMockDeps) + .getBinderCallingUid(); + + try { + mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, new VcnConfig.Builder().build()); + fail("Expected security exception for non system user"); + } catch (SecurityException expected) { + } + } + + @Test + public void testSetVcnConfigRequiresCarrierPrivileges() throws Exception { + setupMockedCarrierPrivilege(false); + + try { + mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, new VcnConfig.Builder().build()); + fail("Expected security exception for missing carrier privileges"); + } catch (SecurityException expected) { + } + } + + @Test + public void testClearVcnConfigRequiresNonSystemServer() throws Exception { + doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid(); + + try { + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + fail("Expected IllegalStateException exception for system server"); + } catch (IllegalStateException expected) { + } + } + + @Test + public void testClearVcnConfigRequiresSystemUser() throws Exception { + doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID)) + .when(mMockDeps) + .getBinderCallingUid(); + + try { + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + fail("Expected security exception for non system user"); + } catch (SecurityException expected) { + } + } + + @Test + public void testClearVcnConfigRequiresCarrierPrivileges() throws Exception { + setupMockedCarrierPrivilege(false); + + try { + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + fail("Expected security exception for missing carrier privileges"); + } catch (SecurityException expected) { + } + } } |