Destroy sockets of apps under background firewall chain restriction

Currently, CS destroys sockets of apps under firewall chain restriction
only when the firewall chains get enabled by setFirewallChainEnabled.

This CL updates CS to also destroy sockets of apps when the
setUidFirewallRule and replaceFirewallChain update uid rule and apps
lose network access due to FIREWALL_CHAIN_BACKGROUND.
This socket destruction is delayed until the cell modem is up in the
same way that CS destroys sockets of frozen apps.

Test: CSSocketDestroyTest
Bug: 300681644
Change-Id: I6a6fb5d6d7b71c0949a4195b84d30efef95bff37
This commit is contained in:
Motomu Utsumi 2024-05-23 23:22:40 +09:00
commit ff555a709c

View file

@ -36,6 +36,7 @@ import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
import static android.net.ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND;
import static android.net.ConnectivityManager.BLOCKED_REASON_LOCKDOWN_VPN;
import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
import static android.net.ConnectivityManager.BLOCKED_REASON_NETWORK_RESTRICTED;
@ -877,6 +878,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private static final int EVENT_UID_FROZEN_STATE_CHANGED = 61;
/**
* Event to update firewall socket destroy reasons for uids.
* obj = List of Pair(uid, socketDestroyReasons)
*/
private static final int EVENT_UPDATE_FIREWALL_DESTROY_SOCKET_REASONS = 62;
/**
* Event to clear firewall socket destroy reasons for all uids.
* arg1 = socketDestroyReason
*/
private static final int EVENT_CLEAR_FIREWALL_DESTROY_SOCKET_REASONS = 63;
/**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
@ -1033,6 +1046,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int DESTROY_SOCKET_REASON_NONE = 0;
private static final int DESTROY_SOCKET_REASON_FROZEN = 1 << 0;
private static final int DESTROY_SOCKET_REASON_FIREWALL_BACKGROUND = 1 << 1;
// Flag to drop packets to VPN addresses ingressing via non-VPN interfaces.
private final boolean mIngressToVpnAddressFiltering;
@ -3404,6 +3418,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
return !mNetworkActivityTracker.isDefaultNetworkActive();
}
private boolean shouldTrackFirewallDestroySocketReasons() {
return mDeps.isAtLeastV();
}
private void updateDestroySocketReasons(final int uid, final int reason,
final boolean addReason) {
final int destroyReasons = mDestroySocketPendingUids.get(uid, DESTROY_SOCKET_REASON_NONE);
@ -3432,6 +3450,43 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
private void handleUpdateFirewallDestroySocketReasons(
List<Pair<Integer, Integer>> reasonsList) {
if (!shouldTrackFirewallDestroySocketReasons()) {
Log.wtf(TAG, "handleUpdateFirewallDestroySocketReasons is called unexpectedly");
return;
}
ensureRunningOnConnectivityServiceThread();
for (Pair<Integer, Integer> uidSocketDestroyReasons: reasonsList) {
final int uid = uidSocketDestroyReasons.first;
final int reasons = uidSocketDestroyReasons.second;
final boolean destroyByFirewallBackground =
(reasons & DESTROY_SOCKET_REASON_FIREWALL_BACKGROUND)
!= DESTROY_SOCKET_REASON_NONE;
updateDestroySocketReasons(uid, DESTROY_SOCKET_REASON_FIREWALL_BACKGROUND,
destroyByFirewallBackground);
}
if (!mDelayDestroySockets || !isCellNetworkIdle()) {
destroyPendingSockets();
}
}
private void handleClearFirewallDestroySocketReasons(final int reason) {
if (!shouldTrackFirewallDestroySocketReasons()) {
Log.wtf(TAG, "handleClearFirewallDestroySocketReasons is called uexpectedly");
return;
}
ensureRunningOnConnectivityServiceThread();
// Unset reason from all pending uids
for (int i = mDestroySocketPendingUids.size() - 1; i >= 0; i--) {
final int uid = mDestroySocketPendingUids.keyAt(i);
updateDestroySocketReasons(uid, reason, false /* addReason */);
}
}
private void destroyPendingSockets() {
ensureRunningOnConnectivityServiceThread();
if (mDestroySocketPendingUids.size() == 0) {
@ -6617,6 +6672,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
UidFrozenStateChangedArgs args = (UidFrozenStateChangedArgs) msg.obj;
handleFrozenUids(args.mUids, args.mFrozenStates);
break;
case EVENT_UPDATE_FIREWALL_DESTROY_SOCKET_REASONS:
handleUpdateFirewallDestroySocketReasons((List) msg.obj);
break;
case EVENT_CLEAR_FIREWALL_DESTROY_SOCKET_REASONS:
handleClearFirewallDestroySocketReasons(msg.arg1);
break;
}
}
}
@ -13734,6 +13795,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
mHandler.sendMessage(mHandler.obtainMessage(EVENT_BLOCKED_REASONS_CHANGED,
List.of(new Pair<>(uid, mBpfNetMaps.getUidNetworkingBlockedReasons(uid)))));
}
if (shouldTrackFirewallDestroySocketReasons()) {
maybePostFirewallDestroySocketReasons(chain, Set.of(uid));
}
}
}
@ -13777,24 +13841,41 @@ public class ConnectivityService extends IConnectivityManager.Stub
return rule;
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private Set<Integer> getUidsOnFirewallChain(final int chain) throws ErrnoException {
if (BpfNetMapsUtils.isFirewallAllowList(chain)) {
return mBpfNetMaps.getUidsWithAllowRuleOnAllowListChain(chain);
} else {
return mBpfNetMaps.getUidsWithDenyRuleOnDenyListChain(chain);
}
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private void closeSocketsForFirewallChainLocked(final int chain)
throws ErrnoException, SocketException, InterruptedIOException {
final Set<Integer> uidsOnChain = getUidsOnFirewallChain(chain);
if (BpfNetMapsUtils.isFirewallAllowList(chain)) {
// Allowlist means the firewall denies all by default, uids must be explicitly allowed
// So, close all non-system socket owned by uids that are not explicitly allowed
Set<Range<Integer>> ranges = new ArraySet<>();
ranges.add(new Range<>(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE));
final Set<Integer> exemptUids = mBpfNetMaps.getUidsWithAllowRuleOnAllowListChain(chain);
mDeps.destroyLiveTcpSockets(ranges, exemptUids);
mDeps.destroyLiveTcpSockets(ranges, uidsOnChain /* exemptUids */);
} else {
// Denylist means the firewall allows all by default, uids must be explicitly denied
// So, close socket owned by uids that are explicitly denied
final Set<Integer> ownerUids = mBpfNetMaps.getUidsWithDenyRuleOnDenyListChain(chain);
mDeps.destroyLiveTcpSocketsByOwnerUids(ownerUids);
mDeps.destroyLiveTcpSocketsByOwnerUids(uidsOnChain /* ownerUids */);
}
}
private void maybePostClearFirewallDestroySocketReasons(int chain) {
if (chain != FIREWALL_CHAIN_BACKGROUND) {
// TODO (b/300681644): Support other firewall chains
return;
}
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CLEAR_FIREWALL_DESTROY_SOCKET_REASONS,
DESTROY_SOCKET_REASON_FIREWALL_BACKGROUND, 0 /* arg2 */));
}
@Override
public void setFirewallChainEnabled(final int chain, final boolean enable) {
enforceNetworkStackOrSettingsPermission();
@ -13820,6 +13901,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (shouldTrackUidsForBlockedStatusCallbacks()) {
updateTrackingUidsBlockedReasons();
}
if (shouldTrackFirewallDestroySocketReasons() && !enable) {
// Clear destroy socket reasons so that CS does not destroy sockets of apps that
// have network access.
maybePostClearFirewallDestroySocketReasons(chain);
}
}
if (mDeps.isAtLeastU() && enable) {
@ -13847,6 +13933,31 @@ public class ConnectivityService extends IConnectivityManager.Stub
uidBlockedReasonsList));
}
private int getFirewallDestroySocketReasons(final int blockedReasons) {
int destroySocketReasons = DESTROY_SOCKET_REASON_NONE;
if ((blockedReasons & BLOCKED_REASON_APP_BACKGROUND) != BLOCKED_REASON_NONE) {
destroySocketReasons |= DESTROY_SOCKET_REASON_FIREWALL_BACKGROUND;
}
return destroySocketReasons;
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@GuardedBy("mBlockedStatusTrackingUids")
private void maybePostFirewallDestroySocketReasons(int chain, Set<Integer> uids) {
if (chain != FIREWALL_CHAIN_BACKGROUND) {
// TODO (b/300681644): Support other firewall chains
return;
}
final ArrayList<Pair<Integer, Integer>> reasonsList = new ArrayList<>();
for (int uid: uids) {
final int blockedReasons = mBpfNetMaps.getUidNetworkingBlockedReasons(uid);
final int destroySocketReaons = getFirewallDestroySocketReasons(blockedReasons);
reasonsList.add(new Pair<>(uid, destroySocketReaons));
}
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UPDATE_FIREWALL_DESTROY_SOCKET_REASONS,
reasonsList));
}
@Override
public boolean getFirewallChainEnabled(final int chain) {
enforceNetworkStackOrSettingsPermission();
@ -13872,11 +13983,29 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
synchronized (mBlockedStatusTrackingUids) {
mBpfNetMaps.replaceUidChain(chain, uids);
// replaceFirewallChain removes uids that are currently on the chain and put |uids| on
// the chain.
// So this method could change blocked reasons of uids that are currently on chain +
// |uids|.
final Set<Integer> affectedUids = new ArraySet<>();
if (shouldTrackFirewallDestroySocketReasons()) {
try {
affectedUids.addAll(getUidsOnFirewallChain(chain));
} catch (ErrnoException e) {
Log.e(TAG, "Failed to get uids on chain(" + chain + "): " + e);
}
for (final int uid: uids) {
affectedUids.add(uid);
}
}
mBpfNetMaps.replaceUidChain(chain, uids);
if (shouldTrackUidsForBlockedStatusCallbacks()) {
updateTrackingUidsBlockedReasons();
}
if (shouldTrackFirewallDestroySocketReasons()) {
maybePostFirewallDestroySocketReasons(chain, affectedUids);
}
}
}