Support cross-user VPN calls (with permission)

Settings and SystemUI need to act on other users than USER_OWNER.

This is gated by INTERACT_ACROSS_USERS_FULL in addition to the existing
CONTROL_VPN checks, so the number of processes able to interfere with
other profiles' VPNs should be quite small.

Bug: 20692490
Bug: 20747154
Bug: 20872408
Change-Id: I6e5d7220f73435bec350719e7b4715935caf4e19
This commit is contained in:
Robin Lee
2015-05-12 18:14:58 +01:00
parent 2c39d1313a
commit 1e71fc8356
2 changed files with 61 additions and 24 deletions

View File

@@ -106,13 +106,13 @@ interface IConnectivityManager
ProxyInfo getDefaultProxy(); ProxyInfo getDefaultProxy();
boolean prepareVpn(String oldPackage, String newPackage); boolean prepareVpn(String oldPackage, String newPackage, int userId);
void setVpnPackageAuthorization(boolean authorized); void setVpnPackageAuthorization(String packageName, int userId, boolean authorized);
ParcelFileDescriptor establishVpn(in VpnConfig config); ParcelFileDescriptor establishVpn(in VpnConfig config);
VpnConfig getVpnConfig(); VpnConfig getVpnConfig(int userId);
void startLegacyVpn(in VpnProfile profile); void startLegacyVpn(in VpnProfile profile);

View File

@@ -1406,6 +1406,22 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
}; };
/**
* Require that the caller is either in the same user or has appropriate permission to interact
* across users.
*
* @param userId Target user for whatever operation the current IPC is supposed to perform.
*/
private void enforceCrossUserPermission(int userId) {
if (userId == UserHandle.getCallingUserId()) {
// Not a cross-user call.
return;
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"ConnectivityService");
}
private void enforceInternetPermission() { private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission( mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET, android.Manifest.permission.INTERNET,
@@ -2941,29 +2957,48 @@ public class ConnectivityService extends IConnectivityManager.Stub
/** /**
* Prepare for a VPN application. * Prepare for a VPN application.
* Permissions are checked in Vpn class. * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
* {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
*
* @param oldPackage Package name of the application which currently controls VPN, which will
* be replaced. If there is no such application, this should should either be
* {@code null} or {@link VpnConfig.LEGACY_VPN}.
* @param newPackage Package name of the application which should gain control of VPN, or
* {@code null} to disable.
* @param userId User for whom to prepare the new VPN.
*
* @hide * @hide
*/ */
@Override @Override
public boolean prepareVpn(String oldPackage, String newPackage) { public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
int userId) {
enforceCrossUserPermission(userId);
throwIfLockdownEnabled(); throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized(mVpns) { synchronized(mVpns) {
return mVpns.get(user).prepare(oldPackage, newPackage); return mVpns.get(userId).prepare(oldPackage, newPackage);
} }
} }
/** /**
* Set whether the current VPN package has the ability to launch VPNs without * Set whether the VPN package has the ability to launch VPNs without user intervention.
* user intervention. This method is used by system-privileged apps. * This method is used by system-privileged apps.
* Permissions are checked in Vpn class. * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
* {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
*
* @param packageName The package for which authorization state should change.
* @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
* explicit user approval, {@code false} if not.
*
* @hide * @hide
*/ */
@Override @Override
public void setVpnPackageAuthorization(boolean authorized) { public void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) {
int user = UserHandle.getUserId(Binder.getCallingUid()); enforceCrossUserPermission(userId);
synchronized(mVpns) { synchronized(mVpns) {
mVpns.get(user).setPackageAuthorization(authorized); mVpns.get(userId).setPackageAuthorization(packageName, authorized);
} }
} }
@@ -3065,16 +3100,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
/** /**
* Returns the information of the ongoing VPN. This method is used by VpnDialogs and * Returns the information of the ongoing VPN for {@code userId}. This method is used by
* not available in ConnectivityManager. * VpnDialogs and not available in ConnectivityManager.
* Permissions are checked in Vpn class. * Permissions are checked in Vpn class.
* @hide * @hide
*/ */
@Override @Override
public VpnConfig getVpnConfig() { public VpnConfig getVpnConfig(int userId) {
int user = UserHandle.getUserId(Binder.getCallingUid()); enforceCrossUserPermission(userId);
synchronized(mVpns) { synchronized(mVpns) {
return mVpns.get(user).getVpnConfig(); return mVpns.get(userId).getVpnConfig();
} }
} }
@@ -4556,6 +4591,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override @Override
public void factoryReset() { public void factoryReset() {
enforceConnectivityInternalPermission(); enforceConnectivityInternalPermission();
final int userId = UserHandle.getCallingUserId();
// Turn airplane mode off // Turn airplane mode off
setAirplaneMode(false); setAirplaneMode(false);
@@ -4565,16 +4602,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
// Turn VPN off // Turn VPN off
VpnConfig vpnConfig = getVpnConfig(); VpnConfig vpnConfig = getVpnConfig(userId);
if (vpnConfig != null) { if (vpnConfig != null) {
if (vpnConfig.legacy) { if (vpnConfig.legacy) {
prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN); prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
} else { } else {
// Prevent this app from initiating VPN connections in the future without // Prevent this app (packagename = vpnConfig.user) from initiating VPN connections
// user intervention. // in the future without user intervention.
setVpnPackageAuthorization(false); setVpnPackageAuthorization(vpnConfig.user, userId, false);
prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN); prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN, userId);
} }
} }
} }