Home | History | Annotate | Download | only in vpn2
      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.net.LinkAddress;
     22 import android.net.RouteInfo;
     23 import android.os.Bundle;
     24 import android.os.Environment;
     25 import android.os.RemoteException;
     26 import android.os.ServiceManager;
     27 import android.security.Credentials;
     28 import android.security.KeyStore;
     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 junit.framework.Assert;
     39 
     40 import org.apache.http.HttpResponse;
     41 import org.apache.http.client.HttpClient;
     42 import org.apache.http.client.methods.HttpGet;
     43 import org.apache.http.impl.client.DefaultHttpClient;
     44 import org.apache.http.util.EntityUtils;
     45 import org.json.JSONException;
     46 import org.json.JSONObject;
     47 
     48 import java.io.File;
     49 import java.io.FileInputStream;
     50 import java.io.IOException;
     51 import java.io.InputStream;
     52 import java.net.UnknownHostException;
     53 import java.nio.charset.StandardCharsets;
     54 import java.util.List;
     55 import java.util.Map;
     56 
     57 /**
     58  * Legacy VPN connection tests
     59  *
     60  * To run the test, use command:
     61  * adb shell am instrument -e class com.android.settings.vpn2.VpnTests -e profile foo.xml
     62  * -w com.android.settings.tests/android.test.InstrumentationTestRunner
     63  *
     64  * VPN profiles are saved in an xml file and will be loaded through {@link VpnProfileParser}.
     65  * Push the profile (foo.xml) to the external storage, e.g adb push foo.xml /sdcard/ before running
     66  * the above command.
     67  *
     68  * A typical profile looks like the following:
     69  * <vpn>
     70  *   <name></name>
     71  *   <type></type>
     72  *   <server></server>
     73  *   <username></username>
     74  *   <password></password>
     75  *   <dnsServers></dnsServers>
     76  *   <searchDomains></searchDomains>
     77  *   <routes></routes>
     78  *   <l2tpSecret></l2tpSecret>
     79  *   <ipsecIdentifier></ipsecIdentifier>
     80  *   <ipsecSecret></ipsecSecret>
     81  *   <ipsecUserCert></ipsecUserCert>
     82  *   <ipsecCaCert></ipsecCaCert>
     83  *   <ipsecServerCert></ipsecServerCert>
     84  * </vpn>
     85  * VPN types include: TYPE_PPTP, TYPE_L2TP_IPSEC_PSK, TYPE_L2TP_IPSEC_RSA,
     86  * TYPE_IPSEC_XAUTH_PSK, TYPE_IPSEC_XAUTH_RSA, TYPE_IPSEC_HYBRID_RSA
     87  */
     88 public class VpnTests extends InstrumentationTestCase {
     89     private static final String TAG = "VpnTests";
     90     /* Maximum time to wait for VPN connection */
     91     private static final long MAX_CONNECTION_TIME = 5 * 60 * 1000;
     92     private static final long VPN_STAY_TIME = 60 * 1000;
     93     private static final int MAX_DISCONNECTION_TRIES = 3;
     94     private static final String EXTERNAL_SERVER =
     95             "http://ip2country.sourceforge.net/ip2c.php?format=JSON";
     96     private static final String VPN_INTERFACE = "ppp0";
     97     private final IConnectivityManager mService = IConnectivityManager.Stub
     98         .asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
     99     private Map<Integer, VpnInfo> mVpnInfoPool = null;
    100     private Context mContext;
    101     private CertInstallerHelper mCertHelper = null;
    102     private KeyStore mKeyStore = KeyStore.getInstance();
    103     private String mPreviousIpAddress = null;
    104     private boolean DEBUG = false;
    105 
    106     @Override
    107     protected void setUp() throws Exception {
    108         super.setUp();
    109         InputStream in = null;
    110         InstrumentationTestRunner mRunner = (InstrumentationTestRunner)getInstrumentation();
    111         mContext = mRunner.getContext();
    112         Bundle arguments = mRunner.getArguments();
    113         String PROFILE_NAME = arguments.getString("profile");
    114         Assert.assertNotNull("Push profile to external storage and load with"
    115                 + "'-e profile <filename>'", PROFILE_NAME);
    116         File profileFile = new File(Environment.getExternalStorageDirectory(), PROFILE_NAME);
    117         in = new FileInputStream(profileFile);
    118         mVpnInfoPool = VpnProfileParser.parse(in);
    119         Assert.assertNotNull("no VPN profiles are parsed", mVpnInfoPool);
    120         if (DEBUG) {
    121             Log.v(TAG, "print out the vpn profiles");
    122             for (Map.Entry<Integer, VpnInfo> profileEntrySet: mVpnInfoPool.entrySet()) {
    123                 VpnInfo vpnInfo = profileEntrySet.getValue();
    124                 printVpnProfile(vpnInfo.getVpnProfile());
    125                 if (vpnInfo.getCertificateFile() != null) {
    126                     Log.d(TAG, "certificate file for this vpn is " + vpnInfo.getCertificateFile());
    127                 }
    128                 if (vpnInfo.getPassword() != null) {
    129                     Log.d(TAG, "password for the certificate file is: " + vpnInfo.getPassword());
    130                 }
    131             }
    132         }
    133         // disconnect existing vpn if there is any
    134         LegacyVpnInfo oldVpn = mService.getLegacyVpnInfo();
    135         if (oldVpn != null) {
    136             Log.v(TAG, "disconnect legacy VPN");
    137             disconnect();
    138             // wait till the legacy VPN is disconnected.
    139             int tries = 0;
    140             while (tries < MAX_DISCONNECTION_TRIES && mService.getLegacyVpnInfo() != null) {
    141                 tries++;
    142                 Thread.sleep(10 * 1000);
    143                 Log.v(TAG, "Wait for legacy VPN to be disconnected.");
    144             }
    145             Assert.assertNull("Failed to disconect VPN", mService.getLegacyVpnInfo());
    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
    154     protected void tearDown() throws Exception {
    155         sleep(VPN_STAY_TIME);
    156         super.tearDown();
    157     }
    158 
    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 
    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      */
    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      */
    218     private void disconnect() throws Exception {
    219         try {
    220             mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
    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      */
    229     private String getIpAddress() {
    230         String ip = null;
    231         try {
    232             HttpClient httpClient = new DefaultHttpClient();
    233             HttpGet httpGet = new HttpGet(EXTERNAL_SERVER);
    234             HttpResponse httpResponse = httpClient.execute(httpGet);
    235             Log.i(TAG, "Response from httpget: " + httpResponse.getStatusLine().toString());
    236 
    237             String entityStr = EntityUtils.toString(httpResponse.getEntity());
    238             JSONObject json_data = new JSONObject(entityStr);
    239             ip = json_data.getString("ip");
    240             Log.v(TAG, "json_data: " + ip);
    241         } catch (IllegalArgumentException e) {
    242             Log.e(TAG, "exception while getting external IP: " + e.toString());
    243         } catch (IOException e) {
    244             Log.e(TAG, "IOException while getting IP: " + e.toString());
    245         } catch (JSONException e) {
    246             Log.e(TAG, "exception while creating JSONObject: " + e.toString());
    247         }
    248         return ip;
    249     }
    250 
    251     /**
    252      * Verify the vpn connection by checking the VPN state and external IP
    253      */
    254     private void validateVpnConnection(VpnProfile profile) throws Exception {
    255         validateVpnConnection(profile, false);
    256     }
    257 
    258     /**
    259      * Verify the vpn connection by checking the VPN state, external IP or ping test
    260      */
    261     private void validateVpnConnection(VpnProfile profile, boolean pingTestFlag) throws Exception {
    262         LegacyVpnInfo legacyVpnInfo = mService.getLegacyVpnInfo();
    263         Assert.assertTrue(legacyVpnInfo != null);
    264 
    265         long start = System.currentTimeMillis();
    266         while (((System.currentTimeMillis() - start)  < MAX_CONNECTION_TIME) &&
    267                 (legacyVpnInfo.state != LegacyVpnInfo.STATE_CONNECTED)) {
    268             Log.v(TAG, "vpn state: " + legacyVpnInfo.state);
    269             sleep(10 * 1000);
    270             legacyVpnInfo = mService.getLegacyVpnInfo();
    271         }
    272 
    273         // the vpn state should be CONNECTED
    274         Assert.assertTrue(legacyVpnInfo.state == LegacyVpnInfo.STATE_CONNECTED);
    275         if (pingTestFlag) {
    276             Assert.assertTrue(pingTest(profile.server));
    277         } else {
    278             String curIpAddress = getIpAddress();
    279             // the outgoing IP address should be the same as the VPN server address
    280             Assert.assertEquals(profile.server, curIpAddress);
    281         }
    282     }
    283 
    284     private boolean pingTest(String server) {
    285         final long PING_TIMER = 3 * 60 * 1000; // 3 minutes
    286         if (server == null || server.isEmpty()) {
    287             return false;
    288         }
    289         long startTime = System.currentTimeMillis();
    290         while ((System.currentTimeMillis() - startTime) < PING_TIMER) {
    291             try {
    292                 Log.v(TAG, "Start ping test, ping " + server);
    293                 Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + server);
    294                 int status = p.waitFor();
    295                 if (status == 0) {
    296                     // if any of the ping test is successful, return true
    297                     return true;
    298                 }
    299             } catch (UnknownHostException e) {
    300                 Log.e(TAG, "Ping test Fail: Unknown Host");
    301             } catch (IOException e) {
    302                 Log.e(TAG, "Ping test Fail:  IOException");
    303             } catch (InterruptedException e) {
    304                 Log.e(TAG, "Ping test Fail: InterruptedException");
    305             }
    306         }
    307         // ping test timeout
    308         return false;
    309     }
    310 
    311     /**
    312      * Install certificates from a file loaded in external stroage on the device
    313      * @param profile vpn profile
    314      * @param fileName certificate file name
    315      * @param password password to extract certificate file
    316      */
    317     private void installCertificatesFromFile(VpnProfile profile, String fileName, String password)
    318             throws Exception {
    319         if (profile == null || fileName == null || password == null) {
    320             throw new Exception ("vpn profile, certificate file name and password can not be null");
    321         }
    322 
    323         int curUid = mContext.getUserId();
    324         mCertHelper.installCertificate(profile, fileName, password);
    325 
    326         if (DEBUG) {
    327             printKeyStore(profile);
    328         }
    329     }
    330 
    331     private void sleep(long time) {
    332         try {
    333             Thread.sleep(time);
    334         } catch (InterruptedException e) {
    335             Log.e(TAG, "interrupted: " + e.toString());
    336         }
    337     }
    338 
    339     /**
    340      * Test PPTP VPN connection
    341      */
    342     @LargeTest
    343     public void testPPTPConnection() throws Exception {
    344         mPreviousIpAddress = getIpAddress();
    345         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_PPTP);
    346         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
    347         connect(vpnProfile);
    348         validateVpnConnection(vpnProfile);
    349     }
    350 
    351     /**
    352      * Test L2TP/IPSec PSK VPN connection
    353      */
    354     @LargeTest
    355     public void testL2tpIpsecPskConnection() throws Exception {
    356         mPreviousIpAddress = getIpAddress();
    357         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_L2TP_IPSEC_PSK);
    358         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
    359         connect(vpnProfile);
    360         validateVpnConnection(vpnProfile);
    361     }
    362 
    363     /**
    364      * Test L2TP/IPSec RSA VPN connection
    365      */
    366     @LargeTest
    367     public void testL2tpIpsecRsaConnection() throws Exception {
    368         mPreviousIpAddress = getIpAddress();
    369         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_L2TP_IPSEC_RSA);
    370         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
    371         if (DEBUG) {
    372             printVpnProfile(vpnProfile);
    373         }
    374         String certFile = curVpnInfo.getCertificateFile();
    375         String password = curVpnInfo.getPassword();
    376         installCertificatesFromFile(vpnProfile, certFile, password);
    377         connect(vpnProfile);
    378         validateVpnConnection(vpnProfile);
    379     }
    380 
    381     /**
    382      * Test IPSec Xauth RSA VPN connection
    383      */
    384     @LargeTest
    385     public void testIpsecXauthRsaConnection() throws Exception {
    386         mPreviousIpAddress = getIpAddress();
    387         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_IPSEC_XAUTH_RSA);
    388         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
    389         if (DEBUG) {
    390             printVpnProfile(vpnProfile);
    391         }
    392         String certFile = curVpnInfo.getCertificateFile();
    393         String password = curVpnInfo.getPassword();
    394         installCertificatesFromFile(vpnProfile, certFile, password);
    395         connect(vpnProfile);
    396         validateVpnConnection(vpnProfile);
    397     }
    398 
    399     /**
    400      * Test IPSec Xauth PSK VPN connection
    401      */
    402     @LargeTest
    403     public void testIpsecXauthPskConnection() throws Exception {
    404         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_IPSEC_XAUTH_PSK);
    405         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
    406         if (DEBUG) {
    407             printVpnProfile(vpnProfile);
    408         }
    409         connect(vpnProfile);
    410         validateVpnConnection(vpnProfile, true);
    411     }
    412 
    413     /**
    414      * Test IPSec Hybrid RSA VPN connection
    415      */
    416     @LargeTest
    417     public void testIpsecHybridRsaConnection() throws Exception {
    418         mPreviousIpAddress = getIpAddress();
    419         VpnInfo curVpnInfo = mVpnInfoPool.get(VpnProfile.TYPE_IPSEC_HYBRID_RSA);
    420         VpnProfile vpnProfile = curVpnInfo.getVpnProfile();
    421         if (DEBUG) {
    422             printVpnProfile(vpnProfile);
    423         }
    424         connect(vpnProfile);
    425         validateVpnConnection(vpnProfile);
    426     }
    427 }
    428