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