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