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