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