1 /* 2 * Hotspot 2.0 OSU client 3 * Copyright (c) 2012-2014, Qualcomm Atheros, Inc. 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9 #include "includes.h" 10 #include <time.h> 11 #include <sys/stat.h> 12 #ifdef ANDROID 13 #include "private/android_filesystem_config.h" 14 #endif /* ANDROID */ 15 16 #include "common.h" 17 #include "utils/browser.h" 18 #include "utils/base64.h" 19 #include "utils/xml-utils.h" 20 #include "utils/http-utils.h" 21 #include "common/wpa_ctrl.h" 22 #include "common/wpa_helpers.h" 23 #include "eap_common/eap_defs.h" 24 #include "crypto/crypto.h" 25 #include "crypto/sha256.h" 26 #include "osu_client.h" 27 28 const char *spp_xsd_fname = "spp.xsd"; 29 30 31 void write_result(struct hs20_osu_client *ctx, const char *fmt, ...) 32 { 33 va_list ap; 34 FILE *f; 35 char buf[500]; 36 37 va_start(ap, fmt); 38 vsnprintf(buf, sizeof(buf), fmt, ap); 39 va_end(ap); 40 write_summary(ctx, "%s", buf); 41 42 if (!ctx->result_file) 43 return; 44 45 f = fopen(ctx->result_file, "w"); 46 if (f == NULL) 47 return; 48 49 va_start(ap, fmt); 50 vfprintf(f, fmt, ap); 51 va_end(ap); 52 fprintf(f, "\n"); 53 fclose(f); 54 } 55 56 57 void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...) 58 { 59 va_list ap; 60 FILE *f; 61 62 if (!ctx->summary_file) 63 return; 64 65 f = fopen(ctx->summary_file, "a"); 66 if (f == NULL) 67 return; 68 69 va_start(ap, fmt); 70 vfprintf(f, fmt, ap); 71 va_end(ap); 72 fprintf(f, "\n"); 73 fclose(f); 74 } 75 76 77 void debug_dump_node(struct hs20_osu_client *ctx, const char *title, 78 xml_node_t *node) 79 { 80 char *str = xml_node_to_str(ctx->xml, node); 81 wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str); 82 free(str); 83 } 84 85 86 static int valid_fqdn(const char *fqdn) 87 { 88 const char *pos; 89 90 /* TODO: could make this more complete.. */ 91 if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255) 92 return 0; 93 for (pos = fqdn; *pos; pos++) { 94 if (*pos >= 'a' && *pos <= 'z') 95 continue; 96 if (*pos >= 'A' && *pos <= 'Z') 97 continue; 98 if (*pos >= '0' && *pos <= '9') 99 continue; 100 if (*pos == '-' || *pos == '.' || *pos == '_') 101 continue; 102 return 0; 103 } 104 return 1; 105 } 106 107 108 static int android_update_permission(const char *path, mode_t mode) 109 { 110 #ifdef ANDROID 111 /* we need to change file/folder permission for Android */ 112 113 if (!path) { 114 wpa_printf(MSG_ERROR, "file path null"); 115 return -1; 116 } 117 118 /* Allow processes running with Group ID as AID_WIFI, 119 * to read files from SP, SP/<fqdn>, Cert and osu-info directories */ 120 if (lchown(path, -1, AID_WIFI)) { 121 wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s", 122 strerror(errno)); 123 return -1; 124 } 125 126 if (chmod(path, mode) < 0) { 127 wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s", 128 strerror(errno)); 129 return -1; 130 } 131 #endif /* ANDROID */ 132 133 return 0; 134 } 135 136 137 int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert) 138 { 139 xml_node_t *node; 140 char *url, *user = NULL, *pw = NULL; 141 char *proto; 142 int ret = -1; 143 144 proto = xml_node_get_attr_value(ctx->xml, getcert, 145 "enrollmentProtocol"); 146 if (!proto) 147 return -1; 148 wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto); 149 write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto); 150 if (os_strcasecmp(proto, "EST") != 0) { 151 wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol"); 152 xml_node_get_attr_value_free(ctx->xml, proto); 153 return -1; 154 } 155 xml_node_get_attr_value_free(ctx->xml, proto); 156 157 node = get_node(ctx->xml, getcert, "enrollmentServerURI"); 158 if (node == NULL) { 159 wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node"); 160 xml_node_get_attr_value_free(ctx->xml, proto); 161 return -1; 162 } 163 url = xml_node_get_text(ctx->xml, node); 164 if (url == NULL) { 165 wpa_printf(MSG_INFO, "Could not get URL text"); 166 return -1; 167 } 168 wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url); 169 write_summary(ctx, "enrollmentServerURI: %s", url); 170 171 node = get_node(ctx->xml, getcert, "estUserID"); 172 if (node == NULL && !ctx->client_cert_present) { 173 wpa_printf(MSG_INFO, "Could not find estUserID node"); 174 goto fail; 175 } 176 if (node) { 177 user = xml_node_get_text(ctx->xml, node); 178 if (user == NULL) { 179 wpa_printf(MSG_INFO, "Could not get estUserID text"); 180 goto fail; 181 } 182 wpa_printf(MSG_INFO, "estUserID: %s", user); 183 write_summary(ctx, "estUserID: %s", user); 184 } 185 186 node = get_node(ctx->xml, getcert, "estPassword"); 187 if (node == NULL && !ctx->client_cert_present) { 188 wpa_printf(MSG_INFO, "Could not find estPassword node"); 189 goto fail; 190 } 191 if (node) { 192 pw = xml_node_get_base64_text(ctx->xml, node, NULL); 193 if (pw == NULL) { 194 wpa_printf(MSG_INFO, "Could not get estPassword text"); 195 goto fail; 196 } 197 wpa_printf(MSG_INFO, "estPassword: %s", pw); 198 } 199 200 mkdir("Cert", S_IRWXU); 201 android_update_permission("Cert", S_IRWXU | S_IRWXG); 202 203 if (est_load_cacerts(ctx, url) < 0 || 204 est_build_csr(ctx, url) < 0 || 205 est_simple_enroll(ctx, url, user, pw) < 0) 206 goto fail; 207 208 ret = 0; 209 fail: 210 xml_node_get_text_free(ctx->xml, url); 211 xml_node_get_text_free(ctx->xml, user); 212 xml_node_get_text_free(ctx->xml, pw); 213 214 return ret; 215 } 216 217 218 static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert, 219 const char *fqdn) 220 { 221 u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN]; 222 char *der, *pem; 223 size_t der_len, pem_len; 224 char *fingerprint; 225 char buf[200]; 226 227 wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn); 228 229 fingerprint = xml_node_get_text(ctx->xml, cert); 230 if (fingerprint == NULL) 231 return -1; 232 if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) { 233 wpa_printf(MSG_INFO, "Invalid SHA256 hash value"); 234 write_result(ctx, "Invalid client certificate SHA256 hash value in PPS"); 235 xml_node_get_text_free(ctx->xml, fingerprint); 236 return -1; 237 } 238 xml_node_get_text_free(ctx->xml, fingerprint); 239 240 der = os_readfile("Cert/est_cert.der", &der_len); 241 if (der == NULL) { 242 wpa_printf(MSG_INFO, "Could not find client certificate from EST"); 243 write_result(ctx, "Could not find client certificate from EST"); 244 return -1; 245 } 246 247 if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) { 248 os_free(der); 249 return -1; 250 } 251 os_free(der); 252 253 if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) { 254 wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO"); 255 write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO"); 256 return -1; 257 } 258 259 wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO"); 260 unlink("Cert/est_cert.der"); 261 262 os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn); 263 if (rename("Cert/est-cacerts.pem", buf) < 0) { 264 wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s", 265 strerror(errno)); 266 return -1; 267 } 268 pem = os_readfile(buf, &pem_len); 269 270 os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn); 271 if (rename("Cert/est_cert.pem", buf) < 0) { 272 wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s", 273 strerror(errno)); 274 os_free(pem); 275 return -1; 276 } 277 278 if (pem) { 279 FILE *f = fopen(buf, "a"); 280 if (f) { 281 fwrite(pem, pem_len, 1, f); 282 fclose(f); 283 } 284 os_free(pem); 285 } 286 287 os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn); 288 if (rename("Cert/privkey-plain.pem", buf) < 0) { 289 wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s", 290 strerror(errno)); 291 return -1; 292 } 293 294 unlink("Cert/est-req.b64"); 295 unlink("Cert/est-req.pem"); 296 rmdir("Cert"); 297 298 return 0; 299 } 300 301 302 #define TMP_CERT_DL_FILE "tmp-cert-download" 303 304 static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params, 305 const char *fname) 306 { 307 xml_node_t *url_node, *hash_node; 308 char *url, *hash; 309 char *cert; 310 size_t len; 311 u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN]; 312 int res; 313 char *b64; 314 FILE *f; 315 316 url_node = get_node(ctx->xml, params, "CertURL"); 317 hash_node = get_node(ctx->xml, params, "CertSHA256Fingerprint"); 318 if (url_node == NULL || hash_node == NULL) 319 return -1; 320 url = xml_node_get_text(ctx->xml, url_node); 321 hash = xml_node_get_text(ctx->xml, hash_node); 322 if (url == NULL || hash == NULL) { 323 xml_node_get_text_free(ctx->xml, url); 324 xml_node_get_text_free(ctx->xml, hash); 325 return -1; 326 } 327 328 wpa_printf(MSG_INFO, "CertURL: %s", url); 329 wpa_printf(MSG_INFO, "SHA256 hash: %s", hash); 330 331 if (hexstr2bin(hash, digest1, SHA256_MAC_LEN) < 0) { 332 wpa_printf(MSG_INFO, "Invalid SHA256 hash value"); 333 write_result(ctx, "Invalid SHA256 hash value for downloaded certificate"); 334 xml_node_get_text_free(ctx->xml, hash); 335 return -1; 336 } 337 xml_node_get_text_free(ctx->xml, hash); 338 339 write_summary(ctx, "Download certificate from %s", url); 340 ctx->no_osu_cert_validation = 1; 341 http_ocsp_set(ctx->http, 1); 342 res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL); 343 http_ocsp_set(ctx->http, 344 (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2); 345 ctx->no_osu_cert_validation = 0; 346 xml_node_get_text_free(ctx->xml, url); 347 if (res < 0) 348 return -1; 349 350 cert = os_readfile(TMP_CERT_DL_FILE, &len); 351 remove(TMP_CERT_DL_FILE); 352 if (cert == NULL) 353 return -1; 354 355 if (sha256_vector(1, (const u8 **) &cert, &len, digest2) < 0) { 356 os_free(cert); 357 return -1; 358 } 359 360 if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) { 361 wpa_printf(MSG_INFO, "Downloaded certificate fingerprint did not match"); 362 write_result(ctx, "Downloaded certificate fingerprint did not match"); 363 os_free(cert); 364 return -1; 365 } 366 367 b64 = base64_encode(cert, len, NULL); 368 os_free(cert); 369 if (b64 == NULL) 370 return -1; 371 372 f = fopen(fname, "wb"); 373 if (f == NULL) { 374 os_free(b64); 375 return -1; 376 } 377 378 fprintf(f, "-----BEGIN CERTIFICATE-----\n" 379 "%s" 380 "-----END CERTIFICATE-----\n", 381 b64); 382 383 os_free(b64); 384 fclose(f); 385 386 wpa_printf(MSG_INFO, "Downloaded certificate into %s and validated fingerprint", 387 fname); 388 write_summary(ctx, "Downloaded certificate into %s and validated fingerprint", 389 fname); 390 391 return 0; 392 } 393 394 395 static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname, 396 const char *ca_fname) 397 { 398 xml_node_t *pps, *node; 399 int ret; 400 401 pps = node_from_file(ctx->xml, pps_fname); 402 if (pps == NULL) { 403 wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 404 return -1; 405 } 406 407 node = get_child_node(ctx->xml, pps, 408 "SubscriptionUpdate/TrustRoot"); 409 if (node == NULL) { 410 wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS"); 411 xml_node_free(ctx->xml, pps); 412 return -1; 413 } 414 415 ret = download_cert(ctx, node, ca_fname); 416 xml_node_free(ctx->xml, pps); 417 418 return ret; 419 } 420 421 422 static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname, 423 const char *ca_fname) 424 { 425 xml_node_t *pps, *node; 426 int ret; 427 428 pps = node_from_file(ctx->xml, pps_fname); 429 if (pps == NULL) { 430 wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 431 return -1; 432 } 433 434 node = get_child_node(ctx->xml, pps, 435 "Policy/PolicyUpdate/TrustRoot"); 436 if (node == NULL) { 437 wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS"); 438 xml_node_free(ctx->xml, pps); 439 return -2; 440 } 441 442 ret = download_cert(ctx, node, ca_fname); 443 xml_node_free(ctx->xml, pps); 444 445 return ret; 446 } 447 448 449 static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname, 450 const char *ca_fname) 451 { 452 xml_node_t *pps, *node, *aaa; 453 int ret; 454 455 pps = node_from_file(ctx->xml, pps_fname); 456 if (pps == NULL) { 457 wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 458 return -1; 459 } 460 461 node = get_child_node(ctx->xml, pps, 462 "AAAServerTrustRoot"); 463 if (node == NULL) { 464 wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS"); 465 xml_node_free(ctx->xml, pps); 466 return -2; 467 } 468 469 aaa = xml_node_first_child(ctx->xml, node); 470 if (aaa == NULL) { 471 wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS"); 472 xml_node_free(ctx->xml, pps); 473 return -1; 474 } 475 476 ret = download_cert(ctx, aaa, ca_fname); 477 xml_node_free(ctx->xml, pps); 478 479 return ret; 480 } 481 482 483 static int download_trust_roots(struct hs20_osu_client *ctx, 484 const char *pps_fname) 485 { 486 char *dir, *pos; 487 char fname[300]; 488 int ret, ret1; 489 490 dir = os_strdup(pps_fname); 491 if (dir == NULL) 492 return -1; 493 pos = os_strrchr(dir, '/'); 494 if (pos == NULL) { 495 os_free(dir); 496 return -1; 497 } 498 *pos = '\0'; 499 500 snprintf(fname, sizeof(fname), "%s/ca.pem", dir); 501 ret = cmd_dl_osu_ca(ctx, pps_fname, fname); 502 snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir); 503 ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname); 504 if (ret == 0 && ret1 == -1) 505 ret = -1; 506 snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir); 507 ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname); 508 if (ret == 0 && ret1 == -1) 509 ret = -1; 510 511 os_free(dir); 512 513 return ret; 514 } 515 516 517 static int server_dnsname_suffix_match(struct hs20_osu_client *ctx, 518 const char *fqdn) 519 { 520 size_t match_len, len, i; 521 const char *val; 522 523 match_len = os_strlen(fqdn); 524 525 for (i = 0; i < ctx->server_dnsname_count; i++) { 526 wpa_printf(MSG_INFO, 527 "Checking suffix match against server dNSName %s", 528 ctx->server_dnsname[i]); 529 val = ctx->server_dnsname[i]; 530 len = os_strlen(val); 531 532 if (match_len > len) 533 continue; 534 535 if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0) 536 continue; /* no match */ 537 538 if (match_len == len) 539 return 1; /* exact match */ 540 541 if (val[len - match_len - 1] == '.') 542 return 1; /* full label match completes suffix match */ 543 544 /* Reject due to incomplete label match */ 545 } 546 547 /* None of the dNSName(s) matched */ 548 return 0; 549 } 550 551 552 int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, 553 xml_node_t *add_mo, char *fname, size_t fname_len) 554 { 555 char *str; 556 char *fqdn, *pos; 557 xml_node_t *tnds, *mo, *cert; 558 const char *name; 559 int ret; 560 561 if (strncmp(uri, "./Wi-Fi/", 8) != 0) { 562 wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'", 563 uri); 564 write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'", 565 uri); 566 return -1; 567 } 568 569 fqdn = strdup(uri + 8); 570 if (fqdn == NULL) 571 return -1; 572 pos = strchr(fqdn, '/'); 573 if (pos) { 574 if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) { 575 wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'", 576 uri); 577 write_result(ctx, "Unsupported location for addMO to " 578 "add PPS MO (extra directory): '%s'", uri); 579 free(fqdn); 580 return -1; 581 } 582 *pos = '\0'; /* remove trailing slash and PPS node name */ 583 } 584 wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn); 585 586 if (!server_dnsname_suffix_match(ctx, fqdn)) { 587 wpa_printf(MSG_INFO, 588 "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d", 589 fqdn, (int) ctx->server_dnsname_count); 590 write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values", 591 fqdn); 592 free(fqdn); 593 return -1; 594 } 595 596 if (!valid_fqdn(fqdn)) { 597 wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn); 598 write_result(ctx, "Invalid FQDN '%s'", fqdn); 599 free(fqdn); 600 return -1; 601 } 602 603 mkdir("SP", S_IRWXU); 604 snprintf(fname, fname_len, "SP/%s", fqdn); 605 if (mkdir(fname, S_IRWXU) < 0) { 606 if (errno != EEXIST) { 607 int err = errno; 608 wpa_printf(MSG_INFO, "mkdir(%s) failed: %s", 609 fname, strerror(err)); 610 free(fqdn); 611 return -1; 612 } 613 } 614 615 android_update_permission("SP", S_IRWXU | S_IRWXG); 616 android_update_permission(fname, S_IRWXU | S_IRWXG); 617 618 snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn); 619 620 if (os_file_exists(fname)) { 621 wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO", 622 fname); 623 write_result(ctx, "PPS file '%s' exists - reject addMO", 624 fname); 625 free(fqdn); 626 return -2; 627 } 628 wpa_printf(MSG_INFO, "Using PPS file: %s", fname); 629 630 str = xml_node_get_text(ctx->xml, add_mo); 631 if (str == NULL) { 632 wpa_printf(MSG_INFO, "Could not extract MO text"); 633 free(fqdn); 634 return -1; 635 } 636 wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str); 637 638 tnds = xml_node_from_buf(ctx->xml, str); 639 xml_node_get_text_free(ctx->xml, str); 640 if (tnds == NULL) { 641 wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text"); 642 free(fqdn); 643 return -1; 644 } 645 646 mo = tnds_to_mo(ctx->xml, tnds); 647 if (mo == NULL) { 648 wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text"); 649 free(fqdn); 650 return -1; 651 } 652 653 debug_dump_node(ctx, "Parsed TNDS", mo); 654 655 name = xml_node_get_localname(ctx->xml, mo); 656 if (os_strcasecmp(name, "PerProviderSubscription") != 0) { 657 wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'", 658 name); 659 free(fqdn); 660 return -1; 661 } 662 663 cert = get_child_node(ctx->xml, mo, 664 "Credential/DigitalCertificate/" 665 "CertSHA256Fingerprint"); 666 if (cert && process_est_cert(ctx, cert, fqdn) < 0) { 667 xml_node_free(ctx->xml, mo); 668 free(fqdn); 669 return -1; 670 } 671 free(fqdn); 672 673 if (node_to_file(ctx->xml, fname, mo) < 0) { 674 wpa_printf(MSG_INFO, "Could not write MO to file"); 675 xml_node_free(ctx->xml, mo); 676 return -1; 677 } 678 xml_node_free(ctx->xml, mo); 679 680 wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname); 681 write_summary(ctx, "A new PPS MO added as '%s'", fname); 682 683 ret = download_trust_roots(ctx, fname); 684 if (ret < 0) { 685 wpa_printf(MSG_INFO, "Remove invalid PPS MO file"); 686 write_summary(ctx, "Remove invalid PPS MO file"); 687 unlink(fname); 688 } 689 690 return ret; 691 } 692 693 694 int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname, 695 xml_node_t *pps) 696 { 697 char *str; 698 FILE *f; 699 char backup[300]; 700 701 if (ctx->client_cert_present) { 702 xml_node_t *cert; 703 cert = get_child_node(ctx->xml, pps, 704 "Credential/DigitalCertificate/" 705 "CertSHA256Fingerprint"); 706 if (cert && os_file_exists("Cert/est_cert.der") && 707 process_est_cert(ctx, cert, ctx->fqdn) < 0) { 708 wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update"); 709 return -1; 710 } 711 } 712 713 wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname); 714 715 str = xml_node_to_str(ctx->xml, pps); 716 if (str == NULL) { 717 wpa_printf(MSG_ERROR, "No node found"); 718 return -1; 719 } 720 wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str); 721 722 snprintf(backup, sizeof(backup), "%s.bak", pps_fname); 723 rename(pps_fname, backup); 724 f = fopen(pps_fname, "w"); 725 if (f == NULL) { 726 wpa_printf(MSG_INFO, "Could not write PPS"); 727 rename(backup, pps_fname); 728 free(str); 729 return -1; 730 } 731 fprintf(f, "%s\n", str); 732 fclose(f); 733 734 free(str); 735 736 return 0; 737 } 738 739 740 void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps, 741 const char *alt_loc, char **user, char **pw) 742 { 743 xml_node_t *node; 744 745 node = get_child_node(ctx->xml, pps, 746 "Credential/UsernamePassword/Username"); 747 if (node) 748 *user = xml_node_get_text(ctx->xml, node); 749 750 node = get_child_node(ctx->xml, pps, 751 "Credential/UsernamePassword/Password"); 752 if (node) 753 *pw = xml_node_get_base64_text(ctx->xml, node, NULL); 754 755 node = get_child_node(ctx->xml, pps, alt_loc); 756 if (node) { 757 xml_node_t *a; 758 a = get_node(ctx->xml, node, "Username"); 759 if (a) { 760 xml_node_get_text_free(ctx->xml, *user); 761 *user = xml_node_get_text(ctx->xml, a); 762 wpa_printf(MSG_INFO, "Use OSU username '%s'", *user); 763 } 764 765 a = get_node(ctx->xml, node, "Password"); 766 if (a) { 767 free(*pw); 768 *pw = xml_node_get_base64_text(ctx->xml, a, NULL); 769 wpa_printf(MSG_INFO, "Use OSU password"); 770 } 771 } 772 } 773 774 775 /* Remove old credentials based on HomeSP/FQDN */ 776 static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn) 777 { 778 char cmd[300]; 779 os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED provisioning_sp=%s", fqdn); 780 if (wpa_command(ctx->ifname, cmd) < 0) 781 wpa_printf(MSG_INFO, "Failed to remove old credential(s)"); 782 } 783 784 785 static void set_pps_cred_policy_spe(struct hs20_osu_client *ctx, int id, 786 xml_node_t *spe) 787 { 788 xml_node_t *ssid; 789 char *txt; 790 791 ssid = get_node(ctx->xml, spe, "SSID"); 792 if (ssid == NULL) 793 return; 794 txt = xml_node_get_text(ctx->xml, ssid); 795 if (txt == NULL) 796 return; 797 wpa_printf(MSG_DEBUG, "- Policy/SPExclusionList/<X+>/SSID = %s", txt); 798 if (set_cred_quoted(ctx->ifname, id, "excluded_ssid", txt) < 0) 799 wpa_printf(MSG_INFO, "Failed to set cred excluded_ssid"); 800 xml_node_get_text_free(ctx->xml, txt); 801 } 802 803 804 static void set_pps_cred_policy_spel(struct hs20_osu_client *ctx, int id, 805 xml_node_t *spel) 806 { 807 xml_node_t *child; 808 809 xml_node_for_each_child(ctx->xml, child, spel) { 810 xml_node_for_each_check(ctx->xml, child); 811 set_pps_cred_policy_spe(ctx, id, child); 812 } 813 } 814 815 816 static void set_pps_cred_policy_prp(struct hs20_osu_client *ctx, int id, 817 xml_node_t *prp) 818 { 819 xml_node_t *node; 820 char *txt = NULL, *pos; 821 char *prio, *country_buf = NULL; 822 const char *country; 823 char val[200]; 824 int priority; 825 826 node = get_node(ctx->xml, prp, "Priority"); 827 if (node == NULL) 828 return; 829 prio = xml_node_get_text(ctx->xml, node); 830 if (prio == NULL) 831 return; 832 wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s", 833 prio); 834 priority = atoi(prio); 835 xml_node_get_text_free(ctx->xml, prio); 836 837 node = get_node(ctx->xml, prp, "Country"); 838 if (node) { 839 country_buf = xml_node_get_text(ctx->xml, node); 840 if (country_buf == NULL) 841 return; 842 country = country_buf; 843 wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s", 844 country); 845 } else { 846 country = "*"; 847 } 848 849 node = get_node(ctx->xml, prp, "FQDN_Match"); 850 if (node == NULL) 851 goto out; 852 txt = xml_node_get_text(ctx->xml, node); 853 if (txt == NULL) 854 goto out; 855 wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s", 856 txt); 857 pos = strrchr(txt, ','); 858 if (pos == NULL) 859 goto out; 860 *pos++ = '\0'; 861 862 snprintf(val, sizeof(val), "%s,%d,%d,%s", txt, 863 strcmp(pos, "includeSubdomains") != 0, priority, country); 864 if (set_cred_quoted(ctx->ifname, id, "roaming_partner", val) < 0) 865 wpa_printf(MSG_INFO, "Failed to set cred roaming_partner"); 866 out: 867 xml_node_get_text_free(ctx->xml, country_buf); 868 xml_node_get_text_free(ctx->xml, txt); 869 } 870 871 872 static void set_pps_cred_policy_prpl(struct hs20_osu_client *ctx, int id, 873 xml_node_t *prpl) 874 { 875 xml_node_t *child; 876 877 xml_node_for_each_child(ctx->xml, child, prpl) { 878 xml_node_for_each_check(ctx->xml, child); 879 set_pps_cred_policy_prp(ctx, id, child); 880 } 881 } 882 883 884 static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client *ctx, int id, 885 xml_node_t *min_backhaul) 886 { 887 xml_node_t *node; 888 char *type, *dl = NULL, *ul = NULL; 889 int home; 890 891 node = get_node(ctx->xml, min_backhaul, "NetworkType"); 892 if (node == NULL) { 893 wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without mandatory NetworkType node"); 894 return; 895 } 896 897 type = xml_node_get_text(ctx->xml, node); 898 if (type == NULL) 899 return; 900 wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/NetworkType = %s", 901 type); 902 if (os_strcasecmp(type, "home") == 0) 903 home = 1; 904 else if (os_strcasecmp(type, "roaming") == 0) 905 home = 0; 906 else { 907 wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold with invalid NetworkType"); 908 xml_node_get_text_free(ctx->xml, type); 909 return; 910 } 911 xml_node_get_text_free(ctx->xml, type); 912 913 node = get_node(ctx->xml, min_backhaul, "DLBandwidth"); 914 if (node) 915 dl = xml_node_get_text(ctx->xml, node); 916 917 node = get_node(ctx->xml, min_backhaul, "ULBandwidth"); 918 if (node) 919 ul = xml_node_get_text(ctx->xml, node); 920 921 if (dl == NULL && ul == NULL) { 922 wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without either DLBandwidth or ULBandwidth nodes"); 923 return; 924 } 925 926 if (dl) 927 wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/DLBandwidth = %s", 928 dl); 929 if (ul) 930 wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/ULBandwidth = %s", 931 ul); 932 933 if (home) { 934 if (dl && 935 set_cred(ctx->ifname, id, "min_dl_bandwidth_home", dl) < 0) 936 wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit"); 937 if (ul && 938 set_cred(ctx->ifname, id, "min_ul_bandwidth_home", ul) < 0) 939 wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit"); 940 } else { 941 if (dl && 942 set_cred(ctx->ifname, id, "min_dl_bandwidth_roaming", dl) < 943 0) 944 wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit"); 945 if (ul && 946 set_cred(ctx->ifname, id, "min_ul_bandwidth_roaming", ul) < 947 0) 948 wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit"); 949 } 950 951 xml_node_get_text_free(ctx->xml, dl); 952 xml_node_get_text_free(ctx->xml, ul); 953 } 954 955 956 static void set_pps_cred_policy_min_backhaul_list(struct hs20_osu_client *ctx, 957 int id, xml_node_t *node) 958 { 959 xml_node_t *child; 960 961 wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold"); 962 963 xml_node_for_each_child(ctx->xml, child, node) { 964 xml_node_for_each_check(ctx->xml, child); 965 set_pps_cred_policy_min_backhaul(ctx, id, child); 966 } 967 } 968 969 970 static void set_pps_cred_policy_update(struct hs20_osu_client *ctx, int id, 971 xml_node_t *node) 972 { 973 wpa_printf(MSG_INFO, "- Policy/PolicyUpdate"); 974 /* Not used in wpa_supplicant */ 975 } 976 977 978 static void set_pps_cred_policy_required_proto_port(struct hs20_osu_client *ctx, 979 int id, xml_node_t *tuple) 980 { 981 xml_node_t *node; 982 char *proto, *port; 983 char *buf; 984 size_t buflen; 985 986 node = get_node(ctx->xml, tuple, "IPProtocol"); 987 if (node == NULL) { 988 wpa_printf(MSG_INFO, "Ignore RequiredProtoPortTuple without mandatory IPProtocol node"); 989 return; 990 } 991 992 proto = xml_node_get_text(ctx->xml, node); 993 if (proto == NULL) 994 return; 995 996 wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/IPProtocol = %s", 997 proto); 998 999 node = get_node(ctx->xml, tuple, "PortNumber"); 1000 port = node ? xml_node_get_text(ctx->xml, node) : NULL; 1001 if (port) { 1002 wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/PortNumber = %s", 1003 port); 1004 buflen = os_strlen(proto) + os_strlen(port) + 10; 1005 buf = os_malloc(buflen); 1006 if (buf) 1007 os_snprintf(buf, buflen, "%s:%s", proto, port); 1008 xml_node_get_text_free(ctx->xml, port); 1009 } else { 1010 buflen = os_strlen(proto) + 10; 1011 buf = os_malloc(buflen); 1012 if (buf) 1013 os_snprintf(buf, buflen, "%s", proto); 1014 } 1015 1016 xml_node_get_text_free(ctx->xml, proto); 1017 1018 if (buf == NULL) 1019 return; 1020 1021 if (set_cred(ctx->ifname, id, "req_conn_capab", buf) < 0) 1022 wpa_printf(MSG_INFO, "Could not set req_conn_capab"); 1023 1024 os_free(buf); 1025 } 1026 1027 1028 static void set_pps_cred_policy_required_proto_ports(struct hs20_osu_client *ctx, 1029 int id, xml_node_t *node) 1030 { 1031 xml_node_t *child; 1032 1033 wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple"); 1034 1035 xml_node_for_each_child(ctx->xml, child, node) { 1036 xml_node_for_each_check(ctx->xml, child); 1037 set_pps_cred_policy_required_proto_port(ctx, id, child); 1038 } 1039 } 1040 1041 1042 static void set_pps_cred_policy_max_bss_load(struct hs20_osu_client *ctx, int id, 1043 xml_node_t *node) 1044 { 1045 char *str = xml_node_get_text(ctx->xml, node); 1046 if (str == NULL) 1047 return; 1048 wpa_printf(MSG_INFO, "- Policy/MaximumBSSLoadValue - %s", str); 1049 if (set_cred(ctx->ifname, id, "max_bss_load", str) < 0) 1050 wpa_printf(MSG_INFO, "Failed to set cred max_bss_load limit"); 1051 xml_node_get_text_free(ctx->xml, str); 1052 } 1053 1054 1055 static void set_pps_cred_policy(struct hs20_osu_client *ctx, int id, 1056 xml_node_t *node) 1057 { 1058 xml_node_t *child; 1059 const char *name; 1060 1061 wpa_printf(MSG_INFO, "- Policy"); 1062 1063 xml_node_for_each_child(ctx->xml, child, node) { 1064 xml_node_for_each_check(ctx->xml, child); 1065 name = xml_node_get_localname(ctx->xml, child); 1066 if (os_strcasecmp(name, "PreferredRoamingPartnerList") == 0) 1067 set_pps_cred_policy_prpl(ctx, id, child); 1068 else if (os_strcasecmp(name, "MinBackhaulThreshold") == 0) 1069 set_pps_cred_policy_min_backhaul_list(ctx, id, child); 1070 else if (os_strcasecmp(name, "PolicyUpdate") == 0) 1071 set_pps_cred_policy_update(ctx, id, child); 1072 else if (os_strcasecmp(name, "SPExclusionList") == 0) 1073 set_pps_cred_policy_spel(ctx, id, child); 1074 else if (os_strcasecmp(name, "RequiredProtoPortTuple") == 0) 1075 set_pps_cred_policy_required_proto_ports(ctx, id, child); 1076 else if (os_strcasecmp(name, "MaximumBSSLoadValue") == 0) 1077 set_pps_cred_policy_max_bss_load(ctx, id, child); 1078 else 1079 wpa_printf(MSG_INFO, "Unknown Policy node '%s'", name); 1080 } 1081 } 1082 1083 1084 static void set_pps_cred_priority(struct hs20_osu_client *ctx, int id, 1085 xml_node_t *node) 1086 { 1087 char *str = xml_node_get_text(ctx->xml, node); 1088 if (str == NULL) 1089 return; 1090 wpa_printf(MSG_INFO, "- CredentialPriority = %s", str); 1091 if (set_cred(ctx->ifname, id, "sp_priority", str) < 0) 1092 wpa_printf(MSG_INFO, "Failed to set cred sp_priority"); 1093 xml_node_get_text_free(ctx->xml, str); 1094 } 1095 1096 1097 static void set_pps_cred_aaa_server_trust_root(struct hs20_osu_client *ctx, 1098 int id, xml_node_t *node) 1099 { 1100 wpa_printf(MSG_INFO, "- AAAServerTrustRoot - TODO"); 1101 } 1102 1103 1104 static void set_pps_cred_sub_update(struct hs20_osu_client *ctx, int id, 1105 xml_node_t *node) 1106 { 1107 wpa_printf(MSG_INFO, "- SubscriptionUpdate"); 1108 /* not used within wpa_supplicant */ 1109 } 1110 1111 1112 static void set_pps_cred_home_sp_network_id(struct hs20_osu_client *ctx, 1113 int id, xml_node_t *node) 1114 { 1115 xml_node_t *ssid_node, *hessid_node; 1116 char *ssid, *hessid; 1117 1118 ssid_node = get_node(ctx->xml, node, "SSID"); 1119 if (ssid_node == NULL) { 1120 wpa_printf(MSG_INFO, "Ignore HomeSP/NetworkID without mandatory SSID node"); 1121 return; 1122 } 1123 1124 hessid_node = get_node(ctx->xml, node, "HESSID"); 1125 1126 ssid = xml_node_get_text(ctx->xml, ssid_node); 1127 if (ssid == NULL) 1128 return; 1129 hessid = hessid_node ? xml_node_get_text(ctx->xml, hessid_node) : NULL; 1130 1131 wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/SSID = %s", ssid); 1132 if (hessid) 1133 wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/HESSID = %s", 1134 hessid); 1135 1136 /* TODO: Configure to wpa_supplicant */ 1137 1138 xml_node_get_text_free(ctx->xml, ssid); 1139 xml_node_get_text_free(ctx->xml, hessid); 1140 } 1141 1142 1143 static void set_pps_cred_home_sp_network_ids(struct hs20_osu_client *ctx, 1144 int id, xml_node_t *node) 1145 { 1146 xml_node_t *child; 1147 1148 wpa_printf(MSG_INFO, "- HomeSP/NetworkID"); 1149 1150 xml_node_for_each_child(ctx->xml, child, node) { 1151 xml_node_for_each_check(ctx->xml, child); 1152 set_pps_cred_home_sp_network_id(ctx, id, child); 1153 } 1154 } 1155 1156 1157 static void set_pps_cred_home_sp_friendly_name(struct hs20_osu_client *ctx, 1158 int id, xml_node_t *node) 1159 { 1160 char *str = xml_node_get_text(ctx->xml, node); 1161 if (str == NULL) 1162 return; 1163 wpa_printf(MSG_INFO, "- HomeSP/FriendlyName = %s", str); 1164 /* not used within wpa_supplicant(?) */ 1165 xml_node_get_text_free(ctx->xml, str); 1166 } 1167 1168 1169 static void set_pps_cred_home_sp_icon_url(struct hs20_osu_client *ctx, 1170 int id, xml_node_t *node) 1171 { 1172 char *str = xml_node_get_text(ctx->xml, node); 1173 if (str == NULL) 1174 return; 1175 wpa_printf(MSG_INFO, "- HomeSP/IconURL = %s", str); 1176 /* not used within wpa_supplicant */ 1177 xml_node_get_text_free(ctx->xml, str); 1178 } 1179 1180 1181 static void set_pps_cred_home_sp_fqdn(struct hs20_osu_client *ctx, int id, 1182 xml_node_t *node) 1183 { 1184 char *str = xml_node_get_text(ctx->xml, node); 1185 if (str == NULL) 1186 return; 1187 wpa_printf(MSG_INFO, "- HomeSP/FQDN = %s", str); 1188 if (set_cred_quoted(ctx->ifname, id, "domain", str) < 0) 1189 wpa_printf(MSG_INFO, "Failed to set cred domain"); 1190 if (set_cred_quoted(ctx->ifname, id, "domain_suffix_match", str) < 0) 1191 wpa_printf(MSG_INFO, "Failed to set cred domain_suffix_match"); 1192 xml_node_get_text_free(ctx->xml, str); 1193 } 1194 1195 1196 static void set_pps_cred_home_sp_oi(struct hs20_osu_client *ctx, int id, 1197 xml_node_t *node) 1198 { 1199 xml_node_t *child; 1200 const char *name; 1201 char *homeoi = NULL; 1202 int required = 0; 1203 char *str; 1204 1205 xml_node_for_each_child(ctx->xml, child, node) { 1206 xml_node_for_each_check(ctx->xml, child); 1207 name = xml_node_get_localname(ctx->xml, child); 1208 if (strcasecmp(name, "HomeOI") == 0 && !homeoi) { 1209 homeoi = xml_node_get_text(ctx->xml, child); 1210 wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOI = %s", 1211 homeoi); 1212 } else if (strcasecmp(name, "HomeOIRequired") == 0) { 1213 str = xml_node_get_text(ctx->xml, child); 1214 wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOIRequired = '%s'", 1215 str); 1216 if (str == NULL) 1217 continue; 1218 required = strcasecmp(str, "true") == 0; 1219 xml_node_get_text_free(ctx->xml, str); 1220 } else 1221 wpa_printf(MSG_INFO, "Unknown HomeOIList node '%s'", 1222 name); 1223 } 1224 1225 if (homeoi == NULL) { 1226 wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> without HomeOI ignored"); 1227 return; 1228 } 1229 1230 wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> '%s' required=%d", 1231 homeoi, required); 1232 1233 if (required) { 1234 if (set_cred(ctx->ifname, id, "required_roaming_consortium", 1235 homeoi) < 0) 1236 wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium"); 1237 } else { 1238 if (set_cred(ctx->ifname, id, "roaming_consortium", homeoi) < 0) 1239 wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium"); 1240 } 1241 1242 xml_node_get_text_free(ctx->xml, homeoi); 1243 } 1244 1245 1246 static void set_pps_cred_home_sp_oi_list(struct hs20_osu_client *ctx, int id, 1247 xml_node_t *node) 1248 { 1249 xml_node_t *child; 1250 1251 wpa_printf(MSG_INFO, "- HomeSP/HomeOIList"); 1252 1253 xml_node_for_each_child(ctx->xml, child, node) { 1254 xml_node_for_each_check(ctx->xml, child); 1255 set_pps_cred_home_sp_oi(ctx, id, child); 1256 } 1257 } 1258 1259 1260 static void set_pps_cred_home_sp_other_partner(struct hs20_osu_client *ctx, 1261 int id, xml_node_t *node) 1262 { 1263 xml_node_t *child; 1264 const char *name; 1265 char *fqdn = NULL; 1266 1267 xml_node_for_each_child(ctx->xml, child, node) { 1268 xml_node_for_each_check(ctx->xml, child); 1269 name = xml_node_get_localname(ctx->xml, child); 1270 if (os_strcasecmp(name, "FQDN") == 0 && !fqdn) { 1271 fqdn = xml_node_get_text(ctx->xml, child); 1272 wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+>/FQDN = %s", 1273 fqdn); 1274 } else 1275 wpa_printf(MSG_INFO, "Unknown OtherHomePartners node '%s'", 1276 name); 1277 } 1278 1279 if (fqdn == NULL) { 1280 wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+> without FQDN ignored"); 1281 return; 1282 } 1283 1284 if (set_cred_quoted(ctx->ifname, id, "domain", fqdn) < 0) 1285 wpa_printf(MSG_INFO, "Failed to set cred domain for OtherHomePartners node"); 1286 1287 xml_node_get_text_free(ctx->xml, fqdn); 1288 } 1289 1290 1291 static void set_pps_cred_home_sp_other_partners(struct hs20_osu_client *ctx, 1292 int id, 1293 xml_node_t *node) 1294 { 1295 xml_node_t *child; 1296 1297 wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners"); 1298 1299 xml_node_for_each_child(ctx->xml, child, node) { 1300 xml_node_for_each_check(ctx->xml, child); 1301 set_pps_cred_home_sp_other_partner(ctx, id, child); 1302 } 1303 } 1304 1305 1306 static void set_pps_cred_home_sp_roaming_consortium_oi( 1307 struct hs20_osu_client *ctx, int id, xml_node_t *node) 1308 { 1309 char *str = xml_node_get_text(ctx->xml, node); 1310 if (str == NULL) 1311 return; 1312 wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str); 1313 if (set_cred_quoted(ctx->ifname, id, "roaming_consortiums", 1314 str) < 0) 1315 wpa_printf(MSG_INFO, "Failed to set cred roaming_consortiums"); 1316 xml_node_get_text_free(ctx->xml, str); 1317 } 1318 1319 1320 static void set_pps_cred_home_sp(struct hs20_osu_client *ctx, int id, 1321 xml_node_t *node) 1322 { 1323 xml_node_t *child; 1324 const char *name; 1325 1326 wpa_printf(MSG_INFO, "- HomeSP"); 1327 1328 xml_node_for_each_child(ctx->xml, child, node) { 1329 xml_node_for_each_check(ctx->xml, child); 1330 name = xml_node_get_localname(ctx->xml, child); 1331 if (os_strcasecmp(name, "NetworkID") == 0) 1332 set_pps_cred_home_sp_network_ids(ctx, id, child); 1333 else if (os_strcasecmp(name, "FriendlyName") == 0) 1334 set_pps_cred_home_sp_friendly_name(ctx, id, child); 1335 else if (os_strcasecmp(name, "IconURL") == 0) 1336 set_pps_cred_home_sp_icon_url(ctx, id, child); 1337 else if (os_strcasecmp(name, "FQDN") == 0) 1338 set_pps_cred_home_sp_fqdn(ctx, id, child); 1339 else if (os_strcasecmp(name, "HomeOIList") == 0) 1340 set_pps_cred_home_sp_oi_list(ctx, id, child); 1341 else if (os_strcasecmp(name, "OtherHomePartners") == 0) 1342 set_pps_cred_home_sp_other_partners(ctx, id, child); 1343 else if (os_strcasecmp(name, "RoamingConsortiumOI") == 0) 1344 set_pps_cred_home_sp_roaming_consortium_oi(ctx, id, 1345 child); 1346 else 1347 wpa_printf(MSG_INFO, "Unknown HomeSP node '%s'", name); 1348 } 1349 } 1350 1351 1352 static void set_pps_cred_sub_params(struct hs20_osu_client *ctx, int id, 1353 xml_node_t *node) 1354 { 1355 wpa_printf(MSG_INFO, "- SubscriptionParameters"); 1356 /* not used within wpa_supplicant */ 1357 } 1358 1359 1360 static void set_pps_cred_creation_date(struct hs20_osu_client *ctx, int id, 1361 xml_node_t *node) 1362 { 1363 char *str = xml_node_get_text(ctx->xml, node); 1364 if (str == NULL) 1365 return; 1366 wpa_printf(MSG_INFO, "- Credential/CreationDate = %s", str); 1367 /* not used within wpa_supplicant */ 1368 xml_node_get_text_free(ctx->xml, str); 1369 } 1370 1371 1372 static void set_pps_cred_expiration_date(struct hs20_osu_client *ctx, int id, 1373 xml_node_t *node) 1374 { 1375 char *str = xml_node_get_text(ctx->xml, node); 1376 if (str == NULL) 1377 return; 1378 wpa_printf(MSG_INFO, "- Credential/ExpirationDate = %s", str); 1379 /* not used within wpa_supplicant */ 1380 xml_node_get_text_free(ctx->xml, str); 1381 } 1382 1383 1384 static void set_pps_cred_username(struct hs20_osu_client *ctx, int id, 1385 xml_node_t *node) 1386 { 1387 char *str = xml_node_get_text(ctx->xml, node); 1388 if (str == NULL) 1389 return; 1390 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Username = %s", 1391 str); 1392 if (set_cred_quoted(ctx->ifname, id, "username", str) < 0) 1393 wpa_printf(MSG_INFO, "Failed to set cred username"); 1394 xml_node_get_text_free(ctx->xml, str); 1395 } 1396 1397 1398 static void set_pps_cred_password(struct hs20_osu_client *ctx, int id, 1399 xml_node_t *node) 1400 { 1401 int len, i; 1402 char *pw, *hex, *pos, *end; 1403 1404 pw = xml_node_get_base64_text(ctx->xml, node, &len); 1405 if (pw == NULL) 1406 return; 1407 1408 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Password = %s", pw); 1409 1410 hex = malloc(len * 2 + 1); 1411 if (hex == NULL) { 1412 free(pw); 1413 return; 1414 } 1415 end = hex + len * 2 + 1; 1416 pos = hex; 1417 for (i = 0; i < len; i++) { 1418 snprintf(pos, end - pos, "%02x", pw[i]); 1419 pos += 2; 1420 } 1421 free(pw); 1422 1423 if (set_cred(ctx->ifname, id, "password", hex) < 0) 1424 wpa_printf(MSG_INFO, "Failed to set cred password"); 1425 free(hex); 1426 } 1427 1428 1429 static void set_pps_cred_machine_managed(struct hs20_osu_client *ctx, int id, 1430 xml_node_t *node) 1431 { 1432 char *str = xml_node_get_text(ctx->xml, node); 1433 if (str == NULL) 1434 return; 1435 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/MachineManaged = %s", 1436 str); 1437 /* not used within wpa_supplicant */ 1438 xml_node_get_text_free(ctx->xml, str); 1439 } 1440 1441 1442 static void set_pps_cred_soft_token_app(struct hs20_osu_client *ctx, int id, 1443 xml_node_t *node) 1444 { 1445 char *str = xml_node_get_text(ctx->xml, node); 1446 if (str == NULL) 1447 return; 1448 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/SoftTokenApp = %s", 1449 str); 1450 /* not used within wpa_supplicant */ 1451 xml_node_get_text_free(ctx->xml, str); 1452 } 1453 1454 1455 static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id, 1456 xml_node_t *node) 1457 { 1458 char *str = xml_node_get_text(ctx->xml, node); 1459 if (str == NULL) 1460 return; 1461 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/AbleToShare = %s", 1462 str); 1463 /* not used within wpa_supplicant */ 1464 xml_node_get_text_free(ctx->xml, str); 1465 } 1466 1467 1468 static void set_pps_cred_eap_method_eap_type(struct hs20_osu_client *ctx, 1469 int id, xml_node_t *node) 1470 { 1471 char *str = xml_node_get_text(ctx->xml, node); 1472 int type; 1473 const char *eap_method = NULL; 1474 1475 if (!str) 1476 return; 1477 wpa_printf(MSG_INFO, 1478 "- Credential/UsernamePassword/EAPMethod/EAPType = %s", str); 1479 type = atoi(str); 1480 switch (type) { 1481 case EAP_TYPE_TLS: 1482 eap_method = "TLS"; 1483 break; 1484 case EAP_TYPE_TTLS: 1485 eap_method = "TTLS"; 1486 break; 1487 case EAP_TYPE_PEAP: 1488 eap_method = "PEAP"; 1489 break; 1490 case EAP_TYPE_PWD: 1491 eap_method = "PWD"; 1492 break; 1493 } 1494 xml_node_get_text_free(ctx->xml, str); 1495 if (!eap_method) { 1496 wpa_printf(MSG_INFO, "Unknown EAPType value"); 1497 return; 1498 } 1499 1500 if (set_cred(ctx->ifname, id, "eap", eap_method) < 0) 1501 wpa_printf(MSG_INFO, "Failed to set cred eap"); 1502 } 1503 1504 1505 static void set_pps_cred_eap_method_inner_method(struct hs20_osu_client *ctx, 1506 int id, xml_node_t *node) 1507 { 1508 char *str = xml_node_get_text(ctx->xml, node); 1509 const char *phase2 = NULL; 1510 1511 if (!str) 1512 return; 1513 wpa_printf(MSG_INFO, 1514 "- Credential/UsernamePassword/EAPMethod/InnerMethod = %s", 1515 str); 1516 if (os_strcmp(str, "PAP") == 0) 1517 phase2 = "auth=PAP"; 1518 else if (os_strcmp(str, "CHAP") == 0) 1519 phase2 = "auth=CHAP"; 1520 else if (os_strcmp(str, "MS-CHAP") == 0) 1521 phase2 = "auth=MSCHAP"; 1522 else if (os_strcmp(str, "MS-CHAP-V2") == 0) 1523 phase2 = "auth=MSCHAPV2"; 1524 xml_node_get_text_free(ctx->xml, str); 1525 if (!phase2) { 1526 wpa_printf(MSG_INFO, "Unknown InnerMethod value"); 1527 return; 1528 } 1529 1530 if (set_cred_quoted(ctx->ifname, id, "phase2", phase2) < 0) 1531 wpa_printf(MSG_INFO, "Failed to set cred phase2"); 1532 } 1533 1534 1535 static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id, 1536 xml_node_t *node) 1537 { 1538 xml_node_t *child; 1539 const char *name; 1540 1541 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod"); 1542 1543 xml_node_for_each_child(ctx->xml, child, node) { 1544 xml_node_for_each_check(ctx->xml, child); 1545 name = xml_node_get_localname(ctx->xml, child); 1546 if (os_strcasecmp(name, "EAPType") == 0) 1547 set_pps_cred_eap_method_eap_type(ctx, id, child); 1548 else if (os_strcasecmp(name, "InnerMethod") == 0) 1549 set_pps_cred_eap_method_inner_method(ctx, id, child); 1550 else 1551 wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword/EAPMethod node '%s'", 1552 name); 1553 } 1554 } 1555 1556 1557 static void set_pps_cred_username_password(struct hs20_osu_client *ctx, int id, 1558 xml_node_t *node) 1559 { 1560 xml_node_t *child; 1561 const char *name; 1562 1563 wpa_printf(MSG_INFO, "- Credential/UsernamePassword"); 1564 1565 xml_node_for_each_child(ctx->xml, child, node) { 1566 xml_node_for_each_check(ctx->xml, child); 1567 name = xml_node_get_localname(ctx->xml, child); 1568 if (os_strcasecmp(name, "Username") == 0) 1569 set_pps_cred_username(ctx, id, child); 1570 else if (os_strcasecmp(name, "Password") == 0) 1571 set_pps_cred_password(ctx, id, child); 1572 else if (os_strcasecmp(name, "MachineManaged") == 0) 1573 set_pps_cred_machine_managed(ctx, id, child); 1574 else if (os_strcasecmp(name, "SoftTokenApp") == 0) 1575 set_pps_cred_soft_token_app(ctx, id, child); 1576 else if (os_strcasecmp(name, "AbleToShare") == 0) 1577 set_pps_cred_able_to_share(ctx, id, child); 1578 else if (os_strcasecmp(name, "EAPMethod") == 0) 1579 set_pps_cred_eap_method(ctx, id, child); 1580 else 1581 wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword node '%s'", 1582 name); 1583 } 1584 } 1585 1586 1587 static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id, 1588 xml_node_t *node, const char *fqdn) 1589 { 1590 char buf[200], dir[200]; 1591 int res; 1592 1593 wpa_printf(MSG_INFO, "- Credential/DigitalCertificate"); 1594 1595 if (getcwd(dir, sizeof(dir)) == NULL) 1596 return; 1597 1598 /* TODO: could build username from Subject of Subject AltName */ 1599 if (set_cred_quoted(ctx->ifname, id, "username", "cert") < 0) { 1600 wpa_printf(MSG_INFO, "Failed to set username"); 1601 } 1602 1603 res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, 1604 fqdn); 1605 if (os_snprintf_error(sizeof(buf), res)) 1606 return; 1607 if (os_file_exists(buf)) { 1608 if (set_cred_quoted(ctx->ifname, id, "client_cert", buf) < 0) { 1609 wpa_printf(MSG_INFO, "Failed to set client_cert"); 1610 } 1611 } 1612 1613 res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir, 1614 fqdn); 1615 if (os_snprintf_error(sizeof(buf), res)) 1616 return; 1617 if (os_file_exists(buf)) { 1618 if (set_cred_quoted(ctx->ifname, id, "private_key", buf) < 0) { 1619 wpa_printf(MSG_INFO, "Failed to set private_key"); 1620 } 1621 } 1622 } 1623 1624 1625 static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id, 1626 xml_node_t *node, const char *fqdn, int sim) 1627 { 1628 char *str = xml_node_get_text(ctx->xml, node); 1629 char buf[200], dir[200]; 1630 int res; 1631 1632 if (str == NULL) 1633 return; 1634 1635 wpa_printf(MSG_INFO, "- Credential/Realm = %s", str); 1636 if (set_cred_quoted(ctx->ifname, id, "realm", str) < 0) 1637 wpa_printf(MSG_INFO, "Failed to set cred realm"); 1638 xml_node_get_text_free(ctx->xml, str); 1639 1640 if (sim) 1641 return; 1642 1643 if (getcwd(dir, sizeof(dir)) == NULL) 1644 return; 1645 res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn); 1646 if (os_snprintf_error(sizeof(buf), res)) 1647 return; 1648 if (os_file_exists(buf)) { 1649 if (set_cred_quoted(ctx->ifname, id, "ca_cert", buf) < 0) { 1650 wpa_printf(MSG_INFO, "Failed to set CA cert"); 1651 } 1652 } 1653 } 1654 1655 1656 static void set_pps_cred_check_aaa_cert_status(struct hs20_osu_client *ctx, 1657 int id, xml_node_t *node) 1658 { 1659 char *str = xml_node_get_text(ctx->xml, node); 1660 1661 if (str == NULL) 1662 return; 1663 1664 wpa_printf(MSG_INFO, "- Credential/CheckAAAServerCertStatus = %s", str); 1665 if (os_strcasecmp(str, "true") == 0 && 1666 set_cred(ctx->ifname, id, "ocsp", "2") < 0) 1667 wpa_printf(MSG_INFO, "Failed to set cred ocsp"); 1668 xml_node_get_text_free(ctx->xml, str); 1669 } 1670 1671 1672 static void set_pps_cred_sim(struct hs20_osu_client *ctx, int id, 1673 xml_node_t *sim, xml_node_t *realm) 1674 { 1675 xml_node_t *node; 1676 char *imsi, *eaptype, *str, buf[20]; 1677 int type; 1678 int mnc_len = 3; 1679 size_t imsi_len; 1680 1681 node = get_node(ctx->xml, sim, "EAPType"); 1682 if (node == NULL) { 1683 wpa_printf(MSG_INFO, "No SIM/EAPType node in credential"); 1684 return; 1685 } 1686 eaptype = xml_node_get_text(ctx->xml, node); 1687 if (eaptype == NULL) { 1688 wpa_printf(MSG_INFO, "Could not extract SIM/EAPType"); 1689 return; 1690 } 1691 wpa_printf(MSG_INFO, " - Credential/SIM/EAPType = %s", eaptype); 1692 type = atoi(eaptype); 1693 xml_node_get_text_free(ctx->xml, eaptype); 1694 1695 switch (type) { 1696 case EAP_TYPE_SIM: 1697 if (set_cred(ctx->ifname, id, "eap", "SIM") < 0) 1698 wpa_printf(MSG_INFO, "Could not set eap=SIM"); 1699 break; 1700 case EAP_TYPE_AKA: 1701 if (set_cred(ctx->ifname, id, "eap", "AKA") < 0) 1702 wpa_printf(MSG_INFO, "Could not set eap=SIM"); 1703 break; 1704 case EAP_TYPE_AKA_PRIME: 1705 if (set_cred(ctx->ifname, id, "eap", "AKA'") < 0) 1706 wpa_printf(MSG_INFO, "Could not set eap=SIM"); 1707 break; 1708 default: 1709 wpa_printf(MSG_INFO, "Unsupported SIM/EAPType %d", type); 1710 return; 1711 } 1712 1713 node = get_node(ctx->xml, sim, "IMSI"); 1714 if (node == NULL) { 1715 wpa_printf(MSG_INFO, "No SIM/IMSI node in credential"); 1716 return; 1717 } 1718 imsi = xml_node_get_text(ctx->xml, node); 1719 if (imsi == NULL) { 1720 wpa_printf(MSG_INFO, "Could not extract SIM/IMSI"); 1721 return; 1722 } 1723 wpa_printf(MSG_INFO, " - Credential/SIM/IMSI = %s", imsi); 1724 imsi_len = os_strlen(imsi); 1725 if (imsi_len < 7 || imsi_len + 2 > sizeof(buf)) { 1726 wpa_printf(MSG_INFO, "Invalid IMSI length"); 1727 xml_node_get_text_free(ctx->xml, imsi); 1728 return; 1729 } 1730 1731 str = xml_node_get_text(ctx->xml, node); 1732 if (str) { 1733 char *pos; 1734 pos = os_strstr(str, "mnc"); 1735 if (pos && os_strlen(pos) >= 6) { 1736 if (os_strncmp(imsi + 3, pos + 3, 3) == 0) 1737 mnc_len = 3; 1738 else if (os_strncmp(imsi + 3, pos + 4, 2) == 0) 1739 mnc_len = 2; 1740 } 1741 xml_node_get_text_free(ctx->xml, str); 1742 } 1743 1744 os_memcpy(buf, imsi, 3 + mnc_len); 1745 buf[3 + mnc_len] = '-'; 1746 os_strlcpy(buf + 3 + mnc_len + 1, imsi + 3 + mnc_len, 1747 sizeof(buf) - 3 - mnc_len - 1); 1748 1749 xml_node_get_text_free(ctx->xml, imsi); 1750 1751 if (set_cred_quoted(ctx->ifname, id, "imsi", buf) < 0) 1752 wpa_printf(MSG_INFO, "Could not set IMSI"); 1753 1754 if (set_cred_quoted(ctx->ifname, id, "milenage", 1755 "90dca4eda45b53cf0f12d7c9c3bc6a89:" 1756 "cb9cccc4b9258e6dca4760379fb82581:000000000123") < 1757 0) 1758 wpa_printf(MSG_INFO, "Could not set Milenage parameters"); 1759 } 1760 1761 1762 static void set_pps_cred_credential(struct hs20_osu_client *ctx, int id, 1763 xml_node_t *node, const char *fqdn) 1764 { 1765 xml_node_t *child, *sim, *realm; 1766 const char *name; 1767 1768 wpa_printf(MSG_INFO, "- Credential"); 1769 1770 sim = get_node(ctx->xml, node, "SIM"); 1771 realm = get_node(ctx->xml, node, "Realm"); 1772 1773 xml_node_for_each_child(ctx->xml, child, node) { 1774 xml_node_for_each_check(ctx->xml, child); 1775 name = xml_node_get_localname(ctx->xml, child); 1776 if (os_strcasecmp(name, "CreationDate") == 0) 1777 set_pps_cred_creation_date(ctx, id, child); 1778 else if (os_strcasecmp(name, "ExpirationDate") == 0) 1779 set_pps_cred_expiration_date(ctx, id, child); 1780 else if (os_strcasecmp(name, "UsernamePassword") == 0) 1781 set_pps_cred_username_password(ctx, id, child); 1782 else if (os_strcasecmp(name, "DigitalCertificate") == 0) 1783 set_pps_cred_digital_cert(ctx, id, child, fqdn); 1784 else if (os_strcasecmp(name, "Realm") == 0) 1785 set_pps_cred_realm(ctx, id, child, fqdn, sim != NULL); 1786 else if (os_strcasecmp(name, "CheckAAAServerCertStatus") == 0) 1787 set_pps_cred_check_aaa_cert_status(ctx, id, child); 1788 else if (os_strcasecmp(name, "SIM") == 0) 1789 set_pps_cred_sim(ctx, id, child, realm); 1790 else 1791 wpa_printf(MSG_INFO, "Unknown Credential node '%s'", 1792 name); 1793 } 1794 } 1795 1796 1797 static void set_pps_credential(struct hs20_osu_client *ctx, int id, 1798 xml_node_t *cred, const char *fqdn) 1799 { 1800 xml_node_t *child; 1801 const char *name; 1802 1803 xml_node_for_each_child(ctx->xml, child, cred) { 1804 xml_node_for_each_check(ctx->xml, child); 1805 name = xml_node_get_localname(ctx->xml, child); 1806 if (os_strcasecmp(name, "Policy") == 0) 1807 set_pps_cred_policy(ctx, id, child); 1808 else if (os_strcasecmp(name, "CredentialPriority") == 0) 1809 set_pps_cred_priority(ctx, id, child); 1810 else if (os_strcasecmp(name, "AAAServerTrustRoot") == 0) 1811 set_pps_cred_aaa_server_trust_root(ctx, id, child); 1812 else if (os_strcasecmp(name, "SubscriptionUpdate") == 0) 1813 set_pps_cred_sub_update(ctx, id, child); 1814 else if (os_strcasecmp(name, "HomeSP") == 0) 1815 set_pps_cred_home_sp(ctx, id, child); 1816 else if (os_strcasecmp(name, "SubscriptionParameters") == 0) 1817 set_pps_cred_sub_params(ctx, id, child); 1818 else if (os_strcasecmp(name, "Credential") == 0) 1819 set_pps_cred_credential(ctx, id, child, fqdn); 1820 else 1821 wpa_printf(MSG_INFO, "Unknown credential node '%s'", 1822 name); 1823 } 1824 } 1825 1826 1827 static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps, 1828 const char *fqdn) 1829 { 1830 xml_node_t *child; 1831 const char *name; 1832 int id; 1833 char *update_identifier = NULL; 1834 1835 /* 1836 * TODO: Could consider more complex mechanism that would remove 1837 * credentials only if there are changes in the information sent to 1838 * wpa_supplicant. 1839 */ 1840 remove_sp_creds(ctx, fqdn); 1841 1842 xml_node_for_each_child(ctx->xml, child, pps) { 1843 xml_node_for_each_check(ctx->xml, child); 1844 name = xml_node_get_localname(ctx->xml, child); 1845 if (os_strcasecmp(name, "UpdateIdentifier") == 0) { 1846 update_identifier = xml_node_get_text(ctx->xml, child); 1847 if (update_identifier) { 1848 wpa_printf(MSG_INFO, "- UpdateIdentifier = %s", 1849 update_identifier); 1850 break; 1851 } 1852 } 1853 } 1854 1855 xml_node_for_each_child(ctx->xml, child, pps) { 1856 xml_node_for_each_check(ctx->xml, child); 1857 name = xml_node_get_localname(ctx->xml, child); 1858 if (os_strcasecmp(name, "UpdateIdentifier") == 0) 1859 continue; 1860 id = add_cred(ctx->ifname); 1861 if (id < 0) { 1862 wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant"); 1863 write_summary(ctx, "Failed to add credential to wpa_supplicant"); 1864 break; 1865 } 1866 write_summary(ctx, "Add a credential to wpa_supplicant"); 1867 if (update_identifier && 1868 set_cred(ctx->ifname, id, "update_identifier", 1869 update_identifier) < 0) 1870 wpa_printf(MSG_INFO, "Failed to set update_identifier"); 1871 if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) < 1872 0) 1873 wpa_printf(MSG_INFO, "Failed to set provisioning_sp"); 1874 wpa_printf(MSG_INFO, "credential localname: '%s'", name); 1875 set_pps_credential(ctx, id, child, fqdn); 1876 ctx->pps_cred_set = 1; 1877 } 1878 1879 xml_node_get_text_free(ctx->xml, update_identifier); 1880 } 1881 1882 1883 void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname) 1884 { 1885 xml_node_t *pps; 1886 const char *fqdn; 1887 char *fqdn_buf = NULL, *pos; 1888 1889 pps = node_from_file(ctx->xml, pps_fname); 1890 if (pps == NULL) { 1891 wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 1892 return; 1893 } 1894 1895 fqdn = os_strstr(pps_fname, "SP/"); 1896 if (fqdn) { 1897 fqdn_buf = os_strdup(fqdn + 3); 1898 if (fqdn_buf == NULL) 1899 return; 1900 pos = os_strchr(fqdn_buf, '/'); 1901 if (pos) 1902 *pos = '\0'; 1903 fqdn = fqdn_buf; 1904 } else 1905 fqdn = "wi-fi.org"; 1906 1907 wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s", 1908 fqdn); 1909 set_pps(ctx, pps, fqdn); 1910 1911 os_free(fqdn_buf); 1912 xml_node_free(ctx->xml, pps); 1913 } 1914 1915 1916 static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname) 1917 { 1918 xml_node_t *pps, *node; 1919 char *fqdn = NULL; 1920 1921 pps = node_from_file(ctx->xml, pps_fname); 1922 if (pps == NULL) { 1923 wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 1924 return -1; 1925 } 1926 1927 node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); 1928 if (node) 1929 fqdn = xml_node_get_text(ctx->xml, node); 1930 1931 xml_node_free(ctx->xml, pps); 1932 1933 if (fqdn) { 1934 FILE *f = fopen("pps-fqdn", "w"); 1935 if (f) { 1936 fprintf(f, "%s", fqdn); 1937 fclose(f); 1938 } 1939 xml_node_get_text_free(ctx->xml, fqdn); 1940 return 0; 1941 } 1942 1943 xml_node_get_text_free(ctx->xml, fqdn); 1944 return -1; 1945 } 1946 1947 1948 static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname, 1949 const char *out_fname, const char *urn, int use_path) 1950 { 1951 xml_node_t *mo, *node; 1952 1953 mo = node_from_file(ctx->xml, in_fname); 1954 if (mo == NULL) { 1955 wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname); 1956 return; 1957 } 1958 1959 node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL); 1960 if (node) { 1961 node_to_file(ctx->xml, out_fname, node); 1962 xml_node_free(ctx->xml, node); 1963 } 1964 1965 xml_node_free(ctx->xml, mo); 1966 } 1967 1968 1969 static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname, 1970 const char *out_fname) 1971 { 1972 xml_node_t *tnds, *mo; 1973 1974 tnds = node_from_file(ctx->xml, in_fname); 1975 if (tnds == NULL) { 1976 wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname); 1977 return; 1978 } 1979 1980 mo = tnds_to_mo(ctx->xml, tnds); 1981 if (mo) { 1982 node_to_file(ctx->xml, out_fname, mo); 1983 xml_node_free(ctx->xml, mo); 1984 } 1985 1986 xml_node_free(ctx->xml, tnds); 1987 } 1988 1989 1990 struct osu_icon { 1991 int id; 1992 char lang[4]; 1993 char mime_type[256]; 1994 char filename[256]; 1995 }; 1996 1997 struct osu_data { 1998 char bssid[20]; 1999 char url[256]; 2000 unsigned int methods; 2001 char osu_ssid[33]; 2002 char osu_ssid2[33]; 2003 char osu_nai[256]; 2004 char osu_nai2[256]; 2005 struct osu_lang_text friendly_name[MAX_OSU_VALS]; 2006 size_t friendly_name_count; 2007 struct osu_lang_text serv_desc[MAX_OSU_VALS]; 2008 size_t serv_desc_count; 2009 struct osu_icon icon[MAX_OSU_VALS]; 2010 size_t icon_count; 2011 }; 2012 2013 2014 static struct osu_data * parse_osu_providers(const char *fname, size_t *count) 2015 { 2016 FILE *f; 2017 char buf[1000]; 2018 struct osu_data *osu = NULL, *last = NULL; 2019 size_t osu_count = 0; 2020 char *pos, *end; 2021 2022 f = fopen(fname, "r"); 2023 if (f == NULL) { 2024 wpa_printf(MSG_ERROR, "Could not open %s", fname); 2025 return NULL; 2026 } 2027 2028 while (fgets(buf, sizeof(buf), f)) { 2029 pos = strchr(buf, '\n'); 2030 if (pos) 2031 *pos = '\0'; 2032 2033 if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) { 2034 last = realloc(osu, (osu_count + 1) * sizeof(*osu)); 2035 if (last == NULL) 2036 break; 2037 osu = last; 2038 last = &osu[osu_count++]; 2039 memset(last, 0, sizeof(*last)); 2040 snprintf(last->bssid, sizeof(last->bssid), "%s", 2041 buf + 13); 2042 continue; 2043 } 2044 if (!last) 2045 continue; 2046 2047 if (strncmp(buf, "uri=", 4) == 0) { 2048 snprintf(last->url, sizeof(last->url), "%s", buf + 4); 2049 continue; 2050 } 2051 2052 if (strncmp(buf, "methods=", 8) == 0) { 2053 last->methods = strtol(buf + 8, NULL, 16); 2054 continue; 2055 } 2056 2057 if (strncmp(buf, "osu_ssid=", 9) == 0) { 2058 snprintf(last->osu_ssid, sizeof(last->osu_ssid), 2059 "%s", buf + 9); 2060 continue; 2061 } 2062 2063 if (strncmp(buf, "osu_ssid2=", 10) == 0) { 2064 snprintf(last->osu_ssid2, sizeof(last->osu_ssid2), 2065 "%s", buf + 10); 2066 continue; 2067 } 2068 2069 if (os_strncmp(buf, "osu_nai=", 8) == 0) { 2070 os_snprintf(last->osu_nai, sizeof(last->osu_nai), 2071 "%s", buf + 8); 2072 continue; 2073 } 2074 2075 if (os_strncmp(buf, "osu_nai2=", 9) == 0) { 2076 os_snprintf(last->osu_nai2, sizeof(last->osu_nai2), 2077 "%s", buf + 9); 2078 continue; 2079 } 2080 2081 if (strncmp(buf, "friendly_name=", 14) == 0) { 2082 struct osu_lang_text *txt; 2083 if (last->friendly_name_count == MAX_OSU_VALS) 2084 continue; 2085 pos = strchr(buf + 14, ':'); 2086 if (pos == NULL) 2087 continue; 2088 *pos++ = '\0'; 2089 txt = &last->friendly_name[last->friendly_name_count++]; 2090 snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 14); 2091 snprintf(txt->text, sizeof(txt->text), "%s", pos); 2092 } 2093 2094 if (strncmp(buf, "desc=", 5) == 0) { 2095 struct osu_lang_text *txt; 2096 if (last->serv_desc_count == MAX_OSU_VALS) 2097 continue; 2098 pos = strchr(buf + 5, ':'); 2099 if (pos == NULL) 2100 continue; 2101 *pos++ = '\0'; 2102 txt = &last->serv_desc[last->serv_desc_count++]; 2103 snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 5); 2104 snprintf(txt->text, sizeof(txt->text), "%s", pos); 2105 } 2106 2107 if (strncmp(buf, "icon=", 5) == 0) { 2108 struct osu_icon *icon; 2109 if (last->icon_count == MAX_OSU_VALS) 2110 continue; 2111 icon = &last->icon[last->icon_count++]; 2112 icon->id = atoi(buf + 5); 2113 pos = strchr(buf, ':'); 2114 if (pos == NULL) 2115 continue; 2116 pos = strchr(pos + 1, ':'); 2117 if (pos == NULL) 2118 continue; 2119 pos = strchr(pos + 1, ':'); 2120 if (pos == NULL) 2121 continue; 2122 pos++; 2123 end = strchr(pos, ':'); 2124 if (!end) 2125 continue; 2126 *end = '\0'; 2127 snprintf(icon->lang, sizeof(icon->lang), "%s", pos); 2128 pos = end + 1; 2129 2130 end = strchr(pos, ':'); 2131 if (end) 2132 *end = '\0'; 2133 snprintf(icon->mime_type, sizeof(icon->mime_type), 2134 "%s", pos); 2135 if (!pos) 2136 continue; 2137 pos = end + 1; 2138 2139 end = strchr(pos, ':'); 2140 if (end) 2141 *end = '\0'; 2142 snprintf(icon->filename, sizeof(icon->filename), 2143 "%s", pos); 2144 continue; 2145 } 2146 } 2147 2148 fclose(f); 2149 2150 *count = osu_count; 2151 return osu; 2152 } 2153 2154 2155 static int osu_connect(struct hs20_osu_client *ctx, const char *bssid, 2156 const char *ssid, const char *ssid2, const char *url, 2157 unsigned int methods, int no_prod_assoc, 2158 const char *osu_nai, const char *osu_nai2) 2159 { 2160 int id; 2161 const char *ifname = ctx->ifname; 2162 char buf[200]; 2163 struct wpa_ctrl *mon; 2164 int res; 2165 2166 if (ssid2 && ssid2[0] == '\0') 2167 ssid2 = NULL; 2168 2169 if (ctx->osu_ssid) { 2170 if (os_strcmp(ssid, ctx->osu_ssid) == 0) { 2171 wpa_printf(MSG_DEBUG, 2172 "Enforced OSU SSID matches ANQP info"); 2173 ssid2 = NULL; 2174 } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) { 2175 wpa_printf(MSG_DEBUG, 2176 "Enforced OSU SSID matches RSN[OSEN] info"); 2177 ssid = ssid2; 2178 } else { 2179 wpa_printf(MSG_INFO, "Enforced OSU SSID did not match"); 2180 write_summary(ctx, "Enforced OSU SSID did not match"); 2181 return -1; 2182 } 2183 } 2184 2185 id = add_network(ifname); 2186 if (id < 0) 2187 return -1; 2188 if (set_network_quoted(ifname, id, "ssid", ssid) < 0) 2189 return -1; 2190 if (ssid2) 2191 osu_nai = osu_nai2; 2192 if (osu_nai && os_strlen(osu_nai) > 0) { 2193 char dir[255], fname[300]; 2194 if (getcwd(dir, sizeof(dir)) == NULL) 2195 return -1; 2196 os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir); 2197 2198 if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0) 2199 return -1; 2200 2201 if (set_network(ifname, id, "proto", "OSEN") < 0 || 2202 set_network(ifname, id, "key_mgmt", "OSEN") < 0 || 2203 set_network(ifname, id, "pairwise", "CCMP") < 0 || 2204 set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 || 2205 set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 || 2206 set_network(ifname, id, "ocsp", "2") < 0 || 2207 set_network_quoted(ifname, id, "identity", osu_nai) < 0 || 2208 set_network_quoted(ifname, id, "ca_cert", fname) < 0) 2209 return -1; 2210 } else if (ssid2) { 2211 wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]"); 2212 write_summary(ctx, "No OSU_NAI set for RSN[OSEN]"); 2213 return -1; 2214 } else { 2215 if (set_network(ifname, id, "key_mgmt", "NONE") < 0) 2216 return -1; 2217 } 2218 2219 mon = open_wpa_mon(ifname); 2220 if (mon == NULL) 2221 return -1; 2222 2223 wpa_printf(MSG_INFO, "Associate with OSU SSID"); 2224 write_summary(ctx, "Associate with OSU SSID"); 2225 snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id); 2226 if (wpa_command(ifname, buf) < 0) 2227 return -1; 2228 2229 res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED", 2230 buf, sizeof(buf)); 2231 2232 wpa_ctrl_detach(mon); 2233 wpa_ctrl_close(mon); 2234 2235 if (res < 0) { 2236 wpa_printf(MSG_INFO, "Could not connect to OSU network"); 2237 write_summary(ctx, "Could not connect to OSU network"); 2238 wpa_printf(MSG_INFO, "Remove OSU network connection"); 2239 snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id); 2240 wpa_command(ifname, buf); 2241 return -1; 2242 } 2243 2244 write_summary(ctx, "Waiting for IP address for subscription registration"); 2245 if (wait_ip_addr(ifname, 15) < 0) { 2246 wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); 2247 } 2248 2249 if (no_prod_assoc) { 2250 if (res < 0) 2251 return -1; 2252 wpa_printf(MSG_INFO, "No production connection used for testing purposes"); 2253 write_summary(ctx, "No production connection used for testing purposes"); 2254 return 0; 2255 } 2256 2257 ctx->no_reconnect = 1; 2258 if (methods & 0x02) { 2259 wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect"); 2260 res = cmd_prov(ctx, url); 2261 } else if (methods & 0x01) { 2262 wpa_printf(MSG_DEBUG, 2263 "Calling cmd_oma_dm_prov from osu_connect"); 2264 res = cmd_oma_dm_prov(ctx, url); 2265 } 2266 2267 wpa_printf(MSG_INFO, "Remove OSU network connection"); 2268 write_summary(ctx, "Remove OSU network connection"); 2269 snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id); 2270 wpa_command(ifname, buf); 2271 2272 if (res < 0) 2273 return -1; 2274 2275 wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); 2276 write_summary(ctx, "Requesting reconnection with updated configuration"); 2277 if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) { 2278 wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect"); 2279 write_summary(ctx, "Failed to request wpa_supplicant to reconnect"); 2280 return -1; 2281 } 2282 2283 return 0; 2284 } 2285 2286 2287 static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir, 2288 int connect, int no_prod_assoc, 2289 const char *friendly_name) 2290 { 2291 char fname[255]; 2292 FILE *f; 2293 struct osu_data *osu = NULL, *last = NULL; 2294 size_t osu_count = 0, i, j; 2295 int ret; 2296 2297 write_summary(ctx, "OSU provider selection"); 2298 2299 if (dir == NULL) { 2300 wpa_printf(MSG_INFO, "Missing dir parameter to osu_select"); 2301 return -1; 2302 } 2303 2304 snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir); 2305 osu = parse_osu_providers(fname, &osu_count); 2306 if (osu == NULL) { 2307 wpa_printf(MSG_INFO, "Could not find any OSU providers from %s", 2308 fname); 2309 write_result(ctx, "No OSU providers available"); 2310 return -1; 2311 } 2312 2313 if (friendly_name) { 2314 for (i = 0; i < osu_count; i++) { 2315 last = &osu[i]; 2316 for (j = 0; j < last->friendly_name_count; j++) { 2317 if (os_strcmp(last->friendly_name[j].text, 2318 friendly_name) == 0) 2319 break; 2320 } 2321 if (j < last->friendly_name_count) 2322 break; 2323 } 2324 if (i == osu_count) { 2325 wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers", 2326 friendly_name); 2327 write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers", 2328 friendly_name); 2329 free(osu); 2330 return -1; 2331 } 2332 2333 wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'", 2334 friendly_name); 2335 write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'", 2336 friendly_name); 2337 ret = i + 1; 2338 goto selected; 2339 } 2340 2341 snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir); 2342 f = fopen(fname, "w"); 2343 if (f == NULL) { 2344 wpa_printf(MSG_INFO, "Could not open %s", fname); 2345 free(osu); 2346 return -1; 2347 } 2348 2349 fprintf(f, "<html><head>" 2350 "<meta http-equiv=\"Content-type\" content=\"text/html; " 2351 "charset=utf-8\"<title>Select service operator</title>" 2352 "</head><body><h1>Select service operator</h1>\n"); 2353 2354 if (osu_count == 0) 2355 fprintf(f, "No online signup available\n"); 2356 2357 for (i = 0; i < osu_count; i++) { 2358 last = &osu[i]; 2359 #ifdef ANDROID 2360 fprintf(f, "<p>\n" 2361 "<a href=\"http://localhost:12345/osu/%d\">" 2362 "<table><tr><td>", (int) i + 1); 2363 #else /* ANDROID */ 2364 fprintf(f, "<p>\n" 2365 "<a href=\"osu://%d\">" 2366 "<table><tr><td>", (int) i + 1); 2367 #endif /* ANDROID */ 2368 for (j = 0; j < last->icon_count; j++) { 2369 fprintf(f, "<img src=\"osu-icon-%d.%s\">\n", 2370 last->icon[j].id, 2371 strcasecmp(last->icon[j].mime_type, 2372 "image/png") == 0 ? "png" : "icon"); 2373 } 2374 fprintf(f, "<td>"); 2375 for (j = 0; j < last->friendly_name_count; j++) { 2376 fprintf(f, "<small>[%s]</small> %s<br>\n", 2377 last->friendly_name[j].lang, 2378 last->friendly_name[j].text); 2379 } 2380 fprintf(f, "<tr><td colspan=2>"); 2381 for (j = 0; j < last->serv_desc_count; j++) { 2382 fprintf(f, "<small>[%s]</small> %s<br>\n", 2383 last->serv_desc[j].lang, 2384 last->serv_desc[j].text); 2385 } 2386 fprintf(f, "</table></a><br><small>BSSID: %s<br>\n" 2387 "SSID: %s<br>\n", 2388 last->bssid, last->osu_ssid); 2389 if (last->osu_ssid2[0]) 2390 fprintf(f, "SSID2: %s<br>\n", last->osu_ssid2); 2391 if (last->osu_nai[0]) 2392 fprintf(f, "NAI: %s<br>\n", last->osu_nai); 2393 if (last->osu_nai2[0]) 2394 fprintf(f, "NAI2: %s<br>\n", last->osu_nai2); 2395 fprintf(f, "URL: %s<br>\n" 2396 "methods:%s%s<br>\n" 2397 "</small></p>\n", 2398 last->url, 2399 last->methods & 0x01 ? " OMA-DM" : "", 2400 last->methods & 0x02 ? " SOAP-XML-SPP" : ""); 2401 } 2402 2403 fprintf(f, "</body></html>\n"); 2404 2405 fclose(f); 2406 2407 snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir); 2408 write_summary(ctx, "Start web browser with OSU provider selection page"); 2409 ret = hs20_web_browser(fname, 0); 2410 2411 selected: 2412 if (ret > 0 && (size_t) ret <= osu_count) { 2413 char *data; 2414 size_t data_len; 2415 2416 wpa_printf(MSG_INFO, "Selected OSU id=%d", ret); 2417 last = &osu[ret - 1]; 2418 ret = 0; 2419 wpa_printf(MSG_INFO, "BSSID: %s", last->bssid); 2420 wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid); 2421 if (last->osu_ssid2[0]) 2422 wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2); 2423 wpa_printf(MSG_INFO, "URL: %s", last->url); 2424 write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s", 2425 ret, last->bssid, last->osu_ssid, last->url); 2426 2427 ctx->friendly_name_count = last->friendly_name_count; 2428 for (j = 0; j < last->friendly_name_count; j++) { 2429 wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s", 2430 last->friendly_name[j].lang, 2431 last->friendly_name[j].text); 2432 os_strlcpy(ctx->friendly_name[j].lang, 2433 last->friendly_name[j].lang, 2434 sizeof(ctx->friendly_name[j].lang)); 2435 os_strlcpy(ctx->friendly_name[j].text, 2436 last->friendly_name[j].text, 2437 sizeof(ctx->friendly_name[j].text)); 2438 } 2439 2440 ctx->icon_count = last->icon_count; 2441 for (j = 0; j < last->icon_count; j++) { 2442 char fname[256]; 2443 2444 os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s", 2445 dir, last->icon[j].id, 2446 strcasecmp(last->icon[j].mime_type, 2447 "image/png") == 0 ? 2448 "png" : "icon"); 2449 wpa_printf(MSG_INFO, "ICON: %s (%s)", 2450 fname, last->icon[j].filename); 2451 os_strlcpy(ctx->icon_filename[j], 2452 last->icon[j].filename, 2453 sizeof(ctx->icon_filename[j])); 2454 2455 data = os_readfile(fname, &data_len); 2456 if (data) { 2457 sha256_vector(1, (const u8 **) &data, &data_len, 2458 ctx->icon_hash[j]); 2459 os_free(data); 2460 } 2461 } 2462 2463 if (connect == 2) { 2464 if (last->methods & 0x02) { 2465 wpa_printf(MSG_DEBUG, 2466 "Calling cmd_prov from cmd_osu_select"); 2467 ret = cmd_prov(ctx, last->url); 2468 } else if (last->methods & 0x01) { 2469 wpa_printf(MSG_DEBUG, 2470 "Calling cmd_oma_dm_prov from cmd_osu_select"); 2471 ret = cmd_oma_dm_prov(ctx, last->url); 2472 } else { 2473 wpa_printf(MSG_DEBUG, 2474 "No supported OSU provisioning method"); 2475 ret = -1; 2476 } 2477 } else if (connect) { 2478 ret = osu_connect(ctx, last->bssid, last->osu_ssid, 2479 last->osu_ssid2, 2480 last->url, last->methods, 2481 no_prod_assoc, last->osu_nai, 2482 last->osu_nai2); 2483 } 2484 } else 2485 ret = -1; 2486 2487 free(osu); 2488 2489 return ret; 2490 } 2491 2492 2493 static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc, 2494 const char *friendly_name) 2495 { 2496 char dir[255]; 2497 char fname[300], buf[400]; 2498 struct wpa_ctrl *mon; 2499 const char *ifname; 2500 int res; 2501 2502 ifname = ctx->ifname; 2503 2504 if (getcwd(dir, sizeof(dir)) == NULL) 2505 return -1; 2506 2507 snprintf(fname, sizeof(fname), "%s/osu-info", dir); 2508 if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 && 2509 errno != EEXIST) { 2510 wpa_printf(MSG_INFO, "mkdir(%s) failed: %s", 2511 fname, strerror(errno)); 2512 return -1; 2513 } 2514 2515 android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 2516 2517 snprintf(buf, sizeof(buf), "SET osu_dir %s", fname); 2518 if (wpa_command(ifname, buf) < 0) { 2519 wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant"); 2520 return -1; 2521 } 2522 2523 mon = open_wpa_mon(ifname); 2524 if (mon == NULL) 2525 return -1; 2526 2527 wpa_printf(MSG_INFO, "Starting OSU fetch"); 2528 write_summary(ctx, "Starting OSU provider information fetch"); 2529 if (wpa_command(ifname, "FETCH_OSU") < 0) { 2530 wpa_printf(MSG_INFO, "Could not start OSU fetch"); 2531 wpa_ctrl_detach(mon); 2532 wpa_ctrl_close(mon); 2533 return -1; 2534 } 2535 res = get_wpa_cli_event(mon, "OSU provider fetch completed", 2536 buf, sizeof(buf)); 2537 2538 wpa_ctrl_detach(mon); 2539 wpa_ctrl_close(mon); 2540 2541 if (res < 0) { 2542 wpa_printf(MSG_INFO, "OSU fetch did not complete"); 2543 write_summary(ctx, "OSU fetch did not complete"); 2544 return -1; 2545 } 2546 wpa_printf(MSG_INFO, "OSU provider fetch completed"); 2547 2548 return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name); 2549 } 2550 2551 2552 static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address, 2553 const char *pps_fname, const char *ca_fname) 2554 { 2555 xml_node_t *pps, *node; 2556 char pps_fname_buf[300]; 2557 char ca_fname_buf[200]; 2558 char *cred_username = NULL; 2559 char *cred_password = NULL; 2560 char *sub_rem_uri = NULL; 2561 char client_cert_buf[200]; 2562 char *client_cert = NULL; 2563 char client_key_buf[200]; 2564 char *client_key = NULL; 2565 int spp; 2566 2567 wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s", 2568 address); 2569 2570 if (!pps_fname) { 2571 char buf[256]; 2572 wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); 2573 if (os_strncmp(address, "fqdn=", 5) == 0) { 2574 wpa_printf(MSG_INFO, "Use requested FQDN from command line"); 2575 os_snprintf(buf, sizeof(buf), "%s", address + 5); 2576 address = NULL; 2577 } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf, 2578 sizeof(buf)) < 0) { 2579 wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant"); 2580 return -1; 2581 } 2582 os_free(ctx->fqdn); 2583 ctx->fqdn = os_strdup(buf); 2584 if (ctx->fqdn == NULL) 2585 return -1; 2586 wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s", 2587 buf); 2588 os_snprintf(pps_fname_buf, sizeof(pps_fname_buf), 2589 "SP/%s/pps.xml", ctx->fqdn); 2590 pps_fname = pps_fname_buf; 2591 2592 os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem", 2593 ctx->fqdn); 2594 ca_fname = ca_fname_buf; 2595 } 2596 2597 if (!os_file_exists(pps_fname)) { 2598 wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible", 2599 pps_fname); 2600 return -1; 2601 } 2602 wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname); 2603 2604 if (ca_fname && !os_file_exists(ca_fname)) { 2605 wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible", 2606 ca_fname); 2607 return -1; 2608 } 2609 wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname); 2610 ctx->ca_fname = ca_fname; 2611 2612 pps = node_from_file(ctx->xml, pps_fname); 2613 if (pps == NULL) { 2614 wpa_printf(MSG_INFO, "Could not read PPS MO"); 2615 return -1; 2616 } 2617 2618 if (!ctx->fqdn) { 2619 char *tmp; 2620 node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); 2621 if (node == NULL) { 2622 wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS"); 2623 return -1; 2624 } 2625 tmp = xml_node_get_text(ctx->xml, node); 2626 if (tmp == NULL) { 2627 wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS"); 2628 return -1; 2629 } 2630 ctx->fqdn = os_strdup(tmp); 2631 xml_node_get_text_free(ctx->xml, tmp); 2632 if (!ctx->fqdn) { 2633 wpa_printf(MSG_INFO, "No FQDN known"); 2634 return -1; 2635 } 2636 } 2637 2638 node = get_child_node(ctx->xml, pps, 2639 "SubscriptionUpdate/UpdateMethod"); 2640 if (node) { 2641 char *tmp; 2642 tmp = xml_node_get_text(ctx->xml, node); 2643 if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0) 2644 spp = 0; 2645 else 2646 spp = 1; 2647 } else { 2648 wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP"); 2649 spp = 1; 2650 } 2651 2652 get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword", 2653 &cred_username, &cred_password); 2654 if (cred_username) 2655 wpa_printf(MSG_INFO, "Using username: %s", cred_username); 2656 if (cred_password) 2657 wpa_printf(MSG_DEBUG, "Using password: %s", cred_password); 2658 2659 if (cred_username == NULL && cred_password == NULL && 2660 get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) { 2661 wpa_printf(MSG_INFO, "Using client certificate"); 2662 os_snprintf(client_cert_buf, sizeof(client_cert_buf), 2663 "SP/%s/client-cert.pem", ctx->fqdn); 2664 client_cert = client_cert_buf; 2665 os_snprintf(client_key_buf, sizeof(client_key_buf), 2666 "SP/%s/client-key.pem", ctx->fqdn); 2667 client_key = client_key_buf; 2668 ctx->client_cert_present = 1; 2669 } 2670 2671 node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI"); 2672 if (node) { 2673 sub_rem_uri = xml_node_get_text(ctx->xml, node); 2674 if (sub_rem_uri && 2675 (!address || os_strcmp(address, sub_rem_uri) != 0)) { 2676 wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s", 2677 sub_rem_uri); 2678 address = sub_rem_uri; 2679 } 2680 } 2681 if (!address) { 2682 wpa_printf(MSG_INFO, "Server URL not known"); 2683 return -1; 2684 } 2685 2686 write_summary(ctx, "Wait for IP address for subscriptiom remediation"); 2687 wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation"); 2688 2689 if (wait_ip_addr(ctx->ifname, 15) < 0) { 2690 wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); 2691 } 2692 2693 if (spp) 2694 spp_sub_rem(ctx, address, pps_fname, 2695 client_cert, client_key, 2696 cred_username, cred_password, pps); 2697 else 2698 oma_dm_sub_rem(ctx, address, pps_fname, 2699 client_cert, client_key, 2700 cred_username, cred_password, pps); 2701 2702 xml_node_get_text_free(ctx->xml, sub_rem_uri); 2703 xml_node_get_text_free(ctx->xml, cred_username); 2704 str_clear_free(cred_password); 2705 xml_node_free(ctx->xml, pps); 2706 return 0; 2707 } 2708 2709 2710 static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address, 2711 const char *pps_fname, const char *ca_fname) 2712 { 2713 xml_node_t *pps; 2714 xml_node_t *node; 2715 char pps_fname_buf[300]; 2716 char ca_fname_buf[200]; 2717 char *uri = NULL; 2718 char *cred_username = NULL; 2719 char *cred_password = NULL; 2720 char client_cert_buf[200]; 2721 char *client_cert = NULL; 2722 char client_key_buf[200]; 2723 char *client_key = NULL; 2724 int spp; 2725 2726 wpa_printf(MSG_INFO, "Policy update requested"); 2727 2728 if (!pps_fname) { 2729 char buf[256]; 2730 int res; 2731 2732 wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); 2733 if (address && os_strncmp(address, "fqdn=", 5) == 0) { 2734 wpa_printf(MSG_INFO, "Use requested FQDN from command line"); 2735 os_snprintf(buf, sizeof(buf), "%s", address + 5); 2736 address = NULL; 2737 } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf, 2738 sizeof(buf)) < 0) { 2739 wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant"); 2740 return -1; 2741 } 2742 os_free(ctx->fqdn); 2743 ctx->fqdn = os_strdup(buf); 2744 if (ctx->fqdn == NULL) 2745 return -1; 2746 wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s", 2747 buf); 2748 os_snprintf(pps_fname_buf, sizeof(pps_fname_buf), 2749 "SP/%s/pps.xml", ctx->fqdn); 2750 pps_fname = pps_fname_buf; 2751 2752 res = os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), 2753 "SP/%s/ca.pem", buf); 2754 if (os_snprintf_error(sizeof(ca_fname_buf), res)) { 2755 os_free(ctx->fqdn); 2756 ctx->fqdn = NULL; 2757 return -1; 2758 } 2759 ca_fname = ca_fname_buf; 2760 } 2761 2762 if (!os_file_exists(pps_fname)) { 2763 wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible", 2764 pps_fname); 2765 return -1; 2766 } 2767 wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname); 2768 2769 if (ca_fname && !os_file_exists(ca_fname)) { 2770 wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible", 2771 ca_fname); 2772 return -1; 2773 } 2774 wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname); 2775 ctx->ca_fname = ca_fname; 2776 2777 pps = node_from_file(ctx->xml, pps_fname); 2778 if (pps == NULL) { 2779 wpa_printf(MSG_INFO, "Could not read PPS MO"); 2780 return -1; 2781 } 2782 2783 if (!ctx->fqdn) { 2784 char *tmp; 2785 node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); 2786 if (node == NULL) { 2787 wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS"); 2788 return -1; 2789 } 2790 tmp = xml_node_get_text(ctx->xml, node); 2791 if (tmp == NULL) { 2792 wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS"); 2793 return -1; 2794 } 2795 ctx->fqdn = os_strdup(tmp); 2796 xml_node_get_text_free(ctx->xml, tmp); 2797 if (!ctx->fqdn) { 2798 wpa_printf(MSG_INFO, "No FQDN known"); 2799 return -1; 2800 } 2801 } 2802 2803 node = get_child_node(ctx->xml, pps, 2804 "Policy/PolicyUpdate/UpdateMethod"); 2805 if (node) { 2806 char *tmp; 2807 tmp = xml_node_get_text(ctx->xml, node); 2808 if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0) 2809 spp = 0; 2810 else 2811 spp = 1; 2812 } else { 2813 wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP"); 2814 spp = 1; 2815 } 2816 2817 get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword", 2818 &cred_username, &cred_password); 2819 if (cred_username) 2820 wpa_printf(MSG_INFO, "Using username: %s", cred_username); 2821 if (cred_password) 2822 wpa_printf(MSG_DEBUG, "Using password: %s", cred_password); 2823 2824 if (cred_username == NULL && cred_password == NULL && 2825 get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) { 2826 wpa_printf(MSG_INFO, "Using client certificate"); 2827 os_snprintf(client_cert_buf, sizeof(client_cert_buf), 2828 "SP/%s/client-cert.pem", ctx->fqdn); 2829 client_cert = client_cert_buf; 2830 os_snprintf(client_key_buf, sizeof(client_key_buf), 2831 "SP/%s/client-key.pem", ctx->fqdn); 2832 client_key = client_key_buf; 2833 } 2834 2835 if (!address) { 2836 node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI"); 2837 if (node) { 2838 uri = xml_node_get_text(ctx->xml, node); 2839 wpa_printf(MSG_INFO, "URI based on PPS: %s", uri); 2840 address = uri; 2841 } 2842 } 2843 if (!address) { 2844 wpa_printf(MSG_INFO, "Server URL not known"); 2845 return -1; 2846 } 2847 2848 if (spp) 2849 spp_pol_upd(ctx, address, pps_fname, 2850 client_cert, client_key, 2851 cred_username, cred_password, pps); 2852 else 2853 oma_dm_pol_upd(ctx, address, pps_fname, 2854 client_cert, client_key, 2855 cred_username, cred_password, pps); 2856 2857 xml_node_get_text_free(ctx->xml, uri); 2858 xml_node_get_text_free(ctx->xml, cred_username); 2859 str_clear_free(cred_password); 2860 xml_node_free(ctx->xml, pps); 2861 2862 return 0; 2863 } 2864 2865 2866 static char * get_hostname(const char *url) 2867 { 2868 const char *pos, *end, *end2; 2869 char *ret; 2870 2871 if (url == NULL) 2872 return NULL; 2873 2874 pos = os_strchr(url, '/'); 2875 if (pos == NULL) 2876 return NULL; 2877 pos++; 2878 if (*pos != '/') 2879 return NULL; 2880 pos++; 2881 2882 end = os_strchr(pos, '/'); 2883 end2 = os_strchr(pos, ':'); 2884 if ((end && end2 && end2 < end) || (!end && end2)) 2885 end = end2; 2886 if (end) 2887 end--; 2888 else { 2889 end = pos; 2890 while (*end) 2891 end++; 2892 if (end > pos) 2893 end--; 2894 } 2895 2896 ret = os_malloc(end - pos + 2); 2897 if (ret == NULL) 2898 return NULL; 2899 2900 os_memcpy(ret, pos, end - pos + 1); 2901 ret[end - pos + 1] = '\0'; 2902 2903 return ret; 2904 } 2905 2906 2907 static int osu_cert_cb(void *_ctx, struct http_cert *cert) 2908 { 2909 struct hs20_osu_client *ctx = _ctx; 2910 size_t i, j; 2911 int found; 2912 char *host = NULL; 2913 2914 wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s)", 2915 !ctx->no_osu_cert_validation, ctx->server_url); 2916 2917 host = get_hostname(ctx->server_url); 2918 2919 for (i = 0; i < ctx->server_dnsname_count; i++) 2920 os_free(ctx->server_dnsname[i]); 2921 os_free(ctx->server_dnsname); 2922 ctx->server_dnsname = os_calloc(cert->num_dnsname, sizeof(char *)); 2923 ctx->server_dnsname_count = 0; 2924 2925 found = 0; 2926 for (i = 0; i < cert->num_dnsname; i++) { 2927 if (ctx->server_dnsname) { 2928 ctx->server_dnsname[ctx->server_dnsname_count] = 2929 os_strdup(cert->dnsname[i]); 2930 if (ctx->server_dnsname[ctx->server_dnsname_count]) 2931 ctx->server_dnsname_count++; 2932 } 2933 if (host && os_strcasecmp(host, cert->dnsname[i]) == 0) 2934 found = 1; 2935 wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]); 2936 } 2937 2938 if (host && !found) { 2939 wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection", 2940 host); 2941 write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection", 2942 host); 2943 os_free(host); 2944 return -1; 2945 } 2946 2947 os_free(host); 2948 2949 for (i = 0; i < cert->num_othername; i++) { 2950 if (os_strcmp(cert->othername[i].oid, 2951 "1.3.6.1.4.1.40808.1.1.1") == 0) { 2952 wpa_hexdump_ascii(MSG_INFO, 2953 "id-wfa-hotspot-friendlyName", 2954 cert->othername[i].data, 2955 cert->othername[i].len); 2956 } 2957 } 2958 2959 for (j = 0; !ctx->no_osu_cert_validation && 2960 j < ctx->friendly_name_count; j++) { 2961 int found = 0; 2962 for (i = 0; i < cert->num_othername; i++) { 2963 if (os_strcmp(cert->othername[i].oid, 2964 "1.3.6.1.4.1.40808.1.1.1") != 0) 2965 continue; 2966 if (cert->othername[i].len < 3) 2967 continue; 2968 if (os_strncasecmp((char *) cert->othername[i].data, 2969 ctx->friendly_name[j].lang, 3) != 0) 2970 continue; 2971 if (os_strncmp((char *) cert->othername[i].data + 3, 2972 ctx->friendly_name[j].text, 2973 cert->othername[i].len - 3) == 0) { 2974 found = 1; 2975 break; 2976 } 2977 } 2978 2979 if (!found) { 2980 wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'", 2981 ctx->friendly_name[j].lang, 2982 ctx->friendly_name[j].text); 2983 write_result(ctx, "No friendly name match found for '[%s]%s'", 2984 ctx->friendly_name[j].lang, 2985 ctx->friendly_name[j].text); 2986 return -1; 2987 } 2988 } 2989 2990 for (i = 0; i < cert->num_logo; i++) { 2991 struct http_logo *logo = &cert->logo[i]; 2992 2993 wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'", 2994 logo->alg_oid, logo->uri); 2995 wpa_hexdump_ascii(MSG_INFO, "hashValue", 2996 logo->hash, logo->hash_len); 2997 } 2998 2999 for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) { 3000 int found = 0; 3001 char *name = ctx->icon_filename[j]; 3002 size_t name_len = os_strlen(name); 3003 3004 wpa_printf(MSG_INFO, 3005 "[%zu] Looking for icon file name '%s' match", 3006 j, name); 3007 for (i = 0; i < cert->num_logo; i++) { 3008 struct http_logo *logo = &cert->logo[i]; 3009 size_t uri_len = os_strlen(logo->uri); 3010 char *pos; 3011 3012 wpa_printf(MSG_INFO, 3013 "[%zu] Comparing to '%s' uri_len=%d name_len=%d", 3014 i, logo->uri, (int) uri_len, (int) name_len); 3015 if (uri_len < 1 + name_len) { 3016 wpa_printf(MSG_INFO, "URI Length is too short"); 3017 continue; 3018 } 3019 pos = &logo->uri[uri_len - name_len - 1]; 3020 if (*pos != '/') 3021 continue; 3022 pos++; 3023 if (os_strcmp(pos, name) == 0) { 3024 found = 1; 3025 break; 3026 } 3027 } 3028 3029 if (!found) { 3030 wpa_printf(MSG_INFO, "No icon filename match found for '%s'", 3031 name); 3032 write_result(ctx, 3033 "No icon filename match found for '%s'", 3034 name); 3035 return -1; 3036 } 3037 } 3038 3039 for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) { 3040 int found = 0; 3041 3042 for (i = 0; i < cert->num_logo; i++) { 3043 struct http_logo *logo = &cert->logo[i]; 3044 3045 if (logo->hash_len != 32) { 3046 wpa_printf(MSG_INFO, 3047 "[%zu][%zu] Icon hash length invalid (should be 32): %d", 3048 j, i, (int) logo->hash_len); 3049 continue; 3050 } 3051 if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) { 3052 found = 1; 3053 break; 3054 } 3055 3056 wpa_printf(MSG_DEBUG, 3057 "[%zu][%zu] Icon hash did not match", j, i); 3058 wpa_hexdump_ascii(MSG_DEBUG, "logo->hash", 3059 logo->hash, 32); 3060 wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]", 3061 ctx->icon_hash[j], 32); 3062 } 3063 3064 if (!found) { 3065 wpa_printf(MSG_INFO, 3066 "No icon hash match (by hash) found"); 3067 write_result(ctx, 3068 "No icon hash match (by hash) found"); 3069 return -1; 3070 } 3071 } 3072 3073 return 0; 3074 } 3075 3076 3077 static int init_ctx(struct hs20_osu_client *ctx) 3078 { 3079 xml_node_t *devinfo, *devid; 3080 3081 os_memset(ctx, 0, sizeof(*ctx)); 3082 ctx->ifname = "wlan0"; 3083 ctx->xml = xml_node_init_ctx(ctx, NULL); 3084 if (ctx->xml == NULL) 3085 return -1; 3086 3087 devinfo = node_from_file(ctx->xml, "devinfo.xml"); 3088 if (devinfo) { 3089 devid = get_node(ctx->xml, devinfo, "DevId"); 3090 if (devid) { 3091 char *tmp = xml_node_get_text(ctx->xml, devid); 3092 3093 if (tmp) { 3094 ctx->devid = os_strdup(tmp); 3095 xml_node_get_text_free(ctx->xml, tmp); 3096 } 3097 } 3098 xml_node_free(ctx->xml, devinfo); 3099 } 3100 3101 ctx->http = http_init_ctx(ctx, ctx->xml); 3102 if (ctx->http == NULL) { 3103 xml_node_deinit_ctx(ctx->xml); 3104 return -1; 3105 } 3106 http_ocsp_set(ctx->http, 2); 3107 http_set_cert_cb(ctx->http, osu_cert_cb, ctx); 3108 3109 return 0; 3110 } 3111 3112 3113 static void deinit_ctx(struct hs20_osu_client *ctx) 3114 { 3115 size_t i; 3116 3117 http_deinit_ctx(ctx->http); 3118 xml_node_deinit_ctx(ctx->xml); 3119 os_free(ctx->fqdn); 3120 os_free(ctx->server_url); 3121 os_free(ctx->devid); 3122 3123 for (i = 0; i < ctx->server_dnsname_count; i++) 3124 os_free(ctx->server_dnsname[i]); 3125 os_free(ctx->server_dnsname); 3126 } 3127 3128 3129 static void check_workarounds(struct hs20_osu_client *ctx) 3130 { 3131 FILE *f; 3132 char buf[100]; 3133 unsigned long int val = 0; 3134 3135 f = fopen("hs20-osu-client.workarounds", "r"); 3136 if (f == NULL) 3137 return; 3138 3139 if (fgets(buf, sizeof(buf), f)) 3140 val = strtoul(buf, NULL, 16); 3141 3142 fclose(f); 3143 3144 if (val) { 3145 wpa_printf(MSG_INFO, "Workarounds enabled: 0x%lx", val); 3146 ctx->workarounds = val; 3147 if (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) 3148 http_ocsp_set(ctx->http, 1); 3149 } 3150 } 3151 3152 3153 static void usage(void) 3154 { 3155 printf("usage: hs20-osu-client [-dddqqKtT] [-S<station ifname>] \\\n" 3156 " [-w<wpa_supplicant ctrl_iface dir>] " 3157 "[-r<result file>] [-f<debug file>] \\\n" 3158 " [-s<summary file>] \\\n" 3159 " [-x<spp.xsd file name>] \\\n" 3160 " <command> [arguments..]\n" 3161 "commands:\n" 3162 "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n" 3163 "- to_tnds2 <XML MO> <XML MO in TNDS format (Path) " 3164 "[URN]>\n" 3165 "- from_tnds <XML MO in TNDS format> <XML MO>\n" 3166 "- set_pps <PerProviderSubscription XML file name>\n" 3167 "- get_fqdn <PerProviderSubscription XML file name>\n" 3168 "- pol_upd [Server URL] [PPS] [CA cert]\n" 3169 "- sub_rem <Server URL> [PPS] [CA cert]\n" 3170 "- prov <Server URL> [CA cert]\n" 3171 "- oma_dm_prov <Server URL> [CA cert]\n" 3172 "- sim_prov <Server URL> [CA cert]\n" 3173 "- oma_dm_sim_prov <Server URL> [CA cert]\n" 3174 "- signup [CA cert]\n" 3175 "- dl_osu_ca <PPS> <CA file>\n" 3176 "- dl_polupd_ca <PPS> <CA file>\n" 3177 "- dl_aaa_ca <PPS> <CA file>\n" 3178 "- browser <URL>\n" 3179 "- parse_cert <X.509 certificate (DER)>\n" 3180 "- osu_select <OSU info directory> [CA cert]\n"); 3181 } 3182 3183 3184 int main(int argc, char *argv[]) 3185 { 3186 struct hs20_osu_client ctx; 3187 int c; 3188 int ret = 0; 3189 int no_prod_assoc = 0; 3190 const char *friendly_name = NULL; 3191 const char *wpa_debug_file_path = NULL; 3192 extern char *wpas_ctrl_path; 3193 extern int wpa_debug_level; 3194 extern int wpa_debug_show_keys; 3195 extern int wpa_debug_timestamp; 3196 3197 if (init_ctx(&ctx) < 0) 3198 return -1; 3199 3200 for (;;) { 3201 c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tTw:x:"); 3202 if (c < 0) 3203 break; 3204 switch (c) { 3205 case 'd': 3206 if (wpa_debug_level > 0) 3207 wpa_debug_level--; 3208 break; 3209 case 'f': 3210 wpa_debug_file_path = optarg; 3211 break; 3212 case 'K': 3213 wpa_debug_show_keys++; 3214 break; 3215 case 'N': 3216 no_prod_assoc = 1; 3217 break; 3218 case 'o': 3219 ctx.osu_ssid = optarg; 3220 break; 3221 case 'O': 3222 friendly_name = optarg; 3223 break; 3224 case 'q': 3225 wpa_debug_level++; 3226 break; 3227 case 'r': 3228 ctx.result_file = optarg; 3229 break; 3230 case 's': 3231 ctx.summary_file = optarg; 3232 break; 3233 case 'S': 3234 ctx.ifname = optarg; 3235 break; 3236 case 't': 3237 wpa_debug_timestamp++; 3238 break; 3239 case 'T': 3240 ctx.ignore_tls = 1; 3241 break; 3242 case 'w': 3243 wpas_ctrl_path = optarg; 3244 break; 3245 case 'x': 3246 spp_xsd_fname = optarg; 3247 break; 3248 case 'h': 3249 default: 3250 usage(); 3251 exit(0); 3252 break; 3253 } 3254 } 3255 3256 if (argc - optind < 1) { 3257 usage(); 3258 exit(0); 3259 } 3260 3261 wpa_debug_open_file(wpa_debug_file_path); 3262 3263 #ifdef __linux__ 3264 setlinebuf(stdout); 3265 #endif /* __linux__ */ 3266 3267 if (ctx.result_file) 3268 unlink(ctx.result_file); 3269 wpa_printf(MSG_DEBUG, "===[hs20-osu-client START - command: %s ]======" 3270 "================", argv[optind]); 3271 check_workarounds(&ctx); 3272 3273 if (strcmp(argv[optind], "to_tnds") == 0) { 3274 if (argc - optind < 2) { 3275 usage(); 3276 exit(0); 3277 } 3278 cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2], 3279 argc > optind + 3 ? argv[optind + 3] : NULL, 3280 0); 3281 } else if (strcmp(argv[optind], "to_tnds2") == 0) { 3282 if (argc - optind < 2) { 3283 usage(); 3284 exit(0); 3285 } 3286 cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2], 3287 argc > optind + 3 ? argv[optind + 3] : NULL, 3288 1); 3289 } else if (strcmp(argv[optind], "from_tnds") == 0) { 3290 if (argc - optind < 2) { 3291 usage(); 3292 exit(0); 3293 } 3294 cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]); 3295 } else if (strcmp(argv[optind], "sub_rem") == 0) { 3296 if (argc - optind < 2) { 3297 usage(); 3298 exit(0); 3299 } 3300 ret = cmd_sub_rem(&ctx, argv[optind + 1], 3301 argc > optind + 2 ? argv[optind + 2] : NULL, 3302 argc > optind + 3 ? argv[optind + 3] : NULL); 3303 } else if (strcmp(argv[optind], "pol_upd") == 0) { 3304 ret = cmd_pol_upd(&ctx, 3305 argc > optind + 1 ? argv[optind + 1] : NULL, 3306 argc > optind + 2 ? argv[optind + 2] : NULL, 3307 argc > optind + 3 ? argv[optind + 3] : NULL); 3308 } else if (strcmp(argv[optind], "prov") == 0) { 3309 if (argc - optind < 2) { 3310 usage(); 3311 exit(0); 3312 } 3313 ctx.ca_fname = argv[optind + 2]; 3314 wpa_printf(MSG_DEBUG, "Calling cmd_prov from main"); 3315 cmd_prov(&ctx, argv[optind + 1]); 3316 } else if (strcmp(argv[optind], "sim_prov") == 0) { 3317 if (argc - optind < 2) { 3318 usage(); 3319 exit(0); 3320 } 3321 ctx.ca_fname = argv[optind + 2]; 3322 cmd_sim_prov(&ctx, argv[optind + 1]); 3323 } else if (strcmp(argv[optind], "dl_osu_ca") == 0) { 3324 if (argc - optind < 2) { 3325 usage(); 3326 exit(0); 3327 } 3328 cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]); 3329 } else if (strcmp(argv[optind], "dl_polupd_ca") == 0) { 3330 if (argc - optind < 2) { 3331 usage(); 3332 exit(0); 3333 } 3334 cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]); 3335 } else if (strcmp(argv[optind], "dl_aaa_ca") == 0) { 3336 if (argc - optind < 2) { 3337 usage(); 3338 exit(0); 3339 } 3340 cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]); 3341 } else if (strcmp(argv[optind], "osu_select") == 0) { 3342 if (argc - optind < 2) { 3343 usage(); 3344 exit(0); 3345 } 3346 ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL; 3347 cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL); 3348 } else if (strcmp(argv[optind], "signup") == 0) { 3349 ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL; 3350 ret = cmd_signup(&ctx, no_prod_assoc, friendly_name); 3351 } else if (strcmp(argv[optind], "set_pps") == 0) { 3352 if (argc - optind < 2) { 3353 usage(); 3354 exit(0); 3355 } 3356 cmd_set_pps(&ctx, argv[optind + 1]); 3357 } else if (strcmp(argv[optind], "get_fqdn") == 0) { 3358 if (argc - optind < 1) { 3359 usage(); 3360 exit(0); 3361 } 3362 ret = cmd_get_fqdn(&ctx, argv[optind + 1]); 3363 } else if (strcmp(argv[optind], "oma_dm_prov") == 0) { 3364 if (argc - optind < 2) { 3365 usage(); 3366 exit(0); 3367 } 3368 ctx.ca_fname = argv[optind + 2]; 3369 cmd_oma_dm_prov(&ctx, argv[optind + 1]); 3370 } else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) { 3371 if (argc - optind < 2) { 3372 usage(); 3373 exit(0); 3374 } 3375 ctx.ca_fname = argv[optind + 2]; 3376 if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) { 3377 write_summary(&ctx, "Failed to complete OMA DM SIM provisioning"); 3378 return -1; 3379 } 3380 } else if (strcmp(argv[optind], "oma_dm_add") == 0) { 3381 if (argc - optind < 2) { 3382 usage(); 3383 exit(0); 3384 } 3385 cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]); 3386 } else if (strcmp(argv[optind], "oma_dm_replace") == 0) { 3387 if (argc - optind < 2) { 3388 usage(); 3389 exit(0); 3390 } 3391 cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]); 3392 } else if (strcmp(argv[optind], "est_csr") == 0) { 3393 if (argc - optind < 2) { 3394 usage(); 3395 exit(0); 3396 } 3397 mkdir("Cert", S_IRWXU); 3398 est_build_csr(&ctx, argv[optind + 1]); 3399 } else if (strcmp(argv[optind], "browser") == 0) { 3400 int ret; 3401 3402 if (argc - optind < 2) { 3403 usage(); 3404 exit(0); 3405 } 3406 3407 wpa_printf(MSG_INFO, "Launch web browser to URL %s", 3408 argv[optind + 1]); 3409 ret = hs20_web_browser(argv[optind + 1], ctx.ignore_tls); 3410 wpa_printf(MSG_INFO, "Web browser result: %d", ret); 3411 } else if (strcmp(argv[optind], "parse_cert") == 0) { 3412 if (argc - optind < 2) { 3413 usage(); 3414 exit(0); 3415 } 3416 3417 wpa_debug_level = MSG_MSGDUMP; 3418 http_parse_x509_certificate(ctx.http, argv[optind + 1]); 3419 wpa_debug_level = MSG_INFO; 3420 } else { 3421 wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]); 3422 } 3423 3424 deinit_ctx(&ctx); 3425 wpa_printf(MSG_DEBUG, 3426 "===[hs20-osu-client END ]======================"); 3427 3428 wpa_debug_close_file(); 3429 3430 return ret; 3431 } 3432