WifiEnterpriseConfig.java revision 9b81319002634cf7118055f7aafaa26c27d4e5e8
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 */ 16package android.net.wifi; 17 18import android.os.Parcel; 19import android.os.Parcelable; 20import android.security.Credentials; 21import android.text.TextUtils; 22 23import java.util.HashMap; 24import java.util.Map; 25 26/** Enterprise configuration details for Wi-Fi @hide */ 27public class WifiEnterpriseConfig implements Parcelable { 28 private static final String TAG = "WifiEnterpriseConfig"; 29 /** 30 * In old configurations, the "private_key" field was used. However, newer 31 * configurations use the key_id field with the engine_id set to "keystore". 32 * If this field is found in the configuration, the migration code is 33 * triggered. 34 */ 35 private static final String OLD_PRIVATE_KEY_NAME = "private_key"; 36 37 /** 38 * String representing the keystore OpenSSL ENGINE's ID. 39 */ 40 private static final String ENGINE_ID_KEYSTORE = "keystore"; 41 42 /** 43 * String representing the keystore URI used for wpa_supplicant. 44 */ 45 private static final String KEYSTORE_URI = "keystore://"; 46 47 /** 48 * String to set the engine value to when it should be enabled. 49 */ 50 private static final String ENGINE_ENABLE = "1"; 51 52 /** 53 * String to set the engine value to when it should be disabled. 54 */ 55 private static final String ENGINE_DISABLE = "0"; 56 57 private static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE; 58 private static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + Credentials.USER_CERTIFICATE; 59 60 private static final String EAP_KEY = "eap"; 61 private static final String PHASE2_KEY = "phase2"; 62 private static final String IDENTITY_KEY = "identity"; 63 private static final String ANON_IDENTITY_KEY = "anonymous_identity"; 64 private static final String PASSWORD_KEY = "password"; 65 private static final String CLIENT_CERT_KEY = "client_cert"; 66 private static final String CA_CERT_KEY = "ca_cert"; 67 private static final String SUBJECT_MATCH_KEY = "subject_match"; 68 private static final String ENGINE_KEY = "engine"; 69 private static final String ENGINE_ID_KEY = "engine_id"; 70 private static final String PRIVATE_KEY_ID_KEY = "key_id"; 71 72 private HashMap<String, String> mFields = new HashMap<String, String>(); 73 74 /** This represents an empty value of an enterprise field. 75 * NULL is used at wpa_supplicant to indicate an empty value 76 */ 77 private static final String EMPTY_VALUE = "NULL"; 78 79 public WifiEnterpriseConfig() { 80 // Set the required defaults 81 mFields.put(EAP_KEY, Eap.strings[Eap.PEAP]); 82 mFields.put(ENGINE_KEY, ENGINE_DISABLE); 83 84 for (String key : new String[] {PHASE2_KEY, IDENTITY_KEY, ANON_IDENTITY_KEY, 85 PASSWORD_KEY, CLIENT_CERT_KEY, ENGINE_ID_KEY, PRIVATE_KEY_ID_KEY, 86 CA_CERT_KEY, SUBJECT_MATCH_KEY}) { 87 mFields.put(key, EMPTY_VALUE); 88 } 89 } 90 91 /** Copy constructor */ 92 public WifiEnterpriseConfig(WifiEnterpriseConfig source) { 93 for (String key : source.mFields.keySet()) { 94 mFields.put(key, source.mFields.get(key)); 95 } 96 } 97 98 @Override 99 public int describeContents() { 100 return 0; 101 } 102 103 /** @Override */ 104 public void writeToParcel(Parcel dest, int flags) { 105 dest.writeInt(mFields.size()); 106 for (Map.Entry<String, String> entry : mFields.entrySet()) { 107 dest.writeString(entry.getKey()); 108 dest.writeString(entry.getValue()); 109 } 110 } 111 112 /** @Override */ 113 public static final Creator<WifiEnterpriseConfig> CREATOR = 114 new Creator<WifiEnterpriseConfig>() { 115 public WifiEnterpriseConfig createFromParcel(Parcel in) { 116 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 117 int count = in.readInt(); 118 for (int i = 0; i < count; i++) { 119 String key = in.readString(); 120 String value = in.readString(); 121 enterpriseConfig.mFields.put(key, value); 122 } 123 return enterpriseConfig; 124 } 125 126 public WifiEnterpriseConfig[] newArray(int size) { 127 return new WifiEnterpriseConfig[size]; 128 } 129 }; 130 131 public static final class Eap { 132 public static final int PEAP = 0; 133 public static final int TLS = 1; 134 public static final int TTLS = 2; 135 public static final int PWD = 3; 136 /** @hide */ 137 public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD" }; 138 } 139 140 public static final class Phase2 { 141 public static final int NONE = 0; 142 public static final int PAP = 1; 143 public static final int MSCHAP = 2; 144 public static final int MSCHAPV2 = 3; 145 public static final int GTC = 4; 146 private static final String PREFIX = "auth="; 147 /** @hide */ 148 public static final String[] strings = {EMPTY_VALUE, "PAP", "MSCHAP", "MSCHAPV2", "GTC" }; 149 } 150 151 /** Internal use only @hide */ 152 public HashMap<String, String> getFields() { 153 return mFields; 154 } 155 156 /** 157 * Set the EAP authentication method. 158 * @param eapMethod is one {@link Eap#PEAP}, {@link Eap#TLS}, {@link Eap#TTLS} or 159 * {@link Eap#PWD} 160 */ 161 public void setEapMethod(int eapMethod) { 162 switch (eapMethod) { 163 /** Valid methods */ 164 case Eap.PEAP: 165 case Eap.PWD: 166 case Eap.TLS: 167 case Eap.TTLS: 168 mFields.put(EAP_KEY, Eap.strings[eapMethod]); 169 break; 170 default: 171 throw new IllegalArgumentException("Unknown EAP method"); 172 } 173 } 174 175 /** 176 * Get the eap method. 177 * @return eap method configured 178 */ 179 public int getEapMethod() { 180 String eapMethod = mFields.get(EAP_KEY); 181 return getStringIndex(Eap.strings, eapMethod, Eap.PEAP); 182 } 183 184 /** 185 * Set Phase 2 authentication method. Sets the inner authentication method to be used in 186 * phase 2 after setting up a secure channel 187 * @param phase2Method is the inner authentication method and can be one of {@link Phase2#NONE}, 188 * {@link Phase2#PAP}, {@link Phase2#MSCHAP}, {@link Phase2#MSCHAPV2}, 189 * {@link Phase2#GTC} 190 * 191 */ 192 public void setPhase2Method(int phase2Method) { 193 switch (phase2Method) { 194 case Phase2.NONE: 195 mFields.put(PHASE2_KEY, EMPTY_VALUE); 196 break; 197 /** Valid methods */ 198 case Phase2.PAP: 199 case Phase2.MSCHAP: 200 case Phase2.MSCHAPV2: 201 case Phase2.GTC: 202 mFields.put(PHASE2_KEY, convertToQuotedString( 203 Phase2.PREFIX + Phase2.strings[phase2Method])); 204 break; 205 default: 206 throw new IllegalArgumentException("Unknown Phase 2 method"); 207 } 208 } 209 210 /** 211 * Get the phase 2 authentication method. 212 * @return a phase 2 method defined at {@link Phase2} 213 * */ 214 public int getPhase2Method() { 215 String phase2Method = mFields.get(PHASE2_KEY); 216 return getStringIndex(Phase2.strings, phase2Method, Phase2.NONE); 217 } 218 219 /** 220 * Set the identity 221 * @param identity 222 */ 223 public void setIdentity(String identity) { 224 setFieldValue(IDENTITY_KEY, identity, ""); 225 } 226 227 /** 228 * Get the identity 229 * @return the identity 230 */ 231 public String getIdentity() { 232 return getFieldValue(IDENTITY_KEY, ""); 233 } 234 235 /** 236 * Set anonymous identity. This is used as the unencrypted identity with 237 * certain EAP types 238 * @param anonymousIdentity the anonymous identity 239 */ 240 public void setAnonymousIdentity(String anonymousIdentity) { 241 setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity, ""); 242 } 243 244 /** Get the anonymous identity 245 * @return anonymous identity 246 */ 247 public String getAnonymousIdentity() { 248 return getFieldValue(ANON_IDENTITY_KEY, ""); 249 } 250 251 /** 252 * Set the password. 253 * @param password the password 254 */ 255 public void setPassword(String password) { 256 setFieldValue(PASSWORD_KEY, password, ""); 257 } 258 259 /** 260 * Set CA certificate alias. 261 * 262 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 263 * a certificate 264 * </p> 265 * @param alias identifies the certificate 266 */ 267 public void setCaCertificate(String alias) { 268 setFieldValue(CA_CERT_KEY, alias, CA_CERT_PREFIX); 269 } 270 271 /** 272 * Get CA certificate alias 273 * @return alias to the CA certificate 274 */ 275 public String getCaCertificate() { 276 return getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); 277 } 278 279 /** 280 * Set Client certificate alias. 281 * 282 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 283 * a certificate 284 * </p> 285 * @param alias identifies the certificate 286 */ 287 public void setClientCertificate(String alias) { 288 setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX); 289 setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY); 290 // Also, set engine parameters 291 if (TextUtils.isEmpty(alias)) { 292 mFields.put(ENGINE_KEY, ENGINE_DISABLE); 293 mFields.put(ENGINE_ID_KEY, EMPTY_VALUE); 294 } else { 295 mFields.put(ENGINE_KEY, ENGINE_ENABLE); 296 mFields.put(ENGINE_ID_KEY, convertToQuotedString(ENGINE_ID_KEYSTORE)); 297 } 298 } 299 300 /** 301 * Get client certificate alias 302 * @return alias to the client certificate 303 */ 304 public String getClientCertificate() { 305 return getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); 306 } 307 308 /** 309 * Set subject match. This is the substring to be matched against the subject of the 310 * authentication server certificate. 311 * @param subjectMatch substring to be matched 312 */ 313 public void setSubjectMatch(String subjectMatch) { 314 setFieldValue(SUBJECT_MATCH_KEY, subjectMatch, ""); 315 } 316 317 /** 318 * Get subject match 319 * @return the subject match string 320 */ 321 public String getSubjectMatch() { 322 return getFieldValue(SUBJECT_MATCH_KEY, ""); 323 } 324 325 /** Migrates the old style TLS config to the new config style. This should only be used 326 * when restoring an old wpa_supplicant.conf or upgrading from a previous 327 * platform version. 328 * @return true if the config was updated 329 * @hide 330 */ 331 public boolean migrateOldEapTlsNative(WifiNative wifiNative, int netId) { 332 String oldPrivateKey = wifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME); 333 /* 334 * If the old configuration value is not present, then there is nothing 335 * to do. 336 */ 337 if (TextUtils.isEmpty(oldPrivateKey)) { 338 return false; 339 } else { 340 // Also ignore it if it's empty quotes. 341 oldPrivateKey = removeDoubleQuotes(oldPrivateKey); 342 if (TextUtils.isEmpty(oldPrivateKey)) { 343 return false; 344 } 345 } 346 347 mFields.put(ENGINE_KEY, ENGINE_ENABLE); 348 mFields.put(ENGINE_ID_KEY, convertToQuotedString(ENGINE_ID_KEYSTORE)); 349 350 /* 351 * The old key started with the keystore:// URI prefix, but we don't 352 * need that anymore. Trim it off if it exists. 353 */ 354 final String keyName; 355 if (oldPrivateKey.startsWith(KEYSTORE_URI)) { 356 keyName = new String(oldPrivateKey.substring(KEYSTORE_URI.length())); 357 } else { 358 keyName = oldPrivateKey; 359 } 360 mFields.put(PRIVATE_KEY_ID_KEY, convertToQuotedString(keyName)); 361 362 wifiNative.setNetworkVariable(netId, ENGINE_KEY, mFields.get(ENGINE_KEY)); 363 wifiNative.setNetworkVariable(netId, ENGINE_ID_KEY, mFields.get(ENGINE_ID_KEY)); 364 wifiNative.setNetworkVariable(netId, PRIVATE_KEY_ID_KEY, mFields.get(PRIVATE_KEY_ID_KEY)); 365 // Remove old private_key string so we don't run this again. 366 wifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE); 367 return true; 368 } 369 370 private String removeDoubleQuotes(String string) { 371 int length = string.length(); 372 if ((length > 1) && (string.charAt(0) == '"') 373 && (string.charAt(length - 1) == '"')) { 374 return string.substring(1, length - 1); 375 } 376 return string; 377 } 378 379 private String convertToQuotedString(String string) { 380 return "\"" + string + "\""; 381 } 382 383 /** Returns the index at which the toBeFound string is found in the array. 384 * @param arr array of strings 385 * @param toBeFound string to be found 386 * @param defaultIndex default index to be returned when string is not found 387 * @return the index into array 388 */ 389 private int getStringIndex(String arr[], String toBeFound, int defaultIndex) { 390 for (int i = 0; i < arr.length; i++) { 391 // toBeFound can be formatted with a prefix. For example, phase2 392 // string has "auth=" as the prefix. 393 if (toBeFound.contains(arr[i])) return i; 394 } 395 return defaultIndex; 396 } 397 398 /** Returns the field value for the key. 399 * @param key into the hash 400 * @param prefix is the prefix that the value may have 401 * @return value 402 */ 403 private String getFieldValue(String key, String prefix) { 404 String value = mFields.get(key); 405 if (EMPTY_VALUE.equals(value)) return ""; 406 return removeDoubleQuotes(value).substring(prefix.length()); 407 } 408 409 /** Set a value with an optional prefix at key 410 * @param key into the hash 411 * @param value to be set 412 * @param prefix an optional value to be prefixed to actual value 413 */ 414 private void setFieldValue(String key, String value, String prefix) { 415 if (TextUtils.isEmpty(value)) { 416 mFields.put(key, EMPTY_VALUE); 417 } else { 418 mFields.put(key, convertToQuotedString(prefix + value)); 419 } 420 } 421 422 @Override 423 public String toString() { 424 StringBuffer sb = new StringBuffer(); 425 for (String key : mFields.keySet()) { 426 sb.append(key).append(" ").append(mFields.get(key)).append("\n"); 427 } 428 return sb.toString(); 429 } 430} 431