From 5fda65f94ce902021070ffc24159fb8f201883b3 Mon Sep 17 00:00:00 2001 From: chiachangwang Date: Wed, 25 May 2022 11:22:33 +0000 Subject: [PATCH] Update VPN app exclusion list when packages are added or removed This commit passes through package added/removed broadcasts, triggering the VPN to update the allowed/excluded UIDs. This is required because the VPN app exclusion list is stored in the keystore. The UID range for the VPN network agent should be updated when the package status is updated. As an example, consider the following sequence: - Package A is installed, and in the exclusion list. - A is uninstalled - VPN starts, A is not installed, so no UID A to exclude - A is re-installed after VPN is started - The VPN would include the app A, potentially breaking it. Bug: 231373589 Test: atest FrameworksNetTests Change-Id: I7ee32103ffdfc97e4c95007f35671cefeb9b22d7 --- .../com/android/server/VpnManagerService.java | 44 ++++++++++++++++--- .../com/android/server/connectivity/Vpn.java | 16 ++++++- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java index d3ef6bed46a09..07b6843b2febd 100644 --- a/services/core/java/com/android/server/VpnManagerService.java +++ b/services/core/java/com/android/server/VpnManagerService.java @@ -45,6 +45,7 @@ import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.INetworkManagementService; +import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.ServiceManager; @@ -131,6 +132,12 @@ public class VpnManagerService extends IVpnManager.Stub { return INetworkManagementService.Stub.asInterface( ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); } + + /** Create a VPN. */ + public Vpn createVpn(Looper looper, Context context, INetworkManagementService nms, + INetd netd, int userId) { + return new Vpn(looper, context, nms, netd, userId, new VpnProfileStore()); + } } public VpnManagerService(Context context, Dependencies deps) { @@ -688,6 +695,7 @@ public class VpnManagerService extends IVpnManager.Stub { // Listen to package add and removal events for all users. intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); @@ -738,6 +746,10 @@ public class VpnManagerService extends IVpnManager.Stub { final boolean isReplacing = intent.getBooleanExtra( Intent.EXTRA_REPLACING, false); onPackageRemoved(packageName, uid, isReplacing); + } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + final boolean isReplacing = intent.getBooleanExtra( + Intent.EXTRA_REPLACING, false); + onPackageAdded(packageName, uid, isReplacing); } else { Log.wtf(TAG, "received unexpected intent: " + action); } @@ -757,15 +769,15 @@ public class VpnManagerService extends IVpnManager.Stub { } }; - private void onUserStarted(int userId) { + @VisibleForTesting + void onUserStarted(int userId) { synchronized (mVpns) { Vpn userVpn = mVpns.get(userId); if (userVpn != null) { loge("Starting user already has a VPN"); return; } - userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, - new VpnProfileStore()); + userVpn = mDeps.createVpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId); mVpns.put(userId, userVpn); if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) { updateLockdownVpn(); @@ -842,7 +854,8 @@ public class VpnManagerService extends IVpnManager.Stub { } } - private void onPackageRemoved(String packageName, int uid, boolean isReplacing) { + @VisibleForTesting + void onPackageRemoved(String packageName, int uid, boolean isReplacing) { if (TextUtils.isEmpty(packageName) || uid < 0) { Log.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid); return; @@ -851,15 +864,34 @@ public class VpnManagerService extends IVpnManager.Stub { final int userId = UserHandle.getUserId(uid); synchronized (mVpns) { final Vpn vpn = mVpns.get(userId); - if (vpn == null) { + if (vpn == null || isReplacing) { return; } // Legacy always-on VPN won't be affected since the package name is not set. - if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) { + if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) { log("Removing always-on VPN package " + packageName + " for user " + userId); vpn.setAlwaysOnPackage(null, false, null); } + + vpn.refreshPlatformVpnAppExclusionList(); + } + } + + @VisibleForTesting + void onPackageAdded(String packageName, int uid, boolean isReplacing) { + if (TextUtils.isEmpty(packageName) || uid < 0) { + Log.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid); + return; + } + + final int userId = UserHandle.getUserId(uid); + synchronized (mVpns) { + final Vpn vpn = mVpns.get(userId); + + if (vpn != null && !isReplacing) { + vpn.refreshPlatformVpnAppExclusionList(); + } } } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 845e932cdff91..030dbada1891f 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -4132,6 +4132,20 @@ public class Vpn { @NonNull List excludedApps) { enforceNotRestrictedUser(); if (!storeAppExclusionList(packageName, excludedApps)) return false; + + updateAppExclusionList(excludedApps); + + return true; + } + + /** + * Triggers an update of the VPN network's excluded UIDs if a VPN is running. + */ + public synchronized void refreshPlatformVpnAppExclusionList() { + updateAppExclusionList(getAppExclusionList(mPackage)); + } + + private synchronized void updateAppExclusionList(@NonNull List excludedApps) { // Re-build and update NetworkCapabilities via NetworkAgent. if (mNetworkAgent != null) { // Only update the platform VPN @@ -4144,8 +4158,6 @@ public class Vpn { mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); } } - - return true; } /**