summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Robin Lee <rgl@google.com> 2015-05-12 18:14:58 +0100
committer Robin Lee <rgl@google.com> 2015-05-18 23:35:31 +0100
commit3b3dd942ec6a0beaccd1cef0723d72786435d8f3 (patch)
tree6bf3cc9895290fa3ec9a509fb87d30afb649697e
parent0125d76fa37d26a29f06371ff349546e21cd3f4d (diff)
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
-rw-r--r--core/java/android/net/IConnectivityManager.aidl6
-rw-r--r--core/java/android/net/VpnService.java9
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java7
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java8
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java79
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java12
6 files changed, 82 insertions, 39 deletions
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 77200a57e498..c1b4a1fb53bc 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -106,13 +106,13 @@ interface IConnectivityManager
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);
- VpnConfig getVpnConfig();
+ VpnConfig getVpnConfig(int userId);
void startLegacyVpn(in VpnProfile profile);
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index a0e65eba5673..2bb48b33f385 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -156,7 +156,7 @@ public class VpnService extends Service {
*/
public static Intent prepare(Context context) {
try {
- if (getService().prepareVpn(context.getPackageName(), null)) {
+ if (getService().prepareVpn(context.getPackageName(), null, UserHandle.myUserId())) {
return null;
}
} catch (RemoteException e) {
@@ -182,10 +182,11 @@ public class VpnService extends Service {
String packageName = context.getPackageName();
try {
// Only prepare if we're not already prepared.
- if (!cm.prepareVpn(packageName, null)) {
- cm.prepareVpn(null, packageName);
+ int userId = UserHandle.myUserId();
+ if (!cm.prepareVpn(packageName, null, userId)) {
+ cm.prepareVpn(null, packageName, userId);
}
- cm.setVpnPackageAuthorization(true);
+ cm.setVpnPackageAuthorization(packageName, userId, true);
} catch (RemoteException e) {
// ignore
}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index ea8b2ec7bb42..48e05823f625 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -21,6 +21,7 @@ import android.content.DialogInterface;
import android.graphics.drawable.Drawable;
import android.net.IConnectivityManager;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.util.Log;
@@ -50,7 +51,7 @@ public class ConfirmDialog extends AlertActivity
mService = IConnectivityManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
- if (mService.prepareVpn(mPackage, null)) {
+ if (mService.prepareVpn(mPackage, null, UserHandle.myUserId())) {
setResult(RESULT_OK);
finish();
return;
@@ -94,10 +95,10 @@ public class ConfirmDialog extends AlertActivity
@Override
public void onClick(DialogInterface dialog, int which) {
try {
- if (mService.prepareVpn(null, mPackage)) {
+ if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
// Authorize this app to initiate VPN connections in the future without user
// intervention.
- mService.setVpnPackageAuthorization(true);
+ mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), true);
setResult(RESULT_OK);
}
} catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index cc8500ae34a8..76b234694790 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -23,6 +23,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
@@ -63,7 +64,7 @@ public class ManageDialog extends AlertActivity implements
mService = IConnectivityManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
- mConfig = mService.getVpnConfig();
+ mConfig = mService.getVpnConfig(UserHandle.myUserId());
// mConfig can be null if we are a restricted user, in that case don't show this dialog
if (mConfig == null) {
@@ -120,10 +121,11 @@ public class ManageDialog extends AlertActivity implements
if (which == DialogInterface.BUTTON_POSITIVE) {
mConfig.configureIntent.send();
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
+ final int myUserId = UserHandle.myUserId();
if (mConfig.legacy) {
- mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
} else {
- mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
+ mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
}
}
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 16b3dcfa8b85..5c0e3287d61d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -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() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -2941,29 +2957,48 @@ public class ConnectivityService extends IConnectivityManager.Stub
/**
* 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
*/
@Override
- public boolean prepareVpn(String oldPackage, String newPackage) {
+ public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
+ int userId) {
+ enforceCrossUserPermission(userId);
throwIfLockdownEnabled();
- int user = UserHandle.getUserId(Binder.getCallingUid());
+
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
- * user intervention. This method is used by system-privileged apps.
- * Permissions are checked in Vpn class.
+ * Set whether the VPN package has the ability to launch VPNs without user intervention.
+ * This method is used by system-privileged apps.
+ * 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
*/
@Override
- public void setVpnPackageAuthorization(boolean authorized) {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ public void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) {
+ enforceCrossUserPermission(userId);
+
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
- * not available in ConnectivityManager.
+ * Returns the information of the ongoing VPN for {@code userId}. This method is used by
+ * VpnDialogs and not available in ConnectivityManager.
* Permissions are checked in Vpn class.
* @hide
*/
@Override
- public VpnConfig getVpnConfig() {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ public VpnConfig getVpnConfig(int userId) {
+ enforceCrossUserPermission(userId);
synchronized(mVpns) {
- return mVpns.get(user).getVpnConfig();
+ return mVpns.get(userId).getVpnConfig();
}
}
@@ -4556,6 +4591,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public void factoryReset() {
enforceConnectivityInternalPermission();
+ final int userId = UserHandle.getCallingUserId();
+
// Turn airplane mode off
setAirplaneMode(false);
@@ -4565,16 +4602,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
// Turn VPN off
- VpnConfig vpnConfig = getVpnConfig();
+ VpnConfig vpnConfig = getVpnConfig(userId);
if (vpnConfig != null) {
if (vpnConfig.legacy) {
- prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
} else {
- // Prevent this app from initiating VPN connections in the future without
- // user intervention.
- setVpnPackageAuthorization(false);
+ // Prevent this app (packagename = vpnConfig.user) from initiating VPN connections
+ // in the future without user intervention.
+ setVpnPackageAuthorization(vpnConfig.user, userId, false);
- prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN);
+ prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN, userId);
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index aeecdf3cab02..e1ec8a636e72 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -298,13 +298,15 @@ public class Vpn {
}
/**
- * Set whether the current package has the ability to launch VPNs without user intervention.
+ * Set whether a package has the ability to launch VPNs without user intervention.
*/
- public void setPackageAuthorization(boolean authorized) {
+ public void setPackageAuthorization(String packageName, boolean authorized) {
// Check if the caller is authorized.
enforceControlPermission();
- if (mPackage == null || VpnConfig.LEGACY_VPN.equals(mPackage)) {
+ int uid = getAppUid(packageName, mUserHandle);
+ if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
+ // Authorization for nonexistent packages (or fake ones) can't be updated.
return;
}
@@ -312,10 +314,10 @@ public class Vpn {
try {
AppOpsManager appOps =
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, mOwnerUID, mPackage,
+ appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
} catch (Exception e) {
- Log.wtf(TAG, "Failed to set app ops for package " + mPackage, e);
+ Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
} finally {
Binder.restoreCallingIdentity(token);
}