1 /* 2 * Hotspot 2.0 SPP 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 <sys/stat.h> 11 12 #include "common.h" 13 #include "browser.h" 14 #include "wpa_ctrl.h" 15 #include "wpa_helpers.h" 16 #include "xml-utils.h" 17 #include "http-utils.h" 18 #include "utils/base64.h" 19 #include "crypto/crypto.h" 20 #include "crypto/sha256.h" 21 #include "osu_client.h" 22 23 24 extern const char *spp_xsd_fname; 25 26 static int hs20_spp_update_response(struct hs20_osu_client *ctx, 27 const char *session_id, 28 const char *spp_status, 29 const char *error_code); 30 static void hs20_policy_update_complete( 31 struct hs20_osu_client *ctx, const char *pps_fname); 32 33 34 static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node, 35 char *attr_name) 36 { 37 return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name); 38 } 39 40 41 static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node, 42 const char *expected_name) 43 { 44 struct xml_node_ctx *xctx = ctx->xml; 45 const char *name; 46 char *err; 47 int ret; 48 49 if (!xml_node_is_element(xctx, node)) 50 return -1; 51 52 name = xml_node_get_localname(xctx, node); 53 if (name == NULL) 54 return -1; 55 56 if (strcmp(expected_name, name) != 0) { 57 wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')", 58 name, expected_name); 59 write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')", 60 name, expected_name); 61 return -1; 62 } 63 64 ret = xml_validate(xctx, node, spp_xsd_fname, &err); 65 if (ret < 0) { 66 wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err); 67 write_summary(ctx, "SPP XML schema validation failed"); 68 os_free(err); 69 } 70 return ret; 71 } 72 73 74 static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns, 75 xml_node_t *parent, const char *urn, 76 const char *fname) 77 { 78 xml_node_t *node; 79 xml_node_t *fnode, *tnds; 80 char *str; 81 82 errno = 0; 83 fnode = node_from_file(ctx, fname); 84 if (!fnode) { 85 wpa_printf(MSG_ERROR, 86 "Failed to create XML node from file: %s, possible error: %s", 87 fname, strerror(errno)); 88 return; 89 } 90 tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2"); 91 xml_node_free(ctx, fnode); 92 if (!tnds) 93 return; 94 95 str = xml_node_to_str(ctx, tnds); 96 xml_node_free(ctx, tnds); 97 if (str == NULL) 98 return; 99 100 node = xml_node_create_text(ctx, parent, ns, "moContainer", str); 101 if (node) 102 xml_node_add_attr(ctx, node, ns, "moURN", urn); 103 os_free(str); 104 } 105 106 107 static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx, 108 xml_namespace_t **ret_ns, 109 const char *session_id, 110 const char *reason) 111 { 112 xml_namespace_t *ns; 113 xml_node_t *spp_node; 114 115 write_summary(ctx, "Building sppPostDevData requestReason='%s'", 116 reason); 117 spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns, 118 "sppPostDevData"); 119 if (spp_node == NULL) 120 return NULL; 121 if (ret_ns) 122 *ret_ns = ns; 123 124 xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0"); 125 xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason); 126 if (session_id) 127 xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", 128 session_id); 129 xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI", 130 "http://localhost:12345/"); 131 132 xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions", 133 "1.0"); 134 xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList", 135 URN_HS20_PPS " " URN_OMA_DM_DEVINFO " " 136 URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT); 137 138 add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO, 139 "devinfo.xml"); 140 add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL, 141 "devdetail.xml"); 142 143 return spp_node; 144 } 145 146 147 static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps, 148 xml_node_t *update) 149 { 150 xml_node_t *node, *parent, *tnds, *unode; 151 char *str; 152 const char *name; 153 char *uri, *pos; 154 char *cdata, *cdata_end; 155 size_t fqdn_len; 156 157 wpa_printf(MSG_INFO, "Processing updateNode"); 158 debug_dump_node(ctx, "updateNode", update); 159 160 uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI"); 161 if (uri == NULL) { 162 wpa_printf(MSG_INFO, "No managementTreeURI present"); 163 return -1; 164 } 165 wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri); 166 167 name = os_strrchr(uri, '/'); 168 if (name == NULL) { 169 wpa_printf(MSG_INFO, "Unexpected URI"); 170 xml_node_get_attr_value_free(ctx->xml, uri); 171 return -1; 172 } 173 name++; 174 wpa_printf(MSG_INFO, "Update interior node: '%s'", name); 175 176 str = xml_node_get_text(ctx->xml, update); 177 if (str == NULL) { 178 wpa_printf(MSG_INFO, "Could not extract MO text"); 179 xml_node_get_attr_value_free(ctx->xml, uri); 180 return -1; 181 } 182 wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str); 183 cdata = strstr(str, "<![CDATA["); 184 cdata_end = strstr(str, "]]>"); 185 if (cdata && cdata_end && cdata_end > cdata && 186 cdata < strstr(str, "MgmtTree") && 187 cdata_end > strstr(str, "/MgmtTree")) { 188 char *tmp; 189 wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container"); 190 tmp = strdup(cdata + 9); 191 if (tmp) { 192 cdata_end = strstr(tmp, "]]>"); 193 if (cdata_end) 194 *cdata_end = '\0'; 195 wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'", 196 tmp); 197 tnds = xml_node_from_buf(ctx->xml, tmp); 198 free(tmp); 199 } else 200 tnds = NULL; 201 } else 202 tnds = xml_node_from_buf(ctx->xml, str); 203 xml_node_get_text_free(ctx->xml, str); 204 if (tnds == NULL) { 205 wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text"); 206 xml_node_get_attr_value_free(ctx->xml, uri); 207 return -1; 208 } 209 210 unode = tnds_to_mo(ctx->xml, tnds); 211 xml_node_free(ctx->xml, tnds); 212 if (unode == NULL) { 213 wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text"); 214 xml_node_get_attr_value_free(ctx->xml, uri); 215 return -1; 216 } 217 218 debug_dump_node(ctx, "Parsed TNDS", unode); 219 220 if (get_node_uri(ctx->xml, unode, name) == NULL) { 221 wpa_printf(MSG_INFO, "[hs20] %s node not found", name); 222 xml_node_free(ctx->xml, unode); 223 xml_node_get_attr_value_free(ctx->xml, uri); 224 return -1; 225 } 226 227 if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) { 228 wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi"); 229 xml_node_free(ctx->xml, unode); 230 xml_node_get_attr_value_free(ctx->xml, uri); 231 return -1; 232 } 233 pos = uri + 8; 234 235 if (ctx->fqdn == NULL) { 236 wpa_printf(MSG_INFO, "FQDN not known"); 237 xml_node_free(ctx->xml, unode); 238 xml_node_get_attr_value_free(ctx->xml, uri); 239 return -1; 240 } 241 fqdn_len = os_strlen(ctx->fqdn); 242 if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 || 243 pos[fqdn_len] != '/') { 244 wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s", 245 ctx->fqdn); 246 xml_node_free(ctx->xml, unode); 247 xml_node_get_attr_value_free(ctx->xml, uri); 248 return -1; 249 } 250 pos += fqdn_len + 1; 251 252 if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) { 253 wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription", 254 ctx->fqdn); 255 xml_node_free(ctx->xml, unode); 256 xml_node_get_attr_value_free(ctx->xml, uri); 257 return -1; 258 } 259 pos += 24; 260 261 wpa_printf(MSG_INFO, "Update command for PPS node %s", pos); 262 263 node = get_node(ctx->xml, pps, pos); 264 if (node) { 265 parent = xml_node_get_parent(ctx->xml, node); 266 xml_node_detach(ctx->xml, node); 267 wpa_printf(MSG_INFO, "Replace '%s' node", name); 268 } else { 269 char *pos2; 270 pos2 = os_strrchr(pos, '/'); 271 if (pos2 == NULL) { 272 parent = pps; 273 } else { 274 *pos2 = '\0'; 275 parent = get_node(ctx->xml, pps, pos); 276 } 277 if (parent == NULL) { 278 wpa_printf(MSG_INFO, "Could not find parent %s", pos); 279 xml_node_free(ctx->xml, unode); 280 xml_node_get_attr_value_free(ctx->xml, uri); 281 return -1; 282 } 283 wpa_printf(MSG_INFO, "Add '%s' node", name); 284 } 285 xml_node_add_child(ctx->xml, parent, unode); 286 287 xml_node_get_attr_value_free(ctx->xml, uri); 288 289 return 0; 290 } 291 292 293 static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update, 294 const char *pps_fname, xml_node_t *pps) 295 { 296 wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)"); 297 xml_node_for_each_sibling(ctx->xml, update) { 298 xml_node_for_each_check(ctx->xml, update); 299 if (process_update_node(ctx, pps, update) < 0) 300 return -1; 301 } 302 303 return update_pps_file(ctx, pps_fname, pps); 304 } 305 306 307 static void hs20_sub_rem_complete(struct hs20_osu_client *ctx, 308 const char *pps_fname) 309 { 310 /* 311 * Update wpa_supplicant credentials and reconnect using updated 312 * information. 313 */ 314 wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials"); 315 cmd_set_pps(ctx, pps_fname); 316 317 if (ctx->no_reconnect) 318 return; 319 320 wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); 321 if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) 322 wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect"); 323 } 324 325 326 static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx, 327 xml_node_t *cmd, 328 const char *session_id, 329 const char *pps_fname) 330 { 331 xml_namespace_t *ns; 332 xml_node_t *node, *ret_node; 333 char *urn; 334 335 urn = get_spp_attr_value(ctx->xml, cmd, "moURN"); 336 if (!urn) { 337 wpa_printf(MSG_INFO, "No URN included"); 338 return NULL; 339 } 340 wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn); 341 if (strcasecmp(urn, URN_HS20_PPS) != 0) { 342 wpa_printf(MSG_INFO, "Unsupported moURN"); 343 xml_node_get_attr_value_free(ctx->xml, urn); 344 return NULL; 345 } 346 xml_node_get_attr_value_free(ctx->xml, urn); 347 348 if (!pps_fname) { 349 wpa_printf(MSG_INFO, "PPS file name no known"); 350 return NULL; 351 } 352 353 node = build_spp_post_dev_data(ctx, &ns, session_id, 354 "MO upload"); 355 if (node == NULL) 356 return NULL; 357 add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname); 358 359 ret_node = soap_send_receive(ctx->http, node); 360 if (ret_node == NULL) 361 return NULL; 362 363 debug_dump_node(ctx, "Received response to MO upload", ret_node); 364 365 if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { 366 wpa_printf(MSG_INFO, "SPP validation failed"); 367 xml_node_free(ctx->xml, ret_node); 368 return NULL; 369 } 370 371 return ret_node; 372 } 373 374 375 static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo, 376 char *fname, size_t fname_len) 377 { 378 char *uri, *urn; 379 int ret; 380 381 debug_dump_node(ctx, "Received addMO", add_mo); 382 383 urn = get_spp_attr_value(ctx->xml, add_mo, "moURN"); 384 if (urn == NULL) { 385 wpa_printf(MSG_INFO, "[hs20] No moURN in addMO"); 386 return -1; 387 } 388 wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn); 389 if (strcasecmp(urn, URN_HS20_PPS) != 0) { 390 wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO"); 391 xml_node_get_attr_value_free(ctx->xml, urn); 392 return -1; 393 } 394 xml_node_get_attr_value_free(ctx->xml, urn); 395 396 uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI"); 397 if (uri == NULL) { 398 wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO"); 399 return -1; 400 } 401 wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri); 402 403 ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len); 404 xml_node_get_attr_value_free(ctx->xml, uri); 405 return ret; 406 } 407 408 409 static int process_spp_user_input_response(struct hs20_osu_client *ctx, 410 const char *session_id, 411 xml_node_t *add_mo) 412 { 413 int ret; 414 char fname[300]; 415 416 debug_dump_node(ctx, "addMO", add_mo); 417 418 wpa_printf(MSG_INFO, "Subscription registration completed"); 419 420 if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) { 421 wpa_printf(MSG_INFO, "Could not add MO"); 422 ret = hs20_spp_update_response( 423 ctx, session_id, 424 "Error occurred", 425 "MO addition or update failed"); 426 return 0; 427 } 428 429 ret = hs20_spp_update_response(ctx, session_id, "OK", NULL); 430 if (ret == 0) 431 hs20_sub_rem_complete(ctx, fname); 432 433 return 0; 434 } 435 436 437 static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx, 438 const char *session_id) 439 { 440 xml_node_t *node, *ret_node; 441 442 node = build_spp_post_dev_data(ctx, NULL, session_id, 443 "User input completed"); 444 if (node == NULL) 445 return NULL; 446 447 ret_node = soap_send_receive(ctx->http, node); 448 if (!ret_node) { 449 if (soap_reinit_client(ctx->http) < 0) 450 return NULL; 451 wpa_printf(MSG_INFO, "Try to finish with re-opened connection"); 452 node = build_spp_post_dev_data(ctx, NULL, session_id, 453 "User input completed"); 454 if (node == NULL) 455 return NULL; 456 ret_node = soap_send_receive(ctx->http, node); 457 if (ret_node == NULL) 458 return NULL; 459 wpa_printf(MSG_INFO, "Continue with new connection"); 460 } 461 462 if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { 463 wpa_printf(MSG_INFO, "SPP validation failed"); 464 xml_node_free(ctx->xml, ret_node); 465 return NULL; 466 } 467 468 return ret_node; 469 } 470 471 472 static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx, 473 xml_node_t *cmd, 474 const char *session_id, 475 const char *pps_fname) 476 { 477 xml_namespace_t *ns; 478 xml_node_t *node, *ret_node; 479 int res; 480 481 wpa_printf(MSG_INFO, "Client certificate enrollment"); 482 483 res = osu_get_certificate(ctx, cmd); 484 if (res < 0) 485 wpa_printf(MSG_INFO, "EST simpleEnroll failed"); 486 487 node = build_spp_post_dev_data(ctx, &ns, session_id, 488 res == 0 ? 489 "Certificate enrollment completed" : 490 "Certificate enrollment failed"); 491 if (node == NULL) 492 return NULL; 493 494 ret_node = soap_send_receive(ctx->http, node); 495 if (ret_node == NULL) 496 return NULL; 497 498 debug_dump_node(ctx, "Received response to certificate enrollment " 499 "completed", ret_node); 500 501 if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { 502 wpa_printf(MSG_INFO, "SPP validation failed"); 503 xml_node_free(ctx->xml, ret_node); 504 return NULL; 505 } 506 507 return ret_node; 508 } 509 510 511 static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec, 512 const char *session_id, const char *pps_fname, 513 xml_node_t *pps, xml_node_t **ret_node) 514 { 515 xml_node_t *cmd; 516 const char *name; 517 char *uri; 518 char *id = strdup(session_id); 519 520 if (id == NULL) 521 return -1; 522 523 *ret_node = NULL; 524 525 debug_dump_node(ctx, "exec", exec); 526 527 xml_node_for_each_child(ctx->xml, cmd, exec) { 528 xml_node_for_each_check(ctx->xml, cmd); 529 break; 530 } 531 if (!cmd) { 532 wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)", 533 cmd); 534 free(id); 535 return -1; 536 } 537 538 name = xml_node_get_localname(ctx->xml, cmd); 539 540 if (strcasecmp(name, "launchBrowserToURI") == 0) { 541 int res; 542 uri = xml_node_get_text(ctx->xml, cmd); 543 if (!uri) { 544 wpa_printf(MSG_INFO, "No URI found"); 545 free(id); 546 return -1; 547 } 548 wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri); 549 write_summary(ctx, "Launch browser to URI '%s'", uri); 550 res = hs20_web_browser(uri, 1); 551 xml_node_get_text_free(ctx->xml, uri); 552 if (res > 0) { 553 wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'", 554 id); 555 write_summary(ctx, "User response in browser completed successfully"); 556 *ret_node = hs20_spp_user_input_completed(ctx, id); 557 free(id); 558 return *ret_node ? 0 : -1; 559 } else { 560 wpa_printf(MSG_INFO, "Failed to receive user response"); 561 write_summary(ctx, "Failed to receive user response"); 562 hs20_spp_update_response( 563 ctx, id, "Error occurred", "Other"); 564 free(id); 565 return -1; 566 } 567 return 0; 568 } 569 570 if (strcasecmp(name, "uploadMO") == 0) { 571 if (pps_fname == NULL) 572 return -1; 573 *ret_node = hs20_spp_upload_mo(ctx, cmd, id, 574 pps_fname); 575 free(id); 576 return *ret_node ? 0 : -1; 577 } 578 579 if (strcasecmp(name, "getCertificate") == 0) { 580 *ret_node = hs20_spp_get_certificate(ctx, cmd, id, 581 pps_fname); 582 free(id); 583 return *ret_node ? 0 : -1; 584 } 585 586 wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name); 587 free(id); 588 return -1; 589 } 590 591 592 enum spp_post_dev_data_use { 593 SPP_SUBSCRIPTION_REMEDIATION, 594 SPP_POLICY_UPDATE, 595 SPP_SUBSCRIPTION_REGISTRATION, 596 }; 597 598 static void process_spp_post_dev_data_response( 599 struct hs20_osu_client *ctx, 600 enum spp_post_dev_data_use use, xml_node_t *node, 601 const char *pps_fname, xml_node_t *pps) 602 { 603 xml_node_t *child; 604 char *status = NULL; 605 xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL; 606 char *session_id = NULL; 607 608 debug_dump_node(ctx, "sppPostDevDataResponse node", node); 609 610 status = get_spp_attr_value(ctx->xml, node, "sppStatus"); 611 if (status == NULL) { 612 wpa_printf(MSG_INFO, "No sppStatus attribute"); 613 goto out; 614 } 615 write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'", 616 status); 617 618 session_id = get_spp_attr_value(ctx->xml, node, "sessionID"); 619 if (session_id == NULL) { 620 wpa_printf(MSG_INFO, "No sessionID attribute"); 621 goto out; 622 } 623 624 wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s' sessionID: '%s'", 625 status, session_id); 626 627 xml_node_for_each_child(ctx->xml, child, node) { 628 const char *name; 629 xml_node_for_each_check(ctx->xml, child); 630 debug_dump_node(ctx, "child", child); 631 name = xml_node_get_localname(ctx->xml, child); 632 wpa_printf(MSG_INFO, "localname: '%s'", name); 633 if (!update && strcasecmp(name, "updateNode") == 0) 634 update = child; 635 if (!exec && strcasecmp(name, "exec") == 0) 636 exec = child; 637 if (!add_mo && strcasecmp(name, "addMO") == 0) 638 add_mo = child; 639 if (!no_mo && strcasecmp(name, "noMOUpdate") == 0) 640 no_mo = child; 641 } 642 643 if (use == SPP_SUBSCRIPTION_REMEDIATION && 644 strcasecmp(status, 645 "Remediation complete, request sppUpdateResponse") == 0) 646 { 647 int res, ret; 648 if (!update && !no_mo) { 649 wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element"); 650 goto out; 651 } 652 wpa_printf(MSG_INFO, "Subscription remediation completed"); 653 res = update_pps(ctx, update, pps_fname, pps); 654 if (res < 0) 655 wpa_printf(MSG_INFO, "Failed to update PPS MO"); 656 ret = hs20_spp_update_response( 657 ctx, session_id, 658 res < 0 ? "Error occurred" : "OK", 659 res < 0 ? "MO addition or update failed" : NULL); 660 if (res == 0 && ret == 0) 661 hs20_sub_rem_complete(ctx, pps_fname); 662 goto out; 663 } 664 665 if (use == SPP_SUBSCRIPTION_REMEDIATION && 666 strcasecmp(status, "Exchange complete, release TLS connection") == 667 0) { 668 if (!no_mo) { 669 wpa_printf(MSG_INFO, "No noMOUpdate element"); 670 goto out; 671 } 672 wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)"); 673 goto out; 674 } 675 676 if (use == SPP_POLICY_UPDATE && 677 strcasecmp(status, "Update complete, request sppUpdateResponse") == 678 0) { 679 int res, ret; 680 wpa_printf(MSG_INFO, "Policy update received - update PPS"); 681 res = update_pps(ctx, update, pps_fname, pps); 682 ret = hs20_spp_update_response( 683 ctx, session_id, 684 res < 0 ? "Error occurred" : "OK", 685 res < 0 ? "MO addition or update failed" : NULL); 686 if (res == 0 && ret == 0) 687 hs20_policy_update_complete(ctx, pps_fname); 688 goto out; 689 } 690 691 if (use == SPP_SUBSCRIPTION_REGISTRATION && 692 strcasecmp(status, "Provisioning complete, request " 693 "sppUpdateResponse") == 0) { 694 if (!add_mo) { 695 wpa_printf(MSG_INFO, "No addMO element - not sure what to do next"); 696 goto out; 697 } 698 process_spp_user_input_response(ctx, session_id, add_mo); 699 node = NULL; 700 goto out; 701 } 702 703 if (strcasecmp(status, "No update available at this time") == 0) { 704 wpa_printf(MSG_INFO, "No update available at this time"); 705 goto out; 706 } 707 708 if (strcasecmp(status, "OK") == 0) { 709 int res; 710 xml_node_t *ret; 711 712 if (!exec) { 713 wpa_printf(MSG_INFO, "No exec element - not sure what to do next"); 714 goto out; 715 } 716 res = hs20_spp_exec(ctx, exec, session_id, 717 pps_fname, pps, &ret); 718 /* xml_node_free(ctx->xml, node); */ 719 node = NULL; 720 if (res == 0 && ret) 721 process_spp_post_dev_data_response(ctx, use, 722 ret, pps_fname, pps); 723 goto out; 724 } 725 726 if (strcasecmp(status, "Error occurred") == 0) { 727 xml_node_t *err; 728 char *code = NULL; 729 err = get_node(ctx->xml, node, "sppError"); 730 if (err) 731 code = xml_node_get_attr_value(ctx->xml, err, 732 "errorCode"); 733 wpa_printf(MSG_INFO, "Error occurred - errorCode=%s", 734 code ? code : "N/A"); 735 xml_node_get_attr_value_free(ctx->xml, code); 736 goto out; 737 } 738 739 wpa_printf(MSG_INFO, 740 "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'", 741 status); 742 out: 743 xml_node_get_attr_value_free(ctx->xml, status); 744 xml_node_get_attr_value_free(ctx->xml, session_id); 745 xml_node_free(ctx->xml, node); 746 } 747 748 749 static int spp_post_dev_data(struct hs20_osu_client *ctx, 750 enum spp_post_dev_data_use use, 751 const char *reason, 752 const char *pps_fname, xml_node_t *pps) 753 { 754 xml_node_t *payload; 755 xml_node_t *ret_node; 756 757 payload = build_spp_post_dev_data(ctx, NULL, NULL, reason); 758 if (payload == NULL) 759 return -1; 760 761 ret_node = soap_send_receive(ctx->http, payload); 762 if (!ret_node) { 763 const char *err = http_get_err(ctx->http); 764 if (err) { 765 wpa_printf(MSG_INFO, "HTTP error: %s", err); 766 write_result(ctx, "HTTP error: %s", err); 767 } else { 768 write_summary(ctx, "Failed to send SOAP message"); 769 } 770 return -1; 771 } 772 773 if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { 774 wpa_printf(MSG_INFO, "SPP validation failed"); 775 xml_node_free(ctx->xml, ret_node); 776 return -1; 777 } 778 779 process_spp_post_dev_data_response(ctx, use, ret_node, 780 pps_fname, pps); 781 return 0; 782 } 783 784 785 void spp_sub_rem(struct hs20_osu_client *ctx, const char *address, 786 const char *pps_fname, 787 const char *client_cert, const char *client_key, 788 const char *cred_username, const char *cred_password, 789 xml_node_t *pps) 790 { 791 wpa_printf(MSG_INFO, "SPP subscription remediation"); 792 write_summary(ctx, "SPP subscription remediation"); 793 794 os_free(ctx->server_url); 795 ctx->server_url = os_strdup(address); 796 797 if (soap_init_client(ctx->http, address, ctx->ca_fname, 798 cred_username, cred_password, client_cert, 799 client_key) == 0) { 800 spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION, 801 "Subscription remediation", pps_fname, pps); 802 } 803 } 804 805 806 static void hs20_policy_update_complete(struct hs20_osu_client *ctx, 807 const char *pps_fname) 808 { 809 wpa_printf(MSG_INFO, "Policy update completed"); 810 811 /* 812 * Update wpa_supplicant credentials and reconnect using updated 813 * information. 814 */ 815 wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials"); 816 cmd_set_pps(ctx, pps_fname); 817 818 wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); 819 if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) 820 wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect"); 821 } 822 823 824 static int process_spp_exchange_complete(struct hs20_osu_client *ctx, 825 xml_node_t *node) 826 { 827 char *status, *session_id; 828 829 debug_dump_node(ctx, "sppExchangeComplete", node); 830 831 status = get_spp_attr_value(ctx->xml, node, "sppStatus"); 832 if (status == NULL) { 833 wpa_printf(MSG_INFO, "No sppStatus attribute"); 834 return -1; 835 } 836 write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'", 837 status); 838 839 session_id = get_spp_attr_value(ctx->xml, node, "sessionID"); 840 if (session_id == NULL) { 841 wpa_printf(MSG_INFO, "No sessionID attribute"); 842 xml_node_get_attr_value_free(ctx->xml, status); 843 return -1; 844 } 845 846 wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s' sessionID: '%s'", 847 status, session_id); 848 xml_node_get_attr_value_free(ctx->xml, session_id); 849 850 if (strcasecmp(status, "Exchange complete, release TLS connection") == 851 0) { 852 xml_node_get_attr_value_free(ctx->xml, status); 853 return 0; 854 } 855 856 wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status); 857 write_summary(ctx, "Unexpected sppStatus '%s'", status); 858 xml_node_get_attr_value_free(ctx->xml, status); 859 return -1; 860 } 861 862 863 static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx, 864 const char *session_id, 865 const char *spp_status, 866 const char *error_code) 867 { 868 xml_namespace_t *ns; 869 xml_node_t *spp_node, *node; 870 871 spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns, 872 "sppUpdateResponse"); 873 if (spp_node == NULL) 874 return NULL; 875 876 xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0"); 877 xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id); 878 xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status); 879 880 if (error_code) { 881 node = xml_node_create(ctx->xml, spp_node, ns, "sppError"); 882 if (node) 883 xml_node_add_attr(ctx->xml, node, NULL, "errorCode", 884 error_code); 885 } 886 887 return spp_node; 888 } 889 890 891 static int hs20_spp_update_response(struct hs20_osu_client *ctx, 892 const char *session_id, 893 const char *spp_status, 894 const char *error_code) 895 { 896 xml_node_t *node, *ret_node; 897 int ret; 898 899 write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'", 900 spp_status, error_code); 901 node = build_spp_update_response(ctx, session_id, spp_status, 902 error_code); 903 if (node == NULL) 904 return -1; 905 ret_node = soap_send_receive(ctx->http, node); 906 if (!ret_node) { 907 if (soap_reinit_client(ctx->http) < 0) 908 return -1; 909 wpa_printf(MSG_INFO, "Try to finish with re-opened connection"); 910 node = build_spp_update_response(ctx, session_id, spp_status, 911 error_code); 912 if (node == NULL) 913 return -1; 914 ret_node = soap_send_receive(ctx->http, node); 915 if (ret_node == NULL) 916 return -1; 917 wpa_printf(MSG_INFO, "Continue with new connection"); 918 } 919 920 if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) { 921 wpa_printf(MSG_INFO, "SPP validation failed"); 922 xml_node_free(ctx->xml, ret_node); 923 return -1; 924 } 925 926 ret = process_spp_exchange_complete(ctx, ret_node); 927 xml_node_free(ctx->xml, ret_node); 928 return ret; 929 } 930 931 932 void spp_pol_upd(struct hs20_osu_client *ctx, const char *address, 933 const char *pps_fname, 934 const char *client_cert, const char *client_key, 935 const char *cred_username, const char *cred_password, 936 xml_node_t *pps) 937 { 938 wpa_printf(MSG_INFO, "SPP policy update"); 939 write_summary(ctx, "SPP policy update"); 940 941 os_free(ctx->server_url); 942 ctx->server_url = os_strdup(address); 943 944 if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username, 945 cred_password, client_cert, client_key) == 0) { 946 spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update", 947 pps_fname, pps); 948 } 949 } 950 951 952 int cmd_prov(struct hs20_osu_client *ctx, const char *url) 953 { 954 unlink("Cert/est_cert.der"); 955 unlink("Cert/est_cert.pem"); 956 957 if (url == NULL) { 958 wpa_printf(MSG_INFO, "Invalid prov command (missing URL)"); 959 return -1; 960 } 961 962 wpa_printf(MSG_INFO, 963 "Credential provisioning requested - URL: %s ca_fname: %s", 964 url, ctx->ca_fname ? ctx->ca_fname : "N/A"); 965 966 os_free(ctx->server_url); 967 ctx->server_url = os_strdup(url); 968 969 if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL, 970 NULL) < 0) 971 return -1; 972 spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION, 973 "Subscription registration", NULL, NULL); 974 975 return ctx->pps_cred_set ? 0 : -1; 976 } 977 978 979 int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url) 980 { 981 if (url == NULL) { 982 wpa_printf(MSG_INFO, "Invalid prov command (missing URL)"); 983 return -1; 984 } 985 986 wpa_printf(MSG_INFO, "SIM provisioning requested"); 987 988 os_free(ctx->server_url); 989 ctx->server_url = os_strdup(url); 990 991 wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning"); 992 993 if (wait_ip_addr(ctx->ifname, 15) < 0) { 994 wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); 995 } 996 997 if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL, 998 NULL) < 0) 999 return -1; 1000 spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION, 1001 "Subscription provisioning", NULL, NULL); 1002 1003 return ctx->pps_cred_set ? 0 : -1; 1004 } 1005