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_quoted(ctx->ifname, id, "required_home_ois", 1235 homeoi) < 0) 1236 wpa_printf(MSG_INFO, 1237 "Failed to set cred required_home_ois"); 1238 } else { 1239 if (set_cred_quoted(ctx->ifname, id, "home_ois", homeoi) < 0) 1240 wpa_printf(MSG_INFO, "Failed to set cred home_ois"); 1241 } 1242 1243 xml_node_get_text_free(ctx->xml, homeoi); 1244 } 1245 1246 1247 static void set_pps_cred_home_sp_oi_list(struct hs20_osu_client *ctx, int id, 1248 xml_node_t *node) 1249 { 1250 xml_node_t *child; 1251 1252 wpa_printf(MSG_INFO, "- HomeSP/HomeOIList"); 1253 1254 xml_node_for_each_child(ctx->xml, child, node) { 1255 xml_node_for_each_check(ctx->xml, child); 1256 set_pps_cred_home_sp_oi(ctx, id, child); 1257 } 1258 } 1259 1260 1261 static void set_pps_cred_home_sp_other_partner(struct hs20_osu_client *ctx, 1262 int id, xml_node_t *node) 1263 { 1264 xml_node_t *child; 1265 const char *name; 1266 char *fqdn = NULL; 1267 1268 xml_node_for_each_child(ctx->xml, child, node) { 1269 xml_node_for_each_check(ctx->xml, child); 1270 name = xml_node_get_localname(ctx->xml, child); 1271 if (os_strcasecmp(name, "FQDN") == 0 && !fqdn) { 1272 fqdn = xml_node_get_text(ctx->xml, child); 1273 wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+>/FQDN = %s", 1274 fqdn); 1275 } else 1276 wpa_printf(MSG_INFO, "Unknown OtherHomePartners node '%s'", 1277 name); 1278 } 1279 1280 if (fqdn == NULL) { 1281 wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+> without FQDN ignored"); 1282 return; 1283 } 1284 1285 if (set_cred_quoted(ctx->ifname, id, "domain", fqdn) < 0) 1286 wpa_printf(MSG_INFO, "Failed to set cred domain for OtherHomePartners node"); 1287 1288 xml_node_get_text_free(ctx->xml, fqdn); 1289 } 1290 1291 1292 static void set_pps_cred_home_sp_other_partners(struct hs20_osu_client *ctx, 1293 int id, 1294 xml_node_t *node) 1295 { 1296 xml_node_t *child; 1297 1298 wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners"); 1299 1300 xml_node_for_each_child(ctx->xml, child, node) { 1301 xml_node_for_each_check(ctx->xml, child); 1302 set_pps_cred_home_sp_other_partner(ctx, id, child); 1303 } 1304 } 1305 1306 1307 static void set_pps_cred_home_sp_roaming_consortium_oi( 1308 struct hs20_osu_client *ctx, int id, xml_node_t *node) 1309 { 1310 char *str = xml_node_get_text(ctx->xml, node); 1311 if (str == NULL) 1312 return; 1313 wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str); 1314 if (set_cred_quoted(ctx->ifname, id, "roaming_consortiums", 1315 str) < 0) 1316 wpa_printf(MSG_INFO, "Failed to set cred roaming_consortiums"); 1317 xml_node_get_text_free(ctx->xml, str); 1318 } 1319 1320 1321 static void set_pps_cred_home_sp(struct hs20_osu_client *ctx, int id, 1322 xml_node_t *node) 1323 { 1324 xml_node_t *child; 1325 const char *name; 1326 1327 wpa_printf(MSG_INFO, "- HomeSP"); 1328 1329 xml_node_for_each_child(ctx->xml, child, node) { 1330 xml_node_for_each_check(ctx->xml, child); 1331 name = xml_node_get_localname(ctx->xml, child); 1332 if (os_strcasecmp(name, "NetworkID") == 0) 1333 set_pps_cred_home_sp_network_ids(ctx, id, child); 1334 else if (os_strcasecmp(name, "FriendlyName") == 0) 1335 set_pps_cred_home_sp_friendly_name(ctx, id, child); 1336 else if (os_strcasecmp(name, "IconURL") == 0) 1337 set_pps_cred_home_sp_icon_url(ctx, id, child); 1338 else if (os_strcasecmp(name, "FQDN") == 0) 1339 set_pps_cred_home_sp_fqdn(ctx, id, child); 1340 else if (os_strcasecmp(name, "HomeOIList") == 0) 1341 set_pps_cred_home_sp_oi_list(ctx, id, child); 1342 else if (os_strcasecmp(name, "OtherHomePartners") == 0) 1343 set_pps_cred_home_sp_other_partners(ctx, id, child); 1344 else if (os_strcasecmp(name, "RoamingConsortiumOI") == 0) 1345 set_pps_cred_home_sp_roaming_consortium_oi(ctx, id, 1346 child); 1347 else 1348 wpa_printf(MSG_INFO, "Unknown HomeSP node '%s'", name); 1349 } 1350 } 1351 1352 1353 static void set_pps_cred_sub_params(struct hs20_osu_client *ctx, int id, 1354 xml_node_t *node) 1355 { 1356 wpa_printf(MSG_INFO, "- SubscriptionParameters"); 1357 /* not used within wpa_supplicant */ 1358 } 1359 1360 1361 static void set_pps_cred_creation_date(struct hs20_osu_client *ctx, int id, 1362 xml_node_t *node) 1363 { 1364 char *str = xml_node_get_text(ctx->xml, node); 1365 if (str == NULL) 1366 return; 1367 wpa_printf(MSG_INFO, "- Credential/CreationDate = %s", str); 1368 /* not used within wpa_supplicant */ 1369 xml_node_get_text_free(ctx->xml, str); 1370 } 1371 1372 1373 static void set_pps_cred_expiration_date(struct hs20_osu_client *ctx, int id, 1374 xml_node_t *node) 1375 { 1376 char *str = xml_node_get_text(ctx->xml, node); 1377 if (str == NULL) 1378 return; 1379 wpa_printf(MSG_INFO, "- Credential/ExpirationDate = %s", str); 1380 /* not used within wpa_supplicant */ 1381 xml_node_get_text_free(ctx->xml, str); 1382 } 1383 1384 1385 static void set_pps_cred_username(struct hs20_osu_client *ctx, int id, 1386 xml_node_t *node) 1387 { 1388 char *str = xml_node_get_text(ctx->xml, node); 1389 if (str == NULL) 1390 return; 1391 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Username = %s", 1392 str); 1393 if (set_cred_quoted(ctx->ifname, id, "username", str) < 0) 1394 wpa_printf(MSG_INFO, "Failed to set cred username"); 1395 xml_node_get_text_free(ctx->xml, str); 1396 } 1397 1398 1399 static void set_pps_cred_password(struct hs20_osu_client *ctx, int id, 1400 xml_node_t *node) 1401 { 1402 int len, i; 1403 char *pw, *hex, *pos, *end; 1404 1405 pw = xml_node_get_base64_text(ctx->xml, node, &len); 1406 if (pw == NULL) 1407 return; 1408 1409 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Password = %s", pw); 1410 1411 hex = malloc(len * 2 + 1); 1412 if (hex == NULL) { 1413 free(pw); 1414 return; 1415 } 1416 end = hex + len * 2 + 1; 1417 pos = hex; 1418 for (i = 0; i < len; i++) { 1419 snprintf(pos, end - pos, "%02x", pw[i]); 1420 pos += 2; 1421 } 1422 free(pw); 1423 1424 if (set_cred(ctx->ifname, id, "password", hex) < 0) 1425 wpa_printf(MSG_INFO, "Failed to set cred password"); 1426 free(hex); 1427 } 1428 1429 1430 static void set_pps_cred_machine_managed(struct hs20_osu_client *ctx, int id, 1431 xml_node_t *node) 1432 { 1433 char *str = xml_node_get_text(ctx->xml, node); 1434 if (str == NULL) 1435 return; 1436 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/MachineManaged = %s", 1437 str); 1438 /* not used within wpa_supplicant */ 1439 xml_node_get_text_free(ctx->xml, str); 1440 } 1441 1442 1443 static void set_pps_cred_soft_token_app(struct hs20_osu_client *ctx, int id, 1444 xml_node_t *node) 1445 { 1446 char *str = xml_node_get_text(ctx->xml, node); 1447 if (str == NULL) 1448 return; 1449 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/SoftTokenApp = %s", 1450 str); 1451 /* not used within wpa_supplicant */ 1452 xml_node_get_text_free(ctx->xml, str); 1453 } 1454 1455 1456 static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id, 1457 xml_node_t *node) 1458 { 1459 char *str = xml_node_get_text(ctx->xml, node); 1460 if (str == NULL) 1461 return; 1462 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/AbleToShare = %s", 1463 str); 1464 /* not used within wpa_supplicant */ 1465 xml_node_get_text_free(ctx->xml, str); 1466 } 1467 1468 1469 static void set_pps_cred_eap_method_eap_type(struct hs20_osu_client *ctx, 1470 int id, xml_node_t *node) 1471 { 1472 char *str = xml_node_get_text(ctx->xml, node); 1473 int type; 1474 const char *eap_method = NULL; 1475 1476 if (!str) 1477 return; 1478 wpa_printf(MSG_INFO, 1479 "- Credential/UsernamePassword/EAPMethod/EAPType = %s", str); 1480 type = atoi(str); 1481 switch (type) { 1482 case EAP_TYPE_TLS: 1483 eap_method = "TLS"; 1484 break; 1485 case EAP_TYPE_TTLS: 1486 eap_method = "TTLS"; 1487 break; 1488 case EAP_TYPE_PEAP: 1489 eap_method = "PEAP"; 1490 break; 1491 case EAP_TYPE_PWD: 1492 eap_method = "PWD"; 1493 break; 1494 } 1495 xml_node_get_text_free(ctx->xml, str); 1496 if (!eap_method) { 1497 wpa_printf(MSG_INFO, "Unknown EAPType value"); 1498 return; 1499 } 1500 1501 if (set_cred(ctx->ifname, id, "eap", eap_method) < 0) 1502 wpa_printf(MSG_INFO, "Failed to set cred eap"); 1503 } 1504 1505 1506 static void set_pps_cred_eap_method_inner_method(struct hs20_osu_client *ctx, 1507 int id, xml_node_t *node) 1508 { 1509 char *str = xml_node_get_text(ctx->xml, node); 1510 const char *phase2 = NULL; 1511 1512 if (!str) 1513 return; 1514 wpa_printf(MSG_INFO, 1515 "- Credential/UsernamePassword/EAPMethod/InnerMethod = %s", 1516 str); 1517 if (os_strcmp(str, "PAP") == 0) 1518 phase2 = "auth=PAP"; 1519 else if (os_strcmp(str, "CHAP") == 0) 1520 phase2 = "auth=CHAP"; 1521 else if (os_strcmp(str, "MS-CHAP") == 0) 1522 phase2 = "auth=MSCHAP"; 1523 else if (os_strcmp(str, "MS-CHAP-V2") == 0) 1524 phase2 = "auth=MSCHAPV2"; 1525 xml_node_get_text_free(ctx->xml, str); 1526 if (!phase2) { 1527 wpa_printf(MSG_INFO, "Unknown InnerMethod value"); 1528 return; 1529 } 1530 1531 if (set_cred_quoted(ctx->ifname, id, "phase2", phase2) < 0) 1532 wpa_printf(MSG_INFO, "Failed to set cred phase2"); 1533 } 1534 1535 1536 static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id, 1537 xml_node_t *node) 1538 { 1539 xml_node_t *child; 1540 const char *name; 1541 1542 wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod"); 1543 1544 xml_node_for_each_child(ctx->xml, child, node) { 1545 xml_node_for_each_check(ctx->xml, child); 1546 name = xml_node_get_localname(ctx->xml, child); 1547 if (os_strcasecmp(name, "EAPType") == 0) 1548 set_pps_cred_eap_method_eap_type(ctx, id, child); 1549 else if (os_strcasecmp(name, "InnerMethod") == 0) 1550 set_pps_cred_eap_method_inner_method(ctx, id, child); 1551 else 1552 wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword/EAPMethod node '%s'", 1553 name); 1554 } 1555 } 1556 1557 1558 static void set_pps_cred_username_password(struct hs20_osu_client *ctx, int id, 1559 xml_node_t *node) 1560 { 1561 xml_node_t *child; 1562 const char *name; 1563 1564 wpa_printf(MSG_INFO, "- Credential/UsernamePassword"); 1565 1566 xml_node_for_each_child(ctx->xml, child, node) { 1567 xml_node_for_each_check(ctx->xml, child); 1568 name = xml_node_get_localname(ctx->xml, child); 1569 if (os_strcasecmp(name, "Username") == 0) 1570 set_pps_cred_username(ctx, id, child); 1571 else if (os_strcasecmp(name, "Password") == 0) 1572 set_pps_cred_password(ctx, id, child); 1573 else if (os_strcasecmp(name, "MachineManaged") == 0) 1574 set_pps_cred_machine_managed(ctx, id, child); 1575 else if (os_strcasecmp(name, "SoftTokenApp") == 0) 1576 set_pps_cred_soft_token_app(ctx, id, child); 1577 else if (os_strcasecmp(name, "AbleToShare") == 0) 1578 set_pps_cred_able_to_share(ctx, id, child); 1579 else if (os_strcasecmp(name, "EAPMethod") == 0) 1580 set_pps_cred_eap_method(ctx, id, child); 1581 else 1582 wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword node '%s'", 1583 name); 1584 } 1585 } 1586 1587 1588 static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id, 1589 xml_node_t *node, const char *fqdn) 1590 { 1591 char buf[200], dir[200]; 1592 int res; 1593 1594 wpa_printf(MSG_INFO, "- Credential/DigitalCertificate"); 1595 1596 if (getcwd(dir, sizeof(dir)) == NULL) 1597 return; 1598 1599 /* TODO: could build username from Subject of Subject AltName */ 1600 if (set_cred_quoted(ctx->ifname, id, "username", "cert") < 0) { 1601 wpa_printf(MSG_INFO, "Failed to set username"); 1602 } 1603 1604 res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, 1605 fqdn); 1606 if (os_snprintf_error(sizeof(buf), res)) 1607 return; 1608 if (os_file_exists(buf)) { 1609 if (set_cred_quoted(ctx->ifname, id, "client_cert", buf) < 0) { 1610 wpa_printf(MSG_INFO, "Failed to set client_cert"); 1611 } 1612 } 1613 1614 res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir, 1615 fqdn); 1616 if (os_snprintf_error(sizeof(buf), res)) 1617 return; 1618 if (os_file_exists(buf)) { 1619 if (set_cred_quoted(ctx->ifname, id, "private_key", buf) < 0) { 1620 wpa_printf(MSG_INFO, "Failed to set private_key"); 1621 } 1622 } 1623 } 1624 1625 1626 static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id, 1627 xml_node_t *node, const char *fqdn, int sim) 1628 { 1629 char *str = xml_node_get_text(ctx->xml, node); 1630 char buf[200], dir[200]; 1631 int res; 1632 1633 if (str == NULL) 1634 return; 1635 1636 wpa_printf(MSG_INFO, "- Credential/Realm = %s", str); 1637 if (set_cred_quoted(ctx->ifname, id, "realm", str) < 0) 1638 wpa_printf(MSG_INFO, "Failed to set cred realm"); 1639 xml_node_get_text_free(ctx->xml, str); 1640 1641 if (sim) 1642 return; 1643 1644 if (getcwd(dir, sizeof(dir)) == NULL) 1645 return; 1646 res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn); 1647 if (os_snprintf_error(sizeof(buf), res)) 1648 return; 1649 if (os_file_exists(buf)) { 1650 if (set_cred_quoted(ctx->ifname, id, "ca_cert", buf) < 0) { 1651 wpa_printf(MSG_INFO, "Failed to set CA cert"); 1652 } 1653 } 1654 } 1655 1656 1657 static void set_pps_cred_check_aaa_cert_status(struct hs20_osu_client *ctx, 1658 int id, xml_node_t *node) 1659 { 1660 char *str = xml_node_get_text(ctx->xml, node); 1661 1662 if (str == NULL) 1663 return; 1664 1665 wpa_printf(MSG_INFO, "- Credential/CheckAAAServerCertStatus = %s", str); 1666 if (os_strcasecmp(str, "true") == 0 && 1667 set_cred(ctx->ifname, id, "ocsp", "2") < 0) 1668 wpa_printf(MSG_INFO, "Failed to set cred ocsp"); 1669 xml_node_get_text_free(ctx->xml, str); 1670 } 1671 1672 1673 static void set_pps_cred_sim(struct hs20_osu_client *ctx, int id, 1674 xml_node_t *sim, xml_node_t *realm) 1675 { 1676 xml_node_t *node; 1677 char *imsi, *eaptype, *str, buf[20]; 1678 int type; 1679 int mnc_len = 3; 1680 size_t imsi_len; 1681 1682 node = get_node(ctx->xml, sim, "EAPType"); 1683 if (node == NULL) { 1684 wpa_printf(MSG_INFO, "No SIM/EAPType node in credential"); 1685 return; 1686 } 1687 eaptype = xml_node_get_text(ctx->xml, node); 1688 if (eaptype == NULL) { 1689 wpa_printf(MSG_INFO, "Could not extract SIM/EAPType"); 1690 return; 1691 } 1692 wpa_printf(MSG_INFO, " - Credential/SIM/EAPType = %s", eaptype); 1693 type = atoi(eaptype); 1694 xml_node_get_text_free(ctx->xml, eaptype); 1695 1696 switch (type) { 1697 case EAP_TYPE_SIM: 1698 if (set_cred(ctx->ifname, id, "eap", "SIM") < 0) 1699 wpa_printf(MSG_INFO, "Could not set eap=SIM"); 1700 break; 1701 case EAP_TYPE_AKA: 1702 if (set_cred(ctx->ifname, id, "eap", "AKA") < 0) 1703 wpa_printf(MSG_INFO, "Could not set eap=SIM"); 1704 break; 1705 case EAP_TYPE_AKA_PRIME: 1706 if (set_cred(ctx->ifname, id, "eap", "AKA'") < 0) 1707 wpa_printf(MSG_INFO, "Could not set eap=SIM"); 1708 break; 1709 default: 1710 wpa_printf(MSG_INFO, "Unsupported SIM/EAPType %d", type); 1711 return; 1712 } 1713 1714 node = get_node(ctx->xml, sim, "IMSI"); 1715 if (node == NULL) { 1716 wpa_printf(MSG_INFO, "No SIM/IMSI node in credential"); 1717 return; 1718 } 1719 imsi = xml_node_get_text(ctx->xml, node); 1720 if (imsi == NULL) { 1721 wpa_printf(MSG_INFO, "Could not extract SIM/IMSI"); 1722 return; 1723 } 1724 wpa_printf(MSG_INFO, " - Credential/SIM/IMSI = %s", imsi); 1725 imsi_len = os_strlen(imsi); 1726 if (imsi_len < 7 || imsi_len + 2 > sizeof(buf)) { 1727 wpa_printf(MSG_INFO, "Invalid IMSI length"); 1728 xml_node_get_text_free(ctx->xml, imsi); 1729 return; 1730 } 1731 1732 str = xml_node_get_text(ctx->xml, node); 1733 if (str) { 1734 char *pos; 1735 pos = os_strstr(str, "mnc"); 1736 if (pos && os_strlen(pos) >= 6) { 1737 if (os_strncmp(imsi + 3, pos + 3, 3) == 0) 1738 mnc_len = 3; 1739 else if (os_strncmp(imsi + 3, pos + 4, 2) == 0) 1740 mnc_len = 2; 1741 } 1742 xml_node_get_text_free(ctx->xml, str); 1743 } 1744 1745 os_memcpy(buf, imsi, 3 + mnc_len); 1746 buf[3 + mnc_len] = '-'; 1747 os_strlcpy(buf + 3 + mnc_len + 1, imsi + 3 + mnc_len, 1748 sizeof(buf) - 3 - mnc_len - 1); 1749 1750 xml_node_get_text_free(ctx->xml, imsi); 1751 1752 if (set_cred_quoted(ctx->ifname, id, "imsi", buf) < 0) 1753 wpa_printf(MSG_INFO, "Could not set IMSI"); 1754 1755 if (set_cred_quoted(ctx->ifname, id, "milenage", 1756 "90dca4eda45b53cf0f12d7c9c3bc6a89:" 1757 "cb9cccc4b9258e6dca4760379fb82581:000000000123") < 1758 0) 1759 wpa_printf(MSG_INFO, "Could not set Milenage parameters"); 1760 } 1761 1762 1763 static void set_pps_cred_credential(struct hs20_osu_client *ctx, int id, 1764 xml_node_t *node, const char *fqdn) 1765 { 1766 xml_node_t *child, *sim, *realm; 1767 const char *name; 1768 1769 wpa_printf(MSG_INFO, "- Credential"); 1770 1771 sim = get_node(ctx->xml, node, "SIM"); 1772 realm = get_node(ctx->xml, node, "Realm"); 1773 1774 xml_node_for_each_child(ctx->xml, child, node) { 1775 xml_node_for_each_check(ctx->xml, child); 1776 name = xml_node_get_localname(ctx->xml, child); 1777 if (os_strcasecmp(name, "CreationDate") == 0) 1778 set_pps_cred_creation_date(ctx, id, child); 1779 else if (os_strcasecmp(name, "ExpirationDate") == 0) 1780 set_pps_cred_expiration_date(ctx, id, child); 1781 else if (os_strcasecmp(name, "UsernamePassword") == 0) 1782 set_pps_cred_username_password(ctx, id, child); 1783 else if (os_strcasecmp(name, "DigitalCertificate") == 0) 1784 set_pps_cred_digital_cert(ctx, id, child, fqdn); 1785 else if (os_strcasecmp(name, "Realm") == 0) 1786 set_pps_cred_realm(ctx, id, child, fqdn, sim != NULL); 1787 else if (os_strcasecmp(name, "CheckAAAServerCertStatus") == 0) 1788 set_pps_cred_check_aaa_cert_status(ctx, id, child); 1789 else if (os_strcasecmp(name, "SIM") == 0) 1790 set_pps_cred_sim(ctx, id, child, realm); 1791 else 1792 wpa_printf(MSG_INFO, "Unknown Credential node '%s'", 1793 name); 1794 } 1795 } 1796 1797 1798 static void set_pps_credential(struct hs20_osu_client *ctx, int id, 1799 xml_node_t *cred, const char *fqdn) 1800 { 1801 xml_node_t *child; 1802 const char *name; 1803 1804 xml_node_for_each_child(ctx->xml, child, cred) { 1805 xml_node_for_each_check(ctx->xml, child); 1806 name = xml_node_get_localname(ctx->xml, child); 1807 if (os_strcasecmp(name, "Policy") == 0) 1808 set_pps_cred_policy(ctx, id, child); 1809 else if (os_strcasecmp(name, "CredentialPriority") == 0) 1810 set_pps_cred_priority(ctx, id, child); 1811 else if (os_strcasecmp(name, "AAAServerTrustRoot") == 0) 1812 set_pps_cred_aaa_server_trust_root(ctx, id, child); 1813 else if (os_strcasecmp(name, "SubscriptionUpdate") == 0) 1814 set_pps_cred_sub_update(ctx, id, child); 1815 else if (os_strcasecmp(name, "HomeSP") == 0) 1816 set_pps_cred_home_sp(ctx, id, child); 1817 else if (os_strcasecmp(name, "SubscriptionParameters") == 0) 1818 set_pps_cred_sub_params(ctx, id, child); 1819 else if (os_strcasecmp(name, "Credential") == 0) 1820 set_pps_cred_credential(ctx, id, child, fqdn); 1821 else 1822 wpa_printf(MSG_INFO, "Unknown credential node '%s'", 1823 name); 1824 } 1825 } 1826 1827 1828 static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps, 1829 const char *fqdn) 1830 { 1831 xml_node_t *child; 1832 const char *name; 1833 int id; 1834 char *update_identifier = NULL; 1835 1836 /* 1837 * TODO: Could consider more complex mechanism that would remove 1838 * credentials only if there are changes in the information sent to 1839 * wpa_supplicant. 1840 */ 1841 remove_sp_creds(ctx, fqdn); 1842 1843 xml_node_for_each_child(ctx->xml, child, pps) { 1844 xml_node_for_each_check(ctx->xml, child); 1845 name = xml_node_get_localname(ctx->xml, child); 1846 if (os_strcasecmp(name, "UpdateIdentifier") == 0) { 1847 update_identifier = xml_node_get_text(ctx->xml, child); 1848 if (update_identifier) { 1849 wpa_printf(MSG_INFO, "- UpdateIdentifier = %s", 1850 update_identifier); 1851 break; 1852 } 1853 } 1854 } 1855 1856 xml_node_for_each_child(ctx->xml, child, pps) { 1857 xml_node_for_each_check(ctx->xml, child); 1858 name = xml_node_get_localname(ctx->xml, child); 1859 if (os_strcasecmp(name, "UpdateIdentifier") == 0) 1860 continue; 1861 id = add_cred(ctx->ifname); 1862 if (id < 0) { 1863 wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant"); 1864 write_summary(ctx, "Failed to add credential to wpa_supplicant"); 1865 break; 1866 } 1867 write_summary(ctx, "Add a credential to wpa_supplicant"); 1868 if (update_identifier && 1869 set_cred(ctx->ifname, id, "update_identifier", 1870 update_identifier) < 0) 1871 wpa_printf(MSG_INFO, "Failed to set update_identifier"); 1872 if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) < 1873 0) 1874 wpa_printf(MSG_INFO, "Failed to set provisioning_sp"); 1875 wpa_printf(MSG_INFO, "credential localname: '%s'", name); 1876 set_pps_credential(ctx, id, child, fqdn); 1877 ctx->pps_cred_set = 1; 1878 } 1879 1880 xml_node_get_text_free(ctx->xml, update_identifier); 1881 } 1882 1883 1884 void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname) 1885 { 1886 xml_node_t *pps; 1887 const char *fqdn; 1888 char *fqdn_buf = NULL, *pos; 1889 1890 pps = node_from_file(ctx->xml, pps_fname); 1891 if (pps == NULL) { 1892 wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 1893 return; 1894 } 1895 1896 fqdn = os_strstr(pps_fname, "SP/"); 1897 if (fqdn) { 1898 fqdn_buf = os_strdup(fqdn + 3); 1899 if (fqdn_buf == NULL) 1900 return; 1901 pos = os_strchr(fqdn_buf, '/'); 1902 if (pos) 1903 *pos = '\0'; 1904 fqdn = fqdn_buf; 1905 } else 1906 fqdn = "wi-fi.org"; 1907 1908 wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s", 1909 fqdn); 1910 set_pps(ctx, pps, fqdn); 1911 1912 os_free(fqdn_buf); 1913 xml_node_free(ctx->xml, pps); 1914 } 1915 1916 1917 static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname) 1918 { 1919 xml_node_t *pps, *node; 1920 char *fqdn = NULL; 1921 1922 pps = node_from_file(ctx->xml, pps_fname); 1923 if (pps == NULL) { 1924 wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); 1925 return -1; 1926 } 1927 1928 node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); 1929 if (node) 1930 fqdn = xml_node_get_text(ctx->xml, node); 1931 1932 xml_node_free(ctx->xml, pps); 1933 1934 if (fqdn) { 1935 FILE *f = fopen("pps-fqdn", "w"); 1936 if (f) { 1937 fprintf(f, "%s", fqdn); 1938 fclose(f); 1939 } 1940 xml_node_get_text_free(ctx->xml, fqdn); 1941 return 0; 1942 } 1943 1944 xml_node_get_text_free(ctx->xml, fqdn); 1945 return -1; 1946 } 1947 1948 1949 static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname, 1950 const char *out_fname, const char *urn, int use_path) 1951 { 1952 xml_node_t *mo, *node; 1953 1954 mo = node_from_file(ctx->xml, in_fname); 1955 if (mo == NULL) { 1956 wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname); 1957 return; 1958 } 1959 1960 node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL); 1961 if (node) { 1962 node_to_file(ctx->xml, out_fname, node); 1963 xml_node_free(ctx->xml, node); 1964 } 1965 1966 xml_node_free(ctx->xml, mo); 1967 } 1968 1969 1970 static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname, 1971 const char *out_fname) 1972 { 1973 xml_node_t *tnds, *mo; 1974 1975 tnds = node_from_file(ctx->xml, in_fname); 1976 if (tnds == NULL) { 1977 wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname); 1978 return; 1979 } 1980 1981 mo = tnds_to_mo(ctx->xml, tnds); 1982 if (mo) { 1983 node_to_file(ctx->xml, out_fname, mo); 1984 xml_node_free(ctx->xml, mo); 1985 } 1986 1987 xml_node_free(ctx->xml, tnds); 1988 } 1989 1990 1991 struct osu_icon { 1992 int id; 1993 char lang[4]; 1994 char mime_type[256]; 1995 char filename[256]; 1996 }; 1997 1998 struct osu_data { 1999 char bssid[20]; 2000 char url[256]; 2001 unsigned int methods; 2002 char osu_ssid[33]; 2003 char osu_ssid2[33]; 2004 char osu_nai[256]; 2005 char osu_nai2[256]; 2006 struct osu_lang_text friendly_name[MAX_OSU_VALS]; 2007 size_t friendly_name_count; 2008 struct osu_lang_text serv_desc[MAX_OSU_VALS]; 2009 size_t serv_desc_count; 2010 struct osu_icon icon[MAX_OSU_VALS]; 2011 size_t icon_count; 2012 }; 2013 2014 2015 static struct osu_data * parse_osu_providers(const char *fname, size_t *count) 2016 { 2017 FILE *f; 2018 char buf[1000]; 2019 struct osu_data *osu = NULL, *last = NULL; 2020 size_t osu_count = 0; 2021 char *pos, *end; 2022 int res; 2023 2024 f = fopen(fname, "r"); 2025 if (f == NULL) { 2026 wpa_printf(MSG_ERROR, "Could not open %s", fname); 2027 return NULL; 2028 } 2029 2030 while (fgets(buf, sizeof(buf), f)) { 2031 pos = strchr(buf, '\n'); 2032 if (pos) 2033 *pos = '\0'; 2034 2035 if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) { 2036 last = realloc(osu, (osu_count + 1) * sizeof(*osu)); 2037 if (last == NULL) 2038 break; 2039 osu = last; 2040 last = &osu[osu_count++]; 2041 memset(last, 0, sizeof(*last)); 2042 res = os_snprintf(last->bssid, sizeof(last->bssid), 2043 "%s", buf + 13); 2044 if (os_snprintf_error(sizeof(last->bssid), res)) 2045 break; 2046 continue; 2047 } 2048 if (!last) 2049 continue; 2050 2051 if (strncmp(buf, "uri=", 4) == 0) { 2052 res = os_snprintf(last->url, sizeof(last->url), 2053 "%s", buf + 4); 2054 if (os_snprintf_error(sizeof(last->url), res)) 2055 break; 2056 continue; 2057 } 2058 2059 if (strncmp(buf, "methods=", 8) == 0) { 2060 last->methods = strtol(buf + 8, NULL, 16); 2061 continue; 2062 } 2063 2064 if (strncmp(buf, "osu_ssid=", 9) == 0) { 2065 res = os_snprintf(last->osu_ssid, 2066 sizeof(last->osu_ssid), 2067 "%s", buf + 9); 2068 if (os_snprintf_error(sizeof(last->osu_ssid), res)) 2069 break; 2070 continue; 2071 } 2072 2073 if (strncmp(buf, "osu_ssid2=", 10) == 0) { 2074 res = os_snprintf(last->osu_ssid2, 2075 sizeof(last->osu_ssid2), 2076 "%s", buf + 10); 2077 if (os_snprintf_error(sizeof(last->osu_ssid2), res)) 2078 break; 2079 continue; 2080 } 2081 2082 if (os_strncmp(buf, "osu_nai=", 8) == 0) { 2083 res = os_snprintf(last->osu_nai, sizeof(last->osu_nai), 2084 "%s", buf + 8); 2085 if (os_snprintf_error(sizeof(last->osu_nai), res)) 2086 break; 2087 continue; 2088 } 2089 2090 if (os_strncmp(buf, "osu_nai2=", 9) == 0) { 2091 res = os_snprintf(last->osu_nai2, 2092 sizeof(last->osu_nai2), 2093 "%s", buf + 9); 2094 if (os_snprintf_error(sizeof(last->osu_nai2), res)) 2095 break; 2096 continue; 2097 } 2098 2099 if (strncmp(buf, "friendly_name=", 14) == 0) { 2100 struct osu_lang_text *txt; 2101 if (last->friendly_name_count == MAX_OSU_VALS) 2102 continue; 2103 pos = strchr(buf + 14, ':'); 2104 if (pos == NULL) 2105 continue; 2106 *pos++ = '\0'; 2107 txt = &last->friendly_name[last->friendly_name_count++]; 2108 res = os_snprintf(txt->lang, sizeof(txt->lang), 2109 "%s", buf + 14); 2110 if (os_snprintf_error(sizeof(txt->lang), res)) 2111 break; 2112 res = os_snprintf(txt->text, sizeof(txt->text), 2113 "%s", pos); 2114 if (os_snprintf_error(sizeof(txt->text), res)) 2115 break; 2116 } 2117 2118 if (strncmp(buf, "desc=", 5) == 0) { 2119 struct osu_lang_text *txt; 2120 if (last->serv_desc_count == MAX_OSU_VALS) 2121 continue; 2122 pos = strchr(buf + 5, ':'); 2123 if (pos == NULL) 2124 continue; 2125 *pos++ = '\0'; 2126 txt = &last->serv_desc[last->serv_desc_count++]; 2127 res = os_snprintf(txt->lang, sizeof(txt->lang), 2128 "%s", buf + 5); 2129 if (os_snprintf_error(sizeof(txt->lang), res)) 2130 break; 2131 res = os_snprintf(txt->text, sizeof(txt->text), 2132 "%s", pos); 2133 if (os_snprintf_error(sizeof(txt->text), res)) 2134 break; 2135 } 2136 2137 if (strncmp(buf, "icon=", 5) == 0) { 2138 struct osu_icon *icon; 2139 if (last->icon_count == MAX_OSU_VALS) 2140 continue; 2141 icon = &last->icon[last->icon_count++]; 2142 icon->id = atoi(buf + 5); 2143 pos = strchr(buf, ':'); 2144 if (pos == NULL) 2145 continue; 2146 pos = strchr(pos + 1, ':'); 2147 if (pos == NULL) 2148 continue; 2149 pos = strchr(pos + 1, ':'); 2150 if (pos == NULL) 2151 continue; 2152 pos++; 2153 end = strchr(pos, ':'); 2154 if (!end) 2155 continue; 2156 *end = '\0'; 2157 res = os_snprintf(icon->lang, sizeof(icon->lang), 2158 "%s", pos); 2159 if (os_snprintf_error(sizeof(icon->lang), res)) 2160 break; 2161 pos = end + 1; 2162 2163 end = strchr(pos, ':'); 2164 if (end) 2165 *end = '\0'; 2166 res = os_snprintf(icon->mime_type, 2167 sizeof(icon->mime_type), "%s", pos); 2168 if (os_snprintf_error(sizeof(icon->mime_type), res)) 2169 break; 2170 if (!end) 2171 continue; 2172 pos = end + 1; 2173 2174 end = strchr(pos, ':'); 2175 if (end) 2176 *end = '\0'; 2177 res = os_snprintf(icon->filename, 2178 sizeof(icon->filename), "%s", pos); 2179 if (os_snprintf_error(sizeof(icon->filename), res)) 2180 break; 2181 continue; 2182 } 2183 } 2184 2185 fclose(f); 2186 2187 *count = osu_count; 2188 return osu; 2189 } 2190 2191 2192 static int osu_connect(struct hs20_osu_client *ctx, const char *bssid, 2193 const char *ssid, const char *ssid2, const char *url, 2194 unsigned int methods, int no_prod_assoc, 2195 const char *osu_nai, const char *osu_nai2) 2196 { 2197 int id; 2198 const char *ifname = ctx->ifname; 2199 char buf[200]; 2200 struct wpa_ctrl *mon; 2201 int res; 2202 2203 if (ssid2 && ssid2[0] == '\0') 2204 ssid2 = NULL; 2205 2206 if (ctx->osu_ssid) { 2207 if (os_strcmp(ssid, ctx->osu_ssid) == 0) { 2208 wpa_printf(MSG_DEBUG, 2209 "Enforced OSU SSID matches ANQP info"); 2210 ssid2 = NULL; 2211 } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) { 2212 wpa_printf(MSG_DEBUG, 2213 "Enforced OSU SSID matches RSN[OSEN] info"); 2214 ssid = ssid2; 2215 } else { 2216 wpa_printf(MSG_INFO, "Enforced OSU SSID did not match"); 2217 write_summary(ctx, "Enforced OSU SSID did not match"); 2218 return -1; 2219 } 2220 } 2221 2222 id = add_network(ifname); 2223 if (id < 0) 2224 return -1; 2225 if (set_network_quoted(ifname, id, "ssid", ssid) < 0) 2226 return -1; 2227 if (ssid2) 2228 osu_nai = osu_nai2; 2229 if (osu_nai && os_strlen(osu_nai) > 0) { 2230 char dir[255], fname[300]; 2231 if (getcwd(dir, sizeof(dir)) == NULL) 2232 return -1; 2233 os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir); 2234 2235 if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0) 2236 return -1; 2237 2238 if (set_network(ifname, id, "proto", "OSEN") < 0 || 2239 set_network(ifname, id, "key_mgmt", "OSEN") < 0 || 2240 set_network(ifname, id, "pairwise", "CCMP") < 0 || 2241 set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 || 2242 set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 || 2243 set_network(ifname, id, "ocsp", "2") < 0 || 2244 set_network_quoted(ifname, id, "identity", osu_nai) < 0 || 2245 set_network_quoted(ifname, id, "ca_cert", fname) < 0) 2246 return -1; 2247 } else if (ssid2) { 2248 wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]"); 2249 write_summary(ctx, "No OSU_NAI set for RSN[OSEN]"); 2250 return -1; 2251 } else { 2252 if (set_network(ifname, id, "key_mgmt", "NONE") < 0) 2253 return -1; 2254 } 2255 2256 mon = open_wpa_mon(ifname); 2257 if (mon == NULL) 2258 return -1; 2259 2260 wpa_printf(MSG_INFO, "Associate with OSU SSID"); 2261 write_summary(ctx, "Associate with OSU SSID"); 2262 snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id); 2263 if (wpa_command(ifname, buf) < 0) 2264 return -1; 2265 2266 res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED", 2267 buf, sizeof(buf)); 2268 2269 wpa_ctrl_detach(mon); 2270 wpa_ctrl_close(mon); 2271 2272 if (res < 0) { 2273 wpa_printf(MSG_INFO, "Could not connect to OSU network"); 2274 write_summary(ctx, "Could not connect to OSU network"); 2275 wpa_printf(MSG_INFO, "Remove OSU network connection"); 2276 snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id); 2277 wpa_command(ifname, buf); 2278 return -1; 2279 } 2280 2281 write_summary(ctx, "Waiting for IP address for subscription registration"); 2282 if (wait_ip_addr(ifname, 15) < 0) { 2283 wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); 2284 } 2285 2286 if (no_prod_assoc) { 2287 if (res < 0) 2288 return -1; 2289 wpa_printf(MSG_INFO, "No production connection used for testing purposes"); 2290 write_summary(ctx, "No production connection used for testing purposes"); 2291 return 0; 2292 } 2293 2294 ctx->no_reconnect = 1; 2295 if (methods & 0x02) { 2296 wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect"); 2297 res = cmd_prov(ctx, url); 2298 } else if (methods & 0x01) { 2299 wpa_printf(MSG_DEBUG, 2300 "Calling cmd_oma_dm_prov from osu_connect"); 2301 res = cmd_oma_dm_prov(ctx, url); 2302 } 2303 2304 wpa_printf(MSG_INFO, "Remove OSU network connection"); 2305 write_summary(ctx, "Remove OSU network connection"); 2306 snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id); 2307 wpa_command(ifname, buf); 2308 2309 if (res < 0) 2310 return -1; 2311 2312 wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); 2313 write_summary(ctx, "Requesting reconnection with updated configuration"); 2314 if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) { 2315 wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect"); 2316 write_summary(ctx, "Failed to request wpa_supplicant to reconnect"); 2317 return -1; 2318 } 2319 2320 return 0; 2321 } 2322 2323 2324 static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir, 2325 int connect, int no_prod_assoc, 2326 const char *friendly_name) 2327 { 2328 char fname[255]; 2329 FILE *f; 2330 struct osu_data *osu = NULL, *last = NULL; 2331 size_t osu_count = 0, i, j; 2332 int ret; 2333 2334 write_summary(ctx, "OSU provider selection"); 2335 2336 if (dir == NULL) { 2337 wpa_printf(MSG_INFO, "Missing dir parameter to osu_select"); 2338 return -1; 2339 } 2340 2341 snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir); 2342 osu = parse_osu_providers(fname, &osu_count); 2343 if (osu == NULL) { 2344 wpa_printf(MSG_INFO, "Could not find any OSU providers from %s", 2345 fname); 2346 write_result(ctx, "No OSU providers available"); 2347 return -1; 2348 } 2349 2350 if (friendly_name) { 2351 for (i = 0; i < osu_count; i++) { 2352 last = &osu[i]; 2353 for (j = 0; j < last->friendly_name_count; j++) { 2354 if (os_strcmp(last->friendly_name[j].text, 2355 friendly_name) == 0) 2356 break; 2357 } 2358 if (j < last->friendly_name_count) 2359 break; 2360 } 2361 if (i == osu_count) { 2362 wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers", 2363 friendly_name); 2364 write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers", 2365 friendly_name); 2366 free(osu); 2367 return -1; 2368 } 2369 2370 wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'", 2371 friendly_name); 2372 write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'", 2373 friendly_name); 2374 ret = i + 1; 2375 goto selected; 2376 } 2377 2378 snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir); 2379 f = fopen(fname, "w"); 2380 if (f == NULL) { 2381 wpa_printf(MSG_INFO, "Could not open %s", fname); 2382 free(osu); 2383 return -1; 2384 } 2385 2386 fprintf(f, "<html><head>" 2387 "<meta http-equiv=\"Content-type\" content=\"text/html; " 2388 "charset=utf-8\"<title>Select service operator</title>" 2389 "</head><body><h1>Select service operator</h1>\n"); 2390 2391 if (osu_count == 0) 2392 fprintf(f, "No online signup available\n"); 2393 2394 for (i = 0; i < osu_count; i++) { 2395 last = &osu[i]; 2396 #ifdef ANDROID 2397 fprintf(f, "<p>\n" 2398 "<a href=\"http://localhost:12345/osu/%d\">" 2399 "<table><tr><td>", (int) i + 1); 2400 #else /* ANDROID */ 2401 fprintf(f, "<p>\n" 2402 "<a href=\"osu://%d\">" 2403 "<table><tr><td>", (int) i + 1); 2404 #endif /* ANDROID */ 2405 for (j = 0; j < last->icon_count; j++) { 2406 fprintf(f, "<img src=\"osu-icon-%d.%s\">\n", 2407 last->icon[j].id, 2408 strcasecmp(last->icon[j].mime_type, 2409 "image/png") == 0 ? "png" : "icon"); 2410 } 2411 fprintf(f, "<td>"); 2412 for (j = 0; j < last->friendly_name_count; j++) { 2413 fprintf(f, "<small>[%s]</small> %s<br>\n", 2414 last->friendly_name[j].lang, 2415 last->friendly_name[j].text); 2416 } 2417 fprintf(f, "<tr><td colspan=2>"); 2418 for (j = 0; j < last->serv_desc_count; j++) { 2419 fprintf(f, "<small>[%s]</small> %s<br>\n", 2420 last->serv_desc[j].lang, 2421 last->serv_desc[j].text); 2422 } 2423 fprintf(f, "</table></a><br><small>BSSID: %s<br>\n" 2424 "SSID: %s<br>\n", 2425 last->bssid, last->osu_ssid); 2426 if (last->osu_ssid2[0]) 2427 fprintf(f, "SSID2: %s<br>\n", last->osu_ssid2); 2428 if (last->osu_nai[0]) 2429 fprintf(f, "NAI: %s<br>\n", last->osu_nai); 2430 if (last->osu_nai2[0]) 2431 fprintf(f, "NAI2: %s<br>\n", last->osu_nai2); 2432 fprintf(f, "URL: %s<br>\n" 2433 "methods:%s%s<br>\n" 2434 "</small></p>\n", 2435 last->url, 2436 last->methods & 0x01 ? " OMA-DM" : "", 2437 last->methods & 0x02 ? " SOAP-XML-SPP" : ""); 2438 } 2439 2440 fprintf(f, "</body></html>\n"); 2441 2442 fclose(f); 2443 2444 snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir); 2445 write_summary(ctx, "Start web browser with OSU provider selection page"); 2446 ret = hs20_web_browser(fname, 0); 2447 2448 selected: 2449 if (ret > 0 && (size_t) ret <= osu_count) { 2450 char *data; 2451 size_t data_len; 2452 2453 wpa_printf(MSG_INFO, "Selected OSU id=%d", ret); 2454 last = &osu[ret - 1]; 2455 ret = 0; 2456 wpa_printf(MSG_INFO, "BSSID: %s", last->bssid); 2457 wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid); 2458 if (last->osu_ssid2[0]) 2459 wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2); 2460 wpa_printf(MSG_INFO, "URL: %s", last->url); 2461 write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s", 2462 ret, last->bssid, last->osu_ssid, last->url); 2463 2464 ctx->friendly_name_count = last->friendly_name_count; 2465 for (j = 0; j < last->friendly_name_count; j++) { 2466 wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s", 2467 last->friendly_name[j].lang, 2468 last->friendly_name[j].text); 2469 os_strlcpy(ctx->friendly_name[j].lang, 2470 last->friendly_name[j].lang, 2471 sizeof(ctx->friendly_name[j].lang)); 2472 os_strlcpy(ctx->friendly_name[j].text, 2473 last->friendly_name[j].text, 2474 sizeof(ctx->friendly_name[j].text)); 2475 } 2476 2477 ctx->icon_count = last->icon_count; 2478 for (j = 0; j < last->icon_count; j++) { 2479 char fname[256]; 2480 2481 os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s", 2482 dir, last->icon[j].id, 2483 strcasecmp(last->icon[j].mime_type, 2484 "image/png") == 0 ? 2485 "png" : "icon"); 2486 wpa_printf(MSG_INFO, "ICON: %s (%s)", 2487 fname, last->icon[j].filename); 2488 os_strlcpy(ctx->icon_filename[j], 2489 last->icon[j].filename, 2490 sizeof(ctx->icon_filename[j])); 2491 2492 data = os_readfile(fname, &data_len); 2493 if (data) { 2494 sha256_vector(1, (const u8 **) &data, &data_len, 2495 ctx->icon_hash[j]); 2496 os_free(data); 2497 } 2498 } 2499 2500 if (connect == 2) { 2501 if (last->methods & 0x02) { 2502 wpa_printf(MSG_DEBUG, 2503 "Calling cmd_prov from cmd_osu_select"); 2504 ret = cmd_prov(ctx, last->url); 2505 } else if (last->methods & 0x01) { 2506 wpa_printf(MSG_DEBUG, 2507 "Calling cmd_oma_dm_prov from cmd_osu_select"); 2508 ret = cmd_oma_dm_prov(ctx, last->url); 2509 } else { 2510 wpa_printf(MSG_DEBUG, 2511 "No supported OSU provisioning method"); 2512 ret = -1; 2513 } 2514 } else if (connect) { 2515 ret = osu_connect(ctx, last->bssid, last->osu_ssid, 2516 last->osu_ssid2, 2517 last->url, last->methods, 2518 no_prod_assoc, last->osu_nai, 2519 last->osu_nai2); 2520 } 2521 } else 2522 ret = -1; 2523 2524 free(osu); 2525 2526 return ret; 2527 } 2528 2529 2530 static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc, 2531 const char *friendly_name) 2532 { 2533 char dir[255]; 2534 char fname[300], buf[400]; 2535 struct wpa_ctrl *mon; 2536 const char *ifname; 2537 int res; 2538 2539 ifname = ctx->ifname; 2540 2541 if (getcwd(dir, sizeof(dir)) == NULL) 2542 return -1; 2543 2544 snprintf(fname, sizeof(fname), "%s/osu-info", dir); 2545 if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 && 2546 errno != EEXIST) { 2547 wpa_printf(MSG_INFO, "mkdir(%s) failed: %s", 2548 fname, strerror(errno)); 2549 return -1; 2550 } 2551 2552 android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 2553 2554 snprintf(buf, sizeof(buf), "SET osu_dir %s", fname); 2555 if (wpa_command(ifname, buf) < 0) { 2556 wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant"); 2557 return -1; 2558 } 2559 2560 mon = open_wpa_mon(ifname); 2561 if (mon == NULL) 2562 return -1; 2563 2564 wpa_printf(MSG_INFO, "Starting OSU fetch"); 2565 write_summary(ctx, "Starting OSU provider information fetch"); 2566 if (wpa_command(ifname, "FETCH_OSU") < 0) { 2567 wpa_printf(MSG_INFO, "Could not start OSU fetch"); 2568 wpa_ctrl_detach(mon); 2569 wpa_ctrl_close(mon); 2570 return -1; 2571 } 2572 res = get_wpa_cli_event(mon, "OSU provider fetch completed", 2573 buf, sizeof(buf)); 2574 2575 wpa_ctrl_detach(mon); 2576 wpa_ctrl_close(mon); 2577 2578 if (res < 0) { 2579 wpa_printf(MSG_INFO, "OSU fetch did not complete"); 2580 write_summary(ctx, "OSU fetch did not complete"); 2581 return -1; 2582 } 2583 wpa_printf(MSG_INFO, "OSU provider fetch completed"); 2584 2585 return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name); 2586 } 2587 2588 2589 static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address, 2590 const char *pps_fname, const char *ca_fname) 2591 { 2592 xml_node_t *pps, *node; 2593 char pps_fname_buf[300]; 2594 char ca_fname_buf[200]; 2595 char *cred_username = NULL; 2596 char *cred_password = NULL; 2597 char *sub_rem_uri = NULL; 2598 char client_cert_buf[200]; 2599 char *client_cert = NULL; 2600 char client_key_buf[200]; 2601 char *client_key = NULL; 2602 int spp; 2603 2604 wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s", 2605 address); 2606 2607 if (!pps_fname) { 2608 char buf[256]; 2609 wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); 2610 if (os_strncmp(address, "fqdn=", 5) == 0) { 2611 wpa_printf(MSG_INFO, "Use requested FQDN from command line"); 2612 os_snprintf(buf, sizeof(buf), "%s", address + 5); 2613 address = NULL; 2614 } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf, 2615 sizeof(buf)) < 0) { 2616 wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant"); 2617 return -1; 2618 } 2619 os_free(ctx->fqdn); 2620 ctx->fqdn = os_strdup(buf); 2621 if (ctx->fqdn == NULL) 2622 return -1; 2623 wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s", 2624 buf); 2625 os_snprintf(pps_fname_buf, sizeof(pps_fname_buf), 2626 "SP/%s/pps.xml", ctx->fqdn); 2627 pps_fname = pps_fname_buf; 2628 2629 os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem", 2630 ctx->fqdn); 2631 ca_fname = ca_fname_buf; 2632 } 2633 2634 if (!os_file_exists(pps_fname)) { 2635 wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible", 2636 pps_fname); 2637 return -1; 2638 } 2639 wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname); 2640 2641 if (ca_fname && !os_file_exists(ca_fname)) { 2642 wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible", 2643 ca_fname); 2644 return -1; 2645 } 2646 wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname); 2647 ctx->ca_fname = ca_fname; 2648 2649 pps = node_from_file(ctx->xml, pps_fname); 2650 if (pps == NULL) { 2651 wpa_printf(MSG_INFO, "Could not read PPS MO"); 2652 return -1; 2653 } 2654 2655 if (!ctx->fqdn) { 2656 char *tmp; 2657 node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); 2658 if (node == NULL) { 2659 wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS"); 2660 return -1; 2661 } 2662 tmp = xml_node_get_text(ctx->xml, node); 2663 if (tmp == NULL) { 2664 wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS"); 2665 return -1; 2666 } 2667 ctx->fqdn = os_strdup(tmp); 2668 xml_node_get_text_free(ctx->xml, tmp); 2669 if (!ctx->fqdn) { 2670 wpa_printf(MSG_INFO, "No FQDN known"); 2671 return -1; 2672 } 2673 } 2674 2675 node = get_child_node(ctx->xml, pps, 2676 "SubscriptionUpdate/UpdateMethod"); 2677 if (node) { 2678 char *tmp; 2679 tmp = xml_node_get_text(ctx->xml, node); 2680 if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0) 2681 spp = 0; 2682 else 2683 spp = 1; 2684 } else { 2685 wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP"); 2686 spp = 1; 2687 } 2688 2689 get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword", 2690 &cred_username, &cred_password); 2691 if (cred_username) 2692 wpa_printf(MSG_INFO, "Using username: %s", cred_username); 2693 if (cred_password) 2694 wpa_printf(MSG_DEBUG, "Using password: %s", cred_password); 2695 2696 if (cred_username == NULL && cred_password == NULL && 2697 get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) { 2698 wpa_printf(MSG_INFO, "Using client certificate"); 2699 os_snprintf(client_cert_buf, sizeof(client_cert_buf), 2700 "SP/%s/client-cert.pem", ctx->fqdn); 2701 client_cert = client_cert_buf; 2702 os_snprintf(client_key_buf, sizeof(client_key_buf), 2703 "SP/%s/client-key.pem", ctx->fqdn); 2704 client_key = client_key_buf; 2705 ctx->client_cert_present = 1; 2706 } 2707 2708 node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI"); 2709 if (node) { 2710 sub_rem_uri = xml_node_get_text(ctx->xml, node); 2711 if (sub_rem_uri && 2712 (!address || os_strcmp(address, sub_rem_uri) != 0)) { 2713 wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s", 2714 sub_rem_uri); 2715 address = sub_rem_uri; 2716 } 2717 } 2718 if (!address) { 2719 wpa_printf(MSG_INFO, "Server URL not known"); 2720 return -1; 2721 } 2722 2723 write_summary(ctx, "Wait for IP address for subscriptiom remediation"); 2724 wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation"); 2725 2726 if (wait_ip_addr(ctx->ifname, 15) < 0) { 2727 wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); 2728 } 2729 2730 if (spp) 2731 spp_sub_rem(ctx, address, pps_fname, 2732 client_cert, client_key, 2733 cred_username, cred_password, pps); 2734 else 2735 oma_dm_sub_rem(ctx, address, pps_fname, 2736 client_cert, client_key, 2737 cred_username, cred_password, pps); 2738 2739 xml_node_get_text_free(ctx->xml, sub_rem_uri); 2740 xml_node_get_text_free(ctx->xml, cred_username); 2741 str_clear_free(cred_password); 2742 xml_node_free(ctx->xml, pps); 2743 return 0; 2744 } 2745 2746 2747 static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address, 2748 const char *pps_fname, const char *ca_fname) 2749 { 2750 xml_node_t *pps; 2751 xml_node_t *node; 2752 char pps_fname_buf[300]; 2753 char ca_fname_buf[200]; 2754 char *uri = NULL; 2755 char *cred_username = NULL; 2756 char *cred_password = NULL; 2757 char client_cert_buf[200]; 2758 char *client_cert = NULL; 2759 char client_key_buf[200]; 2760 char *client_key = NULL; 2761 int spp; 2762 2763 wpa_printf(MSG_INFO, "Policy update requested"); 2764 2765 if (!pps_fname) { 2766 char buf[256]; 2767 int res; 2768 2769 wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); 2770 if (address && os_strncmp(address, "fqdn=", 5) == 0) { 2771 wpa_printf(MSG_INFO, "Use requested FQDN from command line"); 2772 os_snprintf(buf, sizeof(buf), "%s", address + 5); 2773 address = NULL; 2774 } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf, 2775 sizeof(buf)) < 0) { 2776 wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant"); 2777 return -1; 2778 } 2779 os_free(ctx->fqdn); 2780 ctx->fqdn = os_strdup(buf); 2781 if (ctx->fqdn == NULL) 2782 return -1; 2783 wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s", 2784 buf); 2785 os_snprintf(pps_fname_buf, sizeof(pps_fname_buf), 2786 "SP/%s/pps.xml", ctx->fqdn); 2787 pps_fname = pps_fname_buf; 2788 2789 res = os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), 2790 "SP/%s/ca.pem", buf); 2791 if (os_snprintf_error(sizeof(ca_fname_buf), res)) { 2792 os_free(ctx->fqdn); 2793 ctx->fqdn = NULL; 2794 return -1; 2795 } 2796 ca_fname = ca_fname_buf; 2797 } 2798 2799 if (!os_file_exists(pps_fname)) { 2800 wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible", 2801 pps_fname); 2802 return -1; 2803 } 2804 wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname); 2805 2806 if (ca_fname && !os_file_exists(ca_fname)) { 2807 wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible", 2808 ca_fname); 2809 return -1; 2810 } 2811 wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname); 2812 ctx->ca_fname = ca_fname; 2813 2814 pps = node_from_file(ctx->xml, pps_fname); 2815 if (pps == NULL) { 2816 wpa_printf(MSG_INFO, "Could not read PPS MO"); 2817 return -1; 2818 } 2819 2820 if (!ctx->fqdn) { 2821 char *tmp; 2822 node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); 2823 if (node == NULL) { 2824 wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS"); 2825 return -1; 2826 } 2827 tmp = xml_node_get_text(ctx->xml, node); 2828 if (tmp == NULL) { 2829 wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS"); 2830 return -1; 2831 } 2832 ctx->fqdn = os_strdup(tmp); 2833 xml_node_get_text_free(ctx->xml, tmp); 2834 if (!ctx->fqdn) { 2835 wpa_printf(MSG_INFO, "No FQDN known"); 2836 return -1; 2837 } 2838 } 2839 2840 node = get_child_node(ctx->xml, pps, 2841 "Policy/PolicyUpdate/UpdateMethod"); 2842 if (node) { 2843 char *tmp; 2844 tmp = xml_node_get_text(ctx->xml, node); 2845 if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0) 2846 spp = 0; 2847 else 2848 spp = 1; 2849 } else { 2850 wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP"); 2851 spp = 1; 2852 } 2853 2854 get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword", 2855 &cred_username, &cred_password); 2856 if (cred_username) 2857 wpa_printf(MSG_INFO, "Using username: %s", cred_username); 2858 if (cred_password) 2859 wpa_printf(MSG_DEBUG, "Using password: %s", cred_password); 2860 2861 if (cred_username == NULL && cred_password == NULL && 2862 get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) { 2863 wpa_printf(MSG_INFO, "Using client certificate"); 2864 os_snprintf(client_cert_buf, sizeof(client_cert_buf), 2865 "SP/%s/client-cert.pem", ctx->fqdn); 2866 client_cert = client_cert_buf; 2867 os_snprintf(client_key_buf, sizeof(client_key_buf), 2868 "SP/%s/client-key.pem", ctx->fqdn); 2869 client_key = client_key_buf; 2870 } 2871 2872 if (!address) { 2873 node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI"); 2874 if (node) { 2875 uri = xml_node_get_text(ctx->xml, node); 2876 wpa_printf(MSG_INFO, "URI based on PPS: %s", uri); 2877 address = uri; 2878 } 2879 } 2880 if (!address) { 2881 wpa_printf(MSG_INFO, "Server URL not known"); 2882 return -1; 2883 } 2884 2885 if (spp) 2886 spp_pol_upd(ctx, address, pps_fname, 2887 client_cert, client_key, 2888 cred_username, cred_password, pps); 2889 else 2890 oma_dm_pol_upd(ctx, address, pps_fname, 2891 client_cert, client_key, 2892 cred_username, cred_password, pps); 2893 2894 xml_node_get_text_free(ctx->xml, uri); 2895 xml_node_get_text_free(ctx->xml, cred_username); 2896 str_clear_free(cred_password); 2897 xml_node_free(ctx->xml, pps); 2898 2899 return 0; 2900 } 2901 2902 2903 static char * get_hostname(const char *url) 2904 { 2905 const char *pos, *end, *end2; 2906 char *ret; 2907 2908 if (url == NULL) 2909 return NULL; 2910 2911 pos = os_strchr(url, '/'); 2912 if (pos == NULL) 2913 return NULL; 2914 pos++; 2915 if (*pos != '/') 2916 return NULL; 2917 pos++; 2918 2919 end = os_strchr(pos, '/'); 2920 end2 = os_strchr(pos, ':'); 2921 if ((end && end2 && end2 < end) || (!end && end2)) 2922 end = end2; 2923 if (end) 2924 end--; 2925 else { 2926 end = pos; 2927 while (*end) 2928 end++; 2929 if (end > pos) 2930 end--; 2931 } 2932 2933 ret = os_malloc(end - pos + 2); 2934 if (ret == NULL) 2935 return NULL; 2936 2937 os_memcpy(ret, pos, end - pos + 1); 2938 ret[end - pos + 1] = '\0'; 2939 2940 return ret; 2941 } 2942 2943 2944 static int osu_cert_cb(void *_ctx, struct http_cert *cert) 2945 { 2946 struct hs20_osu_client *ctx = _ctx; 2947 size_t i, j; 2948 int found; 2949 char *host = NULL; 2950 2951 wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s server_url=%s)", 2952 !ctx->no_osu_cert_validation, cert->url ? cert->url : "N/A", 2953 ctx->server_url); 2954 2955 if (ctx->no_osu_cert_validation && cert->url) 2956 host = get_hostname(cert->url); 2957 else 2958 host = get_hostname(ctx->server_url); 2959 2960 if (!ctx->no_osu_cert_validation) { 2961 for (i = 0; i < ctx->server_dnsname_count; i++) 2962 os_free(ctx->server_dnsname[i]); 2963 os_free(ctx->server_dnsname); 2964 ctx->server_dnsname = os_calloc(cert->num_dnsname, 2965 sizeof(char *)); 2966 ctx->server_dnsname_count = 0; 2967 } 2968 2969 found = 0; 2970 for (i = 0; i < cert->num_dnsname; i++) { 2971 if (!ctx->no_osu_cert_validation && ctx->server_dnsname) { 2972 ctx->server_dnsname[ctx->server_dnsname_count] = 2973 os_strdup(cert->dnsname[i]); 2974 if (ctx->server_dnsname[ctx->server_dnsname_count]) 2975 ctx->server_dnsname_count++; 2976 } 2977 if (host && os_strcasecmp(host, cert->dnsname[i]) == 0) 2978 found = 1; 2979 wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]); 2980 } 2981 2982 if (host && !found) { 2983 wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection", 2984 host); 2985 write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection", 2986 host); 2987 os_free(host); 2988 return -1; 2989 } 2990 2991 os_free(host); 2992 2993 for (i = 0; i < cert->num_othername; i++) { 2994 if (os_strcmp(cert->othername[i].oid, 2995 "1.3.6.1.4.1.40808.1.1.1") == 0) { 2996 wpa_hexdump_ascii(MSG_INFO, 2997 "id-wfa-hotspot-friendlyName", 2998 cert->othername[i].data, 2999 cert->othername[i].len); 3000 } 3001 } 3002 3003 for (j = 0; !ctx->no_osu_cert_validation && 3004 j < ctx->friendly_name_count; j++) { 3005 int found = 0; 3006 for (i = 0; i < cert->num_othername; i++) { 3007 if (os_strcmp(cert->othername[i].oid, 3008 "1.3.6.1.4.1.40808.1.1.1") != 0) 3009 continue; 3010 if (cert->othername[i].len < 3) 3011 continue; 3012 if (os_strncasecmp((char *) cert->othername[i].data, 3013 ctx->friendly_name[j].lang, 3) != 0) 3014 continue; 3015 if (os_strncmp((char *) cert->othername[i].data + 3, 3016 ctx->friendly_name[j].text, 3017 cert->othername[i].len - 3) == 0) { 3018 found = 1; 3019 break; 3020 } 3021 } 3022 3023 if (!found) { 3024 wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'", 3025 ctx->friendly_name[j].lang, 3026 ctx->friendly_name[j].text); 3027 write_result(ctx, "No friendly name match found for '[%s]%s'", 3028 ctx->friendly_name[j].lang, 3029 ctx->friendly_name[j].text); 3030 return -1; 3031 } 3032 } 3033 3034 for (i = 0; i < cert->num_logo; i++) { 3035 struct http_logo *logo = &cert->logo[i]; 3036 3037 wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'", 3038 logo->alg_oid, logo->uri); 3039 wpa_hexdump_ascii(MSG_INFO, "hashValue", 3040 logo->hash, logo->hash_len); 3041 } 3042 3043 for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) { 3044 int found = 0; 3045 char *name = ctx->icon_filename[j]; 3046 size_t name_len = os_strlen(name); 3047 3048 wpa_printf(MSG_INFO, 3049 "[%zu] Looking for icon file name '%s' match", 3050 j, name); 3051 for (i = 0; i < cert->num_logo; i++) { 3052 struct http_logo *logo = &cert->logo[i]; 3053 size_t uri_len = os_strlen(logo->uri); 3054 char *pos; 3055 3056 wpa_printf(MSG_INFO, 3057 "[%zu] Comparing to '%s' uri_len=%d name_len=%d", 3058 i, logo->uri, (int) uri_len, (int) name_len); 3059 if (uri_len < 1 + name_len) { 3060 wpa_printf(MSG_INFO, "URI Length is too short"); 3061 continue; 3062 } 3063 pos = &logo->uri[uri_len - name_len - 1]; 3064 if (*pos != '/') 3065 continue; 3066 pos++; 3067 if (os_strcmp(pos, name) == 0) { 3068 found = 1; 3069 break; 3070 } 3071 } 3072 3073 if (!found) { 3074 wpa_printf(MSG_INFO, "No icon filename match found for '%s'", 3075 name); 3076 write_result(ctx, 3077 "No icon filename match found for '%s'", 3078 name); 3079 return -1; 3080 } 3081 } 3082 3083 for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) { 3084 int found = 0; 3085 3086 for (i = 0; i < cert->num_logo; i++) { 3087 struct http_logo *logo = &cert->logo[i]; 3088 3089 if (logo->hash_len != 32) { 3090 wpa_printf(MSG_INFO, 3091 "[%zu][%zu] Icon hash length invalid (should be 32): %d", 3092 j, i, (int) logo->hash_len); 3093 continue; 3094 } 3095 if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) { 3096 found = 1; 3097 break; 3098 } 3099 3100 wpa_printf(MSG_DEBUG, 3101 "[%zu][%zu] Icon hash did not match", j, i); 3102 wpa_hexdump_ascii(MSG_DEBUG, "logo->hash", 3103 logo->hash, 32); 3104 wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]", 3105 ctx->icon_hash[j], 32); 3106 } 3107 3108 if (!found) { 3109 wpa_printf(MSG_INFO, 3110 "No icon hash match (by hash) found"); 3111 write_result(ctx, 3112 "No icon hash match (by hash) found"); 3113 return -1; 3114 } 3115 } 3116 3117 return 0; 3118 } 3119 3120 3121 static int init_ctx(struct hs20_osu_client *ctx) 3122 { 3123 xml_node_t *devinfo, *devid; 3124 3125 os_memset(ctx, 0, sizeof(*ctx)); 3126 ctx->ifname = "wlan0"; 3127 ctx->xml = xml_node_init_ctx(ctx, NULL); 3128 if (ctx->xml == NULL) 3129 return -1; 3130 3131 devinfo = node_from_file(ctx->xml, "devinfo.xml"); 3132 if (devinfo) { 3133 devid = get_node(ctx->xml, devinfo, "DevId"); 3134 if (devid) { 3135 char *tmp = xml_node_get_text(ctx->xml, devid); 3136 3137 if (tmp) { 3138 ctx->devid = os_strdup(tmp); 3139 xml_node_get_text_free(ctx->xml, tmp); 3140 } 3141 } 3142 xml_node_free(ctx->xml, devinfo); 3143 } 3144 3145 ctx->http = http_init_ctx(ctx, ctx->xml); 3146 if (ctx->http == NULL) { 3147 xml_node_deinit_ctx(ctx->xml); 3148 return -1; 3149 } 3150 http_ocsp_set(ctx->http, 2); 3151 http_set_cert_cb(ctx->http, osu_cert_cb, ctx); 3152 3153 return 0; 3154 } 3155 3156 3157 static void deinit_ctx(struct hs20_osu_client *ctx) 3158 { 3159 size_t i; 3160 3161 http_deinit_ctx(ctx->http); 3162 xml_node_deinit_ctx(ctx->xml); 3163 os_free(ctx->fqdn); 3164 os_free(ctx->server_url); 3165 os_free(ctx->devid); 3166 3167 for (i = 0; i < ctx->server_dnsname_count; i++) 3168 os_free(ctx->server_dnsname[i]); 3169 os_free(ctx->server_dnsname); 3170 } 3171 3172 3173 static void check_workarounds(struct hs20_osu_client *ctx) 3174 { 3175 FILE *f; 3176 char buf[100]; 3177 unsigned long int val = 0; 3178 3179 f = fopen("hs20-osu-client.workarounds", "r"); 3180 if (f == NULL) 3181 return; 3182 3183 if (fgets(buf, sizeof(buf), f)) 3184 val = strtoul(buf, NULL, 16); 3185 3186 fclose(f); 3187 3188 if (val) { 3189 wpa_printf(MSG_INFO, "Workarounds enabled: 0x%lx", val); 3190 ctx->workarounds = val; 3191 if (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) 3192 http_ocsp_set(ctx->http, 1); 3193 } 3194 } 3195 3196 3197 static void usage(void) 3198 { 3199 printf("usage: hs20-osu-client [-dddqqKtT] [-S<station ifname>] \\\n" 3200 " [-w<wpa_supplicant ctrl_iface dir>] " 3201 "[-r<result file>] [-f<debug file>] \\\n" 3202 " [-s<summary file>] \\\n" 3203 " [-x<spp.xsd file name>] \\\n" 3204 " <command> [arguments..]\n" 3205 "commands:\n" 3206 "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n" 3207 "- to_tnds2 <XML MO> <XML MO in TNDS format (Path) " 3208 "[URN]>\n" 3209 "- from_tnds <XML MO in TNDS format> <XML MO>\n" 3210 "- set_pps <PerProviderSubscription XML file name>\n" 3211 "- get_fqdn <PerProviderSubscription XML file name>\n" 3212 "- pol_upd [Server URL] [PPS] [CA cert]\n" 3213 "- sub_rem <Server URL> [PPS] [CA cert]\n" 3214 "- prov <Server URL> [CA cert]\n" 3215 "- oma_dm_prov <Server URL> [CA cert]\n" 3216 "- sim_prov <Server URL> [CA cert]\n" 3217 "- oma_dm_sim_prov <Server URL> [CA cert]\n" 3218 "- signup [CA cert]\n" 3219 "- dl_osu_ca <PPS> <CA file>\n" 3220 "- dl_polupd_ca <PPS> <CA file>\n" 3221 "- dl_aaa_ca <PPS> <CA file>\n" 3222 "- browser <URL>\n" 3223 "- parse_cert <X.509 certificate (DER)>\n" 3224 "- osu_select <OSU info directory> [CA cert]\n"); 3225 } 3226 3227 3228 int main(int argc, char *argv[]) 3229 { 3230 struct hs20_osu_client ctx; 3231 int c; 3232 int ret = 0; 3233 int no_prod_assoc = 0; 3234 const char *friendly_name = NULL; 3235 const char *wpa_debug_file_path = NULL; 3236 extern char *wpas_ctrl_path; 3237 extern int wpa_debug_level; 3238 extern int wpa_debug_show_keys; 3239 extern int wpa_debug_timestamp; 3240 3241 if (init_ctx(&ctx) < 0) 3242 return -1; 3243 3244 for (;;) { 3245 c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tTw:x:"); 3246 if (c < 0) 3247 break; 3248 switch (c) { 3249 case 'd': 3250 if (wpa_debug_level > 0) 3251 wpa_debug_level--; 3252 break; 3253 case 'f': 3254 wpa_debug_file_path = optarg; 3255 break; 3256 case 'K': 3257 wpa_debug_show_keys++; 3258 break; 3259 case 'N': 3260 no_prod_assoc = 1; 3261 break; 3262 case 'o': 3263 ctx.osu_ssid = optarg; 3264 break; 3265 case 'O': 3266 friendly_name = optarg; 3267 break; 3268 case 'q': 3269 wpa_debug_level++; 3270 break; 3271 case 'r': 3272 ctx.result_file = optarg; 3273 break; 3274 case 's': 3275 ctx.summary_file = optarg; 3276 break; 3277 case 'S': 3278 ctx.ifname = optarg; 3279 break; 3280 case 't': 3281 wpa_debug_timestamp++; 3282 break; 3283 case 'T': 3284 ctx.ignore_tls = 1; 3285 break; 3286 case 'w': 3287 wpas_ctrl_path = optarg; 3288 break; 3289 case 'x': 3290 spp_xsd_fname = optarg; 3291 break; 3292 case 'h': 3293 default: 3294 usage(); 3295 exit(0); 3296 } 3297 } 3298 3299 if (argc - optind < 1) { 3300 usage(); 3301 exit(0); 3302 } 3303 3304 wpa_debug_open_file(wpa_debug_file_path); 3305 3306 #ifdef __linux__ 3307 setlinebuf(stdout); 3308 #endif /* __linux__ */ 3309 3310 if (ctx.result_file) 3311 unlink(ctx.result_file); 3312 wpa_printf(MSG_DEBUG, "===[hs20-osu-client START - command: %s ]======" 3313 "================", argv[optind]); 3314 check_workarounds(&ctx); 3315 3316 if (strcmp(argv[optind], "to_tnds") == 0) { 3317 if (argc - optind < 2) { 3318 usage(); 3319 exit(0); 3320 } 3321 cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2], 3322 argc > optind + 3 ? argv[optind + 3] : NULL, 3323 0); 3324 } else if (strcmp(argv[optind], "to_tnds2") == 0) { 3325 if (argc - optind < 2) { 3326 usage(); 3327 exit(0); 3328 } 3329 cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2], 3330 argc > optind + 3 ? argv[optind + 3] : NULL, 3331 1); 3332 } else if (strcmp(argv[optind], "from_tnds") == 0) { 3333 if (argc - optind < 2) { 3334 usage(); 3335 exit(0); 3336 } 3337 cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]); 3338 } else if (strcmp(argv[optind], "sub_rem") == 0) { 3339 if (argc - optind < 2) { 3340 usage(); 3341 exit(0); 3342 } 3343 ret = cmd_sub_rem(&ctx, argv[optind + 1], 3344 argc > optind + 2 ? argv[optind + 2] : NULL, 3345 argc > optind + 3 ? argv[optind + 3] : NULL); 3346 } else if (strcmp(argv[optind], "pol_upd") == 0) { 3347 ret = cmd_pol_upd(&ctx, 3348 argc > optind + 1 ? argv[optind + 1] : NULL, 3349 argc > optind + 2 ? argv[optind + 2] : NULL, 3350 argc > optind + 3 ? argv[optind + 3] : NULL); 3351 } else if (strcmp(argv[optind], "prov") == 0) { 3352 if (argc - optind < 2) { 3353 usage(); 3354 exit(0); 3355 } 3356 ctx.ca_fname = argv[optind + 2]; 3357 wpa_printf(MSG_DEBUG, "Calling cmd_prov from main"); 3358 cmd_prov(&ctx, argv[optind + 1]); 3359 } else if (strcmp(argv[optind], "sim_prov") == 0) { 3360 if (argc - optind < 2) { 3361 usage(); 3362 exit(0); 3363 } 3364 ctx.ca_fname = argv[optind + 2]; 3365 cmd_sim_prov(&ctx, argv[optind + 1]); 3366 } else if (strcmp(argv[optind], "dl_osu_ca") == 0) { 3367 if (argc - optind < 2) { 3368 usage(); 3369 exit(0); 3370 } 3371 cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]); 3372 } else if (strcmp(argv[optind], "dl_polupd_ca") == 0) { 3373 if (argc - optind < 2) { 3374 usage(); 3375 exit(0); 3376 } 3377 cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]); 3378 } else if (strcmp(argv[optind], "dl_aaa_ca") == 0) { 3379 if (argc - optind < 2) { 3380 usage(); 3381 exit(0); 3382 } 3383 cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]); 3384 } else if (strcmp(argv[optind], "osu_select") == 0) { 3385 if (argc - optind < 2) { 3386 usage(); 3387 exit(0); 3388 } 3389 ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL; 3390 cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL); 3391 } else if (strcmp(argv[optind], "signup") == 0) { 3392 ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL; 3393 ret = cmd_signup(&ctx, no_prod_assoc, friendly_name); 3394 } else if (strcmp(argv[optind], "set_pps") == 0) { 3395 if (argc - optind < 2) { 3396 usage(); 3397 exit(0); 3398 } 3399 cmd_set_pps(&ctx, argv[optind + 1]); 3400 } else if (strcmp(argv[optind], "get_fqdn") == 0) { 3401 if (argc - optind < 1) { 3402 usage(); 3403 exit(0); 3404 } 3405 ret = cmd_get_fqdn(&ctx, argv[optind + 1]); 3406 } else if (strcmp(argv[optind], "oma_dm_prov") == 0) { 3407 if (argc - optind < 2) { 3408 usage(); 3409 exit(0); 3410 } 3411 ctx.ca_fname = argv[optind + 2]; 3412 cmd_oma_dm_prov(&ctx, argv[optind + 1]); 3413 } else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) { 3414 if (argc - optind < 2) { 3415 usage(); 3416 exit(0); 3417 } 3418 ctx.ca_fname = argv[optind + 2]; 3419 if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) { 3420 write_summary(&ctx, "Failed to complete OMA DM SIM provisioning"); 3421 return -1; 3422 } 3423 } else if (strcmp(argv[optind], "oma_dm_add") == 0) { 3424 if (argc - optind < 2) { 3425 usage(); 3426 exit(0); 3427 } 3428 cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]); 3429 } else if (strcmp(argv[optind], "oma_dm_replace") == 0) { 3430 if (argc - optind < 2) { 3431 usage(); 3432 exit(0); 3433 } 3434 cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]); 3435 } else if (strcmp(argv[optind], "est_csr") == 0) { 3436 if (argc - optind < 2) { 3437 usage(); 3438 exit(0); 3439 } 3440 mkdir("Cert", S_IRWXU); 3441 est_build_csr(&ctx, argv[optind + 1]); 3442 } else if (strcmp(argv[optind], "browser") == 0) { 3443 int ret; 3444 3445 if (argc - optind < 2) { 3446 usage(); 3447 exit(0); 3448 } 3449 3450 wpa_printf(MSG_INFO, "Launch web browser to URL %s", 3451 argv[optind + 1]); 3452 ret = hs20_web_browser(argv[optind + 1], ctx.ignore_tls); 3453 wpa_printf(MSG_INFO, "Web browser result: %d", ret); 3454 } else if (strcmp(argv[optind], "parse_cert") == 0) { 3455 if (argc - optind < 2) { 3456 usage(); 3457 exit(0); 3458 } 3459 3460 wpa_debug_level = MSG_MSGDUMP; 3461 http_parse_x509_certificate(ctx.http, argv[optind + 1]); 3462 wpa_debug_level = MSG_INFO; 3463 } else { 3464 wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]); 3465 } 3466 3467 deinit_ctx(&ctx); 3468 wpa_printf(MSG_DEBUG, 3469 "===[hs20-osu-client END ]======================"); 3470 3471 wpa_debug_close_file(); 3472 3473 return ret; 3474 } 3475