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

Commit 33e0d8cb authored by lucaslin's avatar lucaslin
Browse files

Add startOp and finishOp to track the behavior of VPN app

Record VPN appOps data is helpful in future analysis to identity
more PHA(potentially harmful application) VPN.

Bug: 197135981
Test: 1. atest FrameworksNetTests
      2. Manual test by using legacy VPN, VPN app which uses
         VpnService, VPN app which uses VpnManager
Change-Id: Id2d9ab9f76940fca3fd4263c707ea9198494e0d3
parent e89ec676
Loading
Loading
Loading
Loading
+74 −14
Original line number Diff line number Diff line
@@ -126,6 +126,7 @@ import com.android.server.net.BaseNetworkObserver;
import libcore.io.IoUtils;

import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -195,6 +196,7 @@ public class Vpn {

    private final Context mContext;
    private final ConnectivityManager mConnectivityManager;
    private final AppOpsManager mAppOpsManager;
    // The context is for specific user which is created from mUserId
    private final Context mUserIdContext;
    @VisibleForTesting final Dependencies mDeps;
@@ -407,6 +409,46 @@ public class Vpn {
        public boolean isInterfacePresent(final Vpn vpn, final String iface) {
            return vpn.jniCheck(iface) != 0;
        }

        /**
         * @see ParcelFileDescriptor#adoptFd(int)
         */
        public ParcelFileDescriptor adoptFd(Vpn vpn, int mtu) {
            return ParcelFileDescriptor.adoptFd(jniCreate(vpn, mtu));
        }

        /**
         * Call native method to create the VPN interface and return the FileDescriptor of /dev/tun.
         */
        public int jniCreate(Vpn vpn, int mtu) {
            return vpn.jniCreate(mtu);
        }

        /**
         * Call native method to get the interface name of VPN.
         */
        public String jniGetName(Vpn vpn, int fd) {
            return vpn.jniGetName(fd);
        }

        /**
         * Call native method to set the VPN addresses and return the number of addresses.
         */
        public int jniSetAddresses(Vpn vpn, String interfaze, String addresses) {
            return vpn.jniSetAddresses(interfaze, addresses);
        }

        /**
         * @see IoUtils#setBlocking(FileDescriptor, boolean)
         */
        public void setBlocking(FileDescriptor fd, boolean blocking) {
            try {
                IoUtils.setBlocking(fd, blocking);
            } catch (IOException e) {
                throw new IllegalStateException(
                        "Cannot set tunnel's fd as blocking=" + blocking, e);
            }
        }
    }

    public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
@@ -431,6 +473,7 @@ public class Vpn {
        mVpnProfileStore = vpnProfileStore;
        mContext = context;
        mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
        mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
        mDeps = deps;
        mNms = netService;
@@ -826,7 +869,6 @@ public class Vpn {
            VpnProfile profile = getVpnProfilePrivileged(alwaysOnPackage);
            if (profile != null) {
                startVpnProfilePrivileged(profile, alwaysOnPackage);

                // If the above startVpnProfilePrivileged() call returns, the Ikev2VpnProfile was
                // correctly parsed, and the VPN has started running in a different thread. The only
                // other possibility is that the above call threw an exception, which will be
@@ -974,9 +1016,15 @@ public class Vpn {
                } catch (Exception e) {
                    // ignore
                }
                mAppOpsManager.finishOp(
                        AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE, mOwnerUID, mPackage, null);
                mContext.unbindService(mConnection);
                cleanupVpnStateLocked();
            } else if (mVpnRunner != null) {
                if (!VpnConfig.LEGACY_VPN.equals(mPackage)) {
                    mAppOpsManager.finishOp(
                            AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage, null);
                }
                // cleanupVpnStateLocked() is called from mVpnRunner.exit()
                mVpnRunner.exit();
            }
@@ -1041,10 +1089,8 @@ public class Vpn {
                    return false;
            }

            final AppOpsManager appOpMgr =
                    (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
            for (final String appOpStr : toChange) {
                appOpMgr.setMode(
                mAppOpsManager.setMode(
                        appOpStr,
                        uid,
                        packageName,
@@ -1366,9 +1412,9 @@ public class Vpn {
        Set<Range<Integer>> oldUsers = mNetworkCapabilities.getUids();

        // Configure the interface. Abort if any of these steps fails.
        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
        final ParcelFileDescriptor tun = mDeps.adoptFd(this, config.mtu);
        try {
            String interfaze = jniGetName(tun.getFd());
            final String interfaze = mDeps.jniGetName(this, tun.getFd());

            // TEMP use the old jni calls until there is support for netd address setting
            StringBuilder builder = new StringBuilder();
@@ -1376,7 +1422,7 @@ public class Vpn {
                builder.append(" ");
                builder.append(address);
            }
            if (jniSetAddresses(interfaze, builder.toString()) < 1) {
            if (mDeps.jniSetAddresses(this, interfaze, builder.toString()) < 1) {
                throw new IllegalArgumentException("At least one address must be specified");
            }
            Connection connection = new Connection();
@@ -1422,11 +1468,11 @@ public class Vpn {
                jniReset(oldInterface);
            }

            try {
                IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
            } catch (IOException e) {
                throw new IllegalStateException(
                        "Cannot set tunnel's fd as blocking=" + config.blocking, e);
            mDeps.setBlocking(tun.getFileDescriptor(), config.blocking);
            // Record that the VPN connection is established by an app which uses VpnService API.
            if (oldNetworkAgent != mNetworkAgent) {
                mAppOpsManager.startOp(
                        AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE, mOwnerUID, mPackage, null, null);
            }
        } catch (RuntimeException e) {
            IoUtils.closeQuietly(tun);
@@ -1781,9 +1827,17 @@ public class Vpn {
            synchronized (Vpn.this) {
                if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
                    if (mConnection != null) {
                        mAppOpsManager.finishOp(
                                AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE, mOwnerUID, mPackage,
                                null);
                        mContext.unbindService(mConnection);
                        cleanupVpnStateLocked();
                    } else if (mVpnRunner != null) {
                        if (!VpnConfig.LEGACY_VPN.equals(mPackage)) {
                            mAppOpsManager.finishOp(
                                    AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage,
                                    null);
                        }
                        // cleanupVpnStateLocked() is called from mVpnRunner.exit()
                        mVpnRunner.exit();
                    }
@@ -3254,8 +3308,7 @@ public class Vpn {
     *
     * @param packageName the package name of the app provisioning this profile
     */
    public synchronized void startVpnProfile(
            @NonNull String packageName) {
    public synchronized void startVpnProfile(@NonNull String packageName) {
        requireNonNull(packageName, "No package name provided");

        enforceNotRestrictedUser();
@@ -3318,6 +3371,13 @@ public class Vpn {
                    Log.d(TAG, "Unknown VPN profile type: " + profile.type);
                    break;
            }

            // Record that the VPN connection is established by an app which uses VpnManager API.
            if (!VpnConfig.LEGACY_VPN.equals(packageName)) {
                mAppOpsManager.startOp(
                        AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage, null,
                        null);
            }
        } catch (GeneralSecurityException e) {
            // Reset mConfig
            mConfig = null;