Always drop non-VPN ingress in lockdown mode
When "Block connections without VPN" is specified, incoming traffic
from non-VPN interfaces should be blocked regardless of the
determination made by ConnectivityService#getVpnIsolationInterface.
Outgoing traffic to non-VPN interfaces is already blocked in this case.
(Loopback is excluded as usual.)
Test: `adb shell dumpsys connectivity trafficcontroller` will now show
the tunnel interface for uids affected by lockdown when
getVpnIsolationInterface returns null (wildcard), to block non-VPN
ingress to such uids. This will return to 0 (wildcard) when lockdown
is toggled back off.
Also includes squashed change:
Author: Tommy Webb <tommy@calyxinstitute.org>
Date: Mon May 1 16:52:28 2023 -0400
fixup! Always drop non-VPN ingress in lockdown mode
For lockdown purposes, force an update of VPN filtering whenever the
interface names for a VPN have changed, to ensure that the BPF owner
map uses the most up-to-date interface for ingress filtering.
Issue: calyxos#1651
Change-Id: Ia0c75a723134023906134597b395653c7a570686
Co-authored-by: Tommy Webb <tommy@calyxinstitute.org>
Issue: calyxos#1255
Bug: 206482423
Change-Id: Id7954816566cb06bf2e9869ea98b20678835df9d
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 639a0ba..33c0194 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -6800,6 +6800,10 @@
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;
@@ -8389,11 +8393,16 @@
// 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);
updateMtu(newLp, oldLp);
// TODO - figure out what to do for clat
@@ -8544,7 +8553,8 @@
}
}
- 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<String> interfaceDiff = new CompareResult<>(
@@ -8571,6 +8581,7 @@
loge("Exception removing interface: " + e);
}
}
+ return !(interfaceDiff.added.isEmpty() && interfaceDiff.removed.isEmpty());
}
// TODO: move to frameworks/libs/net.
@@ -8684,7 +8695,7 @@
}
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);
@@ -8695,7 +8706,7 @@
return;
}
- if (Objects.equals(oldIface, newIface) && (wasFiltering == needsFiltering)) {
+ if (!force && Objects.equals(oldIface, newIface) && (wasFiltering == needsFiltering)) {
// Nothing changed.
return;
}
@@ -8712,9 +8723,13 @@
// 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 */);
}
}
@@ -9181,9 +9196,15 @@
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!