• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.vpn2;
18 
19 import android.content.Context;
20 import android.net.IConnectivityManager;
21 import android.os.Bundle;
22 import android.os.Environment;
23 import android.os.RemoteException;
24 import android.os.ServiceManager;
25 import android.os.UserHandle;
26 import android.security.Credentials;
27 import android.security.KeyStore;
28 import android.security.NetworkSecurityPolicy;
29 import android.test.InstrumentationTestCase;
30 import android.test.InstrumentationTestRunner;
31 import android.test.suitebuilder.annotation.LargeTest;
32 import android.util.Log;
33 
34 import com.android.internal.net.LegacyVpnInfo;
35 import com.android.internal.net.VpnConfig;
36 import com.android.internal.net.VpnProfile;
37 
38 import java.net.HttpURLConnection;
39 import java.net.URL;
40 import junit.framework.Assert;
41 
42 import libcore.io.Streams;
43 import org.json.JSONException;
44 import org.json.JSONObject;
45 
46 import java.io.File;
47 import java.io.FileInputStream;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.net.UnknownHostException;
51 import java.nio.charset.StandardCharsets;
52 import java.util.List;
53 import java.util.Map;
54 
55 /**
56  * Legacy VPN connection tests
57  *
58  * To run the test, use command:
59  * adb shell am instrument -e class com.android.settings.vpn2.VpnTests -e profile foo.xml
60  * -w com.android.settings.tests/android.test.InstrumentationTestRunner
61  *
62  * VPN profiles are saved in an xml file and will be loaded through {@link VpnProfileParser}.
63  * Push the profile (foo.xml) to the external storage, e.g adb push foo.xml /sdcard/ before running
64  * the above command.
65  *
66  * A typical profile looks like the following:
67  * <vpn>
68  *   <name></name>
69  *   <type></type>
70  *   <server></server>
71  *   <username></username>
72  *   <password></password>
73  *   <dnsServers></dnsServers>
74  *   <searchDomains></searchDomains>
75  *   <routes></routes>
76  *   <l2tpSecret></l2tpSecret>
77  *   <ipsecIdentifier></ipsecIdentifier>
78  *   <ipsecSecret></ipsecSecret>
79  *   <ipsecUserCert></ipsecUserCert>
80  *   <ipsecCaCert></ipsecCaCert>
81  *   <ipsecServerCert></ipsecServerCert>
82  * </vpn>
83  * VPN types include: TYPE_PPTP, TYPE_L2TP_IPSEC_PSK, TYPE_L2TP_IPSEC_RSA,
84  * TYPE_IPSEC_XAUTH_PSK, TYPE_IPSEC_XAUTH_RSA, TYPE_IPSEC_HYBRID_RSA
85  */
86 public class VpnTests extends InstrumentationTestCase {
87     private static final String TAG = "VpnTests";
88     /* Maximum time to wait for VPN connection */
89     private static final long MAX_CONNECTION_TIME = 5 * 60 * 1000;
90     private static final long VPN_STAY_TIME = 60 * 1000;
91     private static final int MAX_DISCONNECTION_TRIES = 3;
92     private static final String EXTERNAL_SERVER =
93             "http://ip2country.sourceforge.net/ip2c.php?format=JSON";
94     private static final String VPN_INTERFACE = "ppp0";
95     private final IConnectivityManager mService = IConnectivityManager.Stub
96         .asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
97     private Map<Integer, VpnInfo> mVpnInfoPool = null;
98     private Context mContext;
99     private CertInstallerHelper mCertHelper = null;
100     private KeyStore mKeyStore = KeyStore.getInstance();
101     private String mPreviousIpAddress = null;
102     private boolean DEBUG = false;
103 
104     @Override
setUp()105     protected void setUp() throws Exception {
106         super.setUp();
107         InputStream in = null;
108         InstrumentationTestRunner mRunner = (InstrumentationTestRunner)getInstrumentation();
109         mContext = mRunner.getContext();
110         Bundle arguments = mRunner.getArguments();
111         String PROFILE_NAME = arguments.getString("profile");
112         Assert.assertNotNull("Push profile to external storage and load with"
113                 + "'-e profile <filename>'", PROFILE_NAME);
114         File profileFile = new File(Environment.getExternalStorageDirectory(), PROFILE_NAME);
115         in = new FileInputStream(profileFile);
116         mVpnInfoPool = VpnProfileParser.parse(in);
117         Assert.assertNotNull("no VPN profiles are parsed", mVpnInfoPool);
118         if (DEBUG) {
119             Log.v(TAG, "print out the vpn profiles");
120             for (Map.Entry<Integer, VpnInfo> profileEntrySet: mVpnInfoPool.entrySet()) {
121                 VpnInfo vpnInfo = profileEntrySet.getValue();
122                 printVpnProfile(vpnInfo.getVpnProfile());
123                 if (vpnInfo.getCertificateFile() != null) {
124                     Log.d(TAG, "certificate file for this vpn is " + vpnInfo.getCertificateFile());
125                 }
126                 if (vpnInfo.getPassword() != null) {
127                     Log.d(TAG, "password for the certificate file is: " + vpnInfo.getPassword());
128                 }
129             }
130         }
131         // disconnect existing vpn if there is any
132         LegacyVpnInfo oldVpn = mService.getLegacyVpnInfo(UserHandle.myUserId());
133         if (oldVpn != null) {
134             Log.v(TAG, "disconnect legacy VPN");
135             disconnect();
136             // wait till the legacy VPN is disconnected.
137             int tries = 0;
138             while (tries < MAX_DISCONNECTION_TRIES &&
139                     mService.getLegacyVpnInfo(UserHandle.myUserId()) != null) {
140                 tries++;
141                 Thread.sleep(10 * 1000);
142                 Log.v(TAG, "Wait for legacy VPN to be disconnected.");
143             }
144             Assert.assertNull("Failed to disconect VPN",
145                     mService.getLegacyVpnInfo(UserHandle.myUserId()));
146             // wait for 30 seconds after the previous VPN is disconnected.
147             sleep(30 * 1000);
148         }
149         // Create CertInstallerHelper to initialize the keystore
150         mCertHelper = new CertInstallerHelper();
151     }
152 
153     @Override
tearDown()154     protected void tearDown() throws Exception {
155         sleep(VPN_STAY_TIME);
156         super.tearDown();
157     }
158 
printVpnProfile(VpnProfile profile)159     private void printVpnProfile(VpnProfile profile) {
160         Log.v(TAG, "profile: ");
161         Log.v(TAG, "key: " + profile.key);
162         Log.v(TAG, "name: " + profile.name);
163         Log.v(TAG, "type: " + profile.type);
164         Log.v(TAG, "server: " + profile.server);
165         Log.v(TAG, "username: " + profile.username);
166         Log.v(TAG, "password: " + profile.password);
167         Log.v(TAG, "dnsServers: " + profile.dnsServers);
168         Log.v(TAG, "searchDomains: " + profile.searchDomains);
169         Log.v(TAG, "routes: " + profile.routes);
170         Log.v(TAG, "mppe: " + profile.mppe);
171         Log.v(TAG, "l2tpSecret: " + profile.l2tpSecret);
172         Log.v(TAG, "ipsecIdentifier: " + profile.ipsecIdentifier);
173         Log.v(TAG, "ipsecSecret: " + profile.ipsecSecret);
174         Log.v(TAG, "ipsecUserCert: " + profile.ipsecUserCert);
175         Log.v(TAG, "ipsecCaCert: " + profile.ipsecCaCert);
176         Log.v(TAG, "ipsecServerCert: " + profile.ipsecServerCert);
177     }
178 
printKeyStore(VpnProfile profile)179     private void printKeyStore(VpnProfile profile) {
180         // print out the information from keystore
181         String privateKey = "";
182         String userCert = "";
183         String caCert = "";
184         String serverCert = "";
185         if (!profile.ipsecUserCert.isEmpty()) {
186             privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
187             byte[] value = mKeyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
188             userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
189         }
190         if (!profile.ipsecCaCert.isEmpty()) {
191             byte[] value = mKeyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
192             caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
193         }
194         if (!profile.ipsecServerCert.isEmpty()) {
195             byte[] value = mKeyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
196             serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
197         }
198         Log.v(TAG, "privateKey: \n" + ((privateKey == null) ? "" : privateKey));
199         Log.v(TAG, "userCert: \n" + ((userCert == null) ? "" : userCert));
200         Log.v(TAG, "caCert: \n" + ((caCert == null) ? "" : caCert));
201         Log.v(TAG, "serverCert: \n" + ((serverCert == null) ? "" : serverCert));
202     }
203 
204     /**
205      * Connect legacy VPN
206      */
connect(VpnProfile profile)207     private void connect(VpnProfile profile) throws Exception {
208         try {
209             mService.startLegacyVpn(profile);
210         } catch (IllegalStateException e) {
211             fail(String.format("start legacy vpn: %s failed: %s", profile.name, e.toString()));
212         }
213     }
214 
215     /**
216      * Disconnect legacy VPN
217      */
disconnect()218     private void disconnect() throws Exception {
219         try {
220             mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, UserHandle.myUserId());
221         } catch (RemoteException e) {
222             Log.e(TAG, String.format("disconnect VPN exception: %s", e.toString()));
223         }
224     }
225 
226     /**
227      * Get external IP address
228      */
getIpAddress()229     private String getIpAddress() {
230         String ip = null;
231         HttpURLConnection urlConnection = null;
232         // TODO: Rewrite this test to use an HTTPS URL.
233         // Because this test uses cleartext HTTP, the network security policy of this app needs to
234         // be temporarily relaxed to permit such traffic.
235         NetworkSecurityPolicy networkSecurityPolicy = NetworkSecurityPolicy.getInstance();
236         boolean cleartextTrafficPermittedBeforeTest =
237                 networkSecurityPolicy.isCleartextTrafficPermitted();
238         networkSecurityPolicy.setCleartextTrafficPermitted(true);
239         try {
240             URL url = new URL(EXTERNAL_SERVER);
241             urlConnection = (HttpURLConnection) url.openConnection();
242             Log.i(TAG, "Response from httpget: " + urlConnection.getResponseCode());
243 
244             InputStream is = urlConnection.getInputStream();
245             String response;
246             try {
247                 response = new String(Streams.readFully(is), StandardCharsets.UTF_8);
248             } finally {
249                 is.close();
250             }
251 
252             JSONObject json_data = new JSONObject(response);
253             ip = json_data.getString("ip");
254             Log.v(TAG, "json_data: " + ip);
255         } catch (IllegalArgumentException e) {
256             Log.e(TAG, "exception while getting external IP: " + e.toString());
257         } catch (IOException e) {
258             Log.e(TAG, "IOException while getting IP: " + e.toString());
259         } catch (JSONException e) {
260             Log.e(TAG, "exception while creating JSONObject: " + e.toString());
261         } finally {
262             networkSecurityPolicy.setCleartextTrafficPermitted(cleartextTrafficPermittedBeforeTest);
263             if (urlConnection != null) {
264                 urlConnection.disconnect();
265             }
266         }
267         return ip;
268     }
269 
270     /**
271      * Verify the vpn connection by checking the VPN state and external IP
272      */
validateVpnConnection(VpnProfile profile)273     private void validateVpnConnection(VpnProfile profile) throws Exception {
274         validateVpnConnection(profile, false);
275     }
276 
277     /**
278      * Verify the vpn connection by checking the VPN state, external IP or ping test
279      */
validateVpnConnection(VpnProfile profile, boolean pingTestFlag)280     private void validateVpnConnection(VpnProfile profile, boolean pingTestFlag) throws Exception {
281         LegacyVpnInfo legacyVpnInfo = mService.getLegacyVpnInfo(UserHandle.myUserId());
282         Assert.assertTrue(legacyVpnInfo != null);
283 
284         long start = System.currentTimeMillis();
285         while (((System.currentTimeMillis() - start)  < MAX_CONNECTION_TIME) &&
286                 (legacyVpnInfo.state != LegacyVpnInfo.STATE_CONNECTED)) {
287             Log.v(TAG, "vpn state: " + legacyVpnInfo.state);
288             sleep(10 * 1000);
289             legacyVpnInfo = mService.getLegacyVpnInfo(UserHandle.myUserId());
290         }
291 
292         // the vpn state should be CONNECTED
293         Assert.assertTrue(legacyVpnInfo.state == LegacyVpnInfo.STATE_CONNECTED);
294         if (pingTestFlag) {
295             Assert.assertTrue(pingTest(profile.server));
296         } else {
297             String curIpAddress = getIpAddress();
298             // the outgoing IP address should be the same as the VPN server address
299             Assert.assertEquals(profile.server, curIpAddress);
300         }
301     }
302 
pingTest(String server)303     private boolean pingTest(String server) {
304         final long PING_TIMER = 3 * 60 * 1000; // 3 minutes
305         if (server == null || server.isEmpty()) {
306             return false;
307         }
308         long startTime = System.currentTimeMillis();
309         while ((System.currentTimeMillis() - startTime) < PING_TIMER) {
310             try {
311                 Log.v(TAG, "Start ping test, ping " + server);
312                 Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + server);
313                 int status = p.waitFor();
314                 if (status == 0) {
315                     // if any of the ping test is successful, return true
316                     return true;
317                 }
318             } catch (UnknownHostException e) {
319                 Log.e(TAG, "Ping test Fail: Unknown Host");
320             } catch (IOException e) {
321                 Log.e(TAG, "Ping test Fail:  IOException");
322             } catch (InterruptedException e) {
323                 Log.e(TAG, "Ping test Fail: InterruptedException");
324             }
325         }
326         // ping test timeout
327         return false;
328     }
329 
330     /**
331      * Install certificates from a file loaded in external stroage on the device
332      * @param profile vpn profile
333      * @param fileName certificate file name
334      * @param password password to extract certificate file
335      */
installCertificatesFromFile(VpnProfile profile, String fileName, String password)336     private void installCertificatesFromFile(VpnProfile profile, String fileName, String password)
337             throws Exception {
338         if (profile == null || fileName == null || password == null) {
339             throw new Exception ("vpn profile, certificate file name and password can not be null");
340         }
341 
342         int curUid = mContext.getUserId();
343         mCertHelper.installCertificate(profile, fileName, password);
344 
345         if (DEBUG) {
346             printKeyStore(profile);
347         }
348     }
349 
sleep(long time)350     private void sleep(long time) {
351         try {
352             Thread.sleep(time);
353         } catch (InterruptedException e) {
354             Log.e(TAG, "interrupted: " + e.toString());
355         }
356     }
357 
358     /**
359      * Test PPTP VPN connection
360      */
361     @LargeTest
testPPTPConnection()362     public void testPPTPConnection() throws Exception {
363         mPreviousIpAddress = getIpAddress();
364         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_PPTP);
365         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
366         connect(vpnProfile);
367         validateVpnConnection(vpnProfile);
368     }
369 
370     /**
371      * Test L2TP/IPSec PSK VPN connection
372      */
373     @LargeTest
testL2tpIpsecPskConnection()374     public void testL2tpIpsecPskConnection() throws Exception {
375         mPreviousIpAddress = getIpAddress();
376         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_L2TP_IPSEC_PSK);
377         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
378         connect(vpnProfile);
379         validateVpnConnection(vpnProfile);
380     }
381 
382     /**
383      * Test L2TP/IPSec RSA VPN connection
384      */
385     @LargeTest
testL2tpIpsecRsaConnection()386     public void testL2tpIpsecRsaConnection() throws Exception {
387         mPreviousIpAddress = getIpAddress();
388         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_L2TP_IPSEC_RSA);
389         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
390         if (DEBUG) {
391             printVpnProfile(vpnProfile);
392         }
393         String certFile = curVpnInfo.getCertificateFile();
394         String password = curVpnInfo.getPassword();
395         installCertificatesFromFile(vpnProfile, certFile, password);
396         connect(vpnProfile);
397         validateVpnConnection(vpnProfile);
398     }
399 
400     /**
401      * Test IPSec Xauth RSA VPN connection
402      */
403     @LargeTest
testIpsecXauthRsaConnection()404     public void testIpsecXauthRsaConnection() throws Exception {
405         mPreviousIpAddress = getIpAddress();
406         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_IPSEC_XAUTH_RSA);
407         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
408         if (DEBUG) {
409             printVpnProfile(vpnProfile);
410         }
411         String certFile = curVpnInfo.getCertificateFile();
412         String password = curVpnInfo.getPassword();
413         installCertificatesFromFile(vpnProfile, certFile, password);
414         connect(vpnProfile);
415         validateVpnConnection(vpnProfile);
416     }
417 
418     /**
419      * Test IPSec Xauth PSK VPN connection
420      */
421     @LargeTest
testIpsecXauthPskConnection()422     public void testIpsecXauthPskConnection() throws Exception {
423         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_IPSEC_XAUTH_PSK);
424         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
425         if (DEBUG) {
426             printVpnProfile(vpnProfile);
427         }
428         connect(vpnProfile);
429         validateVpnConnection(vpnProfile, true);
430     }
431 
432     /**
433      * Test IPSec Hybrid RSA VPN connection
434      */
435     @LargeTest
testIpsecHybridRsaConnection()436     public void testIpsecHybridRsaConnection() throws Exception {
437         mPreviousIpAddress = getIpAddress();
438         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_IPSEC_HYBRID_RSA);
439         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
440         if (DEBUG) {
441             printVpnProfile(vpnProfile);
442         }
443         connect(vpnProfile);
444         validateVpnConnection(vpnProfile);
445     }
446 }
447