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