Merge changes from topic "niap-vpn" into qt-dev

* changes:
  Reinstate new VPN uid filtering unit tests
  Revert new tests and PackageManager mock
  Block incoming non-VPN packets to apps under fully-routed VPN
This commit is contained in:
Lorenzo Colitti
2019-04-25 04:23:01 +00:00
committed by Android (Google) Code Review
7 changed files with 754 additions and 106 deletions

View File

@@ -16,6 +16,8 @@
package com.android.server;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
@@ -60,11 +62,13 @@ import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static com.android.internal.util.TestUtils.waitForIdleHandler;
import static com.android.internal.util.TestUtils.waitForIdleLooper;
import static com.android.internal.util.TestUtils.waitForIdleSerialExecutor;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -72,12 +76,14 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -97,6 +103,10 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
@@ -139,6 +149,7 @@ import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
import android.os.Binder;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@@ -152,6 +163,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.system.Os;
import android.test.mock.MockContentResolver;
@@ -187,6 +199,7 @@ import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@@ -195,6 +208,7 @@ import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
@@ -262,6 +276,8 @@ public class ConnectivityServiceTest {
@Mock IDnsResolver mMockDnsResolver;
@Mock INetd mMockNetd;
@Mock NetworkStackClient mNetworkStack;
@Mock PackageManager mPackageManager;
@Mock UserManager mUserManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -333,6 +349,7 @@ public class ConnectivityServiceTest {
if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
if (Context.NOTIFICATION_SERVICE.equals(name)) return mock(NotificationManager.class);
if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
if (Context.USER_SERVICE.equals(name)) return mUserManager;
return super.getSystemService(name);
}
@@ -345,7 +362,12 @@ public class ConnectivityServiceTest {
public Resources getResources() {
return mResources;
}
}
@Override
public PackageManager getPackageManager() {
return mPackageManager;
}
}
public void waitForIdle(int timeoutMsAsInt) {
long timeoutMs = timeoutMsAsInt;
@@ -1059,7 +1081,7 @@ public class ConnectivityServiceTest {
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
IpConnectivityLog log, INetd netd, IDnsResolver dnsResolver) {
super(context, netManager, statsService, policyManager, dnsResolver, log);
super(context, netManager, statsService, policyManager, dnsResolver, log, netd);
mNetd = netd;
mLingerDelayMs = TEST_LINGER_DELAY_MS;
}
@@ -1198,6 +1220,11 @@ public class ConnectivityServiceTest {
fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
}
private static final int VPN_USER = 0;
private static final int APP1_UID = UserHandle.getUid(VPN_USER, 10100);
private static final int APP2_UID = UserHandle.getUid(VPN_USER, 10101);
private static final int VPN_UID = UserHandle.getUid(VPN_USER, 10043);
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
@@ -1205,11 +1232,17 @@ public class ConnectivityServiceTest {
MockitoAnnotations.initMocks(this);
when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics);
when(mUserManager.getUsers(eq(true))).thenReturn(
Arrays.asList(new UserInfo[] {
new UserInfo(VPN_USER, "", 0),
}));
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
if (Looper.myLooper() == null) {
Looper.prepare();
}
mockDefaultPackages();
FakeSettingsProvider.clearSettingsProvider();
mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
@@ -1262,7 +1295,24 @@ public class ConnectivityServiceTest {
FakeSettingsProvider.clearSettingsProvider();
}
private static int transportToLegacyType(int transport) {
private void mockDefaultPackages() throws Exception {
final String testPackageName = mContext.getPackageName();
final PackageInfo testPackageInfo = mContext.getPackageManager().getPackageInfo(
testPackageName, PackageManager.GET_PERMISSIONS);
when(mPackageManager.getPackagesForUid(Binder.getCallingUid())).thenReturn(
new String[] {testPackageName});
when(mPackageManager.getPackageInfoAsUser(eq(testPackageName), anyInt(),
eq(UserHandle.getCallingUserId()))).thenReturn(testPackageInfo);
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
buildPackageInfo(/* SYSTEM */ false, APP1_UID),
buildPackageInfo(/* SYSTEM */ false, APP2_UID),
buildPackageInfo(/* SYSTEM */ false, VPN_UID)
}));
}
private static int transportToLegacyType(int transport) {
switch (transport) {
case TRANSPORT_ETHERNET:
return TYPE_ETHERNET;
@@ -6120,4 +6170,166 @@ public class ConnectivityServiceTest {
assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
}
@Test
public void testFullyRoutedVpnResultsInInterfaceFilteringRules() throws Exception {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture());
assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID);
assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID);
assertTrue(mService.mPermissionMonitor.getVpnUidRanges("tun0").equals(vpnRange));
vpnNetworkAgent.disconnect();
waitForIdle();
// Disconnected VPN should have interface rules removed
verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture());
assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID);
assertNull(mService.mPermissionMonitor.getVpnUidRanges("tun0"));
}
@Test
public void testLegacyVpnDoesNotResultInInterfaceFilteringRule() throws Exception {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange);
// Legacy VPN should not have interface rules set up
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
}
@Test
public void testLocalIpv4OnlyVpnDoesNotResultInInterfaceFilteringRule()
throws Exception {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0"));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange);
// IPv6 unreachable route should not be misinterpreted as a default route
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
}
@Test
public void testVpnHandoverChangesInterfaceFilteringRule() throws Exception {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture());
assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID);
assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID);
reset(mMockNetd);
InOrder inOrder = inOrder(mMockNetd);
lp.setInterfaceName("tun1");
vpnNetworkAgent.sendLinkProperties(lp);
waitForIdle();
// VPN handover (switch to a new interface) should result in rules being updated (old rules
// removed first, then new rules added)
inOrder.verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture());
assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID);
inOrder.verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture());
assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID);
reset(mMockNetd);
lp = new LinkProperties();
lp.setInterfaceName("tun1");
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun1"));
vpnNetworkAgent.sendLinkProperties(lp);
waitForIdle();
// VPN not routing everything should no longer have interface filtering rules
verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture());
assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID);
reset(mMockNetd);
lp = new LinkProperties();
lp.setInterfaceName("tun1");
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
vpnNetworkAgent.sendLinkProperties(lp);
waitForIdle();
// Back to routing all IPv6 traffic should have filtering rules
verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture());
assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID);
}
@Test
public void testUidUpdateChangesInterfaceFilteringRule() throws Exception {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID,
Collections.singleton(vpnRange));
reset(mMockNetd);
InOrder inOrder = inOrder(mMockNetd);
// Update to new range which is old range minus APP1, i.e. only APP2
final Set<UidRange> newRanges = new HashSet<>(Arrays.asList(
new UidRange(vpnRange.start, APP1_UID - 1),
new UidRange(APP1_UID + 1, vpnRange.stop)));
vpnNetworkAgent.setUids(newRanges);
waitForIdle();
ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
// Verify old rules are removed before new rules are added
inOrder.verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture());
assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID);
inOrder.verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture());
assertContainsExactly(uidCaptor.getValue(), APP2_UID);
}
private MockNetworkAgent establishVpn(LinkProperties lp, int establishingUid,
Set<UidRange> vpnRange) {
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN, lp);
vpnNetworkAgent.getNetworkCapabilities().setEstablishingVpnAppUid(establishingUid);
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.connect();
mMockVpn.setUids(vpnRange);
vpnNetworkAgent.connect(true);
waitForIdle();
return vpnNetworkAgent;
}
private void assertContainsExactly(int[] actual, int... expected) {
int[] sortedActual = Arrays.copyOf(actual, actual.length);
int[] sortedExpected = Arrays.copyOf(expected, expected.length);
Arrays.sort(sortedActual);
Arrays.sort(sortedExpected);
assertArrayEquals(sortedExpected, sortedActual);
}
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
final PackageInfo packageInfo = new PackageInfo();
packageInfo.requestedPermissions = new String[0];
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.privateFlags = 0;
packageInfo.applicationInfo.uid = UserHandle.getUid(UserHandle.USER_SYSTEM,
UserHandle.getAppId(uid));
return packageInfo;
}
}

View File

@@ -28,6 +28,7 @@ import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.os.Process.SYSTEM_UID;
import static com.android.server.connectivity.PermissionMonitor.NETWORK;
@@ -36,13 +37,16 @@ import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
import static junit.framework.Assert.fail;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.anyInt;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,10 +57,12 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageList;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.net.INetd;
import android.net.UidRange;
import android.os.Build;
import android.os.INetworkManagementService;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.SparseIntArray;
import androidx.test.filters.SmallTest;
@@ -73,7 +79,12 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -84,10 +95,12 @@ public class PermissionMonitorTest {
private static final int MOCK_UID2 = 10086;
private static final int SYSTEM_UID1 = 1000;
private static final int SYSTEM_UID2 = 1008;
private static final int VPN_UID = 10002;
private static final String MOCK_PACKAGE1 = "appName1";
private static final String MOCK_PACKAGE2 = "appName2";
private static final String SYSTEM_PACKAGE1 = "sysName1";
private static final String SYSTEM_PACKAGE2 = "sysName2";
private static final String VPN_PACKAGE = "vpnApp";
private static final String PARTITION_SYSTEM = "system";
private static final String PARTITION_OEM = "oem";
private static final String PARTITION_PRODUCT = "product";
@@ -97,9 +110,9 @@ public class PermissionMonitorTest {
@Mock private Context mContext;
@Mock private PackageManager mPackageManager;
@Mock private INetworkManagementService mNMS;
@Mock private INetd mNetdService;
@Mock private PackageManagerInternal mMockPmi;
@Mock private UserManager mUserManager;
private PackageManagerInternal.PackageListObserver mObserver;
private PermissionMonitor mPermissionMonitor;
@@ -108,7 +121,14 @@ public class PermissionMonitorTest {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS, mNetdService));
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mUserManager.getUsers(eq(true))).thenReturn(
Arrays.asList(new UserInfo[] {
new UserInfo(MOCK_USER1, "", 0),
new UserInfo(MOCK_USER2, "", 0),
}));
mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService));
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPmi);
@@ -134,7 +154,7 @@ public class PermissionMonitorTest {
return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
}
private PackageInfo packageInfoWithPermissions(String[] permissions, String partition) {
private static PackageInfo packageInfoWithPermissions(String[] permissions, String partition) {
int[] requestedPermissionsFlags = new int[permissions.length];
for (int i = 0; i < permissions.length; i++) {
requestedPermissionsFlags[i] = REQUESTED_PERMISSION_GRANTED;
@@ -143,7 +163,7 @@ public class PermissionMonitorTest {
requestedPermissionsFlags);
}
private PackageInfo packageInfoWithPermissions(String[] permissions, String partition,
private static PackageInfo packageInfoWithPermissions(String[] permissions, String partition,
int[] requestedPermissionsFlags) {
final PackageInfo packageInfo = new PackageInfo();
packageInfo.requestedPermissions = permissions;
@@ -165,6 +185,18 @@ public class PermissionMonitorTest {
return packageInfo;
}
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid, int userId) {
final PackageInfo pkgInfo;
if (hasSystemPermission) {
pkgInfo = packageInfoWithPermissions(new String[] {CHANGE_NETWORK_STATE, NETWORK_STACK},
PARTITION_SYSTEM);
} else {
pkgInfo = packageInfoWithPermissions(new String[] {}, "");
}
pkgInfo.applicationInfo.uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
return pkgInfo;
}
@Test
public void testHasPermission() {
PackageInfo app = packageInfoWithPermissions(new String[] {}, PARTITION_SYSTEM);
@@ -245,14 +277,14 @@ public class PermissionMonitorTest {
assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
}
private class NMSMonitor {
private class NetdMonitor {
private final HashMap<Integer, Boolean> mApps = new HashMap<>();
NMSMonitor(INetworkManagementService mockNMS) throws Exception {
NetdMonitor(INetd mockNetd) throws Exception {
// Add hook to verify and track result of setPermission.
doAnswer((InvocationOnMock invocation) -> {
final Object[] args = invocation.getArguments();
final Boolean isSystem = args[0].equals("SYSTEM");
final Boolean isSystem = args[0].equals(INetd.PERMISSION_SYSTEM);
for (final int uid : (int[]) args[1]) {
// TODO: Currently, permission monitor will send duplicate commands for each uid
// corresponding to each user. Need to fix that and uncomment below test.
@@ -262,7 +294,7 @@ public class PermissionMonitorTest {
mApps.put(uid, isSystem);
}
return null;
}).when(mockNMS).setPermission(anyString(), any(int[].class));
}).when(mockNetd).networkSetPermissionForUser(anyInt(), any(int[].class));
// Add hook to verify and track result of clearPermission.
doAnswer((InvocationOnMock invocation) -> {
@@ -276,7 +308,7 @@ public class PermissionMonitorTest {
mApps.remove(uid);
}
return null;
}).when(mockNMS).clearPermission(any(int[].class));
}).when(mockNetd).networkClearPermissionForUser(any(int[].class));
}
public void expectPermission(Boolean permission, int[] users, int[] apps) {
@@ -307,7 +339,7 @@ public class PermissionMonitorTest {
@Test
public void testUserAndPackageAddRemove() throws Exception {
final NMSMonitor mNMSMonitor = new NMSMonitor(mNMS);
final NetdMonitor mNetdMonitor = new NetdMonitor(mNetdService);
// MOCK_UID1: MOCK_PACKAGE1 only has network permission.
// SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
@@ -323,48 +355,123 @@ public class PermissionMonitorTest {
// Add SYSTEM_PACKAGE2, expect only have network permission.
mPermissionMonitor.onUserAdded(MOCK_USER1);
addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
mNetdMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
// Add SYSTEM_PACKAGE1, expect permission escalate.
addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
mNetdMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
mPermissionMonitor.onUserAdded(MOCK_USER2);
mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
mNetdMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID});
addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
mNetdMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID});
mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
mNetdMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
new int[]{MOCK_UID1});
// Remove MOCK_UID1, expect no permission left for all user.
mPermissionMonitor.onPackageRemoved(MOCK_UID1);
removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
mNetdMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
// Remove SYSTEM_PACKAGE1, expect permission downgrade.
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID);
mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
mNetdMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID});
mPermissionMonitor.onUserRemoved(MOCK_USER1);
mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID});
mNetdMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID});
// Remove all packages, expect no permission left.
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID);
mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
mNetdMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID, MOCK_UID1});
// Remove last user, expect no redundant clearPermission is invoked.
mPermissionMonitor.onUserRemoved(MOCK_USER2);
mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
mNetdMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID, MOCK_UID1});
}
@Test
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
buildPackageInfo(/* SYSTEM */ true, SYSTEM_UID1, MOCK_USER1),
buildPackageInfo(/* SYSTEM */ false, MOCK_UID1, MOCK_USER1),
buildPackageInfo(/* SYSTEM */ false, MOCK_UID2, MOCK_USER1),
buildPackageInfo(/* SYSTEM */ false, VPN_UID, MOCK_USER1)
}));
when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring();
// Every app on user 0 except MOCK_UID2 are under VPN.
final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
new UidRange(0, MOCK_UID2 - 1),
new UidRange(MOCK_UID2 + 1, UserHandle.PER_USER_RANGE - 1)}));
final Set<UidRange> vpnRange2 = Collections.singleton(new UidRange(MOCK_UID2, MOCK_UID2));
// When VPN is connected, expect a rule to be set up for user app MOCK_UID1
mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange1, VPN_UID);
verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"),
aryEq(new int[] {MOCK_UID1}));
reset(mNetdService);
// When MOCK_UID1 package is uninstalled and reinstalled, expect Netd to be updated
mPermissionMonitor.onPackageRemoved(UserHandle.getUid(MOCK_USER1, MOCK_UID1));
verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, UserHandle.getUid(MOCK_USER1, MOCK_UID1));
verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"),
aryEq(new int[] {MOCK_UID1}));
reset(mNetdService);
// During VPN uid update (vpnRange1 -> vpnRange2), ConnectivityService first deletes the
// old UID rules then adds the new ones. Expect netd to be updated
mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange1, VPN_UID);
verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange2, VPN_UID);
verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"),
aryEq(new int[] {MOCK_UID2}));
reset(mNetdService);
// When VPN is disconnected, expect rules to be torn down
mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange2, VPN_UID);
verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID2}));
assertNull(mPermissionMonitor.getVpnUidRanges("tun0"));
}
@Test
public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
buildPackageInfo(true, SYSTEM_UID1, MOCK_USER1),
buildPackageInfo(false, VPN_UID, MOCK_USER1)
}));
when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring();
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange, VPN_UID);
// Newly-installed package should have uid rules added
mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, UserHandle.getUid(MOCK_USER1, MOCK_UID1));
verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"),
aryEq(new int[] {MOCK_UID1}));
// Removed package should have its uid rules removed
mPermissionMonitor.onPackageRemoved(UserHandle.getUid(MOCK_USER1, MOCK_UID1));
verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
}
// Normal package add/remove operations will trigger multiple intent for uids corresponding to
// each user. To simulate generic package operations, the onPackageAdded/Removed will need to be
// called multiple times with the uid corresponding to each user.