Merge "Add separate user consent for Platform VPNs"

This commit is contained in:
Benedict Wong
2020-02-04 23:56:16 +00:00
committed by Gerrit Code Review
4 changed files with 75 additions and 15 deletions

View File

@@ -116,7 +116,7 @@ interface IConnectivityManager
boolean prepareVpn(String oldPackage, String newPackage, int userId); boolean prepareVpn(String oldPackage, String newPackage, int userId);
void setVpnPackageAuthorization(String packageName, int userId, boolean authorized); void setVpnPackageAuthorization(String packageName, int userId, int vpnType);
ParcelFileDescriptor establishVpn(in VpnConfig config); ParcelFileDescriptor establishVpn(in VpnConfig config);

View File

@@ -112,6 +112,7 @@ import android.net.SocketKeepalive;
import android.net.TetheringManager; import android.net.TetheringManager;
import android.net.UidRange; import android.net.UidRange;
import android.net.Uri; import android.net.Uri;
import android.net.VpnManager;
import android.net.VpnService; import android.net.VpnService;
import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent; import android.net.metrics.NetworkEvent;
@@ -4310,7 +4311,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
throwIfLockdownEnabled(); throwIfLockdownEnabled();
Vpn vpn = mVpns.get(userId); Vpn vpn = mVpns.get(userId);
if (vpn != null) { if (vpn != null) {
return vpn.prepare(oldPackage, newPackage, false); return vpn.prepare(oldPackage, newPackage, VpnManager.TYPE_VPN_SERVICE);
} else { } else {
return false; return false;
} }
@@ -4318,26 +4319,29 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
/** /**
* Set whether the VPN package has the ability to launch VPNs without user intervention. * Set whether the VPN package has the ability to launch VPNs without user intervention. This
* This method is used by system-privileged apps. * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
* VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId}, * class. If the caller is not {@code userId}, {@link
* {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required. * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
* *
* @param packageName The package for which authorization state should change. * @param packageName The package for which authorization state should change.
* @param userId User for whom {@code packageName} is installed. * @param userId User for whom {@code packageName} is installed.
* @param authorized {@code true} if this app should be able to start a VPN connection without * @param authorized {@code true} if this app should be able to start a VPN connection without
* explicit user approval, {@code false} if not. * explicit user approval, {@code false} if not.
* * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
* permissions should be granted. When unauthorizing an app, {@link
* VpnManager.TYPE_VPN_NONE} should be used.
* @hide * @hide
*/ */
@Override @Override
public void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) { public void setVpnPackageAuthorization(
String packageName, int userId, @VpnManager.VpnType int vpnType) {
enforceCrossUserPermission(userId); enforceCrossUserPermission(userId);
synchronized (mVpns) { synchronized (mVpns) {
Vpn vpn = mVpns.get(userId); Vpn vpn = mVpns.get(userId);
if (vpn != null) { if (vpn != null) {
vpn.setPackageAuthorization(packageName, authorized); vpn.setPackageAuthorization(packageName, vpnType);
} }
} }
} }
@@ -7239,7 +7243,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final String alwaysOnPackage = getAlwaysOnVpnPackage(userId); final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
if (alwaysOnPackage != null) { if (alwaysOnPackage != null) {
setAlwaysOnVpnPackage(userId, null, false, null); setAlwaysOnVpnPackage(userId, null, false, null);
setVpnPackageAuthorization(alwaysOnPackage, userId, false); setVpnPackageAuthorization(alwaysOnPackage, userId, VpnManager.TYPE_VPN_NONE);
} }
// Turn Always-on VPN off // Turn Always-on VPN off
@@ -7262,7 +7266,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else { } else {
// Prevent this app (packagename = vpnConfig.user) from initiating // Prevent this app (packagename = vpnConfig.user) from initiating
// VPN connections in the future without user intervention. // VPN connections in the future without user intervention.
setVpnPackageAuthorization(vpnConfig.user, userId, false); setVpnPackageAuthorization(
vpnConfig.user, userId, VpnManager.TYPE_VPN_NONE);
prepareVpn(null, VpnConfig.LEGACY_VPN, userId); prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
} }

View File

@@ -16,6 +16,7 @@
package android.net; package android.net;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
@@ -24,6 +25,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Intent;
import android.test.mock.MockContext; import android.test.mock.MockContext;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
@@ -78,7 +81,13 @@ public class VpnManagerTest {
when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(false); when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(false);
// Expect intent to be returned, as consent has not already been granted. // Expect intent to be returned, as consent has not already been granted.
assertNotNull(mVpnManager.provisionVpnProfile(profile)); final Intent intent = mVpnManager.provisionVpnProfile(profile);
assertNotNull(intent);
final ComponentName expectedComponentName =
ComponentName.unflattenFromString(
"com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog");
assertEquals(expectedComponentName, intent.getComponent());
verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME)); verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
} }

View File

@@ -63,6 +63,7 @@ import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.DetailedState;
import android.net.UidRange; import android.net.UidRange;
import android.net.VpnManager;
import android.net.VpnService; import android.net.VpnService;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.os.Bundle; import android.os.Bundle;
@@ -471,12 +472,12 @@ public class VpnTest {
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser)); order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
// When a new VPN package is set the rules should change to cover that package. // When a new VPN package is set the rules should change to cover that package.
vpn.prepare(null, PKGS[0], false /* isPlatformVpn */); vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser)); order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0)); order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
// When that VPN package is unset, everything should be undone again in reverse. // When that VPN package is unset, everything should be undone again in reverse.
vpn.prepare(null, VpnConfig.LEGACY_VPN, false /* isPlatformVpn */); vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0)); order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser)); order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
} }
@@ -817,6 +818,51 @@ public class VpnTest {
eq(TEST_VPN_PKG)); eq(TEST_VPN_PKG));
} }
@Test
public void testSetPackageAuthorizationVpnService() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_SERVICE));
verify(mAppOps)
.setMode(
eq(AppOpsManager.OP_ACTIVATE_VPN),
eq(Process.myUid()),
eq(TEST_VPN_PKG),
eq(AppOpsManager.MODE_ALLOWED));
}
@Test
public void testSetPackageAuthorizationPlatformVpn() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_PLATFORM));
verify(mAppOps)
.setMode(
eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
eq(Process.myUid()),
eq(TEST_VPN_PKG),
eq(AppOpsManager.MODE_ALLOWED));
}
@Test
public void testSetPackageAuthorizationRevokeAuthorization() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_NONE));
verify(mAppOps)
.setMode(
eq(AppOpsManager.OP_ACTIVATE_VPN),
eq(Process.myUid()),
eq(TEST_VPN_PKG),
eq(AppOpsManager.MODE_IGNORED));
verify(mAppOps)
.setMode(
eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
eq(Process.myUid()),
eq(TEST_VPN_PKG),
eq(AppOpsManager.MODE_IGNORED));
}
/** /**
* Mock some methods of vpn object. * Mock some methods of vpn object.
*/ */