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 public static final String ACTION_STOP_SERVICE = "com.android.cts.vpnfirewall.STOP_SERVICE"; 44 45 private static final String TAG = "ReflectorVpnService"; 46 private static final String DEVICE_AND_PROFILE_OWNER_PACKAGE = 47 "com.android.cts.deviceandprofileowner"; 48 private static final String ACTION_VPN_IS_UP = "com.android.cts.vpnfirewall.VPN_IS_UP"; 49 private static final String ACTION_VPN_ON_START = "com.android.cts.vpnfirewall.VPN_ON_START"; 50 private static final int NOTIFICATION_ID = 1; 51 private static final String NOTIFICATION_CHANNEL_ID = TAG; 52 private static final int MTU = 1799; 53 54 private ParcelFileDescriptor mFd = null; 55 private PingReflector mPingReflector = null; 56 private ConnectivityManager mConnectivityManager = null; 57 private ConnectivityManager.NetworkCallback mNetworkCallback = null; 58 59 private static final String RESTRICTION_ADDRESSES = "vpn.addresses"; 60 private static final String RESTRICTION_ROUTES = "vpn.routes"; 61 private static final String RESTRICTION_ALLOWED = "vpn.allowed"; 62 private static final String RESTRICTION_DISALLOWED = "vpn.disallowed"; 63 /** Service won't create the tunnel, to test lockdown behavior in case of VPN failure. */ 64 private static final String RESTRICTION_DONT_ESTABLISH = "vpn.dont_establish"; 65 private static final String EXTRA_ALWAYS_ON = "always-on"; 66 private static final String EXTRA_LOCKDOWN = "lockdown"; 67 68 @Override onStartCommand(Intent intent, int flags, int startId)69 public int onStartCommand(Intent intent, int flags, int startId) { 70 if (ACTION_STOP_SERVICE.equals(intent.getAction())) { 71 stop(); 72 stopSelf(); 73 } else { 74 // Normal service start 75 // Put ourself in the foreground to stop the system killing us while we wait for orders from 76 // the hostside test. 77 NotificationManager notificationManager = getSystemService(NotificationManager.class); 78 notificationManager.createNotificationChannel(new NotificationChannel( 79 NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID, 80 NotificationManager.IMPORTANCE_DEFAULT)); 81 startForeground(NOTIFICATION_ID, new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) 82 .setSmallIcon(R.drawable.ic_dialog_alert) 83 .build()); 84 start(); 85 } 86 return START_NOT_STICKY; 87 } 88 89 @Override onCreate()90 public void onCreate() { 91 mConnectivityManager = getSystemService(ConnectivityManager.class); 92 } 93 94 @Override onDestroy()95 public void onDestroy() { 96 stop(); 97 NotificationManager notificationManager = getSystemService(NotificationManager.class); 98 notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID); 99 ensureNetworkCallbackUnregistered(); 100 super.onDestroy(); 101 } 102 ensureNetworkCallbackUnregistered()103 private void ensureNetworkCallbackUnregistered() { 104 if (null == mConnectivityManager || null == mNetworkCallback) return; 105 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 106 mNetworkCallback = null; 107 } 108 start()109 private void start() { 110 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 111 final Bundle restrictions = um.getApplicationRestrictions(getPackageName()); 112 113 final Intent intent = new Intent(ACTION_VPN_ON_START); 114 intent.setPackage(DEVICE_AND_PROFILE_OWNER_PACKAGE); 115 sendBroadcast(intent); 116 117 if (restrictions.getBoolean(RESTRICTION_DONT_ESTABLISH)) { 118 stopSelf(); 119 return; 120 } 121 122 VpnService.prepare(this); 123 124 ensureNetworkCallbackUnregistered(); 125 final NetworkRequest request = new NetworkRequest.Builder() 126 .addTransportType(NetworkCapabilities.TRANSPORT_VPN) 127 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 128 .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 129 .build(); 130 mNetworkCallback = new ConnectivityManager.NetworkCallback() { 131 @Override 132 public void onAvailable(final Network net) { 133 final Intent intent = new Intent(ACTION_VPN_IS_UP); 134 intent.setPackage(DEVICE_AND_PROFILE_OWNER_PACKAGE); 135 intent.putExtra(EXTRA_ALWAYS_ON, isAlwaysOn()); 136 intent.putExtra(EXTRA_LOCKDOWN, isLockdownEnabled()); 137 sendBroadcast(intent); 138 ensureNetworkCallbackUnregistered(); 139 } 140 }; 141 mConnectivityManager.registerNetworkCallback(request, mNetworkCallback); 142 143 Builder builder = new Builder(); 144 145 146 String[] addressArray = restrictions.getStringArray(RESTRICTION_ADDRESSES); 147 if (addressArray == null) { 148 // Addresses for IPv4/IPv6 documentation purposes according to rfc5737/rfc3849. 149 addressArray = new String[] {"192.0.2.3/32", "2001:db8:1:2::/128"}; 150 }; 151 for (int i = 0; i < addressArray.length; i++) { 152 String[] prefixAndMask = addressArray[i].split("/"); 153 try { 154 InetAddress address = InetAddress.getByName(prefixAndMask[0]); 155 int prefixLength = Integer.parseInt(prefixAndMask[1]); 156 builder.addAddress(address, prefixLength); 157 } catch (NumberFormatException | UnknownHostException e) { 158 Log.w(TAG, "Ill-formed address: " + addressArray[i]); 159 continue; 160 } 161 } 162 163 String[] routeArray = restrictions.getStringArray(RESTRICTION_ROUTES); 164 if (routeArray == null) { 165 routeArray = new String[] {"0.0.0.0/0", "::/0"}; 166 } 167 for (int i = 0; i < routeArray.length; i++) { 168 String[] prefixAndMask = routeArray[i].split("/"); 169 try { 170 InetAddress address = InetAddress.getByName(prefixAndMask[0]); 171 int prefixLength = Integer.parseInt(prefixAndMask[1]); 172 builder.addRoute(address, prefixLength); 173 } catch (NumberFormatException | UnknownHostException e) { 174 Log.w(TAG, "Ill-formed route: " + routeArray[i]); 175 continue; 176 } 177 } 178 179 String[] allowedArray = restrictions.getStringArray(RESTRICTION_ALLOWED); 180 if (allowedArray != null) { 181 for (int i = 0; i < allowedArray.length; i++) { 182 String allowedPackage = allowedArray[i]; 183 if (!TextUtils.isEmpty(allowedPackage)) { 184 try { 185 builder.addAllowedApplication(allowedPackage); 186 } catch(NameNotFoundException e) { 187 Log.w(TAG, "Allowed package not found: " + allowedPackage); 188 continue; 189 } 190 } 191 } 192 } 193 194 String[] disallowedArray = restrictions.getStringArray(RESTRICTION_DISALLOWED); 195 if (disallowedArray != null) { 196 for (int i = 0; i < disallowedArray.length; i++) { 197 String disallowedPackage = disallowedArray[i]; 198 if (!TextUtils.isEmpty(disallowedPackage)) { 199 try { 200 builder.addDisallowedApplication(disallowedPackage); 201 } catch(NameNotFoundException e) { 202 Log.w(TAG, "Disallowed package not found: " + disallowedPackage); 203 continue; 204 } 205 } 206 } 207 } 208 209 if (allowedArray == null && 210 (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1 211 || SystemProperties.getInt("service.adb.tcp.port", -1) > -1)) { 212 try { 213 // If adb TCP port opened the test may be running by adb over network. 214 // Add com.android.shell application into disallowed list to exclude adb socket 215 // for VPN tests. 216 builder.addDisallowedApplication("com.android.shell"); 217 } catch(NameNotFoundException e) { 218 Log.w(TAG, "com.android.shell not found"); 219 } 220 } 221 222 builder.setMtu(MTU); 223 builder.setBlocking(true); 224 builder.setSession(TAG); 225 226 mFd = builder.establish(); 227 if (mFd == null) { 228 Log.e(TAG, "Unable to establish file descriptor for VPN connection"); 229 return; 230 } 231 Log.i(TAG, "Established, fd=" + mFd.getFd()); 232 233 mPingReflector = new PingReflector(mFd.getFileDescriptor(), MTU); 234 mPingReflector.start(); 235 } 236 stop()237 private void stop() { 238 if (mPingReflector != null) { 239 mPingReflector.interrupt(); 240 mPingReflector = null; 241 } 242 try { 243 if (mFd != null) { 244 Log.i(TAG, "Closing filedescriptor"); 245 mFd.close(); 246 } 247 } catch(IOException e) { 248 Log.w(TAG, "Closing filedescriptor failed", e); 249 } finally { 250 mFd = null; 251 } 252 } 253 } 254