xref: /freebsd/contrib/wpa/hs20/client/spp_client.c (revision bd81e07d2761cf1c13063eb49a5c0cb4a6951318)
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 
24 static 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);
28 static void hs20_policy_update_complete(
29 	struct hs20_osu_client *ctx, const char *pps_fname);
30 
31 
32 static 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 
39 static 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 
72 static 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 
100 static 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 
140 static 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 
286 static 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 
300 static 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 
319 static 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 
368 static 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 
402 static 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 
430 static 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 
465 static 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 
504 static 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 
585 enum spp_post_dev_data_use {
586 	SPP_SUBSCRIPTION_REMEDIATION,
587 	SPP_POLICY_UPDATE,
588 	SPP_SUBSCRIPTION_REGISTRATION,
589 };
590 
591 static 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);
735 out:
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 
742 static 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 
778 void 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 
799 static 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 
817 static 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 
856 static 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 
884 static 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 
925 void 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 
945 int 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 
970 int 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