xref: /freebsd/contrib/wpa/hs20/client/spp_client.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
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 	}
568 
569 	if (strcasecmp(name, "uploadMO") == 0) {
570 		if (pps_fname == NULL)
571 			return -1;
572 		*ret_node = hs20_spp_upload_mo(ctx, cmd, id,
573 					       pps_fname);
574 		free(id);
575 		return *ret_node ? 0 : -1;
576 	}
577 
578 	if (strcasecmp(name, "getCertificate") == 0) {
579 		*ret_node = hs20_spp_get_certificate(ctx, cmd, id,
580 						     pps_fname);
581 		free(id);
582 		return *ret_node ? 0 : -1;
583 	}
584 
585 	wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
586 	free(id);
587 	return -1;
588 }
589 
590 
591 enum spp_post_dev_data_use {
592 	SPP_SUBSCRIPTION_REMEDIATION,
593 	SPP_POLICY_UPDATE,
594 	SPP_SUBSCRIPTION_REGISTRATION,
595 };
596 
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)597 static void process_spp_post_dev_data_response(
598 	struct hs20_osu_client *ctx,
599 	enum spp_post_dev_data_use use, xml_node_t *node,
600 	const char *pps_fname, xml_node_t *pps)
601 {
602 	xml_node_t *child;
603 	char *status = NULL;
604 	xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
605 	char *session_id = NULL;
606 
607 	debug_dump_node(ctx, "sppPostDevDataResponse node", node);
608 
609 	status = get_spp_attr_value(ctx->xml, node, "sppStatus");
610 	if (status == NULL) {
611 		wpa_printf(MSG_INFO, "No sppStatus attribute");
612 		goto out;
613 	}
614 	write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
615 		      status);
616 
617 	session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
618 	if (session_id == NULL) {
619 		wpa_printf(MSG_INFO, "No sessionID attribute");
620 		goto out;
621 	}
622 
623 	wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s'  sessionID: '%s'",
624 		   status, session_id);
625 
626 	xml_node_for_each_child(ctx->xml, child, node) {
627 		const char *name;
628 		xml_node_for_each_check(ctx->xml, child);
629 		debug_dump_node(ctx, "child", child);
630 		name = xml_node_get_localname(ctx->xml, child);
631 		wpa_printf(MSG_INFO, "localname: '%s'", name);
632 		if (!update && strcasecmp(name, "updateNode") == 0)
633 			update = child;
634 		if (!exec && strcasecmp(name, "exec") == 0)
635 			exec = child;
636 		if (!add_mo && strcasecmp(name, "addMO") == 0)
637 			add_mo = child;
638 		if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
639 			no_mo = child;
640 	}
641 
642 	if (use == SPP_SUBSCRIPTION_REMEDIATION &&
643 	    strcasecmp(status,
644 		       "Remediation complete, request sppUpdateResponse") == 0)
645 	{
646 		int res, ret;
647 		if (!update && !no_mo) {
648 			wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
649 			goto out;
650 		}
651 		wpa_printf(MSG_INFO, "Subscription remediation completed");
652 		res = update_pps(ctx, update, pps_fname, pps);
653 		if (res < 0)
654 			wpa_printf(MSG_INFO, "Failed to update PPS MO");
655 		ret = hs20_spp_update_response(
656 			ctx, session_id,
657 			res < 0 ? "Error occurred" : "OK",
658 			res < 0 ? "MO addition or update failed" : NULL);
659 		if (res == 0 && ret == 0)
660 			hs20_sub_rem_complete(ctx, pps_fname);
661 		goto out;
662 	}
663 
664 	if (use == SPP_SUBSCRIPTION_REMEDIATION &&
665 	    strcasecmp(status, "Exchange complete, release TLS connection") ==
666 	    0) {
667 		if (!no_mo) {
668 			wpa_printf(MSG_INFO, "No noMOUpdate element");
669 			goto out;
670 		}
671 		wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
672 		goto out;
673 	}
674 
675 	if (use == SPP_POLICY_UPDATE &&
676 	    strcasecmp(status, "Update complete, request sppUpdateResponse") ==
677 	    0) {
678 		int res, ret;
679 		wpa_printf(MSG_INFO, "Policy update received - update PPS");
680 		res = update_pps(ctx, update, pps_fname, pps);
681 		ret = hs20_spp_update_response(
682 			ctx, session_id,
683 			res < 0 ? "Error occurred" : "OK",
684 			res < 0 ? "MO addition or update failed" : NULL);
685 		if (res == 0 && ret == 0)
686 			hs20_policy_update_complete(ctx, pps_fname);
687 		goto out;
688 	}
689 
690 	if (use == SPP_SUBSCRIPTION_REGISTRATION &&
691 	    strcasecmp(status, "Provisioning complete, request "
692 		       "sppUpdateResponse")  == 0) {
693 		if (!add_mo) {
694 			wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
695 			goto out;
696 		}
697 		process_spp_user_input_response(ctx, session_id, add_mo);
698 		node = NULL;
699 		goto out;
700 	}
701 
702 	if (strcasecmp(status, "No update available at this time") == 0) {
703 		wpa_printf(MSG_INFO, "No update available at this time");
704 		goto out;
705 	}
706 
707 	if (strcasecmp(status, "OK") == 0) {
708 		int res;
709 		xml_node_t *ret;
710 
711 		if (!exec) {
712 			wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
713 			goto out;
714 		}
715 		res = hs20_spp_exec(ctx, exec, session_id,
716 				    pps_fname, pps, &ret);
717 		/* xml_node_free(ctx->xml, node); */
718 		node = NULL;
719 		if (res == 0 && ret)
720 			process_spp_post_dev_data_response(ctx, use,
721 							   ret, pps_fname, pps);
722 		goto out;
723 	}
724 
725 	if (strcasecmp(status, "Error occurred") == 0) {
726 		xml_node_t *err;
727 		char *code = NULL;
728 		err = get_node(ctx->xml, node, "sppError");
729 		if (err)
730 			code = xml_node_get_attr_value(ctx->xml, err,
731 						       "errorCode");
732 		wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
733 			   code ? code : "N/A");
734 		xml_node_get_attr_value_free(ctx->xml, code);
735 		goto out;
736 	}
737 
738 	wpa_printf(MSG_INFO,
739 		   "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
740 		   status);
741 out:
742 	xml_node_get_attr_value_free(ctx->xml, status);
743 	xml_node_get_attr_value_free(ctx->xml, session_id);
744 	xml_node_free(ctx->xml, node);
745 }
746 
747 
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)748 static int spp_post_dev_data(struct hs20_osu_client *ctx,
749 			     enum spp_post_dev_data_use use,
750 			     const char *reason,
751 			     const char *pps_fname, xml_node_t *pps)
752 {
753 	xml_node_t *payload;
754 	xml_node_t *ret_node;
755 
756 	payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
757 	if (payload == NULL)
758 		return -1;
759 
760 	ret_node = soap_send_receive(ctx->http, payload);
761 	if (!ret_node) {
762 		const char *err = http_get_err(ctx->http);
763 		if (err) {
764 			wpa_printf(MSG_INFO, "HTTP error: %s", err);
765 			write_result(ctx, "HTTP error: %s", err);
766 		} else {
767 			write_summary(ctx, "Failed to send SOAP message");
768 		}
769 		return -1;
770 	}
771 
772 	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
773 		wpa_printf(MSG_INFO, "SPP validation failed");
774 		xml_node_free(ctx->xml, ret_node);
775 		return -1;
776 	}
777 
778 	process_spp_post_dev_data_response(ctx, use, ret_node,
779 					   pps_fname, pps);
780 	return 0;
781 }
782 
783 
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)784 void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
785 		 const char *pps_fname,
786 		 const char *client_cert, const char *client_key,
787 		 const char *cred_username, const char *cred_password,
788 		 xml_node_t *pps)
789 {
790 	wpa_printf(MSG_INFO, "SPP subscription remediation");
791 	write_summary(ctx, "SPP subscription remediation");
792 
793 	os_free(ctx->server_url);
794 	ctx->server_url = os_strdup(address);
795 
796 	if (soap_init_client(ctx->http, address, ctx->ca_fname,
797 			     cred_username, cred_password, client_cert,
798 			     client_key) == 0) {
799 		spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
800 				  "Subscription remediation", pps_fname, pps);
801 	}
802 }
803 
804 
hs20_policy_update_complete(struct hs20_osu_client * ctx,const char * pps_fname)805 static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
806 					const char *pps_fname)
807 {
808 	wpa_printf(MSG_INFO, "Policy update completed");
809 
810 	/*
811 	 * Update wpa_supplicant credentials and reconnect using updated
812 	 * information.
813 	 */
814 	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
815 	cmd_set_pps(ctx, pps_fname);
816 
817 	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
818 	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
819 		wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
820 }
821 
822 
process_spp_exchange_complete(struct hs20_osu_client * ctx,xml_node_t * node)823 static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
824 					 xml_node_t *node)
825 {
826 	char *status, *session_id;
827 
828 	debug_dump_node(ctx, "sppExchangeComplete", node);
829 
830 	status = get_spp_attr_value(ctx->xml, node, "sppStatus");
831 	if (status == NULL) {
832 		wpa_printf(MSG_INFO, "No sppStatus attribute");
833 		return -1;
834 	}
835 	write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
836 		      status);
837 
838 	session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
839 	if (session_id == NULL) {
840 		wpa_printf(MSG_INFO, "No sessionID attribute");
841 		xml_node_get_attr_value_free(ctx->xml, status);
842 		return -1;
843 	}
844 
845 	wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s'  sessionID: '%s'",
846 		   status, session_id);
847 	xml_node_get_attr_value_free(ctx->xml, session_id);
848 
849 	if (strcasecmp(status, "Exchange complete, release TLS connection") ==
850 	    0) {
851 		xml_node_get_attr_value_free(ctx->xml, status);
852 		return 0;
853 	}
854 
855 	wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
856 	write_summary(ctx, "Unexpected sppStatus '%s'", status);
857 	xml_node_get_attr_value_free(ctx->xml, status);
858 	return -1;
859 }
860 
861 
build_spp_update_response(struct hs20_osu_client * ctx,const char * session_id,const char * spp_status,const char * error_code)862 static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
863 					      const char *session_id,
864 					      const char *spp_status,
865 					      const char *error_code)
866 {
867 	xml_namespace_t *ns;
868 	xml_node_t *spp_node, *node;
869 
870 	spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
871 					"sppUpdateResponse");
872 	if (spp_node == NULL)
873 		return NULL;
874 
875 	xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
876 	xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
877 	xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
878 
879 	if (error_code) {
880 		node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
881 		if (node)
882 			xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
883 					  error_code);
884 	}
885 
886 	return spp_node;
887 }
888 
889 
hs20_spp_update_response(struct hs20_osu_client * ctx,const char * session_id,const char * spp_status,const char * error_code)890 static int hs20_spp_update_response(struct hs20_osu_client *ctx,
891 				    const char *session_id,
892 				    const char *spp_status,
893 				    const char *error_code)
894 {
895 	xml_node_t *node, *ret_node;
896 	int ret;
897 
898 	write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
899 		      spp_status, error_code);
900 	node = build_spp_update_response(ctx, session_id, spp_status,
901 					 error_code);
902 	if (node == NULL)
903 		return -1;
904 	ret_node = soap_send_receive(ctx->http, node);
905 	if (!ret_node) {
906 		if (soap_reinit_client(ctx->http) < 0)
907 			return -1;
908 		wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
909 		node = build_spp_update_response(ctx, session_id, spp_status,
910 						 error_code);
911 		if (node == NULL)
912 			return -1;
913 		ret_node = soap_send_receive(ctx->http, node);
914 		if (ret_node == NULL)
915 			return -1;
916 		wpa_printf(MSG_INFO, "Continue with new connection");
917 	}
918 
919 	if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
920 		wpa_printf(MSG_INFO, "SPP validation failed");
921 		xml_node_free(ctx->xml, ret_node);
922 		return -1;
923 	}
924 
925 	ret = process_spp_exchange_complete(ctx, ret_node);
926 	xml_node_free(ctx->xml, ret_node);
927 	return ret;
928 }
929 
930 
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)931 void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
932 		 const char *pps_fname,
933 		 const char *client_cert, const char *client_key,
934 		 const char *cred_username, const char *cred_password,
935 		 xml_node_t *pps)
936 {
937 	wpa_printf(MSG_INFO, "SPP policy update");
938 	write_summary(ctx, "SPP policy update");
939 
940 	os_free(ctx->server_url);
941 	ctx->server_url = os_strdup(address);
942 
943 	if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
944 			     cred_password, client_cert, client_key) == 0) {
945 		spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
946 				  pps_fname, pps);
947 	}
948 }
949 
950 
cmd_prov(struct hs20_osu_client * ctx,const char * url)951 int cmd_prov(struct hs20_osu_client *ctx, const char *url)
952 {
953 	unlink("Cert/est_cert.der");
954 	unlink("Cert/est_cert.pem");
955 
956 	if (url == NULL) {
957 		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
958 		return -1;
959 	}
960 
961 	wpa_printf(MSG_INFO,
962 		   "Credential provisioning requested - URL: %s ca_fname: %s",
963 		   url, ctx->ca_fname ? ctx->ca_fname : "N/A");
964 
965 	os_free(ctx->server_url);
966 	ctx->server_url = os_strdup(url);
967 
968 	if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
969 			     NULL) < 0)
970 		return -1;
971 	spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
972 			  "Subscription registration", NULL, NULL);
973 
974 	return ctx->pps_cred_set ? 0 : -1;
975 }
976 
977 
cmd_sim_prov(struct hs20_osu_client * ctx,const char * url)978 int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
979 {
980 	if (url == NULL) {
981 		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
982 		return -1;
983 	}
984 
985 	wpa_printf(MSG_INFO, "SIM provisioning requested");
986 
987 	os_free(ctx->server_url);
988 	ctx->server_url = os_strdup(url);
989 
990 	wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
991 
992 	if (wait_ip_addr(ctx->ifname, 15) < 0) {
993 		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
994 	}
995 
996 	if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
997 			     NULL) < 0)
998 		return -1;
999 	spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
1000 			  "Subscription provisioning", NULL, NULL);
1001 
1002 	return ctx->pps_cred_set ? 0 : -1;
1003 }
1004