Home | History | Annotate | Download | only in net
      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 
     17 package com.android.internal.net;
     18 
     19 import android.os.Parcel;
     20 import android.os.Parcelable;
     21 import android.text.TextUtils;
     22 
     23 import java.net.InetAddress;
     24 import java.nio.charset.StandardCharsets;
     25 
     26 /**
     27  * Parcel-like entity class for VPN profiles. To keep things simple, all
     28  * fields are package private. Methods are provided for serialization, so
     29  * storage can be implemented easily. Two rules are set for this class.
     30  * First, all fields must be kept non-null. Second, always make a copy
     31  * using clone() before modifying.
     32  *
     33  * @hide
     34  */
     35 public class VpnProfile implements Cloneable, Parcelable {
     36     private static final String TAG = "VpnProfile";
     37 
     38     // Match these constants with R.array.vpn_types.
     39     public static final int TYPE_PPTP = 0;
     40     public static final int TYPE_L2TP_IPSEC_PSK = 1;
     41     public static final int TYPE_L2TP_IPSEC_RSA = 2;
     42     public static final int TYPE_IPSEC_XAUTH_PSK = 3;
     43     public static final int TYPE_IPSEC_XAUTH_RSA = 4;
     44     public static final int TYPE_IPSEC_HYBRID_RSA = 5;
     45     public static final int TYPE_MAX = 5;
     46 
     47     // Entity fields.
     48     public final String key;           // -1
     49     public String name = "";           // 0
     50     public int type = TYPE_PPTP;       // 1
     51     public String server = "";         // 2
     52     public String username = "";       // 3
     53     public String password = "";       // 4
     54     public String dnsServers = "";     // 5
     55     public String searchDomains = "";  // 6
     56     public String routes = "";         // 7
     57     public boolean mppe = true;        // 8
     58     public String l2tpSecret = "";     // 9
     59     public String ipsecIdentifier = "";// 10
     60     public String ipsecSecret = "";    // 11
     61     public String ipsecUserCert = "";  // 12
     62     public String ipsecCaCert = "";    // 13
     63     public String ipsecServerCert = "";// 14
     64 
     65     // Helper fields.
     66     public boolean saveLogin = false;
     67 
     68     public VpnProfile(String key) {
     69         this.key = key;
     70     }
     71 
     72     public VpnProfile(Parcel in) {
     73         key = in.readString();
     74         name = in.readString();
     75         type = in.readInt();
     76         server = in.readString();
     77         username = in.readString();
     78         password = in.readString();
     79         dnsServers = in.readString();
     80         searchDomains = in.readString();
     81         routes = in.readString();
     82         mppe = in.readInt() != 0;
     83         l2tpSecret = in.readString();
     84         ipsecIdentifier = in.readString();
     85         ipsecSecret = in.readString();
     86         ipsecUserCert = in.readString();
     87         ipsecCaCert = in.readString();
     88         ipsecServerCert = in.readString();
     89         saveLogin = in.readInt() != 0;
     90     }
     91 
     92     @Override
     93     public void writeToParcel(Parcel out, int flags) {
     94         out.writeString(key);
     95         out.writeString(name);
     96         out.writeInt(type);
     97         out.writeString(server);
     98         out.writeString(username);
     99         out.writeString(password);
    100         out.writeString(dnsServers);
    101         out.writeString(searchDomains);
    102         out.writeString(routes);
    103         out.writeInt(mppe ? 1 : 0);
    104         out.writeString(l2tpSecret);
    105         out.writeString(ipsecIdentifier);
    106         out.writeString(ipsecSecret);
    107         out.writeString(ipsecUserCert);
    108         out.writeString(ipsecCaCert);
    109         out.writeString(ipsecServerCert);
    110         out.writeInt(saveLogin ? 1 : 0);
    111     }
    112 
    113     public static VpnProfile decode(String key, byte[] value) {
    114         try {
    115             if (key == null) {
    116                 return null;
    117             }
    118 
    119             String[] values = new String(value, StandardCharsets.UTF_8).split("\0", -1);
    120             // There can be 14 or 15 values in ICS MR1.
    121             if (values.length < 14 || values.length > 15) {
    122                 return null;
    123             }
    124 
    125             VpnProfile profile = new VpnProfile(key);
    126             profile.name = values[0];
    127             profile.type = Integer.parseInt(values[1]);
    128             if (profile.type < 0 || profile.type > TYPE_MAX) {
    129                 return null;
    130             }
    131             profile.server = values[2];
    132             profile.username = values[3];
    133             profile.password = values[4];
    134             profile.dnsServers = values[5];
    135             profile.searchDomains = values[6];
    136             profile.routes = values[7];
    137             profile.mppe = Boolean.parseBoolean(values[8]);
    138             profile.l2tpSecret = values[9];
    139             profile.ipsecIdentifier = values[10];
    140             profile.ipsecSecret = values[11];
    141             profile.ipsecUserCert = values[12];
    142             profile.ipsecCaCert = values[13];
    143             profile.ipsecServerCert = (values.length > 14) ? values[14] : "";
    144 
    145             profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
    146             return profile;
    147         } catch (Exception e) {
    148             // ignore
    149         }
    150         return null;
    151     }
    152 
    153     public byte[] encode() {
    154         StringBuilder builder = new StringBuilder(name);
    155         builder.append('\0').append(type);
    156         builder.append('\0').append(server);
    157         builder.append('\0').append(saveLogin ? username : "");
    158         builder.append('\0').append(saveLogin ? password : "");
    159         builder.append('\0').append(dnsServers);
    160         builder.append('\0').append(searchDomains);
    161         builder.append('\0').append(routes);
    162         builder.append('\0').append(mppe);
    163         builder.append('\0').append(l2tpSecret);
    164         builder.append('\0').append(ipsecIdentifier);
    165         builder.append('\0').append(ipsecSecret);
    166         builder.append('\0').append(ipsecUserCert);
    167         builder.append('\0').append(ipsecCaCert);
    168         builder.append('\0').append(ipsecServerCert);
    169         return builder.toString().getBytes(StandardCharsets.UTF_8);
    170     }
    171 
    172     /**
    173      * Tests if profile is valid for lockdown, which requires IPv4 address for
    174      * both server and DNS. Server hostnames would require using DNS before
    175      * connection.
    176      */
    177     public boolean isValidLockdownProfile() {
    178         return isTypeValidForLockdown()
    179                 && isServerAddressNumeric()
    180                 && hasDns()
    181                 && areDnsAddressesNumeric();
    182     }
    183 
    184     /** Returns {@code true} if the VPN type is valid for lockdown. */
    185     public boolean isTypeValidForLockdown() {
    186         // b/7064069: lockdown firewall blocks ports used for PPTP
    187         return type != TYPE_PPTP;
    188     }
    189 
    190     /** Returns {@code true} if the server address is numeric, e.g. 8.8.8.8 */
    191     public boolean isServerAddressNumeric() {
    192         try {
    193             InetAddress.parseNumericAddress(server);
    194         } catch (IllegalArgumentException e) {
    195             return false;
    196         }
    197         return true;
    198     }
    199 
    200     /** Returns {@code true} if one or more DNS servers are specified. */
    201     public boolean hasDns() {
    202         return !TextUtils.isEmpty(dnsServers);
    203     }
    204 
    205     /**
    206      * Returns {@code true} if all DNS servers have numeric addresses,
    207      * e.g. 8.8.8.8
    208      */
    209     public boolean areDnsAddressesNumeric() {
    210         try {
    211             for (String dnsServer : dnsServers.split(" +")) {
    212                 InetAddress.parseNumericAddress(dnsServer);
    213             }
    214         } catch (IllegalArgumentException e) {
    215             return false;
    216         }
    217         return true;
    218     }
    219 
    220     public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() {
    221         @Override
    222         public VpnProfile createFromParcel(Parcel in) {
    223             return new VpnProfile(in);
    224         }
    225 
    226         @Override
    227         public VpnProfile[] newArray(int size) {
    228             return new VpnProfile[size];
    229         }
    230     };
    231 
    232     @Override
    233     public int describeContents() {
    234         return 0;
    235     }
    236 }
    237