blob: 5dc8640ff41f82e6209decc709b4542771961eee [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;
Tony Makde7f7d12016-06-30 11:19:20 +010030import android.app.Notification;
31import android.app.NotificationManager;
Jeff Davidson90b1b9f2014-08-22 13:05:43 -070032import android.app.PendingIntent;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070033import android.content.BroadcastReceiver;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070034import android.content.ComponentName;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070035import android.content.Context;
36import android.content.Intent;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070037import android.content.IntentFilter;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070038import android.content.ServiceConnection;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070039import android.content.pm.PackageManager;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040040import android.content.pm.PackageManager.NameNotFoundException;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070041import android.content.pm.ResolveInfo;
Chad Brubakerc2865192013-07-10 14:46:23 -070042import android.content.pm.UserInfo;
Jeff Sharkey899223b2012-08-04 15:24:58 -070043import android.net.ConnectivityManager;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070044import android.net.INetworkManagementEventObserver;
Lorenzo Colitti50262792014-09-19 01:53:35 +090045import android.net.IpPrefix;
Chad Brubaker4ca19e82013-06-14 11:16:51 -070046import android.net.LinkAddress;
Jeff Sharkey82f85212012-08-24 11:17:25 -070047import android.net.LinkProperties;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070048import android.net.LocalSocket;
49import android.net.LocalSocketAddress;
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -080050import android.net.Network;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040051import android.net.NetworkAgent;
52import android.net.NetworkCapabilities;
Jeff Sharkey899223b2012-08-04 15:24:58 -070053import android.net.NetworkInfo;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040054import android.net.NetworkInfo.DetailedState;
Sreeram Ramachandran8cd33ed2014-07-23 15:23:15 -070055import android.net.NetworkMisc;
Jeff Sharkey82f85212012-08-24 11:17:25 -070056import android.net.RouteInfo;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040057import android.net.UidRange;
Robin Lee812800c2016-05-13 15:38:08 +010058import android.net.Uri;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070059import android.os.Binder;
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -080060import android.os.FileUtils;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070061import android.os.IBinder;
Jeff Sharkey899223b2012-08-04 15:24:58 -070062import android.os.INetworkManagementService;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040063import android.os.Looper;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070064import android.os.Parcel;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070065import android.os.ParcelFileDescriptor;
Robin Lee812800c2016-05-13 15:38:08 +010066import android.os.PatternMatcher;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070067import android.os.Process;
Jeff Sharkey899223b2012-08-04 15:24:58 -070068import android.os.RemoteException;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070069import android.os.SystemClock;
Jeff Sharkey088f29f2012-08-05 14:55:04 -070070import android.os.SystemService;
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -070071import android.os.UserHandle;
Chad Brubakerc2865192013-07-10 14:46:23 -070072import android.os.UserManager;
Robin Lee812800c2016-05-13 15:38:08 +010073import android.provider.Settings;
Jeff Sharkey82f85212012-08-24 11:17:25 -070074import android.security.Credentials;
75import android.security.KeyStore;
Paul Jensene75b9e32015-04-06 11:54:53 -040076import android.text.TextUtils;
Robin Lee4d03abc2016-05-09 12:32:27 +010077import android.util.ArraySet;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070078import android.util.Log;
79
Tony Makde7f7d12016-06-30 11:19:20 +010080import com.android.internal.R;
Chad Brubakerc2865192013-07-10 14:46:23 -070081import com.android.internal.annotations.GuardedBy;
Robin Lee4d03abc2016-05-09 12:32:27 +010082import com.android.internal.annotations.VisibleForTesting;
Chia-chi Yeh2e467642011-07-04 03:23:12 -070083import com.android.internal.net.LegacyVpnInfo;
Chia-chi Yeh04ba25c2011-06-15 17:07:27 -070084import com.android.internal.net.VpnConfig;
Wenchao Tongf5ea3402015-03-04 13:26:38 -080085import com.android.internal.net.VpnInfo;
Jeff Sharkey82f85212012-08-24 11:17:25 -070086import com.android.internal.net.VpnProfile;
Jeff Sharkey899223b2012-08-04 15:24:58 -070087import com.android.server.net.BaseNetworkObserver;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070088
Jeff Davidson05542602014-08-11 14:07:27 -070089import libcore.io.IoUtils;
90
Chia-chi Yeh97a61562011-07-14 15:05:05 -070091import java.io.File;
Jeff Davidson6bbf39c2014-07-23 10:14:53 -070092import java.io.IOException;
Chia-chi Yeh97a61562011-07-14 15:05:05 -070093import java.io.InputStream;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070094import java.io.OutputStream;
Jeff Sharkey82f85212012-08-24 11:17:25 -070095import java.net.Inet4Address;
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -070096import java.net.Inet6Address;
97import java.net.InetAddress;
Elliott Hughesd396a442013-06-28 16:24:48 -070098import java.nio.charset.StandardCharsets;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040099import java.util.ArrayList;
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700100import java.util.Arrays;
Robin Lee4d03abc2016-05-09 12:32:27 +0100101import java.util.Collection;
102import java.util.Collections;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400103import java.util.List;
Robin Lee4d03abc2016-05-09 12:32:27 +0100104import java.util.Set;
Paul Jensen0784eea2014-08-19 16:00:24 -0400105import java.util.SortedSet;
106import java.util.TreeSet;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700107import java.util.concurrent.atomic.AtomicInteger;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700108
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700109/**
110 * @hide
111 */
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400112public class Vpn {
113 private static final String NETWORKTYPE = "VPN";
Jeff Sharkey899223b2012-08-04 15:24:58 -0700114 private static final String TAG = "Vpn";
115 private static final boolean LOGD = true;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400116
Jeff Sharkey899223b2012-08-04 15:24:58 -0700117 // TODO: create separate trackers for each unique VPN to support
118 // automated reconnection
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700119
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400120 private Context mContext;
121 private NetworkInfo mNetworkInfo;
122 private String mPackage;
123 private int mOwnerUID;
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700124 private String mInterface;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700125 private Connection mConnection;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700126 private LegacyVpnRunner mLegacyVpnRunner;
Jeff Davidson90b1b9f2014-08-22 13:05:43 -0700127 private PendingIntent mStatusIntent;
Jeff Sharkey57666932013-04-30 17:01:57 -0700128 private volatile boolean mEnableTeardown = true;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400129 private final INetworkManagementService mNetd;
Chad Brubakerc2865192013-07-10 14:46:23 -0700130 private VpnConfig mConfig;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400131 private NetworkAgent mNetworkAgent;
132 private final Looper mLooper;
133 private final NetworkCapabilities mNetworkCapabilities;
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000134 private final SystemServices mSystemServices;
Chad Brubakerc2865192013-07-10 14:46:23 -0700135
Robin Lee4d03abc2016-05-09 12:32:27 +0100136 /**
Robin Lee17e61832016-05-09 13:46:28 +0100137 * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
138 * only applies to {@link VpnService} connections.
139 */
140 private boolean mAlwaysOn = false;
141
142 /**
143 * Whether to disable traffic outside of this VPN even when the VPN is not connected. System
144 * apps can still bypass by choosing explicit networks. Has no effect if {@link mAlwaysOn} is
145 * not set.
146 */
147 private boolean mLockdown = false;
148
149 /**
Robin Lee4d03abc2016-05-09 12:32:27 +0100150 * List of UIDs that are set to use this VPN by default. Normally, every UID in the user is
151 * added to this set but that can be changed by adding allowed or disallowed applications. It
152 * is non-null iff the VPN is connected.
153 *
154 * Unless the VPN has set allowBypass=true, these UIDs are forced into the VPN.
155 *
156 * @see VpnService.Builder#addAllowedApplication(String)
157 * @see VpnService.Builder#addDisallowedApplication(String)
158 */
Chad Brubakerc2865192013-07-10 14:46:23 -0700159 @GuardedBy("this")
Robin Lee4d03abc2016-05-09 12:32:27 +0100160 private Set<UidRange> mVpnUsers = null;
Chad Brubakerc2865192013-07-10 14:46:23 -0700161
Robin Lee17e61832016-05-09 13:46:28 +0100162 /**
163 * List of UIDs for which networking should be blocked until VPN is ready, during brief periods
164 * when VPN is not running. For example, during system startup or after a crash.
165 * @see mLockdown
166 */
167 @GuardedBy("this")
168 private Set<UidRange> mBlockedUsers = new ArraySet<>();
169
Paul Jensen0784eea2014-08-19 16:00:24 -0400170 // Handle of user initiating VPN.
171 private final int mUserHandle;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700172
Robin Lee812800c2016-05-13 15:38:08 +0100173 // Listen to package remove and change event in this user
174 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
175 @Override
176 public void onReceive(Context context, Intent intent) {
177 final Uri data = intent.getData();
178 final String packageName = data == null ? null : data.getSchemeSpecificPart();
179 if (packageName == null) {
180 return;
181 }
182
183 synchronized (Vpn.this) {
184 // Avoid race that always-on package has been unset
185 if (!packageName.equals(getAlwaysOnPackage())) {
186 return;
187 }
188
189 final String action = intent.getAction();
190 Log.i(TAG, "Received broadcast " + action + " for always-on package " + packageName
191 + " in user " + mUserHandle);
192
193 switch(action) {
194 case Intent.ACTION_PACKAGE_REPLACED:
195 // Start vpn after app upgrade
196 startAlwaysOnVpn();
197 break;
198 case Intent.ACTION_PACKAGE_REMOVED:
199 final boolean isPackageRemoved = !intent.getBooleanExtra(
200 Intent.EXTRA_REPLACING, false);
201 if (isPackageRemoved) {
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000202 setAlwaysOnPackage(null, false);
Robin Lee812800c2016-05-13 15:38:08 +0100203 }
204 break;
205 }
206 }
207 }
208 };
209
210 private boolean mIsPackageIntentReceiverRegistered = false;
211
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400212 public Vpn(Looper looper, Context context, INetworkManagementService netService,
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000213 @UserIdInt int userHandle) {
214 this(looper, context, netService, userHandle, new SystemServices(context));
215 }
216
217 @VisibleForTesting
218 protected Vpn(Looper looper, Context context, INetworkManagementService netService,
219 int userHandle, SystemServices systemServices) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700220 mContext = context;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400221 mNetd = netService;
Paul Jensen0784eea2014-08-19 16:00:24 -0400222 mUserHandle = userHandle;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400223 mLooper = looper;
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000224 mSystemServices = systemServices;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400225
226 mPackage = VpnConfig.LEGACY_VPN;
Paul Jensen0784eea2014-08-19 16:00:24 -0400227 mOwnerUID = getAppUid(mPackage, mUserHandle);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700228
229 try {
230 netService.registerObserver(mObserver);
231 } catch (RemoteException e) {
232 Log.wtf(TAG, "Problem registering observer", e);
233 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400234
235 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
236 // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
237 mNetworkCapabilities = new NetworkCapabilities();
238 mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
239 mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000240
241 loadAlwaysOnPackage();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700242 }
243
Jeff Sharkey57666932013-04-30 17:01:57 -0700244 /**
Jeff Sharkey57666932013-04-30 17:01:57 -0700245 * Set if this object is responsible for watching for {@link NetworkInfo}
246 * teardown. When {@code false}, teardown is handled externally by someone
247 * else.
248 */
249 public void setEnableTeardown(boolean enableTeardown) {
250 mEnableTeardown = enableTeardown;
251 }
252
Jeff Sharkey899223b2012-08-04 15:24:58 -0700253 /**
254 * Update current state, dispaching event to listeners.
255 */
Tony Makde7f7d12016-06-30 11:19:20 +0100256 @VisibleForTesting
257 protected void updateState(DetailedState detailedState, String reason) {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700258 if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
259 mNetworkInfo.setDetailedState(detailedState, reason, null);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400260 if (mNetworkAgent != null) {
261 mNetworkAgent.sendNetworkInfo(mNetworkInfo);
262 }
Tony Makde7f7d12016-06-30 11:19:20 +0100263 updateAlwaysOnNotification(detailedState);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700264 }
265
266 /**
Robin Leec3736bc2017-03-10 16:19:54 +0000267 * Chooses whether to force all connections to go though VPN.
268 *
269 * Used to enable/disable legacy VPN lockdown.
270 *
271 * This uses the same ip rule mechanism as {@link #setAlwaysOnPackage(String, boolean)};
272 * previous settings from calling that function will be replaced and saved with the
273 * always-on state.
274 *
275 * @param lockdown whether to prevent all traffic outside of a VPN.
276 */
277 public synchronized void setLockdown(boolean lockdown) {
278 enforceControlPermissionOrInternalCaller();
279
280 setVpnForcedLocked(lockdown);
281 mLockdown = lockdown;
282
283 // Update app lockdown setting if it changed. Legacy VPN lockdown status is controlled by
284 // LockdownVpnTracker.isEnabled() which keeps track of its own state.
285 if (mAlwaysOn) {
286 saveAlwaysOnPackage();
287 }
288 }
289
290 /**
Robin Lee244ce8e2016-01-05 18:03:46 +0000291 * Configures an always-on VPN connection through a specific application.
292 * This connection is automatically granted and persisted after a reboot.
293 *
294 * <p>The designated package should exist and declare a {@link VpnService} in its
295 * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
296 * otherwise the call will fail.
297 *
Robin Lee17e61832016-05-09 13:46:28 +0100298 * @param packageName the package to designate as always-on VPN supplier.
299 * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
Robin Lee9ff1a582016-06-10 16:41:10 +0100300 * @return {@code true} if the package has been set as always-on, {@code false} otherwise.
Robin Lee244ce8e2016-01-05 18:03:46 +0000301 */
Robin Lee17e61832016-05-09 13:46:28 +0100302 public synchronized boolean setAlwaysOnPackage(String packageName, boolean lockdown) {
Robin Lee244ce8e2016-01-05 18:03:46 +0000303 enforceControlPermissionOrInternalCaller();
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000304
305 if (setAlwaysOnPackageInternal(packageName, lockdown)) {
306 saveAlwaysOnPackage();
307 return true;
308 }
309 return false;
310 }
311
312 /**
313 * Configures an always-on VPN connection through a specific application, the same as
314 * {@link #setAlwaysOnPackage}.
315 *
316 * Does not perform permission checks. Does not persist any of the changes to storage.
317 *
318 * @param packageName the package to designate as always-on VPN supplier.
319 * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
320 * @return {@code true} if the package has been set as always-on, {@code false} otherwise.
321 */
322 @GuardedBy("this")
323 private boolean setAlwaysOnPackageInternal(String packageName, boolean lockdown) {
Robin Lee9ff1a582016-06-10 16:41:10 +0100324 if (VpnConfig.LEGACY_VPN.equals(packageName)) {
325 Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on.");
326 return false;
327 }
Robin Lee244ce8e2016-01-05 18:03:46 +0000328
Robin Lee244ce8e2016-01-05 18:03:46 +0000329 if (packageName != null) {
Robin Lee9ff1a582016-06-10 16:41:10 +0100330 // Pre-authorize new always-on VPN package.
Robin Lee244ce8e2016-01-05 18:03:46 +0000331 if (!setPackageAuthorization(packageName, true)) {
332 return false;
333 }
Robin Lee9ff1a582016-06-10 16:41:10 +0100334 mAlwaysOn = true;
335 } else {
336 packageName = VpnConfig.LEGACY_VPN;
337 mAlwaysOn = false;
Robin Lee244ce8e2016-01-05 18:03:46 +0000338 }
339
Robin Lee17e61832016-05-09 13:46:28 +0100340 mLockdown = (mAlwaysOn && lockdown);
Tony Makde7f7d12016-06-30 11:19:20 +0100341 if (isCurrentPreparedPackage(packageName)) {
342 updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
343 } else {
344 // Prepare this app. The notification will update as a side-effect of updateState().
Robin Lee9ff1a582016-06-10 16:41:10 +0100345 prepareInternal(packageName);
346 }
Robin Lee812800c2016-05-13 15:38:08 +0100347 maybeRegisterPackageChangeReceiverLocked(packageName);
Robin Lee17e61832016-05-09 13:46:28 +0100348 setVpnForcedLocked(mLockdown);
Robin Lee244ce8e2016-01-05 18:03:46 +0000349 return true;
350 }
351
Robin Lee9ff1a582016-06-10 16:41:10 +0100352 private static boolean isNullOrLegacyVpn(String packageName) {
353 return packageName == null || VpnConfig.LEGACY_VPN.equals(packageName);
354 }
355
Robin Lee812800c2016-05-13 15:38:08 +0100356 private void unregisterPackageChangeReceiverLocked() {
357 // register previous intent filter
358 if (mIsPackageIntentReceiverRegistered) {
359 mContext.unregisterReceiver(mPackageIntentReceiver);
360 mIsPackageIntentReceiverRegistered = false;
361 }
362 }
363
364 private void maybeRegisterPackageChangeReceiverLocked(String packageName) {
365 // Unregister IntentFilter listening for previous always-on package change
366 unregisterPackageChangeReceiverLocked();
367
Robin Lee9ff1a582016-06-10 16:41:10 +0100368 if (!isNullOrLegacyVpn(packageName)) {
Robin Lee812800c2016-05-13 15:38:08 +0100369 mIsPackageIntentReceiverRegistered = true;
370
371 IntentFilter intentFilter = new IntentFilter();
372 // Protected intent can only be sent by system. No permission required in register.
373 intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
374 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
375 intentFilter.addDataScheme("package");
376 intentFilter.addDataSchemeSpecificPart(packageName, PatternMatcher.PATTERN_LITERAL);
377 mContext.registerReceiverAsUser(
378 mPackageIntentReceiver, UserHandle.of(mUserHandle), intentFilter, null, null);
379 }
380 }
381
Robin Lee244ce8e2016-01-05 18:03:46 +0000382 /**
383 * @return the package name of the VPN controller responsible for always-on VPN,
384 * or {@code null} if none is set or always-on VPN is controlled through
385 * lockdown instead.
386 * @hide
387 */
388 public synchronized String getAlwaysOnPackage() {
389 enforceControlPermissionOrInternalCaller();
Robin Lee17e61832016-05-09 13:46:28 +0100390 return (mAlwaysOn ? mPackage : null);
Robin Lee244ce8e2016-01-05 18:03:46 +0000391 }
392
393 /**
Robin Lee812800c2016-05-13 15:38:08 +0100394 * Save the always-on package and lockdown config into Settings.Secure
395 */
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000396 @GuardedBy("this")
397 private void saveAlwaysOnPackage() {
Robin Lee812800c2016-05-13 15:38:08 +0100398 final long token = Binder.clearCallingIdentity();
399 try {
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000400 mSystemServices.settingsSecurePutStringForUser(Settings.Secure.ALWAYS_ON_VPN_APP,
Robin Lee812800c2016-05-13 15:38:08 +0100401 getAlwaysOnPackage(), mUserHandle);
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000402 mSystemServices.settingsSecurePutIntForUser(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
Robin Leec3736bc2017-03-10 16:19:54 +0000403 (mAlwaysOn && mLockdown ? 1 : 0), mUserHandle);
Robin Lee812800c2016-05-13 15:38:08 +0100404 } finally {
405 Binder.restoreCallingIdentity(token);
406 }
407 }
408
409 /**
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000410 * Load the always-on package and lockdown config from Settings.Secure
Robin Lee812800c2016-05-13 15:38:08 +0100411 */
Robin Leeb8c2a2b2017-03-10 16:17:06 +0000412 @GuardedBy("this")
413 private void loadAlwaysOnPackage() {
414 final long token = Binder.clearCallingIdentity();
415 try {
416 final String alwaysOnPackage = mSystemServices.settingsSecureGetStringForUser(
417 Settings.Secure.ALWAYS_ON_VPN_APP, mUserHandle);
418 final boolean alwaysOnLockdown = mSystemServices.settingsSecureGetIntForUser(
419 Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, 0 /*default*/, mUserHandle) != 0;
420 setAlwaysOnPackageInternal(alwaysOnPackage, alwaysOnLockdown);
421 } finally {
422 Binder.restoreCallingIdentity(token);
Robin Lee812800c2016-05-13 15:38:08 +0100423 }
424 }
425
426 /**
427 * @return {@code true} if the service was started, the service was already connected, or there
428 * was no always-on VPN to start. {@code false} otherwise.
429 */
430 public boolean startAlwaysOnVpn() {
431 final String alwaysOnPackage;
432 synchronized (this) {
433 alwaysOnPackage = getAlwaysOnPackage();
434 // Skip if there is no service to start.
435 if (alwaysOnPackage == null) {
436 return true;
437 }
438 // Skip if the service is already established. This isn't bulletproof: it's not bound
439 // until after establish(), so if it's mid-setup onStartCommand will be sent twice,
440 // which may restart the connection.
441 if (getNetworkInfo().isConnected()) {
442 return true;
443 }
444 }
445
446 // Start the VPN service declared in the app's manifest.
447 Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
448 serviceIntent.setPackage(alwaysOnPackage);
449 try {
450 return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserHandle)) != null;
451 } catch (RuntimeException e) {
452 Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
453 return false;
454 }
455 }
456
457 /**
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700458 * Prepare for a VPN application. This method is designed to solve
459 * race conditions. It first compares the current prepared package
460 * with {@code oldPackage}. If they are the same, the prepared
461 * package is revoked and replaced with {@code newPackage}. If
462 * {@code oldPackage} is {@code null}, the comparison is omitted.
463 * If {@code newPackage} is the same package or {@code null}, the
464 * revocation is omitted. This method returns {@code true} if the
465 * operation is succeeded.
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700466 *
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700467 * Legacy VPN is handled specially since it is not a real package.
468 * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
469 * it can be revoked by itself.
470 *
Victor Chang98a633a2016-05-27 17:30:49 +0100471 * Note: when we added VPN pre-consent in http://ag/522961 the names oldPackage
472 * and newPackage become misleading, because when an app is pre-consented, we
473 * actually prepare oldPackage, not newPackage.
474 *
475 * Their meanings actually are:
476 *
477 * - oldPackage non-null, newPackage null: App calling VpnService#prepare().
478 * - oldPackage null, newPackage non-null: ConfirmDialog calling prepareVpn().
Robin Lee812800c2016-05-13 15:38:08 +0100479 * - oldPackage null, newPackage=LEGACY_VPN: Used internally to disconnect
Victor Chang98a633a2016-05-27 17:30:49 +0100480 * and revoke any current app VPN and re-prepare legacy vpn.
481 *
Robin Lee812800c2016-05-13 15:38:08 +0100482 * TODO: Rename the variables - or split this method into two - and end this confusion.
483 * TODO: b/29032008 Migrate code from prepare(oldPackage=non-null, newPackage=LEGACY_VPN)
484 * to prepare(oldPackage=null, newPackage=LEGACY_VPN)
Victor Chang98a633a2016-05-27 17:30:49 +0100485 *
486 * @param oldPackage The package name of the old VPN application
487 * @param newPackage The package name of the new VPN application
488 *
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700489 * @return true if the operation is succeeded.
Chia-chi Yehe9107902011-07-02 01:48:50 -0700490 */
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700491 public synchronized boolean prepare(String oldPackage, String newPackage) {
Jeff Davidson0a775ce2015-04-27 15:02:48 -0700492 if (oldPackage != null) {
Victor Chang98a633a2016-05-27 17:30:49 +0100493 // Stop an existing always-on VPN from being dethroned by other apps.
Robin Lee812800c2016-05-13 15:38:08 +0100494 if (mAlwaysOn && !isCurrentPreparedPackage(oldPackage)) {
Victor Chang98a633a2016-05-27 17:30:49 +0100495 return false;
496 }
497
498 // Package is not same or old package was reinstalled.
499 if (!isCurrentPreparedPackage(oldPackage)) {
Jeff Davidson0a775ce2015-04-27 15:02:48 -0700500 // The package doesn't match. We return false (to obtain user consent) unless the
501 // user has already consented to that VPN package.
502 if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
503 prepareInternal(oldPackage);
504 return true;
505 }
506 return false;
507 } else if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
508 && !isVpnUserPreConsented(oldPackage)) {
509 // Currently prepared VPN is revoked, so unprepare it and return false.
510 prepareInternal(VpnConfig.LEGACY_VPN);
511 return false;
Jeff Davidson05542602014-08-11 14:07:27 -0700512 }
Chia-chi Yehe9107902011-07-02 01:48:50 -0700513 }
514
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700515 // Return true if we do not need to revoke.
Jeff Davidsonbe085872014-10-24 10:35:53 -0700516 if (newPackage == null || (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
Victor Chang98a633a2016-05-27 17:30:49 +0100517 isCurrentPreparedPackage(newPackage))) {
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700518 return true;
519 }
520
Robin Lee1b1bcd72016-02-12 18:11:30 +0000521 // Check that the caller is authorized.
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700522 enforceControlPermission();
Chia-chi Yehe9107902011-07-02 01:48:50 -0700523
Victor Chang98a633a2016-05-27 17:30:49 +0100524 // Stop an existing always-on VPN from being dethroned by other apps.
Robin Lee812800c2016-05-13 15:38:08 +0100525 if (mAlwaysOn && !isCurrentPreparedPackage(newPackage)) {
Victor Chang98a633a2016-05-27 17:30:49 +0100526 return false;
527 }
528
Jeff Davidson11008a72014-11-20 13:12:46 -0800529 prepareInternal(newPackage);
530 return true;
531 }
Chia-chi Yehe9107902011-07-02 01:48:50 -0700532
Victor Chang98a633a2016-05-27 17:30:49 +0100533 private boolean isCurrentPreparedPackage(String packageName) {
534 // We can't just check that packageName matches mPackage, because if the app was uninstalled
535 // and reinstalled it will no longer be prepared. Instead check the UID.
536 return getAppUid(packageName, mUserHandle) == mOwnerUID;
537 }
538
Jeff Davidson11008a72014-11-20 13:12:46 -0800539 /** Prepare the VPN for the given package. Does not perform permission checks. */
540 private void prepareInternal(String newPackage) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400541 long token = Binder.clearCallingIdentity();
542 try {
Jeff Davidson11008a72014-11-20 13:12:46 -0800543 // Reset the interface.
544 if (mInterface != null) {
545 mStatusIntent = null;
546 agentDisconnect();
547 jniReset(mInterface);
548 mInterface = null;
549 mVpnUsers = null;
550 }
551
552 // Revoke the connection or stop LegacyVpnRunner.
553 if (mConnection != null) {
554 try {
555 mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
556 Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
557 } catch (Exception e) {
558 // ignore
559 }
560 mContext.unbindService(mConnection);
561 mConnection = null;
562 } else if (mLegacyVpnRunner != null) {
563 mLegacyVpnRunner.exit();
564 mLegacyVpnRunner = null;
565 }
566
567 try {
568 mNetd.denyProtect(mOwnerUID);
569 } catch (Exception e) {
570 Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
571 }
572
573 Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
574 mPackage = newPackage;
575 mOwnerUID = getAppUid(newPackage, mUserHandle);
576 try {
577 mNetd.allowProtect(mOwnerUID);
578 } catch (Exception e) {
579 Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
580 }
581 mConfig = null;
582
583 updateState(DetailedState.IDLE, "prepare");
Robin Leec3736bc2017-03-10 16:19:54 +0000584 setVpnForcedLocked(mLockdown);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400585 } finally {
586 Binder.restoreCallingIdentity(token);
587 }
Chia-chi Yehe9107902011-07-02 01:48:50 -0700588 }
589
Jeff Davidson05542602014-08-11 14:07:27 -0700590 /**
Robin Lee3b3dd942015-05-12 18:14:58 +0100591 * Set whether a package has the ability to launch VPNs without user intervention.
Jeff Davidson05542602014-08-11 14:07:27 -0700592 */
Robin Lee244ce8e2016-01-05 18:03:46 +0000593 public boolean setPackageAuthorization(String packageName, boolean authorized) {
Jeff Davidson05542602014-08-11 14:07:27 -0700594 // Check if the caller is authorized.
Robin Lee244ce8e2016-01-05 18:03:46 +0000595 enforceControlPermissionOrInternalCaller();
Jeff Davidson05542602014-08-11 14:07:27 -0700596
Robin Lee3b3dd942015-05-12 18:14:58 +0100597 int uid = getAppUid(packageName, mUserHandle);
598 if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
599 // Authorization for nonexistent packages (or fake ones) can't be updated.
Robin Lee244ce8e2016-01-05 18:03:46 +0000600 return false;
Jeff Davidson05542602014-08-11 14:07:27 -0700601 }
602
603 long token = Binder.clearCallingIdentity();
604 try {
605 AppOpsManager appOps =
606 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Robin Lee3b3dd942015-05-12 18:14:58 +0100607 appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
Jeff Davidson05542602014-08-11 14:07:27 -0700608 authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
Robin Lee244ce8e2016-01-05 18:03:46 +0000609 return true;
Jeff Davidson05542602014-08-11 14:07:27 -0700610 } catch (Exception e) {
Robin Lee3b3dd942015-05-12 18:14:58 +0100611 Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
Jeff Davidson05542602014-08-11 14:07:27 -0700612 } finally {
613 Binder.restoreCallingIdentity(token);
614 }
Robin Lee244ce8e2016-01-05 18:03:46 +0000615 return false;
Jeff Davidson05542602014-08-11 14:07:27 -0700616 }
617
618 private boolean isVpnUserPreConsented(String packageName) {
619 AppOpsManager appOps =
620 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
621
622 // Verify that the caller matches the given package and has permission to activate VPNs.
623 return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
624 packageName) == AppOpsManager.MODE_ALLOWED;
625 }
626
Paul Jensen0784eea2014-08-19 16:00:24 -0400627 private int getAppUid(String app, int userHandle) {
Jeff Davidson05542602014-08-11 14:07:27 -0700628 if (VpnConfig.LEGACY_VPN.equals(app)) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400629 return Process.myUid();
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700630 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400631 PackageManager pm = mContext.getPackageManager();
632 int result;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700633 try {
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700634 result = pm.getPackageUidAsUser(app, userHandle);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400635 } catch (NameNotFoundException e) {
636 result = -1;
637 }
638 return result;
639 }
640
641 public NetworkInfo getNetworkInfo() {
642 return mNetworkInfo;
643 }
644
Paul Jensen31a94f42015-02-13 14:18:39 -0500645 public int getNetId() {
646 return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET;
647 }
648
Lorenzo Colitti60446162014-09-20 00:14:31 +0900649 private LinkProperties makeLinkProperties() {
650 boolean allowIPv4 = mConfig.allowIPv4;
651 boolean allowIPv6 = mConfig.allowIPv6;
652
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400653 LinkProperties lp = new LinkProperties();
Lorenzo Colitti60446162014-09-20 00:14:31 +0900654
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400655 lp.setInterfaceName(mInterface);
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700656
Lorenzo Colitti60446162014-09-20 00:14:31 +0900657 if (mConfig.addresses != null) {
658 for (LinkAddress address : mConfig.addresses) {
659 lp.addLinkAddress(address);
660 allowIPv4 |= address.getAddress() instanceof Inet4Address;
661 allowIPv6 |= address.getAddress() instanceof Inet6Address;
662 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400663 }
Lorenzo Colitti60446162014-09-20 00:14:31 +0900664
665 if (mConfig.routes != null) {
666 for (RouteInfo route : mConfig.routes) {
667 lp.addRoute(route);
668 InetAddress address = route.getDestination().getAddress();
669 allowIPv4 |= address instanceof Inet4Address;
670 allowIPv6 |= address instanceof Inet6Address;
671 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400672 }
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700673
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400674 if (mConfig.dnsServers != null) {
675 for (String dnsServer : mConfig.dnsServers) {
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700676 InetAddress address = InetAddress.parseNumericAddress(dnsServer);
677 lp.addDnsServer(address);
Lorenzo Colitti60446162014-09-20 00:14:31 +0900678 allowIPv4 |= address instanceof Inet4Address;
679 allowIPv6 |= address instanceof Inet6Address;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400680 }
681 }
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700682
Lorenzo Colitti60446162014-09-20 00:14:31 +0900683 if (!allowIPv4) {
684 lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
685 }
686 if (!allowIPv6) {
687 lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
688 }
689
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400690 // Concatenate search domains into a string.
691 StringBuilder buffer = new StringBuilder();
692 if (mConfig.searchDomains != null) {
693 for (String domain : mConfig.searchDomains) {
694 buffer.append(domain).append(' ');
695 }
696 }
697 lp.setDomains(buffer.toString().trim());
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700698
Lorenzo Colitti60446162014-09-20 00:14:31 +0900699 // TODO: Stop setting the MTU in jniCreate and set it here.
700
701 return lp;
702 }
703
704 private void agentConnect() {
705 LinkProperties lp = makeLinkProperties();
706
707 if (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()) {
708 mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
709 } else {
710 mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
711 }
712
Robin Lee323f29d2016-05-04 16:38:06 +0100713 mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700714
Sreeram Ramachandran8cd33ed2014-07-23 15:23:15 -0700715 NetworkMisc networkMisc = new NetworkMisc();
Robin Lee17e61832016-05-09 13:46:28 +0100716 networkMisc.allowBypass = mConfig.allowBypass && !mLockdown;
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700717
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400718 long token = Binder.clearCallingIdentity();
719 try {
720 mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
Sreeram Ramachandran8cd33ed2014-07-23 15:23:15 -0700721 mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
Jeff Davidson05542602014-08-11 14:07:27 -0700722 @Override
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400723 public void unwanted() {
724 // We are user controlled, not driven by NetworkRequest.
Jeff Davidson05542602014-08-11 14:07:27 -0700725 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400726 };
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700727 } finally {
728 Binder.restoreCallingIdentity(token);
729 }
Sreeram Ramachandran42065ac2014-07-27 00:37:35 -0700730
Robin Lee4d03abc2016-05-09 12:32:27 +0100731 mVpnUsers = createUserAndRestrictedProfilesRanges(mUserHandle,
732 mConfig.allowedApplications, mConfig.disallowedApplications);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400733 mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
Robin Lee323f29d2016-05-04 16:38:06 +0100734
735 mNetworkInfo.setIsAvailable(true);
736 updateState(DetailedState.CONNECTED, "agentConnect");
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400737 }
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700738
Fyodor Kupolov1c363152015-09-02 13:27:21 -0700739 private boolean canHaveRestrictedProfile(int userId) {
740 long token = Binder.clearCallingIdentity();
741 try {
742 return UserManager.get(mContext).canHaveRestrictedProfile(userId);
743 } finally {
744 Binder.restoreCallingIdentity(token);
745 }
746 }
747
Tony Makde7f7d12016-06-30 11:19:20 +0100748 private void agentDisconnect(NetworkAgent networkAgent) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400749 if (networkAgent != null) {
Tony Makde7f7d12016-06-30 11:19:20 +0100750 NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
751 networkInfo.setIsAvailable(false);
752 networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400753 networkAgent.sendNetworkInfo(networkInfo);
754 }
755 }
756
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400757 private void agentDisconnect() {
758 if (mNetworkInfo.isConnected()) {
Tony Makde7f7d12016-06-30 11:19:20 +0100759 mNetworkInfo.setIsAvailable(false);
760 updateState(DetailedState.DISCONNECTED, "agentDisconnect");
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400761 mNetworkAgent = null;
762 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700763 }
764
765 /**
Chia-chi Yehe9107902011-07-02 01:48:50 -0700766 * Establish a VPN network and return the file descriptor of the VPN
767 * interface. This methods returns {@code null} if the application is
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700768 * revoked or not prepared.
Chia-chi Yehe9107902011-07-02 01:48:50 -0700769 *
770 * @param config The parameters to configure the network.
771 * @return The file descriptor of the VPN interface.
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700772 */
Chia-chi Yeh04ba25c2011-06-15 17:07:27 -0700773 public synchronized ParcelFileDescriptor establish(VpnConfig config) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700774 // Check if the caller is already prepared.
Chad Brubakerc2865192013-07-10 14:46:23 -0700775 UserManager mgr = UserManager.get(mContext);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400776 if (Binder.getCallingUid() != mOwnerUID) {
Chia-chi Yeh7b0b8342011-06-17 14:34:11 -0700777 return null;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700778 }
Jeff Davidson0a775ce2015-04-27 15:02:48 -0700779 // Check to ensure consent hasn't been revoked since we were prepared.
780 if (!isVpnUserPreConsented(mPackage)) {
781 return null;
782 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700783 // Check if the service is properly declared.
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700784 Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
785 intent.setClassName(mPackage, config.user);
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700786 long token = Binder.clearCallingIdentity();
787 try {
Chad Brubakerc2865192013-07-10 14:46:23 -0700788 // Restricted users are not allowed to create VPNs, they are tied to Owner
Paul Jensen0784eea2014-08-19 16:00:24 -0400789 UserInfo user = mgr.getUserInfo(mUserHandle);
Robin Lee628ae0d2016-05-20 14:53:48 +0100790 if (user.isRestricted()) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700791 throw new SecurityException("Restricted users cannot establish VPNs");
792 }
793
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700794 ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
Paul Jensen0784eea2014-08-19 16:00:24 -0400795 null, 0, mUserHandle);
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700796 if (info == null) {
797 throw new SecurityException("Cannot find " + config.user);
798 }
799 if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
800 throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
801 }
802 } catch (RemoteException e) {
803 throw new SecurityException("Cannot find " + config.user);
804 } finally {
805 Binder.restoreCallingIdentity(token);
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700806 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700807
Chad Brubaker850eb672014-03-21 21:02:47 +0000808 // Save the old config in case we need to go back.
809 VpnConfig oldConfig = mConfig;
810 String oldInterface = mInterface;
811 Connection oldConnection = mConnection;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400812 NetworkAgent oldNetworkAgent = mNetworkAgent;
813 mNetworkAgent = null;
Robin Lee4d03abc2016-05-09 12:32:27 +0100814 Set<UidRange> oldUsers = mVpnUsers;
Chad Brubaker850eb672014-03-21 21:02:47 +0000815
Chia-chi Yehe9107902011-07-02 01:48:50 -0700816 // Configure the interface. Abort if any of these steps fails.
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700817 ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700818 try {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700819 updateState(DetailedState.CONNECTING, "establish");
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700820 String interfaze = jniGetName(tun.getFd());
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700821
Chad Brubakerc2865192013-07-10 14:46:23 -0700822 // TEMP use the old jni calls until there is support for netd address setting
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700823 StringBuilder builder = new StringBuilder();
824 for (LinkAddress address : config.addresses) {
825 builder.append(" " + address);
826 }
827 if (jniSetAddresses(interfaze, builder.toString()) < 1) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700828 throw new IllegalArgumentException("At least one address must be specified");
829 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700830 Connection connection = new Connection();
Dianne Hackbornd69e4c12015-04-24 09:54:54 -0700831 if (!mContext.bindServiceAsUser(intent, connection,
832 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
833 new UserHandle(mUserHandle))) {
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700834 throw new IllegalStateException("Cannot bind " + config.user);
835 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000836
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700837 mConnection = connection;
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700838 mInterface = interfaze;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700839
840 // Fill more values.
841 config.user = mPackage;
842 config.interfaze = mInterface;
Chad Brubakerc2865192013-07-10 14:46:23 -0700843 config.startTime = SystemClock.elapsedRealtime();
844 mConfig = config;
Chad Brubaker850eb672014-03-21 21:02:47 +0000845
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700846 // Set up forwarding and DNS rules.
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400847 agentConnect();
Chad Brubaker850eb672014-03-21 21:02:47 +0000848
849 if (oldConnection != null) {
850 mContext.unbindService(oldConnection);
851 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400852 // Remove the old tun's user forwarding rules
853 // The new tun's user rules have already been added so they will take over
854 // as rules are deleted. This prevents data leakage as the rules are moved over.
855 agentDisconnect(oldNetworkAgent);
Chad Brubaker850eb672014-03-21 21:02:47 +0000856 if (oldInterface != null && !oldInterface.equals(interfaze)) {
Chad Brubaker850eb672014-03-21 21:02:47 +0000857 jniReset(oldInterface);
858 }
Jeff Davidson6bbf39c2014-07-23 10:14:53 -0700859
860 try {
861 IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
862 } catch (IOException e) {
863 throw new IllegalStateException(
864 "Cannot set tunnel's fd as blocking=" + config.blocking, e);
865 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000866 } catch (RuntimeException e) {
Chad Brubaker850eb672014-03-21 21:02:47 +0000867 IoUtils.closeQuietly(tun);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400868 agentDisconnect();
Chad Brubaker850eb672014-03-21 21:02:47 +0000869 // restore old state
870 mConfig = oldConfig;
871 mConnection = oldConnection;
872 mVpnUsers = oldUsers;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400873 mNetworkAgent = oldNetworkAgent;
Chad Brubaker850eb672014-03-21 21:02:47 +0000874 mInterface = oldInterface;
875 throw e;
Chad Brubakerc2865192013-07-10 14:46:23 -0700876 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000877 Log.i(TAG, "Established by " + config.user + " on " + mInterface);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700878 return tun;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700879 }
880
Chad Brubakerc2865192013-07-10 14:46:23 -0700881 private boolean isRunningLocked() {
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -0800882 return mNetworkAgent != null && mInterface != null;
883 }
884
885 // Returns true if the VPN has been established and the calling UID is its owner. Used to check
886 // that a call to mutate VPN state is admissible.
887 private boolean isCallerEstablishedOwnerLocked() {
888 return isRunningLocked() && Binder.getCallingUid() == mOwnerUID;
Chad Brubakerc2865192013-07-10 14:46:23 -0700889 }
890
Paul Jensen0784eea2014-08-19 16:00:24 -0400891 // Note: Return type guarantees results are deduped and sorted, which callers require.
892 private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
893 SortedSet<Integer> uids = new TreeSet<Integer>();
894 for (String app : packageNames) {
895 int uid = getAppUid(app, userHandle);
896 if (uid != -1) uids.add(uid);
897 }
898 return uids;
899 }
900
Robin Lee4d03abc2016-05-09 12:32:27 +0100901 /**
902 * Creates a {@link Set} of non-intersecting {@link UidRange} objects including all UIDs
903 * associated with one user, and any restricted profiles attached to that user.
904 *
905 * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
906 * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs
907 * in each user and profile will be included.
908 *
909 * @param userHandle The userId to create UID ranges for along with any of its restricted
910 * profiles.
911 * @param allowedApplications (optional) whitelist of applications to include.
912 * @param disallowedApplications (optional) blacklist of applications to exclude.
913 */
914 @VisibleForTesting
915 Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userHandle,
916 @Nullable List<String> allowedApplications,
917 @Nullable List<String> disallowedApplications) {
918 final Set<UidRange> ranges = new ArraySet<>();
Robert Greenwalt69887e82013-09-24 11:05:57 -0700919
Robin Lee4d03abc2016-05-09 12:32:27 +0100920 // Assign the top-level user to the set of ranges
921 addUserToRanges(ranges, userHandle, allowedApplications, disallowedApplications);
922
923 // If the user can have restricted profiles, assign all its restricted profiles too
924 if (canHaveRestrictedProfile(userHandle)) {
925 final long token = Binder.clearCallingIdentity();
926 List<UserInfo> users;
927 try {
Robin Lee17e61832016-05-09 13:46:28 +0100928 users = UserManager.get(mContext).getUsers(true);
Robin Lee4d03abc2016-05-09 12:32:27 +0100929 } finally {
930 Binder.restoreCallingIdentity(token);
931 }
932 for (UserInfo user : users) {
933 if (user.isRestricted() && (user.restrictedProfileParentId == userHandle)) {
934 addUserToRanges(ranges, user.id, allowedApplications, disallowedApplications);
935 }
936 }
937 }
938 return ranges;
939 }
940
941 /**
942 * Updates a {@link Set} of non-intersecting {@link UidRange} objects to include all UIDs
943 * associated with one user.
944 *
945 * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
946 * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs
947 * in the user will be included.
948 *
949 * @param ranges {@link Set} of {@link UidRange}s to which to add.
950 * @param userHandle The userId to add to {@param ranges}.
951 * @param allowedApplications (optional) whitelist of applications to include.
952 * @param disallowedApplications (optional) blacklist of applications to exclude.
953 */
954 @VisibleForTesting
955 void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userHandle,
956 @Nullable List<String> allowedApplications,
957 @Nullable List<String> disallowedApplications) {
958 if (allowedApplications != null) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400959 // Add ranges covering all UIDs for allowedApplications.
960 int start = -1, stop = -1;
Robin Lee4d03abc2016-05-09 12:32:27 +0100961 for (int uid : getAppsUids(allowedApplications, userHandle)) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400962 if (start == -1) {
963 start = uid;
964 } else if (uid != stop + 1) {
Robin Lee4d03abc2016-05-09 12:32:27 +0100965 ranges.add(new UidRange(start, stop));
Paul Jensen0784eea2014-08-19 16:00:24 -0400966 start = uid;
967 }
968 stop = uid;
969 }
Robin Lee4d03abc2016-05-09 12:32:27 +0100970 if (start != -1) ranges.add(new UidRange(start, stop));
971 } else if (disallowedApplications != null) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400972 // Add all ranges for user skipping UIDs for disallowedApplications.
973 final UidRange userRange = UidRange.createForUser(userHandle);
974 int start = userRange.start;
Robin Lee4d03abc2016-05-09 12:32:27 +0100975 for (int uid : getAppsUids(disallowedApplications, userHandle)) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400976 if (uid == start) {
977 start++;
978 } else {
Robin Lee4d03abc2016-05-09 12:32:27 +0100979 ranges.add(new UidRange(start, uid - 1));
Paul Jensen0784eea2014-08-19 16:00:24 -0400980 start = uid + 1;
981 }
982 }
Robin Lee4d03abc2016-05-09 12:32:27 +0100983 if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop));
Paul Jensen0784eea2014-08-19 16:00:24 -0400984 } else {
985 // Add all UIDs for the user.
Robin Lee4d03abc2016-05-09 12:32:27 +0100986 ranges.add(UidRange.createForUser(userHandle));
Paul Jensen0784eea2014-08-19 16:00:24 -0400987 }
Chad Brubakerc2865192013-07-10 14:46:23 -0700988 }
989
Paul Jensen0784eea2014-08-19 16:00:24 -0400990 // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
991 // apply to userHandle.
992 private List<UidRange> uidRangesForUser(int userHandle) {
993 final UidRange userRange = UidRange.createForUser(userHandle);
994 final List<UidRange> ranges = new ArrayList<UidRange>();
995 for (UidRange range : mVpnUsers) {
Robin Lee4d03abc2016-05-09 12:32:27 +0100996 if (userRange.containsRange(range)) {
Paul Jensen0784eea2014-08-19 16:00:24 -0400997 ranges.add(range);
998 }
999 }
1000 return ranges;
1001 }
1002
1003 private void removeVpnUserLocked(int userHandle) {
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -08001004 if (mVpnUsers == null) {
Jeff Davidson90b1b9f2014-08-22 13:05:43 -07001005 throw new IllegalStateException("VPN is not active");
1006 }
Paul Jensen0784eea2014-08-19 16:00:24 -04001007 final List<UidRange> ranges = uidRangesForUser(userHandle);
Jeff Davidson90b1b9f2014-08-22 13:05:43 -07001008 if (mNetworkAgent != null) {
Paul Jensen0784eea2014-08-19 16:00:24 -04001009 mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
Jeff Davidson90b1b9f2014-08-22 13:05:43 -07001010 }
Paul Jensen0784eea2014-08-19 16:00:24 -04001011 mVpnUsers.removeAll(ranges);
Chad Brubakerc2865192013-07-10 14:46:23 -07001012 }
1013
Fyodor Kupolov1c363152015-09-02 13:27:21 -07001014 public void onUserAdded(int userHandle) {
1015 // If the user is restricted tie them to the parent user's VPN
1016 UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
Robin Lee17e61832016-05-09 13:46:28 +01001017 if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
Fyodor Kupolov1c363152015-09-02 13:27:21 -07001018 synchronized(Vpn.this) {
Robin Lee17e61832016-05-09 13:46:28 +01001019 if (mVpnUsers != null) {
1020 try {
1021 addUserToRanges(mVpnUsers, userHandle, mConfig.allowedApplications,
1022 mConfig.disallowedApplications);
1023 if (mNetworkAgent != null) {
1024 final List<UidRange> ranges = uidRangesForUser(userHandle);
1025 mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
1026 }
1027 } catch (Exception e) {
1028 Log.wtf(TAG, "Failed to add restricted user to owner", e);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001029 }
Robin Lee17e61832016-05-09 13:46:28 +01001030 }
Robin Leec3736bc2017-03-10 16:19:54 +00001031 setVpnForcedLocked(mLockdown);
Chad Brubakerc2865192013-07-10 14:46:23 -07001032 }
1033 }
1034 }
1035
Fyodor Kupolov1c363152015-09-02 13:27:21 -07001036 public void onUserRemoved(int userHandle) {
Chad Brubakerc2865192013-07-10 14:46:23 -07001037 // clean up if restricted
Fyodor Kupolov1c363152015-09-02 13:27:21 -07001038 UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
Robin Lee17e61832016-05-09 13:46:28 +01001039 if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
Fyodor Kupolov1c363152015-09-02 13:27:21 -07001040 synchronized(Vpn.this) {
Robin Lee17e61832016-05-09 13:46:28 +01001041 if (mVpnUsers != null) {
1042 try {
1043 removeVpnUserLocked(userHandle);
1044 } catch (Exception e) {
1045 Log.wtf(TAG, "Failed to remove restricted user to owner", e);
1046 }
1047 }
Robin Leec3736bc2017-03-10 16:19:54 +00001048 setVpnForcedLocked(mLockdown);
Chad Brubakerc2865192013-07-10 14:46:23 -07001049 }
1050 }
1051 }
1052
Chad Brubakerbf6ff2c2013-07-16 18:59:12 -07001053 /**
Robin Lee17e61832016-05-09 13:46:28 +01001054 * Called when the user associated with this VPN has just been stopped.
1055 */
1056 public synchronized void onUserStopped() {
1057 // Switch off networking lockdown (if it was enabled)
Robin Leec3736bc2017-03-10 16:19:54 +00001058 setLockdown(false);
Robin Lee17e61832016-05-09 13:46:28 +01001059 mAlwaysOn = false;
1060
Robin Lee812800c2016-05-13 15:38:08 +01001061 unregisterPackageChangeReceiverLocked();
Robin Lee17e61832016-05-09 13:46:28 +01001062 // Quit any active connections
1063 agentDisconnect();
1064 }
1065
1066 /**
1067 * Restrict network access from all UIDs affected by this {@link Vpn}, apart from the VPN
1068 * service app itself, to only sockets that have had {@code protect()} called on them. All
1069 * non-VPN traffic is blocked via a {@code PROHIBIT} response from the kernel.
1070 *
1071 * The exception for the VPN UID isn't technically necessary -- setup should use protected
1072 * sockets -- but in practice it saves apps that don't protect their sockets from breaking.
1073 *
1074 * Calling multiple times with {@param enforce} = {@code true} will recreate the set of UIDs to
1075 * block every time, and if anything has changed update using {@link #setAllowOnlyVpnForUids}.
1076 *
1077 * @param enforce {@code true} to require that all traffic under the jurisdiction of this
1078 * {@link Vpn} goes through a VPN connection or is blocked until one is
1079 * available, {@code false} to lift the requirement.
1080 *
1081 * @see #mBlockedUsers
1082 */
1083 @GuardedBy("this")
1084 private void setVpnForcedLocked(boolean enforce) {
Robin Leec3736bc2017-03-10 16:19:54 +00001085 final List<String> exemptedPackages =
1086 isNullOrLegacyVpn(mPackage) ? null : Collections.singletonList(mPackage);
1087 setVpnForcedWithExemptionsLocked(enforce, exemptedPackages);
1088 }
1089
1090 /**
1091 * @see #setVpnForcedLocked
1092 */
1093 @GuardedBy("this")
1094 private void setVpnForcedWithExemptionsLocked(boolean enforce,
1095 @Nullable List<String> exemptedPackages) {
Robin Lee17e61832016-05-09 13:46:28 +01001096 final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers);
Robin Leec3736bc2017-03-10 16:19:54 +00001097
1098 Set<UidRange> addedRanges = Collections.emptySet();
Robin Lee17e61832016-05-09 13:46:28 +01001099 if (enforce) {
Robin Leec3736bc2017-03-10 16:19:54 +00001100 addedRanges = createUserAndRestrictedProfilesRanges(mUserHandle,
Robin Lee17e61832016-05-09 13:46:28 +01001101 /* allowedApplications */ null,
Robin Leec3736bc2017-03-10 16:19:54 +00001102 /* disallowedApplications */ exemptedPackages);
Robin Lee17e61832016-05-09 13:46:28 +01001103
1104 removedRanges.removeAll(addedRanges);
1105 addedRanges.removeAll(mBlockedUsers);
Robin Lee17e61832016-05-09 13:46:28 +01001106 }
Robin Leec3736bc2017-03-10 16:19:54 +00001107
1108 setAllowOnlyVpnForUids(false, removedRanges);
1109 setAllowOnlyVpnForUids(true, addedRanges);
Robin Lee17e61832016-05-09 13:46:28 +01001110 }
1111
1112 /**
1113 * Either add or remove a list of {@link UidRange}s to the list of UIDs that are only allowed
1114 * to make connections through sockets that have had {@code protect()} called on them.
1115 *
1116 * @param enforce {@code true} to add to the blacklist, {@code false} to remove.
1117 * @param ranges {@link Collection} of {@link UidRange}s to add (if {@param enforce} is
1118 * {@code true}) or to remove.
1119 * @return {@code true} if all of the UIDs were added/removed. {@code false} otherwise,
1120 * including added ranges that already existed or removed ones that didn't.
1121 */
1122 @GuardedBy("this")
1123 private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRange> ranges) {
1124 if (ranges.size() == 0) {
1125 return true;
1126 }
1127 final UidRange[] rangesArray = ranges.toArray(new UidRange[ranges.size()]);
1128 try {
1129 mNetd.setAllowOnlyVpnForUids(enforce, rangesArray);
1130 } catch (RemoteException | RuntimeException e) {
1131 Log.e(TAG, "Updating blocked=" + enforce
1132 + " for UIDs " + Arrays.toString(ranges.toArray()) + " failed", e);
1133 return false;
1134 }
1135 if (enforce) {
1136 mBlockedUsers.addAll(ranges);
1137 } else {
1138 mBlockedUsers.removeAll(ranges);
1139 }
1140 return true;
1141 }
1142
1143 /**
Chad Brubakerbf6ff2c2013-07-16 18:59:12 -07001144 * Return the configuration of the currently running VPN.
1145 */
1146 public VpnConfig getVpnConfig() {
1147 enforceControlPermission();
1148 return mConfig;
1149 }
1150
Jeff Sharkey899223b2012-08-04 15:24:58 -07001151 @Deprecated
1152 public synchronized void interfaceStatusChanged(String iface, boolean up) {
1153 try {
1154 mObserver.interfaceStatusChanged(iface, up);
1155 } catch (RemoteException e) {
1156 // ignored; target is local
Chia-chi Yehaa1727f2011-07-14 18:55:33 -07001157 }
1158 }
1159
Jeff Sharkey899223b2012-08-04 15:24:58 -07001160 private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
1161 @Override
1162 public void interfaceStatusChanged(String interfaze, boolean up) {
1163 synchronized (Vpn.this) {
1164 if (!up && mLegacyVpnRunner != null) {
1165 mLegacyVpnRunner.check(interfaze);
1166 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -07001167 }
Chia-chi Yehaa1727f2011-07-14 18:55:33 -07001168 }
Chia-chi Yehaa1727f2011-07-14 18:55:33 -07001169
Jeff Sharkey899223b2012-08-04 15:24:58 -07001170 @Override
1171 public void interfaceRemoved(String interfaze) {
1172 synchronized (Vpn.this) {
1173 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
Jeff Davidson90b1b9f2014-08-22 13:05:43 -07001174 mStatusIntent = null;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001175 mVpnUsers = null;
Paul Jensenc4c72312014-12-08 14:04:51 -05001176 mConfig = null;
Jeff Sharkey899223b2012-08-04 15:24:58 -07001177 mInterface = null;
1178 if (mConnection != null) {
1179 mContext.unbindService(mConnection);
1180 mConnection = null;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001181 agentDisconnect();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001182 } else if (mLegacyVpnRunner != null) {
1183 mLegacyVpnRunner.exit();
1184 mLegacyVpnRunner = null;
1185 }
1186 }
1187 }
1188 }
1189 };
Haoyu Baidb3c8672012-06-20 14:29:57 -07001190
Chia-chi Yehdadc8572012-06-08 13:05:58 -07001191 private void enforceControlPermission() {
Jeff Davidsonbc19c182014-11-11 13:20:01 -08001192 mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
Chia-chi Yehdadc8572012-06-08 13:05:58 -07001193 }
1194
Robin Lee244ce8e2016-01-05 18:03:46 +00001195 private void enforceControlPermissionOrInternalCaller() {
1196 // Require caller to be either an application with CONTROL_VPN permission or a process
1197 // in the system server.
1198 mContext.enforceCallingOrSelfPermission(Manifest.permission.CONTROL_VPN,
1199 "Unauthorized Caller");
1200 }
1201
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -07001202 private class Connection implements ServiceConnection {
1203 private IBinder mService;
1204
1205 @Override
1206 public void onServiceConnected(ComponentName name, IBinder service) {
1207 mService = service;
1208 }
1209
1210 @Override
1211 public void onServiceDisconnected(ComponentName name) {
1212 mService = null;
1213 }
1214 }
1215
Jeff Davidson90b1b9f2014-08-22 13:05:43 -07001216 private void prepareStatusIntent() {
1217 final long token = Binder.clearCallingIdentity();
1218 try {
1219 mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
1220 } finally {
1221 Binder.restoreCallingIdentity(token);
1222 }
1223 }
1224
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -07001225 public synchronized boolean addAddress(String address, int prefixLength) {
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -08001226 if (!isCallerEstablishedOwnerLocked()) {
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -07001227 return false;
1228 }
1229 boolean success = jniAddAddress(mInterface, address, prefixLength);
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -08001230 mNetworkAgent.sendLinkProperties(makeLinkProperties());
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -07001231 return success;
1232 }
1233
1234 public synchronized boolean removeAddress(String address, int prefixLength) {
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -08001235 if (!isCallerEstablishedOwnerLocked()) {
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -07001236 return false;
1237 }
1238 boolean success = jniDelAddress(mInterface, address, prefixLength);
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -08001239 mNetworkAgent.sendLinkProperties(makeLinkProperties());
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -07001240 return success;
1241 }
1242
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -08001243 public synchronized boolean setUnderlyingNetworks(Network[] networks) {
1244 if (!isCallerEstablishedOwnerLocked()) {
1245 return false;
1246 }
1247 if (networks == null) {
1248 mConfig.underlyingNetworks = null;
1249 } else {
1250 mConfig.underlyingNetworks = new Network[networks.length];
1251 for (int i = 0; i < networks.length; ++i) {
1252 if (networks[i] == null) {
1253 mConfig.underlyingNetworks[i] = null;
1254 } else {
1255 mConfig.underlyingNetworks[i] = new Network(networks[i].netId);
1256 }
1257 }
1258 }
1259 return true;
1260 }
1261
1262 public synchronized Network[] getUnderlyingNetworks() {
1263 if (!isRunningLocked()) {
1264 return null;
1265 }
1266 return mConfig.underlyingNetworks;
1267 }
1268
Wenchao Tongf5ea3402015-03-04 13:26:38 -08001269 /**
1270 * This method should only be called by ConnectivityService. Because it doesn't
1271 * have enough data to fill VpnInfo.primaryUnderlyingIface field.
1272 */
1273 public synchronized VpnInfo getVpnInfo() {
1274 if (!isRunningLocked()) {
1275 return null;
1276 }
1277
1278 VpnInfo info = new VpnInfo();
1279 info.ownerUid = mOwnerUID;
1280 info.vpnIface = mInterface;
1281 return info;
1282 }
1283
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -08001284 public synchronized boolean appliesToUid(int uid) {
1285 if (!isRunningLocked()) {
1286 return false;
1287 }
1288 for (UidRange uidRange : mVpnUsers) {
Robin Lee4d03abc2016-05-09 12:32:27 +01001289 if (uidRange.contains(uid)) {
Sreeram Ramachandranc2c0bea2014-11-11 16:09:21 -08001290 return true;
1291 }
1292 }
1293 return false;
1294 }
1295
Robin Lee17e61832016-05-09 13:46:28 +01001296 /**
Robin Leeebbcb542016-05-24 16:35:10 +01001297 * @return {@code true} if {@param uid} is blocked by an always-on VPN.
1298 * A UID is blocked if it's included in one of the mBlockedUsers ranges and the VPN is
1299 * not connected, or if the VPN is connected but does not apply to the UID.
Robin Lee17e61832016-05-09 13:46:28 +01001300 *
1301 * @see #mBlockedUsers
1302 */
1303 public synchronized boolean isBlockingUid(int uid) {
Robin Leeebbcb542016-05-24 16:35:10 +01001304 if (!mLockdown) {
1305 return false;
Robin Lee17e61832016-05-09 13:46:28 +01001306 }
Robin Leeebbcb542016-05-24 16:35:10 +01001307
1308 if (mNetworkInfo.isConnected()) {
1309 return !appliesToUid(uid);
1310 } else {
1311 for (UidRange uidRange : mBlockedUsers) {
1312 if (uidRange.contains(uid)) {
1313 return true;
1314 }
1315 }
1316 return false;
1317 }
Robin Lee17e61832016-05-09 13:46:28 +01001318 }
1319
Tony Makde7f7d12016-06-30 11:19:20 +01001320 private void updateAlwaysOnNotification(DetailedState networkState) {
1321 final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
Tony Makde7f7d12016-06-30 11:19:20 +01001322
Tony Makde7f7d12016-06-30 11:19:20 +01001323 final UserHandle user = UserHandle.of(mUserHandle);
1324 final long token = Binder.clearCallingIdentity();
1325 try {
1326 final NotificationManager notificationManager = NotificationManager.from(mContext);
1327 if (!visible) {
1328 notificationManager.cancelAsUser(TAG, 0, user);
1329 return;
1330 }
1331 final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
Robin Leeb8c2a2b2017-03-10 16:17:06 +00001332 final PendingIntent configIntent = mSystemServices.pendingIntentGetActivityAsUser(
1333 intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, user);
Tony Makde7f7d12016-06-30 11:19:20 +01001334 final Notification.Builder builder = new Notification.Builder(mContext)
1335 .setDefaults(0)
1336 .setSmallIcon(R.drawable.vpn_connected)
1337 .setContentTitle(mContext.getString(R.string.vpn_lockdown_disconnected))
1338 .setContentText(mContext.getString(R.string.vpn_lockdown_config))
1339 .setContentIntent(configIntent)
1340 .setCategory(Notification.CATEGORY_SYSTEM)
1341 .setPriority(Notification.PRIORITY_LOW)
1342 .setVisibility(Notification.VISIBILITY_PUBLIC)
1343 .setOngoing(true)
1344 .setColor(mContext.getColor(R.color.system_notification_accent_color));
1345 notificationManager.notifyAsUser(TAG, 0, builder.build(), user);
1346 } finally {
1347 Binder.restoreCallingIdentity(token);
1348 }
1349 }
1350
Robin Leeb8c2a2b2017-03-10 16:17:06 +00001351 /**
1352 * Facade for system service calls that change, or depend on, state outside of
1353 * {@link ConnectivityService} and have hard-to-mock interfaces.
1354 *
1355 * @see com.android.server.connectivity.VpnTest
1356 */
1357 @VisibleForTesting
1358 public static class SystemServices {
1359 private final Context mContext;
1360
1361 public SystemServices(@NonNull Context context) {
1362 mContext = context;
1363 }
1364
1365 /**
1366 * @see PendingIntent#getActivityAsUser()
1367 */
1368 public PendingIntent pendingIntentGetActivityAsUser(
1369 Intent intent, int flags, UserHandle user) {
1370 return PendingIntent.getActivityAsUser(mContext, 0 /*request*/, intent, flags,
1371 null /*options*/, user);
1372 }
1373
1374 /**
1375 * @see Settings.Secure#putStringForUser
1376 */
1377 public void settingsSecurePutStringForUser(String key, String value, int userId) {
1378 Settings.Secure.putStringForUser(mContext.getContentResolver(), key, value, userId);
1379 }
1380
1381 /**
1382 * @see Settings.Secure#putIntForUser
1383 */
1384 public void settingsSecurePutIntForUser(String key, int value, int userId) {
1385 Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userId);
1386 }
1387
1388 /**
1389 * @see Settings.Secure#getStringForUser
1390 */
1391 public String settingsSecureGetStringForUser(String key, int userId) {
1392 return Settings.Secure.getStringForUser(mContext.getContentResolver(), key, userId);
1393 }
1394
1395 /**
1396 * @see Settings.Secure#getIntForUser
1397 */
1398 public int settingsSecureGetIntForUser(String key, int def, int userId) {
1399 return Settings.Secure.getIntForUser(mContext.getContentResolver(), key, def, userId);
1400 }
1401 }
1402
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001403 private native int jniCreate(int mtu);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -07001404 private native String jniGetName(int tun);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001405 private native int jniSetAddresses(String interfaze, String addresses);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -07001406 private native void jniReset(String interfaze);
1407 private native int jniCheck(String interfaze);
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -07001408 private native boolean jniAddAddress(String interfaze, String address, int prefixLen);
1409 private native boolean jniDelAddress(String interfaze, String address, int prefixLen);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001410
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +09001411 private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
1412 for (RouteInfo route : prop.getAllRoutes()) {
Jeff Sharkey82f85212012-08-24 11:17:25 -07001413 // Currently legacy VPN only works on IPv4.
1414 if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +09001415 return route;
Jeff Sharkey82f85212012-08-24 11:17:25 -07001416 }
1417 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001418
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +09001419 throw new IllegalStateException("Unable to find IPv4 default gateway");
Jeff Sharkey82f85212012-08-24 11:17:25 -07001420 }
1421
1422 /**
1423 * Start legacy VPN, controlling native daemons as needed. Creates a
1424 * secondary thread to perform connection work, returning quickly.
Jeff Davidsonb21298a2015-02-10 10:02:11 -08001425 *
1426 * Should only be called to respond to Binder requests as this enforces caller permission. Use
1427 * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
1428 * permission check only when the caller is trusted (or the call is initiated by the system).
Jeff Sharkey82f85212012-08-24 11:17:25 -07001429 */
1430 public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
Robert Greenwalt5a6bdc42013-02-15 10:56:35 -08001431 enforceControlPermission();
Jeff Davidsonb21298a2015-02-10 10:02:11 -08001432 long token = Binder.clearCallingIdentity();
1433 try {
1434 startLegacyVpnPrivileged(profile, keyStore, egress);
1435 } finally {
1436 Binder.restoreCallingIdentity(token);
1437 }
1438 }
1439
1440 /**
1441 * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
1442 * permissions under the assumption that the caller is the system.
1443 *
1444 * Callers are responsible for checking permissions if needed.
1445 */
1446 public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
1447 LinkProperties egress) {
Julia Reynoldsf5116d02014-07-01 11:10:41 -04001448 UserManager mgr = UserManager.get(mContext);
Paul Jensen0784eea2014-08-19 16:00:24 -04001449 UserInfo user = mgr.getUserInfo(mUserHandle);
Nicolas Prevot95778ff2015-01-06 15:43:05 +00001450 if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
1451 new UserHandle(mUserHandle))) {
Julia Reynoldsf5116d02014-07-01 11:10:41 -04001452 throw new SecurityException("Restricted users cannot establish VPNs");
1453 }
Jeff Sharkey82f85212012-08-24 11:17:25 -07001454
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +09001455 final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
1456 final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
1457 final String iface = ipv4DefaultRoute.getInterface();
Jeff Sharkey82f85212012-08-24 11:17:25 -07001458
1459 // Load certificates.
1460 String privateKey = "";
1461 String userCert = "";
1462 String caCert = "";
1463 String serverCert = "";
1464 if (!profile.ipsecUserCert.isEmpty()) {
1465 privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
1466 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
Elliott Hughesd396a442013-06-28 16:24:48 -07001467 userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -07001468 }
1469 if (!profile.ipsecCaCert.isEmpty()) {
1470 byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
Elliott Hughesd396a442013-06-28 16:24:48 -07001471 caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -07001472 }
1473 if (!profile.ipsecServerCert.isEmpty()) {
1474 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
Elliott Hughesd396a442013-06-28 16:24:48 -07001475 serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -07001476 }
1477 if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
1478 throw new IllegalStateException("Cannot load credentials");
1479 }
1480
1481 // Prepare arguments for racoon.
1482 String[] racoon = null;
1483 switch (profile.type) {
1484 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
1485 racoon = new String[] {
1486 iface, profile.server, "udppsk", profile.ipsecIdentifier,
1487 profile.ipsecSecret, "1701",
1488 };
1489 break;
1490 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
1491 racoon = new String[] {
1492 iface, profile.server, "udprsa", privateKey, userCert,
1493 caCert, serverCert, "1701",
1494 };
1495 break;
1496 case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
1497 racoon = new String[] {
1498 iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
1499 profile.ipsecSecret, profile.username, profile.password, "", gateway,
1500 };
1501 break;
1502 case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
1503 racoon = new String[] {
1504 iface, profile.server, "xauthrsa", privateKey, userCert,
1505 caCert, serverCert, profile.username, profile.password, "", gateway,
1506 };
1507 break;
1508 case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
1509 racoon = new String[] {
1510 iface, profile.server, "hybridrsa",
1511 caCert, serverCert, profile.username, profile.password, "", gateway,
1512 };
1513 break;
1514 }
1515
1516 // Prepare arguments for mtpd.
1517 String[] mtpd = null;
1518 switch (profile.type) {
1519 case VpnProfile.TYPE_PPTP:
1520 mtpd = new String[] {
1521 iface, "pptp", profile.server, "1723",
1522 "name", profile.username, "password", profile.password,
1523 "linkname", "vpn", "refuse-eap", "nodefaultroute",
1524 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
1525 (profile.mppe ? "+mppe" : "nomppe"),
1526 };
1527 break;
1528 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
1529 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
1530 mtpd = new String[] {
1531 iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
1532 "name", profile.username, "password", profile.password,
1533 "linkname", "vpn", "refuse-eap", "nodefaultroute",
1534 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
1535 };
1536 break;
1537 }
1538
1539 VpnConfig config = new VpnConfig();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001540 config.legacy = true;
Jeff Sharkey82f85212012-08-24 11:17:25 -07001541 config.user = profile.key;
1542 config.interfaze = iface;
1543 config.session = profile.name;
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001544
1545 config.addLegacyRoutes(profile.routes);
Jeff Sharkey82f85212012-08-24 11:17:25 -07001546 if (!profile.dnsServers.isEmpty()) {
1547 config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
1548 }
1549 if (!profile.searchDomains.isEmpty()) {
1550 config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
1551 }
Jeff Sharkey82f85212012-08-24 11:17:25 -07001552 startLegacyVpn(config, racoon, mtpd);
1553 }
1554
1555 private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
Jeff Davidsonb21298a2015-02-10 10:02:11 -08001556 stopLegacyVpnPrivileged();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001557
Jeff Davidsonb21298a2015-02-10 10:02:11 -08001558 // Prepare for the new request.
1559 prepareInternal(VpnConfig.LEGACY_VPN);
Jeff Sharkey899223b2012-08-04 15:24:58 -07001560 updateState(DetailedState.CONNECTING, "startLegacyVpn");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001561
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001562 // Start a new LegacyVpnRunner and we are done!
Chia-chi Yeh100155a2011-07-03 16:52:38 -07001563 mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
1564 mLegacyVpnRunner.start();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001565 }
1566
Jeff Davidsonb21298a2015-02-10 10:02:11 -08001567 /** Stop legacy VPN. Permissions must be checked by callers. */
1568 public synchronized void stopLegacyVpnPrivileged() {
Jeff Sharkey899223b2012-08-04 15:24:58 -07001569 if (mLegacyVpnRunner != null) {
1570 mLegacyVpnRunner.exit();
1571 mLegacyVpnRunner = null;
1572
1573 synchronized (LegacyVpnRunner.TAG) {
1574 // wait for old thread to completely finish before spinning up
1575 // new instance, otherwise state updates can be out of order.
1576 }
1577 }
1578 }
1579
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001580 /**
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001581 * Return the information of the current ongoing legacy VPN.
1582 */
1583 public synchronized LegacyVpnInfo getLegacyVpnInfo() {
Chia-chi Yehdadc8572012-06-08 13:05:58 -07001584 // Check if the caller is authorized.
1585 enforceControlPermission();
sj.cha08bbca02015-03-23 11:35:24 +09001586 return getLegacyVpnInfoPrivileged();
1587 }
1588
1589 /**
1590 * Return the information of the current ongoing legacy VPN.
1591 * Callers are responsible for checking permissions if needed.
1592 */
1593 public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
Jeff Sharkey899223b2012-08-04 15:24:58 -07001594 if (mLegacyVpnRunner == null) return null;
1595
1596 final LegacyVpnInfo info = new LegacyVpnInfo();
Chad Brubakerc2865192013-07-10 14:46:23 -07001597 info.key = mConfig.user;
Jeff Sharkey899223b2012-08-04 15:24:58 -07001598 info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
Jeff Davidson90b1b9f2014-08-22 13:05:43 -07001599 if (mNetworkInfo.isConnected()) {
1600 info.intent = mStatusIntent;
1601 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001602 return info;
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001603 }
1604
Jeff Sharkey69ddab42012-08-25 00:05:46 -07001605 public VpnConfig getLegacyVpnConfig() {
1606 if (mLegacyVpnRunner != null) {
Chad Brubakerc2865192013-07-10 14:46:23 -07001607 return mConfig;
Jeff Sharkey69ddab42012-08-25 00:05:46 -07001608 } else {
1609 return null;
1610 }
1611 }
1612
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001613 /**
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001614 * Bringing up a VPN connection takes time, and that is all this thread
1615 * does. Here we have plenty of time. The only thing we need to take
1616 * care of is responding to interruptions as soon as possible. Otherwise
1617 * requests will be piled up. This can be done in a Handler as a state
1618 * machine, but it is much easier to read in the current form.
1619 */
1620 private class LegacyVpnRunner extends Thread {
1621 private static final String TAG = "LegacyVpnRunner";
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001622
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001623 private final String[] mDaemons;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001624 private final String[][] mArguments;
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001625 private final LocalSocket[] mSockets;
Robert Greenwalt53c04bd2012-10-12 17:02:45 -07001626 private final String mOuterInterface;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001627 private final AtomicInteger mOuterConnection =
1628 new AtomicInteger(ConnectivityManager.TYPE_NONE);
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001629
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001630 private long mTimer = -1;
1631
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001632 /**
1633 * Watch for the outer connection (passing in the constructor) going away.
1634 */
1635 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1636 @Override
1637 public void onReceive(Context context, Intent intent) {
Jeff Sharkey57666932013-04-30 17:01:57 -07001638 if (!mEnableTeardown) return;
1639
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001640 if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
1641 if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
1642 ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
1643 NetworkInfo info = (NetworkInfo)intent.getExtra(
1644 ConnectivityManager.EXTRA_NETWORK_INFO);
1645 if (info != null && !info.isConnectedOrConnecting()) {
1646 try {
1647 mObserver.interfaceStatusChanged(mOuterInterface, false);
1648 } catch (RemoteException e) {}
1649 }
1650 }
1651 }
1652 }
1653 };
1654
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001655 public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001656 super(TAG);
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001657 mConfig = config;
1658 mDaemons = new String[] {"racoon", "mtpd"};
Jeff Sharkey899223b2012-08-04 15:24:58 -07001659 // TODO: clear arguments from memory once launched
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001660 mArguments = new String[][] {racoon, mtpd};
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001661 mSockets = new LocalSocket[mDaemons.length];
Robert Greenwalt53c04bd2012-10-12 17:02:45 -07001662
1663 // This is the interface which VPN is running on,
1664 // mConfig.interfaze will change to point to OUR
1665 // internal interface soon. TODO - add inner/outer to mconfig
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001666 // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001667 // we will leave the VPN up. We should check that it's still there/connected after
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001668 // registering
Robert Greenwalt53c04bd2012-10-12 17:02:45 -07001669 mOuterInterface = mConfig.interfaze;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001670
Paul Jensene75b9e32015-04-06 11:54:53 -04001671 if (!TextUtils.isEmpty(mOuterInterface)) {
1672 final ConnectivityManager cm = ConnectivityManager.from(mContext);
1673 for (Network network : cm.getAllNetworks()) {
1674 final LinkProperties lp = cm.getLinkProperties(network);
Lorenzo Colitti1b60d112015-07-02 13:03:03 +09001675 if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) {
Paul Jensene75b9e32015-04-06 11:54:53 -04001676 final NetworkInfo networkInfo = cm.getNetworkInfo(network);
1677 if (networkInfo != null) mOuterConnection.set(networkInfo.getType());
1678 }
1679 }
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001680 }
1681
1682 IntentFilter filter = new IntentFilter();
1683 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
1684 mContext.registerReceiver(mBroadcastReceiver, filter);
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001685 }
1686
Chia-chi Yehaa1727f2011-07-14 18:55:33 -07001687 public void check(String interfaze) {
Robert Greenwalt53c04bd2012-10-12 17:02:45 -07001688 if (interfaze.equals(mOuterInterface)) {
Chia-chi Yehaa1727f2011-07-14 18:55:33 -07001689 Log.i(TAG, "Legacy VPN is going down with " + interfaze);
1690 exit();
1691 }
1692 }
1693
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001694 public void exit() {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001695 // We assume that everything is reset after stopping the daemons.
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001696 interrupt();
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001697 agentDisconnect();
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -07001698 try {
1699 mContext.unregisterReceiver(mBroadcastReceiver);
1700 } catch (IllegalArgumentException e) {}
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001701 }
1702
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001703 @Override
1704 public void run() {
1705 // Wait for the previous thread since it has been interrupted.
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001706 Log.v(TAG, "Waiting");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001707 synchronized (TAG) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001708 Log.v(TAG, "Executing");
Hisanobu Watanabe047454c2016-06-07 19:55:41 +09001709 try {
1710 execute();
1711 monitorDaemons();
1712 interrupted(); // Clear interrupt flag if execute called exit.
1713 } catch (InterruptedException e) {
1714 } finally {
1715 for (LocalSocket socket : mSockets) {
1716 IoUtils.closeQuietly(socket);
1717 }
1718 // This sleep is necessary for racoon to successfully complete sending delete
1719 // message to server.
1720 try {
1721 Thread.sleep(50);
1722 } catch (InterruptedException e) {
1723 }
1724 for (String daemon : mDaemons) {
1725 SystemService.stop(daemon);
1726 }
1727 }
1728 agentDisconnect();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001729 }
1730 }
1731
1732 private void checkpoint(boolean yield) throws InterruptedException {
1733 long now = SystemClock.elapsedRealtime();
1734 if (mTimer == -1) {
1735 mTimer = now;
1736 Thread.sleep(1);
Chia-chi Yeh7ef86112011-07-22 15:46:52 -07001737 } else if (now - mTimer <= 60000) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001738 Thread.sleep(yield ? 200 : 1);
1739 } else {
Jeff Sharkey899223b2012-08-04 15:24:58 -07001740 updateState(DetailedState.FAILED, "checkpoint");
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001741 throw new IllegalStateException("Time is up");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001742 }
1743 }
1744
1745 private void execute() {
1746 // Catch all exceptions so we can clean up few things.
Jeff Sharkey899223b2012-08-04 15:24:58 -07001747 boolean initFinished = false;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001748 try {
1749 // Initialize the timer.
1750 checkpoint(false);
1751
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001752 // Wait for the daemons to stop.
1753 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001754 while (!SystemService.isStopped(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001755 checkpoint(true);
1756 }
1757 }
1758
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001759 // Clear the previous state.
1760 File state = new File("/data/misc/vpn/state");
1761 state.delete();
1762 if (state.exists()) {
1763 throw new IllegalStateException("Cannot delete the state");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001764 }
Chia-chi Yehc1872732011-12-08 16:51:41 -08001765 new File("/data/misc/vpn/abort").delete();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001766 initFinished = true;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001767
Chia-chi Yehe9107902011-07-02 01:48:50 -07001768 // Check if we need to restart any of the daemons.
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001769 boolean restart = false;
1770 for (String[] arguments : mArguments) {
1771 restart = restart || (arguments != null);
1772 }
1773 if (!restart) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001774 agentDisconnect();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001775 return;
1776 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001777 updateState(DetailedState.CONNECTING, "execute");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001778
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001779 // Start the daemon with arguments.
1780 for (int i = 0; i < mDaemons.length; ++i) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001781 String[] arguments = mArguments[i];
1782 if (arguments == null) {
1783 continue;
1784 }
1785
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001786 // Start the daemon.
1787 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001788 SystemService.start(daemon);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001789
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001790 // Wait for the daemon to start.
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001791 while (!SystemService.isRunning(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001792 checkpoint(true);
1793 }
1794
1795 // Create the control socket.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001796 mSockets[i] = new LocalSocket();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001797 LocalSocketAddress address = new LocalSocketAddress(
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001798 daemon, LocalSocketAddress.Namespace.RESERVED);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001799
1800 // Wait for the socket to connect.
1801 while (true) {
1802 try {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001803 mSockets[i].connect(address);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001804 break;
1805 } catch (Exception e) {
1806 // ignore
1807 }
1808 checkpoint(true);
1809 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001810 mSockets[i].setSoTimeout(500);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001811
1812 // Send over the arguments.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001813 OutputStream out = mSockets[i].getOutputStream();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001814 for (String argument : arguments) {
Elliott Hughesd396a442013-06-28 16:24:48 -07001815 byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001816 if (bytes.length >= 0xFFFF) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001817 throw new IllegalArgumentException("Argument is too large");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001818 }
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001819 out.write(bytes.length >> 8);
1820 out.write(bytes.length);
1821 out.write(bytes);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001822 checkpoint(false);
1823 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001824 out.write(0xFF);
1825 out.write(0xFF);
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001826 out.flush();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001827
1828 // Wait for End-of-File.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001829 InputStream in = mSockets[i].getInputStream();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001830 while (true) {
1831 try {
1832 if (in.read() == -1) {
1833 break;
1834 }
1835 } catch (Exception e) {
1836 // ignore
1837 }
1838 checkpoint(true);
1839 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001840 }
1841
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001842 // Wait for the daemons to create the new state.
1843 while (!state.exists()) {
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001844 // Check if a running daemon is dead.
1845 for (int i = 0; i < mDaemons.length; ++i) {
1846 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001847 if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001848 throw new IllegalStateException(daemon + " is dead");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001849 }
1850 }
1851 checkpoint(true);
1852 }
1853
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001854 // Now we are connected. Read and parse the new state.
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -08001855 String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
Lorenzo Colitti50262792014-09-19 01:53:35 +09001856 if (parameters.length != 7) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001857 throw new IllegalStateException("Cannot parse the state");
1858 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001859
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001860 // Set the interface and the addresses in the config.
1861 mConfig.interfaze = parameters[0].trim();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001862
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001863 mConfig.addLegacyAddresses(parameters[1]);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001864 // Set the routes if they are not set in the config.
1865 if (mConfig.routes == null || mConfig.routes.isEmpty()) {
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001866 mConfig.addLegacyRoutes(parameters[2]);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001867 }
1868
1869 // Set the DNS servers if they are not set in the config.
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001870 if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001871 String dnsServers = parameters[3].trim();
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001872 if (!dnsServers.isEmpty()) {
1873 mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
1874 }
1875 }
1876
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001877 // Set the search domains if they are not set in the config.
1878 if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
1879 String searchDomains = parameters[4].trim();
1880 if (!searchDomains.isEmpty()) {
1881 mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
1882 }
1883 }
Chia-chi Yehe9107902011-07-02 01:48:50 -07001884
Lorenzo Colitti50262792014-09-19 01:53:35 +09001885 // Add a throw route for the VPN server endpoint, if one was specified.
1886 String endpoint = parameters[5];
1887 if (!endpoint.isEmpty()) {
1888 try {
1889 InetAddress addr = InetAddress.parseNumericAddress(endpoint);
1890 if (addr instanceof Inet4Address) {
1891 mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW));
1892 } else if (addr instanceof Inet6Address) {
1893 mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW));
1894 } else {
1895 Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint);
1896 }
1897 } catch (IllegalArgumentException e) {
1898 Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e);
1899 }
1900 }
1901
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001902 // Here is the last step and it must be done synchronously.
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001903 synchronized (Vpn.this) {
Vinit Deshapnde2b862e52013-10-02 11:50:39 -07001904 // Set the start time
1905 mConfig.startTime = SystemClock.elapsedRealtime();
1906
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001907 // Check if the thread is interrupted while we are waiting.
1908 checkpoint(false);
1909
Chia-chi Yehe9107902011-07-02 01:48:50 -07001910 // Check if the interface is gone while we are waiting.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -07001911 if (jniCheck(mConfig.interfaze) == 0) {
Chia-chi Yeh34e78132011-07-03 03:07:07 -07001912 throw new IllegalStateException(mConfig.interfaze + " is gone");
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001913 }
Chia-chi Yehe9107902011-07-02 01:48:50 -07001914
1915 // Now INetworkManagementEventObserver is watching our back.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -07001916 mInterface = mConfig.interfaze;
Robin Lee4d03abc2016-05-09 12:32:27 +01001917 prepareStatusIntent();
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001918
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001919 agentConnect();
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001920
1921 Log.i(TAG, "Connected!");
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001922 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001923 } catch (Exception e) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001924 Log.i(TAG, "Aborting", e);
Lorenzo Colitti43840602014-08-20 16:01:44 -07001925 updateState(DetailedState.FAILED, e.getMessage());
Chia-chi Yehe9107902011-07-02 01:48:50 -07001926 exit();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001927 }
1928 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001929
1930 /**
1931 * Monitor the daemons we started, moving to disconnected state if the
1932 * underlying services fail.
1933 */
Hisanobu Watanabe047454c2016-06-07 19:55:41 +09001934 private void monitorDaemons() throws InterruptedException{
Jeff Sharkey899223b2012-08-04 15:24:58 -07001935 if (!mNetworkInfo.isConnected()) {
1936 return;
1937 }
Hisanobu Watanabe047454c2016-06-07 19:55:41 +09001938 while (true) {
1939 Thread.sleep(2000);
1940 for (int i = 0; i < mDaemons.length; i++) {
1941 if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1942 return;
Jeff Sharkey899223b2012-08-04 15:24:58 -07001943 }
1944 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001945 }
1946 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001947 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -07001948}