15b9c547cSRui Paulo /* 25b9c547cSRui Paulo * Hotspot 2.0 SPP client 35b9c547cSRui Paulo * Copyright (c) 2012-2014, Qualcomm Atheros, Inc. 45b9c547cSRui Paulo * 55b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license. 65b9c547cSRui Paulo * See README for more details. 75b9c547cSRui Paulo */ 85b9c547cSRui Paulo 95b9c547cSRui Paulo #include "includes.h" 105b9c547cSRui Paulo #include <sys/stat.h> 115b9c547cSRui Paulo 125b9c547cSRui Paulo #include "common.h" 135b9c547cSRui Paulo #include "browser.h" 145b9c547cSRui Paulo #include "wpa_ctrl.h" 155b9c547cSRui Paulo #include "wpa_helpers.h" 165b9c547cSRui Paulo #include "xml-utils.h" 175b9c547cSRui Paulo #include "http-utils.h" 185b9c547cSRui Paulo #include "utils/base64.h" 195b9c547cSRui Paulo #include "crypto/crypto.h" 205b9c547cSRui Paulo #include "crypto/sha256.h" 215b9c547cSRui Paulo #include "osu_client.h" 225b9c547cSRui Paulo 235b9c547cSRui Paulo 24325151a3SRui Paulo extern const char *spp_xsd_fname; 25325151a3SRui Paulo 265b9c547cSRui Paulo static int hs20_spp_update_response(struct hs20_osu_client *ctx, 275b9c547cSRui Paulo const char *session_id, 285b9c547cSRui Paulo const char *spp_status, 295b9c547cSRui Paulo const char *error_code); 305b9c547cSRui Paulo static void hs20_policy_update_complete( 315b9c547cSRui Paulo struct hs20_osu_client *ctx, const char *pps_fname); 325b9c547cSRui Paulo 335b9c547cSRui Paulo 345b9c547cSRui Paulo static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node, 355b9c547cSRui Paulo char *attr_name) 365b9c547cSRui Paulo { 375b9c547cSRui Paulo return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name); 385b9c547cSRui Paulo } 395b9c547cSRui Paulo 405b9c547cSRui Paulo 415b9c547cSRui Paulo static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node, 425b9c547cSRui Paulo const char *expected_name) 435b9c547cSRui Paulo { 445b9c547cSRui Paulo struct xml_node_ctx *xctx = ctx->xml; 455b9c547cSRui Paulo const char *name; 465b9c547cSRui Paulo char *err; 475b9c547cSRui Paulo int ret; 485b9c547cSRui Paulo 495b9c547cSRui Paulo if (!xml_node_is_element(xctx, node)) 505b9c547cSRui Paulo return -1; 515b9c547cSRui Paulo 525b9c547cSRui Paulo name = xml_node_get_localname(xctx, node); 535b9c547cSRui Paulo if (name == NULL) 545b9c547cSRui Paulo return -1; 555b9c547cSRui Paulo 565b9c547cSRui Paulo if (strcmp(expected_name, name) != 0) { 575b9c547cSRui Paulo wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')", 585b9c547cSRui Paulo name, expected_name); 595b9c547cSRui Paulo write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')", 605b9c547cSRui Paulo name, expected_name); 615b9c547cSRui Paulo return -1; 625b9c547cSRui Paulo } 635b9c547cSRui Paulo 64325151a3SRui Paulo ret = xml_validate(xctx, node, spp_xsd_fname, &err); 655b9c547cSRui Paulo if (ret < 0) { 665b9c547cSRui Paulo wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err); 675b9c547cSRui Paulo write_summary(ctx, "SPP XML schema validation failed"); 685b9c547cSRui Paulo os_free(err); 695b9c547cSRui Paulo } 705b9c547cSRui Paulo return ret; 715b9c547cSRui Paulo } 725b9c547cSRui Paulo 735b9c547cSRui Paulo 745b9c547cSRui Paulo static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns, 755b9c547cSRui Paulo xml_node_t *parent, const char *urn, 765b9c547cSRui Paulo const char *fname) 775b9c547cSRui Paulo { 785b9c547cSRui Paulo xml_node_t *node; 795b9c547cSRui Paulo xml_node_t *fnode, *tnds; 805b9c547cSRui Paulo char *str; 815b9c547cSRui Paulo 82325151a3SRui Paulo errno = 0; 835b9c547cSRui Paulo fnode = node_from_file(ctx, fname); 84325151a3SRui Paulo if (!fnode) { 85325151a3SRui Paulo wpa_printf(MSG_ERROR, 86325151a3SRui Paulo "Failed to create XML node from file: %s, possible error: %s", 87325151a3SRui Paulo fname, strerror(errno)); 885b9c547cSRui Paulo return; 89325151a3SRui Paulo } 905b9c547cSRui Paulo tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2"); 915b9c547cSRui Paulo xml_node_free(ctx, fnode); 925b9c547cSRui Paulo if (!tnds) 935b9c547cSRui Paulo return; 945b9c547cSRui Paulo 955b9c547cSRui Paulo str = xml_node_to_str(ctx, tnds); 965b9c547cSRui Paulo xml_node_free(ctx, tnds); 975b9c547cSRui Paulo if (str == NULL) 985b9c547cSRui Paulo return; 995b9c547cSRui Paulo 1005b9c547cSRui Paulo node = xml_node_create_text(ctx, parent, ns, "moContainer", str); 1015b9c547cSRui Paulo if (node) 1025b9c547cSRui Paulo xml_node_add_attr(ctx, node, ns, "moURN", urn); 1035b9c547cSRui Paulo os_free(str); 1045b9c547cSRui Paulo } 1055b9c547cSRui Paulo 1065b9c547cSRui Paulo 1075b9c547cSRui Paulo static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx, 1085b9c547cSRui Paulo xml_namespace_t **ret_ns, 1095b9c547cSRui Paulo const char *session_id, 1105b9c547cSRui Paulo const char *reason) 1115b9c547cSRui Paulo { 1125b9c547cSRui Paulo xml_namespace_t *ns; 1135b9c547cSRui Paulo xml_node_t *spp_node; 1145b9c547cSRui Paulo 1155b9c547cSRui Paulo write_summary(ctx, "Building sppPostDevData requestReason='%s'", 1165b9c547cSRui Paulo reason); 1175b9c547cSRui Paulo spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns, 1185b9c547cSRui Paulo "sppPostDevData"); 1195b9c547cSRui Paulo if (spp_node == NULL) 1205b9c547cSRui Paulo return NULL; 1215b9c547cSRui Paulo if (ret_ns) 1225b9c547cSRui Paulo *ret_ns = ns; 1235b9c547cSRui Paulo 1245b9c547cSRui Paulo xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0"); 1255b9c547cSRui Paulo xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason); 1265b9c547cSRui Paulo if (session_id) 1275b9c547cSRui Paulo xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", 1285b9c547cSRui Paulo session_id); 1295b9c547cSRui Paulo xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI", 1305b9c547cSRui Paulo "http://localhost:12345/"); 1315b9c547cSRui Paulo 1325b9c547cSRui Paulo xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions", 1335b9c547cSRui Paulo "1.0"); 1345b9c547cSRui Paulo xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList", 1355b9c547cSRui Paulo URN_HS20_PPS " " URN_OMA_DM_DEVINFO " " 1365b9c547cSRui Paulo URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT); 1375b9c547cSRui Paulo 1385b9c547cSRui Paulo add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO, 1395b9c547cSRui Paulo "devinfo.xml"); 1405b9c547cSRui Paulo add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL, 1415b9c547cSRui Paulo "devdetail.xml"); 1425b9c547cSRui Paulo 1435b9c547cSRui Paulo return spp_node; 1445b9c547cSRui Paulo } 1455b9c547cSRui Paulo 1465b9c547cSRui Paulo 1475b9c547cSRui Paulo static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps, 1485b9c547cSRui Paulo xml_node_t *update) 1495b9c547cSRui Paulo { 1505b9c547cSRui Paulo xml_node_t *node, *parent, *tnds, *unode; 1515b9c547cSRui Paulo char *str; 1525b9c547cSRui Paulo const char *name; 1535b9c547cSRui Paulo char *uri, *pos; 1545b9c547cSRui Paulo char *cdata, *cdata_end; 1555b9c547cSRui Paulo size_t fqdn_len; 1565b9c547cSRui Paulo 1575b9c547cSRui Paulo wpa_printf(MSG_INFO, "Processing updateNode"); 1585b9c547cSRui Paulo debug_dump_node(ctx, "updateNode", update); 1595b9c547cSRui Paulo 1605b9c547cSRui Paulo uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI"); 1615b9c547cSRui Paulo if (uri == NULL) { 1625b9c547cSRui Paulo wpa_printf(MSG_INFO, "No managementTreeURI present"); 1635b9c547cSRui Paulo return -1; 1645b9c547cSRui Paulo } 1655b9c547cSRui Paulo wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri); 1665b9c547cSRui Paulo 1675b9c547cSRui Paulo name = os_strrchr(uri, '/'); 1685b9c547cSRui Paulo if (name == NULL) { 1695b9c547cSRui Paulo wpa_printf(MSG_INFO, "Unexpected URI"); 1705b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 1715b9c547cSRui Paulo return -1; 1725b9c547cSRui Paulo } 1735b9c547cSRui Paulo name++; 1745b9c547cSRui Paulo wpa_printf(MSG_INFO, "Update interior node: '%s'", name); 1755b9c547cSRui Paulo 1765b9c547cSRui Paulo str = xml_node_get_text(ctx->xml, update); 1775b9c547cSRui Paulo if (str == NULL) { 1785b9c547cSRui Paulo wpa_printf(MSG_INFO, "Could not extract MO text"); 1795b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 1805b9c547cSRui Paulo return -1; 1815b9c547cSRui Paulo } 1825b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str); 1835b9c547cSRui Paulo cdata = strstr(str, "<![CDATA["); 1845b9c547cSRui Paulo cdata_end = strstr(str, "]]>"); 1855b9c547cSRui Paulo if (cdata && cdata_end && cdata_end > cdata && 1865b9c547cSRui Paulo cdata < strstr(str, "MgmtTree") && 1875b9c547cSRui Paulo cdata_end > strstr(str, "/MgmtTree")) { 1885b9c547cSRui Paulo char *tmp; 1895b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container"); 1905b9c547cSRui Paulo tmp = strdup(cdata + 9); 1915b9c547cSRui Paulo if (tmp) { 1925b9c547cSRui Paulo cdata_end = strstr(tmp, "]]>"); 1935b9c547cSRui Paulo if (cdata_end) 1945b9c547cSRui Paulo *cdata_end = '\0'; 1955b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'", 1965b9c547cSRui Paulo tmp); 1975b9c547cSRui Paulo tnds = xml_node_from_buf(ctx->xml, tmp); 1985b9c547cSRui Paulo free(tmp); 1995b9c547cSRui Paulo } else 2005b9c547cSRui Paulo tnds = NULL; 2015b9c547cSRui Paulo } else 2025b9c547cSRui Paulo tnds = xml_node_from_buf(ctx->xml, str); 2035b9c547cSRui Paulo xml_node_get_text_free(ctx->xml, str); 2045b9c547cSRui Paulo if (tnds == NULL) { 2055b9c547cSRui Paulo wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text"); 2065b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 2075b9c547cSRui Paulo return -1; 2085b9c547cSRui Paulo } 2095b9c547cSRui Paulo 2105b9c547cSRui Paulo unode = tnds_to_mo(ctx->xml, tnds); 2115b9c547cSRui Paulo xml_node_free(ctx->xml, tnds); 2125b9c547cSRui Paulo if (unode == NULL) { 2135b9c547cSRui Paulo wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text"); 2145b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 2155b9c547cSRui Paulo return -1; 2165b9c547cSRui Paulo } 2175b9c547cSRui Paulo 2185b9c547cSRui Paulo debug_dump_node(ctx, "Parsed TNDS", unode); 2195b9c547cSRui Paulo 2205b9c547cSRui Paulo if (get_node_uri(ctx->xml, unode, name) == NULL) { 2215b9c547cSRui Paulo wpa_printf(MSG_INFO, "[hs20] %s node not found", name); 2225b9c547cSRui Paulo xml_node_free(ctx->xml, unode); 2235b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 2245b9c547cSRui Paulo return -1; 2255b9c547cSRui Paulo } 2265b9c547cSRui Paulo 2275b9c547cSRui Paulo if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) { 2285b9c547cSRui Paulo wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi"); 2295b9c547cSRui Paulo xml_node_free(ctx->xml, unode); 2305b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 2315b9c547cSRui Paulo return -1; 2325b9c547cSRui Paulo } 2335b9c547cSRui Paulo pos = uri + 8; 2345b9c547cSRui Paulo 2355b9c547cSRui Paulo if (ctx->fqdn == NULL) { 2365b9c547cSRui Paulo wpa_printf(MSG_INFO, "FQDN not known"); 2375b9c547cSRui Paulo xml_node_free(ctx->xml, unode); 2385b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 2395b9c547cSRui Paulo return -1; 2405b9c547cSRui Paulo } 2415b9c547cSRui Paulo fqdn_len = os_strlen(ctx->fqdn); 2425b9c547cSRui Paulo if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 || 2435b9c547cSRui Paulo pos[fqdn_len] != '/') { 2445b9c547cSRui Paulo wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s", 2455b9c547cSRui Paulo ctx->fqdn); 2465b9c547cSRui Paulo xml_node_free(ctx->xml, unode); 2475b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 2485b9c547cSRui Paulo return -1; 2495b9c547cSRui Paulo } 2505b9c547cSRui Paulo pos += fqdn_len + 1; 2515b9c547cSRui Paulo 2525b9c547cSRui Paulo if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) { 2535b9c547cSRui Paulo wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription", 2545b9c547cSRui Paulo ctx->fqdn); 2555b9c547cSRui Paulo xml_node_free(ctx->xml, unode); 2565b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 2575b9c547cSRui Paulo return -1; 2585b9c547cSRui Paulo } 2595b9c547cSRui Paulo pos += 24; 2605b9c547cSRui Paulo 2615b9c547cSRui Paulo wpa_printf(MSG_INFO, "Update command for PPS node %s", pos); 2625b9c547cSRui Paulo 2635b9c547cSRui Paulo node = get_node(ctx->xml, pps, pos); 2645b9c547cSRui Paulo if (node) { 2655b9c547cSRui Paulo parent = xml_node_get_parent(ctx->xml, node); 2665b9c547cSRui Paulo xml_node_detach(ctx->xml, node); 2675b9c547cSRui Paulo wpa_printf(MSG_INFO, "Replace '%s' node", name); 2685b9c547cSRui Paulo } else { 2695b9c547cSRui Paulo char *pos2; 2705b9c547cSRui Paulo pos2 = os_strrchr(pos, '/'); 2715b9c547cSRui Paulo if (pos2 == NULL) { 2725b9c547cSRui Paulo parent = pps; 2735b9c547cSRui Paulo } else { 2745b9c547cSRui Paulo *pos2 = '\0'; 2755b9c547cSRui Paulo parent = get_node(ctx->xml, pps, pos); 2765b9c547cSRui Paulo } 2775b9c547cSRui Paulo if (parent == NULL) { 2785b9c547cSRui Paulo wpa_printf(MSG_INFO, "Could not find parent %s", pos); 2795b9c547cSRui Paulo xml_node_free(ctx->xml, unode); 2805b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 2815b9c547cSRui Paulo return -1; 2825b9c547cSRui Paulo } 2835b9c547cSRui Paulo wpa_printf(MSG_INFO, "Add '%s' node", name); 2845b9c547cSRui Paulo } 2855b9c547cSRui Paulo xml_node_add_child(ctx->xml, parent, unode); 2865b9c547cSRui Paulo 2875b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 2885b9c547cSRui Paulo 2895b9c547cSRui Paulo return 0; 2905b9c547cSRui Paulo } 2915b9c547cSRui Paulo 2925b9c547cSRui Paulo 2935b9c547cSRui Paulo static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update, 2945b9c547cSRui Paulo const char *pps_fname, xml_node_t *pps) 2955b9c547cSRui Paulo { 2965b9c547cSRui Paulo wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)"); 2975b9c547cSRui Paulo xml_node_for_each_sibling(ctx->xml, update) { 2985b9c547cSRui Paulo xml_node_for_each_check(ctx->xml, update); 2995b9c547cSRui Paulo if (process_update_node(ctx, pps, update) < 0) 3005b9c547cSRui Paulo return -1; 3015b9c547cSRui Paulo } 3025b9c547cSRui Paulo 3035b9c547cSRui Paulo return update_pps_file(ctx, pps_fname, pps); 3045b9c547cSRui Paulo } 3055b9c547cSRui Paulo 3065b9c547cSRui Paulo 3075b9c547cSRui Paulo static void hs20_sub_rem_complete(struct hs20_osu_client *ctx, 3085b9c547cSRui Paulo const char *pps_fname) 3095b9c547cSRui Paulo { 3105b9c547cSRui Paulo /* 3115b9c547cSRui Paulo * Update wpa_supplicant credentials and reconnect using updated 3125b9c547cSRui Paulo * information. 3135b9c547cSRui Paulo */ 3145b9c547cSRui Paulo wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials"); 3155b9c547cSRui Paulo cmd_set_pps(ctx, pps_fname); 3165b9c547cSRui Paulo 3175b9c547cSRui Paulo if (ctx->no_reconnect) 3185b9c547cSRui Paulo return; 3195b9c547cSRui Paulo 3205b9c547cSRui Paulo wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); 3215b9c547cSRui Paulo if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) 3225b9c547cSRui Paulo wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect"); 3235b9c547cSRui Paulo } 3245b9c547cSRui Paulo 3255b9c547cSRui Paulo 3265b9c547cSRui Paulo static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx, 3275b9c547cSRui Paulo xml_node_t *cmd, 3285b9c547cSRui Paulo const char *session_id, 3295b9c547cSRui Paulo const char *pps_fname) 3305b9c547cSRui Paulo { 3315b9c547cSRui Paulo xml_namespace_t *ns; 3325b9c547cSRui Paulo xml_node_t *node, *ret_node; 3335b9c547cSRui Paulo char *urn; 3345b9c547cSRui Paulo 3355b9c547cSRui Paulo urn = get_spp_attr_value(ctx->xml, cmd, "moURN"); 3365b9c547cSRui Paulo if (!urn) { 3375b9c547cSRui Paulo wpa_printf(MSG_INFO, "No URN included"); 3385b9c547cSRui Paulo return NULL; 3395b9c547cSRui Paulo } 3405b9c547cSRui Paulo wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn); 3415b9c547cSRui Paulo if (strcasecmp(urn, URN_HS20_PPS) != 0) { 3425b9c547cSRui Paulo wpa_printf(MSG_INFO, "Unsupported moURN"); 3435b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, urn); 3445b9c547cSRui Paulo return NULL; 3455b9c547cSRui Paulo } 3465b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, urn); 3475b9c547cSRui Paulo 3485b9c547cSRui Paulo if (!pps_fname) { 3495b9c547cSRui Paulo wpa_printf(MSG_INFO, "PPS file name no known"); 3505b9c547cSRui Paulo return NULL; 3515b9c547cSRui Paulo } 3525b9c547cSRui Paulo 3535b9c547cSRui Paulo node = build_spp_post_dev_data(ctx, &ns, session_id, 3545b9c547cSRui Paulo "MO upload"); 3555b9c547cSRui Paulo if (node == NULL) 3565b9c547cSRui Paulo return NULL; 3575b9c547cSRui Paulo add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname); 3585b9c547cSRui Paulo 3595b9c547cSRui Paulo ret_node = soap_send_receive(ctx->http, node); 3605b9c547cSRui Paulo if (ret_node == NULL) 3615b9c547cSRui Paulo return NULL; 3625b9c547cSRui Paulo 3635b9c547cSRui Paulo debug_dump_node(ctx, "Received response to MO upload", ret_node); 3645b9c547cSRui Paulo 3655b9c547cSRui Paulo if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { 3665b9c547cSRui Paulo wpa_printf(MSG_INFO, "SPP validation failed"); 3675b9c547cSRui Paulo xml_node_free(ctx->xml, ret_node); 3685b9c547cSRui Paulo return NULL; 3695b9c547cSRui Paulo } 3705b9c547cSRui Paulo 3715b9c547cSRui Paulo return ret_node; 3725b9c547cSRui Paulo } 3735b9c547cSRui Paulo 3745b9c547cSRui Paulo 3755b9c547cSRui Paulo static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo, 3765b9c547cSRui Paulo char *fname, size_t fname_len) 3775b9c547cSRui Paulo { 3785b9c547cSRui Paulo char *uri, *urn; 3795b9c547cSRui Paulo int ret; 3805b9c547cSRui Paulo 3815b9c547cSRui Paulo debug_dump_node(ctx, "Received addMO", add_mo); 3825b9c547cSRui Paulo 3835b9c547cSRui Paulo urn = get_spp_attr_value(ctx->xml, add_mo, "moURN"); 3845b9c547cSRui Paulo if (urn == NULL) { 3855b9c547cSRui Paulo wpa_printf(MSG_INFO, "[hs20] No moURN in addMO"); 3865b9c547cSRui Paulo return -1; 3875b9c547cSRui Paulo } 3885b9c547cSRui Paulo wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn); 3895b9c547cSRui Paulo if (strcasecmp(urn, URN_HS20_PPS) != 0) { 3905b9c547cSRui Paulo wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO"); 3915b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, urn); 3925b9c547cSRui Paulo return -1; 3935b9c547cSRui Paulo } 3945b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, urn); 3955b9c547cSRui Paulo 3965b9c547cSRui Paulo uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI"); 3975b9c547cSRui Paulo if (uri == NULL) { 3985b9c547cSRui Paulo wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO"); 3995b9c547cSRui Paulo return -1; 4005b9c547cSRui Paulo } 4015b9c547cSRui Paulo wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri); 4025b9c547cSRui Paulo 4035b9c547cSRui Paulo ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len); 4045b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, uri); 4055b9c547cSRui Paulo return ret; 4065b9c547cSRui Paulo } 4075b9c547cSRui Paulo 4085b9c547cSRui Paulo 4095b9c547cSRui Paulo static int process_spp_user_input_response(struct hs20_osu_client *ctx, 4105b9c547cSRui Paulo const char *session_id, 4115b9c547cSRui Paulo xml_node_t *add_mo) 4125b9c547cSRui Paulo { 4135b9c547cSRui Paulo int ret; 4145b9c547cSRui Paulo char fname[300]; 4155b9c547cSRui Paulo 4165b9c547cSRui Paulo debug_dump_node(ctx, "addMO", add_mo); 4175b9c547cSRui Paulo 4185b9c547cSRui Paulo wpa_printf(MSG_INFO, "Subscription registration completed"); 4195b9c547cSRui Paulo 4205b9c547cSRui Paulo if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) { 4215b9c547cSRui Paulo wpa_printf(MSG_INFO, "Could not add MO"); 4225b9c547cSRui Paulo ret = hs20_spp_update_response( 4235b9c547cSRui Paulo ctx, session_id, 4245b9c547cSRui Paulo "Error occurred", 4255b9c547cSRui Paulo "MO addition or update failed"); 4265b9c547cSRui Paulo return 0; 4275b9c547cSRui Paulo } 4285b9c547cSRui Paulo 4295b9c547cSRui Paulo ret = hs20_spp_update_response(ctx, session_id, "OK", NULL); 4305b9c547cSRui Paulo if (ret == 0) 4315b9c547cSRui Paulo hs20_sub_rem_complete(ctx, fname); 4325b9c547cSRui Paulo 4335b9c547cSRui Paulo return 0; 4345b9c547cSRui Paulo } 4355b9c547cSRui Paulo 4365b9c547cSRui Paulo 4375b9c547cSRui Paulo static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx, 4385b9c547cSRui Paulo const char *session_id) 4395b9c547cSRui Paulo { 4405b9c547cSRui Paulo xml_node_t *node, *ret_node; 4415b9c547cSRui Paulo 4425b9c547cSRui Paulo node = build_spp_post_dev_data(ctx, NULL, session_id, 4435b9c547cSRui Paulo "User input completed"); 4445b9c547cSRui Paulo if (node == NULL) 4455b9c547cSRui Paulo return NULL; 4465b9c547cSRui Paulo 4475b9c547cSRui Paulo ret_node = soap_send_receive(ctx->http, node); 4485b9c547cSRui Paulo if (!ret_node) { 4495b9c547cSRui Paulo if (soap_reinit_client(ctx->http) < 0) 4505b9c547cSRui Paulo return NULL; 4515b9c547cSRui Paulo wpa_printf(MSG_INFO, "Try to finish with re-opened connection"); 4525b9c547cSRui Paulo node = build_spp_post_dev_data(ctx, NULL, session_id, 4535b9c547cSRui Paulo "User input completed"); 4545b9c547cSRui Paulo if (node == NULL) 4555b9c547cSRui Paulo return NULL; 4565b9c547cSRui Paulo ret_node = soap_send_receive(ctx->http, node); 4575b9c547cSRui Paulo if (ret_node == NULL) 4585b9c547cSRui Paulo return NULL; 4595b9c547cSRui Paulo wpa_printf(MSG_INFO, "Continue with new connection"); 4605b9c547cSRui Paulo } 4615b9c547cSRui Paulo 4625b9c547cSRui Paulo if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { 4635b9c547cSRui Paulo wpa_printf(MSG_INFO, "SPP validation failed"); 4645b9c547cSRui Paulo xml_node_free(ctx->xml, ret_node); 4655b9c547cSRui Paulo return NULL; 4665b9c547cSRui Paulo } 4675b9c547cSRui Paulo 4685b9c547cSRui Paulo return ret_node; 4695b9c547cSRui Paulo } 4705b9c547cSRui Paulo 4715b9c547cSRui Paulo 4725b9c547cSRui Paulo static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx, 4735b9c547cSRui Paulo xml_node_t *cmd, 4745b9c547cSRui Paulo const char *session_id, 4755b9c547cSRui Paulo const char *pps_fname) 4765b9c547cSRui Paulo { 4775b9c547cSRui Paulo xml_namespace_t *ns; 4785b9c547cSRui Paulo xml_node_t *node, *ret_node; 4795b9c547cSRui Paulo int res; 4805b9c547cSRui Paulo 4815b9c547cSRui Paulo wpa_printf(MSG_INFO, "Client certificate enrollment"); 4825b9c547cSRui Paulo 4835b9c547cSRui Paulo res = osu_get_certificate(ctx, cmd); 4845b9c547cSRui Paulo if (res < 0) 4855b9c547cSRui Paulo wpa_printf(MSG_INFO, "EST simpleEnroll failed"); 4865b9c547cSRui Paulo 4875b9c547cSRui Paulo node = build_spp_post_dev_data(ctx, &ns, session_id, 4885b9c547cSRui Paulo res == 0 ? 4895b9c547cSRui Paulo "Certificate enrollment completed" : 4905b9c547cSRui Paulo "Certificate enrollment failed"); 4915b9c547cSRui Paulo if (node == NULL) 4925b9c547cSRui Paulo return NULL; 4935b9c547cSRui Paulo 4945b9c547cSRui Paulo ret_node = soap_send_receive(ctx->http, node); 4955b9c547cSRui Paulo if (ret_node == NULL) 4965b9c547cSRui Paulo return NULL; 4975b9c547cSRui Paulo 4985b9c547cSRui Paulo debug_dump_node(ctx, "Received response to certificate enrollment " 4995b9c547cSRui Paulo "completed", ret_node); 5005b9c547cSRui Paulo 5015b9c547cSRui Paulo if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { 5025b9c547cSRui Paulo wpa_printf(MSG_INFO, "SPP validation failed"); 5035b9c547cSRui Paulo xml_node_free(ctx->xml, ret_node); 5045b9c547cSRui Paulo return NULL; 5055b9c547cSRui Paulo } 5065b9c547cSRui Paulo 5075b9c547cSRui Paulo return ret_node; 5085b9c547cSRui Paulo } 5095b9c547cSRui Paulo 5105b9c547cSRui Paulo 5115b9c547cSRui Paulo static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec, 5125b9c547cSRui Paulo const char *session_id, const char *pps_fname, 5135b9c547cSRui Paulo xml_node_t *pps, xml_node_t **ret_node) 5145b9c547cSRui Paulo { 5155b9c547cSRui Paulo xml_node_t *cmd; 5165b9c547cSRui Paulo const char *name; 5175b9c547cSRui Paulo char *uri; 5185b9c547cSRui Paulo char *id = strdup(session_id); 5195b9c547cSRui Paulo 5205b9c547cSRui Paulo if (id == NULL) 5215b9c547cSRui Paulo return -1; 5225b9c547cSRui Paulo 5235b9c547cSRui Paulo *ret_node = NULL; 5245b9c547cSRui Paulo 5255b9c547cSRui Paulo debug_dump_node(ctx, "exec", exec); 5265b9c547cSRui Paulo 5275b9c547cSRui Paulo xml_node_for_each_child(ctx->xml, cmd, exec) { 5285b9c547cSRui Paulo xml_node_for_each_check(ctx->xml, cmd); 5295b9c547cSRui Paulo break; 5305b9c547cSRui Paulo } 5315b9c547cSRui Paulo if (!cmd) { 5325b9c547cSRui Paulo wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)", 5335b9c547cSRui Paulo cmd); 5345b9c547cSRui Paulo free(id); 5355b9c547cSRui Paulo return -1; 5365b9c547cSRui Paulo } 5375b9c547cSRui Paulo 5385b9c547cSRui Paulo name = xml_node_get_localname(ctx->xml, cmd); 5395b9c547cSRui Paulo 5405b9c547cSRui Paulo if (strcasecmp(name, "launchBrowserToURI") == 0) { 5415b9c547cSRui Paulo int res; 5425b9c547cSRui Paulo uri = xml_node_get_text(ctx->xml, cmd); 5435b9c547cSRui Paulo if (!uri) { 5445b9c547cSRui Paulo wpa_printf(MSG_INFO, "No URI found"); 5455b9c547cSRui Paulo free(id); 5465b9c547cSRui Paulo return -1; 5475b9c547cSRui Paulo } 5485b9c547cSRui Paulo wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri); 5495b9c547cSRui Paulo write_summary(ctx, "Launch browser to URI '%s'", uri); 550*c1d255d3SCy Schubert res = hs20_web_browser(uri, 1); 5515b9c547cSRui Paulo xml_node_get_text_free(ctx->xml, uri); 5525b9c547cSRui Paulo if (res > 0) { 5535b9c547cSRui Paulo wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'", 5545b9c547cSRui Paulo id); 5555b9c547cSRui Paulo write_summary(ctx, "User response in browser completed successfully"); 5565b9c547cSRui Paulo *ret_node = hs20_spp_user_input_completed(ctx, id); 5575b9c547cSRui Paulo free(id); 5585b9c547cSRui Paulo return *ret_node ? 0 : -1; 5595b9c547cSRui Paulo } else { 5605b9c547cSRui Paulo wpa_printf(MSG_INFO, "Failed to receive user response"); 5615b9c547cSRui Paulo write_summary(ctx, "Failed to receive user response"); 5625b9c547cSRui Paulo hs20_spp_update_response( 5635b9c547cSRui Paulo ctx, id, "Error occurred", "Other"); 5645b9c547cSRui Paulo free(id); 5655b9c547cSRui Paulo return -1; 5665b9c547cSRui Paulo } 5675b9c547cSRui Paulo } 5685b9c547cSRui Paulo 5695b9c547cSRui Paulo if (strcasecmp(name, "uploadMO") == 0) { 5705b9c547cSRui Paulo if (pps_fname == NULL) 5715b9c547cSRui Paulo return -1; 5725b9c547cSRui Paulo *ret_node = hs20_spp_upload_mo(ctx, cmd, id, 5735b9c547cSRui Paulo pps_fname); 5745b9c547cSRui Paulo free(id); 5755b9c547cSRui Paulo return *ret_node ? 0 : -1; 5765b9c547cSRui Paulo } 5775b9c547cSRui Paulo 5785b9c547cSRui Paulo if (strcasecmp(name, "getCertificate") == 0) { 5795b9c547cSRui Paulo *ret_node = hs20_spp_get_certificate(ctx, cmd, id, 5805b9c547cSRui Paulo pps_fname); 5815b9c547cSRui Paulo free(id); 5825b9c547cSRui Paulo return *ret_node ? 0 : -1; 5835b9c547cSRui Paulo } 5845b9c547cSRui Paulo 5855b9c547cSRui Paulo wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name); 5865b9c547cSRui Paulo free(id); 5875b9c547cSRui Paulo return -1; 5885b9c547cSRui Paulo } 5895b9c547cSRui Paulo 5905b9c547cSRui Paulo 5915b9c547cSRui Paulo enum spp_post_dev_data_use { 5925b9c547cSRui Paulo SPP_SUBSCRIPTION_REMEDIATION, 5935b9c547cSRui Paulo SPP_POLICY_UPDATE, 5945b9c547cSRui Paulo SPP_SUBSCRIPTION_REGISTRATION, 5955b9c547cSRui Paulo }; 5965b9c547cSRui Paulo 5975b9c547cSRui Paulo static void process_spp_post_dev_data_response( 5985b9c547cSRui Paulo struct hs20_osu_client *ctx, 5995b9c547cSRui Paulo enum spp_post_dev_data_use use, xml_node_t *node, 6005b9c547cSRui Paulo const char *pps_fname, xml_node_t *pps) 6015b9c547cSRui Paulo { 6025b9c547cSRui Paulo xml_node_t *child; 6035b9c547cSRui Paulo char *status = NULL; 6045b9c547cSRui Paulo xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL; 6055b9c547cSRui Paulo char *session_id = NULL; 6065b9c547cSRui Paulo 6075b9c547cSRui Paulo debug_dump_node(ctx, "sppPostDevDataResponse node", node); 6085b9c547cSRui Paulo 6095b9c547cSRui Paulo status = get_spp_attr_value(ctx->xml, node, "sppStatus"); 6105b9c547cSRui Paulo if (status == NULL) { 6115b9c547cSRui Paulo wpa_printf(MSG_INFO, "No sppStatus attribute"); 6125b9c547cSRui Paulo goto out; 6135b9c547cSRui Paulo } 6145b9c547cSRui Paulo write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'", 6155b9c547cSRui Paulo status); 6165b9c547cSRui Paulo 6175b9c547cSRui Paulo session_id = get_spp_attr_value(ctx->xml, node, "sessionID"); 6185b9c547cSRui Paulo if (session_id == NULL) { 6195b9c547cSRui Paulo wpa_printf(MSG_INFO, "No sessionID attribute"); 6205b9c547cSRui Paulo goto out; 6215b9c547cSRui Paulo } 6225b9c547cSRui Paulo 6235b9c547cSRui Paulo wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s' sessionID: '%s'", 6245b9c547cSRui Paulo status, session_id); 6255b9c547cSRui Paulo 6265b9c547cSRui Paulo xml_node_for_each_child(ctx->xml, child, node) { 6275b9c547cSRui Paulo const char *name; 6285b9c547cSRui Paulo xml_node_for_each_check(ctx->xml, child); 6295b9c547cSRui Paulo debug_dump_node(ctx, "child", child); 6305b9c547cSRui Paulo name = xml_node_get_localname(ctx->xml, child); 6315b9c547cSRui Paulo wpa_printf(MSG_INFO, "localname: '%s'", name); 6325b9c547cSRui Paulo if (!update && strcasecmp(name, "updateNode") == 0) 6335b9c547cSRui Paulo update = child; 6345b9c547cSRui Paulo if (!exec && strcasecmp(name, "exec") == 0) 6355b9c547cSRui Paulo exec = child; 6365b9c547cSRui Paulo if (!add_mo && strcasecmp(name, "addMO") == 0) 6375b9c547cSRui Paulo add_mo = child; 6385b9c547cSRui Paulo if (!no_mo && strcasecmp(name, "noMOUpdate") == 0) 6395b9c547cSRui Paulo no_mo = child; 6405b9c547cSRui Paulo } 6415b9c547cSRui Paulo 6425b9c547cSRui Paulo if (use == SPP_SUBSCRIPTION_REMEDIATION && 6435b9c547cSRui Paulo strcasecmp(status, 6445b9c547cSRui Paulo "Remediation complete, request sppUpdateResponse") == 0) 6455b9c547cSRui Paulo { 6465b9c547cSRui Paulo int res, ret; 6475b9c547cSRui Paulo if (!update && !no_mo) { 6485b9c547cSRui Paulo wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element"); 6495b9c547cSRui Paulo goto out; 6505b9c547cSRui Paulo } 6515b9c547cSRui Paulo wpa_printf(MSG_INFO, "Subscription remediation completed"); 6525b9c547cSRui Paulo res = update_pps(ctx, update, pps_fname, pps); 6535b9c547cSRui Paulo if (res < 0) 6545b9c547cSRui Paulo wpa_printf(MSG_INFO, "Failed to update PPS MO"); 6555b9c547cSRui Paulo ret = hs20_spp_update_response( 6565b9c547cSRui Paulo ctx, session_id, 6575b9c547cSRui Paulo res < 0 ? "Error occurred" : "OK", 6585b9c547cSRui Paulo res < 0 ? "MO addition or update failed" : NULL); 6595b9c547cSRui Paulo if (res == 0 && ret == 0) 6605b9c547cSRui Paulo hs20_sub_rem_complete(ctx, pps_fname); 6615b9c547cSRui Paulo goto out; 6625b9c547cSRui Paulo } 6635b9c547cSRui Paulo 6645b9c547cSRui Paulo if (use == SPP_SUBSCRIPTION_REMEDIATION && 6655b9c547cSRui Paulo strcasecmp(status, "Exchange complete, release TLS connection") == 6665b9c547cSRui Paulo 0) { 6675b9c547cSRui Paulo if (!no_mo) { 6685b9c547cSRui Paulo wpa_printf(MSG_INFO, "No noMOUpdate element"); 6695b9c547cSRui Paulo goto out; 6705b9c547cSRui Paulo } 6715b9c547cSRui Paulo wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)"); 6725b9c547cSRui Paulo goto out; 6735b9c547cSRui Paulo } 6745b9c547cSRui Paulo 6755b9c547cSRui Paulo if (use == SPP_POLICY_UPDATE && 6765b9c547cSRui Paulo strcasecmp(status, "Update complete, request sppUpdateResponse") == 6775b9c547cSRui Paulo 0) { 6785b9c547cSRui Paulo int res, ret; 6795b9c547cSRui Paulo wpa_printf(MSG_INFO, "Policy update received - update PPS"); 6805b9c547cSRui Paulo res = update_pps(ctx, update, pps_fname, pps); 6815b9c547cSRui Paulo ret = hs20_spp_update_response( 6825b9c547cSRui Paulo ctx, session_id, 6835b9c547cSRui Paulo res < 0 ? "Error occurred" : "OK", 6845b9c547cSRui Paulo res < 0 ? "MO addition or update failed" : NULL); 6855b9c547cSRui Paulo if (res == 0 && ret == 0) 6865b9c547cSRui Paulo hs20_policy_update_complete(ctx, pps_fname); 6875b9c547cSRui Paulo goto out; 6885b9c547cSRui Paulo } 6895b9c547cSRui Paulo 6905b9c547cSRui Paulo if (use == SPP_SUBSCRIPTION_REGISTRATION && 6915b9c547cSRui Paulo strcasecmp(status, "Provisioning complete, request " 6925b9c547cSRui Paulo "sppUpdateResponse") == 0) { 6935b9c547cSRui Paulo if (!add_mo) { 6945b9c547cSRui Paulo wpa_printf(MSG_INFO, "No addMO element - not sure what to do next"); 6955b9c547cSRui Paulo goto out; 6965b9c547cSRui Paulo } 6975b9c547cSRui Paulo process_spp_user_input_response(ctx, session_id, add_mo); 6985b9c547cSRui Paulo node = NULL; 6995b9c547cSRui Paulo goto out; 7005b9c547cSRui Paulo } 7015b9c547cSRui Paulo 7025b9c547cSRui Paulo if (strcasecmp(status, "No update available at this time") == 0) { 7035b9c547cSRui Paulo wpa_printf(MSG_INFO, "No update available at this time"); 7045b9c547cSRui Paulo goto out; 7055b9c547cSRui Paulo } 7065b9c547cSRui Paulo 7075b9c547cSRui Paulo if (strcasecmp(status, "OK") == 0) { 7085b9c547cSRui Paulo int res; 7095b9c547cSRui Paulo xml_node_t *ret; 7105b9c547cSRui Paulo 7115b9c547cSRui Paulo if (!exec) { 7125b9c547cSRui Paulo wpa_printf(MSG_INFO, "No exec element - not sure what to do next"); 7135b9c547cSRui Paulo goto out; 7145b9c547cSRui Paulo } 7155b9c547cSRui Paulo res = hs20_spp_exec(ctx, exec, session_id, 7165b9c547cSRui Paulo pps_fname, pps, &ret); 7175b9c547cSRui Paulo /* xml_node_free(ctx->xml, node); */ 7185b9c547cSRui Paulo node = NULL; 7195b9c547cSRui Paulo if (res == 0 && ret) 7205b9c547cSRui Paulo process_spp_post_dev_data_response(ctx, use, 7215b9c547cSRui Paulo ret, pps_fname, pps); 7225b9c547cSRui Paulo goto out; 7235b9c547cSRui Paulo } 7245b9c547cSRui Paulo 7255b9c547cSRui Paulo if (strcasecmp(status, "Error occurred") == 0) { 7265b9c547cSRui Paulo xml_node_t *err; 7275b9c547cSRui Paulo char *code = NULL; 7285b9c547cSRui Paulo err = get_node(ctx->xml, node, "sppError"); 7295b9c547cSRui Paulo if (err) 7305b9c547cSRui Paulo code = xml_node_get_attr_value(ctx->xml, err, 7315b9c547cSRui Paulo "errorCode"); 7325b9c547cSRui Paulo wpa_printf(MSG_INFO, "Error occurred - errorCode=%s", 7335b9c547cSRui Paulo code ? code : "N/A"); 7345b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, code); 7355b9c547cSRui Paulo goto out; 7365b9c547cSRui Paulo } 7375b9c547cSRui Paulo 7385b9c547cSRui Paulo wpa_printf(MSG_INFO, 7395b9c547cSRui Paulo "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'", 7405b9c547cSRui Paulo status); 7415b9c547cSRui Paulo out: 7425b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, status); 7435b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, session_id); 7445b9c547cSRui Paulo xml_node_free(ctx->xml, node); 7455b9c547cSRui Paulo } 7465b9c547cSRui Paulo 7475b9c547cSRui Paulo 7485b9c547cSRui Paulo static int spp_post_dev_data(struct hs20_osu_client *ctx, 7495b9c547cSRui Paulo enum spp_post_dev_data_use use, 7505b9c547cSRui Paulo const char *reason, 7515b9c547cSRui Paulo const char *pps_fname, xml_node_t *pps) 7525b9c547cSRui Paulo { 7535b9c547cSRui Paulo xml_node_t *payload; 7545b9c547cSRui Paulo xml_node_t *ret_node; 7555b9c547cSRui Paulo 7565b9c547cSRui Paulo payload = build_spp_post_dev_data(ctx, NULL, NULL, reason); 7575b9c547cSRui Paulo if (payload == NULL) 7585b9c547cSRui Paulo return -1; 7595b9c547cSRui Paulo 7605b9c547cSRui Paulo ret_node = soap_send_receive(ctx->http, payload); 7615b9c547cSRui Paulo if (!ret_node) { 7625b9c547cSRui Paulo const char *err = http_get_err(ctx->http); 7635b9c547cSRui Paulo if (err) { 7645b9c547cSRui Paulo wpa_printf(MSG_INFO, "HTTP error: %s", err); 7655b9c547cSRui Paulo write_result(ctx, "HTTP error: %s", err); 7665b9c547cSRui Paulo } else { 7675b9c547cSRui Paulo write_summary(ctx, "Failed to send SOAP message"); 7685b9c547cSRui Paulo } 7695b9c547cSRui Paulo return -1; 7705b9c547cSRui Paulo } 7715b9c547cSRui Paulo 7725b9c547cSRui Paulo if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { 7735b9c547cSRui Paulo wpa_printf(MSG_INFO, "SPP validation failed"); 7745b9c547cSRui Paulo xml_node_free(ctx->xml, ret_node); 7755b9c547cSRui Paulo return -1; 7765b9c547cSRui Paulo } 7775b9c547cSRui Paulo 7785b9c547cSRui Paulo process_spp_post_dev_data_response(ctx, use, ret_node, 7795b9c547cSRui Paulo pps_fname, pps); 7805b9c547cSRui Paulo return 0; 7815b9c547cSRui Paulo } 7825b9c547cSRui Paulo 7835b9c547cSRui Paulo 7845b9c547cSRui Paulo void spp_sub_rem(struct hs20_osu_client *ctx, const char *address, 7855b9c547cSRui Paulo const char *pps_fname, 7865b9c547cSRui Paulo const char *client_cert, const char *client_key, 7875b9c547cSRui Paulo const char *cred_username, const char *cred_password, 7885b9c547cSRui Paulo xml_node_t *pps) 7895b9c547cSRui Paulo { 7905b9c547cSRui Paulo wpa_printf(MSG_INFO, "SPP subscription remediation"); 7915b9c547cSRui Paulo write_summary(ctx, "SPP subscription remediation"); 7925b9c547cSRui Paulo 7935b9c547cSRui Paulo os_free(ctx->server_url); 7945b9c547cSRui Paulo ctx->server_url = os_strdup(address); 7955b9c547cSRui Paulo 7965b9c547cSRui Paulo if (soap_init_client(ctx->http, address, ctx->ca_fname, 7975b9c547cSRui Paulo cred_username, cred_password, client_cert, 7985b9c547cSRui Paulo client_key) == 0) { 7995b9c547cSRui Paulo spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION, 8005b9c547cSRui Paulo "Subscription remediation", pps_fname, pps); 8015b9c547cSRui Paulo } 8025b9c547cSRui Paulo } 8035b9c547cSRui Paulo 8045b9c547cSRui Paulo 8055b9c547cSRui Paulo static void hs20_policy_update_complete(struct hs20_osu_client *ctx, 8065b9c547cSRui Paulo const char *pps_fname) 8075b9c547cSRui Paulo { 8085b9c547cSRui Paulo wpa_printf(MSG_INFO, "Policy update completed"); 8095b9c547cSRui Paulo 8105b9c547cSRui Paulo /* 8115b9c547cSRui Paulo * Update wpa_supplicant credentials and reconnect using updated 8125b9c547cSRui Paulo * information. 8135b9c547cSRui Paulo */ 8145b9c547cSRui Paulo wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials"); 8155b9c547cSRui Paulo cmd_set_pps(ctx, pps_fname); 8165b9c547cSRui Paulo 8175b9c547cSRui Paulo wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); 8185b9c547cSRui Paulo if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) 8195b9c547cSRui Paulo wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect"); 8205b9c547cSRui Paulo } 8215b9c547cSRui Paulo 8225b9c547cSRui Paulo 8235b9c547cSRui Paulo static int process_spp_exchange_complete(struct hs20_osu_client *ctx, 8245b9c547cSRui Paulo xml_node_t *node) 8255b9c547cSRui Paulo { 8265b9c547cSRui Paulo char *status, *session_id; 8275b9c547cSRui Paulo 8285b9c547cSRui Paulo debug_dump_node(ctx, "sppExchangeComplete", node); 8295b9c547cSRui Paulo 8305b9c547cSRui Paulo status = get_spp_attr_value(ctx->xml, node, "sppStatus"); 8315b9c547cSRui Paulo if (status == NULL) { 8325b9c547cSRui Paulo wpa_printf(MSG_INFO, "No sppStatus attribute"); 8335b9c547cSRui Paulo return -1; 8345b9c547cSRui Paulo } 8355b9c547cSRui Paulo write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'", 8365b9c547cSRui Paulo status); 8375b9c547cSRui Paulo 8385b9c547cSRui Paulo session_id = get_spp_attr_value(ctx->xml, node, "sessionID"); 8395b9c547cSRui Paulo if (session_id == NULL) { 8405b9c547cSRui Paulo wpa_printf(MSG_INFO, "No sessionID attribute"); 8415b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, status); 8425b9c547cSRui Paulo return -1; 8435b9c547cSRui Paulo } 8445b9c547cSRui Paulo 8455b9c547cSRui Paulo wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s' sessionID: '%s'", 8465b9c547cSRui Paulo status, session_id); 8475b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, session_id); 8485b9c547cSRui Paulo 8495b9c547cSRui Paulo if (strcasecmp(status, "Exchange complete, release TLS connection") == 8505b9c547cSRui Paulo 0) { 8515b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, status); 8525b9c547cSRui Paulo return 0; 8535b9c547cSRui Paulo } 8545b9c547cSRui Paulo 8555b9c547cSRui Paulo wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status); 8565b9c547cSRui Paulo write_summary(ctx, "Unexpected sppStatus '%s'", status); 8575b9c547cSRui Paulo xml_node_get_attr_value_free(ctx->xml, status); 8585b9c547cSRui Paulo return -1; 8595b9c547cSRui Paulo } 8605b9c547cSRui Paulo 8615b9c547cSRui Paulo 8625b9c547cSRui Paulo static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx, 8635b9c547cSRui Paulo const char *session_id, 8645b9c547cSRui Paulo const char *spp_status, 8655b9c547cSRui Paulo const char *error_code) 8665b9c547cSRui Paulo { 8675b9c547cSRui Paulo xml_namespace_t *ns; 8685b9c547cSRui Paulo xml_node_t *spp_node, *node; 8695b9c547cSRui Paulo 8705b9c547cSRui Paulo spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns, 8715b9c547cSRui Paulo "sppUpdateResponse"); 8725b9c547cSRui Paulo if (spp_node == NULL) 8735b9c547cSRui Paulo return NULL; 8745b9c547cSRui Paulo 8755b9c547cSRui Paulo xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0"); 8765b9c547cSRui Paulo xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id); 8775b9c547cSRui Paulo xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status); 8785b9c547cSRui Paulo 8795b9c547cSRui Paulo if (error_code) { 8805b9c547cSRui Paulo node = xml_node_create(ctx->xml, spp_node, ns, "sppError"); 8815b9c547cSRui Paulo if (node) 8825b9c547cSRui Paulo xml_node_add_attr(ctx->xml, node, NULL, "errorCode", 8835b9c547cSRui Paulo error_code); 8845b9c547cSRui Paulo } 8855b9c547cSRui Paulo 8865b9c547cSRui Paulo return spp_node; 8875b9c547cSRui Paulo } 8885b9c547cSRui Paulo 8895b9c547cSRui Paulo 8905b9c547cSRui Paulo static int hs20_spp_update_response(struct hs20_osu_client *ctx, 8915b9c547cSRui Paulo const char *session_id, 8925b9c547cSRui Paulo const char *spp_status, 8935b9c547cSRui Paulo const char *error_code) 8945b9c547cSRui Paulo { 8955b9c547cSRui Paulo xml_node_t *node, *ret_node; 8965b9c547cSRui Paulo int ret; 8975b9c547cSRui Paulo 8985b9c547cSRui Paulo write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'", 8995b9c547cSRui Paulo spp_status, error_code); 9005b9c547cSRui Paulo node = build_spp_update_response(ctx, session_id, spp_status, 9015b9c547cSRui Paulo error_code); 9025b9c547cSRui Paulo if (node == NULL) 9035b9c547cSRui Paulo return -1; 9045b9c547cSRui Paulo ret_node = soap_send_receive(ctx->http, node); 9055b9c547cSRui Paulo if (!ret_node) { 9065b9c547cSRui Paulo if (soap_reinit_client(ctx->http) < 0) 9075b9c547cSRui Paulo return -1; 9085b9c547cSRui Paulo wpa_printf(MSG_INFO, "Try to finish with re-opened connection"); 9095b9c547cSRui Paulo node = build_spp_update_response(ctx, session_id, spp_status, 9105b9c547cSRui Paulo error_code); 9115b9c547cSRui Paulo if (node == NULL) 9125b9c547cSRui Paulo return -1; 9135b9c547cSRui Paulo ret_node = soap_send_receive(ctx->http, node); 9145b9c547cSRui Paulo if (ret_node == NULL) 9155b9c547cSRui Paulo return -1; 9165b9c547cSRui Paulo wpa_printf(MSG_INFO, "Continue with new connection"); 9175b9c547cSRui Paulo } 9185b9c547cSRui Paulo 9195b9c547cSRui Paulo if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) { 9205b9c547cSRui Paulo wpa_printf(MSG_INFO, "SPP validation failed"); 9215b9c547cSRui Paulo xml_node_free(ctx->xml, ret_node); 9225b9c547cSRui Paulo return -1; 9235b9c547cSRui Paulo } 9245b9c547cSRui Paulo 9255b9c547cSRui Paulo ret = process_spp_exchange_complete(ctx, ret_node); 9265b9c547cSRui Paulo xml_node_free(ctx->xml, ret_node); 9275b9c547cSRui Paulo return ret; 9285b9c547cSRui Paulo } 9295b9c547cSRui Paulo 9305b9c547cSRui Paulo 9315b9c547cSRui Paulo void spp_pol_upd(struct hs20_osu_client *ctx, const char *address, 9325b9c547cSRui Paulo const char *pps_fname, 9335b9c547cSRui Paulo const char *client_cert, const char *client_key, 9345b9c547cSRui Paulo const char *cred_username, const char *cred_password, 9355b9c547cSRui Paulo xml_node_t *pps) 9365b9c547cSRui Paulo { 9375b9c547cSRui Paulo wpa_printf(MSG_INFO, "SPP policy update"); 9385b9c547cSRui Paulo write_summary(ctx, "SPP policy update"); 9395b9c547cSRui Paulo 9405b9c547cSRui Paulo os_free(ctx->server_url); 9415b9c547cSRui Paulo ctx->server_url = os_strdup(address); 9425b9c547cSRui Paulo 9435b9c547cSRui Paulo if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username, 9445b9c547cSRui Paulo cred_password, client_cert, client_key) == 0) { 9455b9c547cSRui Paulo spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update", 9465b9c547cSRui Paulo pps_fname, pps); 9475b9c547cSRui Paulo } 9485b9c547cSRui Paulo } 9495b9c547cSRui Paulo 9505b9c547cSRui Paulo 9515b9c547cSRui Paulo int cmd_prov(struct hs20_osu_client *ctx, const char *url) 9525b9c547cSRui Paulo { 9535b9c547cSRui Paulo unlink("Cert/est_cert.der"); 9545b9c547cSRui Paulo unlink("Cert/est_cert.pem"); 9555b9c547cSRui Paulo 9565b9c547cSRui Paulo if (url == NULL) { 9575b9c547cSRui Paulo wpa_printf(MSG_INFO, "Invalid prov command (missing URL)"); 9585b9c547cSRui Paulo return -1; 9595b9c547cSRui Paulo } 9605b9c547cSRui Paulo 961325151a3SRui Paulo wpa_printf(MSG_INFO, 962325151a3SRui Paulo "Credential provisioning requested - URL: %s ca_fname: %s", 963325151a3SRui Paulo url, ctx->ca_fname ? ctx->ca_fname : "N/A"); 9645b9c547cSRui Paulo 9655b9c547cSRui Paulo os_free(ctx->server_url); 9665b9c547cSRui Paulo ctx->server_url = os_strdup(url); 9675b9c547cSRui Paulo 9685b9c547cSRui Paulo if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL, 9695b9c547cSRui Paulo NULL) < 0) 9705b9c547cSRui Paulo return -1; 9715b9c547cSRui Paulo spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION, 9725b9c547cSRui Paulo "Subscription registration", NULL, NULL); 9735b9c547cSRui Paulo 9745b9c547cSRui Paulo return ctx->pps_cred_set ? 0 : -1; 9755b9c547cSRui Paulo } 9765b9c547cSRui Paulo 9775b9c547cSRui Paulo 9785b9c547cSRui Paulo int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url) 9795b9c547cSRui Paulo { 9805b9c547cSRui Paulo if (url == NULL) { 9815b9c547cSRui Paulo wpa_printf(MSG_INFO, "Invalid prov command (missing URL)"); 9825b9c547cSRui Paulo return -1; 9835b9c547cSRui Paulo } 9845b9c547cSRui Paulo 9855b9c547cSRui Paulo wpa_printf(MSG_INFO, "SIM provisioning requested"); 9865b9c547cSRui Paulo 9875b9c547cSRui Paulo os_free(ctx->server_url); 9885b9c547cSRui Paulo ctx->server_url = os_strdup(url); 9895b9c547cSRui Paulo 9905b9c547cSRui Paulo wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning"); 9915b9c547cSRui Paulo 9925b9c547cSRui Paulo if (wait_ip_addr(ctx->ifname, 15) < 0) { 9935b9c547cSRui Paulo wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); 9945b9c547cSRui Paulo } 9955b9c547cSRui Paulo 9965b9c547cSRui Paulo if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL, 9975b9c547cSRui Paulo NULL) < 0) 9985b9c547cSRui Paulo return -1; 9995b9c547cSRui Paulo spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION, 10005b9c547cSRui Paulo "Subscription provisioning", NULL, NULL); 10015b9c547cSRui Paulo 10025b9c547cSRui Paulo return ctx->pps_cred_set ? 0 : -1; 10035b9c547cSRui Paulo } 1004