1 package com.android.server.wifi.hotspot2.omadm; 2 3 import android.util.Base64; 4 import android.util.Log; 5 6 import com.android.server.wifi.IMSIParameter; 7 import com.android.server.wifi.anqp.eap.EAP; 8 import com.android.server.wifi.anqp.eap.EAPMethod; 9 import com.android.server.wifi.anqp.eap.ExpandedEAPMethod; 10 import com.android.server.wifi.anqp.eap.InnerAuthEAP; 11 import com.android.server.wifi.anqp.eap.NonEAPInnerAuth; 12 import com.android.server.wifi.hotspot2.Utils; 13 import com.android.server.wifi.hotspot2.pps.Credential; 14 import com.android.server.wifi.hotspot2.pps.HomeSP; 15 16 import org.xml.sax.SAXException; 17 18 import java.io.BufferedInputStream; 19 import java.io.BufferedOutputStream; 20 import java.io.File; 21 import java.io.FileInputStream; 22 import java.io.FileOutputStream; 23 import java.io.IOException; 24 import java.nio.charset.StandardCharsets; 25 import java.text.DateFormat; 26 import java.text.ParseException; 27 import java.text.SimpleDateFormat; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collection; 31 import java.util.Collections; 32 import java.util.Date; 33 import java.util.HashMap; 34 import java.util.HashSet; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Set; 38 import java.util.TimeZone; 39 40 /** 41 * Handles provisioning of PerProviderSubscription data. 42 */ 43 public class MOManager { 44 45 public static final String TAG_AAAServerTrustRoot = "AAAServerTrustRoot"; 46 public static final String TAG_AbleToShare = "AbleToShare"; 47 public static final String TAG_CertificateType = "CertificateType"; 48 public static final String TAG_CertSHA256Fingerprint = "CertSHA256Fingerprint"; 49 public static final String TAG_CertURL = "CertURL"; 50 public static final String TAG_CheckAAAServerCertStatus = "CheckAAAServerCertStatus"; 51 public static final String TAG_Country = "Country"; 52 public static final String TAG_CreationDate = "CreationDate"; 53 public static final String TAG_Credential = "Credential"; 54 public static final String TAG_CredentialPriority = "CredentialPriority"; 55 public static final String TAG_DataLimit = "DataLimit"; 56 public static final String TAG_DigitalCertificate = "DigitalCertificate"; 57 public static final String TAG_DLBandwidth = "DLBandwidth"; 58 public static final String TAG_EAPMethod = "EAPMethod"; 59 public static final String TAG_EAPType = "EAPType"; 60 public static final String TAG_ExpirationDate = "ExpirationDate"; 61 public static final String TAG_Extension = "Extension"; 62 public static final String TAG_FQDN = "FQDN"; 63 public static final String TAG_FQDN_Match = "FQDN_Match"; 64 public static final String TAG_FriendlyName = "FriendlyName"; 65 public static final String TAG_HESSID = "HESSID"; 66 public static final String TAG_HomeOI = "HomeOI"; 67 public static final String TAG_HomeOIList = "HomeOIList"; 68 public static final String TAG_HomeOIRequired = "HomeOIRequired"; 69 public static final String TAG_HomeSP = "HomeSP"; 70 public static final String TAG_IconURL = "IconURL"; 71 public static final String TAG_IMSI = "IMSI"; 72 public static final String TAG_InnerEAPType = "InnerEAPType"; 73 public static final String TAG_InnerMethod = "InnerMethod"; 74 public static final String TAG_InnerVendorID = "InnerVendorID"; 75 public static final String TAG_InnerVendorType = "InnerVendorType"; 76 public static final String TAG_IPProtocol = "IPProtocol"; 77 public static final String TAG_MachineManaged = "MachineManaged"; 78 public static final String TAG_MaximumBSSLoadValue = "MaximumBSSLoadValue"; 79 public static final String TAG_MinBackhaulThreshold = "MinBackhaulThreshold"; 80 public static final String TAG_NetworkID = "NetworkID"; 81 public static final String TAG_NetworkType = "NetworkType"; 82 public static final String TAG_Other = "Other"; 83 public static final String TAG_OtherHomePartners = "OtherHomePartners"; 84 public static final String TAG_Password = "Password"; 85 public static final String TAG_PerProviderSubscription = "PerProviderSubscription"; 86 public static final String TAG_Policy = "Policy"; 87 public static final String TAG_PolicyUpdate = "PolicyUpdate"; 88 public static final String TAG_PortNumber = "PortNumber"; 89 public static final String TAG_PreferredRoamingPartnerList = "PreferredRoamingPartnerList"; 90 public static final String TAG_Priority = "Priority"; 91 public static final String TAG_Realm = "Realm"; 92 public static final String TAG_RequiredProtoPortTuple = "RequiredProtoPortTuple"; 93 public static final String TAG_Restriction = "Restriction"; 94 public static final String TAG_RoamingConsortiumOI = "RoamingConsortiumOI"; 95 public static final String TAG_SIM = "SIM"; 96 public static final String TAG_SoftTokenApp = "SoftTokenApp"; 97 public static final String TAG_SPExclusionList = "SPExclusionList"; 98 public static final String TAG_SSID = "SSID"; 99 public static final String TAG_StartDate = "StartDate"; 100 public static final String TAG_SubscriptionParameters = "SubscriptionParameters"; 101 public static final String TAG_SubscriptionUpdate = "SubscriptionUpdate"; 102 public static final String TAG_TimeLimit = "TimeLimit"; 103 public static final String TAG_TrustRoot = "TrustRoot"; 104 public static final String TAG_TypeOfSubscription = "TypeOfSubscription"; 105 public static final String TAG_ULBandwidth = "ULBandwidth"; 106 public static final String TAG_UpdateIdentifier = "UpdateIdentifier"; 107 public static final String TAG_UpdateInterval = "UpdateInterval"; 108 public static final String TAG_UpdateMethod = "UpdateMethod"; 109 public static final String TAG_URI = "URI"; 110 public static final String TAG_UsageLimits = "UsageLimits"; 111 public static final String TAG_UsageTimePeriod = "UsageTimePeriod"; 112 public static final String TAG_Username = "Username"; 113 public static final String TAG_UsernamePassword = "UsernamePassword"; 114 public static final String TAG_VendorId = "VendorId"; 115 public static final String TAG_VendorType = "VendorType"; 116 117 private static final DateFormat DTFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 118 119 static { 120 DTFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 121 } 122 123 private final File mPpsFile; 124 private final boolean mEnabled; 125 private final Map<String, HomeSP> mSPs; 126 127 public MOManager(File ppsFile, boolean hs2enabled) { 128 mPpsFile = ppsFile; 129 mEnabled = hs2enabled; 130 mSPs = new HashMap<>(); 131 } 132 133 public File getPpsFile() { 134 return mPpsFile; 135 } 136 137 public boolean isEnabled() { 138 return mEnabled; 139 } 140 141 public boolean isConfigured() { 142 return mEnabled && !mSPs.isEmpty(); 143 } 144 145 public Map<String, HomeSP> getLoadedSPs() { 146 return Collections.unmodifiableMap(mSPs); 147 } 148 149 public List<HomeSP> loadAllSPs() throws IOException { 150 151 if (!mEnabled || !mPpsFile.exists()) { 152 return Collections.emptyList(); 153 } 154 155 try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(mPpsFile))) { 156 MOTree moTree = MOTree.unmarshal(in); 157 mSPs.clear(); 158 if (moTree == null) { 159 return Collections.emptyList(); // Empty file 160 } 161 162 List<HomeSP> sps = buildSPs(moTree); 163 if (sps != null) { 164 for (HomeSP sp : sps) { 165 if (mSPs.put(sp.getFQDN(), sp) != null) { 166 throw new OMAException("Multiple SPs for FQDN '" + sp.getFQDN() + "'"); 167 } else { 168 Log.d(Utils.hs2LogTag(getClass()), "retrieved " + sp.getFQDN() + " from PPS"); 169 } 170 } 171 return sps; 172 173 } else { 174 throw new OMAException("Failed to build HomeSP"); 175 } 176 } 177 } 178 179 public static HomeSP buildSP(String xml) throws IOException, SAXException { 180 OMAParser omaParser = new OMAParser(); 181 MOTree tree = omaParser.parse(xml, OMAConstants.LOC_PPS + ":1.0"); 182 List<HomeSP> spList = buildSPs(tree); 183 if (spList.size() != 1) { 184 throw new OMAException("Expected exactly one HomeSP, got " + spList.size()); 185 } 186 return spList.iterator().next(); 187 } 188 189 public HomeSP addSP(String xml) throws IOException, SAXException { 190 OMAParser omaParser = new OMAParser(); 191 MOTree tree = omaParser.parse(xml, OMAConstants.LOC_PPS + ":1.0"); 192 List<HomeSP> spList = buildSPs(tree); 193 if (spList.size() != 1) { 194 throw new OMAException("Expected exactly one HomeSP, got " + spList.size()); 195 } 196 HomeSP sp = spList.iterator().next(); 197 String fqdn = sp.getFQDN(); 198 if (mSPs.put(fqdn, sp) != null) { 199 throw new OMAException("SP " + fqdn + " already exists"); 200 } 201 202 BufferedOutputStream out = null; 203 try { 204 out = new BufferedOutputStream(new FileOutputStream(mPpsFile, true)); 205 tree.marshal(out); 206 out.flush(); 207 } finally { 208 if (out != null) { 209 try { 210 out.close(); 211 } catch (IOException ioe) { 212 /**/ 213 } 214 } 215 } 216 217 return sp; 218 } 219 220 public HomeSP getHomeSP(String fqdn) { 221 return mSPs.get(fqdn); 222 } 223 224 public void addSP(HomeSP homeSP) throws IOException { 225 if (!mEnabled) { 226 throw new IOException("HS2.0 not enabled on this device"); 227 } 228 if (mSPs.containsKey(homeSP.getFQDN())) { 229 Log.d(Utils.hs2LogTag(getClass()), "HS20 profile for " + 230 homeSP.getFQDN() + " already exists"); 231 return; 232 } 233 Log.d(Utils.hs2LogTag(getClass()), "Adding new HS20 profile for " + homeSP.getFQDN()); 234 mSPs.put(homeSP.getFQDN(), homeSP); 235 writeMO(mSPs.values(), mPpsFile); 236 } 237 238 public void removeSP(String fqdn) throws IOException { 239 if (mSPs.remove(fqdn) == null) { 240 Log.d(Utils.hs2LogTag(getClass()), "No HS20 profile to delete for " + fqdn); 241 return; 242 } 243 Log.d(Utils.hs2LogTag(getClass()), "Deleting HS20 profile for " + fqdn); 244 writeMO(mSPs.values(), mPpsFile); 245 } 246 247 public void updateAndSaveAllSps(Collection<HomeSP> homeSPs) throws IOException { 248 249 boolean dirty = false; 250 List<HomeSP> newSet = new ArrayList<>(homeSPs.size()); 251 252 Map<String, HomeSP> spClone = new HashMap<>(mSPs); 253 for (HomeSP homeSP : homeSPs) { 254 Log.d(Utils.hs2LogTag(getClass()), "Passed HomeSP: " + homeSP); 255 HomeSP existing = spClone.remove(homeSP.getFQDN()); 256 if (existing == null) { 257 dirty = true; 258 newSet.add(homeSP); 259 Log.d(Utils.hs2LogTag(getClass()), "New HomeSP"); 260 } 261 else if (!homeSP.deepEquals(existing)) { 262 dirty = true; 263 newSet.add(homeSP.getClone(existing.getCredential().getPassword())); 264 Log.d(Utils.hs2LogTag(getClass()), "Non-equal HomeSP: " + existing); 265 } 266 else { 267 newSet.add(existing); 268 Log.d(Utils.hs2LogTag(getClass()), "Keeping HomeSP: " + existing); 269 } 270 } 271 272 Log.d(Utils.hs2LogTag(getClass()), 273 String.format("Saving all SPs (%s): current %s (%d), new %s (%d)", 274 dirty ? "dirty" : "clean", 275 fqdnList(mSPs.values()), mSPs.size(), 276 fqdnList(newSet), newSet.size())); 277 278 if (!dirty && spClone.isEmpty()) { 279 Log.d(Utils.hs2LogTag(getClass()), "Not persisting"); 280 return; 281 } 282 283 rewriteMO(newSet, mSPs, mPpsFile); 284 } 285 286 private static void rewriteMO(Collection<HomeSP> homeSPs, Map<String, HomeSP> current, File f) 287 throws IOException { 288 289 current.clear(); 290 291 OMAConstructed ppsNode = new OMAConstructed(null, TAG_PerProviderSubscription, null); 292 int instance = 0; 293 for (HomeSP homeSP : homeSPs) { 294 buildHomeSPTree(homeSP, ppsNode, instance++); 295 current.put(homeSP.getFQDN(), homeSP); 296 } 297 298 MOTree tree = new MOTree(OMAConstants.LOC_PPS + ":1.0", "1.2", ppsNode); 299 try (BufferedOutputStream out = 300 new BufferedOutputStream(new FileOutputStream(f, false))) { 301 tree.marshal(out); 302 out.flush(); 303 } 304 } 305 306 private static void writeMO(Collection<HomeSP> homeSPs, File f) throws IOException { 307 308 OMAConstructed ppsNode = new OMAConstructed(null, TAG_PerProviderSubscription, null); 309 int instance = 0; 310 for (HomeSP homeSP : homeSPs) { 311 buildHomeSPTree(homeSP, ppsNode, instance++); 312 } 313 314 MOTree tree = new MOTree(OMAConstants.LOC_PPS + ":1.0", "1.2", ppsNode); 315 try (BufferedOutputStream out = 316 new BufferedOutputStream(new FileOutputStream(f, false))) { 317 tree.marshal(out); 318 out.flush(); 319 } 320 } 321 322 private static String fqdnList(Collection<HomeSP> sps) { 323 StringBuilder sb = new StringBuilder(); 324 boolean first = true; 325 for (HomeSP sp : sps) { 326 if (first) { 327 first = false; 328 } 329 else { 330 sb.append(", "); 331 } 332 sb.append(sp.getFQDN()); 333 } 334 return sb.toString(); 335 } 336 337 private static void buildHomeSPTree(HomeSP homeSP, OMAConstructed root, int spInstance) 338 throws IOException { 339 OMANode providerSubNode = root.addChild(getInstanceString(spInstance), null, null, null); 340 341 // The HomeSP: 342 OMANode homeSpNode = providerSubNode.addChild(TAG_HomeSP, null, null, null); 343 if (!homeSP.getSSIDs().isEmpty()) { 344 OMAConstructed nwkIDNode = 345 (OMAConstructed) homeSpNode.addChild(TAG_NetworkID, null, null, null); 346 int instance = 0; 347 for (Map.Entry<String, Long> entry : homeSP.getSSIDs().entrySet()) { 348 OMAConstructed inode = 349 (OMAConstructed) nwkIDNode.addChild(getInstanceString(instance++), null, null, null); 350 inode.addChild(TAG_SSID, null, entry.getKey(), null); 351 if (entry.getValue() != null) { 352 inode.addChild(TAG_HESSID, null, String.format("%012x", entry.getValue()), null); 353 } 354 } 355 } 356 357 homeSpNode.addChild(TAG_FriendlyName, null, homeSP.getFriendlyName(), null); 358 359 if (homeSP.getIconURL() != null) { 360 homeSpNode.addChild(TAG_IconURL, null, homeSP.getIconURL(), null); 361 } 362 363 homeSpNode.addChild(TAG_FQDN, null, homeSP.getFQDN(), null); 364 365 if (!homeSP.getMatchAllOIs().isEmpty() || !homeSP.getMatchAnyOIs().isEmpty()) { 366 OMAConstructed homeOIList = 367 (OMAConstructed) homeSpNode.addChild(TAG_HomeOIList, null, null, null); 368 369 int instance = 0; 370 for (Long oi : homeSP.getMatchAllOIs()) { 371 OMAConstructed inode = 372 (OMAConstructed) homeOIList.addChild(getInstanceString(instance++), 373 null, null, null); 374 inode.addChild(TAG_HomeOI, null, String.format("%x", oi), null); 375 inode.addChild(TAG_HomeOIRequired, null, "TRUE", null); 376 } 377 for (Long oi : homeSP.getMatchAnyOIs()) { 378 OMAConstructed inode = 379 (OMAConstructed) homeOIList.addChild(getInstanceString(instance++), 380 null, null, null); 381 inode.addChild(TAG_HomeOI, null, String.format("%x", oi), null); 382 inode.addChild(TAG_HomeOIRequired, null, "FALSE", null); 383 } 384 } 385 386 if (!homeSP.getOtherHomePartners().isEmpty()) { 387 OMAConstructed otherPartners = 388 (OMAConstructed) homeSpNode.addChild(TAG_OtherHomePartners, null, null, null); 389 int instance = 0; 390 for (String fqdn : homeSP.getOtherHomePartners()) { 391 OMAConstructed inode = 392 (OMAConstructed) otherPartners.addChild(getInstanceString(instance++), 393 null, null, null); 394 inode.addChild(TAG_FQDN, null, fqdn, null); 395 } 396 } 397 398 if (!homeSP.getRoamingConsortiums().isEmpty()) { 399 homeSpNode.addChild(TAG_RoamingConsortiumOI, null, getRCList(homeSP.getRoamingConsortiums()), null); 400 } 401 402 // The Credential: 403 OMANode credentialNode = providerSubNode.addChild(TAG_Credential, null, null, null); 404 Credential cred = homeSP.getCredential(); 405 EAPMethod method = cred.getEAPMethod(); 406 407 if (cred.getCtime() > 0) { 408 credentialNode.addChild(TAG_CreationDate, 409 null, DTFormat.format(new Date(cred.getCtime())), null); 410 } 411 if (cred.getExpTime() > 0) { 412 credentialNode.addChild(TAG_ExpirationDate, 413 null, DTFormat.format(new Date(cred.getExpTime())), null); 414 } 415 416 if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_SIM 417 || method.getEAPMethodID() == EAP.EAPMethodID.EAP_AKA 418 || method.getEAPMethodID() == EAP.EAPMethodID.EAP_AKAPrim) { 419 420 OMANode simNode = credentialNode.addChild(TAG_SIM, null, null, null); 421 simNode.addChild(TAG_IMSI, null, cred.getImsi().toString(), null); 422 simNode.addChild(TAG_EAPType, null, 423 Integer.toString(EAP.mapEAPMethod(method.getEAPMethodID())), null); 424 425 } else if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_TTLS) { 426 427 OMANode unpNode = credentialNode.addChild(TAG_UsernamePassword, null, null, null); 428 unpNode.addChild(TAG_Username, null, cred.getUserName(), null); 429 unpNode.addChild(TAG_Password, null, 430 Base64.encodeToString(cred.getPassword().getBytes(StandardCharsets.UTF_8), 431 Base64.DEFAULT), null); 432 OMANode eapNode = unpNode.addChild(TAG_EAPMethod, null, null, null); 433 eapNode.addChild(TAG_EAPType, null, 434 Integer.toString(EAP.mapEAPMethod(method.getEAPMethodID())), null); 435 eapNode.addChild(TAG_InnerMethod, null, 436 ((NonEAPInnerAuth) method.getAuthParam()).getOMAtype(), null); 437 438 } else if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_TLS) { 439 440 OMANode certNode = credentialNode.addChild(TAG_DigitalCertificate, null, null, null); 441 certNode.addChild(TAG_CertificateType, null, Credential.CertTypeX509, null); 442 certNode.addChild(TAG_CertSHA256Fingerprint, null, 443 Utils.toHex(cred.getFingerPrint()), null); 444 445 } else { 446 throw new OMAException("Invalid credential on " + homeSP.getFQDN()); 447 } 448 449 credentialNode.addChild(TAG_Realm, null, cred.getRealm(), null); 450 451 // !!! Note: This node defines CRL checking through OSCP, I suspect we won't be able 452 // to do that so it is commented out: 453 //credentialNode.addChild(TAG_CheckAAAServerCertStatus, null, "TRUE", null); 454 } 455 456 private static String getInstanceString(int instance) { 457 return "i" + instance; 458 } 459 460 private static String getRCList(Collection<Long> rcs) { 461 StringBuilder builder = new StringBuilder(); 462 boolean first = true; 463 for (Long roamingConsortium : rcs) { 464 if (first) { 465 first = false; 466 } 467 else { 468 builder.append(','); 469 } 470 builder.append(String.format("%x", roamingConsortium)); 471 } 472 return builder.toString(); 473 } 474 475 private static List<HomeSP> buildSPs(MOTree moTree) throws OMAException { 476 OMAConstructed spList; 477 if (moTree.getRoot().getName().equals(TAG_PerProviderSubscription)) { 478 // The PPS file is rooted at PPS instead of MgmtTree to conserve space 479 spList = moTree.getRoot(); 480 } 481 else { 482 List<String> spPath = Arrays.asList(TAG_PerProviderSubscription); 483 spList = moTree.getRoot().getListValue(spPath.iterator()); 484 } 485 486 List<HomeSP> homeSPs = new ArrayList<>(); 487 488 if (spList == null) { 489 return homeSPs; 490 } 491 for (OMANode spRoot : spList.getChildren()) { 492 homeSPs.add(buildHomeSP(spRoot)); 493 } 494 495 return homeSPs; 496 } 497 498 private static HomeSP buildHomeSP(OMANode ppsRoot) throws OMAException { 499 OMANode spRoot = ppsRoot.getChild(TAG_HomeSP); 500 501 String fqdn = spRoot.getScalarValue(Arrays.asList(TAG_FQDN).iterator()); 502 String friendlyName = spRoot.getScalarValue(Arrays.asList(TAG_FriendlyName).iterator()); 503 String iconURL = spRoot.getScalarValue(Arrays.asList(TAG_IconURL).iterator()); 504 505 HashSet<Long> roamingConsortiums = new HashSet<>(); 506 String oiString = spRoot.getScalarValue(Arrays.asList(TAG_RoamingConsortiumOI).iterator()); 507 if (oiString != null) { 508 for (String oi : oiString.split(",")) { 509 roamingConsortiums.add(Long.parseLong(oi.trim(), 16)); 510 } 511 } 512 513 Map<String, Long> ssids = new HashMap<>(); 514 515 OMANode ssidListNode = spRoot.getListValue(Arrays.asList(TAG_NetworkID).iterator()); 516 if (ssidListNode != null) { 517 for (OMANode ssidRoot : ssidListNode.getChildren()) { 518 OMANode hessidNode = ssidRoot.getChild(TAG_HESSID); 519 ssids.put(ssidRoot.getChild(TAG_SSID).getValue(), getMac(hessidNode)); 520 } 521 } 522 523 Set<Long> matchAnyOIs = new HashSet<>(); 524 List<Long> matchAllOIs = new ArrayList<>(); 525 OMANode homeOIListNode = spRoot.getListValue(Arrays.asList(TAG_HomeOIList).iterator()); 526 if (homeOIListNode != null) { 527 for (OMANode homeOIRoot : homeOIListNode.getChildren()) { 528 String homeOI = homeOIRoot.getChild(TAG_HomeOI).getValue(); 529 if (Boolean.parseBoolean(homeOIRoot.getChild(TAG_HomeOIRequired).getValue())) { 530 matchAllOIs.add(Long.parseLong(homeOI, 16)); 531 } else { 532 matchAnyOIs.add(Long.parseLong(homeOI, 16)); 533 } 534 } 535 } 536 537 Set<String> otherHomePartners = new HashSet<>(); 538 OMANode otherListNode = 539 spRoot.getListValue(Arrays.asList(TAG_OtherHomePartners).iterator()); 540 if (otherListNode != null) { 541 for (OMANode fqdnNode : otherListNode.getChildren()) { 542 otherHomePartners.add(fqdnNode.getChild(TAG_FQDN).getValue()); 543 } 544 } 545 546 Credential credential = buildCredential(ppsRoot.getChild(TAG_Credential)); 547 548 return new HomeSP(ssids, fqdn, roamingConsortiums, otherHomePartners, 549 matchAnyOIs, matchAllOIs, friendlyName, iconURL, credential); 550 } 551 552 private static Credential buildCredential(OMANode credNode) throws OMAException { 553 long ctime = getTime(credNode.getChild(TAG_CreationDate)); 554 long expTime = getTime(credNode.getChild(TAG_ExpirationDate)); 555 String realm = getString(credNode.getChild(TAG_Realm)); 556 boolean checkAAACert = getBoolean(credNode.getChild(TAG_CheckAAAServerCertStatus)); 557 558 OMANode unNode = credNode.getChild(TAG_UsernamePassword); 559 OMANode certNode = credNode.getChild(TAG_DigitalCertificate); 560 OMANode simNode = credNode.getChild(TAG_SIM); 561 562 int alternatives = 0; 563 alternatives += unNode != null ? 1 : 0; 564 alternatives += certNode != null ? 1 : 0; 565 alternatives += simNode != null ? 1 : 0; 566 if (alternatives != 1) { 567 throw new OMAException("Expected exactly one credential type, got " + alternatives); 568 } 569 570 if (unNode != null) { 571 String userName = getString(unNode.getChild(TAG_Username)); 572 String password = getString(unNode.getChild(TAG_Password)); 573 boolean machineManaged = getBoolean(unNode.getChild(TAG_MachineManaged)); 574 String softTokenApp = getString(unNode.getChild(TAG_SoftTokenApp)); 575 boolean ableToShare = getBoolean(unNode.getChild(TAG_AbleToShare)); 576 577 OMANode eapMethodNode = unNode.getChild(TAG_EAPMethod); 578 int eapID = getInteger(eapMethodNode.getChild(TAG_EAPType)); 579 580 EAP.EAPMethodID eapMethodID = EAP.mapEAPMethod(eapID); 581 if (eapMethodID == null) { 582 throw new OMAException("Unknown EAP method: " + eapID); 583 } 584 585 Long vid = getOptionalInteger(eapMethodNode.getChild(TAG_VendorId)); 586 Long vtype = getOptionalInteger(eapMethodNode.getChild(TAG_VendorType)); 587 Long innerEAPType = getOptionalInteger(eapMethodNode.getChild(TAG_InnerEAPType)); 588 EAP.EAPMethodID innerEAPMethod = null; 589 if (innerEAPType != null) { 590 innerEAPMethod = EAP.mapEAPMethod(innerEAPType.intValue()); 591 if (innerEAPMethod == null) { 592 throw new OMAException("Bad inner EAP method: " + innerEAPType); 593 } 594 } 595 596 Long innerVid = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorID)); 597 Long innerVtype = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorType)); 598 String innerNonEAPMethod = getString(eapMethodNode.getChild(TAG_InnerMethod)); 599 600 EAPMethod eapMethod; 601 if (innerEAPMethod != null) { 602 eapMethod = new EAPMethod(eapMethodID, new InnerAuthEAP(innerEAPMethod)); 603 } else if (vid != null) { 604 eapMethod = new EAPMethod(eapMethodID, 605 new ExpandedEAPMethod(EAP.AuthInfoID.ExpandedEAPMethod, 606 vid.intValue(), vtype)); 607 } else if (innerVid != null) { 608 eapMethod = 609 new EAPMethod(eapMethodID, new ExpandedEAPMethod(EAP.AuthInfoID 610 .ExpandedInnerEAPMethod, innerVid.intValue(), innerVtype)); 611 } else if (innerNonEAPMethod != null) { 612 eapMethod = new EAPMethod(eapMethodID, new NonEAPInnerAuth(innerNonEAPMethod)); 613 } else { 614 throw new OMAException("Incomplete set of EAP parameters"); 615 } 616 617 return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, userName, 618 password, machineManaged, softTokenApp, ableToShare); 619 } 620 if (certNode != null) { 621 try { 622 String certTypeString = getString(certNode.getChild(TAG_CertificateType)); 623 byte[] fingerPrint = getOctets(certNode.getChild(TAG_CertSHA256Fingerprint)); 624 625 EAPMethod eapMethod = new EAPMethod(EAP.EAPMethodID.EAP_TLS, null); 626 627 return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, 628 Credential.mapCertType(certTypeString), fingerPrint); 629 } 630 catch (NumberFormatException nfe) { 631 throw new OMAException("Bad hex string: " + nfe.toString()); 632 } 633 } 634 if (simNode != null) { 635 try { 636 IMSIParameter imsi = new IMSIParameter(getString(simNode.getChild(TAG_IMSI))); 637 638 EAPMethod eapMethod = 639 new EAPMethod(EAP.mapEAPMethod(getInteger(simNode.getChild(TAG_EAPType))), 640 null); 641 642 return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, imsi); 643 } 644 catch (IOException ioe) { 645 throw new OMAException("Failed to parse IMSI: " + ioe); 646 } 647 } 648 throw new OMAException("Missing credential parameters"); 649 } 650 651 private static boolean getBoolean(OMANode boolNode) { 652 return boolNode != null && Boolean.parseBoolean(boolNode.getValue()); 653 } 654 655 private static String getString(OMANode stringNode) { 656 return stringNode != null ? stringNode.getValue() : null; 657 } 658 659 private static int getInteger(OMANode intNode) throws OMAException { 660 if (intNode == null) { 661 throw new OMAException("Missing integer value"); 662 } 663 try { 664 return Integer.parseInt(intNode.getValue()); 665 } catch (NumberFormatException nfe) { 666 throw new OMAException("Invalid integer: " + intNode.getValue()); 667 } 668 } 669 670 private static Long getMac(OMANode macNode) throws OMAException { 671 if (macNode == null) { 672 return null; 673 } 674 try { 675 return Long.parseLong(macNode.getValue(), 16); 676 } catch (NumberFormatException nfe) { 677 throw new OMAException("Invalid MAC: " + macNode.getValue()); 678 } 679 } 680 681 private static Long getOptionalInteger(OMANode intNode) throws OMAException { 682 if (intNode == null) { 683 return null; 684 } 685 try { 686 return Long.parseLong(intNode.getValue()); 687 } catch (NumberFormatException nfe) { 688 throw new OMAException("Invalid integer: " + intNode.getValue()); 689 } 690 } 691 692 private static long getTime(OMANode timeNode) throws OMAException { 693 if (timeNode == null) { 694 return Utils.UNSET_TIME; 695 } 696 String timeText = timeNode.getValue(); 697 try { 698 Date date = DTFormat.parse(timeText); 699 return date.getTime(); 700 } catch (ParseException pe) { 701 throw new OMAException("Badly formatted time: " + timeText); 702 } 703 } 704 705 private static byte[] getOctets(OMANode octetNode) throws OMAException { 706 if (octetNode == null) { 707 throw new OMAException("Missing byte value"); 708 } 709 return Utils.hexToBytes(octetNode.getValue()); 710 } 711 } 712