Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c92383fd authored by Chiachang Wang's avatar Chiachang Wang
Browse files

Resolve UidRange dependency between NMS and CS module

ConnectivityService is going to become a mainline module which
cannot access hidden APIs. Thus, replace the VPN uid range
controlling APIs from NMS to INetd directly.

Bug: 170598012
Test: atest FrameworksNetTests
Test: atest HostsideVpnTests
Test: manually test to connect to VPN and check the uid range
Change-Id: Ie6656ef36f54c2f14d5a2899e763a29b70a30f5d
parent d0c6af4b
Loading
Loading
Loading
Loading
+0 −12
Original line number Diff line number Diff line
@@ -320,16 +320,6 @@ interface INetworkManagementService
    void setFirewallUidRules(int chain, in int[] uids, in int[] rules);
    void setFirewallChainEnabled(int chain, boolean enable);

    /**
     * Set all packets from users in ranges to go through VPN specified by netId.
     */
    void addVpnUidRanges(int netId, in UidRange[] ranges);

    /**
     * Clears the special VPN rules for users in ranges and VPN specified by netId.
     */
    void removeVpnUidRanges(int netId, in UidRange[] ranges);

    /**
     * Start listening for mobile activity state changes.
     */
@@ -361,7 +351,5 @@ interface INetworkManagementService
    void removeInterfaceFromLocalNetwork(String iface);
    int removeRoutesFromLocalNetwork(in List<RouteInfo> routes);

    void setAllowOnlyVpnForUids(boolean enable, in UidRange[] uidRanges);

    boolean isNetworkRestricted(int uid);
}
+15 −7
Original line number Diff line number Diff line
@@ -129,6 +129,7 @@ import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.TetheringManager;
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.Uri;
import android.net.VpnManager;
import android.net.VpnService;
@@ -5152,7 +5153,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                loge("Starting user already has a VPN");
                return;
            }
            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId, mKeyStore);
            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
            mVpns.put(userId, userVpn);
            if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
                updateLockdownVpn();
@@ -6621,6 +6622,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
                && (lp.hasIpv6DefaultRoute() || lp.hasIpv6UnreachableDefaultRoute());
    }

    private static UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set<UidRange> ranges) {
        final UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.size()];
        int index = 0;
        for (UidRange range : ranges) {
            stableRanges[index] = new UidRangeParcel(range.start, range.stop);
            index++;
        }
        return stableRanges;
    }

    private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
            NetworkCapabilities newNc) {
        Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUids();
@@ -6640,14 +6651,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
            // removing old range works because, unlike the filtering rules below, it's possible to
            // add duplicate UID routing rules.
            if (!newRanges.isEmpty()) {
                final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
                newRanges.toArray(addedRangesArray);
                mNMS.addVpnUidRanges(nai.network.getNetId(), addedRangesArray);
                mNetd.networkAddUidRanges(nai.network.netId, toUidRangeStableParcels(newRanges));
            }
            if (!prevRanges.isEmpty()) {
                final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
                prevRanges.toArray(removedRangesArray);
                mNMS.removeVpnUidRanges(nai.network.getNetId(), removedRangesArray);
                mNetd.networkRemoveUidRanges(
                        nai.network.netId, toUidRangeStableParcels(prevRanges));
            }
            final boolean wasFiltering = requiresVpnIsolation(nai, prevNc, nai.linkProperties);
            final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties);
+2 −56
Original line number Diff line number Diff line
@@ -60,7 +60,6 @@ import android.net.NetworkStack;
import android.net.NetworkStats;
import android.net.RouteInfo;
import android.net.TetherStatsParcel;
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.shared.NetdUtils;
import android.net.shared.RouteUtils;
@@ -1393,38 +1392,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
        }
    }

    private static UidRangeParcel makeUidRangeParcel(int start, int stop) {
        UidRangeParcel range = new UidRangeParcel();
        range.start = start;
        range.stop = stop;
        return range;
    }

    private static UidRangeParcel[] toStableParcels(UidRange[] ranges) {
        UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.length];
        for (int i = 0; i < ranges.length; i++) {
            stableRanges[i] = makeUidRangeParcel(ranges[i].start, ranges[i].stop);
        }
        return stableRanges;
    }

    @Override
    public void setAllowOnlyVpnForUids(boolean add, UidRange[] uidRanges)
            throws ServiceSpecificException {
        NetworkStack.checkNetworkStackPermission(mContext);
        try {
            mNetdService.networkRejectNonSecureVpn(add, toStableParcels(uidRanges));
        } catch (ServiceSpecificException e) {
            Log.w(TAG, "setAllowOnlyVpnForUids(" + add + ", " + Arrays.toString(uidRanges) + ")"
                    + ": netd command failed", e);
            throw e;
        } catch (RemoteException e) {
            Log.w(TAG, "setAllowOnlyVpnForUids(" + add + ", " + Arrays.toString(uidRanges) + ")"
                    + ": netd command failed", e);
            throw e.rethrowAsRuntimeException();
        }
    }

    private void applyUidCleartextNetworkPolicy(int uid, int policy) {
        final int policyValue;
        switch (policy) {
@@ -1552,27 +1519,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
        return stats;
    }

    @Override
    public void addVpnUidRanges(int netId, UidRange[] ranges) {
        NetworkStack.checkNetworkStackPermission(mContext);

        try {
            mNetdService.networkAddUidRanges(netId, toStableParcels(ranges));
        } catch (RemoteException | ServiceSpecificException e) {
            throw new IllegalStateException(e);
        }
    }

    @Override
    public void removeVpnUidRanges(int netId, UidRange[] ranges) {
        NetworkStack.checkNetworkStackPermission(mContext);
        try {
            mNetdService.networkRemoveUidRanges(netId, toStableParcels(ranges));
        } catch (RemoteException | ServiceSpecificException e) {
            throw new IllegalStateException(e);
        }
    }

    @Override
    public void setFirewallEnabled(boolean enabled) {
        enforceSystemUid();
@@ -1616,7 +1562,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
            ranges = new UidRangeParcel[] {
                // TODO: is there a better way of finding all existing users? If so, we could
                // specify their ranges here.
                makeUidRangeParcel(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE),
                new UidRangeParcel(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE),
            };
            // ... except for the UIDs that have allow rules.
            synchronized (mRulesLock) {
@@ -1647,7 +1593,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
                for (int i = 0; i < ranges.length; i++) {
                    if (rules.valueAt(i) == FIREWALL_RULE_DENY) {
                        int uid = rules.keyAt(i);
                        ranges[numUids] = makeUidRangeParcel(uid, uid);
                        ranges[numUids] = new UidRangeParcel(uid, uid);
                        numUids++;
                    }
                }
+39 −24
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.net.DnsResolver;
import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.Ikev2VpnProfile;
import android.net.IpPrefix;
@@ -68,6 +69,7 @@ import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -188,7 +190,8 @@ public class Vpn {

    private PendingIntent mStatusIntent;
    private volatile boolean mEnableTeardown = true;
    private final INetworkManagementService mNetd;
    private final INetworkManagementService mNms;
    private final INetd mNetd;
    @VisibleForTesting
    protected VpnConfig mConfig;
    private final NetworkProvider mNetworkProvider;
@@ -234,7 +237,7 @@ public class Vpn {
     * @see mLockdown
     */
    @GuardedBy("this")
    private final Set<UidRange> mBlockedUidsAsToldToNetd = new ArraySet<>();
    private final Set<UidRangeParcel> mBlockedUidsAsToldToNetd = new ArraySet<>();

    // The user id of initiating VPN.
    private final int mUserId;
@@ -363,22 +366,23 @@ public class Vpn {
        }
    }

    public Vpn(Looper looper, Context context, INetworkManagementService netService,
    public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
            @UserIdInt int userId, @NonNull KeyStore keyStore) {
        this(looper, context, new Dependencies(), netService, userId, keyStore,
        this(looper, context, new Dependencies(), netService, netd, userId, keyStore,
                new SystemServices(context), new Ikev2SessionCreator());
    }

    @VisibleForTesting
    protected Vpn(Looper looper, Context context, Dependencies deps,
            INetworkManagementService netService,
            INetworkManagementService netService, INetd netd,
            int userId, @NonNull KeyStore keyStore, SystemServices systemServices,
            Ikev2SessionCreator ikev2SessionCreator) {
        mContext = context;
        mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
        mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
        mDeps = deps;
        mNetd = netService;
        mNms = netService;
        mNetd = netd;
        mUserId = userId;
        mLooper = looper;
        mSystemServices = systemServices;
@@ -912,7 +916,7 @@ public class Vpn {
            }

            try {
                mNetd.denyProtect(mOwnerUID);
                mNms.denyProtect(mOwnerUID);
            } catch (Exception e) {
                Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
            }
@@ -922,7 +926,7 @@ public class Vpn {
            mOwnerUID = getAppUid(newPackage, mUserId);
            mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
            try {
                mNetd.allowProtect(mOwnerUID);
                mNms.allowProtect(mOwnerUID);
            } catch (Exception e) {
                Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
            }
@@ -1579,24 +1583,25 @@ public class Vpn {
            exemptedPackages = new ArrayList<>(mLockdownAllowlist);
            exemptedPackages.add(mPackage);
        }
        final Set<UidRange> rangesToTellNetdToRemove = new ArraySet<>(mBlockedUidsAsToldToNetd);
        final Set<UidRangeParcel> rangesToTellNetdToRemove =
                new ArraySet<>(mBlockedUidsAsToldToNetd);

        final Set<UidRange> rangesToTellNetdToAdd;
        final Set<UidRangeParcel> rangesToTellNetdToAdd;
        if (enforce) {
            final Set<UidRange> rangesThatShouldBeBlocked =
            final Set<UidRange> restrictedProfilesRanges =
                    createUserAndRestrictedProfilesRanges(mUserId,
                    /* allowedApplications */ null,
                    /* disallowedApplications */ exemptedPackages);
            final Set<UidRangeParcel> rangesThatShouldBeBlocked = new ArraySet<>();

            // The UID range of the first user (0-99999) would block the IPSec traffic, which comes
            // directly from the kernel and is marked as uid=0. So we adjust the range to allow
            // it through (b/69873852).
            for (UidRange range : rangesThatShouldBeBlocked) {
                if (range.start == 0) {
                    rangesThatShouldBeBlocked.remove(range);
                    if (range.stop != 0) {
                        rangesThatShouldBeBlocked.add(new UidRange(1, range.stop));
                    }
            for (UidRange range : restrictedProfilesRanges) {
                if (range.start == 0 && range.stop != 0) {
                    rangesThatShouldBeBlocked.add(new UidRangeParcel(1, range.stop));
                } else if (range.start != 0) {
                    rangesThatShouldBeBlocked.add(new UidRangeParcel(range.start, range.stop));
                }
            }

@@ -1628,13 +1633,13 @@ public class Vpn {
     *         including added ranges that already existed or removed ones that didn't.
     */
    @GuardedBy("this")
    private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRange> ranges) {
    private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRangeParcel> ranges) {
        if (ranges.size() == 0) {
            return true;
        }
        final UidRange[] rangesArray = ranges.toArray(new UidRange[ranges.size()]);
        final UidRangeParcel[] stableRanges = ranges.toArray(new UidRangeParcel[ranges.size()]);
        try {
            mNetd.setAllowOnlyVpnForUids(enforce, rangesArray);
            mNetd.networkRejectNonSecureVpn(enforce, stableRanges);
        } catch (RemoteException | RuntimeException e) {
            Log.e(TAG, "Updating blocked=" + enforce
                    + " for UIDs " + Arrays.toString(ranges.toArray()) + " failed", e);
@@ -1849,8 +1854,18 @@ public class Vpn {
        if (mNetworkInfo.isConnected()) {
            return !appliesToUid(uid);
        } else {
            return UidRange.containsUid(mBlockedUidsAsToldToNetd, uid);
            return containsUid(mBlockedUidsAsToldToNetd, uid);
        }
    }

    private boolean containsUid(Collection<UidRangeParcel> ranges, int uid) {
        if (ranges == null) return false;
        for (UidRangeParcel range : ranges) {
            if (range.start <= uid && uid <= range.stop) {
                return true;
            }
        }
        return false;
    }

    private void updateAlwaysOnNotification(DetailedState networkState) {
@@ -2495,7 +2510,7 @@ public class Vpn {
                                    address /* unused */,
                                    address /* unused */,
                                    network);
                    mNetd.setInterfaceUp(mTunnelIface.getInterfaceName());
                    mNms.setInterfaceUp(mTunnelIface.getInterfaceName());

                    mSession = mIkev2SessionCreator.createIkeSession(
                            mContext,
+12 −5
Original line number Diff line number Diff line
@@ -187,6 +187,7 @@ import android.net.RouteInfo;
import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.Uri;
import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
@@ -1053,7 +1054,7 @@ public class ConnectivityServiceTest {

        public MockVpn(int userId) {
            super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
                    userId, mock(KeyStore.class));
                    mMockNetd, userId, mock(KeyStore.class));
            mConfig = new VpnConfig();
        }

@@ -1092,10 +1093,11 @@ public class ConnectivityServiceTest {
            mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
                    mNetworkCapabilities);
            mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
            verify(mNetworkManagementService, times(1))
                    .addVpnUidRanges(eq(mMockVpn.getNetId()), eq(uids.toArray(new UidRange[0])));
            verify(mNetworkManagementService, never())
                    .removeVpnUidRanges(eq(mMockVpn.getNetId()), any());

            verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()),
                    eq(toUidRangeStableParcels(uids)));
            verify(mMockNetd, never())
                    .networkRemoveUidRanges(eq(mMockVpn.getNetId()), any());
            mAgentRegistered = true;
            mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
            mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
@@ -1167,6 +1169,11 @@ public class ConnectivityServiceTest {
        }
    }

    private UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set<UidRange> ranges) {
        return ranges.stream().map(
                r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new);
    }

    private void mockVpn(int uid) {
        synchronized (mService.mVpns) {
            int userId = UserHandle.getUserId(uid);
Loading