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