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