blob: 8c4e113b2b84fbdfd63d28827327bde2f20dde74 [file] [log] [blame]
Chia-chi Yehff3bdca2011-05-23 17:26:46 -07001/*
2 * Copyright (C) 2011 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
17package com.android.server.connectivity;
18
Jeff Sharkey899223b2012-08-04 15:24:58 -070019import static android.Manifest.permission.BIND_VPN_SERVICE;
Paul Jensen31a94f42015-02-13 14:18:39 -050020import static android.net.ConnectivityManager.NETID_UNSET;
Lorenzo Colitti50262792014-09-19 01:53:35 +090021import static android.net.RouteInfo.RTN_THROW;
Lorenzo Colitti60446162014-09-20 00:14:31 +090022import static android.net.RouteInfo.RTN_UNREACHABLE;
Jeff Sharkey899223b2012-08-04 15:24:58 -070023
Jeff Davidsonbc19c182014-11-11 13:20:01 -080024import android.Manifest;
Robin Lee4d03abc2016-05-09 12:32:27 +010025import android.annotation.NonNull;
26import android.annotation.Nullable;
27import android.annotation.UserIdInt;
Chad Brubaker4ca19e82013-06-14 11:16:51 -070028import android.app.AppGlobals;
Jeff Davidson05542602014-08-11 14:07:27 -070029import android.app.AppOpsManager;
Jeff Davidson90b1b9f2014-08-22 13:05:43 -070030import android.app.PendingIntent;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070031import android.content.BroadcastReceiver;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070032import android.content.ComponentName;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070033import android.content.Context;
34import android.content.Intent;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070035import android.content.IntentFilter;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070036import android.content.ServiceConnection;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070037import android.content.pm.PackageManager;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040038import android.content.pm.PackageManager.NameNotFoundException;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070039import android.content.pm.ResolveInfo;
Chad Brubakerc2865192013-07-10 14:46:23 -070040import android.content.pm.UserInfo;
Jeff Sharkey899223b2012-08-04 15:24:58 -070041import android.net.ConnectivityManager;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070042import android.net.INetworkManagementEventObserver;
Lorenzo Colitti50262792014-09-19 01:53:35 +090043import android.net.IpPrefix;
Chad Brubaker4ca19e82013-06-14 11:16:51 -070044import android.net.LinkAddress;
Jeff Sharkey82f85212012-08-24 11:17:25 -070045import android.net.LinkProperties;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070046import android.net.LocalSocket;
47import android.net.LocalSocketAddress;
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -080048import android.net.Network;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040049import android.net.NetworkAgent;
50import android.net.NetworkCapabilities;
Jeff Sharkey899223b2012-08-04 15:24:58 -070051import android.net.NetworkInfo;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040052import android.net.NetworkInfo.DetailedState;
Sreeram Ramachandran8cd33ed2014-07-23 15:23:15 -070053import android.net.NetworkMisc;
Jeff Sharkey82f85212012-08-24 11:17:25 -070054import android.net.RouteInfo;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040055import android.net.UidRange;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070056import android.os.Binder;
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -080057import android.os.FileUtils;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070058import android.os.IBinder;
Jeff Sharkey899223b2012-08-04 15:24:58 -070059import android.os.INetworkManagementService;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040060import android.os.Looper;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070061import android.os.Parcel;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070062import android.os.ParcelFileDescriptor;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070063import android.os.Process;
Jeff Sharkey899223b2012-08-04 15:24:58 -070064import android.os.RemoteException;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070065import android.os.SystemClock;
Jeff Sharkey088f29f2012-08-05 14:55:04 -070066import android.os.SystemService;
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -070067import android.os.UserHandle;
Chad Brubakerc2865192013-07-10 14:46:23 -070068import android.os.UserManager;
Robin Lee244ce8e2016-01-05 18:03:46 +000069import android.provider.Settings;
Jeff Sharkey82f85212012-08-24 11:17:25 -070070import android.security.Credentials;
71import android.security.KeyStore;
Paul Jensene75b9e32015-04-06 11:54:53 -040072import android.text.TextUtils;
Robin Lee4d03abc2016-05-09 12:32:27 +010073import android.util.ArraySet;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070074import android.util.Log;
75
Chad Brubakerc2865192013-07-10 14:46:23 -070076import com.android.internal.annotations.GuardedBy;
Robin Lee4d03abc2016-05-09 12:32:27 +010077import com.android.internal.annotations.VisibleForTesting;
Chia-chi Yeh2e467642011-07-04 03:23:12 -070078import com.android.internal.net.LegacyVpnInfo;
Chia-chi Yeh04ba25c2011-06-15 17:07:27 -070079import com.android.internal.net.VpnConfig;
Wenchao Tongf5ea3402015-03-04 13:26:38 -080080import com.android.internal.net.VpnInfo;
Jeff Sharkey82f85212012-08-24 11:17:25 -070081import com.android.internal.net.VpnProfile;
Jeff Sharkey899223b2012-08-04 15:24:58 -070082import com.android.server.net.BaseNetworkObserver;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070083
Jeff Davidson05542602014-08-11 14:07:27 -070084import libcore.io.IoUtils;
85
Chia-chi Yeh97a61562011-07-14 15:05:05 -070086import java.io.File;
Jeff Davidson6bbf39c2014-07-23 10:14:53 -070087import java.io.IOException;
Chia-chi Yeh97a61562011-07-14 15:05:05 -070088import java.io.InputStream;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070089import java.io.OutputStream;
Jeff Sharkey82f85212012-08-24 11:17:25 -070090import java.net.Inet4Address;
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -070091import java.net.Inet6Address;
92import java.net.InetAddress;
Elliott Hughesd396a442013-06-28 16:24:48 -070093import java.nio.charset.StandardCharsets;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040094import java.util.ArrayList;
Chia-chi Yeh41d16852011-07-01 02:12:06 -070095import java.util.Arrays;
Robin Lee4d03abc2016-05-09 12:32:27 +010096import java.util.Collection;
97import java.util.Collections;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040098import java.util.List;
Robin Lee4d03abc2016-05-09 12:32:27 +010099import java.util.Set;
Paul Jensen0784eea2014-08-19 16:00:24 -0400100import java.util.SortedSet;
101import java.util.TreeSet;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700102import java.util.concurrent.atomic.AtomicInteger;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700103
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700104/**
105 * @hide
106 */
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400107public class Vpn {
108 private static final String NETWORKTYPE = "VPN";
Jeff Sharkey899223b2012-08-04 15:24:58 -0700109 private static final String TAG = "Vpn";
110 private static final boolean LOGD = true;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400111
Jeff Sharkey899223b2012-08-04 15:24:58 -0700112 // TODO: create separate trackers for each unique VPN to support
113 // automated reconnection
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700114
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400115 private Context mContext;
116 private NetworkInfo mNetworkInfo;
117 private String mPackage;
118 private int mOwnerUID;
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700119 private String mInterface;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700120 private Connection mConnection;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700121 private LegacyVpnRunner mLegacyVpnRunner;
Jeff Davidson90b1b9f2014-08-22 13:05:43 -0700122 private PendingIntent mStatusIntent;
Jeff Sharkey57666932013-04-30 17:01:57 -0700123 private volatile boolean mEnableTeardown = true;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400124 private final INetworkManagementService mNetd;
Chad Brubakerc2865192013-07-10 14:46:23 -0700125 private VpnConfig mConfig;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400126 private NetworkAgent mNetworkAgent;
127 private final Looper mLooper;
128 private final NetworkCapabilities mNetworkCapabilities;
Chad Brubakerc2865192013-07-10 14:46:23 -0700129
Robin Lee4d03abc2016-05-09 12:32:27 +0100130 /**
131 * List of UIDs that are set to use this VPN by default. Normally, every UID in the user is
132 * added to this set but that can be changed by adding allowed or disallowed applications. It
133 * is non-null iff the VPN is connected.
134 *
135 * Unless the VPN has set allowBypass=true, these UIDs are forced into the VPN.
136 *
137 * @see VpnService.Builder#addAllowedApplication(String)
138 * @see VpnService.Builder#addDisallowedApplication(String)
139 */
Chad Brubakerc2865192013-07-10 14:46:23 -0700140 @GuardedBy("this")
Robin Lee4d03abc2016-05-09 12:32:27 +0100141 private Set<UidRange> mVpnUsers = null;
Chad Brubakerc2865192013-07-10 14:46:23 -0700142
Paul Jensen0784eea2014-08-19 16:00:24 -0400143 // Handle of user initiating VPN.
144 private final int mUserHandle;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700145
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400146 public Vpn(Looper looper, Context context, INetworkManagementService netService,
Paul Jensene75b9e32015-04-06 11:54:53 -0400147 int userHandle) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700148 mContext = context;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400149 mNetd = netService;
Paul Jensen0784eea2014-08-19 16:00:24 -0400150 mUserHandle = userHandle;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400151 mLooper = looper;
152
153 mPackage = VpnConfig.LEGACY_VPN;
Paul Jensen0784eea2014-08-19 16:00:24 -0400154 mOwnerUID = getAppUid(mPackage, mUserHandle);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700155
156 try {
157 netService.registerObserver(mObserver);
158 } catch (RemoteException e) {
159 Log.wtf(TAG, "Problem registering observer", e);
160 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400161
162 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
163 // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
164 mNetworkCapabilities = new NetworkCapabilities();
165 mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
166 mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700167 }
168
Jeff Sharkey57666932013-04-30 17:01:57 -0700169 /**
Jeff Sharkey57666932013-04-30 17:01:57 -0700170 * Set if this object is responsible for watching for {@link NetworkInfo}
171 * teardown. When {@code false}, teardown is handled externally by someone
172 * else.
173 */
174 public void setEnableTeardown(boolean enableTeardown) {
175 mEnableTeardown = enableTeardown;
176 }
177
Jeff Sharkey899223b2012-08-04 15:24:58 -0700178 /**
179 * Update current state, dispaching event to listeners.
180 */
181 private void updateState(DetailedState detailedState, String reason) {
182 if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
183 mNetworkInfo.setDetailedState(detailedState, reason, null);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400184 if (mNetworkAgent != null) {
185 mNetworkAgent.sendNetworkInfo(mNetworkInfo);
186 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700187 }
188
189 /**
Robin Lee244ce8e2016-01-05 18:03:46 +0000190 * Configures an always-on VPN connection through a specific application.
191 * This connection is automatically granted and persisted after a reboot.
192 *
193 * <p>The designated package should exist and declare a {@link VpnService} in its
194 * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
195 * otherwise the call will fail.
196 *
197 * @param newPackage the package to designate as always-on VPN supplier.
198 */
199 public synchronized boolean setAlwaysOnPackage(String packageName) {
200 enforceControlPermissionOrInternalCaller();
201
202 // Disconnect current VPN.
203 prepareInternal(VpnConfig.LEGACY_VPN);
204
205 // Pre-authorize new always-on VPN package.
206 if (packageName != null) {
207 if (!setPackageAuthorization(packageName, true)) {
208 return false;
209 }
Robin Lee78378842016-02-12 11:51:33 +0000210 prepareInternal(packageName);
Robin Lee244ce8e2016-01-05 18:03:46 +0000211 }
212
213 // Save the new package name in Settings.Secure.
214 final long token = Binder.clearCallingIdentity();
215 try {
216 Settings.Secure.putStringForUser(mContext.getContentResolver(),
217 Settings.Secure.ALWAYS_ON_VPN_APP, packageName, mUserHandle);
218 } finally {
219 Binder.restoreCallingIdentity(token);
220 }
221 return true;
222 }
223
224 /**
225 * @return the package name of the VPN controller responsible for always-on VPN,
226 * or {@code null} if none is set or always-on VPN is controlled through
227 * lockdown instead.
228 * @hide
229 */
230 public synchronized String getAlwaysOnPackage() {
231 enforceControlPermissionOrInternalCaller();
232
233 final long token = Binder.clearCallingIdentity();
234 try {
235 return Settings.Secure.getStringForUser(mContext.getContentResolver(),
236 Settings.Secure.ALWAYS_ON_VPN_APP, mUserHandle);
237 } finally {
238 Binder.restoreCallingIdentity(token);
239 }
240 }
241
242 /**
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700243 * Prepare for a VPN application. This method is designed to solve
244 * race conditions. It first compares the current prepared package
245 * with {@code oldPackage}. If they are the same, the prepared
246 * package is revoked and replaced with {@code newPackage}. If
247 * {@code oldPackage} is {@code null}, the comparison is omitted.
248 * If {@code newPackage} is the same package or {@code null}, the
249 * revocation is omitted. This method returns {@code true} if the
250 * operation is succeeded.
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700251 *
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700252 * Legacy VPN is handled specially since it is not a real package.
253 * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
254 * it can be revoked by itself.
255 *
256 * @param oldPackage The package name of the old VPN application.
257 * @param newPackage The package name of the new VPN application.
258 * @return true if the operation is succeeded.
Chia-chi Yehe9107902011-07-02 01:48:50 -0700259 */
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700260 public synchronized boolean prepare(String oldPackage, String newPackage) {
Jeff Davidson0a775ce2015-04-27 15:02:48 -0700261 if (oldPackage != null) {
262 if (getAppUid(oldPackage, mUserHandle) != mOwnerUID) {
263 // The package doesn't match. We return false (to obtain user consent) unless the
264 // user has already consented to that VPN package.
265 if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
266 prepareInternal(oldPackage);
267 return true;
268 }
269 return false;
270 } else if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
271 && !isVpnUserPreConsented(oldPackage)) {
272 // Currently prepared VPN is revoked, so unprepare it and return false.
273 prepareInternal(VpnConfig.LEGACY_VPN);
274 return false;
Jeff Davidson05542602014-08-11 14:07:27 -0700275 }
Chia-chi Yehe9107902011-07-02 01:48:50 -0700276 }
277
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700278 // Return true if we do not need to revoke.
Jeff Davidsonbe085872014-10-24 10:35:53 -0700279 if (newPackage == null || (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
280 getAppUid(newPackage, mUserHandle) == mOwnerUID)) {
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700281 return true;
282 }
283
Robin Lee1b1bcd72016-02-12 18:11:30 +0000284 // Stop an existing always-on VPN from being dethroned by other apps.
285 if (getAlwaysOnPackage() != null) {
286 return false;
287 }
288
289 // Check that the caller is authorized.
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700290 enforceControlPermission();
Chia-chi Yehe9107902011-07-02 01:48:50 -0700291
Jeff Davidson11008a72014-11-20 13:12:46 -0800292 prepareInternal(newPackage);
293 return true;
294 }
Chia-chi Yehe9107902011-07-02 01:48:50 -0700295
Jeff Davidson11008a72014-11-20 13:12:46 -0800296 /** Prepare the VPN for the given package. Does not perform permission checks. */
297 private void prepareInternal(String newPackage) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400298 long token = Binder.clearCallingIdentity();
299 try {
Jeff Davidson11008a72014-11-20 13:12:46 -0800300 // Reset the interface.
301 if (mInterface != null) {
302 mStatusIntent = null;
303 agentDisconnect();
304 jniReset(mInterface);
305 mInterface = null;
306 mVpnUsers = null;
307 }
308
309 // Revoke the connection or stop LegacyVpnRunner.
310 if (mConnection != null) {
311 try {
312 mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
313 Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
314 } catch (Exception e) {
315 // ignore
316 }
317 mContext.unbindService(mConnection);
318 mConnection = null;
319 } else if (mLegacyVpnRunner != null) {
320 mLegacyVpnRunner.exit();
321 mLegacyVpnRunner = null;
322 }
323
324 try {
325 mNetd.denyProtect(mOwnerUID);
326 } catch (Exception e) {
327 Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
328 }
329
330 Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
331 mPackage = newPackage;
332 mOwnerUID = getAppUid(newPackage, mUserHandle);
333 try {
334 mNetd.allowProtect(mOwnerUID);
335 } catch (Exception e) {
336 Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
337 }
338 mConfig = null;
339
340 updateState(DetailedState.IDLE, "prepare");
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400341 } finally {
342 Binder.restoreCallingIdentity(token);
343 }
Chia-chi Yehe9107902011-07-02 01:48:50 -0700344 }
345
Jeff Davidson05542602014-08-11 14:07:27 -0700346 /**
Robin Lee3b3dd942015-05-12 18:14:58 +0100347 * Set whether a package has the ability to launch VPNs without user intervention.
Jeff Davidson05542602014-08-11 14:07:27 -0700348 */
Robin Lee244ce8e2016-01-05 18:03:46 +0000349 public boolean setPackageAuthorization(String packageName, boolean authorized) {
Jeff Davidson05542602014-08-11 14:07:27 -0700350 // Check if the caller is authorized.
Robin Lee244ce8e2016-01-05 18:03:46 +0000351 enforceControlPermissionOrInternalCaller();
Jeff Davidson05542602014-08-11 14:07:27 -0700352
Robin Lee3b3dd942015-05-12 18:14:58 +0100353 int uid = getAppUid(packageName, mUserHandle);
354 if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
355 // Authorization for nonexistent packages (or fake ones) can't be updated.
Robin Lee244ce8e2016-01-05 18:03:46 +0000356 return false;
Jeff Davidson05542602014-08-11 14:07:27 -0700357 }
358
359 long token = Binder.clearCallingIdentity();
360 try {
361 AppOpsManager appOps =
362 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Robin Lee3b3dd942015-05-12 18:14:58 +0100363 appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
Jeff Davidson05542602014-08-11 14:07:27 -0700364 authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
Robin Lee244ce8e2016-01-05 18:03:46 +0000365 return true;
Jeff Davidson05542602014-08-11 14:07:27 -0700366 } catch (Exception e) {
Robin Lee3b3dd942015-05-12 18:14:58 +0100367 Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
Jeff Davidson05542602014-08-11 14:07:27 -0700368 } finally {
369 Binder.restoreCallingIdentity(token);
370 }
Robin Lee244ce8e2016-01-05 18:03:46 +0000371 return false;
Jeff Davidson05542602014-08-11 14:07:27 -0700372 }
373
374 private boolean isVpnUserPreConsented(String packageName) {
375 AppOpsManager appOps =
376 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
377
378 // Verify that the caller matches the given package and has permission to activate VPNs.
379 return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
380 packageName) == AppOpsManager.MODE_ALLOWED;
381 }
382
Paul Jensen0784eea2014-08-19 16:00:24 -0400383 private int getAppUid(String app, int userHandle) {
Jeff Davidson05542602014-08-11 14:07:27 -0700384 if (VpnConfig.LEGACY_VPN.equals(app)) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400385 return Process.myUid();
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700386 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400387 PackageManager pm = mContext.getPackageManager();
388 int result;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700389 try {
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700390 result = pm.getPackageUidAsUser(app, userHandle);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400391 } catch (NameNotFoundException e) {
392 result = -1;
393 }
394 return result;
395 }
396
397 public NetworkInfo getNetworkInfo() {
398 return mNetworkInfo;
399 }
400
Paul Jensen31a94f42015-02-13 14:18:39 -0500401 public int getNetId() {
402 return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET;
403 }
404
Lorenzo Colitti60446162014-09-20 00:14:31 +0900405 private LinkProperties makeLinkProperties() {
406 boolean allowIPv4 = mConfig.allowIPv4;
407 boolean allowIPv6 = mConfig.allowIPv6;
408
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400409 LinkProperties lp = new LinkProperties();
Lorenzo Colitti60446162014-09-20 00:14:31 +0900410
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400411 lp.setInterfaceName(mInterface);
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700412
Lorenzo Colitti60446162014-09-20 00:14:31 +0900413 if (mConfig.addresses != null) {
414 for (LinkAddress address : mConfig.addresses) {
415 lp.addLinkAddress(address);
416 allowIPv4 |= address.getAddress() instanceof Inet4Address;
417 allowIPv6 |= address.getAddress() instanceof Inet6Address;
418 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400419 }
Lorenzo Colitti60446162014-09-20 00:14:31 +0900420
421 if (mConfig.routes != null) {
422 for (RouteInfo route : mConfig.routes) {
423 lp.addRoute(route);
424 InetAddress address = route.getDestination().getAddress();
425 allowIPv4 |= address instanceof Inet4Address;
426 allowIPv6 |= address instanceof Inet6Address;
427 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400428 }
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700429
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400430 if (mConfig.dnsServers != null) {
431 for (String dnsServer : mConfig.dnsServers) {
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700432 InetAddress address = InetAddress.parseNumericAddress(dnsServer);
433 lp.addDnsServer(address);
Lorenzo Colitti60446162014-09-20 00:14:31 +0900434 allowIPv4 |= address instanceof Inet4Address;
435 allowIPv6 |= address instanceof Inet6Address;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400436 }
437 }
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700438
Lorenzo Colitti60446162014-09-20 00:14:31 +0900439 if (!allowIPv4) {
440 lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
441 }
442 if (!allowIPv6) {
443 lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
444 }
445
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400446 // Concatenate search domains into a string.
447 StringBuilder buffer = new StringBuilder();
448 if (mConfig.searchDomains != null) {
449 for (String domain : mConfig.searchDomains) {
450 buffer.append(domain).append(' ');
451 }
452 }
453 lp.setDomains(buffer.toString().trim());
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700454
Lorenzo Colitti60446162014-09-20 00:14:31 +0900455 // TODO: Stop setting the MTU in jniCreate and set it here.
456
457 return lp;
458 }
459
460 private void agentConnect() {
461 LinkProperties lp = makeLinkProperties();
462
463 if (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()) {
464 mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
465 } else {
466 mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
467 }
468
Robin Lee323f29d2016-05-04 16:38:06 +0100469 mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700470
Sreeram Ramachandran8cd33ed2014-07-23 15:23:15 -0700471 NetworkMisc networkMisc = new NetworkMisc();
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700472 networkMisc.allowBypass = mConfig.allowBypass;
473
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400474 long token = Binder.clearCallingIdentity();
475 try {
476 mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
Sreeram Ramachandran8cd33ed2014-07-23 15:23:15 -0700477 mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
Jeff Davidson05542602014-08-11 14:07:27 -0700478 @Override
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400479 public void unwanted() {
480 // We are user controlled, not driven by NetworkRequest.
Jeff Davidson05542602014-08-11 14:07:27 -0700481 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400482 };
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700483 } finally {
484 Binder.restoreCallingIdentity(token);
485 }
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700486
Robin Lee4d03abc2016-05-09 12:32:27 +0100487 mVpnUsers = createUserAndRestrictedProfilesRanges(mUserHandle,
488 mConfig.allowedApplications, mConfig.disallowedApplications);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400489 mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
Robin Lee323f29d2016-05-04 16:38:06 +0100490
491 mNetworkInfo.setIsAvailable(true);
492 updateState(DetailedState.CONNECTED, "agentConnect");
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400493 }
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700494
Fyodor Kupolov1c363152015-09-02 13:27:21 -0700495 private boolean canHaveRestrictedProfile(int userId) {
496 long token = Binder.clearCallingIdentity();
497 try {
498 return UserManager.get(mContext).canHaveRestrictedProfile(userId);
499 } finally {
500 Binder.restoreCallingIdentity(token);
501 }
502 }
503
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400504 private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
505 networkInfo.setIsAvailable(false);
506 networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
507 if (networkAgent != null) {
508 networkAgent.sendNetworkInfo(networkInfo);
509 }
510 }
511
512 private void agentDisconnect(NetworkAgent networkAgent) {
513 NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
514 agentDisconnect(networkInfo, networkAgent);
515 }
516
517 private void agentDisconnect() {
518 if (mNetworkInfo.isConnected()) {
519 agentDisconnect(mNetworkInfo, mNetworkAgent);
520 mNetworkAgent = null;
521 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700522 }
523
524 /**
Chia-chi Yehe9107902011-07-02 01:48:50 -0700525 * Establish a VPN network and return the file descriptor of the VPN
526 * interface. This methods returns {@code null} if the application is
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700527 * revoked or not prepared.
Chia-chi Yehe9107902011-07-02 01:48:50 -0700528 *
529 * @param config The parameters to configure the network.
530 * @return The file descriptor of the VPN interface.
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700531 */
Chia-chi Yeh04ba25c2011-06-15 17:07:27 -0700532 public synchronized ParcelFileDescriptor establish(VpnConfig config) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700533 // Check if the caller is already prepared.
Chad Brubakerc2865192013-07-10 14:46:23 -0700534 UserManager mgr = UserManager.get(mContext);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400535 if (Binder.getCallingUid() != mOwnerUID) {
Chia-chi Yeh7b0b8342011-06-17 14:34:11 -0700536 return null;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700537 }
Jeff Davidson0a775ce2015-04-27 15:02:48 -0700538 // Check to ensure consent hasn't been revoked since we were prepared.
539 if (!isVpnUserPreConsented(mPackage)) {
540 return null;
541 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700542 // Check if the service is properly declared.
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700543 Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
544 intent.setClassName(mPackage, config.user);
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700545 long token = Binder.clearCallingIdentity();
546 try {
Chad Brubakerc2865192013-07-10 14:46:23 -0700547 // Restricted users are not allowed to create VPNs, they are tied to Owner
Paul Jensen0784eea2014-08-19 16:00:24 -0400548 UserInfo user = mgr.getUserInfo(mUserHandle);
Nicolas Prevot95778ff2015-01-06 15:43:05 +0000549 if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
550 new UserHandle(mUserHandle))) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700551 throw new SecurityException("Restricted users cannot establish VPNs");
552 }
553
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700554 ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
Paul Jensen0784eea2014-08-19 16:00:24 -0400555 null, 0, mUserHandle);
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700556 if (info == null) {
557 throw new SecurityException("Cannot find " + config.user);
558 }
559 if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
560 throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
561 }
562 } catch (RemoteException e) {
563 throw new SecurityException("Cannot find " + config.user);
564 } finally {
565 Binder.restoreCallingIdentity(token);
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700566 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700567
Chad Brubaker850eb672014-03-21 21:02:47 +0000568 // Save the old config in case we need to go back.
569 VpnConfig oldConfig = mConfig;
570 String oldInterface = mInterface;
571 Connection oldConnection = mConnection;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400572 NetworkAgent oldNetworkAgent = mNetworkAgent;
573 mNetworkAgent = null;
Robin Lee4d03abc2016-05-09 12:32:27 +0100574 Set<UidRange> oldUsers = mVpnUsers;
Chad Brubaker850eb672014-03-21 21:02:47 +0000575
Chia-chi Yehe9107902011-07-02 01:48:50 -0700576 // Configure the interface. Abort if any of these steps fails.
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700577 ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700578 try {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700579 updateState(DetailedState.CONNECTING, "establish");
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700580 String interfaze = jniGetName(tun.getFd());
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700581
Chad Brubakerc2865192013-07-10 14:46:23 -0700582 // TEMP use the old jni calls until there is support for netd address setting
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700583 StringBuilder builder = new StringBuilder();
584 for (LinkAddress address : config.addresses) {
585 builder.append(" " + address);
586 }
587 if (jniSetAddresses(interfaze, builder.toString()) < 1) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700588 throw new IllegalArgumentException("At least one address must be specified");
589 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700590 Connection connection = new Connection();
Dianne Hackbornd69e4c12015-04-24 09:54:54 -0700591 if (!mContext.bindServiceAsUser(intent, connection,
592 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
593 new UserHandle(mUserHandle))) {
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700594 throw new IllegalStateException("Cannot bind " + config.user);
595 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000596
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700597 mConnection = connection;
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700598 mInterface = interfaze;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700599
600 // Fill more values.
601 config.user = mPackage;
602 config.interfaze = mInterface;
Chad Brubakerc2865192013-07-10 14:46:23 -0700603 config.startTime = SystemClock.elapsedRealtime();
604 mConfig = config;
Chad Brubaker850eb672014-03-21 21:02:47 +0000605
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700606 // Set up forwarding and DNS rules.
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400607 agentConnect();
Chad Brubaker850eb672014-03-21 21:02:47 +0000608
609 if (oldConnection != null) {
610 mContext.unbindService(oldConnection);
611 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400612 // Remove the old tun's user forwarding rules
613 // The new tun's user rules have already been added so they will take over
614 // as rules are deleted. This prevents data leakage as the rules are moved over.
615 agentDisconnect(oldNetworkAgent);
Chad Brubaker850eb672014-03-21 21:02:47 +0000616 if (oldInterface != null && !oldInterface.equals(interfaze)) {
Chad Brubaker850eb672014-03-21 21:02:47 +0000617 jniReset(oldInterface);
618 }
Jeff Davidson6bbf39c2014-07-23 10:14:53 -0700619
620 try {
621 IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
622 } catch (IOException e) {
623 throw new IllegalStateException(
624 "Cannot set tunnel's fd as blocking=" + config.blocking, e);
625 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000626 } catch (RuntimeException e) {
Chad Brubaker850eb672014-03-21 21:02:47 +0000627 IoUtils.closeQuietly(tun);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400628 agentDisconnect();
Chad Brubaker850eb672014-03-21 21:02:47 +0000629 // restore old state
630 mConfig = oldConfig;
631 mConnection = oldConnection;
632 mVpnUsers = oldUsers;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400633 mNetworkAgent = oldNetworkAgent;
Chad Brubaker850eb672014-03-21 21:02:47 +0000634 mInterface = oldInterface;
635 throw e;
Chad Brubakerc2865192013-07-10 14:46:23 -0700636 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000637 Log.i(TAG, "Established by " + config.user + " on " + mInterface);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700638 return tun;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700639 }
640
Chad Brubakerc2865192013-07-10 14:46:23 -0700641 private boolean isRunningLocked() {
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -0800642 return mNetworkAgent != null && mInterface != null;
643 }
644
645 // Returns true if the VPN has been established and the calling UID is its owner. Used to check
646 // that a call to mutate VPN state is admissible.
647 private boolean isCallerEstablishedOwnerLocked() {
648 return isRunningLocked() && Binder.getCallingUid() == mOwnerUID;
Chad Brubakerc2865192013-07-10 14:46:23 -0700649 }
650
Paul Jensen0784eea2014-08-19 16:00:24 -0400651 // Note: Return type guarantees results are deduped and sorted, which callers require.
652 private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
653 SortedSet<Integer> uids = new TreeSet<Integer>();
654 for (String app : packageNames) {
655 int uid = getAppUid(app, userHandle);
656 if (uid != -1) uids.add(uid);
657 }
658 return uids;
659 }
660
Robin Lee4d03abc2016-05-09 12:32:27 +0100661 /**
662 * Creates a {@link Set} of non-intersecting {@link UidRange} objects including all UIDs
663 * associated with one user, and any restricted profiles attached to that user.
664 *
665 * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
666 * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs
667 * in each user and profile will be included.
668 *
669 * @param userHandle The userId to create UID ranges for along with any of its restricted
670 * profiles.
671 * @param allowedApplications (optional) whitelist of applications to include.
672 * @param disallowedApplications (optional) blacklist of applications to exclude.
673 */
674 @VisibleForTesting
675 Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userHandle,
676 @Nullable List<String> allowedApplications,
677 @Nullable List<String> disallowedApplications) {
678 final Set<UidRange> ranges = new ArraySet<>();
Robert Greenwalt69887e82013-09-24 11:05:57 -0700679
Robin Lee4d03abc2016-05-09 12:32:27 +0100680 // Assign the top-level user to the set of ranges
681 addUserToRanges(ranges, userHandle, allowedApplications, disallowedApplications);
682
683 // If the user can have restricted profiles, assign all its restricted profiles too
684 if (canHaveRestrictedProfile(userHandle)) {
685 final long token = Binder.clearCallingIdentity();
686 List<UserInfo> users;
687 try {
688 users = UserManager.get(mContext).getUsers();
689 } finally {
690 Binder.restoreCallingIdentity(token);
691 }
692 for (UserInfo user : users) {
693 if (user.isRestricted() && (user.restrictedProfileParentId == userHandle)) {
694 addUserToRanges(ranges, user.id, allowedApplications, disallowedApplications);
695 }
696 }
697 }
698 return ranges;
699 }
700
701 /**
702 * Updates a {@link Set} of non-intersecting {@link UidRange} objects to include all UIDs
703 * associated with one user.
704 *
705 * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
706 * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs
707 * in the user will be included.
708 *
709 * @param ranges {@link Set} of {@link UidRange}s to which to add.
710 * @param userHandle The userId to add to {@param ranges}.
711 * @param allowedApplications (optional) whitelist of applications to include.
712 * @param disallowedApplications (optional) blacklist of applications to exclude.
713 */
714 @VisibleForTesting
715 void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userHandle,
716 @Nullable List<String> allowedApplications,
717 @Nullable List<String> disallowedApplications) {
718 if (allowedApplications != null) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400719 // Add ranges covering all UIDs for allowedApplications.
720 int start = -1, stop = -1;
Robin Lee4d03abc2016-05-09 12:32:27 +0100721 for (int uid : getAppsUids(allowedApplications, userHandle)) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400722 if (start == -1) {
723 start = uid;
724 } else if (uid != stop + 1) {
Robin Lee4d03abc2016-05-09 12:32:27 +0100725 ranges.add(new UidRange(start, stop));
Paul Jensen0784eea2014-08-19 16:00:24 -0400726 start = uid;
727 }
728 stop = uid;
729 }
Robin Lee4d03abc2016-05-09 12:32:27 +0100730 if (start != -1) ranges.add(new UidRange(start, stop));
731 } else if (disallowedApplications != null) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400732 // Add all ranges for user skipping UIDs for disallowedApplications.
733 final UidRange userRange = UidRange.createForUser(userHandle);
734 int start = userRange.start;
Robin Lee4d03abc2016-05-09 12:32:27 +0100735 for (int uid : getAppsUids(disallowedApplications, userHandle)) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400736 if (uid == start) {
737 start++;
738 } else {
Robin Lee4d03abc2016-05-09 12:32:27 +0100739 ranges.add(new UidRange(start, uid - 1));
Paul Jensen0784eea2014-08-19 16:00:24 -0400740 start = uid + 1;
741 }
742 }
Robin Lee4d03abc2016-05-09 12:32:27 +0100743 if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop));
Paul Jensen0784eea2014-08-19 16:00:24 -0400744 } else {
745 // Add all UIDs for the user.
Robin Lee4d03abc2016-05-09 12:32:27 +0100746 ranges.add(UidRange.createForUser(userHandle));
Paul Jensen0784eea2014-08-19 16:00:24 -0400747 }
Chad Brubakerc2865192013-07-10 14:46:23 -0700748 }
749
Paul Jensen0784eea2014-08-19 16:00:24 -0400750 // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
751 // apply to userHandle.
752 private List<UidRange> uidRangesForUser(int userHandle) {
753 final UidRange userRange = UidRange.createForUser(userHandle);
754 final List<UidRange> ranges = new ArrayList<UidRange>();
755 for (UidRange range : mVpnUsers) {
Robin Lee4d03abc2016-05-09 12:32:27 +0100756 if (userRange.containsRange(range)) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400757 ranges.add(range);
758 }
759 }
760 return ranges;
761 }
762
763 private void removeVpnUserLocked(int userHandle) {
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -0800764 if (mVpnUsers == null) {
Jeff Davidson90b1b9f2014-08-22 13:05:43 -0700765 throw new IllegalStateException("VPN is not active");
766 }
Paul Jensen0784eea2014-08-19 16:00:24 -0400767 final List<UidRange> ranges = uidRangesForUser(userHandle);
Jeff Davidson90b1b9f2014-08-22 13:05:43 -0700768 if (mNetworkAgent != null) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400769 mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
Jeff Davidson90b1b9f2014-08-22 13:05:43 -0700770 }
Paul Jensen0784eea2014-08-19 16:00:24 -0400771 mVpnUsers.removeAll(ranges);
Chad Brubakerc2865192013-07-10 14:46:23 -0700772 }
773
Fyodor Kupolov1c363152015-09-02 13:27:21 -0700774 public void onUserAdded(int userHandle) {
775 // If the user is restricted tie them to the parent user's VPN
776 UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
Fyodor Kupolov49d5a012016-03-04 13:41:58 -0800777 if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle
778 && mVpnUsers != null) {
Fyodor Kupolov1c363152015-09-02 13:27:21 -0700779 synchronized(Vpn.this) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700780 try {
Robin Lee4d03abc2016-05-09 12:32:27 +0100781 addUserToRanges(mVpnUsers, userHandle, mConfig.allowedApplications,
782 mConfig.disallowedApplications);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400783 if (mNetworkAgent != null) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400784 final List<UidRange> ranges = uidRangesForUser(userHandle);
785 mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400786 }
Chad Brubakerc2865192013-07-10 14:46:23 -0700787 } catch (Exception e) {
788 Log.wtf(TAG, "Failed to add restricted user to owner", e);
789 }
790 }
791 }
792 }
793
Fyodor Kupolov1c363152015-09-02 13:27:21 -0700794 public void onUserRemoved(int userHandle) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700795 // clean up if restricted
Fyodor Kupolov1c363152015-09-02 13:27:21 -0700796 UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
Fyodor Kupolov49d5a012016-03-04 13:41:58 -0800797 if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle
798 && mVpnUsers != null) {
Fyodor Kupolov1c363152015-09-02 13:27:21 -0700799 synchronized(Vpn.this) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700800 try {
Paul Jensen0784eea2014-08-19 16:00:24 -0400801 removeVpnUserLocked(userHandle);
Chad Brubakerc2865192013-07-10 14:46:23 -0700802 } catch (Exception e) {
803 Log.wtf(TAG, "Failed to remove restricted user to owner", e);
804 }
805 }
806 }
807 }
808
Chad Brubakerbf6ff2c2013-07-16 18:59:12 -0700809 /**
810 * Return the configuration of the currently running VPN.
811 */
812 public VpnConfig getVpnConfig() {
813 enforceControlPermission();
814 return mConfig;
815 }
816
Jeff Sharkey899223b2012-08-04 15:24:58 -0700817 @Deprecated
818 public synchronized void interfaceStatusChanged(String iface, boolean up) {
819 try {
820 mObserver.interfaceStatusChanged(iface, up);
821 } catch (RemoteException e) {
822 // ignored; target is local
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700823 }
824 }
825
Jeff Sharkey899223b2012-08-04 15:24:58 -0700826 private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
827 @Override
828 public void interfaceStatusChanged(String interfaze, boolean up) {
829 synchronized (Vpn.this) {
830 if (!up && mLegacyVpnRunner != null) {
831 mLegacyVpnRunner.check(interfaze);
832 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700833 }
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700834 }
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700835
Jeff Sharkey899223b2012-08-04 15:24:58 -0700836 @Override
837 public void interfaceRemoved(String interfaze) {
838 synchronized (Vpn.this) {
839 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
Jeff Davidson90b1b9f2014-08-22 13:05:43 -0700840 mStatusIntent = null;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400841 mVpnUsers = null;
Paul Jensenc4c72312014-12-08 14:04:51 -0500842 mConfig = null;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700843 mInterface = null;
844 if (mConnection != null) {
845 mContext.unbindService(mConnection);
846 mConnection = null;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400847 agentDisconnect();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700848 } else if (mLegacyVpnRunner != null) {
849 mLegacyVpnRunner.exit();
850 mLegacyVpnRunner = null;
851 }
852 }
853 }
854 }
855 };
Haoyu Baidb3c8672012-06-20 14:29:57 -0700856
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700857 private void enforceControlPermission() {
Jeff Davidsonbc19c182014-11-11 13:20:01 -0800858 mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700859 }
860
Robin Lee244ce8e2016-01-05 18:03:46 +0000861 private void enforceControlPermissionOrInternalCaller() {
862 // Require caller to be either an application with CONTROL_VPN permission or a process
863 // in the system server.
864 mContext.enforceCallingOrSelfPermission(Manifest.permission.CONTROL_VPN,
865 "Unauthorized Caller");
866 }
867
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700868 private class Connection implements ServiceConnection {
869 private IBinder mService;
870
871 @Override
872 public void onServiceConnected(ComponentName name, IBinder service) {
873 mService = service;
874 }
875
876 @Override
877 public void onServiceDisconnected(ComponentName name) {
878 mService = null;
879 }
880 }
881
Jeff Davidson90b1b9f2014-08-22 13:05:43 -0700882 private void prepareStatusIntent() {
883 final long token = Binder.clearCallingIdentity();
884 try {
885 mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
886 } finally {
887 Binder.restoreCallingIdentity(token);
888 }
889 }
890
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700891 public synchronized boolean addAddress(String address, int prefixLength) {
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -0800892 if (!isCallerEstablishedOwnerLocked()) {
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700893 return false;
894 }
895 boolean success = jniAddAddress(mInterface, address, prefixLength);
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -0800896 mNetworkAgent.sendLinkProperties(makeLinkProperties());
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700897 return success;
898 }
899
900 public synchronized boolean removeAddress(String address, int prefixLength) {
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -0800901 if (!isCallerEstablishedOwnerLocked()) {
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700902 return false;
903 }
904 boolean success = jniDelAddress(mInterface, address, prefixLength);
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -0800905 mNetworkAgent.sendLinkProperties(makeLinkProperties());
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700906 return success;
907 }
908
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -0800909 public synchronized boolean setUnderlyingNetworks(Network[] networks) {
910 if (!isCallerEstablishedOwnerLocked()) {
911 return false;
912 }
913 if (networks == null) {
914 mConfig.underlyingNetworks = null;
915 } else {
916 mConfig.underlyingNetworks = new Network[networks.length];
917 for (int i = 0; i < networks.length; ++i) {
918 if (networks[i] == null) {
919 mConfig.underlyingNetworks[i] = null;
920 } else {
921 mConfig.underlyingNetworks[i] = new Network(networks[i].netId);
922 }
923 }
924 }
925 return true;
926 }
927
928 public synchronized Network[] getUnderlyingNetworks() {
929 if (!isRunningLocked()) {
930 return null;
931 }
932 return mConfig.underlyingNetworks;
933 }
934
Wenchao Tongf5ea3402015-03-04 13:26:38 -0800935 /**
936 * This method should only be called by ConnectivityService. Because it doesn't
937 * have enough data to fill VpnInfo.primaryUnderlyingIface field.
938 */
939 public synchronized VpnInfo getVpnInfo() {
940 if (!isRunningLocked()) {
941 return null;
942 }
943
944 VpnInfo info = new VpnInfo();
945 info.ownerUid = mOwnerUID;
946 info.vpnIface = mInterface;
947 return info;
948 }
949
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -0800950 public synchronized boolean appliesToUid(int uid) {
951 if (!isRunningLocked()) {
952 return false;
953 }
954 for (UidRange uidRange : mVpnUsers) {
Robin Lee4d03abc2016-05-09 12:32:27 +0100955 if (uidRange.contains(uid)) {
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -0800956 return true;
957 }
958 }
959 return false;
960 }
961
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700962 private native int jniCreate(int mtu);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700963 private native String jniGetName(int tun);
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700964 private native int jniSetAddresses(String interfaze, String addresses);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700965 private native void jniReset(String interfaze);
966 private native int jniCheck(String interfaze);
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700967 private native boolean jniAddAddress(String interfaze, String address, int prefixLen);
968 private native boolean jniDelAddress(String interfaze, String address, int prefixLen);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700969
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900970 private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
971 for (RouteInfo route : prop.getAllRoutes()) {
Jeff Sharkey82f85212012-08-24 11:17:25 -0700972 // Currently legacy VPN only works on IPv4.
973 if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900974 return route;
Jeff Sharkey82f85212012-08-24 11:17:25 -0700975 }
976 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700977
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900978 throw new IllegalStateException("Unable to find IPv4 default gateway");
Jeff Sharkey82f85212012-08-24 11:17:25 -0700979 }
980
981 /**
982 * Start legacy VPN, controlling native daemons as needed. Creates a
983 * secondary thread to perform connection work, returning quickly.
Jeff Davidsonb21298a2015-02-10 10:02:11 -0800984 *
985 * Should only be called to respond to Binder requests as this enforces caller permission. Use
986 * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
987 * permission check only when the caller is trusted (or the call is initiated by the system).
Jeff Sharkey82f85212012-08-24 11:17:25 -0700988 */
989 public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
Robert Greenwalt5a6bdc42013-02-15 10:56:35 -0800990 enforceControlPermission();
Jeff Davidsonb21298a2015-02-10 10:02:11 -0800991 long token = Binder.clearCallingIdentity();
992 try {
993 startLegacyVpnPrivileged(profile, keyStore, egress);
994 } finally {
995 Binder.restoreCallingIdentity(token);
996 }
997 }
998
999 /**
1000 * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
1001 * permissions under the assumption that the caller is the system.
1002 *
1003 * Callers are responsible for checking permissions if needed.
1004 */
1005 public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
1006 LinkProperties egress) {
Julia Reynoldsf5116d02014-07-01 11:10:41 -04001007 UserManager mgr = UserManager.get(mContext);
Paul Jensen0784eea2014-08-19 16:00:24 -04001008 UserInfo user = mgr.getUserInfo(mUserHandle);
Nicolas Prevot95778ff2015-01-06 15:43:05 +00001009 if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
1010 new UserHandle(mUserHandle))) {
Julia Reynoldsf5116d02014-07-01 11:10:41 -04001011 throw new SecurityException("Restricted users cannot establish VPNs");
1012 }
Jeff Sharkey82f85212012-08-24 11:17:25 -07001013
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +09001014 final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
1015 final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
1016 final String iface = ipv4DefaultRoute.getInterface();
Jeff Sharkey82f85212012-08-24 11:17:25 -07001017
1018 // Load certificates.
1019 String privateKey = "";
1020 String userCert = "";
1021 String caCert = "";
1022 String serverCert = "";
1023 if (!profile.ipsecUserCert.isEmpty()) {
1024 privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
1025 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
Elliott Hughesd396a442013-06-28 16:24:48 -07001026 userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -07001027 }
1028 if (!profile.ipsecCaCert.isEmpty()) {
1029 byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
Elliott Hughesd396a442013-06-28 16:24:48 -07001030 caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -07001031 }
1032 if (!profile.ipsecServerCert.isEmpty()) {
1033 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
Elliott Hughesd396a442013-06-28 16:24:48 -07001034 serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -07001035 }
1036 if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
1037 throw new IllegalStateException("Cannot load credentials");
1038 }
1039
1040 // Prepare arguments for racoon.
1041 String[] racoon = null;
1042 switch (profile.type) {
1043 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
1044 racoon = new String[] {
1045 iface, profile.server, "udppsk", profile.ipsecIdentifier,
1046 profile.ipsecSecret, "1701",
1047 };
1048 break;
1049 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
1050 racoon = new String[] {
1051 iface, profile.server, "udprsa", privateKey, userCert,
1052 caCert, serverCert, "1701",
1053 };
1054 break;
1055 case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
1056 racoon = new String[] {
1057 iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
1058 profile.ipsecSecret, profile.username, profile.password, "", gateway,
1059 };
1060 break;
1061 case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
1062 racoon = new String[] {
1063 iface, profile.server, "xauthrsa", privateKey, userCert,
1064 caCert, serverCert, profile.username, profile.password, "", gateway,
1065 };
1066 break;
1067 case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
1068 racoon = new String[] {
1069 iface, profile.server, "hybridrsa",
1070 caCert, serverCert, profile.username, profile.password, "", gateway,
1071 };
1072 break;
1073 }
1074
1075 // Prepare arguments for mtpd.
1076 String[] mtpd = null;
1077 switch (profile.type) {
1078 case VpnProfile.TYPE_PPTP:
1079 mtpd = new String[] {
1080 iface, "pptp", profile.server, "1723",
1081 "name", profile.username, "password", profile.password,
1082 "linkname", "vpn", "refuse-eap", "nodefaultroute",
1083 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
1084 (profile.mppe ? "+mppe" : "nomppe"),
1085 };
1086 break;
1087 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
1088 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
1089 mtpd = new String[] {
1090 iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
1091 "name", profile.username, "password", profile.password,
1092 "linkname", "vpn", "refuse-eap", "nodefaultroute",
1093 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
1094 };
1095 break;
1096 }
1097
1098 VpnConfig config = new VpnConfig();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001099 config.legacy = true;
Jeff Sharkey82f85212012-08-24 11:17:25 -07001100 config.user = profile.key;
1101 config.interfaze = iface;
1102 config.session = profile.name;
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001103
1104 config.addLegacyRoutes(profile.routes);
Jeff Sharkey82f85212012-08-24 11:17:25 -07001105 if (!profile.dnsServers.isEmpty()) {
1106 config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
1107 }
1108 if (!profile.searchDomains.isEmpty()) {
1109 config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
1110 }
Jeff Sharkey82f85212012-08-24 11:17:25 -07001111 startLegacyVpn(config, racoon, mtpd);
1112 }
1113
1114 private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
Jeff Davidsonb21298a2015-02-10 10:02:11 -08001115 stopLegacyVpnPrivileged();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001116
Jeff Davidsonb21298a2015-02-10 10:02:11 -08001117 // Prepare for the new request.
1118 prepareInternal(VpnConfig.LEGACY_VPN);
Jeff Sharkey899223b2012-08-04 15:24:58 -07001119 updateState(DetailedState.CONNECTING, "startLegacyVpn");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001120
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001121 // Start a new LegacyVpnRunner and we are done!
Chia-chi Yeh100155a2011-07-03 16:52:38 -07001122 mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
1123 mLegacyVpnRunner.start();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001124 }
1125
Jeff Davidsonb21298a2015-02-10 10:02:11 -08001126 /** Stop legacy VPN. Permissions must be checked by callers. */
1127 public synchronized void stopLegacyVpnPrivileged() {
Jeff Sharkey899223b2012-08-04 15:24:58 -07001128 if (mLegacyVpnRunner != null) {
1129 mLegacyVpnRunner.exit();
1130 mLegacyVpnRunner = null;
1131
1132 synchronized (LegacyVpnRunner.TAG) {
1133 // wait for old thread to completely finish before spinning up
1134 // new instance, otherwise state updates can be out of order.
1135 }
1136 }
1137 }
1138
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001139 /**
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001140 * Return the information of the current ongoing legacy VPN.
1141 */
1142 public synchronized LegacyVpnInfo getLegacyVpnInfo() {
Chia-chi Yehdadc8572012-06-08 13:05:58 -07001143 // Check if the caller is authorized.
1144 enforceControlPermission();
sj.cha08bbca02015-03-23 11:35:24 +09001145 return getLegacyVpnInfoPrivileged();
1146 }
1147
1148 /**
1149 * Return the information of the current ongoing legacy VPN.
1150 * Callers are responsible for checking permissions if needed.
1151 */
1152 public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
Jeff Sharkey899223b2012-08-04 15:24:58 -07001153 if (mLegacyVpnRunner == null) return null;
1154
1155 final LegacyVpnInfo info = new LegacyVpnInfo();
Chad Brubakerc2865192013-07-10 14:46:23 -07001156 info.key = mConfig.user;
Jeff Sharkey899223b2012-08-04 15:24:58 -07001157 info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
Jeff Davidson90b1b9f2014-08-22 13:05:43 -07001158 if (mNetworkInfo.isConnected()) {
1159 info.intent = mStatusIntent;
1160 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001161 return info;
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001162 }
1163
Jeff Sharkey69ddab42012-08-25 00:05:46 -07001164 public VpnConfig getLegacyVpnConfig() {
1165 if (mLegacyVpnRunner != null) {
Chad Brubakerc2865192013-07-10 14:46:23 -07001166 return mConfig;
Jeff Sharkey69ddab42012-08-25 00:05:46 -07001167 } else {
1168 return null;
1169 }
1170 }
1171
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001172 /**
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001173 * Bringing up a VPN connection takes time, and that is all this thread
1174 * does. Here we have plenty of time. The only thing we need to take
1175 * care of is responding to interruptions as soon as possible. Otherwise
1176 * requests will be piled up. This can be done in a Handler as a state
1177 * machine, but it is much easier to read in the current form.
1178 */
1179 private class LegacyVpnRunner extends Thread {
1180 private static final String TAG = "LegacyVpnRunner";
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001181
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001182 private final String[] mDaemons;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001183 private final String[][] mArguments;
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001184 private final LocalSocket[] mSockets;
Robert Greenwalt53c04bd2012-10-12 17:02:45 -07001185 private final String mOuterInterface;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001186 private final AtomicInteger mOuterConnection =
1187 new AtomicInteger(ConnectivityManager.TYPE_NONE);
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001188
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001189 private long mTimer = -1;
1190
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001191 /**
1192 * Watch for the outer connection (passing in the constructor) going away.
1193 */
1194 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1195 @Override
1196 public void onReceive(Context context, Intent intent) {
Jeff Sharkey57666932013-04-30 17:01:57 -07001197 if (!mEnableTeardown) return;
1198
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001199 if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
1200 if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
1201 ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
1202 NetworkInfo info = (NetworkInfo)intent.getExtra(
1203 ConnectivityManager.EXTRA_NETWORK_INFO);
1204 if (info != null && !info.isConnectedOrConnecting()) {
1205 try {
1206 mObserver.interfaceStatusChanged(mOuterInterface, false);
1207 } catch (RemoteException e) {}
1208 }
1209 }
1210 }
1211 }
1212 };
1213
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001214 public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001215 super(TAG);
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001216 mConfig = config;
1217 mDaemons = new String[] {"racoon", "mtpd"};
Jeff Sharkey899223b2012-08-04 15:24:58 -07001218 // TODO: clear arguments from memory once launched
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001219 mArguments = new String[][] {racoon, mtpd};
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001220 mSockets = new LocalSocket[mDaemons.length];
Robert Greenwalt53c04bd2012-10-12 17:02:45 -07001221
1222 // This is the interface which VPN is running on,
1223 // mConfig.interfaze will change to point to OUR
1224 // internal interface soon. TODO - add inner/outer to mconfig
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001225 // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001226 // we will leave the VPN up. We should check that it's still there/connected after
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001227 // registering
Robert Greenwalt53c04bd2012-10-12 17:02:45 -07001228 mOuterInterface = mConfig.interfaze;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001229
Paul Jensene75b9e32015-04-06 11:54:53 -04001230 if (!TextUtils.isEmpty(mOuterInterface)) {
1231 final ConnectivityManager cm = ConnectivityManager.from(mContext);
1232 for (Network network : cm.getAllNetworks()) {
1233 final LinkProperties lp = cm.getLinkProperties(network);
Lorenzo Colitti1b60d112015-07-02 13:03:03 +09001234 if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) {
Paul Jensene75b9e32015-04-06 11:54:53 -04001235 final NetworkInfo networkInfo = cm.getNetworkInfo(network);
1236 if (networkInfo != null) mOuterConnection.set(networkInfo.getType());
1237 }
1238 }
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001239 }
1240
1241 IntentFilter filter = new IntentFilter();
1242 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
1243 mContext.registerReceiver(mBroadcastReceiver, filter);
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001244 }
1245
Chia-chi Yehaa1727f2011-07-14 18:55:33 -07001246 public void check(String interfaze) {
Robert Greenwalt53c04bd2012-10-12 17:02:45 -07001247 if (interfaze.equals(mOuterInterface)) {
Chia-chi Yehaa1727f2011-07-14 18:55:33 -07001248 Log.i(TAG, "Legacy VPN is going down with " + interfaze);
1249 exit();
1250 }
1251 }
1252
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001253 public void exit() {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001254 // We assume that everything is reset after stopping the daemons.
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001255 interrupt();
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001256 for (LocalSocket socket : mSockets) {
Jeff Sharkey065b2992012-08-05 14:16:48 -07001257 IoUtils.closeQuietly(socket);
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001258 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001259 agentDisconnect();
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001260 try {
1261 mContext.unregisterReceiver(mBroadcastReceiver);
1262 } catch (IllegalArgumentException e) {}
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001263 }
1264
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001265 @Override
1266 public void run() {
1267 // Wait for the previous thread since it has been interrupted.
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001268 Log.v(TAG, "Waiting");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001269 synchronized (TAG) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001270 Log.v(TAG, "Executing");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001271 execute();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001272 monitorDaemons();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001273 }
1274 }
1275
1276 private void checkpoint(boolean yield) throws InterruptedException {
1277 long now = SystemClock.elapsedRealtime();
1278 if (mTimer == -1) {
1279 mTimer = now;
1280 Thread.sleep(1);
Chia-chi Yeh7ef86112011-07-22 15:46:52 -07001281 } else if (now - mTimer <= 60000) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001282 Thread.sleep(yield ? 200 : 1);
1283 } else {
Jeff Sharkey899223b2012-08-04 15:24:58 -07001284 updateState(DetailedState.FAILED, "checkpoint");
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001285 throw new IllegalStateException("Time is up");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001286 }
1287 }
1288
1289 private void execute() {
1290 // Catch all exceptions so we can clean up few things.
Jeff Sharkey899223b2012-08-04 15:24:58 -07001291 boolean initFinished = false;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001292 try {
1293 // Initialize the timer.
1294 checkpoint(false);
1295
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001296 // Wait for the daemons to stop.
1297 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001298 while (!SystemService.isStopped(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001299 checkpoint(true);
1300 }
1301 }
1302
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001303 // Clear the previous state.
1304 File state = new File("/data/misc/vpn/state");
1305 state.delete();
1306 if (state.exists()) {
1307 throw new IllegalStateException("Cannot delete the state");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001308 }
Chia-chi Yehc1872732011-12-08 16:51:41 -08001309 new File("/data/misc/vpn/abort").delete();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001310 initFinished = true;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001311
Chia-chi Yehe9107902011-07-02 01:48:50 -07001312 // Check if we need to restart any of the daemons.
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001313 boolean restart = false;
1314 for (String[] arguments : mArguments) {
1315 restart = restart || (arguments != null);
1316 }
1317 if (!restart) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001318 agentDisconnect();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001319 return;
1320 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001321 updateState(DetailedState.CONNECTING, "execute");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001322
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001323 // Start the daemon with arguments.
1324 for (int i = 0; i < mDaemons.length; ++i) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001325 String[] arguments = mArguments[i];
1326 if (arguments == null) {
1327 continue;
1328 }
1329
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001330 // Start the daemon.
1331 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001332 SystemService.start(daemon);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001333
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001334 // Wait for the daemon to start.
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001335 while (!SystemService.isRunning(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001336 checkpoint(true);
1337 }
1338
1339 // Create the control socket.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001340 mSockets[i] = new LocalSocket();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001341 LocalSocketAddress address = new LocalSocketAddress(
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001342 daemon, LocalSocketAddress.Namespace.RESERVED);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001343
1344 // Wait for the socket to connect.
1345 while (true) {
1346 try {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001347 mSockets[i].connect(address);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001348 break;
1349 } catch (Exception e) {
1350 // ignore
1351 }
1352 checkpoint(true);
1353 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001354 mSockets[i].setSoTimeout(500);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001355
1356 // Send over the arguments.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001357 OutputStream out = mSockets[i].getOutputStream();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001358 for (String argument : arguments) {
Elliott Hughesd396a442013-06-28 16:24:48 -07001359 byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001360 if (bytes.length >= 0xFFFF) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001361 throw new IllegalArgumentException("Argument is too large");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001362 }
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001363 out.write(bytes.length >> 8);
1364 out.write(bytes.length);
1365 out.write(bytes);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001366 checkpoint(false);
1367 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001368 out.write(0xFF);
1369 out.write(0xFF);
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001370 out.flush();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001371
1372 // Wait for End-of-File.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001373 InputStream in = mSockets[i].getInputStream();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001374 while (true) {
1375 try {
1376 if (in.read() == -1) {
1377 break;
1378 }
1379 } catch (Exception e) {
1380 // ignore
1381 }
1382 checkpoint(true);
1383 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001384 }
1385
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001386 // Wait for the daemons to create the new state.
1387 while (!state.exists()) {
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001388 // Check if a running daemon is dead.
1389 for (int i = 0; i < mDaemons.length; ++i) {
1390 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001391 if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001392 throw new IllegalStateException(daemon + " is dead");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001393 }
1394 }
1395 checkpoint(true);
1396 }
1397
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001398 // Now we are connected. Read and parse the new state.
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -08001399 String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
Lorenzo Colitti50262792014-09-19 01:53:35 +09001400 if (parameters.length != 7) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001401 throw new IllegalStateException("Cannot parse the state");
1402 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001403
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001404 // Set the interface and the addresses in the config.
1405 mConfig.interfaze = parameters[0].trim();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001406
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001407 mConfig.addLegacyAddresses(parameters[1]);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001408 // Set the routes if they are not set in the config.
1409 if (mConfig.routes == null || mConfig.routes.isEmpty()) {
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001410 mConfig.addLegacyRoutes(parameters[2]);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001411 }
1412
1413 // Set the DNS servers if they are not set in the config.
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001414 if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001415 String dnsServers = parameters[3].trim();
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001416 if (!dnsServers.isEmpty()) {
1417 mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
1418 }
1419 }
1420
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001421 // Set the search domains if they are not set in the config.
1422 if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
1423 String searchDomains = parameters[4].trim();
1424 if (!searchDomains.isEmpty()) {
1425 mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
1426 }
1427 }
Chia-chi Yehe9107902011-07-02 01:48:50 -07001428
Lorenzo Colitti50262792014-09-19 01:53:35 +09001429 // Add a throw route for the VPN server endpoint, if one was specified.
1430 String endpoint = parameters[5];
1431 if (!endpoint.isEmpty()) {
1432 try {
1433 InetAddress addr = InetAddress.parseNumericAddress(endpoint);
1434 if (addr instanceof Inet4Address) {
1435 mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW));
1436 } else if (addr instanceof Inet6Address) {
1437 mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW));
1438 } else {
1439 Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint);
1440 }
1441 } catch (IllegalArgumentException e) {
1442 Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e);
1443 }
1444 }
1445
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001446 // Here is the last step and it must be done synchronously.
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001447 synchronized (Vpn.this) {
Vinit Deshapnde2b862e52013-10-02 11:50:39 -07001448 // Set the start time
1449 mConfig.startTime = SystemClock.elapsedRealtime();
1450
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001451 // Check if the thread is interrupted while we are waiting.
1452 checkpoint(false);
1453
Chia-chi Yehe9107902011-07-02 01:48:50 -07001454 // Check if the interface is gone while we are waiting.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -07001455 if (jniCheck(mConfig.interfaze) == 0) {
Chia-chi Yeh34e78132011-07-03 03:07:07 -07001456 throw new IllegalStateException(mConfig.interfaze + " is gone");
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001457 }
Chia-chi Yehe9107902011-07-02 01:48:50 -07001458
1459 // Now INetworkManagementEventObserver is watching our back.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -07001460 mInterface = mConfig.interfaze;
Robin Lee4d03abc2016-05-09 12:32:27 +01001461 prepareStatusIntent();
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001462
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001463 agentConnect();
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001464
1465 Log.i(TAG, "Connected!");
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001466 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001467 } catch (Exception e) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001468 Log.i(TAG, "Aborting", e);
Lorenzo Colitti43840602014-08-20 16:01:44 -07001469 updateState(DetailedState.FAILED, e.getMessage());
Chia-chi Yehe9107902011-07-02 01:48:50 -07001470 exit();
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001471 } finally {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001472 // Kill the daemons if they fail to stop.
Jeff Sharkey899223b2012-08-04 15:24:58 -07001473 if (!initFinished) {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001474 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001475 SystemService.stop(daemon);
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001476 }
1477 }
1478
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001479 // Do not leave an unstable state.
Jeff Sharkey899223b2012-08-04 15:24:58 -07001480 if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001481 agentDisconnect();
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001482 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001483 }
1484 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001485
1486 /**
1487 * Monitor the daemons we started, moving to disconnected state if the
1488 * underlying services fail.
1489 */
1490 private void monitorDaemons() {
1491 if (!mNetworkInfo.isConnected()) {
1492 return;
1493 }
1494
1495 try {
1496 while (true) {
1497 Thread.sleep(2000);
1498 for (int i = 0; i < mDaemons.length; i++) {
1499 if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1500 return;
1501 }
1502 }
1503 }
1504 } catch (InterruptedException e) {
1505 Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
1506 } finally {
1507 for (String daemon : mDaemons) {
1508 SystemService.stop(daemon);
1509 }
1510
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001511 agentDisconnect();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001512 }
1513 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001514 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -07001515}