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

Commit 28ab1a69 authored by Jean Chalard's avatar Jean Chalard Committed by Automerger Merge Worker
Browse files

Merge "Restart Bypassable VPNs when changing lockdown mode state" am:...

Merge "Restart Bypassable VPNs when changing lockdown mode state" am: 15e5cec7 am: 3a2d2ef7 am: 7582e5ac am: ecb50f8f

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2221794



Change-Id: I6ca7ad45e6feb456473c52330ef278e5ba61bfe9
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 9b4f6b58 ecb50f8f
Loading
Loading
Loading
Loading
+39 −18
Original line number Original line Diff line number Diff line
@@ -707,6 +707,14 @@ public class Vpn {
                boolean isIpv4) {
                boolean isIpv4) {
            return MtuUtils.getMtu(childProposals, maxMtu, underlyingMtu, isIpv4);
            return MtuUtils.getMtu(childProposals, maxMtu, underlyingMtu, isIpv4);
        }
        }

        /** Verify the binder calling UID is the one passed in arguments */
        public void verifyCallingUidAndPackage(Context context, String packageName, int userId) {
            final int callingUid = Binder.getCallingUid();
            if (getAppUid(context, packageName, userId) != callingUid) {
                throw new SecurityException(packageName + " does not belong to uid " + callingUid);
            }
        }
    }
    }


    @VisibleForTesting
    @VisibleForTesting
@@ -754,7 +762,7 @@ public class Vpn {
        mUserManager = mContext.getSystemService(UserManager.class);
        mUserManager = mContext.getSystemService(UserManager.class);


        mPackage = VpnConfig.LEGACY_VPN;
        mPackage = VpnConfig.LEGACY_VPN;
        mOwnerUID = getAppUid(mPackage, mUserId);
        mOwnerUID = getAppUid(mContext, mPackage, mUserId);
        mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(mPackage);
        mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(mPackage);


        try {
        try {
@@ -851,7 +859,7 @@ public class Vpn {
    }
    }


    /**
    /**
     * Chooses whether to force all connections to go though VPN.
     * Chooses whether to force all connections to go through VPN.
     *
     *
     * Used to enable/disable legacy VPN lockdown.
     * Used to enable/disable legacy VPN lockdown.
     *
     *
@@ -859,7 +867,7 @@ public class Vpn {
     * {@link #setAlwaysOnPackage(String, boolean, List<String>)}; previous settings from calling
     * {@link #setAlwaysOnPackage(String, boolean, List<String>)}; previous settings from calling
     * that function will be replaced and saved with the always-on state.
     * that function will be replaced and saved with the always-on state.
     *
     *
     * @param lockdown whether to prevent all traffic outside of a VPN.
     * @param lockdown whether to prevent all traffic outside of the VPN.
     */
     */
    public synchronized void setLockdown(boolean lockdown) {
    public synchronized void setLockdown(boolean lockdown) {
        enforceControlPermissionOrInternalCaller();
        enforceControlPermissionOrInternalCaller();
@@ -1136,6 +1144,7 @@ public class Vpn {
            mAlwaysOn = false;
            mAlwaysOn = false;
        }
        }


        final boolean oldLockdownState = mLockdown;
        mLockdown = (mAlwaysOn && lockdown);
        mLockdown = (mAlwaysOn && lockdown);
        mLockdownAllowlist = (mLockdown && lockdownAllowlist != null)
        mLockdownAllowlist = (mLockdown && lockdownAllowlist != null)
                ? Collections.unmodifiableList(new ArrayList<>(lockdownAllowlist))
                ? Collections.unmodifiableList(new ArrayList<>(lockdownAllowlist))
@@ -1146,6 +1155,13 @@ public class Vpn {
        if (isCurrentPreparedPackage(packageName)) {
        if (isCurrentPreparedPackage(packageName)) {
            updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
            updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
            setVpnForcedLocked(mLockdown);
            setVpnForcedLocked(mLockdown);

            // Lockdown forces the VPN to be non-bypassable (see #agentConnect) because it makes
            // no sense for a VPN to be bypassable when connected but not when not connected.
            // As such, changes in lockdown need to restart the agent.
            if (mNetworkAgent != null && oldLockdownState != mLockdown) {
                startNewNetworkAgent(mNetworkAgent, "Lockdown mode changed");
            }
        } else {
        } else {
            // Prepare this app. The notification will update as a side-effect of updateState().
            // Prepare this app. The notification will update as a side-effect of updateState().
            // It also calls setVpnForcedLocked().
            // It also calls setVpnForcedLocked().
@@ -1383,7 +1399,8 @@ public class Vpn {
        // We can't just check that packageName matches mPackage, because if the app was uninstalled
        // We can't just check that packageName matches mPackage, because if the app was uninstalled
        // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the
        // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the
        // calling package may not be the same as the prepared package. Check both UID and package.
        // calling package may not be the same as the prepared package. Check both UID and package.
        return getAppUid(packageName, mUserId) == mOwnerUID && mPackage.equals(packageName);
        return getAppUid(mContext, packageName, mUserId) == mOwnerUID
                && mPackage.equals(packageName);
    }
    }


    /** Prepare the VPN for the given package. Does not perform permission checks. */
    /** Prepare the VPN for the given package. Does not perform permission checks. */
@@ -1424,7 +1441,7 @@ public class Vpn {


            Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
            Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
            mPackage = newPackage;
            mPackage = newPackage;
            mOwnerUID = getAppUid(newPackage, mUserId);
            mOwnerUID = getAppUid(mContext, newPackage, mUserId);
            mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
            mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
            try {
            try {
                mNms.allowProtect(mOwnerUID);
                mNms.allowProtect(mOwnerUID);
@@ -1445,7 +1462,7 @@ public class Vpn {
        // Check if the caller is authorized.
        // Check if the caller is authorized.
        enforceControlPermissionOrInternalCaller();
        enforceControlPermissionOrInternalCaller();


        final int uid = getAppUid(packageName, mUserId);
        final int uid = getAppUid(mContext, packageName, mUserId);
        if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
        if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
            // Authorization for nonexistent packages (or fake ones) can't be updated.
            // Authorization for nonexistent packages (or fake ones) can't be updated.
            return false;
            return false;
@@ -1525,11 +1542,11 @@ public class Vpn {
                || isVpnServicePreConsented(context, packageName);
                || isVpnServicePreConsented(context, packageName);
    }
    }


    private int getAppUid(final String app, final int userId) {
    private static int getAppUid(final Context context, final String app, final int userId) {
        if (VpnConfig.LEGACY_VPN.equals(app)) {
        if (VpnConfig.LEGACY_VPN.equals(app)) {
            return Process.myUid();
            return Process.myUid();
        }
        }
        PackageManager pm = mContext.getPackageManager();
        PackageManager pm = context.getPackageManager();
        final long token = Binder.clearCallingIdentity();
        final long token = Binder.clearCallingIdentity();
        try {
        try {
            return pm.getPackageUidAsUser(app, userId);
            return pm.getPackageUidAsUser(app, userId);
@@ -1658,6 +1675,10 @@ public class Vpn {
     */
     */
    private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) {
    private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) {
        // NetworkAgentConfig cannot be updated without registering a new NetworkAgent.
        // NetworkAgentConfig cannot be updated without registering a new NetworkAgent.
        // Strictly speaking, bypassability is affected by lockdown and therefore it's possible
        // it doesn't actually change even if mConfig.allowBypass changed. It might be theoretically
        // possible to do handover in this case, but this is far from obvious to VPN authors and
        // it's simpler if the rule is just "can't update in place if you change allow bypass".
        if (oldConfig.allowBypass != mConfig.allowBypass) {
        if (oldConfig.allowBypass != mConfig.allowBypass) {
            Log.i(TAG, "Handover not possible due to changes to allowBypass");
            Log.i(TAG, "Handover not possible due to changes to allowBypass");
            return false;
            return false;
@@ -1699,10 +1720,11 @@ public class Vpn {
        mLegacyState = LegacyVpnInfo.STATE_CONNECTING;
        mLegacyState = LegacyVpnInfo.STATE_CONNECTING;
        updateState(DetailedState.CONNECTING, "agentConnect");
        updateState(DetailedState.CONNECTING, "agentConnect");


        final boolean bypassable = mConfig.allowBypass && !mLockdown;
        final NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig.Builder()
        final NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig.Builder()
                .setLegacyType(ConnectivityManager.TYPE_VPN)
                .setLegacyType(ConnectivityManager.TYPE_VPN)
                .setLegacyTypeName("VPN")
                .setLegacyTypeName("VPN")
                .setBypassableVpn(mConfig.allowBypass && !mLockdown)
                .setBypassableVpn(bypassable)
                .setVpnRequiresValidation(mConfig.requiresInternetValidation)
                .setVpnRequiresValidation(mConfig.requiresInternetValidation)
                .setLocalRoutesExcludedForVpn(mConfig.excludeLocalRoutes)
                .setLocalRoutesExcludedForVpn(mConfig.excludeLocalRoutes)
                .build();
                .build();
@@ -1716,7 +1738,7 @@ public class Vpn {
        capsBuilder.setTransportInfo(new VpnTransportInfo(
        capsBuilder.setTransportInfo(new VpnTransportInfo(
                getActiveVpnType(),
                getActiveVpnType(),
                mConfig.session,
                mConfig.session,
                mConfig.allowBypass,
                bypassable,
                expensive));
                expensive));


        // Only apps targeting Q and above can explicitly declare themselves as metered.
        // Only apps targeting Q and above can explicitly declare themselves as metered.
@@ -1747,6 +1769,10 @@ public class Vpn {
            Binder.restoreCallingIdentity(token);
            Binder.restoreCallingIdentity(token);
        }
        }
        updateState(DetailedState.CONNECTED, "agentConnect");
        updateState(DetailedState.CONNECTED, "agentConnect");
        if (isIkev2VpnRunner()) {
            final IkeSessionWrapper session = ((IkeV2VpnRunner) mVpnRunner).mSession;
            if (null != session) session.setUnderpinnedNetwork(mNetworkAgent.getNetwork());
        }
    }
    }


    private static boolean areLongLivedTcpConnectionsExpensive(@NonNull VpnRunner runner) {
    private static boolean areLongLivedTcpConnectionsExpensive(@NonNull VpnRunner runner) {
@@ -1941,7 +1967,7 @@ public class Vpn {
    private SortedSet<Integer> getAppsUids(List<String> packageNames, int userId) {
    private SortedSet<Integer> getAppsUids(List<String> packageNames, int userId) {
        SortedSet<Integer> uids = new TreeSet<>();
        SortedSet<Integer> uids = new TreeSet<>();
        for (String app : packageNames) {
        for (String app : packageNames) {
            int uid = getAppUid(app, userId);
            int uid = getAppUid(mContext, app, userId);
            if (uid != -1) uids.add(uid);
            if (uid != -1) uids.add(uid);
            // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
            // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
            // ConnectivityServiceTest.
            // ConnectivityServiceTest.
@@ -3260,7 +3286,6 @@ public class Vpn {
                            prepareStatusIntent();
                            prepareStatusIntent();
                        }
                        }
                        agentConnect(this::onValidationStatus);
                        agentConnect(this::onValidationStatus);
                        mSession.setUnderpinnedNetwork(mNetworkAgent.getNetwork());
                        return; // Link properties are already sent.
                        return; // Link properties are already sent.
                    } else {
                    } else {
                        // Underlying networks also set in agentConnect()
                        // Underlying networks also set in agentConnect()
@@ -3377,7 +3402,6 @@ public class Vpn {
                    if (!removedAddrs.isEmpty()) {
                    if (!removedAddrs.isEmpty()) {
                        startNewNetworkAgent(
                        startNewNetworkAgent(
                                mNetworkAgent, "MTU too low for IPv6; restarting network agent");
                                mNetworkAgent, "MTU too low for IPv6; restarting network agent");
                        mSession.setUnderpinnedNetwork(mNetworkAgent.getNetwork());


                        for (LinkAddress removed : removedAddrs) {
                        for (LinkAddress removed : removedAddrs) {
                            mTunnelIface.removeAddress(
                            mTunnelIface.removeAddress(
@@ -3656,7 +3680,7 @@ public class Vpn {
            final VpnTransportInfo info = new VpnTransportInfo(
            final VpnTransportInfo info = new VpnTransportInfo(
                    getActiveVpnType(),
                    getActiveVpnType(),
                    mConfig.session,
                    mConfig.session,
                    mConfig.allowBypass,
                    mConfig.allowBypass && !mLockdown,
                    areLongLivedTcpConnectionsExpensive(keepaliveDelaySec));
                    areLongLivedTcpConnectionsExpensive(keepaliveDelaySec));
            final boolean ncUpdateRequired = !info.equals(mNetworkCapabilities.getTransportInfo());
            final boolean ncUpdateRequired = !info.equals(mNetworkCapabilities.getTransportInfo());
            if (ncUpdateRequired) {
            if (ncUpdateRequired) {
@@ -4513,10 +4537,7 @@ public class Vpn {
    }
    }


    private void verifyCallingUidAndPackage(String packageName) {
    private void verifyCallingUidAndPackage(String packageName) {
        final int callingUid = Binder.getCallingUid();
        mDeps.verifyCallingUidAndPackage(mContext, packageName, mUserId);
        if (getAppUid(packageName, mUserId) != callingUid) {
            throw new SecurityException(packageName + " does not belong to uid " + callingUid);
        }
    }
    }


    @VisibleForTesting
    @VisibleForTesting