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