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