diff options
-rw-r--r-- | core/java/android/net/VpnService.java | 30 | ||||
-rw-r--r-- | core/java/com/android/internal/net/VpnConfig.java | 6 | ||||
-rw-r--r-- | services/core/java/com/android/server/connectivity/Vpn.java | 121 |
3 files changed, 122 insertions, 35 deletions
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index b023b0b..050be40 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -24,6 +24,7 @@ import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.net.NetworkUtils; import android.os.Binder; @@ -32,6 +33,7 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import com.android.internal.net.VpnConfig; @@ -518,6 +520,16 @@ public class VpnService extends Service { return this; } + private void verifyApp(String packageName) throws PackageManager.NameNotFoundException { + IPackageManager pm = IPackageManager.Stub.asInterface( + ServiceManager.getService("package")); + try { + pm.getApplicationInfo(packageName, 0, UserHandle.getCallingUserId()); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + } + /** * Adds an application that's allowed to access the VPN connection. * @@ -541,7 +553,14 @@ public class VpnService extends Service { */ public Builder addAllowedApplication(String packageName) throws PackageManager.NameNotFoundException { - // TODO + if (mConfig.disallowedApplications != null) { + throw new UnsupportedOperationException("addDisallowedApplication already called"); + } + verifyApp(packageName); + if (mConfig.allowedApplications == null) { + mConfig.allowedApplications = new ArrayList<String>(); + } + mConfig.allowedApplications.add(packageName); return this; } @@ -566,7 +585,14 @@ public class VpnService extends Service { */ public Builder addDisallowedApplication(String packageName) throws PackageManager.NameNotFoundException { - // TODO + if (mConfig.allowedApplications != null) { + throw new UnsupportedOperationException("addAllowedApplication already called"); + } + verifyApp(packageName); + if (mConfig.disallowedApplications == null) { + mConfig.disallowedApplications = new ArrayList<String>(); + } + mConfig.disallowedApplications.add(packageName); return this; } diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index 9c3f419..3d016be 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -90,6 +90,8 @@ public class VpnConfig implements Parcelable { public List<RouteInfo> routes = new ArrayList<RouteInfo>(); public List<String> dnsServers; public List<String> searchDomains; + public List<String> allowedApplications; + public List<String> disallowedApplications; public PendingIntent configureIntent; public long startTime = -1; public boolean legacy; @@ -151,6 +153,8 @@ public class VpnConfig implements Parcelable { out.writeTypedList(routes); out.writeStringList(dnsServers); out.writeStringList(searchDomains); + out.writeStringList(allowedApplications); + out.writeStringList(disallowedApplications); out.writeParcelable(configureIntent, flags); out.writeLong(startTime); out.writeInt(legacy ? 1 : 0); @@ -173,6 +177,8 @@ public class VpnConfig implements Parcelable { in.readTypedList(config.routes, RouteInfo.CREATOR); config.dnsServers = in.createStringArrayList(); config.searchDomains = in.createStringArrayList(); + config.allowedApplications = in.createStringArrayList(); + config.disallowedApplications = in.createStringArrayList(); config.configureIntent = in.readParcelable(null); config.startTime = in.readLong(); config.legacy = in.readInt() != 0; diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 94aa421..afc2a39 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -17,6 +17,7 @@ package com.android.server.connectivity; import static android.Manifest.permission.BIND_VPN_SERVICE; +import static android.os.UserHandle.PER_USER_RANGE; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; @@ -84,6 +85,8 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; /** @@ -120,38 +123,39 @@ public class Vpn { private List<UidRange> mVpnUsers = null; private BroadcastReceiver mUserIntentReceiver = null; - private final int mUserId; + // Handle of user initiating VPN. + private final int mUserHandle; public Vpn(Looper looper, Context context, INetworkManagementService netService, - IConnectivityManager connService, int userId) { + IConnectivityManager connService, int userHandle) { mContext = context; mNetd = netService; mConnService = connService; - mUserId = userId; + mUserHandle = userHandle; mLooper = looper; mPackage = VpnConfig.LEGACY_VPN; - mOwnerUID = getAppUid(mPackage); + mOwnerUID = getAppUid(mPackage, mUserHandle); try { netService.registerObserver(mObserver); } catch (RemoteException e) { Log.wtf(TAG, "Problem registering observer", e); } - if (userId == UserHandle.USER_OWNER) { + if (userHandle == UserHandle.USER_OWNER) { // Owner's VPN also needs to handle restricted users mUserIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, + final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - if (userId == UserHandle.USER_NULL) return; + if (userHandle == UserHandle.USER_NULL) return; if (Intent.ACTION_USER_ADDED.equals(action)) { - onUserAdded(userId); + onUserAdded(userHandle); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { - onUserRemoved(userId); + onUserRemoved(userHandle); } } }; @@ -272,7 +276,7 @@ public class Vpn { Log.i(TAG, "Switched from " + mPackage + " to " + newPackage); mPackage = newPackage; - mOwnerUID = getAppUid(newPackage); + mOwnerUID = getAppUid(newPackage, mUserHandle); token = Binder.clearCallingIdentity(); try { mNetd.allowProtect(mOwnerUID); @@ -320,14 +324,14 @@ public class Vpn { packageName) == AppOpsManager.MODE_ALLOWED; } - private int getAppUid(String app) { + private int getAppUid(String app, int userHandle) { if (VpnConfig.LEGACY_VPN.equals(app)) { return Process.myUid(); } PackageManager pm = mContext.getPackageManager(); int result; try { - result = pm.getPackageUid(app, mUserId); + result = pm.getPackageUid(app, userHandle); } catch (NameNotFoundException e) { result = -1; } @@ -400,9 +404,9 @@ public class Vpn { mNetworkAgent.blockAddressFamily(AF_INET6); } - addVpnUserLocked(mUserId); + addVpnUserLocked(mUserHandle); // If we are owner assign all Restricted Users to this VPN - if (mUserId == UserHandle.USER_OWNER) { + if (mUserHandle == UserHandle.USER_OWNER) { token = Binder.clearCallingIdentity(); List<UserInfo> users; try { @@ -459,13 +463,13 @@ public class Vpn { long token = Binder.clearCallingIdentity(); try { // Restricted users are not allowed to create VPNs, they are tied to Owner - UserInfo user = mgr.getUserInfo(mUserId); + UserInfo user = mgr.getUserInfo(mUserHandle); if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) { throw new SecurityException("Restricted users cannot establish VPNs"); } ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent, - null, 0, mUserId); + null, 0, mUserHandle); if (info == null) { throw new SecurityException("Cannot find " + config.user); } @@ -504,7 +508,7 @@ public class Vpn { } Connection connection = new Connection(); if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE, - new UserHandle(mUserId))) { + new UserHandle(mUserHandle))) { throw new IllegalStateException("Cannot bind " + config.user); } @@ -562,41 +566,92 @@ public class Vpn { return mVpnUsers != null; } + // Note: Return type guarantees results are deduped and sorted, which callers require. + private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) { + SortedSet<Integer> uids = new TreeSet<Integer>(); + for (String app : packageNames) { + int uid = getAppUid(app, userHandle); + if (uid != -1) uids.add(uid); + } + return uids; + } + // Note: This function adds to mVpnUsers but does not publish list to NetworkAgent. - private void addVpnUserLocked(int user) { + private void addVpnUserLocked(int userHandle) { if (!isRunningLocked()) { throw new IllegalStateException("VPN is not active"); } - // add the user - mVpnUsers.add(UidRange.createForUser(user)); + if (mConfig.allowedApplications != null) { + // Add ranges covering all UIDs for allowedApplications. + int start = -1, stop = -1; + for (int uid : getAppsUids(mConfig.allowedApplications, userHandle)) { + if (start == -1) { + start = uid; + } else if (uid != stop + 1) { + mVpnUsers.add(new UidRange(start, stop)); + start = uid; + } + stop = uid; + } + if (start != -1) mVpnUsers.add(new UidRange(start, stop)); + } else if (mConfig.disallowedApplications != null) { + // Add all ranges for user skipping UIDs for disallowedApplications. + final UidRange userRange = UidRange.createForUser(userHandle); + int start = userRange.start; + for (int uid : getAppsUids(mConfig.disallowedApplications, userHandle)) { + if (uid == start) { + start++; + } else { + mVpnUsers.add(new UidRange(start, uid - 1)); + start = uid + 1; + } + } + if (start <= userRange.stop) mVpnUsers.add(new UidRange(start, userRange.stop)); + } else { + // Add all UIDs for the user. + mVpnUsers.add(UidRange.createForUser(userHandle)); + } prepareStatusIntent(); } - private void removeVpnUserLocked(int user) { + // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that + // apply to userHandle. + private List<UidRange> uidRangesForUser(int userHandle) { + final UidRange userRange = UidRange.createForUser(userHandle); + final List<UidRange> ranges = new ArrayList<UidRange>(); + for (UidRange range : mVpnUsers) { + if (range.start >= userRange.start && range.stop <= userRange.stop) { + ranges.add(range); + } + } + return ranges; + } + + private void removeVpnUserLocked(int userHandle) { if (!isRunningLocked()) { throw new IllegalStateException("VPN is not active"); } - UidRange uidRange = UidRange.createForUser(user); + final List<UidRange> ranges = uidRangesForUser(userHandle); if (mNetworkAgent != null) { - mNetworkAgent.removeUidRanges(new UidRange[] { uidRange }); + mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()])); } - mVpnUsers.remove(uidRange); + mVpnUsers.removeAll(ranges); mStatusIntent = null; } - private void onUserAdded(int userId) { + private void onUserAdded(int userHandle) { // If the user is restricted tie them to the owner's VPN synchronized(Vpn.this) { UserManager mgr = UserManager.get(mContext); - UserInfo user = mgr.getUserInfo(userId); + UserInfo user = mgr.getUserInfo(userHandle); if (user.isRestricted()) { try { - addVpnUserLocked(userId); + addVpnUserLocked(userHandle); if (mNetworkAgent != null) { - UidRange uidRange = UidRange.createForUser(userId); - mNetworkAgent.addUidRanges(new UidRange[] { uidRange }); + final List<UidRange> ranges = uidRangesForUser(userHandle); + mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()])); } } catch (Exception e) { Log.wtf(TAG, "Failed to add restricted user to owner", e); @@ -605,14 +660,14 @@ public class Vpn { } } - private void onUserRemoved(int userId) { + private void onUserRemoved(int userHandle) { // clean up if restricted synchronized(Vpn.this) { UserManager mgr = UserManager.get(mContext); - UserInfo user = mgr.getUserInfo(userId); + UserInfo user = mgr.getUserInfo(userHandle); if (user.isRestricted()) { try { - removeVpnUserLocked(userId); + removeVpnUserLocked(userHandle); } catch (Exception e) { Log.wtf(TAG, "Failed to remove restricted user to owner", e); } @@ -787,7 +842,7 @@ public class Vpn { throw new IllegalStateException("KeyStore isn't unlocked"); } UserManager mgr = UserManager.get(mContext); - UserInfo user = mgr.getUserInfo(mUserId); + UserInfo user = mgr.getUserInfo(mUserHandle); if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) { throw new SecurityException("Restricted users cannot establish VPNs"); } |