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