Merge tag 'android-14.0.0_r50' into staging/lineage-21.0_merge-android-14.0.0_r50
Android 14.0.0 Release 50 (AP2A.240605.024)
# -----BEGIN PGP SIGNATURE-----
#
# iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCZmdzpwAKCRDorT+BmrEO
# eK/kAJ0Qc8hX0IpaFh36xMDaWU5CkqYhyACfTAoS8UiuCbThp5WSk27HBcN9WyA=
# =14Bx
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue Jun 11 00:44:07 2024 EEST
# gpg: using DSA key 4340D13570EF945E83810964E8AD3F819AB10E78
# gpg: Good signature from "The Android Open Source Project <initial-contribution@android.com>" [marginal]
# gpg: initial-contribution@android.com: Verified 2514 signatures in the past
# 2 years. Encrypted 4 messages in the past 2 years.
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg: It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 4340 D135 70EF 945E 8381 0964 E8AD 3F81 9AB1 0E78
# By Kangping Dong (45) and others
# Via Automerger Merge Worker (1227) and others
* tag 'android-14.0.0_r50': (327 commits)
Import translations. DO NOT MERGE ANYWHERE
Add testAllowedUids for devices without FEATURE_TELEPHONY_SUBSCRIPTION
Fix flakes due to networks obtained via sync APIs
Import translations. DO NOT MERGE ANYWHERE
Revert "24Q2: use mainline netbpfload from apex"
24Q2: use mainline netbpfload from apex
Fix the ignore_on_* flags for egress tracing.
Remove unused library visibility
Connect to IBluetoothFinder and use it
Update the imports to androidx.test.filters.*
Move VpnManagerServiceTest and VpnTest to frameworks
Use a TAP test network for MeshCoP service test cases
Fix flaky multicast tests in Thread E2E test.
NetBpfLoader: create /sys/fs/bpf/loader dir
Revert "Use a TAP test network for MeshCoP service test cases"
Use a TAP test network for MeshCoP service test cases
[mdns] add service-side impl for NSD service TTL support
Adding additional transport satellite support at getSubIdForMobile()
netbpfload: fail if platform exec of apex fails
[Thread] make Thread state customisable via resource overlay
...
Conflicts:
netbpfload/NetBpfLoad.cpp
service/src/com/android/server/ConnectivityService.java
Change-Id: I6d5c5cbdd45f1dba28581d2dbf28851bb29cfd5d
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index e4e6c70..394234c 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -76,6 +76,7 @@
"net-utils-device-common-netlink",
"netd-client",
"tetheringstatsprotos",
+ "org.lineageos.platform.lineagesettings",
],
defaults: ["TetheringExternalLibs"],
libs: [
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 873961a..c44a0de 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -26,6 +26,7 @@
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
@@ -151,6 +152,8 @@
import com.android.networkstack.tethering.util.VersionedBroadcastListener;
import com.android.networkstack.tethering.wear.WearableConnectionManager;
+import lineageos.providers.LineageSettings;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.InetAddress;
@@ -501,6 +504,17 @@
}
startTrackDefaultNetwork();
+
+ // Listen for allowing tethering upstream via VPN settings changes
+ final ContentObserver vpnSettingObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean self) {
+ // Reconsider tethering upstream
+ mUpstreamNetworkMonitor.maybeUpdateDefaultNetworkCallback();
+ }
+ };
+ mContext.getContentResolver().registerContentObserver(LineageSettings.Secure.getUriFor(
+ LineageSettings.Secure.TETHERING_ALLOW_VPN_UPSTREAMS), false, vpnSettingObserver);
}
private class TetheringThreadExecutor implements Executor {
@@ -2305,6 +2319,12 @@
}
public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
+ // Disable hw offload on vpn upstream interfaces.
+ // setUpstreamLinkProperties() interprets null as disable.
+ if (ns != null && ns.networkCapabilities != null
+ && !ns.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_VPN)) {
+ ns = null;
+ }
mOffloadController.setUpstreamLinkProperties(
(ns != null) ? ns.linkProperties : null);
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 298940e..9016cdd 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -16,7 +16,6 @@
package com.android.networkstack.tethering;
-import static android.content.Context.TELEPHONY_SERVICE;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
@@ -34,7 +33,6 @@
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@@ -505,10 +503,7 @@
/** Check whether dun is required. */
public static boolean checkDunRequired(Context ctx) {
- final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
- // TelephonyManager would uses the active data subscription, which should be the one used
- // by tethering.
- return (tm != null) ? tm.isTetheringApnRequired() : false;
+ return false;
}
public int getOffloadPollInterval() {
diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 7a05d74..577a5e1 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -37,18 +37,25 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Handler;
+import android.os.Process;
+import android.provider.Settings;
import android.util.Log;
+import android.util.Range;
import android.util.SparseIntArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.SharedLog;
import com.android.networkstack.apishim.ConnectivityManagerShimImpl;
import com.android.networkstack.apishim.common.ConnectivityManagerShim;
+import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.networkstack.tethering.util.PrefixUtils;
+import lineageos.providers.LineageSettings;
+
import java.util.HashMap;
import java.util.HashSet;
import java.util.Objects;
@@ -84,6 +91,9 @@
private static final boolean DBG = false;
private static final boolean VDBG = false;
+ // Copied from frameworks/base/core/java/android/provider/Settings.java
+ private static final String ALWAYS_ON_VPN_LOCKDOWN = "always_on_vpn_lockdown";
+
public static final int EVENT_ON_CAPABILITIES = 1;
public static final int EVENT_ON_LINKPROPERTIES = 2;
public static final int EVENT_ON_LOST = 3;
@@ -132,6 +142,8 @@
// The current system default network (not really used yet).
private Network mDefaultInternetNetwork;
private boolean mPreferTestNetworks;
+ // Set if the Internet is considered reachable via the main user's VPN network
+ private Network mTetheringUpstreamVpn;
public UpstreamNetworkMonitor(Context ctx, Handler h, SharedLog log, EventListener listener) {
mContext = ctx;
@@ -156,8 +168,7 @@
return;
}
ConnectivityManagerShim mCmShim = ConnectivityManagerShimImpl.newInstance(mContext);
- mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
- mCmShim.registerSystemDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
+ registerAppropriateDefaultNetworkCallback();
if (mEntitlementMgr == null) {
mEntitlementMgr = entitle;
}
@@ -186,9 +197,39 @@
releaseCallback(mListenAllCallback);
mListenAllCallback = null;
+ mTetheringUpstreamVpn = null;
mNetworkMap.clear();
}
+ public void maybeUpdateDefaultNetworkCallback() {
+ final NetworkCallback prevNetworkCallback = mDefaultNetworkCallback;
+ if (prevNetworkCallback != null) {
+ registerAppropriateDefaultNetworkCallback();
+ cm().unregisterNetworkCallback(prevNetworkCallback);
+ }
+ }
+
+ private void registerAppropriateDefaultNetworkCallback() {
+ mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
+ final ConnectivityManagerShim cmShim = ConnectivityManagerShimImpl.newInstance(mContext);
+ if (isAllowedToUseVpnUpstreams() && SdkLevel.isAtLeastU()) {
+ try {
+ cmShim.registerDefaultNetworkCallbackForUid(Process.ROOT_UID,
+ mDefaultNetworkCallback, mHandler);
+ } catch (UnsupportedApiLevelException e) {
+ Log.wtf(TAG, "Unexpected exception registering network callback for root UID"
+ + " to support hotspot VPN upstreams", e);
+ }
+ } else {
+ cmShim.registerSystemDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
+ }
+ }
+
+ private boolean isAllowedToUseVpnUpstreams() {
+ return LineageSettings.Secure.getInt(mContext.getContentResolver(),
+ LineageSettings.Secure.TETHERING_ALLOW_VPN_UPSTREAMS, 0) == 1;
+ }
+
private void reevaluateUpstreamRequirements(boolean tryCell, boolean autoUpstream,
boolean dunRequired) {
final boolean mobileRequestRequired = tryCell && (dunRequired || !autoUpstream);
@@ -317,6 +358,15 @@
* Returns null if no current upstream is available.
*/
public UpstreamNetworkState getCurrentPreferredUpstream() {
+ // Use VPN upstreams if hotspot settings allow.
+ if (isAllowedToUseVpnUpstreams()) {
+ if (mTetheringUpstreamVpn != null) {
+ return mNetworkMap.get(mTetheringUpstreamVpn);
+ } else if (Settings.Secure.getInt(mContext.getContentResolver(),
+ ALWAYS_ON_VPN_LOCKDOWN, 0) == 1) {
+ return null;
+ }
+ }
final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null)
? mNetworkMap.get(mDefaultInternetNetwork)
: null;
@@ -358,6 +408,7 @@
}
private void handleNetCap(Network network, NetworkCapabilities newNc) {
+ if (isSystemVpnUsable(newNc)) mTetheringUpstreamVpn = network;
final UpstreamNetworkState prev = mNetworkMap.get(network);
if (prev == null || newNc.equals(prev.networkCapabilities)) {
// Ignore notifications about networks for which we have not yet
@@ -423,6 +474,10 @@
// - deletes the entry from the map only when the LISTEN_ALL
// callback gets notified.
+ if (network.equals(mTetheringUpstreamVpn)) {
+ mTetheringUpstreamVpn = null;
+ }
+
if (!mNetworkMap.containsKey(network)) {
// Ignore loss of networks about which we had not previously
// learned any information or for which we have already processed
@@ -636,6 +691,22 @@
&& !isCellular(ns.networkCapabilities);
}
+ private static boolean isSystemVpnUsable(NetworkCapabilities nc) {
+ return (nc != null)
+ && appliesToRootUid(nc) // Only VPNs in system user apply to root UID
+ && !nc.hasCapability(NET_CAPABILITY_NOT_VPN)
+ && nc.hasCapability(NET_CAPABILITY_INTERNET);
+ }
+
+ private static boolean appliesToRootUid(NetworkCapabilities nc) {
+ final Set<Range<Integer>> uids = nc.getUids();
+ if (uids == null) return true;
+ for (final Range<Integer> range : uids) {
+ if (range.contains(Process.ROOT_UID)) return true;
+ }
+ return false;
+ }
+
private static UpstreamNetworkState findFirstDunNetwork(
Iterable<UpstreamNetworkState> netStates) {
for (UpstreamNetworkState ns : netStates) {
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 5e401aa..0fad7ec 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -398,6 +398,21 @@
return true; // disallowed interface
}
+static __always_inline inline int bpf_owner_firewall_match(uint32_t uid) {
+ if (is_system_uid(uid)) return PASS;
+
+ const BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
+ const UidOwnerValue* uidEntry = bpf_uid_owner_map_lookup_elem(&uid);
+ const uint32_t uidRules = uidEntry ? uidEntry->rule : 0;
+
+ if (enabledRules & (FIREWALL_DROP_IF_SET | FIREWALL_DROP_IF_UNSET)
+ & (uidRules ^ FIREWALL_DROP_IF_UNSET)) {
+ return DROP;
+ }
+
+ return PASS;
+}
+
static __always_inline inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid,
const struct egress_bool egress,
const struct kver_uint kver) {
@@ -648,15 +663,14 @@
return BPF_NOMATCH;
}
-static __always_inline inline uint8_t get_app_permissions() {
- uint64_t gid_uid = bpf_get_current_uid_gid();
+static __always_inline inline uint8_t get_app_permissions(uint32_t uid) {
/*
* A given app is guaranteed to have the same app ID in all the profiles in
* which it is installed, and install permission is granted to app for all
* user at install time so we only check the appId part of a request uid at
* run time. See UserHandle#isSameApp for detail.
*/
- uint32_t appId = (gid_uid & 0xffffffff) % AID_USER_OFFSET; // == PER_USER_RANGE == 100000
+ uint32_t appId = uid % AID_USER_OFFSET; // == PER_USER_RANGE == 100000
uint8_t* permissions = bpf_uid_permission_map_lookup_elem(&appId);
// if UID not in map, then default to just INTERNET permission.
return permissions ? *permissions : BPF_PERMISSION_INTERNET;
@@ -665,8 +679,13 @@
DEFINE_NETD_BPF_PROG_KVER("cgroupsock/inet/create", AID_ROOT, AID_ROOT, inet_socket_create,
KVER_4_14)
(struct bpf_sock* sk) {
+ uint64_t uid = bpf_get_current_uid_gid() & 0xffffffff;
// A return value of 1 means allow, everything else means deny.
- return (get_app_permissions() & BPF_PERMISSION_INTERNET) ? 1 : 0;
+ if (get_app_permissions(uid) & BPF_PERMISSION_INTERNET) {
+ return bpf_owner_firewall_match(uid) == PASS ? 1 : 0;
+ } else {
+ return 0;
+ }
}
LICENSE("Apache 2.0");
diff --git a/bpf_progs/netd.h b/bpf_progs/netd.h
index 098147f..67c86e0 100644
--- a/bpf_progs/netd.h
+++ b/bpf_progs/netd.h
@@ -248,6 +248,9 @@
#define DROP_IF_UNSET (DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH \
| LOW_POWER_STANDBY_MATCH | BACKGROUND_MATCH)
+#define FIREWALL_DROP_IF_SET (OEM_DENY_1_MATCH)
+#define FIREWALL_DROP_IF_UNSET (RESTRICTED_MATCH)
+
// Warning: funky bit-wise arithmetic: in parallel, for all DROP_IF_SET/UNSET rules
// check whether the rules are globally enabled, and if so whether the rules are
// set/unset for the specific uid. DROP if that is the case for ANY of the rules.
diff --git a/framework-t/src/android/net/TrafficStats.java b/framework-t/src/android/net/TrafficStats.java
index a69b38d..d509082 100644
--- a/framework-t/src/android/net/TrafficStats.java
+++ b/framework-t/src/android/net/TrafficStats.java
@@ -238,8 +238,7 @@
private static class SocketTagger extends dalvik.system.SocketTagger {
- // TODO: set to false
- private static final boolean LOGD = true;
+ private static final boolean LOGD = false;
SocketTagger() {
}
diff --git a/netbpfload/NetBpfLoad.cpp b/netbpfload/NetBpfLoad.cpp
index ed7d048..8af4dd4 100644
--- a/netbpfload/NetBpfLoad.cpp
+++ b/netbpfload/NetBpfLoad.cpp
@@ -289,13 +289,11 @@
}
if (isAtLeastT && !android::bpf::isAtLeastKernelVersion(4, 9, 0)) {
- ALOGE("Android T requires kernel 4.9.");
- return 1;
+ ALOGW("Android T requires kernel 4.9.");
}
if (isAtLeastU && !android::bpf::isAtLeastKernelVersion(4, 14, 0)) {
- ALOGE("Android U requires kernel 4.14.");
- return 1;
+ ALOGW("Android U requires kernel 4.14.");
}
if (isAtLeastV && !android::bpf::isAtLeastKernelVersion(4, 19, 0)) {
@@ -353,12 +351,14 @@
// kernel does not have CONFIG_BPF_JIT=y)
// BPF_JIT is required by R VINTF (which means 4.14/4.19/5.4 kernels),
// but 4.14/4.19 were released with P & Q, and only 5.4 is new in R+.
- if (writeProcSysFile("/proc/sys/net/core/bpf_jit_enable", "1\n")) return 1;
+ if (writeProcSysFile("/proc/sys/net/core/bpf_jit_enable", "1\n") &&
+ android::bpf::isAtLeastKernelVersion(4, 14, 0)) return 1;
// Enable JIT kallsyms export for privileged users only
// (Note: this (open) will fail with ENOENT 'No such file or directory' if
// kernel does not have CONFIG_HAVE_EBPF_JIT=y)
- if (writeProcSysFile("/proc/sys/net/core/bpf_jit_kallsyms", "1\n")) return 1;
+ if (writeProcSysFile("/proc/sys/net/core/bpf_jit_kallsyms", "1\n") &&
+ android::bpf::isAtLeastKernelVersion(4, 14, 0)) return 1;
}
// Create all the pin subdirectories
diff --git a/netbpfload/loader.cpp b/netbpfload/loader.cpp
index c534b2c..d9d3b50 100644
--- a/netbpfload/loader.cpp
+++ b/netbpfload/loader.cpp
@@ -574,6 +574,14 @@
static bool mapMatchesExpectations(const unique_fd& fd, const string& mapName,
const struct bpf_map_def& mapDef, const enum bpf_map_type type) {
+ // bpfGetFd... family of functions require at minimum a 4.14 kernel,
+ // so on 4.9-T kernels just pretend the map matches our expectations.
+ // Additionally we'll get almost equivalent test coverage on newer devices/kernels.
+ // This is because the primary failure mode we're trying to detect here
+ // is either a source code misconfiguration (which is likely kernel independent)
+ // or a newly introduced kernel feature/bug (which is unlikely to get backported to 4.9).
+ if (!isAtLeastKernelVersion(4, 14, 0)) return true;
+
// Assuming fd is a valid Bpf Map file descriptor then
// all the following should always succeed on a 4.14+ kernel.
// If they somehow do fail, they'll return -1 (and set errno),
@@ -711,6 +719,16 @@
}
enum bpf_map_type type = md[i].type;
+ if (type == BPF_MAP_TYPE_DEVMAP && !isAtLeastKernelVersion(4, 14, 0)) {
+ // On Linux Kernels older than 4.14 this map type doesn't exist, but it can kind
+ // of be approximated: ARRAY has the same userspace api, though it is not usable
+ // by the same ebpf programs. However, that's okay because the bpf_redirect_map()
+ // helper doesn't exist on 4.9-T anyway (so the bpf program would fail to load,
+ // and thus needs to be tagged as 4.14+ either way), so there's nothing useful you
+ // could do with a DEVMAP anyway (that isn't already provided by an ARRAY)...
+ // Hence using an ARRAY instead of a DEVMAP simply makes life easier for userspace.
+ type = BPF_MAP_TYPE_ARRAY;
+ }
if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4, 0)) {
// On Linux Kernels older than 5.4 this map type doesn't exist, but it can kind
// of be approximated: HASH has the same userspace visible api.
@@ -766,7 +784,8 @@
.max_entries = max_entries,
.map_flags = md[i].map_flags,
};
- strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
+ if (isAtLeastKernelVersion(4, 14, 0))
+ strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
fd.reset(bpf(BPF_MAP_CREATE, req));
saved_errno = errno;
ALOGD("bpf_create_map name %s, ret: %d", mapNames[i].c_str(), fd.get());
@@ -1008,7 +1027,8 @@
.log_size = static_cast<__u32>(log_buf.size()),
.expected_attach_type = cs[i].expected_attach_type,
};
- strlcpy(req.prog_name, cs[i].name.c_str(), sizeof(req.prog_name));
+ if (isAtLeastKernelVersion(4, 14, 0))
+ strlcpy(req.prog_name, cs[i].name.c_str(), sizeof(req.prog_name));
fd.reset(bpf(BPF_PROG_LOAD, req));
ALOGD("BPF_PROG_LOAD call for %s (%s) returned fd: %d (%s)", elfPath,
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index a00c363..2252e07 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -75,16 +75,6 @@
// This code was mainlined in T, so this should be trivially satisfied.
if (!modules::sdklevel::IsAtLeastT()) return Status("S- platform is unsupported");
- // S requires eBPF support which was only added in 4.9, so this should be satisfied.
- if (!bpf::isAtLeastKernelVersion(4, 9, 0)) {
- return Status("kernel version < 4.9.0 is unsupported");
- }
-
- // U bumps the kernel requirement up to 4.14
- if (modules::sdklevel::IsAtLeastU() && !bpf::isAtLeastKernelVersion(4, 14, 0)) {
- return Status("U+ platform with kernel version < 4.14.0 is unsupported");
- }
-
if (modules::sdklevel::IsAtLeastV()) {
// V bumps the kernel requirement up to 4.19
// see also: //system/netd/tests/kernel_test.cpp TestKernel419
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 6839c22..e33bb2e 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -267,6 +267,7 @@
import android.util.Range;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.util.StatsEvent;
import androidx.annotation.RequiresApi;
@@ -379,6 +380,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
/**
* @hide
@@ -481,6 +483,333 @@
*/
private final SparseIntArray mUidBlockedReasons = new SparseIntArray();
+ /** Map of UID to its bit-packed allowed transports. */
+ private SparseLongArray mUidAllowedTransports = new SparseLongArray();
+
+ /** Allowed UID ranges provided to Netd, tracked based on the netId to which they belong. */
+ @GuardedBy("mNetworkForNetId")
+ private Map<Integer, NativeUidRangeConfig> mNetIdToAllowlist =
+ new HashMap<Integer, NativeUidRangeConfig>();
+
+ /**
+ * UIDs that we wish to be denied access to networks based on certain policies, grouped by the
+ * netId to which they belong. Used to prevent affected UIDs from getting online through a VPN
+ * or from receiving incoming traffic when their active network is denied to them.
+ */
+ @GuardedBy("mNetworkForNetId")
+ private Map<Integer, List<Integer>> mNetIdToDisallowedUids = new HashMap<>();
+
+ private void setUidsAllowedTransports(@NonNull final int[] uids,
+ @NonNull final long[] allowedTransportsPacked) {
+ mHandler.post(() -> handleSetUidsAllowedTransports(uids, allowedTransportsPacked));
+ }
+
+ private void handleSetUidsAllowedTransports(@NonNull final int[] uids,
+ @NonNull final long[] allowedTransportsPacked) {
+ for (int i = 0; i < uids.length; i++) {
+ final int uid = uids[i];
+ final long transportsPacked = allowedTransportsPacked[i];
+ mUidAllowedTransports.put(uid, transportsPacked);
+ }
+ if (DDBG) Log.d(TAG, "setUidsAllowedTransports: Processing " + uids.length + " UIDs...");
+ for (final var nai : mNetworkAgentInfos) {
+ updateDisallowedUidsForNetwork(nai);
+ if (nai.isVPN()) {
+ // VPNs manage their own allowed UID ranges, so we handle them via denylist above.
+ continue;
+ }
+ for (final int uid : uids) {
+ if (nai.networkCapabilities.appliesToUid(uid)) {
+ updateAllowedUidsForNetwork(nai);
+ break;
+ }
+ }
+ }
+ if (DDBG) Log.d(TAG, "setUidsAllowedTransports: Processed " + uids.length + " UIDs.");
+ }
+
+ // The last UID denylist that we supplied to BPF.
+ // To support access across threads, do not modify after assignment.
+ private Set<Integer> mLastDisallowedUidsDenylist = Set.of();
+
+ /**
+ * Computes which policies apply to the given network to determine which UIDs should be
+ * prevented from accessing that network. Then, for each network with disallowed UIDs, checks
+ * whether the network is currently the active network for the UID, and if so, prevents the
+ * UID from accessing networks entirely via a denylist. Without this, UIDs are able to use VPNs
+ * even if their policy disallows it, because the VPN itself manages its allowed UID ranges.
+ * Additionally, this prevents incoming traffic for UIDs whose active network is disallowed.
+ *
+ * NOTE: UIDs with restricted networking permission are never disallowed.
+ */
+ private void updateDisallowedUidsForNetwork(@NonNull final NetworkAgentInfo nai) {
+ ensureRunningOnConnectivityServiceThread();
+
+ final var netId = nai.network.netId;
+ final var ourTag = "updateDisallowedUidsForNetwork(" + netId + "): ";
+ if (DDBG) Log.d(TAG, ourTag + "begin");
+ final var disallowedUids = getUidsDisallowedByPolicyForNetwork(nai);
+ final Set<Integer> prevDenylist;
+ final Set<Integer> newDenylist = new ArraySet<Integer>();
+ synchronized (mNetworkForNetId) {
+ prevDenylist = mLastDisallowedUidsDenylist;
+ if (mNetworkForNetId.contains(netId)) {
+ // Only store the disallowed UIDs if this network still exists.
+ mNetIdToDisallowedUids.put(netId, disallowedUids);
+ }
+ for (final var entry : mNetIdToDisallowedUids.entrySet()) {
+ final var entryNetId = entry.getKey();
+ final var entryDisallowedUids = entry.getValue();
+ entryDisallowedUids.forEach(uid -> {
+ if (mPermissionMonitor.hasRestrictedNetworksPermission(uid)) {
+ // Skip denylist for UIDs with restricted networks permission.
+ return;
+ }
+ // Disallowing UIDs is only applicable if the network is the UID's active
+ // network. For us, a VPN that is still connecting should still be considered,
+ // because it is *about* to become the active network, and we are reacting
+ // early. If we do not include connecting VPNs, we get no result.
+ // Also, if we don't know the active network, denying is fine for now.
+ final var activeNetwork = getActiveNetworkOrConnectingVpnForUidInternal(
+ uid, true /* ignoreBlocked */);
+ if (activeNetwork == null || entryNetId == activeNetwork.netId) {
+ newDenylist.add(uid);
+ }
+ });
+ }
+ mLastDisallowedUidsDenylist = Collections.unmodifiableSet(newDenylist);
+ }
+ // NOTE: Setting UID rules individually is exponentially faster than replacing the whole
+ // firewall chain for some reason (<1ms vs 10+ms, even with few UIDs for the latter
+ // and hundreds for the former).
+ final var toAdd = newDenylist.stream().filter(uid -> !prevDenylist.contains(uid))
+ .collect(Collectors.toSet());
+ if (DDBG) {
+ synchronized (mNetworkForNetId) {
+ Log.d(TAG, ourTag + "Disallowed UIDs per netId: " + mNetIdToDisallowedUids);
+ }
+ Log.d(TAG, ourTag + "Prev denylist: " + prevDenylist);
+ Log.d(TAG, ourTag + "New denylist: " + newDenylist);
+ }
+ final var toRemove = prevDenylist.stream().filter(uid -> !newDenylist.contains(uid))
+ .collect(Collectors.toSet());
+ if (DDBG) Log.d(TAG, ourTag + "toAdd: " + toAdd + "; toRemove: " + toRemove);
+ if (toAdd.isEmpty() && toRemove.isEmpty()) {
+ // Nothing to do.
+ return;
+ }
+ boolean anyFailed = false;
+ for (final int uid : toAdd) {
+ try {
+ mBpfNetMaps.setUidRule(ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1, uid,
+ FIREWALL_RULE_DENY);
+ } catch (ServiceSpecificException e) {
+ loge(ourTag + "Failed to add uid " + uid, e);
+ anyFailed = true;
+ break;
+ }
+ }
+ if (!anyFailed) {
+ for (final int uid : toRemove) {
+ try {
+ mBpfNetMaps.setUidRule(ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1, uid,
+ FIREWALL_RULE_ALLOW);
+ } catch (ServiceSpecificException e) {
+ loge(ourTag + "Failed to remove uid " + uid, e);
+ anyFailed = true;
+ break;
+ }
+ }
+ }
+ if (anyFailed) {
+ // Should never happen, but just in case.
+ replaceFirewallChain(ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1,
+ newDenylist.stream().mapToInt(Integer::intValue).toArray());
+ }
+ // Tell NPMS about the changes, primarily so the firewall icon can reflect them.
+ mPolicyManager.notifyDenylistChanged(toAdd.stream().mapToInt(Integer::intValue).toArray(),
+ toRemove.stream().mapToInt(Integer::intValue).toArray());
+ if (DDBG) Log.d(TAG, ourTag + "end");
+ }
+
+
+ /**
+ * Generates an allowlist configuration for Netd that reflects a network's allowed UIDs and
+ * includes a sub priority to allow the network to act as a default network for the UIDs
+ * if it is considered to be the default network overall. This default network handling is
+ * required, or else UIDs that do not make specific network requests will have no connectivity.
+ */
+ private NativeUidRangeConfig getAllowlistedNativeUidRangeConfigForNetwork(
+ @NonNull final NetworkAgentInfo nai) {
+ final int netId = nai.network.netId;
+ final var uidsAllowedByPolicy = getUidRangeParcelsAllowedByPolicyForNetwork(nai);
+ final boolean isDefault = isDefaultNetwork(nai);
+ final int subPriority = isDefault ? PREFERENCE_ORDER_LOWEST_WITH_DEFAULT
+ : PREFERENCE_ORDER_IRRELEVANT_BECAUSE_NOT_DEFAULT;
+ return new NativeUidRangeConfig(netId, uidsAllowedByPolicy, subPriority);
+ }
+
+ /**
+ * Updates our tracked configurations for Netd based on changes to default networks, and
+ * informs Netd if any changes need to be made to ensure that allowed UIDs do or do not utilize
+ * changed networks as their default network. This default network handling is required, or
+ * else UIDs that do not make specific network requests will have no connectivity or will
+ * have their traffic traverse the wrong network.
+ */
+ private void updateUidDefaultNetworkRules(@Nullable final NetworkAgentInfo newDefaultNetwork) {
+ final Integer defaultNetId = newDefaultNetwork == null ? null
+ : newDefaultNetwork.network.netId;
+ final var configs = new ArrayList<Pair<NativeUidRangeConfig, NativeUidRangeConfig>>();
+ final var networksForUpdateDisallowedUids = new ArrayList<NetworkAgentInfo>();
+ synchronized (mNetworkForNetId) {
+ for (final var config : mNetIdToAllowlist.values()) {
+ final var configIsDefault =
+ config.subPriority == PREFERENCE_ORDER_LOWEST_WITH_DEFAULT;
+ final int subPriority;
+ if (configIsDefault && !Objects.equals(config.netId, defaultNetId)) {
+ // Remove and replace any existing rules with a subPriority for default.
+ subPriority = PREFERENCE_ORDER_IRRELEVANT_BECAUSE_NOT_DEFAULT;
+ } else if (!configIsDefault && Objects.equals(config.netId, defaultNetId)) {
+ // Remove and replace any existing rules with a subPriority for default.
+ subPriority = PREFERENCE_ORDER_LOWEST_WITH_DEFAULT;
+ } else {
+ continue;
+ }
+ final var newConfig =
+ new NativeUidRangeConfig(config.netId, config.uidRanges, subPriority);
+ mNetIdToAllowlist.put(newConfig.netId, newConfig);
+ configs.add(new Pair<>(config, newConfig));
+ final var nai = mNetworkForNetId.get(config.netId);
+ if (nai != null) {
+ networksForUpdateDisallowedUids.add(nai);
+ }
+ }
+ }
+ for (final var pair : configs) {
+ try {
+ mNetd.networkAddUidRangesParcel(pair.second);
+ mNetd.networkRemoveUidRangesParcel(pair.first);
+ } catch (RemoteException | ServiceSpecificException e) {
+ loge("updateUidDefaultNetworkRules: Exception while updating", e);
+ }
+ }
+ for (final var nai : networksForUpdateDisallowedUids) {
+ updateDisallowedUidsForNetwork(nai);
+ }
+ }
+
+ /**
+ * Generates and stores an allowlist configuration based on the policies that apply to the
+ * given network, and sends this configuration to Netd. Uses the same methods for adding UID
+ * range-based IP rules as is used by VPNs or by restricted networks which manually specify
+ * their allowed UIDs, ensuring absent UIDs cannot be routed there. {@see updateAllowedUids}
+ */
+ private void updateAllowedUidsForNetwork(@NonNull final NetworkAgentInfo nai) {
+ if (!nai.networkInfo.isConnected()) {
+ // Network is not connected, so we cannot currently update allowed UIDs.
+ // If we try, Netd responds with: "Machine is not on the network (code 64)"
+ return;
+ }
+ final int netId = nai.network.netId;
+ final var ourTag = "updateAllowedUidsForNetwork(" + netId + "): ";
+ if (DDBG) Log.d(TAG, ourTag + "begin");
+ final var config = getAllowlistedNativeUidRangeConfigForNetwork(nai);
+ final NativeUidRangeConfig lastConfig;
+ synchronized (mNetworkForNetId) {
+ lastConfig = mNetIdToAllowlist.get(netId);
+ mNetIdToAllowlist.put(netId, config);
+ }
+ try {
+ if (config.uidRanges != null && config.uidRanges.length > 0) {
+ mNetd.networkAddUidRangesParcel(config);
+ }
+ if (lastConfig != null && lastConfig.uidRanges != null
+ && lastConfig.uidRanges.length > 0) {
+ mNetd.networkRemoveUidRangesParcel(lastConfig);
+ }
+ } catch (Exception e) {
+ loge(ourTag + "Exception encountered", e);
+ }
+ if (DDBG) Log.d(TAG, ourTag + "end");
+ }
+
+ private static final long TRANSPORT_VPN_FLAG = 1 << TRANSPORT_VPN;
+
+ /**
+ * Returns true if all of the provided transports are allowed, or if transports represent
+ * a VPN and VPNs are allowed. Otherwise, returns false.
+ */
+ private static boolean areTransportsAllowed(final long transports,
+ final long allowedTransports) {
+ // TODO: In the future, we may want to provide a way to restrict VPN access based on
+ // its other transports, e.g. to block apps from using mobile data even if that data
+ // is over a VPN. This is possible by removing or extending the check below.
+ if ((transports & TRANSPORT_VPN_FLAG) != 0) {
+ // For a VPN, all that matters is VPN access, nothing else.
+ return (allowedTransports & TRANSPORT_VPN_FLAG) != 0;
+ }
+ return (allowedTransports & transports) == transports;
+ }
+
+ /**
+ * Returns a list of UIDs that are policy-restricted based on the provided network and its
+ * capabilities. Networks that are restricted by the system choose their own allowed UIDs,
+ * so we do not disallow any UIDs for those networks.
+ */
+ private List<Integer> getUidsDisallowedByPolicyForNetwork(
+ @NonNull final NetworkAgentInfo nai) {
+ final var ourTag = "getUidsDisallowedByPolicyForNetwork(" + nai.network.netId + "): ";
+ final var uids = new ArrayList<Integer>();
+ if (!nai.networkInfo.isConnectedOrConnecting()) {
+ // Network is not connected so nothing needs to be disallowed.
+ if (DDBG) Log.d(TAG, ourTag + "not connected, so empty");
+ return uids;
+ }
+ final NetworkCapabilities nc = nai.networkCapabilities;
+ if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+ // We do not want to meddle with networks that are already considered restricted.
+ // These will have their own allowed UIDs.
+ if (DDBG) Log.d(TAG, ourTag + "restricted, so empty");
+ return uids;
+ }
+ final long packedTransports = BitUtils.packBits(nc.getTransportTypes());
+ if (packedTransports == 0L) {
+ // If we are not supplied with transports to check, nothing is allowed.
+ if (DDBG) Log.d(TAG, ourTag + "transports == 0L, so empty");
+ return uids;
+ }
+ final int size = mUidAllowedTransports.size();
+ int lastUid = 0;
+ for (int i = 0; i < size; i++) {
+ int uid = mUidAllowedTransports.keyAt(i);
+ long allowedTransports = mUidAllowedTransports.valueAt(i);
+ if (!nc.appliesToUid(uid)) continue;
+ if (!areTransportsAllowed(packedTransports, allowedTransports)) {
+ uids.add(uid);
+ }
+ }
+ return uids;
+ }
+
+ /**
+ * Returns an array of UidRangeParcel representing all possible UIDs that are not restricted
+ * by policy, based on the provided network and its capabilities.
+ */
+ private UidRangeParcel[] getUidRangeParcelsAllowedByPolicyForNetwork(
+ @NonNull final NetworkAgentInfo nai) {
+ final var uids = getUidsDisallowedByPolicyForNetwork(nai);
+ final var ranges = new ArrayList<UidRangeParcel>();
+ int lastUid = 0;
+ for (final Integer uid : uids) {
+ if (uid != lastUid) {
+ ranges.add(new UidRangeParcel(lastUid, uid - 1));
+ }
+ lastUid = uid + 1;
+ }
+ ranges.add(new UidRangeParcel(lastUid, Integer.MAX_VALUE));
+ return ranges.toArray(new UidRangeParcel[0]);
+ }
+
private final Context mContext;
private final ConnectivityResources mResources;
private final int mWakeUpMark;
@@ -574,6 +903,8 @@
// with role_sms role and android.permission.SATELLITE_COMMUNICATION permission detected
@VisibleForTesting
static final int PREFERENCE_ORDER_SATELLITE_FALLBACK = 40;
+ // Lowest subpriority that still adds default network rules.
+ static final int PREFERENCE_ORDER_LOWEST_WITH_DEFAULT = 998;
// Preference order that signifies the network shouldn't be set as a default network for
// the UIDs, only give them access to it. TODO : replace this with a boolean
// in NativeUidRangeConfig
@@ -1816,6 +2147,13 @@
mSatelliteAccessController = null;
}
+ // Enable the OEM denylist chain. {@see mNetIdToDisallowedUids}
+ try {
+ mBpfNetMaps.setChildChain(ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1, true);
+ } catch (ServiceSpecificException e) {
+ logwtf("Could not enable fw_oem_deny_1 chain", e);
+ }
+
// To ensure uid state is synchronized with Network Policy, register for
// NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
// reading existing policy from disk.
@@ -2203,6 +2541,21 @@
}
@Nullable
+ private NetworkAgentInfo getConnectedOrConnectingVpnForUid(int uid) {
+ synchronized (mNetworkForNetId) {
+ for (int i = 0; i < mNetworkForNetId.size(); i++) {
+ final NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
+ if (nai.isVPN()
+ && (nai.everConnected() || nai.networkInfo.isConnectedOrConnecting())
+ && nai.networkCapabilities.appliesToUid(uid)) {
+ return nai;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Nullable
private Network[] getVpnUnderlyingNetworks(int uid) {
if (mLockdownEnabled) return null;
final NetworkAgentInfo nai = getVpnForUid(uid);
@@ -2238,6 +2591,7 @@
if (ignoreBlocked) {
return false;
}
+ if (isUidCurrentlyDisallowedByPolicy(uid)) return true;
if (isUidBlockedByVpn(uid, mVpnBlockedUidRanges)) return true;
final long ident = Binder.clearCallingIdentity();
try {
@@ -2248,6 +2602,11 @@
}
}
+ /** Check if UID is currently disallowed general network access based on policies. */
+ private boolean isUidCurrentlyDisallowedByPolicy(int uid) {
+ return mLastDisallowedUidsDenylist.contains(uid);
+ }
+
private void maybeLogBlockedNetworkInfo(NetworkInfo ni, int uid) {
if (ni == null || !LOGD_BLOCKED_NETWORKINFO) {
return;
@@ -2341,7 +2700,17 @@
@Nullable
private Network getActiveNetworkForUidInternal(final int uid, boolean ignoreBlocked) {
- final NetworkAgentInfo vpnNai = getVpnForUid(uid);
+ return getActiveNetworkForUidInternal(getVpnForUid(uid), uid, ignoreBlocked);
+ }
+
+ private Network getActiveNetworkOrConnectingVpnForUidInternal(final int uid,
+ boolean ignoreBlocked) {
+ return getActiveNetworkForUidInternal(
+ getConnectedOrConnectingVpnForUid(uid), uid, ignoreBlocked);
+ }
+
+ private Network getActiveNetworkForUidInternal(final NetworkAgentInfo vpnNai,
+ final int uid, boolean ignoreBlocked) {
if (vpnNai != null) {
final NetworkCapabilities requiredCaps = createDefaultNetworkCapabilitiesForUid(uid);
if (requiredCaps.satisfiedByNetworkCapabilities(vpnNai.networkCapabilities)) {
@@ -3280,6 +3649,11 @@
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_BLOCKED_REASON_CHANGED,
uid, blockedReasons));
}
+
+ @Override
+ public void onUidsAllowedTransportsChanged(int[] uids, long[] allowedTransports) {
+ setUidsAllowedTransports(uids, allowedTransports);
+ }
};
private void handleUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {
@@ -5024,7 +5398,10 @@
final boolean wasDefault = isDefaultNetwork(nai);
synchronized (mNetworkForNetId) {
mNetworkForNetId.remove(nai.network.getNetId());
+ mNetIdToAllowlist.remove(nai.network.getNetId());
+ mNetIdToDisallowedUids.remove(nai.network.getNetId());
}
+ updateDisallowedUidsForNetwork(nai);
mNetIdManager.releaseNetId(nai.network.getNetId());
// Just in case.
mLegacyTypeTracker.remove(nai, wasDefault);
@@ -5149,7 +5526,10 @@
// Remove the NetworkAgent, but don't mark the netId as
// available until we've told netd to delete it below.
mNetworkForNetId.remove(nai.network.getNetId());
+ mNetIdToAllowlist.remove(nai.network.getNetId());
+ mNetIdToDisallowedUids.remove(nai.network.getNetId());
}
+ updateDisallowedUidsForNetwork(nai);
propagateUnderlyingNetworkCapabilities(nai.network);
// Update allowed network lists in netd. This should be called after removing nai
// from mNetworkAgentInfos.
@@ -7002,6 +7382,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;
@@ -8569,6 +8953,7 @@
nai.notifyRegistered();
NetworkInfo networkInfo = nai.networkInfo;
updateNetworkInfo(nai, networkInfo);
+ updateDisallowedUidsForNetwork(nai);
updateVpnUids(nai, null, nai.networkCapabilities);
}
@@ -8675,11 +9060,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
@@ -8830,7 +9220,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<>(
@@ -8857,6 +9248,7 @@
loge("Exception removing interface: " + e);
}
}
+ return !(interfaceDiff.added.isEmpty() && interfaceDiff.removed.isEmpty());
}
/**
@@ -8938,7 +9330,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);
@@ -8949,7 +9341,7 @@
return;
}
- if (Objects.equals(oldIface, newIface) && (wasFiltering == needsFiltering)) {
+ if (!force && Objects.equals(oldIface, newIface) && (wasFiltering == needsFiltering)) {
// Nothing changed.
return;
}
@@ -8966,9 +9358,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 */);
}
}
@@ -9215,6 +9611,9 @@
updateNetworkPermissions(nai, newNc);
final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc);
+ if (prevNc != null && (!prevNc.equalsUids(newNc) || !prevNc.equalsTransportTypes(newNc))) {
+ updateDisallowedUidsForNetwork(nai);
+ }
updateVpnUids(nai, prevNc, newNc);
updateAllowedUids(nai, prevNc, newNc);
nai.updateScoreForNetworkAgentUpdate();
@@ -9604,9 +10003,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!
@@ -9616,6 +10021,9 @@
private void updateAllowedUids(@NonNull NetworkAgentInfo nai,
@Nullable NetworkCapabilities prevNc, @Nullable NetworkCapabilities newNc) {
+ if (!nai.isVPN()) {
+ updateAllowedUidsForNetwork(nai);
+ }
// In almost all cases both NC code for empty access UIDs. return as fast as possible.
final boolean prevEmpty = null == prevNc || prevNc.getAllowedUidsNoCopy().isEmpty();
final boolean newEmpty = null == newNc || newNc.getAllowedUidsNoCopy().isEmpty();
@@ -9996,6 +10404,7 @@
} catch (RemoteException | ServiceSpecificException e) {
loge("Exception setting app default network", e);
}
+ updateUidDefaultNetworkRules(newDefaultNetwork);
}
/**
@@ -10080,6 +10489,7 @@
} catch (RemoteException | ServiceSpecificException e) {
loge("Exception setting default network :" + e);
}
+ updateUidDefaultNetworkRules(newDefaultNetwork);
}
private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
@@ -10773,6 +11183,7 @@
updateCapabilitiesForNetwork(networkAgent);
}
networkAgent.onNetworkCreated();
+ updateDisallowedUidsForNetwork(networkAgent);
updateAllowedUids(networkAgent, null, networkAgent.networkCapabilities);
updateProfileAllowedNetworks();
}
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index beaa174..c9b6387 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -526,7 +526,6 @@
// TODO : remove carryover package check in the future(b/31479477). All apps should just
// request the appropriate permission for their use case since android Q.
return isCarryoverPackage(app.applicationInfo)
- || isUidAllowedOnRestrictedNetworks(app.applicationInfo)
|| hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK)
|| hasPermission(app, NETWORK_STACK)
|| hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
@@ -969,6 +968,7 @@
// packets to that UID is fine.
final Set<Integer> changedUids = intersectUids(rangesToAdd, mAllApps);
removeBypassingUids(changedUids, vpnAppUid);
+ removeVpnLockdownUids(iface, changedUids);
updateVpnUidsInterfaceRules(iface, changedUids, true /* add */);
if (mVpnInterfaceUidRanges.containsKey(iface)) {
mVpnInterfaceUidRanges.get(iface).addAll(rangesToAdd);
@@ -991,6 +991,7 @@
// ranges and update Netd about them.
final Set<Integer> changedUids = intersectUids(rangesToRemove, mAllApps);
removeBypassingUids(changedUids, vpnAppUid);
+ removeVpnLockdownUids(iface, changedUids);
updateVpnUidsInterfaceRules(iface, changedUids, false /* add */);
Set<UidRange> existingRanges = mVpnInterfaceUidRanges.getOrDefault(iface, null);
if (existingRanges == null) {
@@ -1004,6 +1005,27 @@
}
/**
+ * 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<UidRange> rangesToModify, int vpnAppUid, boolean add) {
+ if (iface != null) {
+ Set<Integer> uidsToModify = intersectUids(rangesToModify, mAllApps);
+ removeBypassingUids(uidsToModify, vpnAppUid);
+ Set<Integer> vpnLockdownUids = intersectUids(mVpnLockdownUidRanges.getSet(), mAllApps);
+ uidsToModify.retainAll(vpnLockdownUids);
+ updateVpnUidsInterfaceRules(iface, uidsToModify, add);
+ }
+ }
+
+ /**
* Called when UID ranges under VPN Lockdown are updated
*
* @param add {@code true} if the uids are to be added to the Lockdown, {@code false} if they
@@ -1092,6 +1114,18 @@
}
/**
+ * 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<Integer> 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.
*