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

Commit 602a43cd authored by Chad Brubaker's avatar Chad Brubaker Committed by Steve Kondik
Browse files

Only allow System apps to make VPN exempt routes

requestRouteToHost will only allow system applications to make routes
exempt from the VPN's routing rules.

If a VPN is currently running and a non-system app requests a route it
will only succeed if that host is currently covered by a VPN exempt
routing rule. Otherwise it will fail.

For example, if a VPN is running and the MMS network is brought online
those routes will be added as VPN exempt. If an application then tries
to request a route to a MMS endpoint it will succeed because the routes
already exist. If an application tries to request a route to a host
covered by the VPN the call will fail.

Bug: 12937545
Change-Id: If7bcec91bbb96c62c8fb69748c975847e6c00b6f
parent fe9b529c
Loading
Loading
Loading
Loading
+69 −6
Original line number Diff line number Diff line
@@ -46,7 +46,9 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -418,6 +420,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {

    private SettingsObserver mSettingsObserver;

    private AppOpsManager mAppOpsManager;

    NetworkConfig[] mNetConfigs;
    int mNetworksDefined;

@@ -708,6 +712,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
        filter = new IntentFilter();
        filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
        mContext.registerReceiver(mProvisioningReceiver, filter);

        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
    }

    /**
@@ -1530,6 +1536,40 @@ public class ConnectivityService extends IConnectivityManager.Stub {
        }
    }

    /**
     * Check if the address falls into any of currently running VPN's route's.
     */
    private boolean isAddressUnderVpn(InetAddress address) {
        synchronized (mVpns) {
            synchronized (mRoutesLock) {
                int uid = UserHandle.getCallingUserId();
                Vpn vpn = mVpns.get(uid);
                if (vpn == null) {
                    return false;
                }

                // Check if an exemption exists for this address.
                for (LinkAddress destination : mExemptAddresses) {
                    if (!NetworkUtils.addressTypeMatches(address, destination.getAddress())) {
                        continue;
                    }

                    int prefix = destination.getNetworkPrefixLength();
                    InetAddress addrMasked = NetworkUtils.getNetworkPart(address, prefix);
                    InetAddress destMasked = NetworkUtils.getNetworkPart(destination.getAddress(),
                            prefix);

                    if (addrMasked.equals(destMasked)) {
                        return false;
                    }
                }

                // Finally check if the address is covered by the VPN.
                return vpn.isAddressCovered(address);
            }
        }
    }

    /**
     * @deprecated use requestRouteToHostAddress instead
     *
@@ -1566,6 +1606,34 @@ public class ConnectivityService extends IConnectivityManager.Stub {
        if (mProtectedNetworks.contains(networkType)) {
            enforceConnectivityInternalPermission();
        }
        boolean exempt;
        InetAddress addr;
        try {
            addr = InetAddress.getByAddress(hostAddress);
        } catch (UnknownHostException e) {
            if (DBG) log("requestRouteToHostAddress got " + e.toString());
            return false;
        }
        // System apps may request routes bypassing the VPN to keep other networks working.
        if (Binder.getCallingUid() == Process.SYSTEM_UID) {
            exempt = true;
        } else {
            mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
            try {
                ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(packageName,
                        0);
                exempt = (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
            } catch (NameNotFoundException e) {
                throw new IllegalArgumentException("Failed to find calling package details", e);
            }
        }

        // Non-exempt routeToHost's can only be added if the host is not covered by the VPN.
        // This can be either because the VPN's routes do not cover the destination or a
        // system application added an exemption that covers this destination.
        if (!exempt && isAddressUnderVpn(addr)) {
            return false;
        }

        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
            if (DBG) log("requestRouteToHostAddress on invalid network: " + networkType);
@@ -1589,18 +1657,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
        }
        final long token = Binder.clearCallingIdentity();
        try {
            InetAddress addr = InetAddress.getByAddress(hostAddress);
            LinkProperties lp = tracker.getLinkProperties();
            boolean ok = addRouteToAddress(lp, addr, EXEMPT);
            boolean ok = addRouteToAddress(lp, addr, exempt);
            if (DBG) log("requestRouteToHostAddress ok=" + ok);
            return ok;
        } catch (UnknownHostException e) {
            if (DBG) log("requestRouteToHostAddress got " + e.toString());
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        if (DBG) log("requestRouteToHostAddress X bottom return false");
        return false;
    }

    private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable,
+14 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.NetworkInfo;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.NetworkInfo.DetailedState;
import android.os.Binder;
@@ -77,6 +78,7 @@ import com.android.server.net.BaseNetworkObserver;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
@@ -445,6 +447,18 @@ public class Vpn extends BaseNetworkStateTracker {
        return tun;
    }

    /**
     * Check if a given address is covered by the VPN's routing rules.
     */
    public boolean isAddressCovered(InetAddress address) {
        synchronized (Vpn.this) {
            if (!isRunningLocked()) {
                return false;
            }
            return RouteInfo.selectBestRoute(mConfig.routes, address) != null;
        }
    }

    private boolean isRunningLocked() {
        return mVpnUsers != null;
    }