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 VpnProfile(String key)68 public VpnProfile(String key) { 69 this.key = key; 70 } 71 VpnProfile(Parcel in)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 writeToParcel(Parcel out, int flags)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 decode(String key, byte[] value)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 encode()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 */ isValidLockdownProfile()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. */ isTypeValidForLockdown()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 */ isServerAddressNumeric()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. */ hasDns()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 */ areDnsAddressesNumeric()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 describeContents()233 public int describeContents() { 234 return 0; 235 } 236 } 237