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:
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user