Home | History | Annotate | Download | only in vpnfirewall
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.vpnfirewall;
     18 
     19 import android.R;
     20 import android.app.Notification;
     21 import android.app.NotificationChannel;
     22 import android.app.NotificationManager;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.pm.PackageManager.NameNotFoundException;
     26 import android.net.ConnectivityManager;
     27 import android.net.Network;
     28 import android.net.NetworkCapabilities;
     29 import android.net.NetworkRequest;
     30 import android.net.VpnService;
     31 import android.os.Bundle;
     32 import android.os.ParcelFileDescriptor;
     33 import android.os.UserManager;
     34 import android.text.TextUtils;
     35 import android.util.Log;
     36 import android.os.SystemProperties;
     37 
     38 import java.io.IOException;
     39 import java.net.InetAddress;
     40 import java.net.UnknownHostException;
     41 
     42 public class ReflectorVpnService extends VpnService {
     43     private static final String TAG = "ReflectorVpnService";
     44     private static final String DEVICE_AND_PROFILE_OWNER_PACKAGE =
     45         "com.android.cts.deviceandprofileowner";
     46     private static final String ACTION_VPN_IS_UP = "com.android.cts.vpnfirewall.VPN_IS_UP";
     47     private static final String ACTION_VPN_ON_START = "com.android.cts.vpnfirewall.VPN_ON_START";
     48     private static final int NOTIFICATION_ID = 1;
     49     private static final String NOTIFICATION_CHANNEL_ID = TAG;
     50     private static final int MTU = 1799;
     51 
     52     private ParcelFileDescriptor mFd = null;
     53     private PingReflector mPingReflector = null;
     54     private ConnectivityManager mConnectivityManager = null;
     55     private ConnectivityManager.NetworkCallback mNetworkCallback = null;
     56 
     57     private static final String RESTRICTION_ADDRESSES = "vpn.addresses";
     58     private static final String RESTRICTION_ROUTES = "vpn.routes";
     59     private static final String RESTRICTION_ALLOWED = "vpn.allowed";
     60     private static final String RESTRICTION_DISALLOWED = "vpn.disallowed";
     61     /** Service won't create the tunnel, to test lockdown behavior in case of VPN failure. */
     62     private static final String RESTRICTION_DONT_ESTABLISH = "vpn.dont_establish";
     63     private static final String EXTRA_ALWAYS_ON = "always-on";
     64     private static final String EXTRA_LOCKDOWN = "lockdown";
     65 
     66     @Override
     67     public int onStartCommand(Intent intent, int flags, int startId) {
     68         // Put ourself in the foreground to stop the system killing us while we wait for orders from
     69         // the hostside test.
     70         NotificationManager notificationManager = getSystemService(NotificationManager.class);
     71         notificationManager.createNotificationChannel(new NotificationChannel(
     72                 NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
     73                 NotificationManager.IMPORTANCE_DEFAULT));
     74         startForeground(NOTIFICATION_ID, new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
     75                 .setSmallIcon(R.drawable.ic_dialog_alert)
     76                 .build());
     77         start();
     78         return START_NOT_STICKY;
     79     }
     80 
     81     @Override
     82     public void onCreate() {
     83         mConnectivityManager = getSystemService(ConnectivityManager.class);
     84     }
     85 
     86     @Override
     87     public void onDestroy() {
     88         stop();
     89         NotificationManager notificationManager = getSystemService(NotificationManager.class);
     90         notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
     91         ensureNetworkCallbackUnregistered();
     92         super.onDestroy();
     93     }
     94 
     95     private void ensureNetworkCallbackUnregistered() {
     96         if (null == mConnectivityManager || null == mNetworkCallback) return;
     97         mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
     98         mNetworkCallback = null;
     99     }
    100 
    101     private void start() {
    102         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
    103         final Bundle restrictions = um.getApplicationRestrictions(getPackageName());
    104 
    105         final Intent intent = new Intent(ACTION_VPN_ON_START);
    106         intent.setPackage(DEVICE_AND_PROFILE_OWNER_PACKAGE);
    107         sendBroadcast(intent);
    108 
    109         if (restrictions.getBoolean(RESTRICTION_DONT_ESTABLISH)) {
    110             stopSelf();
    111             return;
    112         }
    113 
    114         VpnService.prepare(this);
    115 
    116         ensureNetworkCallbackUnregistered();
    117         final NetworkRequest request = new NetworkRequest.Builder()
    118             .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    119             .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    120             .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    121             .build();
    122         mNetworkCallback = new ConnectivityManager.NetworkCallback() {
    123                 @Override
    124                 public void onAvailable(final Network net) {
    125                     final Intent intent = new Intent(ACTION_VPN_IS_UP);
    126                     intent.setPackage(DEVICE_AND_PROFILE_OWNER_PACKAGE);
    127                     intent.putExtra(EXTRA_ALWAYS_ON, isAlwaysOn());
    128                     intent.putExtra(EXTRA_LOCKDOWN, isLockdownEnabled());
    129                     sendBroadcast(intent);
    130                     ensureNetworkCallbackUnregistered();
    131                 }
    132             };
    133         mConnectivityManager.registerNetworkCallback(request, mNetworkCallback);
    134 
    135         Builder builder = new Builder();
    136 
    137 
    138         String[] addressArray = restrictions.getStringArray(RESTRICTION_ADDRESSES);
    139         if (addressArray == null) {
    140             // Addresses for IPv4/IPv6 documentation purposes according to rfc5737/rfc3849.
    141             addressArray = new String[] {"192.0.2.3/32", "2001:db8:1:2::/128"};
    142         };
    143         for (int i = 0; i < addressArray.length; i++) {
    144             String[] prefixAndMask = addressArray[i].split("/");
    145             try {
    146                 InetAddress address = InetAddress.getByName(prefixAndMask[0]);
    147                 int prefixLength = Integer.parseInt(prefixAndMask[1]);
    148                 builder.addAddress(address, prefixLength);
    149             } catch (NumberFormatException | UnknownHostException e) {
    150                 Log.w(TAG, "Ill-formed address: " + addressArray[i]);
    151                 continue;
    152             }
    153         }
    154 
    155         String[] routeArray = restrictions.getStringArray(RESTRICTION_ROUTES);
    156         if (routeArray == null) {
    157             routeArray = new String[] {"0.0.0.0/0", "::/0"};
    158         }
    159         for (int i = 0; i < routeArray.length; i++) {
    160             String[] prefixAndMask = routeArray[i].split("/");
    161             try {
    162                 InetAddress address = InetAddress.getByName(prefixAndMask[0]);
    163                 int prefixLength = Integer.parseInt(prefixAndMask[1]);
    164                 builder.addRoute(address, prefixLength);
    165             } catch (NumberFormatException | UnknownHostException e) {
    166                 Log.w(TAG, "Ill-formed route: " + routeArray[i]);
    167                 continue;
    168             }
    169         }
    170 
    171         String[] allowedArray = restrictions.getStringArray(RESTRICTION_ALLOWED);
    172         if (allowedArray != null) {
    173             for (int i = 0; i < allowedArray.length; i++) {
    174                 String allowedPackage = allowedArray[i];
    175                 if (!TextUtils.isEmpty(allowedPackage)) {
    176                     try {
    177                         builder.addAllowedApplication(allowedPackage);
    178                     } catch(NameNotFoundException e) {
    179                         Log.w(TAG, "Allowed package not found: " + allowedPackage);
    180                         continue;
    181                     }
    182                 }
    183             }
    184         }
    185 
    186         String[] disallowedArray = restrictions.getStringArray(RESTRICTION_DISALLOWED);
    187         if (disallowedArray != null) {
    188             for (int i = 0; i < disallowedArray.length; i++) {
    189                 String disallowedPackage = disallowedArray[i];
    190                 if (!TextUtils.isEmpty(disallowedPackage)) {
    191                     try {
    192                         builder.addDisallowedApplication(disallowedPackage);
    193                     } catch(NameNotFoundException e) {
    194                         Log.w(TAG, "Disallowed package not found: " + disallowedPackage);
    195                         continue;
    196                     }
    197                 }
    198             }
    199         }
    200 
    201         if (allowedArray == null &&
    202             (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
    203             || SystemProperties.getInt("service.adb.tcp.port", -1) > -1)) {
    204             try {
    205                 // If adb TCP port opened the test may be running by adb over network.
    206                 // Add com.android.shell application into blacklist to exclude adb socket
    207                 // for VPN tests.
    208                 builder.addDisallowedApplication("com.android.shell");
    209             } catch(NameNotFoundException e) {
    210                 Log.w(TAG, "com.android.shell not found");
    211             }
    212         }
    213 
    214         builder.setMtu(MTU);
    215         builder.setBlocking(true);
    216         builder.setSession(TAG);
    217 
    218         mFd = builder.establish();
    219         if (mFd == null) {
    220             Log.e(TAG, "Unable to establish file descriptor for VPN connection");
    221             return;
    222         }
    223         Log.i(TAG, "Established, fd=" + mFd.getFd());
    224 
    225         mPingReflector = new PingReflector(mFd.getFileDescriptor(), MTU);
    226         mPingReflector.start();
    227     }
    228 
    229     private void stop() {
    230         if (mPingReflector != null) {
    231             mPingReflector.interrupt();
    232             mPingReflector = null;
    233         }
    234         try {
    235             if (mFd != null) {
    236                 Log.i(TAG, "Closing filedescriptor");
    237                 mFd.close();
    238             }
    239         } catch(IOException e) {
    240             Log.w(TAG, "Closing filedescriptor failed", e);
    241         } finally {
    242             mFd = null;
    243         }
    244     }
    245 }
    246