Vpn.java revision c023453a2b79b338aea36b48fd610a099379d34c
1/*
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
19import static android.Manifest.permission.BIND_VPN_SERVICE;
20
21import android.app.AppGlobals;
22import android.app.Notification;
23import android.app.NotificationManager;
24import android.app.PendingIntent;
25import android.content.BroadcastReceiver;
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.ServiceConnection;
31import android.content.pm.ApplicationInfo;
32import android.content.pm.PackageManager;
33import android.content.pm.ResolveInfo;
34import android.content.pm.UserInfo;
35import android.graphics.Bitmap;
36import android.graphics.Canvas;
37import android.graphics.drawable.Drawable;
38import android.net.BaseNetworkStateTracker;
39import android.net.ConnectivityManager;
40import android.net.IConnectivityManager;
41import android.net.INetworkManagementEventObserver;
42import android.net.LinkAddress;
43import android.net.LinkProperties;
44import android.net.LocalSocket;
45import android.net.LocalSocketAddress;
46import android.net.NetworkInfo;
47import android.net.NetworkUtils;
48import android.net.RouteInfo;
49import android.net.NetworkInfo.DetailedState;
50import android.os.Binder;
51import android.os.FileUtils;
52import android.os.IBinder;
53import android.os.INetworkManagementService;
54import android.os.Parcel;
55import android.os.ParcelFileDescriptor;
56import android.os.Process;
57import android.os.RemoteException;
58import android.os.SystemClock;
59import android.os.SystemService;
60import android.os.UserHandle;
61import android.os.UserManager;
62import android.security.Credentials;
63import android.security.KeyStore;
64import android.util.Log;
65import android.util.SparseBooleanArray;
66
67import com.android.internal.annotations.GuardedBy;
68import com.android.internal.R;
69import com.android.internal.net.LegacyVpnInfo;
70import com.android.internal.net.VpnConfig;
71import com.android.internal.net.VpnProfile;
72import com.android.server.ConnectivityService.VpnCallback;
73import com.android.server.net.BaseNetworkObserver;
74
75import java.io.File;
76import java.io.InputStream;
77import java.io.OutputStream;
78import java.net.InetAddress;
79import java.net.Inet4Address;
80import java.nio.charset.StandardCharsets;
81import java.util.Arrays;
82import java.util.concurrent.atomic.AtomicInteger;
83
84import libcore.io.IoUtils;
85
86/**
87 * @hide
88 */
89public class Vpn extends BaseNetworkStateTracker {
90    private static final String TAG = "Vpn";
91    private static final boolean LOGD = true;
92
93    // TODO: create separate trackers for each unique VPN to support
94    // automated reconnection
95
96    private final VpnCallback mCallback;
97
98    private String mPackage = VpnConfig.LEGACY_VPN;
99    private String mInterface;
100    private Connection mConnection;
101    private LegacyVpnRunner mLegacyVpnRunner;
102    private PendingIntent mStatusIntent;
103    private volatile boolean mEnableNotif = true;
104    private volatile boolean mEnableTeardown = true;
105    private final IConnectivityManager mConnService;
106    private VpnConfig mConfig;
107
108    /* list of users using this VPN. */
109    @GuardedBy("this")
110    private SparseBooleanArray mVpnUsers = null;
111    private BroadcastReceiver mUserIntentReceiver = null;
112
113    private final int mUserId;
114
115    public Vpn(Context context, VpnCallback callback, INetworkManagementService netService,
116            IConnectivityManager connService, int userId) {
117        // TODO: create dedicated TYPE_VPN network type
118        super(ConnectivityManager.TYPE_DUMMY);
119        mContext = context;
120        mCallback = callback;
121        mConnService = connService;
122        mUserId = userId;
123
124        try {
125            netService.registerObserver(mObserver);
126        } catch (RemoteException e) {
127            Log.wtf(TAG, "Problem registering observer", e);
128        }
129        if (userId == UserHandle.USER_OWNER) {
130            // Owner's VPN also needs to handle restricted users
131            mUserIntentReceiver = new BroadcastReceiver() {
132                @Override
133                public void onReceive(Context context, Intent intent) {
134                    final String action = intent.getAction();
135                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
136                            UserHandle.USER_NULL);
137                    if (userId == UserHandle.USER_NULL) return;
138
139                    if (Intent.ACTION_USER_ADDED.equals(action)) {
140                        onUserAdded(userId);
141                    } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
142                        onUserRemoved(userId);
143                    }
144                }
145            };
146
147            IntentFilter intentFilter = new IntentFilter();
148            intentFilter.addAction(Intent.ACTION_USER_ADDED);
149            intentFilter.addAction(Intent.ACTION_USER_REMOVED);
150            mContext.registerReceiverAsUser(
151                    mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
152        }
153    }
154
155    /**
156     * Set if this object is responsible for showing its own notifications. When
157     * {@code false}, notifications are handled externally by someone else.
158     */
159    public void setEnableNotifications(boolean enableNotif) {
160        mEnableNotif = enableNotif;
161    }
162
163    /**
164     * Set if this object is responsible for watching for {@link NetworkInfo}
165     * teardown. When {@code false}, teardown is handled externally by someone
166     * else.
167     */
168    public void setEnableTeardown(boolean enableTeardown) {
169        mEnableTeardown = enableTeardown;
170    }
171
172    @Override
173    protected void startMonitoringInternal() {
174        // Ignored; events are sent through callbacks for now
175    }
176
177    @Override
178    public boolean teardown() {
179        // TODO: finish migration to unique tracker for each VPN
180        throw new UnsupportedOperationException();
181    }
182
183    @Override
184    public boolean reconnect() {
185        // TODO: finish migration to unique tracker for each VPN
186        throw new UnsupportedOperationException();
187    }
188
189    @Override
190    public String getTcpBufferSizesPropName() {
191        return PROP_TCP_BUFFER_UNKNOWN;
192    }
193
194    /**
195     * Update current state, dispaching event to listeners.
196     */
197    private void updateState(DetailedState detailedState, String reason) {
198        if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
199        mNetworkInfo.setDetailedState(detailedState, reason, null);
200        mCallback.onStateChanged(new NetworkInfo(mNetworkInfo));
201    }
202
203    /**
204     * Prepare for a VPN application. This method is designed to solve
205     * race conditions. It first compares the current prepared package
206     * with {@code oldPackage}. If they are the same, the prepared
207     * package is revoked and replaced with {@code newPackage}. If
208     * {@code oldPackage} is {@code null}, the comparison is omitted.
209     * If {@code newPackage} is the same package or {@code null}, the
210     * revocation is omitted. This method returns {@code true} if the
211     * operation is succeeded.
212     *
213     * Legacy VPN is handled specially since it is not a real package.
214     * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
215     * it can be revoked by itself.
216     *
217     * @param oldPackage The package name of the old VPN application.
218     * @param newPackage The package name of the new VPN application.
219     * @return true if the operation is succeeded.
220     */
221    public synchronized boolean prepare(String oldPackage, String newPackage) {
222        // Return false if the package does not match.
223        if (oldPackage != null && !oldPackage.equals(mPackage)) {
224            return false;
225        }
226
227        // Return true if we do not need to revoke.
228        if (newPackage == null ||
229                (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
230            return true;
231        }
232
233        // Check if the caller is authorized.
234        enforceControlPermission();
235
236        // Reset the interface and hide the notification.
237        if (mInterface != null) {
238            final long token = Binder.clearCallingIdentity();
239            try {
240                mCallback.restore();
241                final int size = mVpnUsers.size();
242                final boolean forwardDns = (mConfig.dnsServers != null &&
243                        mConfig.dnsServers.size() != 0);
244                for (int i = 0; i < size; i++) {
245                    int user = mVpnUsers.keyAt(i);
246                    mCallback.clearUserForwarding(mInterface, user, forwardDns);
247                    hideNotification(user);
248                }
249
250                mCallback.clearMarkedForwarding(mInterface);
251            } finally {
252                Binder.restoreCallingIdentity(token);
253            }
254            jniReset(mInterface);
255            mInterface = null;
256            mVpnUsers = null;
257        }
258
259        // Revoke the connection or stop LegacyVpnRunner.
260        if (mConnection != null) {
261            try {
262                mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
263                        Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
264            } catch (Exception e) {
265                // ignore
266            }
267            mContext.unbindService(mConnection);
268            mConnection = null;
269        } else if (mLegacyVpnRunner != null) {
270            mLegacyVpnRunner.exit();
271            mLegacyVpnRunner = null;
272        }
273
274        Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
275        mPackage = newPackage;
276        mConfig = null;
277        updateState(DetailedState.IDLE, "prepare");
278        return true;
279    }
280
281    /**
282     * Protect a socket from routing changes by binding it to the given
283     * interface. The socket is NOT closed by this method.
284     *
285     * @param socket The socket to be bound.
286     * @param interfaze The name of the interface.
287     */
288    public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
289
290        PackageManager pm = mContext.getPackageManager();
291        int appUid = pm.getPackageUid(mPackage, mUserId);
292        if (Binder.getCallingUid() != appUid) {
293            throw new SecurityException("Unauthorized Caller");
294        }
295        // protect the socket from routing rules
296        final long token = Binder.clearCallingIdentity();
297        try {
298            mCallback.protect(socket);
299        } finally {
300            Binder.restoreCallingIdentity(token);
301        }
302        // bind the socket to the interface
303        jniProtect(socket.getFd(), interfaze);
304
305    }
306
307    /**
308     * Establish a VPN network and return the file descriptor of the VPN
309     * interface. This methods returns {@code null} if the application is
310     * revoked or not prepared.
311     *
312     * @param config The parameters to configure the network.
313     * @return The file descriptor of the VPN interface.
314     */
315    public synchronized ParcelFileDescriptor establish(VpnConfig config) {
316        // Check if the caller is already prepared.
317        UserManager mgr = UserManager.get(mContext);
318        PackageManager pm = mContext.getPackageManager();
319        ApplicationInfo app = null;
320        try {
321            app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
322            if (Binder.getCallingUid() != app.uid) {
323                return null;
324            }
325        } catch (Exception e) {
326            return null;
327        }
328        // Check if the service is properly declared.
329        Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
330        intent.setClassName(mPackage, config.user);
331        long token = Binder.clearCallingIdentity();
332        try {
333            // Restricted users are not allowed to create VPNs, they are tied to Owner
334            UserInfo user = mgr.getUserInfo(mUserId);
335            if (user.isRestricted()) {
336                throw new SecurityException("Restricted users cannot establish VPNs");
337            }
338
339            ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
340                                                                        null, 0, mUserId);
341            if (info == null) {
342                throw new SecurityException("Cannot find " + config.user);
343            }
344            if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
345                throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
346            }
347        } catch (RemoteException e) {
348                throw new SecurityException("Cannot find " + config.user);
349        } finally {
350            Binder.restoreCallingIdentity(token);
351        }
352
353        // Configure the interface. Abort if any of these steps fails.
354        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
355        try {
356            updateState(DetailedState.CONNECTING, "establish");
357            String interfaze = jniGetName(tun.getFd());
358
359            // TEMP use the old jni calls until there is support for netd address setting
360            StringBuilder builder = new StringBuilder();
361            for (LinkAddress address : config.addresses) {
362                builder.append(" " + address);
363            }
364            if (jniSetAddresses(interfaze, builder.toString()) < 1) {
365                throw new IllegalArgumentException("At least one address must be specified");
366            }
367            Connection connection = new Connection();
368            if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
369                        new UserHandle(mUserId))) {
370                throw new IllegalStateException("Cannot bind " + config.user);
371            }
372            if (mConnection != null) {
373                mContext.unbindService(mConnection);
374            }
375            if (mInterface != null && !mInterface.equals(interfaze)) {
376                jniReset(mInterface);
377            }
378            mConnection = connection;
379            mInterface = interfaze;
380
381            // Fill more values.
382            config.user = mPackage;
383            config.interfaze = mInterface;
384            config.startTime = SystemClock.elapsedRealtime();
385            mConfig = config;
386            // Set up forwarding and DNS rules.
387            mVpnUsers = new SparseBooleanArray();
388            token = Binder.clearCallingIdentity();
389            try {
390                mCallback.setMarkedForwarding(mInterface);
391                mCallback.setRoutes(interfaze, config.routes);
392                mCallback.override(mInterface, config.dnsServers, config.searchDomains);
393                addVpnUserLocked(mUserId);
394
395            } finally {
396                Binder.restoreCallingIdentity(token);
397            }
398
399        } catch (RuntimeException e) {
400            updateState(DetailedState.FAILED, "establish");
401            IoUtils.closeQuietly(tun);
402            // make sure marked forwarding is cleared if it was set
403            try {
404                mCallback.clearMarkedForwarding(mInterface);
405            } catch (Exception ingored) {
406                // ignored
407            }
408            throw e;
409        }
410        Log.i(TAG, "Established by " + config.user + " on " + mInterface);
411
412
413        // If we are owner assign all Restricted Users to this VPN
414        if (mUserId == UserHandle.USER_OWNER) {
415            token = Binder.clearCallingIdentity();
416            try {
417                for (UserInfo user : mgr.getUsers()) {
418                    if (user.isRestricted()) {
419                        try {
420                            addVpnUserLocked(user.id);
421                        } catch (Exception e) {
422                            Log.wtf(TAG, "Failed to add user " + user.id + " to owner's VPN");
423                        }
424                    }
425                }
426            } finally {
427                Binder.restoreCallingIdentity(token);
428            }
429        }
430        // TODO: ensure that contract class eventually marks as connected
431        updateState(DetailedState.AUTHENTICATING, "establish");
432        return tun;
433    }
434
435    /**
436     * Check if a given address is covered by the VPN's routing rules.
437     */
438    public boolean isAddressCovered(InetAddress address) {
439        synchronized (Vpn.this) {
440            if (!isRunningLocked()) {
441                return false;
442            }
443            return RouteInfo.selectBestRoute(mConfig.routes, address) != null;
444        }
445    }
446
447    private boolean isRunningLocked() {
448        return mVpnUsers != null;
449    }
450
451    private void addVpnUserLocked(int user) {
452        enforceControlPermission();
453
454        if (!isRunningLocked()) {
455            throw new IllegalStateException("VPN is not active");
456        }
457
458        final boolean forwardDns = (mConfig.dnsServers != null &&
459                mConfig.dnsServers.size() != 0);
460
461        // add the user
462        mCallback.addUserForwarding(mInterface, user, forwardDns);
463        mVpnUsers.put(user, true);
464
465        // show the notification
466        if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
467            // Load everything for the user's notification
468            PackageManager pm = mContext.getPackageManager();
469            ApplicationInfo app = null;
470            try {
471                app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
472            } catch (RemoteException e) {
473                throw new IllegalStateException("Invalid application");
474            }
475            String label = app.loadLabel(pm).toString();
476            // Load the icon and convert it into a bitmap.
477            Drawable icon = app.loadIcon(pm);
478            Bitmap bitmap = null;
479            if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
480                int width = mContext.getResources().getDimensionPixelSize(
481                        android.R.dimen.notification_large_icon_width);
482                int height = mContext.getResources().getDimensionPixelSize(
483                        android.R.dimen.notification_large_icon_height);
484                icon.setBounds(0, 0, width, height);
485                bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
486                Canvas c = new Canvas(bitmap);
487                icon.draw(c);
488                c.setBitmap(null);
489            }
490            showNotification(label, bitmap, user);
491        } else {
492            showNotification(null, null, user);
493        }
494    }
495
496    private void removeVpnUserLocked(int user) {
497            enforceControlPermission();
498
499            if (!isRunningLocked()) {
500                throw new IllegalStateException("VPN is not active");
501            }
502            final boolean forwardDns = (mConfig.dnsServers != null &&
503                    mConfig.dnsServers.size() != 0);
504            mCallback.clearUserForwarding(mInterface, user, forwardDns);
505            mVpnUsers.delete(user);
506            hideNotification(user);
507    }
508
509    private void onUserAdded(int userId) {
510        // If the user is restricted tie them to the owner's VPN
511        synchronized(Vpn.this) {
512            UserManager mgr = UserManager.get(mContext);
513            UserInfo user = mgr.getUserInfo(userId);
514            if (user.isRestricted()) {
515                try {
516                    addVpnUserLocked(userId);
517                } catch (Exception e) {
518                    Log.wtf(TAG, "Failed to add restricted user to owner", e);
519                }
520            }
521        }
522    }
523
524    private void onUserRemoved(int userId) {
525        // clean up if restricted
526        synchronized(Vpn.this) {
527            UserManager mgr = UserManager.get(mContext);
528            UserInfo user = mgr.getUserInfo(userId);
529            if (user.isRestricted()) {
530                try {
531                    removeVpnUserLocked(userId);
532                } catch (Exception e) {
533                    Log.wtf(TAG, "Failed to remove restricted user to owner", e);
534                }
535            }
536        }
537    }
538
539    /**
540     * Return the configuration of the currently running VPN.
541     */
542    public VpnConfig getVpnConfig() {
543        enforceControlPermission();
544        return mConfig;
545    }
546
547    @Deprecated
548    public synchronized void interfaceStatusChanged(String iface, boolean up) {
549        try {
550            mObserver.interfaceStatusChanged(iface, up);
551        } catch (RemoteException e) {
552            // ignored; target is local
553        }
554    }
555
556    private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
557        @Override
558        public void interfaceStatusChanged(String interfaze, boolean up) {
559            synchronized (Vpn.this) {
560                if (!up && mLegacyVpnRunner != null) {
561                    mLegacyVpnRunner.check(interfaze);
562                }
563            }
564        }
565
566        @Override
567        public void interfaceRemoved(String interfaze) {
568            synchronized (Vpn.this) {
569                if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
570                    final long token = Binder.clearCallingIdentity();
571                    try {
572                        final int size = mVpnUsers.size();
573                        final boolean forwardDns = (mConfig.dnsServers != null &&
574                                mConfig.dnsServers.size() != 0);
575                        for (int i = 0; i < size; i++) {
576                            int user = mVpnUsers.keyAt(i);
577                            mCallback.clearUserForwarding(mInterface, user, forwardDns);
578                            hideNotification(user);
579                        }
580                        mVpnUsers = null;
581                        mCallback.clearMarkedForwarding(mInterface);
582
583                        mCallback.restore();
584                    } finally {
585                        Binder.restoreCallingIdentity(token);
586                    }
587                    mInterface = null;
588                    if (mConnection != null) {
589                        mContext.unbindService(mConnection);
590                        mConnection = null;
591                        updateState(DetailedState.DISCONNECTED, "interfaceRemoved");
592                    } else if (mLegacyVpnRunner != null) {
593                        mLegacyVpnRunner.exit();
594                        mLegacyVpnRunner = null;
595                    }
596                }
597            }
598        }
599    };
600
601    private void enforceControlPermission() {
602        // System user is allowed to control VPN.
603        if (Binder.getCallingUid() == Process.SYSTEM_UID) {
604            return;
605        }
606        int appId = UserHandle.getAppId(Binder.getCallingUid());
607        final long token = Binder.clearCallingIdentity();
608        try {
609            // System VPN dialogs are also allowed to control VPN.
610            PackageManager pm = mContext.getPackageManager();
611            ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0);
612            if (((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) && (appId == app.uid)) {
613                return;
614            }
615        } catch (Exception e) {
616            // ignore
617        } finally {
618            Binder.restoreCallingIdentity(token);
619        }
620
621        throw new SecurityException("Unauthorized Caller");
622    }
623
624    private class Connection implements ServiceConnection {
625        private IBinder mService;
626
627        @Override
628        public void onServiceConnected(ComponentName name, IBinder service) {
629            mService = service;
630        }
631
632        @Override
633        public void onServiceDisconnected(ComponentName name) {
634            mService = null;
635        }
636    }
637
638    private void showNotification(String label, Bitmap icon, int user) {
639        if (!mEnableNotif) return;
640        mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
641
642        NotificationManager nm = (NotificationManager)
643                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
644
645        if (nm != null) {
646            String title = (label == null) ? mContext.getString(R.string.vpn_title) :
647                    mContext.getString(R.string.vpn_title_long, label);
648            String text = (mConfig.session == null) ? mContext.getString(R.string.vpn_text) :
649                    mContext.getString(R.string.vpn_text_long, mConfig.session);
650
651            Notification notification = new Notification.Builder(mContext)
652                    .setSmallIcon(R.drawable.vpn_connected)
653                    .setLargeIcon(icon)
654                    .setContentTitle(title)
655                    .setContentText(text)
656                    .setContentIntent(mStatusIntent)
657                    .setDefaults(0)
658                    .setOngoing(true)
659                    .build();
660            nm.notifyAsUser(null, R.drawable.vpn_connected, notification, new UserHandle(user));
661        }
662    }
663
664    private void hideNotification(int user) {
665        if (!mEnableNotif) return;
666        mStatusIntent = null;
667
668        NotificationManager nm = (NotificationManager)
669                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
670
671        if (nm != null) {
672            nm.cancelAsUser(null, R.drawable.vpn_connected, new UserHandle(user));
673        }
674    }
675
676    private native int jniCreate(int mtu);
677    private native String jniGetName(int tun);
678    private native int jniSetAddresses(String interfaze, String addresses);
679    private native int jniSetRoutes(String interfaze, String routes);
680    private native void jniReset(String interfaze);
681    private native int jniCheck(String interfaze);
682    private native void jniProtect(int socket, String interfaze);
683
684    private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
685        for (RouteInfo route : prop.getAllRoutes()) {
686            // Currently legacy VPN only works on IPv4.
687            if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
688                return route;
689            }
690        }
691
692        throw new IllegalStateException("Unable to find IPv4 default gateway");
693    }
694
695    /**
696     * Start legacy VPN, controlling native daemons as needed. Creates a
697     * secondary thread to perform connection work, returning quickly.
698     */
699    public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
700        enforceControlPermission();
701        if (!keyStore.isUnlocked()) {
702            throw new IllegalStateException("KeyStore isn't unlocked");
703        }
704
705        final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
706        final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
707        final String iface = ipv4DefaultRoute.getInterface();
708
709        // Load certificates.
710        String privateKey = "";
711        String userCert = "";
712        String caCert = "";
713        String serverCert = "";
714        if (!profile.ipsecUserCert.isEmpty()) {
715            privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
716            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
717            userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
718        }
719        if (!profile.ipsecCaCert.isEmpty()) {
720            byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
721            caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
722        }
723        if (!profile.ipsecServerCert.isEmpty()) {
724            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
725            serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
726        }
727        if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
728            throw new IllegalStateException("Cannot load credentials");
729        }
730
731        // Prepare arguments for racoon.
732        String[] racoon = null;
733        switch (profile.type) {
734            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
735                racoon = new String[] {
736                    iface, profile.server, "udppsk", profile.ipsecIdentifier,
737                    profile.ipsecSecret, "1701",
738                };
739                break;
740            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
741                racoon = new String[] {
742                    iface, profile.server, "udprsa", privateKey, userCert,
743                    caCert, serverCert, "1701",
744                };
745                break;
746            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
747                racoon = new String[] {
748                    iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
749                    profile.ipsecSecret, profile.username, profile.password, "", gateway,
750                };
751                break;
752            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
753                racoon = new String[] {
754                    iface, profile.server, "xauthrsa", privateKey, userCert,
755                    caCert, serverCert, profile.username, profile.password, "", gateway,
756                };
757                break;
758            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
759                racoon = new String[] {
760                    iface, profile.server, "hybridrsa",
761                    caCert, serverCert, profile.username, profile.password, "", gateway,
762                };
763                break;
764        }
765
766        // Prepare arguments for mtpd.
767        String[] mtpd = null;
768        switch (profile.type) {
769            case VpnProfile.TYPE_PPTP:
770                mtpd = new String[] {
771                    iface, "pptp", profile.server, "1723",
772                    "name", profile.username, "password", profile.password,
773                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
774                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
775                    (profile.mppe ? "+mppe" : "nomppe"),
776                };
777                break;
778            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
779            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
780                mtpd = new String[] {
781                    iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
782                    "name", profile.username, "password", profile.password,
783                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
784                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
785                };
786                break;
787        }
788
789        VpnConfig config = new VpnConfig();
790        config.legacy = true;
791        config.user = profile.key;
792        config.interfaze = iface;
793        config.session = profile.name;
794
795        config.addLegacyRoutes(profile.routes);
796        if (!profile.dnsServers.isEmpty()) {
797            config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
798        }
799        if (!profile.searchDomains.isEmpty()) {
800            config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
801        }
802        startLegacyVpn(config, racoon, mtpd);
803    }
804
805    private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
806        stopLegacyVpn();
807
808        // Prepare for the new request. This also checks the caller.
809        prepare(null, VpnConfig.LEGACY_VPN);
810        updateState(DetailedState.CONNECTING, "startLegacyVpn");
811
812        // Start a new LegacyVpnRunner and we are done!
813        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
814        mLegacyVpnRunner.start();
815    }
816
817    public synchronized void stopLegacyVpn() {
818        if (mLegacyVpnRunner != null) {
819            mLegacyVpnRunner.exit();
820            mLegacyVpnRunner = null;
821
822            synchronized (LegacyVpnRunner.TAG) {
823                // wait for old thread to completely finish before spinning up
824                // new instance, otherwise state updates can be out of order.
825            }
826        }
827    }
828
829    /**
830     * Return the information of the current ongoing legacy VPN.
831     */
832    public synchronized LegacyVpnInfo getLegacyVpnInfo() {
833        // Check if the caller is authorized.
834        enforceControlPermission();
835        if (mLegacyVpnRunner == null) return null;
836
837        final LegacyVpnInfo info = new LegacyVpnInfo();
838        info.key = mConfig.user;
839        info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
840        if (mNetworkInfo.isConnected()) {
841            info.intent = mStatusIntent;
842        }
843        return info;
844    }
845
846    public VpnConfig getLegacyVpnConfig() {
847        if (mLegacyVpnRunner != null) {
848            return mConfig;
849        } else {
850            return null;
851        }
852    }
853
854    /**
855     * Bringing up a VPN connection takes time, and that is all this thread
856     * does. Here we have plenty of time. The only thing we need to take
857     * care of is responding to interruptions as soon as possible. Otherwise
858     * requests will be piled up. This can be done in a Handler as a state
859     * machine, but it is much easier to read in the current form.
860     */
861    private class LegacyVpnRunner extends Thread {
862        private static final String TAG = "LegacyVpnRunner";
863
864        private final String[] mDaemons;
865        private final String[][] mArguments;
866        private final LocalSocket[] mSockets;
867        private final String mOuterInterface;
868        private final AtomicInteger mOuterConnection =
869                new AtomicInteger(ConnectivityManager.TYPE_NONE);
870
871        private long mTimer = -1;
872
873        /**
874         * Watch for the outer connection (passing in the constructor) going away.
875         */
876        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
877            @Override
878            public void onReceive(Context context, Intent intent) {
879                if (!mEnableTeardown) return;
880
881                if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
882                    if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
883                            ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
884                        NetworkInfo info = (NetworkInfo)intent.getExtra(
885                                ConnectivityManager.EXTRA_NETWORK_INFO);
886                        if (info != null && !info.isConnectedOrConnecting()) {
887                            try {
888                                mObserver.interfaceStatusChanged(mOuterInterface, false);
889                            } catch (RemoteException e) {}
890                        }
891                    }
892                }
893            }
894        };
895
896        public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
897            super(TAG);
898            mConfig = config;
899            mDaemons = new String[] {"racoon", "mtpd"};
900            // TODO: clear arguments from memory once launched
901            mArguments = new String[][] {racoon, mtpd};
902            mSockets = new LocalSocket[mDaemons.length];
903
904            // This is the interface which VPN is running on,
905            // mConfig.interfaze will change to point to OUR
906            // internal interface soon. TODO - add inner/outer to mconfig
907            // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
908            // we will leave the VPN up.  We should check that it's still there/connected after
909            // registering
910            mOuterInterface = mConfig.interfaze;
911
912            try {
913                mOuterConnection.set(
914                        mConnService.findConnectionTypeForIface(mOuterInterface));
915            } catch (Exception e) {
916                mOuterConnection.set(ConnectivityManager.TYPE_NONE);
917            }
918
919            IntentFilter filter = new IntentFilter();
920            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
921            mContext.registerReceiver(mBroadcastReceiver, filter);
922        }
923
924        public void check(String interfaze) {
925            if (interfaze.equals(mOuterInterface)) {
926                Log.i(TAG, "Legacy VPN is going down with " + interfaze);
927                exit();
928            }
929        }
930
931        public void exit() {
932            // We assume that everything is reset after stopping the daemons.
933            interrupt();
934            for (LocalSocket socket : mSockets) {
935                IoUtils.closeQuietly(socket);
936            }
937            updateState(DetailedState.DISCONNECTED, "exit");
938            try {
939                mContext.unregisterReceiver(mBroadcastReceiver);
940            } catch (IllegalArgumentException e) {}
941        }
942
943        @Override
944        public void run() {
945            // Wait for the previous thread since it has been interrupted.
946            Log.v(TAG, "Waiting");
947            synchronized (TAG) {
948                Log.v(TAG, "Executing");
949                execute();
950                monitorDaemons();
951            }
952        }
953
954        private void checkpoint(boolean yield) throws InterruptedException {
955            long now = SystemClock.elapsedRealtime();
956            if (mTimer == -1) {
957                mTimer = now;
958                Thread.sleep(1);
959            } else if (now - mTimer <= 60000) {
960                Thread.sleep(yield ? 200 : 1);
961            } else {
962                updateState(DetailedState.FAILED, "checkpoint");
963                throw new IllegalStateException("Time is up");
964            }
965        }
966
967        private void execute() {
968            // Catch all exceptions so we can clean up few things.
969            boolean initFinished = false;
970            try {
971                // Initialize the timer.
972                checkpoint(false);
973
974                // Wait for the daemons to stop.
975                for (String daemon : mDaemons) {
976                    while (!SystemService.isStopped(daemon)) {
977                        checkpoint(true);
978                    }
979                }
980
981                // Clear the previous state.
982                File state = new File("/data/misc/vpn/state");
983                state.delete();
984                if (state.exists()) {
985                    throw new IllegalStateException("Cannot delete the state");
986                }
987                new File("/data/misc/vpn/abort").delete();
988                initFinished = true;
989
990                // Check if we need to restart any of the daemons.
991                boolean restart = false;
992                for (String[] arguments : mArguments) {
993                    restart = restart || (arguments != null);
994                }
995                if (!restart) {
996                    updateState(DetailedState.DISCONNECTED, "execute");
997                    return;
998                }
999                updateState(DetailedState.CONNECTING, "execute");
1000
1001                // Start the daemon with arguments.
1002                for (int i = 0; i < mDaemons.length; ++i) {
1003                    String[] arguments = mArguments[i];
1004                    if (arguments == null) {
1005                        continue;
1006                    }
1007
1008                    // Start the daemon.
1009                    String daemon = mDaemons[i];
1010                    SystemService.start(daemon);
1011
1012                    // Wait for the daemon to start.
1013                    while (!SystemService.isRunning(daemon)) {
1014                        checkpoint(true);
1015                    }
1016
1017                    // Create the control socket.
1018                    mSockets[i] = new LocalSocket();
1019                    LocalSocketAddress address = new LocalSocketAddress(
1020                            daemon, LocalSocketAddress.Namespace.RESERVED);
1021
1022                    // Wait for the socket to connect.
1023                    while (true) {
1024                        try {
1025                            mSockets[i].connect(address);
1026                            break;
1027                        } catch (Exception e) {
1028                            // ignore
1029                        }
1030                        checkpoint(true);
1031                    }
1032                    mSockets[i].setSoTimeout(500);
1033
1034                    // Send over the arguments.
1035                    OutputStream out = mSockets[i].getOutputStream();
1036                    for (String argument : arguments) {
1037                        byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
1038                        if (bytes.length >= 0xFFFF) {
1039                            throw new IllegalArgumentException("Argument is too large");
1040                        }
1041                        out.write(bytes.length >> 8);
1042                        out.write(bytes.length);
1043                        out.write(bytes);
1044                        checkpoint(false);
1045                    }
1046                    out.write(0xFF);
1047                    out.write(0xFF);
1048                    out.flush();
1049
1050                    // Wait for End-of-File.
1051                    InputStream in = mSockets[i].getInputStream();
1052                    while (true) {
1053                        try {
1054                            if (in.read() == -1) {
1055                                break;
1056                            }
1057                        } catch (Exception e) {
1058                            // ignore
1059                        }
1060                        checkpoint(true);
1061                    }
1062                }
1063
1064                // Wait for the daemons to create the new state.
1065                while (!state.exists()) {
1066                    // Check if a running daemon is dead.
1067                    for (int i = 0; i < mDaemons.length; ++i) {
1068                        String daemon = mDaemons[i];
1069                        if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
1070                            throw new IllegalStateException(daemon + " is dead");
1071                        }
1072                    }
1073                    checkpoint(true);
1074                }
1075
1076                // Now we are connected. Read and parse the new state.
1077                String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
1078                if (parameters.length != 6) {
1079                    throw new IllegalStateException("Cannot parse the state");
1080                }
1081
1082                // Set the interface and the addresses in the config.
1083                mConfig.interfaze = parameters[0].trim();
1084
1085                mConfig.addLegacyAddresses(parameters[1]);
1086                // Set the routes if they are not set in the config.
1087                if (mConfig.routes == null || mConfig.routes.isEmpty()) {
1088                    mConfig.addLegacyRoutes(parameters[2]);
1089                }
1090
1091                // Set the DNS servers if they are not set in the config.
1092                if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
1093                    String dnsServers = parameters[3].trim();
1094                    if (!dnsServers.isEmpty()) {
1095                        mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
1096                    }
1097                }
1098
1099                // Set the search domains if they are not set in the config.
1100                if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
1101                    String searchDomains = parameters[4].trim();
1102                    if (!searchDomains.isEmpty()) {
1103                        mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
1104                    }
1105                }
1106
1107                // Set the routes.
1108                long token = Binder.clearCallingIdentity();
1109                try {
1110                    mCallback.setMarkedForwarding(mConfig.interfaze);
1111                    mCallback.setRoutes(mConfig.interfaze, mConfig.routes);
1112                } finally {
1113                    Binder.restoreCallingIdentity(token);
1114                }
1115
1116                // Here is the last step and it must be done synchronously.
1117                synchronized (Vpn.this) {
1118                    // Set the start time
1119                    mConfig.startTime = SystemClock.elapsedRealtime();
1120
1121                    // Check if the thread is interrupted while we are waiting.
1122                    checkpoint(false);
1123
1124                    // Check if the interface is gone while we are waiting.
1125                    if (jniCheck(mConfig.interfaze) == 0) {
1126                        throw new IllegalStateException(mConfig.interfaze + " is gone");
1127                    }
1128
1129                    // Now INetworkManagementEventObserver is watching our back.
1130                    mInterface = mConfig.interfaze;
1131                    mVpnUsers = new SparseBooleanArray();
1132
1133                    token = Binder.clearCallingIdentity();
1134                    try {
1135                        mCallback.override(mInterface, mConfig.dnsServers, mConfig.searchDomains);
1136                        addVpnUserLocked(mUserId);
1137                    } finally {
1138                        Binder.restoreCallingIdentity(token);
1139                    }
1140
1141                    // Assign all restircted users to this VPN
1142                    // (Legacy VPNs are Owner only)
1143                    UserManager mgr = UserManager.get(mContext);
1144                    token = Binder.clearCallingIdentity();
1145                    try {
1146                        for (UserInfo user : mgr.getUsers()) {
1147                            if (user.isRestricted()) {
1148                                try {
1149                                    addVpnUserLocked(user.id);
1150                                } catch (Exception e) {
1151                                    Log.wtf(TAG, "Failed to add user " + user.id
1152                                            + " to owner's VPN");
1153                                }
1154                            }
1155                        }
1156                    } finally {
1157                        Binder.restoreCallingIdentity(token);
1158                    }
1159                    Log.i(TAG, "Connected!");
1160                    updateState(DetailedState.CONNECTED, "execute");
1161                }
1162            } catch (Exception e) {
1163                Log.i(TAG, "Aborting", e);
1164                // make sure the routing is cleared
1165                try {
1166                    mCallback.clearMarkedForwarding(mConfig.interfaze);
1167                } catch (Exception ignored) {
1168                }
1169                exit();
1170            } finally {
1171                // Kill the daemons if they fail to stop.
1172                if (!initFinished) {
1173                    for (String daemon : mDaemons) {
1174                        SystemService.stop(daemon);
1175                    }
1176                }
1177
1178                // Do not leave an unstable state.
1179                if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
1180                    updateState(DetailedState.FAILED, "execute");
1181                }
1182            }
1183        }
1184
1185        /**
1186         * Monitor the daemons we started, moving to disconnected state if the
1187         * underlying services fail.
1188         */
1189        private void monitorDaemons() {
1190            if (!mNetworkInfo.isConnected()) {
1191                return;
1192            }
1193
1194            try {
1195                while (true) {
1196                    Thread.sleep(2000);
1197                    for (int i = 0; i < mDaemons.length; i++) {
1198                        if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1199                            return;
1200                        }
1201                    }
1202                }
1203            } catch (InterruptedException e) {
1204                Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
1205            } finally {
1206                for (String daemon : mDaemons) {
1207                    SystemService.stop(daemon);
1208                }
1209
1210                updateState(DetailedState.DISCONNECTED, "babysit");
1211            }
1212        }
1213    }
1214}
1215