xref: /freebsd/contrib/wpa/hs20/client/spp_client.c (revision c1d255d3)
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 extern const char *spp_xsd_fname;
25 
26 static 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);
30 static void hs20_policy_update_complete(
31 	struct hs20_osu_client *ctx, const char *pps_fname);
32 
33 
get_spp_attr_value(struct xml_node_ctx * ctx,xml_node_t * node,char * attr_name)34 static 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 
hs20_spp_validate(struct hs20_osu_client * ctx,xml_node_t * node,const char * expected_name)41 static 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 
add_mo_container(struct xml_node_ctx * ctx,xml_namespace_t * ns,xml_node_t * parent,const char * urn,const char * fname)74 static 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 
build_spp_post_dev_data(struct hs20_osu_client * ctx,xml_namespace_t ** ret_ns,const char * session_id,const char * reason)107 static 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 
process_update_node(struct hs20_osu_client * ctx,xml_node_t * pps,xml_node_t * update)147 static 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 
update_pps(struct hs20_osu_client * ctx,xml_node_t * update,const char * pps_fname,xml_node_t * pps)293 static 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 
hs20_sub_rem_complete(struct hs20_osu_client * ctx,const char * pps_fname)307 static 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 
hs20_spp_upload_mo(struct hs20_osu_client * ctx,xml_node_t * cmd,const char * session_id,const char * pps_fname)326 static 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 
hs20_add_mo(struct hs20_osu_client * ctx,xml_node_t * add_mo,char * fname,size_t fname_len)375 static 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 
process_spp_user_input_response(struct hs20_osu_client * ctx,const char * session_id,xml_node_t * add_mo)409 static 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 
hs20_spp_user_input_completed(struct hs20_osu_client * ctx,const char * session_id)437 static 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 
hs20_spp_get_certificate(struct hs20_osu_client * ctx,xml_node_t * cmd,const char * session_id,const char * pps_fname)472 static 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 
hs20_spp_exec(struct hs20_osu_client * ctx,xml_node_t * exec,const char * session_id,const char * pps_fname,xml_node_t * pps,xml_node_t ** ret_node)511 static 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 
592 enum spp_post_dev_data_use {
593 	SPP_SUBSCRIPTION_REMEDIATION,
594 	SPP_POLICY_UPDATE,
595 	SPP_SUBSCRIPTION_REGISTRATION,
596 };
597 
process_spp_post_dev_data_response(struct hs20_osu_client * ctx,enum spp_post_dev_data_use use,xml_node_t * node,const char * pps_fname,xml_node_t * pps)598 static 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);
742 out:
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 
spp_post_dev_data(struct hs20_osu_client * ctx,enum spp_post_dev_data_use use,const char * reason,const char * pps_fname,xml_node_t * pps)749 static 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 
spp_sub_rem(struct hs20_osu_client * ctx,const char * address,const char * pps_fname,const char * client_cert,const char * client_key,const char * cred_username,const char * cred_password,xml_node_t * pps)785 void 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 
hs20_policy_update_complete(struct hs20_osu_client * ctx,const char * pps_fname)806 static 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 
process_spp_exchange_complete(struct hs20_osu_client * ctx,xml_node_t * node)824 static 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 
build_spp_update_response(struct hs20_osu_client * ctx,const char * session_id,const char * spp_status,const char * error_code)863 static 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 
hs20_spp_update_response(struct hs20_osu_client * ctx,const char * session_id,const char * spp_status,const char * error_code)891 static 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 
spp_pol_upd(struct hs20_osu_client * ctx,const char * address,const char * pps_fname,const char * client_cert,const char * client_key,const char * cred_username,const char * cred_password,xml_node_t * pps)932 void 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 
cmd_prov(struct hs20_osu_client * ctx,const char * url)952 int 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 
cmd_sim_prov(struct hs20_osu_client * ctx,const char * url)979 int 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