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