diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index 986953264f..802a99fcea 100644 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -7954,6 +7954,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { final boolean curMetered = nai.networkCapabilities.isMetered(); maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mVpnBlockedUidRanges, newVpnBlockedUidRanges); + + if (nai.isVPN()) { + updateVpnFiltering(nai.linkProperties, nai.linkProperties, nai, true /* force */); + } }); mVpnBlockedUidRanges = newVpnBlockedUidRanges; @@ -10090,11 +10094,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { // the LinkProperties for the network are accurate. networkAgent.clatd.fixupLinkProperties(oldLp, newLp); - updateInterfaces(newLp, oldLp, netId, networkAgent); + final boolean anyIfaceChanges = + updateInterfaces(newLp, oldLp, netId, networkAgent); + + // If any interface names changed, ensure VPN filtering is updated so that ingress filtering + // uses the most up-to-date allowed interface. + final boolean shouldForceUpdateVpnFiltering = networkAgent.isVPN() && anyIfaceChanges; // update filtering rules, need to happen after the interface update so netd knows about the // new interface (the interface name -> index map becomes initialized) - updateVpnFiltering(newLp, oldLp, networkAgent); + updateVpnFiltering(newLp, oldLp, networkAgent, shouldForceUpdateVpnFiltering); updateIngressToVpnAddressFiltering(newLp, oldLp, networkAgent); @@ -10247,7 +10256,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - private void updateInterfaces(final @NonNull LinkProperties newLp, + /** Return whether there were any added or removed interface names. */ + private boolean updateInterfaces(final @NonNull LinkProperties newLp, final @Nullable LinkProperties oldLp, final int netId, final @NonNull NetworkAgentInfo nai) { final CompareResult interfaceDiff = new CompareResult<>( @@ -10281,6 +10291,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { loge("Exception removing interface: " + e); } } + return !(interfaceDiff.added.isEmpty() && interfaceDiff.removed.isEmpty()); } /** @@ -10569,7 +10580,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } private void updateVpnFiltering(@NonNull LinkProperties newLp, @Nullable LinkProperties oldLp, - @NonNull NetworkAgentInfo nai) { + @NonNull NetworkAgentInfo nai, boolean force) { final String oldIface = getVpnIsolationInterface(nai, nai.networkCapabilities, oldLp); final String newIface = getVpnIsolationInterface(nai, nai.networkCapabilities, newLp); final boolean wasFiltering = requiresVpnAllowRule(nai, oldLp, oldIface); @@ -10580,7 +10591,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { return; } - if (Objects.equals(oldIface, newIface) && (wasFiltering == needsFiltering)) { + if (!force && Objects.equals(oldIface, newIface) && (wasFiltering == needsFiltering)) { // Nothing changed. return; } @@ -10597,9 +10608,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { // old rules are being removed. if (wasFiltering) { mPermissionMonitor.onVpnUidRangesRemoved(oldIface, ranges, vpnAppUid); + mPermissionMonitor.updateVpnLockdownUidInterfaceRules(oldLp.getInterfaceName(), ranges, + vpnAppUid, false /* add */); } if (needsFiltering) { mPermissionMonitor.onVpnUidRangesAdded(newIface, ranges, vpnAppUid); + mPermissionMonitor.updateVpnLockdownUidInterfaceRules(newLp.getInterfaceName(), ranges, + vpnAppUid, true /* add */); } } @@ -11339,9 +11354,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (wasFiltering && !prevRanges.isEmpty()) { mPermissionMonitor.onVpnUidRangesRemoved(oldIface, prevRanges, prevNc.getOwnerUid()); + mPermissionMonitor.updateVpnLockdownUidInterfaceRules( + nai.linkProperties.getInterfaceName(), prevRanges, prevNc.getOwnerUid(), + false /* add */); } if (shouldFilter && !newRanges.isEmpty()) { mPermissionMonitor.onVpnUidRangesAdded(newIface, newRanges, newNc.getOwnerUid()); + mPermissionMonitor.updateVpnLockdownUidInterfaceRules( + nai.linkProperties.getInterfaceName(), newRanges, newNc.getOwnerUid(), + true /* add */); } } catch (Exception e) { // Never crash! diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java index 5de5f61ff1..2686abe635 100755 --- a/service/src/com/android/server/connectivity/PermissionMonitor.java +++ b/service/src/com/android/server/connectivity/PermissionMonitor.java @@ -1025,6 +1025,7 @@ public class PermissionMonitor { // packets to that UID is fine. final Set changedUids = intersectUids(rangesToAdd, mAllApps); removeBypassingUids(changedUids, vpnAppUid); + removeVpnLockdownUids(iface, changedUids); updateVpnUidsInterfaceRules(iface, changedUids, true /* add */); if (mVpnInterfaceUidRanges.containsKey(iface)) { mVpnInterfaceUidRanges.get(iface).addAll(rangesToAdd); @@ -1047,6 +1048,7 @@ public class PermissionMonitor { // ranges and update Netd about them. final Set changedUids = intersectUids(rangesToRemove, mAllApps); removeBypassingUids(changedUids, vpnAppUid); + removeVpnLockdownUids(iface, changedUids); updateVpnUidsInterfaceRules(iface, changedUids, false /* add */); Set existingRanges = mVpnInterfaceUidRanges.getOrDefault(iface, null); if (existingRanges == null) { @@ -1059,6 +1061,27 @@ public class PermissionMonitor { } } + /** + * Called when a set of UID ranges are added/removed from an active VPN network and when + * UID ranges under VPN Lockdown are updated + * + * @param iface The VPN network's interface name. Null iface indicates that the interface is not + * available. + * @param rangesToModify Existing UID ranges to be modified on the VPN network + * @param add {@code true} to add the UID rules, {@code false} to remove them. + * @param vpnAppUid The uid of the VPN app + */ + public synchronized void updateVpnLockdownUidInterfaceRules(@Nullable String iface, + Set rangesToModify, int vpnAppUid, boolean add) { + if (iface != null) { + Set uidsToModify = intersectUids(rangesToModify, mAllApps); + removeBypassingUids(uidsToModify, vpnAppUid); + Set vpnLockdownUids = intersectUids(mVpnLockdownUidRanges.getSet(), mAllApps); + uidsToModify.retainAll(vpnLockdownUids); + updateVpnUidsInterfaceRules(iface, uidsToModify, add); + } + } + /** * Called when UID ranges under VPN Lockdown are updated * @@ -1147,6 +1170,18 @@ public class PermissionMonitor { uids.removeIf(this::hasRestrictedNetworksPermission); } + /** + * Remove all apps which are under VPN Lockdown from the list of uids + * + * @param iface The interface name of the active VPN connection + * @param uids The list of uids to operate on + */ + private void removeVpnLockdownUids(@Nullable String iface, Set uids) { + if (iface == null) { + uids.removeAll(intersectUids(mVpnLockdownUidRanges.getSet(), mAllApps)); + } + } + /** * Update netd about the list of uids that are under an active VPN connection which they cannot * bypass.