Home | History | Annotate | Download | only in client
      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