xref: /freebsd/contrib/wpa/hs20/client/osu_client.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
1 /*
2  * Hotspot 2.0 OSU 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 <time.h>
11 #include <sys/stat.h>
12 #ifdef ANDROID
13 #include "private/android_filesystem_config.h"
14 #endif /* ANDROID */
15 
16 #include "common.h"
17 #include "utils/browser.h"
18 #include "utils/base64.h"
19 #include "utils/xml-utils.h"
20 #include "utils/http-utils.h"
21 #include "common/wpa_ctrl.h"
22 #include "common/wpa_helpers.h"
23 #include "eap_common/eap_defs.h"
24 #include "crypto/crypto.h"
25 #include "crypto/sha256.h"
26 #include "osu_client.h"
27 
28 
29 void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
30 {
31 	va_list ap;
32 	FILE *f;
33 	char buf[500];
34 
35 	va_start(ap, fmt);
36 	vsnprintf(buf, sizeof(buf), fmt, ap);
37 	va_end(ap);
38 	write_summary(ctx, "%s", buf);
39 
40 	if (!ctx->result_file)
41 		return;
42 
43 	f = fopen(ctx->result_file, "w");
44 	if (f == NULL)
45 		return;
46 
47 	va_start(ap, fmt);
48 	vfprintf(f, fmt, ap);
49 	va_end(ap);
50 	fprintf(f, "\n");
51 	fclose(f);
52 }
53 
54 
55 void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
56 {
57 	va_list ap;
58 	FILE *f;
59 
60 	if (!ctx->summary_file)
61 		return;
62 
63 	f = fopen(ctx->summary_file, "a");
64 	if (f == NULL)
65 		return;
66 
67 	va_start(ap, fmt);
68 	vfprintf(f, fmt, ap);
69 	va_end(ap);
70 	fprintf(f, "\n");
71 	fclose(f);
72 }
73 
74 
75 void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
76 		     xml_node_t *node)
77 {
78 	char *str = xml_node_to_str(ctx->xml, node);
79 	wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str);
80 	free(str);
81 }
82 
83 
84 static int valid_fqdn(const char *fqdn)
85 {
86 	const char *pos;
87 
88 	/* TODO: could make this more complete.. */
89 	if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255)
90 		return 0;
91 	for (pos = fqdn; *pos; pos++) {
92 		if (*pos >= 'a' && *pos <= 'z')
93 			continue;
94 		if (*pos >= 'A' && *pos <= 'Z')
95 			continue;
96 		if (*pos >= '0' && *pos <= '9')
97 			continue;
98 		if (*pos == '-' || *pos == '.' || *pos == '_')
99 			continue;
100 		return 0;
101 	}
102 	return 1;
103 }
104 
105 
106 int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
107 {
108 	xml_node_t *node;
109 	char *url, *user = NULL, *pw = NULL;
110 	char *proto;
111 	int ret = -1;
112 
113 	proto = xml_node_get_attr_value(ctx->xml, getcert,
114 					"enrollmentProtocol");
115 	if (!proto)
116 		return -1;
117 	wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto);
118 	write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto);
119 	if (os_strcasecmp(proto, "EST") != 0) {
120 		wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol");
121 		xml_node_get_attr_value_free(ctx->xml, proto);
122 		return -1;
123 	}
124 	xml_node_get_attr_value_free(ctx->xml, proto);
125 
126 	node = get_node(ctx->xml, getcert, "enrollmentServerURI");
127 	if (node == NULL) {
128 		wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node");
129 		xml_node_get_attr_value_free(ctx->xml, proto);
130 		return -1;
131 	}
132 	url = xml_node_get_text(ctx->xml, node);
133 	if (url == NULL) {
134 		wpa_printf(MSG_INFO, "Could not get URL text");
135 		return -1;
136 	}
137 	wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url);
138 	write_summary(ctx, "enrollmentServerURI: %s", url);
139 
140 	node = get_node(ctx->xml, getcert, "estUserID");
141 	if (node == NULL && !ctx->client_cert_present) {
142 		wpa_printf(MSG_INFO, "Could not find estUserID node");
143 		goto fail;
144 	}
145 	if (node) {
146 		user = xml_node_get_text(ctx->xml, node);
147 		if (user == NULL) {
148 			wpa_printf(MSG_INFO, "Could not get estUserID text");
149 			goto fail;
150 		}
151 		wpa_printf(MSG_INFO, "estUserID: %s", user);
152 		write_summary(ctx, "estUserID: %s", user);
153 	}
154 
155 	node = get_node(ctx->xml, getcert, "estPassword");
156 	if (node == NULL && !ctx->client_cert_present) {
157 		wpa_printf(MSG_INFO, "Could not find estPassword node");
158 		goto fail;
159 	}
160 	if (node) {
161 		pw = xml_node_get_base64_text(ctx->xml, node, NULL);
162 		if (pw == NULL) {
163 			wpa_printf(MSG_INFO, "Could not get estPassword text");
164 			goto fail;
165 		}
166 		wpa_printf(MSG_INFO, "estPassword: %s", pw);
167 	}
168 
169 	mkdir("Cert", S_IRWXU);
170 	if (est_load_cacerts(ctx, url) < 0 ||
171 	    est_build_csr(ctx, url) < 0 ||
172 	    est_simple_enroll(ctx, url, user, pw) < 0)
173 		goto fail;
174 
175 	ret = 0;
176 fail:
177 	xml_node_get_text_free(ctx->xml, url);
178 	xml_node_get_text_free(ctx->xml, user);
179 	xml_node_get_text_free(ctx->xml, pw);
180 
181 	return ret;
182 }
183 
184 
185 static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert,
186 			    const char *fqdn)
187 {
188 	u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
189 	char *der, *pem;
190 	size_t der_len, pem_len;
191 	char *fingerprint;
192 	char buf[200];
193 
194 	wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn);
195 
196 	fingerprint = xml_node_get_text(ctx->xml, cert);
197 	if (fingerprint == NULL)
198 		return -1;
199 	if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) {
200 		wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
201 		write_result(ctx, "Invalid client certificate SHA256 hash value in PPS");
202 		xml_node_get_text_free(ctx->xml, fingerprint);
203 		return -1;
204 	}
205 	xml_node_get_text_free(ctx->xml, fingerprint);
206 
207 	der = os_readfile("Cert/est_cert.der", &der_len);
208 	if (der == NULL) {
209 		wpa_printf(MSG_INFO, "Could not find client certificate from EST");
210 		write_result(ctx, "Could not find client certificate from EST");
211 		return -1;
212 	}
213 
214 	if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) {
215 		os_free(der);
216 		return -1;
217 	}
218 	os_free(der);
219 
220 	if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
221 		wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO");
222 		write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO");
223 		return -1;
224 	}
225 
226 	wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO");
227 	unlink("Cert/est_cert.der");
228 
229 	os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn);
230 	if (rename("Cert/est-cacerts.pem", buf) < 0) {
231 		wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s",
232 			   strerror(errno));
233 		return -1;
234 	}
235 	pem = os_readfile(buf, &pem_len);
236 
237 	os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn);
238 	if (rename("Cert/est_cert.pem", buf) < 0) {
239 		wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s",
240 			   strerror(errno));
241 		os_free(pem);
242 		return -1;
243 	}
244 
245 	if (pem) {
246 		FILE *f = fopen(buf, "a");
247 		if (f) {
248 			fwrite(pem, pem_len, 1, f);
249 			fclose(f);
250 		}
251 		os_free(pem);
252 	}
253 
254 	os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn);
255 	if (rename("Cert/privkey-plain.pem", buf) < 0) {
256 		wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s",
257 			   strerror(errno));
258 		return -1;
259 	}
260 
261 	unlink("Cert/est-req.b64");
262 	unlink("Cert/est-req.pem");
263 	unlink("Cert/est-resp.raw");
264 	rmdir("Cert");
265 
266 	return 0;
267 }
268 
269 
270 #define TMP_CERT_DL_FILE "tmp-cert-download"
271 
272 static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
273 			 const char *fname)
274 {
275 	xml_node_t *url_node, *hash_node;
276 	char *url, *hash;
277 	char *cert;
278 	size_t len;
279 	u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
280 	int res;
281 	unsigned char *b64;
282 	FILE *f;
283 
284 	url_node = get_node(ctx->xml, params, "CertURL");
285 	hash_node = get_node(ctx->xml, params, "CertSHA256Fingerprint");
286 	if (url_node == NULL || hash_node == NULL)
287 		return -1;
288 	url = xml_node_get_text(ctx->xml, url_node);
289 	hash = xml_node_get_text(ctx->xml, hash_node);
290 	if (url == NULL || hash == NULL) {
291 		xml_node_get_text_free(ctx->xml, url);
292 		xml_node_get_text_free(ctx->xml, hash);
293 		return -1;
294 	}
295 
296 	wpa_printf(MSG_INFO, "CertURL: %s", url);
297 	wpa_printf(MSG_INFO, "SHA256 hash: %s", hash);
298 
299 	if (hexstr2bin(hash, digest1, SHA256_MAC_LEN) < 0) {
300 		wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
301 		write_result(ctx, "Invalid SHA256 hash value for downloaded certificate");
302 		xml_node_get_text_free(ctx->xml, hash);
303 		return -1;
304 	}
305 	xml_node_get_text_free(ctx->xml, hash);
306 
307 	write_summary(ctx, "Download certificate from %s", url);
308 	ctx->no_osu_cert_validation = 1;
309 	http_ocsp_set(ctx->http, 1);
310 	res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL);
311 	http_ocsp_set(ctx->http,
312 		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
313 	ctx->no_osu_cert_validation = 0;
314 	xml_node_get_text_free(ctx->xml, url);
315 	if (res < 0)
316 		return -1;
317 
318 	cert = os_readfile(TMP_CERT_DL_FILE, &len);
319 	remove(TMP_CERT_DL_FILE);
320 	if (cert == NULL)
321 		return -1;
322 
323 	if (sha256_vector(1, (const u8 **) &cert, &len, digest2) < 0) {
324 		os_free(cert);
325 		return -1;
326 	}
327 
328 	if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
329 		wpa_printf(MSG_INFO, "Downloaded certificate fingerprint did not match");
330 		write_result(ctx, "Downloaded certificate fingerprint did not match");
331 		os_free(cert);
332 		return -1;
333 	}
334 
335 	b64 = base64_encode((unsigned char *) cert, len, NULL);
336 	os_free(cert);
337 	if (b64 == NULL)
338 		return -1;
339 
340 	f = fopen(fname, "wb");
341 	if (f == NULL) {
342 		os_free(b64);
343 		return -1;
344 	}
345 
346 	fprintf(f, "-----BEGIN CERTIFICATE-----\n"
347 		"%s"
348 		"-----END CERTIFICATE-----\n",
349 		b64);
350 
351 	os_free(b64);
352 	fclose(f);
353 
354 	wpa_printf(MSG_INFO, "Downloaded certificate into %s and validated fingerprint",
355 		   fname);
356 	write_summary(ctx, "Downloaded certificate into %s and validated fingerprint",
357 		      fname);
358 
359 	return 0;
360 }
361 
362 
363 static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname,
364 			 const char *ca_fname)
365 {
366 	xml_node_t *pps, *node;
367 	int ret;
368 
369 	pps = node_from_file(ctx->xml, pps_fname);
370 	if (pps == NULL) {
371 		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
372 		return -1;
373 	}
374 
375 	node = get_child_node(ctx->xml, pps,
376 			      "SubscriptionUpdate/TrustRoot");
377 	if (node == NULL) {
378 		wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS");
379 		xml_node_free(ctx->xml, pps);
380 		return -1;
381 	}
382 
383 	ret = download_cert(ctx, node, ca_fname);
384 	xml_node_free(ctx->xml, pps);
385 
386 	return ret;
387 }
388 
389 
390 static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname,
391 			    const char *ca_fname)
392 {
393 	xml_node_t *pps, *node;
394 	int ret;
395 
396 	pps = node_from_file(ctx->xml, pps_fname);
397 	if (pps == NULL) {
398 		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
399 		return -1;
400 	}
401 
402 	node = get_child_node(ctx->xml, pps,
403 			      "Policy/PolicyUpdate/TrustRoot");
404 	if (node == NULL) {
405 		wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
406 		xml_node_free(ctx->xml, pps);
407 		return -1;
408 	}
409 
410 	ret = download_cert(ctx, node, ca_fname);
411 	xml_node_free(ctx->xml, pps);
412 
413 	return ret;
414 }
415 
416 
417 static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
418 			 const char *ca_fname)
419 {
420 	xml_node_t *pps, *node, *aaa;
421 	int ret;
422 
423 	pps = node_from_file(ctx->xml, pps_fname);
424 	if (pps == NULL) {
425 		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
426 		return -1;
427 	}
428 
429 	node = get_child_node(ctx->xml, pps,
430 			      "AAAServerTrustRoot");
431 	if (node == NULL) {
432 		wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
433 		xml_node_free(ctx->xml, pps);
434 		return -1;
435 	}
436 
437 	aaa = xml_node_first_child(ctx->xml, node);
438 	if (aaa == NULL) {
439 		wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
440 		xml_node_free(ctx->xml, pps);
441 		return -1;
442 	}
443 
444 	ret = download_cert(ctx, aaa, ca_fname);
445 	xml_node_free(ctx->xml, pps);
446 
447 	return ret;
448 }
449 
450 
451 static int download_trust_roots(struct hs20_osu_client *ctx,
452 				const char *pps_fname)
453 {
454 	char *dir, *pos;
455 	char fname[300];
456 	int ret;
457 
458 	dir = os_strdup(pps_fname);
459 	if (dir == NULL)
460 		return -1;
461 	pos = os_strrchr(dir, '/');
462 	if (pos == NULL) {
463 		os_free(dir);
464 		return -1;
465 	}
466 	*pos = '\0';
467 
468 	snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
469 	ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
470 	snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
471 	cmd_dl_polupd_ca(ctx, pps_fname, fname);
472 	snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
473 	cmd_dl_aaa_ca(ctx, pps_fname, fname);
474 
475 	os_free(dir);
476 
477 	return ret;
478 }
479 
480 
481 static int server_dnsname_suffix_match(struct hs20_osu_client *ctx,
482 				       const char *fqdn)
483 {
484 	size_t match_len, len, i;
485 	const char *val;
486 
487 	match_len = os_strlen(fqdn);
488 
489 	for (i = 0; i < ctx->server_dnsname_count; i++) {
490 		wpa_printf(MSG_INFO,
491 			   "Checking suffix match against server dNSName %s",
492 			   ctx->server_dnsname[i]);
493 		val = ctx->server_dnsname[i];
494 		len = os_strlen(val);
495 
496 		if (match_len > len)
497 			continue;
498 
499 		if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0)
500 			continue; /* no match */
501 
502 		if (match_len == len)
503 			return 1; /* exact match */
504 
505 		if (val[len - match_len - 1] == '.')
506 			return 1; /* full label match completes suffix match */
507 
508 		/* Reject due to incomplete label match */
509 	}
510 
511 	/* None of the dNSName(s) matched */
512 	return 0;
513 }
514 
515 
516 int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
517 		    xml_node_t *add_mo, char *fname, size_t fname_len)
518 {
519 	char *str;
520 	char *fqdn, *pos;
521 	xml_node_t *tnds, *mo, *cert;
522 	const char *name;
523 	int ret;
524 
525 	if (strncmp(uri, "./Wi-Fi/", 8) != 0) {
526 		wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'",
527 			   uri);
528 		write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'",
529 			     uri);
530 		return -1;
531 	}
532 
533 	fqdn = strdup(uri + 8);
534 	if (fqdn == NULL)
535 		return -1;
536 	pos = strchr(fqdn, '/');
537 	if (pos) {
538 		if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) {
539 			wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'",
540 				   uri);
541 			write_result(ctx, "Unsupported location for addMO to "
542 				     "add PPS MO (extra directory): '%s'", uri);
543 			return -1;
544 		}
545 		*pos = '\0'; /* remove trailing slash and PPS node name */
546 	}
547 	wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
548 
549 	if (!server_dnsname_suffix_match(ctx, fqdn)) {
550 		wpa_printf(MSG_INFO, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
551 			   fqdn);
552 		write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
553 			     fqdn);
554 		free(fqdn);
555 		return -1;
556 	}
557 
558 	if (!valid_fqdn(fqdn)) {
559 		wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn);
560 		write_result(ctx, "Invalid FQDN '%s'", fqdn);
561 		free(fqdn);
562 		return -1;
563 	}
564 
565 	mkdir("SP", S_IRWXU);
566 	snprintf(fname, fname_len, "SP/%s", fqdn);
567 	if (mkdir(fname, S_IRWXU) < 0) {
568 		if (errno != EEXIST) {
569 			int err = errno;
570 			wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
571 				   fname, strerror(err));
572 			free(fqdn);
573 			return -1;
574 		}
575 	}
576 
577 #ifdef ANDROID
578 	/* Allow processes running with Group ID as AID_WIFI,
579 	 * to read files from SP/<fqdn> directory */
580 	if (chown(fname, -1, AID_WIFI)) {
581 		wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s",
582 			   strerror(errno));
583 		/* Try to continue anyway */
584 	}
585 	if (chmod(fname, S_IRWXU | S_IRGRP | S_IXGRP) < 0) {
586 		wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
587 			   strerror(errno));
588 		/* Try to continue anyway */
589 	}
590 #endif /* ANDROID */
591 
592 	snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
593 
594 	if (os_file_exists(fname)) {
595 		wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO",
596 			   fname);
597 		write_result(ctx, "PPS file '%s' exists - reject addMO",
598 			     fname);
599 		free(fqdn);
600 		return -2;
601 	}
602 	wpa_printf(MSG_INFO, "Using PPS file: %s", fname);
603 
604 	str = xml_node_get_text(ctx->xml, add_mo);
605 	if (str == NULL) {
606 		wpa_printf(MSG_INFO, "Could not extract MO text");
607 		free(fqdn);
608 		return -1;
609 	}
610 	wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str);
611 
612 	tnds = xml_node_from_buf(ctx->xml, str);
613 	xml_node_get_text_free(ctx->xml, str);
614 	if (tnds == NULL) {
615 		wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text");
616 		free(fqdn);
617 		return -1;
618 	}
619 
620 	mo = tnds_to_mo(ctx->xml, tnds);
621 	if (mo == NULL) {
622 		wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text");
623 		free(fqdn);
624 		return -1;
625 	}
626 
627 	debug_dump_node(ctx, "Parsed TNDS", mo);
628 
629 	name = xml_node_get_localname(ctx->xml, mo);
630 	if (os_strcasecmp(name, "PerProviderSubscription") != 0) {
631 		wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'",
632 			   name);
633 		free(fqdn);
634 		return -1;
635 	}
636 
637 	cert = get_child_node(ctx->xml, mo,
638 			      "Credential/DigitalCertificate/"
639 			      "CertSHA256Fingerprint");
640 	if (cert && process_est_cert(ctx, cert, fqdn) < 0) {
641 		xml_node_free(ctx->xml, mo);
642 		free(fqdn);
643 		return -1;
644 	}
645 	free(fqdn);
646 
647 	if (node_to_file(ctx->xml, fname, mo) < 0) {
648 		wpa_printf(MSG_INFO, "Could not write MO to file");
649 		xml_node_free(ctx->xml, mo);
650 		return -1;
651 	}
652 	xml_node_free(ctx->xml, mo);
653 
654 	wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname);
655 	write_summary(ctx, "A new PPS MO added as '%s'", fname);
656 
657 	ret = download_trust_roots(ctx, fname);
658 	if (ret < 0) {
659 		wpa_printf(MSG_INFO, "Remove invalid PPS MO file");
660 		write_summary(ctx, "Remove invalid PPS MO file");
661 		unlink(fname);
662 	}
663 
664 	return ret;
665 }
666 
667 
668 int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
669 		    xml_node_t *pps)
670 {
671 	char *str;
672 	FILE *f;
673 	char backup[300];
674 
675 	if (ctx->client_cert_present) {
676 		xml_node_t *cert;
677 		cert = get_child_node(ctx->xml, pps,
678 				      "Credential/DigitalCertificate/"
679 				      "CertSHA256Fingerprint");
680 		if (cert && os_file_exists("Cert/est_cert.der") &&
681 		    process_est_cert(ctx, cert, ctx->fqdn) < 0) {
682 			wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update");
683 			return -1;
684 		}
685 	}
686 
687 	wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname);
688 
689 	str = xml_node_to_str(ctx->xml, pps);
690 	if (str == NULL) {
691 		wpa_printf(MSG_ERROR, "No node found");
692 		return -1;
693 	}
694 	wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str);
695 
696 	snprintf(backup, sizeof(backup), "%s.bak", pps_fname);
697 	rename(pps_fname, backup);
698 	f = fopen(pps_fname, "w");
699 	if (f == NULL) {
700 		wpa_printf(MSG_INFO, "Could not write PPS");
701 		rename(backup, pps_fname);
702 		free(str);
703 		return -1;
704 	}
705 	fprintf(f, "%s\n", str);
706 	fclose(f);
707 
708 	free(str);
709 
710 	return 0;
711 }
712 
713 
714 void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
715 		 const char *alt_loc, char **user, char **pw)
716 {
717 	xml_node_t *node;
718 
719 	node = get_child_node(ctx->xml, pps,
720 			      "Credential/UsernamePassword/Username");
721 	if (node)
722 		*user = xml_node_get_text(ctx->xml, node);
723 
724 	node = get_child_node(ctx->xml, pps,
725 			      "Credential/UsernamePassword/Password");
726 	if (node)
727 		*pw = xml_node_get_base64_text(ctx->xml, node, NULL);
728 
729 	node = get_child_node(ctx->xml, pps, alt_loc);
730 	if (node) {
731 		xml_node_t *a;
732 		a = get_node(ctx->xml, node, "Username");
733 		if (a) {
734 			xml_node_get_text_free(ctx->xml, *user);
735 			*user = xml_node_get_text(ctx->xml, a);
736 			wpa_printf(MSG_INFO, "Use OSU username '%s'", *user);
737 		}
738 
739 		a = get_node(ctx->xml, node, "Password");
740 		if (a) {
741 			free(*pw);
742 			*pw = xml_node_get_base64_text(ctx->xml, a, NULL);
743 			wpa_printf(MSG_INFO, "Use OSU password");
744 		}
745 	}
746 }
747 
748 
749 /* Remove old credentials based on HomeSP/FQDN */
750 static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
751 {
752 	char cmd[300];
753 	os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED provisioning_sp=%s", fqdn);
754 	if (wpa_command(ctx->ifname, cmd) < 0)
755 		wpa_printf(MSG_INFO, "Failed to remove old credential(s)");
756 }
757 
758 
759 static void set_pps_cred_policy_spe(struct hs20_osu_client *ctx, int id,
760 				    xml_node_t *spe)
761 {
762 	xml_node_t *ssid;
763 	char *txt;
764 
765 	ssid = get_node(ctx->xml, spe, "SSID");
766 	if (ssid == NULL)
767 		return;
768 	txt = xml_node_get_text(ctx->xml, ssid);
769 	if (txt == NULL)
770 		return;
771 	wpa_printf(MSG_DEBUG, "- Policy/SPExclusionList/<X+>/SSID = %s", txt);
772 	if (set_cred_quoted(ctx->ifname, id, "excluded_ssid", txt) < 0)
773 		wpa_printf(MSG_INFO, "Failed to set cred excluded_ssid");
774 	xml_node_get_text_free(ctx->xml, txt);
775 }
776 
777 
778 static void set_pps_cred_policy_spel(struct hs20_osu_client *ctx, int id,
779 				     xml_node_t *spel)
780 {
781 	xml_node_t *child;
782 
783 	xml_node_for_each_child(ctx->xml, child, spel) {
784 		xml_node_for_each_check(ctx->xml, child);
785 		set_pps_cred_policy_spe(ctx, id, child);
786 	}
787 }
788 
789 
790 static void set_pps_cred_policy_prp(struct hs20_osu_client *ctx, int id,
791 				    xml_node_t *prp)
792 {
793 	xml_node_t *node;
794 	char *txt = NULL, *pos;
795 	char *prio, *country_buf = NULL;
796 	const char *country;
797 	char val[200];
798 	int priority;
799 
800 	node = get_node(ctx->xml, prp, "Priority");
801 	if (node == NULL)
802 		return;
803 	prio = xml_node_get_text(ctx->xml, node);
804 	if (prio == NULL)
805 		return;
806 	wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s",
807 		   prio);
808 	priority = atoi(prio);
809 	xml_node_get_text_free(ctx->xml, prio);
810 
811 	node = get_node(ctx->xml, prp, "Country");
812 	if (node) {
813 		country_buf = xml_node_get_text(ctx->xml, node);
814 		if (country_buf == NULL)
815 			return;
816 		country = country_buf;
817 		wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s",
818 			   country);
819 	} else {
820 		country = "*";
821 	}
822 
823 	node = get_node(ctx->xml, prp, "FQDN_Match");
824 	if (node == NULL)
825 		goto out;
826 	txt = xml_node_get_text(ctx->xml, node);
827 	if (txt == NULL)
828 		goto out;
829 	wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s",
830 		   txt);
831 	pos = strrchr(txt, ',');
832 	if (pos == NULL)
833 		goto out;
834 	*pos++ = '\0';
835 
836 	snprintf(val, sizeof(val), "%s,%d,%d,%s", txt,
837 		 strcmp(pos, "includeSubdomains") != 0, priority, country);
838 	if (set_cred_quoted(ctx->ifname, id, "roaming_partner", val) < 0)
839 		wpa_printf(MSG_INFO, "Failed to set cred roaming_partner");
840 out:
841 	xml_node_get_text_free(ctx->xml, country_buf);
842 	xml_node_get_text_free(ctx->xml, txt);
843 }
844 
845 
846 static void set_pps_cred_policy_prpl(struct hs20_osu_client *ctx, int id,
847 				     xml_node_t *prpl)
848 {
849 	xml_node_t *child;
850 
851 	xml_node_for_each_child(ctx->xml, child, prpl) {
852 		xml_node_for_each_check(ctx->xml, child);
853 		set_pps_cred_policy_prp(ctx, id, child);
854 	}
855 }
856 
857 
858 static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client *ctx, int id,
859 					     xml_node_t *min_backhaul)
860 {
861 	xml_node_t *node;
862 	char *type, *dl = NULL, *ul = NULL;
863 	int home;
864 
865 	node = get_node(ctx->xml, min_backhaul, "NetworkType");
866 	if (node == NULL) {
867 		wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without mandatory NetworkType node");
868 		return;
869 	}
870 
871 	type = xml_node_get_text(ctx->xml, node);
872 	if (type == NULL)
873 		return;
874 	wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/NetworkType = %s",
875 		   type);
876 	if (os_strcasecmp(type, "home") == 0)
877 		home = 1;
878 	else if (os_strcasecmp(type, "roaming") == 0)
879 		home = 0;
880 	else {
881 		wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold with invalid NetworkType");
882 		xml_node_get_text_free(ctx->xml, type);
883 		return;
884 	}
885 	xml_node_get_text_free(ctx->xml, type);
886 
887 	node = get_node(ctx->xml, min_backhaul, "DLBandwidth");
888 	if (node)
889 		dl = xml_node_get_text(ctx->xml, node);
890 
891 	node = get_node(ctx->xml, min_backhaul, "ULBandwidth");
892 	if (node)
893 		ul = xml_node_get_text(ctx->xml, node);
894 
895 	if (dl == NULL && ul == NULL) {
896 		wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without either DLBandwidth or ULBandwidth nodes");
897 		return;
898 	}
899 
900 	if (dl)
901 		wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/DLBandwidth = %s",
902 			   dl);
903 	if (ul)
904 		wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/ULBandwidth = %s",
905 			   ul);
906 
907 	if (home) {
908 		if (dl &&
909 		    set_cred(ctx->ifname, id, "min_dl_bandwidth_home", dl) < 0)
910 			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
911 		if (ul &&
912 		    set_cred(ctx->ifname, id, "min_ul_bandwidth_home", ul) < 0)
913 			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
914 	} else {
915 		if (dl &&
916 		    set_cred(ctx->ifname, id, "min_dl_bandwidth_roaming", dl) <
917 		    0)
918 			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
919 		if (ul &&
920 		    set_cred(ctx->ifname, id, "min_ul_bandwidth_roaming", ul) <
921 		    0)
922 			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
923 	}
924 
925 	xml_node_get_text_free(ctx->xml, dl);
926 	xml_node_get_text_free(ctx->xml, ul);
927 }
928 
929 
930 static void set_pps_cred_policy_min_backhaul_list(struct hs20_osu_client *ctx,
931 						  int id, xml_node_t *node)
932 {
933 	xml_node_t *child;
934 
935 	wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold");
936 
937 	xml_node_for_each_child(ctx->xml, child, node) {
938 		xml_node_for_each_check(ctx->xml, child);
939 		set_pps_cred_policy_min_backhaul(ctx, id, child);
940 	}
941 }
942 
943 
944 static void set_pps_cred_policy_update(struct hs20_osu_client *ctx, int id,
945 				       xml_node_t *node)
946 {
947 	wpa_printf(MSG_INFO, "- Policy/PolicyUpdate");
948 	/* Not used in wpa_supplicant */
949 }
950 
951 
952 static void set_pps_cred_policy_required_proto_port(struct hs20_osu_client *ctx,
953 						    int id, xml_node_t *tuple)
954 {
955 	xml_node_t *node;
956 	char *proto, *port;
957 	char *buf;
958 	size_t buflen;
959 
960 	node = get_node(ctx->xml, tuple, "IPProtocol");
961 	if (node == NULL) {
962 		wpa_printf(MSG_INFO, "Ignore RequiredProtoPortTuple without mandatory IPProtocol node");
963 		return;
964 	}
965 
966 	proto = xml_node_get_text(ctx->xml, node);
967 	if (proto == NULL)
968 		return;
969 
970 	wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/IPProtocol = %s",
971 		   proto);
972 
973 	node = get_node(ctx->xml, tuple, "PortNumber");
974 	port = node ? xml_node_get_text(ctx->xml, node) : NULL;
975 	if (port) {
976 		wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/PortNumber = %s",
977 			   port);
978 		buflen = os_strlen(proto) + os_strlen(port) + 10;
979 		buf = os_malloc(buflen);
980 		if (buf)
981 			os_snprintf(buf, buflen, "%s:%s", proto, port);
982 		xml_node_get_text_free(ctx->xml, port);
983 	} else {
984 		buflen = os_strlen(proto) + 10;
985 		buf = os_malloc(buflen);
986 		if (buf)
987 			os_snprintf(buf, buflen, "%s", proto);
988 	}
989 
990 	xml_node_get_text_free(ctx->xml, proto);
991 
992 	if (buf == NULL)
993 		return;
994 
995 	if (set_cred(ctx->ifname, id, "req_conn_capab", buf) < 0)
996 		wpa_printf(MSG_INFO, "Could not set req_conn_capab");
997 
998 	os_free(buf);
999 }
1000 
1001 
1002 static void set_pps_cred_policy_required_proto_ports(struct hs20_osu_client *ctx,
1003 						     int id, xml_node_t *node)
1004 {
1005 	xml_node_t *child;
1006 
1007 	wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple");
1008 
1009 	xml_node_for_each_child(ctx->xml, child, node) {
1010 		xml_node_for_each_check(ctx->xml, child);
1011 		set_pps_cred_policy_required_proto_port(ctx, id, child);
1012 	}
1013 }
1014 
1015 
1016 static void set_pps_cred_policy_max_bss_load(struct hs20_osu_client *ctx, int id,
1017 					     xml_node_t *node)
1018 {
1019 	char *str = xml_node_get_text(ctx->xml, node);
1020 	if (str == NULL)
1021 		return;
1022 	wpa_printf(MSG_INFO, "- Policy/MaximumBSSLoadValue - %s", str);
1023 	if (set_cred(ctx->ifname, id, "max_bss_load", str) < 0)
1024 		wpa_printf(MSG_INFO, "Failed to set cred max_bss_load limit");
1025 	xml_node_get_text_free(ctx->xml, str);
1026 }
1027 
1028 
1029 static void set_pps_cred_policy(struct hs20_osu_client *ctx, int id,
1030 				xml_node_t *node)
1031 {
1032 	xml_node_t *child;
1033 	const char *name;
1034 
1035 	wpa_printf(MSG_INFO, "- Policy");
1036 
1037 	xml_node_for_each_child(ctx->xml, child, node) {
1038 		xml_node_for_each_check(ctx->xml, child);
1039 		name = xml_node_get_localname(ctx->xml, child);
1040 		if (os_strcasecmp(name, "PreferredRoamingPartnerList") == 0)
1041 			set_pps_cred_policy_prpl(ctx, id, child);
1042 		else if (os_strcasecmp(name, "MinBackhaulThreshold") == 0)
1043 			set_pps_cred_policy_min_backhaul_list(ctx, id, child);
1044 		else if (os_strcasecmp(name, "PolicyUpdate") == 0)
1045 			set_pps_cred_policy_update(ctx, id, child);
1046 		else if (os_strcasecmp(name, "SPExclusionList") == 0)
1047 			set_pps_cred_policy_spel(ctx, id, child);
1048 		else if (os_strcasecmp(name, "RequiredProtoPortTuple") == 0)
1049 			set_pps_cred_policy_required_proto_ports(ctx, id, child);
1050 		else if (os_strcasecmp(name, "MaximumBSSLoadValue") == 0)
1051 			set_pps_cred_policy_max_bss_load(ctx, id, child);
1052 		else
1053 			wpa_printf(MSG_INFO, "Unknown Policy node '%s'", name);
1054 	}
1055 }
1056 
1057 
1058 static void set_pps_cred_priority(struct hs20_osu_client *ctx, int id,
1059 				  xml_node_t *node)
1060 {
1061 	char *str = xml_node_get_text(ctx->xml, node);
1062 	if (str == NULL)
1063 		return;
1064 	wpa_printf(MSG_INFO, "- CredentialPriority = %s", str);
1065 	if (set_cred(ctx->ifname, id, "sp_priority", str) < 0)
1066 		wpa_printf(MSG_INFO, "Failed to set cred sp_priority");
1067 	xml_node_get_text_free(ctx->xml, str);
1068 }
1069 
1070 
1071 static void set_pps_cred_aaa_server_trust_root(struct hs20_osu_client *ctx,
1072 					       int id, xml_node_t *node)
1073 {
1074 	wpa_printf(MSG_INFO, "- AAAServerTrustRoot - TODO");
1075 }
1076 
1077 
1078 static void set_pps_cred_sub_update(struct hs20_osu_client *ctx, int id,
1079 				    xml_node_t *node)
1080 {
1081 	wpa_printf(MSG_INFO, "- SubscriptionUpdate");
1082 	/* not used within wpa_supplicant */
1083 }
1084 
1085 
1086 static void set_pps_cred_home_sp_network_id(struct hs20_osu_client *ctx,
1087 					    int id, xml_node_t *node)
1088 {
1089 	xml_node_t *ssid_node, *hessid_node;
1090 	char *ssid, *hessid;
1091 
1092 	ssid_node = get_node(ctx->xml, node, "SSID");
1093 	if (ssid_node == NULL) {
1094 		wpa_printf(MSG_INFO, "Ignore HomeSP/NetworkID without mandatory SSID node");
1095 		return;
1096 	}
1097 
1098 	hessid_node = get_node(ctx->xml, node, "HESSID");
1099 
1100 	ssid = xml_node_get_text(ctx->xml, ssid_node);
1101 	if (ssid == NULL)
1102 		return;
1103 	hessid = hessid_node ? xml_node_get_text(ctx->xml, hessid_node) : NULL;
1104 
1105 	wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/SSID = %s", ssid);
1106 	if (hessid)
1107 		wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/HESSID = %s",
1108 			   hessid);
1109 
1110 	/* TODO: Configure to wpa_supplicant */
1111 
1112 	xml_node_get_text_free(ctx->xml, ssid);
1113 	xml_node_get_text_free(ctx->xml, hessid);
1114 }
1115 
1116 
1117 static void set_pps_cred_home_sp_network_ids(struct hs20_osu_client *ctx,
1118 					     int id, xml_node_t *node)
1119 {
1120 	xml_node_t *child;
1121 
1122 	wpa_printf(MSG_INFO, "- HomeSP/NetworkID");
1123 
1124 	xml_node_for_each_child(ctx->xml, child, node) {
1125 		xml_node_for_each_check(ctx->xml, child);
1126 		set_pps_cred_home_sp_network_id(ctx, id, child);
1127 	}
1128 }
1129 
1130 
1131 static void set_pps_cred_home_sp_friendly_name(struct hs20_osu_client *ctx,
1132 					       int id, xml_node_t *node)
1133 {
1134 	char *str = xml_node_get_text(ctx->xml, node);
1135 	if (str == NULL)
1136 		return;
1137 	wpa_printf(MSG_INFO, "- HomeSP/FriendlyName = %s", str);
1138 	/* not used within wpa_supplicant(?) */
1139 	xml_node_get_text_free(ctx->xml, str);
1140 }
1141 
1142 
1143 static void set_pps_cred_home_sp_icon_url(struct hs20_osu_client *ctx,
1144 					  int id, xml_node_t *node)
1145 {
1146 	char *str = xml_node_get_text(ctx->xml, node);
1147 	if (str == NULL)
1148 		return;
1149 	wpa_printf(MSG_INFO, "- HomeSP/IconURL = %s", str);
1150 	/* not used within wpa_supplicant */
1151 	xml_node_get_text_free(ctx->xml, str);
1152 }
1153 
1154 
1155 static void set_pps_cred_home_sp_fqdn(struct hs20_osu_client *ctx, int id,
1156 				      xml_node_t *node)
1157 {
1158 	char *str = xml_node_get_text(ctx->xml, node);
1159 	if (str == NULL)
1160 		return;
1161 	wpa_printf(MSG_INFO, "- HomeSP/FQDN = %s", str);
1162 	if (set_cred_quoted(ctx->ifname, id, "domain", str) < 0)
1163 		wpa_printf(MSG_INFO, "Failed to set cred domain");
1164 	if (set_cred_quoted(ctx->ifname, id, "domain_suffix_match", str) < 0)
1165 		wpa_printf(MSG_INFO, "Failed to set cred domain_suffix_match");
1166 	xml_node_get_text_free(ctx->xml, str);
1167 }
1168 
1169 
1170 static void set_pps_cred_home_sp_oi(struct hs20_osu_client *ctx, int id,
1171 				    xml_node_t *node)
1172 {
1173 	xml_node_t *child;
1174 	const char *name;
1175 	char *homeoi = NULL;
1176 	int required = 0;
1177 	char *str;
1178 
1179 	xml_node_for_each_child(ctx->xml, child, node) {
1180 		xml_node_for_each_check(ctx->xml, child);
1181 		name = xml_node_get_localname(ctx->xml, child);
1182 		if (strcasecmp(name, "HomeOI") == 0 && !homeoi) {
1183 			homeoi = xml_node_get_text(ctx->xml, child);
1184 			wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOI = %s",
1185 				   homeoi);
1186 		} else if (strcasecmp(name, "HomeOIRequired") == 0) {
1187 			str = xml_node_get_text(ctx->xml, child);
1188 			wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOIRequired = '%s'",
1189 				   str);
1190 			if (str == NULL)
1191 				continue;
1192 			required = strcasecmp(str, "true") == 0;
1193 			xml_node_get_text_free(ctx->xml, str);
1194 		} else
1195 			wpa_printf(MSG_INFO, "Unknown HomeOIList node '%s'",
1196 				   name);
1197 	}
1198 
1199 	if (homeoi == NULL) {
1200 		wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> without HomeOI ignored");
1201 		return;
1202 	}
1203 
1204 	wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> '%s' required=%d",
1205 		   homeoi, required);
1206 
1207 	if (required) {
1208 		if (set_cred(ctx->ifname, id, "required_roaming_consortium",
1209 			     homeoi) < 0)
1210 			wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium");
1211 	} else {
1212 		if (set_cred_quoted(ctx->ifname, id, "roaming_consortium",
1213 				    homeoi) < 0)
1214 			wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium");
1215 	}
1216 
1217 	xml_node_get_text_free(ctx->xml, homeoi);
1218 }
1219 
1220 
1221 static void set_pps_cred_home_sp_oi_list(struct hs20_osu_client *ctx, int id,
1222 					 xml_node_t *node)
1223 {
1224 	xml_node_t *child;
1225 
1226 	wpa_printf(MSG_INFO, "- HomeSP/HomeOIList");
1227 
1228 	xml_node_for_each_child(ctx->xml, child, node) {
1229 		xml_node_for_each_check(ctx->xml, child);
1230 		set_pps_cred_home_sp_oi(ctx, id, child);
1231 	}
1232 }
1233 
1234 
1235 static void set_pps_cred_home_sp_other_partner(struct hs20_osu_client *ctx,
1236 					       int id, xml_node_t *node)
1237 {
1238 	xml_node_t *child;
1239 	const char *name;
1240 	char *fqdn = NULL;
1241 
1242 	xml_node_for_each_child(ctx->xml, child, node) {
1243 		xml_node_for_each_check(ctx->xml, child);
1244 		name = xml_node_get_localname(ctx->xml, child);
1245 		if (os_strcasecmp(name, "FQDN") == 0 && !fqdn) {
1246 			fqdn = xml_node_get_text(ctx->xml, child);
1247 			wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+>/FQDN = %s",
1248 				   fqdn);
1249 		} else
1250 			wpa_printf(MSG_INFO, "Unknown OtherHomePartners node '%s'",
1251 				   name);
1252 	}
1253 
1254 	if (fqdn == NULL) {
1255 		wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+> without FQDN ignored");
1256 		return;
1257 	}
1258 
1259 	if (set_cred_quoted(ctx->ifname, id, "domain", fqdn) < 0)
1260 		wpa_printf(MSG_INFO, "Failed to set cred domain for OtherHomePartners node");
1261 
1262 	xml_node_get_text_free(ctx->xml, fqdn);
1263 }
1264 
1265 
1266 static void set_pps_cred_home_sp_other_partners(struct hs20_osu_client *ctx,
1267 						int id,
1268 						xml_node_t *node)
1269 {
1270 	xml_node_t *child;
1271 
1272 	wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners");
1273 
1274 	xml_node_for_each_child(ctx->xml, child, node) {
1275 		xml_node_for_each_check(ctx->xml, child);
1276 		set_pps_cred_home_sp_other_partner(ctx, id, child);
1277 	}
1278 }
1279 
1280 
1281 static void set_pps_cred_home_sp_roaming_consortium_oi(
1282 	struct hs20_osu_client *ctx, int id, xml_node_t *node)
1283 {
1284 	char *str = xml_node_get_text(ctx->xml, node);
1285 	if (str == NULL)
1286 		return;
1287 	wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str);
1288 	/* TODO: Set to wpa_supplicant */
1289 	xml_node_get_text_free(ctx->xml, str);
1290 }
1291 
1292 
1293 static void set_pps_cred_home_sp(struct hs20_osu_client *ctx, int id,
1294 				 xml_node_t *node)
1295 {
1296 	xml_node_t *child;
1297 	const char *name;
1298 
1299 	wpa_printf(MSG_INFO, "- HomeSP");
1300 
1301 	xml_node_for_each_child(ctx->xml, child, node) {
1302 		xml_node_for_each_check(ctx->xml, child);
1303 		name = xml_node_get_localname(ctx->xml, child);
1304 		if (os_strcasecmp(name, "NetworkID") == 0)
1305 			set_pps_cred_home_sp_network_ids(ctx, id, child);
1306 		else if (os_strcasecmp(name, "FriendlyName") == 0)
1307 			set_pps_cred_home_sp_friendly_name(ctx, id, child);
1308 		else if (os_strcasecmp(name, "IconURL") == 0)
1309 			set_pps_cred_home_sp_icon_url(ctx, id, child);
1310 		else if (os_strcasecmp(name, "FQDN") == 0)
1311 			set_pps_cred_home_sp_fqdn(ctx, id, child);
1312 		else if (os_strcasecmp(name, "HomeOIList") == 0)
1313 			set_pps_cred_home_sp_oi_list(ctx, id, child);
1314 		else if (os_strcasecmp(name, "OtherHomePartners") == 0)
1315 			set_pps_cred_home_sp_other_partners(ctx, id, child);
1316 		else if (os_strcasecmp(name, "RoamingConsortiumOI") == 0)
1317 			set_pps_cred_home_sp_roaming_consortium_oi(ctx, id,
1318 								   child);
1319 		else
1320 			wpa_printf(MSG_INFO, "Unknown HomeSP node '%s'", name);
1321 	}
1322 }
1323 
1324 
1325 static void set_pps_cred_sub_params(struct hs20_osu_client *ctx, int id,
1326 				    xml_node_t *node)
1327 {
1328 	wpa_printf(MSG_INFO, "- SubscriptionParameters");
1329 	/* not used within wpa_supplicant */
1330 }
1331 
1332 
1333 static void set_pps_cred_creation_date(struct hs20_osu_client *ctx, int id,
1334 				       xml_node_t *node)
1335 {
1336 	char *str = xml_node_get_text(ctx->xml, node);
1337 	if (str == NULL)
1338 		return;
1339 	wpa_printf(MSG_INFO, "- Credential/CreationDate = %s", str);
1340 	/* not used within wpa_supplicant */
1341 	xml_node_get_text_free(ctx->xml, str);
1342 }
1343 
1344 
1345 static void set_pps_cred_expiration_date(struct hs20_osu_client *ctx, int id,
1346 					 xml_node_t *node)
1347 {
1348 	char *str = xml_node_get_text(ctx->xml, node);
1349 	if (str == NULL)
1350 		return;
1351 	wpa_printf(MSG_INFO, "- Credential/ExpirationDate = %s", str);
1352 	/* not used within wpa_supplicant */
1353 	xml_node_get_text_free(ctx->xml, str);
1354 }
1355 
1356 
1357 static void set_pps_cred_username(struct hs20_osu_client *ctx, int id,
1358 				  xml_node_t *node)
1359 {
1360 	char *str = xml_node_get_text(ctx->xml, node);
1361 	if (str == NULL)
1362 		return;
1363 	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Username = %s",
1364 		   str);
1365 	if (set_cred_quoted(ctx->ifname, id, "username", str) < 0)
1366 		wpa_printf(MSG_INFO, "Failed to set cred username");
1367 	xml_node_get_text_free(ctx->xml, str);
1368 }
1369 
1370 
1371 static void set_pps_cred_password(struct hs20_osu_client *ctx, int id,
1372 				  xml_node_t *node)
1373 {
1374 	int len, i;
1375 	char *pw, *hex, *pos, *end;
1376 
1377 	pw = xml_node_get_base64_text(ctx->xml, node, &len);
1378 	if (pw == NULL)
1379 		return;
1380 
1381 	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Password = %s", pw);
1382 
1383 	hex = malloc(len * 2 + 1);
1384 	if (hex == NULL) {
1385 		free(pw);
1386 		return;
1387 	}
1388 	end = hex + len * 2 + 1;
1389 	pos = hex;
1390 	for (i = 0; i < len; i++) {
1391 		snprintf(pos, end - pos, "%02x", pw[i]);
1392 		pos += 2;
1393 	}
1394 	free(pw);
1395 
1396 	if (set_cred(ctx->ifname, id, "password", hex) < 0)
1397 		wpa_printf(MSG_INFO, "Failed to set cred password");
1398 	free(hex);
1399 }
1400 
1401 
1402 static void set_pps_cred_machine_managed(struct hs20_osu_client *ctx, int id,
1403 					 xml_node_t *node)
1404 {
1405 	char *str = xml_node_get_text(ctx->xml, node);
1406 	if (str == NULL)
1407 		return;
1408 	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/MachineManaged = %s",
1409 		   str);
1410 	/* not used within wpa_supplicant */
1411 	xml_node_get_text_free(ctx->xml, str);
1412 }
1413 
1414 
1415 static void set_pps_cred_soft_token_app(struct hs20_osu_client *ctx, int id,
1416 					xml_node_t *node)
1417 {
1418 	char *str = xml_node_get_text(ctx->xml, node);
1419 	if (str == NULL)
1420 		return;
1421 	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/SoftTokenApp = %s",
1422 		   str);
1423 	/* not used within wpa_supplicant */
1424 	xml_node_get_text_free(ctx->xml, str);
1425 }
1426 
1427 
1428 static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id,
1429 				       xml_node_t *node)
1430 {
1431 	char *str = xml_node_get_text(ctx->xml, node);
1432 	if (str == NULL)
1433 		return;
1434 	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/AbleToShare = %s",
1435 		   str);
1436 	/* not used within wpa_supplicant */
1437 	xml_node_get_text_free(ctx->xml, str);
1438 }
1439 
1440 
1441 static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id,
1442 				    xml_node_t *node)
1443 {
1444 	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod - TODO");
1445 }
1446 
1447 
1448 static void set_pps_cred_username_password(struct hs20_osu_client *ctx, int id,
1449 					   xml_node_t *node)
1450 {
1451 	xml_node_t *child;
1452 	const char *name;
1453 
1454 	wpa_printf(MSG_INFO, "- Credential/UsernamePassword");
1455 
1456 	xml_node_for_each_child(ctx->xml, child, node) {
1457 		xml_node_for_each_check(ctx->xml, child);
1458 		name = xml_node_get_localname(ctx->xml, child);
1459 		if (os_strcasecmp(name, "Username") == 0)
1460 			set_pps_cred_username(ctx, id, child);
1461 		else if (os_strcasecmp(name, "Password") == 0)
1462 			set_pps_cred_password(ctx, id, child);
1463 		else if (os_strcasecmp(name, "MachineManaged") == 0)
1464 			set_pps_cred_machine_managed(ctx, id, child);
1465 		else if (os_strcasecmp(name, "SoftTokenApp") == 0)
1466 			set_pps_cred_soft_token_app(ctx, id, child);
1467 		else if (os_strcasecmp(name, "AbleToShare") == 0)
1468 			set_pps_cred_able_to_share(ctx, id, child);
1469 		else if (os_strcasecmp(name, "EAPMethod") == 0)
1470 			set_pps_cred_eap_method(ctx, id, child);
1471 		else
1472 			wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword node '%s'",
1473 				   name);
1474 	}
1475 }
1476 
1477 
1478 static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id,
1479 				      xml_node_t *node, const char *fqdn)
1480 {
1481 	char buf[200], dir[200];
1482 
1483 	wpa_printf(MSG_INFO, "- Credential/DigitalCertificate");
1484 
1485 	if (getcwd(dir, sizeof(dir)) == NULL)
1486 		return;
1487 
1488 	/* TODO: could build username from Subject of Subject AltName */
1489 	if (set_cred_quoted(ctx->ifname, id, "username", "cert") < 0) {
1490 		wpa_printf(MSG_INFO, "Failed to set username");
1491 	}
1492 
1493 	snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, fqdn);
1494 	if (os_file_exists(buf)) {
1495 		if (set_cred_quoted(ctx->ifname, id, "client_cert", buf) < 0) {
1496 			wpa_printf(MSG_INFO, "Failed to set client_cert");
1497 		}
1498 	}
1499 
1500 	snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir, fqdn);
1501 	if (os_file_exists(buf)) {
1502 		if (set_cred_quoted(ctx->ifname, id, "private_key", buf) < 0) {
1503 			wpa_printf(MSG_INFO, "Failed to set private_key");
1504 		}
1505 	}
1506 }
1507 
1508 
1509 static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id,
1510 			       xml_node_t *node, const char *fqdn, int sim)
1511 {
1512 	char *str = xml_node_get_text(ctx->xml, node);
1513 	char buf[200], dir[200];
1514 
1515 	if (str == NULL)
1516 		return;
1517 
1518 	wpa_printf(MSG_INFO, "- Credential/Realm = %s", str);
1519 	if (set_cred_quoted(ctx->ifname, id, "realm", str) < 0)
1520 		wpa_printf(MSG_INFO, "Failed to set cred realm");
1521 	xml_node_get_text_free(ctx->xml, str);
1522 
1523 	if (sim)
1524 		return;
1525 
1526 	if (getcwd(dir, sizeof(dir)) == NULL)
1527 		return;
1528 	snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn);
1529 	if (os_file_exists(buf)) {
1530 		if (set_cred_quoted(ctx->ifname, id, "ca_cert", buf) < 0) {
1531 			wpa_printf(MSG_INFO, "Failed to set CA cert");
1532 		}
1533 	}
1534 }
1535 
1536 
1537 static void set_pps_cred_check_aaa_cert_status(struct hs20_osu_client *ctx,
1538 					       int id, xml_node_t *node)
1539 {
1540 	char *str = xml_node_get_text(ctx->xml, node);
1541 
1542 	if (str == NULL)
1543 		return;
1544 
1545 	wpa_printf(MSG_INFO, "- Credential/CheckAAAServerCertStatus = %s", str);
1546 	if (os_strcasecmp(str, "true") == 0 &&
1547 	    set_cred(ctx->ifname, id, "ocsp", "2") < 0)
1548 		wpa_printf(MSG_INFO, "Failed to set cred ocsp");
1549 	xml_node_get_text_free(ctx->xml, str);
1550 }
1551 
1552 
1553 static void set_pps_cred_sim(struct hs20_osu_client *ctx, int id,
1554 			     xml_node_t *sim, xml_node_t *realm)
1555 {
1556 	xml_node_t *node;
1557 	char *imsi, *eaptype, *str, buf[20];
1558 	int type;
1559 	int mnc_len = 3;
1560 	size_t imsi_len;
1561 
1562 	node = get_node(ctx->xml, sim, "EAPType");
1563 	if (node == NULL) {
1564 		wpa_printf(MSG_INFO, "No SIM/EAPType node in credential");
1565 		return;
1566 	}
1567 	eaptype = xml_node_get_text(ctx->xml, node);
1568 	if (eaptype == NULL) {
1569 		wpa_printf(MSG_INFO, "Could not extract SIM/EAPType");
1570 		return;
1571 	}
1572 	wpa_printf(MSG_INFO, " - Credential/SIM/EAPType = %s", eaptype);
1573 	type = atoi(eaptype);
1574 	xml_node_get_text_free(ctx->xml, eaptype);
1575 
1576 	switch (type) {
1577 	case EAP_TYPE_SIM:
1578 		if (set_cred(ctx->ifname, id, "eap", "SIM") < 0)
1579 			wpa_printf(MSG_INFO, "Could not set eap=SIM");
1580 		break;
1581 	case EAP_TYPE_AKA:
1582 		if (set_cred(ctx->ifname, id, "eap", "AKA") < 0)
1583 			wpa_printf(MSG_INFO, "Could not set eap=SIM");
1584 		break;
1585 	case EAP_TYPE_AKA_PRIME:
1586 		if (set_cred(ctx->ifname, id, "eap", "AKA'") < 0)
1587 			wpa_printf(MSG_INFO, "Could not set eap=SIM");
1588 		break;
1589 	default:
1590 		wpa_printf(MSG_INFO, "Unsupported SIM/EAPType %d", type);
1591 		return;
1592 	}
1593 
1594 	node = get_node(ctx->xml, sim, "IMSI");
1595 	if (node == NULL) {
1596 		wpa_printf(MSG_INFO, "No SIM/IMSI node in credential");
1597 		return;
1598 	}
1599 	imsi = xml_node_get_text(ctx->xml, node);
1600 	if (imsi == NULL) {
1601 		wpa_printf(MSG_INFO, "Could not extract SIM/IMSI");
1602 		return;
1603 	}
1604 	wpa_printf(MSG_INFO, " - Credential/SIM/IMSI = %s", imsi);
1605 	imsi_len = os_strlen(imsi);
1606 	if (imsi_len < 7 || imsi_len + 2 > sizeof(buf)) {
1607 		wpa_printf(MSG_INFO, "Invalid IMSI length");
1608 		xml_node_get_text_free(ctx->xml, imsi);
1609 		return;
1610 	}
1611 
1612 	str = xml_node_get_text(ctx->xml, node);
1613 	if (str) {
1614 		char *pos;
1615 		pos = os_strstr(str, "mnc");
1616 		if (pos && os_strlen(pos) >= 6) {
1617 			if (os_strncmp(imsi + 3, pos + 3, 3) == 0)
1618 				mnc_len = 3;
1619 			else if (os_strncmp(imsi + 3, pos + 4, 2) == 0)
1620 				mnc_len = 2;
1621 		}
1622 		xml_node_get_text_free(ctx->xml, str);
1623 	}
1624 
1625 	os_memcpy(buf, imsi, 3 + mnc_len);
1626 	buf[3 + mnc_len] = '-';
1627 	os_strlcpy(buf + 3 + mnc_len + 1, imsi + 3 + mnc_len,
1628 		   sizeof(buf) - 3 - mnc_len - 1);
1629 
1630 	xml_node_get_text_free(ctx->xml, imsi);
1631 
1632 	if (set_cred_quoted(ctx->ifname, id, "imsi", buf) < 0)
1633 		wpa_printf(MSG_INFO, "Could not set IMSI");
1634 
1635 	if (set_cred_quoted(ctx->ifname, id, "milenage",
1636 			    "90dca4eda45b53cf0f12d7c9c3bc6a89:"
1637 			    "cb9cccc4b9258e6dca4760379fb82581:000000000123") <
1638 	    0)
1639 		wpa_printf(MSG_INFO, "Could not set Milenage parameters");
1640 }
1641 
1642 
1643 static void set_pps_cred_credential(struct hs20_osu_client *ctx, int id,
1644 				    xml_node_t *node, const char *fqdn)
1645 {
1646 	xml_node_t *child, *sim, *realm;
1647 	const char *name;
1648 
1649 	wpa_printf(MSG_INFO, "- Credential");
1650 
1651 	sim = get_node(ctx->xml, node, "SIM");
1652 	realm = get_node(ctx->xml, node, "Realm");
1653 
1654 	xml_node_for_each_child(ctx->xml, child, node) {
1655 		xml_node_for_each_check(ctx->xml, child);
1656 		name = xml_node_get_localname(ctx->xml, child);
1657 		if (os_strcasecmp(name, "CreationDate") == 0)
1658 			set_pps_cred_creation_date(ctx, id, child);
1659 		else if (os_strcasecmp(name, "ExpirationDate") == 0)
1660 			set_pps_cred_expiration_date(ctx, id, child);
1661 		else if (os_strcasecmp(name, "UsernamePassword") == 0)
1662 			set_pps_cred_username_password(ctx, id, child);
1663 		else if (os_strcasecmp(name, "DigitalCertificate") == 0)
1664 			set_pps_cred_digital_cert(ctx, id, child, fqdn);
1665 		else if (os_strcasecmp(name, "Realm") == 0)
1666 			set_pps_cred_realm(ctx, id, child, fqdn, sim != NULL);
1667 		else if (os_strcasecmp(name, "CheckAAAServerCertStatus") == 0)
1668 			set_pps_cred_check_aaa_cert_status(ctx, id, child);
1669 		else if (os_strcasecmp(name, "SIM") == 0)
1670 			set_pps_cred_sim(ctx, id, child, realm);
1671 		else
1672 			wpa_printf(MSG_INFO, "Unknown Credential node '%s'",
1673 				   name);
1674 	}
1675 }
1676 
1677 
1678 static void set_pps_credential(struct hs20_osu_client *ctx, int id,
1679 			       xml_node_t *cred, const char *fqdn)
1680 {
1681 	xml_node_t *child;
1682 	const char *name;
1683 
1684 	xml_node_for_each_child(ctx->xml, child, cred) {
1685 		xml_node_for_each_check(ctx->xml, child);
1686 		name = xml_node_get_localname(ctx->xml, child);
1687 		if (os_strcasecmp(name, "Policy") == 0)
1688 			set_pps_cred_policy(ctx, id, child);
1689 		else if (os_strcasecmp(name, "CredentialPriority") == 0)
1690 			set_pps_cred_priority(ctx, id, child);
1691 		else if (os_strcasecmp(name, "AAAServerTrustRoot") == 0)
1692 			set_pps_cred_aaa_server_trust_root(ctx, id, child);
1693 		else if (os_strcasecmp(name, "SubscriptionUpdate") == 0)
1694 			set_pps_cred_sub_update(ctx, id, child);
1695 		else if (os_strcasecmp(name, "HomeSP") == 0)
1696 			set_pps_cred_home_sp(ctx, id, child);
1697 		else if (os_strcasecmp(name, "SubscriptionParameters") == 0)
1698 			set_pps_cred_sub_params(ctx, id, child);
1699 		else if (os_strcasecmp(name, "Credential") == 0)
1700 			set_pps_cred_credential(ctx, id, child, fqdn);
1701 		else
1702 			wpa_printf(MSG_INFO, "Unknown credential node '%s'",
1703 				   name);
1704 	}
1705 }
1706 
1707 
1708 static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps,
1709 		    const char *fqdn)
1710 {
1711 	xml_node_t *child;
1712 	const char *name;
1713 	int id;
1714 	char *update_identifier = NULL;
1715 
1716 	/*
1717 	 * TODO: Could consider more complex mechanism that would remove
1718 	 * credentials only if there are changes in the information sent to
1719 	 * wpa_supplicant.
1720 	 */
1721 	remove_sp_creds(ctx, fqdn);
1722 
1723 	xml_node_for_each_child(ctx->xml, child, pps) {
1724 		xml_node_for_each_check(ctx->xml, child);
1725 		name = xml_node_get_localname(ctx->xml, child);
1726 		if (os_strcasecmp(name, "UpdateIdentifier") == 0) {
1727 			update_identifier = xml_node_get_text(ctx->xml, child);
1728 			if (update_identifier) {
1729 				wpa_printf(MSG_INFO, "- UpdateIdentifier = %s",
1730 					   update_identifier);
1731 				break;
1732 			}
1733 		}
1734 	}
1735 
1736 	xml_node_for_each_child(ctx->xml, child, pps) {
1737 		xml_node_for_each_check(ctx->xml, child);
1738 		name = xml_node_get_localname(ctx->xml, child);
1739 		if (os_strcasecmp(name, "UpdateIdentifier") == 0)
1740 			continue;
1741 		id = add_cred(ctx->ifname);
1742 		if (id < 0) {
1743 			wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant");
1744 			write_summary(ctx, "Failed to add credential to wpa_supplicant");
1745 			break;
1746 		}
1747 		write_summary(ctx, "Add a credential to wpa_supplicant");
1748 		if (update_identifier &&
1749 		    set_cred(ctx->ifname, id, "update_identifier",
1750 			     update_identifier) < 0)
1751 			wpa_printf(MSG_INFO, "Failed to set update_identifier");
1752 		if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) <
1753 		    0)
1754 			wpa_printf(MSG_INFO, "Failed to set provisioning_sp");
1755 		wpa_printf(MSG_INFO, "credential localname: '%s'", name);
1756 		set_pps_credential(ctx, id, child, fqdn);
1757 		ctx->pps_cred_set = 1;
1758 	}
1759 
1760 	xml_node_get_text_free(ctx->xml, update_identifier);
1761 }
1762 
1763 
1764 void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
1765 {
1766 	xml_node_t *pps;
1767 	const char *fqdn;
1768 	char *fqdn_buf = NULL, *pos;
1769 
1770 	pps = node_from_file(ctx->xml, pps_fname);
1771 	if (pps == NULL) {
1772 		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
1773 		return;
1774 	}
1775 
1776 	fqdn = os_strstr(pps_fname, "SP/");
1777 	if (fqdn) {
1778 		fqdn_buf = os_strdup(fqdn + 3);
1779 		if (fqdn_buf == NULL)
1780 			return;
1781 		pos = os_strchr(fqdn_buf, '/');
1782 		if (pos)
1783 			*pos = '\0';
1784 		fqdn = fqdn_buf;
1785 	} else
1786 		fqdn = "wi-fi.org";
1787 
1788 	wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s",
1789 		   fqdn);
1790 	set_pps(ctx, pps, fqdn);
1791 
1792 	os_free(fqdn_buf);
1793 	xml_node_free(ctx->xml, pps);
1794 }
1795 
1796 
1797 static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname)
1798 {
1799 	xml_node_t *pps, *node;
1800 	char *fqdn = NULL;
1801 
1802 	pps = node_from_file(ctx->xml, pps_fname);
1803 	if (pps == NULL) {
1804 		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
1805 		return -1;
1806 	}
1807 
1808 	node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
1809 	if (node)
1810 		fqdn = xml_node_get_text(ctx->xml, node);
1811 
1812 	xml_node_free(ctx->xml, pps);
1813 
1814 	if (fqdn) {
1815 		FILE *f = fopen("pps-fqdn", "w");
1816 		if (f) {
1817 			fprintf(f, "%s", fqdn);
1818 			fclose(f);
1819 		}
1820 		xml_node_get_text_free(ctx->xml, fqdn);
1821 		return 0;
1822 	}
1823 
1824 	xml_node_get_text_free(ctx->xml, fqdn);
1825 	return -1;
1826 }
1827 
1828 
1829 static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname,
1830 			const char *out_fname, const char *urn, int use_path)
1831 {
1832 	xml_node_t *mo, *node;
1833 
1834 	mo = node_from_file(ctx->xml, in_fname);
1835 	if (mo == NULL) {
1836 		wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
1837 		return;
1838 	}
1839 
1840 	node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL);
1841 	if (node) {
1842 		node_to_file(ctx->xml, out_fname, node);
1843 		xml_node_free(ctx->xml, node);
1844 	}
1845 
1846 	xml_node_free(ctx->xml, mo);
1847 }
1848 
1849 
1850 static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname,
1851 			  const char *out_fname)
1852 {
1853 	xml_node_t *tnds, *mo;
1854 
1855 	tnds = node_from_file(ctx->xml, in_fname);
1856 	if (tnds == NULL) {
1857 		wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
1858 		return;
1859 	}
1860 
1861 	mo = tnds_to_mo(ctx->xml, tnds);
1862 	if (mo) {
1863 		node_to_file(ctx->xml, out_fname, mo);
1864 		xml_node_free(ctx->xml, mo);
1865 	}
1866 
1867 	xml_node_free(ctx->xml, tnds);
1868 }
1869 
1870 
1871 struct osu_icon {
1872 	int id;
1873 	char lang[4];
1874 	char mime_type[256];
1875 	char filename[256];
1876 };
1877 
1878 struct osu_data {
1879 	char bssid[20];
1880 	char url[256];
1881 	unsigned int methods;
1882 	char osu_ssid[33];
1883 	char osu_nai[256];
1884 	struct osu_lang_text friendly_name[MAX_OSU_VALS];
1885 	size_t friendly_name_count;
1886 	struct osu_lang_text serv_desc[MAX_OSU_VALS];
1887 	size_t serv_desc_count;
1888 	struct osu_icon icon[MAX_OSU_VALS];
1889 	size_t icon_count;
1890 };
1891 
1892 
1893 static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
1894 {
1895 	FILE *f;
1896 	char buf[1000];
1897 	struct osu_data *osu = NULL, *last = NULL;
1898 	size_t osu_count = 0;
1899 	char *pos, *end;
1900 
1901 	f = fopen(fname, "r");
1902 	if (f == NULL) {
1903 		wpa_printf(MSG_ERROR, "Could not open %s", fname);
1904 		return NULL;
1905 	}
1906 
1907 	while (fgets(buf, sizeof(buf), f)) {
1908 		pos = strchr(buf, '\n');
1909 		if (pos)
1910 			*pos = '\0';
1911 
1912 		if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) {
1913 			last = realloc(osu, (osu_count + 1) * sizeof(*osu));
1914 			if (last == NULL)
1915 				break;
1916 			osu = last;
1917 			last = &osu[osu_count++];
1918 			memset(last, 0, sizeof(*last));
1919 			snprintf(last->bssid, sizeof(last->bssid), "%s",
1920 				 buf + 13);
1921 			continue;
1922 		}
1923 		if (!last)
1924 			continue;
1925 
1926 		if (strncmp(buf, "uri=", 4) == 0) {
1927 			snprintf(last->url, sizeof(last->url), "%s", buf + 4);
1928 			continue;
1929 		}
1930 
1931 		if (strncmp(buf, "methods=", 8) == 0) {
1932 			last->methods = strtol(buf + 8, NULL, 16);
1933 			continue;
1934 		}
1935 
1936 		if (strncmp(buf, "osu_ssid=", 9) == 0) {
1937 			snprintf(last->osu_ssid, sizeof(last->osu_ssid),
1938 				 "%s", buf + 9);
1939 			continue;
1940 		}
1941 
1942 		if (os_strncmp(buf, "osu_nai=", 8) == 0) {
1943 			os_snprintf(last->osu_nai, sizeof(last->osu_nai),
1944 				    "%s", buf + 8);
1945 			continue;
1946 		}
1947 
1948 		if (strncmp(buf, "friendly_name=", 14) == 0) {
1949 			struct osu_lang_text *txt;
1950 			if (last->friendly_name_count == MAX_OSU_VALS)
1951 				continue;
1952 			pos = strchr(buf + 14, ':');
1953 			if (pos == NULL)
1954 				continue;
1955 			*pos++ = '\0';
1956 			txt = &last->friendly_name[last->friendly_name_count++];
1957 			snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 14);
1958 			snprintf(txt->text, sizeof(txt->text), "%s", pos);
1959 		}
1960 
1961 		if (strncmp(buf, "desc=", 5) == 0) {
1962 			struct osu_lang_text *txt;
1963 			if (last->serv_desc_count == MAX_OSU_VALS)
1964 				continue;
1965 			pos = strchr(buf + 5, ':');
1966 			if (pos == NULL)
1967 				continue;
1968 			*pos++ = '\0';
1969 			txt = &last->serv_desc[last->serv_desc_count++];
1970 			snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 5);
1971 			snprintf(txt->text, sizeof(txt->text), "%s", pos);
1972 		}
1973 
1974 		if (strncmp(buf, "icon=", 5) == 0) {
1975 			struct osu_icon *icon;
1976 			if (last->icon_count == MAX_OSU_VALS)
1977 				continue;
1978 			icon = &last->icon[last->icon_count++];
1979 			icon->id = atoi(buf + 5);
1980 			pos = strchr(buf, ':');
1981 			if (pos == NULL)
1982 				continue;
1983 			pos = strchr(pos + 1, ':');
1984 			if (pos == NULL)
1985 				continue;
1986 			pos = strchr(pos + 1, ':');
1987 			if (pos == NULL)
1988 				continue;
1989 			pos++;
1990 			end = strchr(pos, ':');
1991 			if (!end)
1992 				continue;
1993 			*end = '\0';
1994 			snprintf(icon->lang, sizeof(icon->lang), "%s", pos);
1995 			pos = end + 1;
1996 
1997 			end = strchr(pos, ':');
1998 			if (end)
1999 				*end = '\0';
2000 			snprintf(icon->mime_type, sizeof(icon->mime_type),
2001 				 "%s", pos);
2002 			if (!pos)
2003 				continue;
2004 			pos = end + 1;
2005 
2006 			end = strchr(pos, ':');
2007 			if (end)
2008 				*end = '\0';
2009 			snprintf(icon->filename, sizeof(icon->filename),
2010 				 "%s", pos);
2011 			continue;
2012 		}
2013 	}
2014 
2015 	fclose(f);
2016 
2017 	*count = osu_count;
2018 	return osu;
2019 }
2020 
2021 
2022 static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
2023 		       const char *ssid, const char *url,
2024 		       unsigned int methods, int no_prod_assoc,
2025 		       const char *osu_nai)
2026 {
2027 	int id;
2028 	const char *ifname = ctx->ifname;
2029 	char buf[200];
2030 	struct wpa_ctrl *mon;
2031 	int res;
2032 
2033 	id = add_network(ifname);
2034 	if (id < 0)
2035 		return -1;
2036 	if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
2037 		return -1;
2038 	if (osu_nai && os_strlen(osu_nai) > 0) {
2039 		char dir[255], fname[300];
2040 		if (getcwd(dir, sizeof(dir)) == NULL)
2041 			return -1;
2042 		os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
2043 
2044 		if (set_network(ifname, id, "proto", "OSEN") < 0 ||
2045 		    set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
2046 		    set_network(ifname, id, "pairwise", "CCMP") < 0 ||
2047 		    set_network(ifname, id, "group", "GTK_NOT_USED") < 0 ||
2048 		    set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 ||
2049 		    set_network(ifname, id, "ocsp", "2") < 0 ||
2050 		    set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
2051 		    set_network_quoted(ifname, id, "ca_cert", fname) < 0)
2052 			return -1;
2053 	} else {
2054 		if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
2055 			return -1;
2056 	}
2057 
2058 	mon = open_wpa_mon(ifname);
2059 	if (mon == NULL)
2060 		return -1;
2061 
2062 	wpa_printf(MSG_INFO, "Associate with OSU SSID");
2063 	write_summary(ctx, "Associate with OSU SSID");
2064 	snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id);
2065 	if (wpa_command(ifname, buf) < 0)
2066 		return -1;
2067 
2068 	res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED",
2069 				buf, sizeof(buf));
2070 
2071 	wpa_ctrl_detach(mon);
2072 	wpa_ctrl_close(mon);
2073 
2074 	if (res < 0) {
2075 		wpa_printf(MSG_INFO, "Could not connect");
2076 		write_summary(ctx, "Could not connect to OSU network");
2077 		wpa_printf(MSG_INFO, "Remove OSU network connection");
2078 		snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
2079 		wpa_command(ifname, buf);
2080 		return -1;
2081 	}
2082 
2083 	write_summary(ctx, "Waiting for IP address for subscription registration");
2084 	if (wait_ip_addr(ifname, 15) < 0) {
2085 		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
2086 	}
2087 
2088 	if (no_prod_assoc) {
2089 		if (res < 0)
2090 			return -1;
2091 		wpa_printf(MSG_INFO, "No production connection used for testing purposes");
2092 		write_summary(ctx, "No production connection used for testing purposes");
2093 		return 0;
2094 	}
2095 
2096 	ctx->no_reconnect = 1;
2097 	if (methods & 0x02)
2098 		res = cmd_prov(ctx, url);
2099 	else if (methods & 0x01)
2100 		res = cmd_oma_dm_prov(ctx, url);
2101 
2102 	wpa_printf(MSG_INFO, "Remove OSU network connection");
2103 	write_summary(ctx, "Remove OSU network connection");
2104 	snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
2105 	wpa_command(ifname, buf);
2106 
2107 	if (res < 0)
2108 		return -1;
2109 
2110 	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
2111 	write_summary(ctx, "Requesting reconnection with updated configuration");
2112 	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
2113 		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
2114 		write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
2115 		return -1;
2116 	}
2117 
2118 	return 0;
2119 }
2120 
2121 
2122 static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
2123 			  int connect, int no_prod_assoc,
2124 			  const char *friendly_name)
2125 {
2126 	char fname[255];
2127 	FILE *f;
2128 	struct osu_data *osu = NULL, *last = NULL;
2129 	size_t osu_count, i, j;
2130 	int ret;
2131 
2132 	write_summary(ctx, "OSU provider selection");
2133 
2134 	if (dir == NULL) {
2135 		wpa_printf(MSG_INFO, "Missing dir parameter to osu_select");
2136 		return -1;
2137 	}
2138 
2139 	snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir);
2140 	osu = parse_osu_providers(fname, &osu_count);
2141 	if (osu == NULL) {
2142 		wpa_printf(MSG_INFO, "Could not any OSU providers from %s",
2143 			   fname);
2144 		write_result(ctx, "No OSU providers available");
2145 		return -1;
2146 	}
2147 
2148 	if (friendly_name) {
2149 		for (i = 0; i < osu_count; i++) {
2150 			last = &osu[i];
2151 			for (j = 0; j < last->friendly_name_count; j++) {
2152 				if (os_strcmp(last->friendly_name[j].text,
2153 					      friendly_name) == 0)
2154 					break;
2155 			}
2156 			if (j < last->friendly_name_count)
2157 				break;
2158 		}
2159 		if (i == osu_count) {
2160 			wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers",
2161 				   friendly_name);
2162 			write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers",
2163 				      friendly_name);
2164 			free(osu);
2165 			return -1;
2166 		}
2167 
2168 		wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'",
2169 			   friendly_name);
2170 		write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'",
2171 			      friendly_name);
2172 		ret = i + 1;
2173 		goto selected;
2174 	}
2175 
2176 	snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir);
2177 	f = fopen(fname, "w");
2178 	if (f == NULL) {
2179 		wpa_printf(MSG_INFO, "Could not open %s", fname);
2180 		free(osu);
2181 		return -1;
2182 	}
2183 
2184 	fprintf(f, "<html><head>"
2185 		"<meta http-equiv=\"Content-type\" content=\"text/html; "
2186 		"charset=utf-8\"<title>Select service operator</title>"
2187 		"</head><body><h1>Select service operator</h1>\n");
2188 
2189 	if (osu_count == 0)
2190 		fprintf(f, "No online signup available\n");
2191 
2192 	for (i = 0; i < osu_count; i++) {
2193 		last = &osu[i];
2194 #ifdef ANDROID
2195 		fprintf(f, "<p>\n"
2196 			"<a href=\"http://localhost:12345/osu/%d\">"
2197 			"<table><tr><td>", (int) i + 1);
2198 #else /* ANDROID */
2199 		fprintf(f, "<p>\n"
2200 			"<a href=\"osu://%d\">"
2201 			"<table><tr><td>", (int) i + 1);
2202 #endif /* ANDROID */
2203 		for (j = 0; j < last->icon_count; j++) {
2204 			fprintf(f, "<img src=\"osu-icon-%d.%s\">\n",
2205 				last->icon[j].id,
2206 				strcasecmp(last->icon[j].mime_type,
2207 					   "image/png") == 0 ? "png" : "icon");
2208 		}
2209 		fprintf(f, "<td>");
2210 		for (j = 0; j < last->friendly_name_count; j++) {
2211 			fprintf(f, "<small>[%s]</small> %s<br>\n",
2212 				last->friendly_name[j].lang,
2213 				last->friendly_name[j].text);
2214 		}
2215 		fprintf(f, "<tr><td colspan=2>");
2216 		for (j = 0; j < last->serv_desc_count; j++) {
2217 			fprintf(f, "<small>[%s]</small> %s<br>\n",
2218 				last->serv_desc[j].lang,
2219 				last->serv_desc[j].text);
2220 		}
2221 		fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
2222 			"SSID: %s<br>\n",
2223 			last->bssid, last->osu_ssid);
2224 		if (last->osu_nai)
2225 			fprintf(f, "NAI: %s<br>\n", last->osu_nai);
2226 		fprintf(f, "URL: %s<br>\n"
2227 			"methods:%s%s<br>\n"
2228 			"</small></p>\n",
2229 			last->url,
2230 			last->methods & 0x01 ? " OMA-DM" : "",
2231 			last->methods & 0x02 ? " SOAP-XML-SPP" : "");
2232 	}
2233 
2234 	fprintf(f, "</body></html>\n");
2235 
2236 	fclose(f);
2237 
2238 	snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir);
2239 	write_summary(ctx, "Start web browser with OSU provider selection page");
2240 	ret = hs20_web_browser(fname);
2241 
2242 selected:
2243 	if (ret > 0 && (size_t) ret <= osu_count) {
2244 		char *data;
2245 		size_t data_len;
2246 
2247 		wpa_printf(MSG_INFO, "Selected OSU id=%d", ret);
2248 		last = &osu[ret - 1];
2249 		ret = 0;
2250 		wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
2251 		wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
2252 		wpa_printf(MSG_INFO, "URL: %s", last->url);
2253 		write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
2254 			      ret, last->bssid, last->osu_ssid, last->url);
2255 
2256 		ctx->friendly_name_count = last->friendly_name_count;
2257 		for (j = 0; j < last->friendly_name_count; j++) {
2258 			wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s",
2259 				   last->friendly_name[j].lang,
2260 				   last->friendly_name[j].text);
2261 			os_strlcpy(ctx->friendly_name[j].lang,
2262 				   last->friendly_name[j].lang,
2263 				   sizeof(ctx->friendly_name[j].lang));
2264 			os_strlcpy(ctx->friendly_name[j].text,
2265 				   last->friendly_name[j].text,
2266 				   sizeof(ctx->friendly_name[j].text));
2267 		}
2268 
2269 		ctx->icon_count = last->icon_count;
2270 		for (j = 0; j < last->icon_count; j++) {
2271 			char fname[256];
2272 
2273 			os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s",
2274 				    dir, last->icon[j].id,
2275 				    strcasecmp(last->icon[j].mime_type,
2276 					       "image/png") == 0 ?
2277 				    "png" : "icon");
2278 			wpa_printf(MSG_INFO, "ICON: %s (%s)",
2279 				   fname, last->icon[j].filename);
2280 			os_strlcpy(ctx->icon_filename[j],
2281 				   last->icon[j].filename,
2282 				   sizeof(ctx->icon_filename[j]));
2283 
2284 			data = os_readfile(fname, &data_len);
2285 			if (data) {
2286 				sha256_vector(1, (const u8 **) &data, &data_len,
2287 					      ctx->icon_hash[j]);
2288 				os_free(data);
2289 			}
2290 		}
2291 
2292 		if (connect == 2) {
2293 			if (last->methods & 0x02)
2294 				ret = cmd_prov(ctx, last->url);
2295 			else if (last->methods & 0x01)
2296 				ret = cmd_oma_dm_prov(ctx, last->url);
2297 			else
2298 				ret = -1;
2299 		} else if (connect)
2300 			ret = osu_connect(ctx, last->bssid, last->osu_ssid,
2301 					  last->url, last->methods,
2302 					  no_prod_assoc, last->osu_nai);
2303 	} else
2304 		ret = -1;
2305 
2306 	free(osu);
2307 
2308 	return ret;
2309 }
2310 
2311 
2312 static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc,
2313 		      const char *friendly_name)
2314 {
2315 	char dir[255];
2316 	char fname[300], buf[400];
2317 	struct wpa_ctrl *mon;
2318 	const char *ifname;
2319 	int res;
2320 
2321 	ifname = ctx->ifname;
2322 
2323 	if (getcwd(dir, sizeof(dir)) == NULL)
2324 		return -1;
2325 
2326 	snprintf(fname, sizeof(fname), "%s/osu-info", dir);
2327 	if (mkdir(fname, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) {
2328 		wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
2329 			   fname, strerror(errno));
2330 		return -1;
2331 	}
2332 
2333 	snprintf(buf, sizeof(buf), "SET osu_dir %s", fname);
2334 	if (wpa_command(ifname, buf) < 0) {
2335 		wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant");
2336 		return -1;
2337 	}
2338 
2339 	mon = open_wpa_mon(ifname);
2340 	if (mon == NULL)
2341 		return -1;
2342 
2343 	wpa_printf(MSG_INFO, "Starting OSU fetch");
2344 	write_summary(ctx, "Starting OSU provider information fetch");
2345 	if (wpa_command(ifname, "FETCH_OSU") < 0) {
2346 		wpa_printf(MSG_INFO, "Could not start OSU fetch");
2347 		wpa_ctrl_detach(mon);
2348 		wpa_ctrl_close(mon);
2349 		return -1;
2350 	}
2351 	res = get_wpa_cli_event(mon, "OSU provider fetch completed",
2352 				buf, sizeof(buf));
2353 
2354 	wpa_ctrl_detach(mon);
2355 	wpa_ctrl_close(mon);
2356 
2357 	if (res < 0) {
2358 		wpa_printf(MSG_INFO, "OSU fetch did not complete");
2359 		write_summary(ctx, "OSU fetch did not complete");
2360 		return -1;
2361 	}
2362 	wpa_printf(MSG_INFO, "OSU provider fetch completed");
2363 
2364 	return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name);
2365 }
2366 
2367 
2368 static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
2369 		       const char *pps_fname, const char *ca_fname)
2370 {
2371 	xml_node_t *pps, *node;
2372 	char pps_fname_buf[300];
2373 	char ca_fname_buf[200];
2374 	char *cred_username = NULL;
2375 	char *cred_password = NULL;
2376 	char *sub_rem_uri = NULL;
2377 	char client_cert_buf[200];
2378 	char *client_cert = NULL;
2379 	char client_key_buf[200];
2380 	char *client_key = NULL;
2381 	int spp;
2382 
2383 	wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s",
2384 		   address);
2385 
2386 	if (!pps_fname) {
2387 		char buf[256];
2388 		wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
2389 		if (os_strncmp(address, "fqdn=", 5) == 0) {
2390 			wpa_printf(MSG_INFO, "Use requested FQDN from command line");
2391 			os_snprintf(buf, sizeof(buf), "%s", address + 5);
2392 			address = NULL;
2393 		} else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
2394 					  sizeof(buf)) < 0) {
2395 			wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
2396 			return -1;
2397 		}
2398 		os_free(ctx->fqdn);
2399 		ctx->fqdn = os_strdup(buf);
2400 		if (ctx->fqdn == NULL)
2401 			return -1;
2402 		wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
2403 			   buf);
2404 		os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
2405 			    "SP/%s/pps.xml", ctx->fqdn);
2406 		pps_fname = pps_fname_buf;
2407 
2408 		os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
2409 			    ctx->fqdn);
2410 		ca_fname = ca_fname_buf;
2411 	}
2412 
2413 	if (!os_file_exists(pps_fname)) {
2414 		wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
2415 			   pps_fname);
2416 		return -1;
2417 	}
2418 	wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
2419 
2420 	if (ca_fname && !os_file_exists(ca_fname)) {
2421 		wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
2422 			   ca_fname);
2423 		return -1;
2424 	}
2425 	wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
2426 	ctx->ca_fname = ca_fname;
2427 
2428 	pps = node_from_file(ctx->xml, pps_fname);
2429 	if (pps == NULL) {
2430 		wpa_printf(MSG_INFO, "Could not read PPS MO");
2431 		return -1;
2432 	}
2433 
2434 	if (!ctx->fqdn) {
2435 		char *tmp;
2436 		node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
2437 		if (node == NULL) {
2438 			wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
2439 			return -1;
2440 		}
2441 		tmp = xml_node_get_text(ctx->xml, node);
2442 		if (tmp == NULL) {
2443 			wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
2444 			return -1;
2445 		}
2446 		ctx->fqdn = os_strdup(tmp);
2447 		xml_node_get_text_free(ctx->xml, tmp);
2448 		if (!ctx->fqdn) {
2449 			wpa_printf(MSG_INFO, "No FQDN known");
2450 			return -1;
2451 		}
2452 	}
2453 
2454 	node = get_child_node(ctx->xml, pps,
2455 			      "SubscriptionUpdate/UpdateMethod");
2456 	if (node) {
2457 		char *tmp;
2458 		tmp = xml_node_get_text(ctx->xml, node);
2459 		if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
2460 			spp = 0;
2461 		else
2462 			spp = 1;
2463 	} else {
2464 		wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
2465 		spp = 1;
2466 	}
2467 
2468 	get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword",
2469 		    &cred_username, &cred_password);
2470 	if (cred_username)
2471 		wpa_printf(MSG_INFO, "Using username: %s", cred_username);
2472 	if (cred_password)
2473 		wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
2474 
2475 	if (cred_username == NULL && cred_password == NULL &&
2476 	    get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
2477 		wpa_printf(MSG_INFO, "Using client certificate");
2478 		os_snprintf(client_cert_buf, sizeof(client_cert_buf),
2479 			    "SP/%s/client-cert.pem", ctx->fqdn);
2480 		client_cert = client_cert_buf;
2481 		os_snprintf(client_key_buf, sizeof(client_key_buf),
2482 			    "SP/%s/client-key.pem", ctx->fqdn);
2483 		client_key = client_key_buf;
2484 		ctx->client_cert_present = 1;
2485 	}
2486 
2487 	node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI");
2488 	if (node) {
2489 		sub_rem_uri = xml_node_get_text(ctx->xml, node);
2490 		if (sub_rem_uri &&
2491 		    (!address || os_strcmp(address, sub_rem_uri) != 0)) {
2492 			wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s",
2493 				   sub_rem_uri);
2494 			address = sub_rem_uri;
2495 		}
2496 	}
2497 	if (!address) {
2498 		wpa_printf(MSG_INFO, "Server URL not known");
2499 		return -1;
2500 	}
2501 
2502 	write_summary(ctx, "Wait for IP address for subscriptiom remediation");
2503 	wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation");
2504 
2505 	if (wait_ip_addr(ctx->ifname, 15) < 0) {
2506 		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
2507 	}
2508 
2509 	if (spp)
2510 		spp_sub_rem(ctx, address, pps_fname,
2511 			    client_cert, client_key,
2512 			    cred_username, cred_password, pps);
2513 	else
2514 		oma_dm_sub_rem(ctx, address, pps_fname,
2515 			       client_cert, client_key,
2516 			       cred_username, cred_password, pps);
2517 
2518 	xml_node_get_text_free(ctx->xml, sub_rem_uri);
2519 	xml_node_get_text_free(ctx->xml, cred_username);
2520 	str_clear_free(cred_password);
2521 	xml_node_free(ctx->xml, pps);
2522 	return 0;
2523 }
2524 
2525 
2526 static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address,
2527 		       const char *pps_fname, const char *ca_fname)
2528 {
2529 	xml_node_t *pps;
2530 	xml_node_t *node;
2531 	char pps_fname_buf[300];
2532 	char ca_fname_buf[200];
2533 	char *uri = NULL;
2534 	char *cred_username = NULL;
2535 	char *cred_password = NULL;
2536 	char client_cert_buf[200];
2537 	char *client_cert = NULL;
2538 	char client_key_buf[200];
2539 	char *client_key = NULL;
2540 	int spp;
2541 
2542 	wpa_printf(MSG_INFO, "Policy update requested");
2543 
2544 	if (!pps_fname) {
2545 		char buf[256];
2546 		wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
2547 		if (os_strncmp(address, "fqdn=", 5) == 0) {
2548 			wpa_printf(MSG_INFO, "Use requested FQDN from command line");
2549 			os_snprintf(buf, sizeof(buf), "%s", address + 5);
2550 			address = NULL;
2551 		} else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
2552 					  sizeof(buf)) < 0) {
2553 			wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
2554 			return -1;
2555 		}
2556 		os_free(ctx->fqdn);
2557 		ctx->fqdn = os_strdup(buf);
2558 		if (ctx->fqdn == NULL)
2559 			return -1;
2560 		wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
2561 			   buf);
2562 		os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
2563 			    "SP/%s/pps.xml", ctx->fqdn);
2564 		pps_fname = pps_fname_buf;
2565 
2566 		os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
2567 			    buf);
2568 		ca_fname = ca_fname_buf;
2569 	}
2570 
2571 	if (!os_file_exists(pps_fname)) {
2572 		wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
2573 			   pps_fname);
2574 		return -1;
2575 	}
2576 	wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
2577 
2578 	if (ca_fname && !os_file_exists(ca_fname)) {
2579 		wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
2580 			   ca_fname);
2581 		return -1;
2582 	}
2583 	wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
2584 	ctx->ca_fname = ca_fname;
2585 
2586 	pps = node_from_file(ctx->xml, pps_fname);
2587 	if (pps == NULL) {
2588 		wpa_printf(MSG_INFO, "Could not read PPS MO");
2589 		return -1;
2590 	}
2591 
2592 	if (!ctx->fqdn) {
2593 		char *tmp;
2594 		node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
2595 		if (node == NULL) {
2596 			wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
2597 			return -1;
2598 		}
2599 		tmp = xml_node_get_text(ctx->xml, node);
2600 		if (tmp == NULL) {
2601 			wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
2602 			return -1;
2603 		}
2604 		ctx->fqdn = os_strdup(tmp);
2605 		xml_node_get_text_free(ctx->xml, tmp);
2606 		if (!ctx->fqdn) {
2607 			wpa_printf(MSG_INFO, "No FQDN known");
2608 			return -1;
2609 		}
2610 	}
2611 
2612 	node = get_child_node(ctx->xml, pps,
2613 			      "Policy/PolicyUpdate/UpdateMethod");
2614 	if (node) {
2615 		char *tmp;
2616 		tmp = xml_node_get_text(ctx->xml, node);
2617 		if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
2618 			spp = 0;
2619 		else
2620 			spp = 1;
2621 	} else {
2622 		wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
2623 		spp = 1;
2624 	}
2625 
2626 	get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword",
2627 		    &cred_username, &cred_password);
2628 	if (cred_username)
2629 		wpa_printf(MSG_INFO, "Using username: %s", cred_username);
2630 	if (cred_password)
2631 		wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
2632 
2633 	if (cred_username == NULL && cred_password == NULL &&
2634 	    get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
2635 		wpa_printf(MSG_INFO, "Using client certificate");
2636 		os_snprintf(client_cert_buf, sizeof(client_cert_buf),
2637 			    "SP/%s/client-cert.pem", ctx->fqdn);
2638 		client_cert = client_cert_buf;
2639 		os_snprintf(client_key_buf, sizeof(client_key_buf),
2640 			    "SP/%s/client-key.pem", ctx->fqdn);
2641 		client_key = client_key_buf;
2642 	}
2643 
2644 	if (!address) {
2645 		node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI");
2646 		if (node) {
2647 			uri = xml_node_get_text(ctx->xml, node);
2648 			wpa_printf(MSG_INFO, "URI based on PPS: %s", uri);
2649 			address = uri;
2650 		}
2651 	}
2652 	if (!address) {
2653 		wpa_printf(MSG_INFO, "Server URL not known");
2654 		return -1;
2655 	}
2656 
2657 	if (spp)
2658 		spp_pol_upd(ctx, address, pps_fname,
2659 			    client_cert, client_key,
2660 			    cred_username, cred_password, pps);
2661 	else
2662 		oma_dm_pol_upd(ctx, address, pps_fname,
2663 			       client_cert, client_key,
2664 			       cred_username, cred_password, pps);
2665 
2666 	xml_node_get_text_free(ctx->xml, uri);
2667 	xml_node_get_text_free(ctx->xml, cred_username);
2668 	str_clear_free(cred_password);
2669 	xml_node_free(ctx->xml, pps);
2670 
2671 	return 0;
2672 }
2673 
2674 
2675 static char * get_hostname(const char *url)
2676 {
2677 	const char *pos, *end, *end2;
2678 	char *ret;
2679 
2680 	if (url == NULL)
2681 		return NULL;
2682 
2683 	pos = os_strchr(url, '/');
2684 	if (pos == NULL)
2685 		return NULL;
2686 	pos++;
2687 	if (*pos != '/')
2688 		return NULL;
2689 	pos++;
2690 
2691 	end = os_strchr(pos, '/');
2692 	end2 = os_strchr(pos, ':');
2693 	if (end && end2 && end2 < end)
2694 		end = end2;
2695 	if (end)
2696 		end--;
2697 	else {
2698 		end = pos;
2699 		while (*end)
2700 			end++;
2701 		if (end > pos)
2702 			end--;
2703 	}
2704 
2705 	ret = os_malloc(end - pos + 2);
2706 	if (ret == NULL)
2707 		return NULL;
2708 
2709 	os_memcpy(ret, pos, end - pos + 1);
2710 	ret[end - pos + 1] = '\0';
2711 
2712 	return ret;
2713 }
2714 
2715 
2716 static int osu_cert_cb(void *_ctx, struct http_cert *cert)
2717 {
2718 	struct hs20_osu_client *ctx = _ctx;
2719 	unsigned int i, j;
2720 	int found;
2721 	char *host = NULL;
2722 
2723 	wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d)",
2724 		   !ctx->no_osu_cert_validation);
2725 
2726 	host = get_hostname(ctx->server_url);
2727 
2728 	for (i = 0; i < ctx->server_dnsname_count; i++)
2729 		os_free(ctx->server_dnsname[i]);
2730 	os_free(ctx->server_dnsname);
2731 	ctx->server_dnsname = os_calloc(cert->num_dnsname, sizeof(char *));
2732 	ctx->server_dnsname_count = 0;
2733 
2734 	found = 0;
2735 	for (i = 0; i < cert->num_dnsname; i++) {
2736 		if (ctx->server_dnsname) {
2737 			ctx->server_dnsname[ctx->server_dnsname_count] =
2738 				os_strdup(cert->dnsname[i]);
2739 			if (ctx->server_dnsname[ctx->server_dnsname_count])
2740 				ctx->server_dnsname_count++;
2741 		}
2742 		if (host && os_strcasecmp(host, cert->dnsname[i]) == 0)
2743 			found = 1;
2744 		wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]);
2745 	}
2746 
2747 	if (host && !found) {
2748 		wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection",
2749 			   host);
2750 		write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection",
2751 			     host);
2752 		os_free(host);
2753 		return -1;
2754 	}
2755 
2756 	os_free(host);
2757 
2758 	for (i = 0; i < cert->num_othername; i++) {
2759 		if (os_strcmp(cert->othername[i].oid,
2760 			      "1.3.6.1.4.1.40808.1.1.1") == 0) {
2761 			wpa_hexdump_ascii(MSG_INFO,
2762 					  "id-wfa-hotspot-friendlyName",
2763 					  cert->othername[i].data,
2764 					  cert->othername[i].len);
2765 		}
2766 	}
2767 
2768 	for (j = 0; !ctx->no_osu_cert_validation &&
2769 		     j < ctx->friendly_name_count; j++) {
2770 		int found = 0;
2771 		for (i = 0; i < cert->num_othername; i++) {
2772 			if (os_strcmp(cert->othername[i].oid,
2773 				      "1.3.6.1.4.1.40808.1.1.1") != 0)
2774 				continue;
2775 			if (cert->othername[i].len < 3)
2776 				continue;
2777 			if (os_strncasecmp((char *) cert->othername[i].data,
2778 					   ctx->friendly_name[j].lang, 3) != 0)
2779 				continue;
2780 			if (os_strncmp((char *) cert->othername[i].data + 3,
2781 				       ctx->friendly_name[j].text,
2782 				       cert->othername[i].len - 3) == 0) {
2783 				found = 1;
2784 				break;
2785 			}
2786 		}
2787 
2788 		if (!found) {
2789 			wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'",
2790 				   ctx->friendly_name[j].lang,
2791 				   ctx->friendly_name[j].text);
2792 			write_result(ctx, "No friendly name match found for '[%s]%s'",
2793 				     ctx->friendly_name[j].lang,
2794 				     ctx->friendly_name[j].text);
2795 			return -1;
2796 		}
2797 	}
2798 
2799 	for (i = 0; i < cert->num_logo; i++) {
2800 		struct http_logo *logo = &cert->logo[i];
2801 
2802 		wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'",
2803 			   logo->alg_oid, logo->uri);
2804 		wpa_hexdump_ascii(MSG_INFO, "hashValue",
2805 				  logo->hash, logo->hash_len);
2806 	}
2807 
2808 	for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
2809 		int found = 0;
2810 		char *name = ctx->icon_filename[j];
2811 		size_t name_len = os_strlen(name);
2812 
2813 		wpa_printf(MSG_INFO, "Looking for icon file name '%s' match",
2814 			   name);
2815 		for (i = 0; i < cert->num_logo; i++) {
2816 			struct http_logo *logo = &cert->logo[i];
2817 			size_t uri_len = os_strlen(logo->uri);
2818 			char *pos;
2819 
2820 			wpa_printf(MSG_INFO, "Comparing to '%s' uri_len=%d name_len=%d",
2821 				   logo->uri, (int) uri_len, (int) name_len);
2822 			if (uri_len < 1 + name_len)
2823 				continue;
2824 			pos = &logo->uri[uri_len - name_len - 1];
2825 			if (*pos != '/')
2826 				continue;
2827 			pos++;
2828 			if (os_strcmp(pos, name) == 0) {
2829 				found = 1;
2830 				break;
2831 			}
2832 		}
2833 
2834 		if (!found) {
2835 			wpa_printf(MSG_INFO, "No icon filename match found for '%s'",
2836 				   name);
2837 			write_result(ctx,
2838 				     "No icon filename match found for '%s'",
2839 				     name);
2840 			return -1;
2841 		}
2842 	}
2843 
2844 	for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
2845 		int found = 0;
2846 
2847 		for (i = 0; i < cert->num_logo; i++) {
2848 			struct http_logo *logo = &cert->logo[i];
2849 
2850 			if (logo->hash_len != 32)
2851 				continue;
2852 			if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
2853 				found = 1;
2854 				break;
2855 			}
2856 		}
2857 
2858 		if (!found) {
2859 			wpa_printf(MSG_INFO, "No icon hash match found");
2860 			write_result(ctx, "No icon hash match found");
2861 			return -1;
2862 		}
2863 	}
2864 
2865 	return 0;
2866 }
2867 
2868 
2869 static int init_ctx(struct hs20_osu_client *ctx)
2870 {
2871 	xml_node_t *devinfo, *devid;
2872 
2873 	os_memset(ctx, 0, sizeof(*ctx));
2874 	ctx->ifname = "wlan0";
2875 	ctx->xml = xml_node_init_ctx(ctx, NULL);
2876 	if (ctx->xml == NULL)
2877 		return -1;
2878 
2879 	devinfo = node_from_file(ctx->xml, "devinfo.xml");
2880 	if (!devinfo) {
2881 		wpa_printf(MSG_ERROR, "devinfo.xml not found");
2882 		return -1;
2883 	}
2884 
2885 	devid = get_node(ctx->xml, devinfo, "DevId");
2886 	if (devid) {
2887 		char *tmp = xml_node_get_text(ctx->xml, devid);
2888 		if (tmp) {
2889 			ctx->devid = os_strdup(tmp);
2890 			xml_node_get_text_free(ctx->xml, tmp);
2891 		}
2892 	}
2893 	xml_node_free(ctx->xml, devinfo);
2894 
2895 	if (ctx->devid == NULL) {
2896 		wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml");
2897 		return -1;
2898 	}
2899 
2900 	ctx->http = http_init_ctx(ctx, ctx->xml);
2901 	if (ctx->http == NULL) {
2902 		xml_node_deinit_ctx(ctx->xml);
2903 		return -1;
2904 	}
2905 	http_ocsp_set(ctx->http, 2);
2906 	http_set_cert_cb(ctx->http, osu_cert_cb, ctx);
2907 
2908 	return 0;
2909 }
2910 
2911 
2912 static void deinit_ctx(struct hs20_osu_client *ctx)
2913 {
2914 	size_t i;
2915 
2916 	http_deinit_ctx(ctx->http);
2917 	xml_node_deinit_ctx(ctx->xml);
2918 	os_free(ctx->fqdn);
2919 	os_free(ctx->server_url);
2920 	os_free(ctx->devid);
2921 
2922 	for (i = 0; i < ctx->server_dnsname_count; i++)
2923 		os_free(ctx->server_dnsname[i]);
2924 	os_free(ctx->server_dnsname);
2925 }
2926 
2927 
2928 static void check_workarounds(struct hs20_osu_client *ctx)
2929 {
2930 	FILE *f;
2931 	char buf[100];
2932 	unsigned long int val = 0;
2933 
2934 	f = fopen("hs20-osu-client.workarounds", "r");
2935 	if (f == NULL)
2936 		return;
2937 
2938 	if (fgets(buf, sizeof(buf), f))
2939 		val = strtoul(buf, NULL, 16);
2940 
2941 	fclose(f);
2942 
2943 	if (val) {
2944 		wpa_printf(MSG_INFO, "Workarounds enabled: 0x%lx", val);
2945 		ctx->workarounds = val;
2946 		if (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL)
2947 			http_ocsp_set(ctx->http, 1);
2948 	}
2949 }
2950 
2951 
2952 static void usage(void)
2953 {
2954 	printf("usage: hs20-osu-client [-dddqqKt] [-S<station ifname>] \\\n"
2955 	       "    [-w<wpa_supplicant ctrl_iface dir>] "
2956 	       "[-r<result file>] [-f<debug file>] \\\n"
2957 	       "    [-s<summary file>] \\\n"
2958 	       "    <command> [arguments..]\n"
2959 	       "commands:\n"
2960 	       "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n"
2961 	       "- to_tnds2 <XML MO> <XML MO in TNDS format (Path) "
2962 	       "[URN]>\n"
2963 	       "- from_tnds <XML MO in TNDS format> <XML MO>\n"
2964 	       "- set_pps <PerProviderSubscription XML file name>\n"
2965 	       "- get_fqdn <PerProviderSubscription XML file name>\n"
2966 	       "- pol_upd [Server URL] [PPS] [CA cert]\n"
2967 	       "- sub_rem <Server URL> [PPS] [CA cert]\n"
2968 	       "- prov <Server URL> [CA cert]\n"
2969 	       "- oma_dm_prov <Server URL> [CA cert]\n"
2970 	       "- sim_prov <Server URL> [CA cert]\n"
2971 	       "- oma_dm_sim_prov <Server URL> [CA cert]\n"
2972 	       "- signup [CA cert]\n"
2973 	       "- dl_osu_ca <PPS> <CA file>\n"
2974 	       "- dl_polupd_ca <PPS> <CA file>\n"
2975 	       "- dl_aaa_ca <PPS> <CA file>\n"
2976 	       "- browser <URL>\n"
2977 	       "- parse_cert <X.509 certificate (DER)>\n"
2978 	       "- osu_select <OSU info directory> [CA cert]\n");
2979 }
2980 
2981 
2982 int main(int argc, char *argv[])
2983 {
2984 	struct hs20_osu_client ctx;
2985 	int c;
2986 	int ret = 0;
2987 	int no_prod_assoc = 0;
2988 	const char *friendly_name = NULL;
2989 	const char *wpa_debug_file_path = NULL;
2990 	extern char *wpas_ctrl_path;
2991 	extern int wpa_debug_level;
2992 	extern int wpa_debug_show_keys;
2993 	extern int wpa_debug_timestamp;
2994 
2995 	if (init_ctx(&ctx) < 0)
2996 		return -1;
2997 
2998 	for (;;) {
2999 		c = getopt(argc, argv, "df:hi:KNO:qr:s:S:tw:");
3000 		if (c < 0)
3001 			break;
3002 		switch (c) {
3003 		case 'd':
3004 			if (wpa_debug_level > 0)
3005 				wpa_debug_level--;
3006 			break;
3007 		case 'f':
3008 			wpa_debug_file_path = optarg;
3009 			break;
3010 		case 'K':
3011 			wpa_debug_show_keys++;
3012 			break;
3013 		case 'N':
3014 			no_prod_assoc = 1;
3015 			break;
3016 		case 'O':
3017 			friendly_name = optarg;
3018 			break;
3019 		case 'q':
3020 			wpa_debug_level++;
3021 			break;
3022 		case 'r':
3023 			ctx.result_file = optarg;
3024 			break;
3025 		case 's':
3026 			ctx.summary_file = optarg;
3027 			break;
3028 		case 'S':
3029 			ctx.ifname = optarg;
3030 			break;
3031 		case 't':
3032 			wpa_debug_timestamp++;
3033 			break;
3034 		case 'w':
3035 			wpas_ctrl_path = optarg;
3036 			break;
3037 		case 'h':
3038 		default:
3039 			usage();
3040 			exit(0);
3041 			break;
3042 		}
3043 	}
3044 
3045 	if (argc - optind < 1) {
3046 		usage();
3047 		exit(0);
3048 	}
3049 
3050 	wpa_debug_open_file(wpa_debug_file_path);
3051 
3052 #ifdef __linux__
3053 	setlinebuf(stdout);
3054 #endif /* __linux__ */
3055 
3056 	if (ctx.result_file)
3057 		unlink(ctx.result_file);
3058 	wpa_printf(MSG_DEBUG, "===[hs20-osu-client START - command: %s ]======"
3059 		   "================", argv[optind]);
3060 	check_workarounds(&ctx);
3061 
3062 	if (strcmp(argv[optind], "to_tnds") == 0) {
3063 		if (argc - optind < 2) {
3064 			usage();
3065 			exit(0);
3066 		}
3067 		cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
3068 			    argc > optind + 3 ? argv[optind + 3] : NULL,
3069 			    0);
3070 	} else if (strcmp(argv[optind], "to_tnds2") == 0) {
3071 		if (argc - optind < 2) {
3072 			usage();
3073 			exit(0);
3074 		}
3075 		cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
3076 			    argc > optind + 3 ? argv[optind + 3] : NULL,
3077 			    1);
3078 	} else if (strcmp(argv[optind], "from_tnds") == 0) {
3079 		if (argc - optind < 2) {
3080 			usage();
3081 			exit(0);
3082 		}
3083 		cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]);
3084 	} else if (strcmp(argv[optind], "sub_rem") == 0) {
3085 		if (argc - optind < 2) {
3086 			usage();
3087 			exit(0);
3088 		}
3089 		if (argc - optind < 2)
3090 			wpa_printf(MSG_ERROR, "Server URL missing from command line");
3091 		else
3092 			ret = cmd_sub_rem(&ctx, argv[optind + 1],
3093 					  argc > optind + 2 ?
3094 					  argv[optind + 2] : NULL,
3095 					  argc > optind + 3 ?
3096 					  argv[optind + 3] : NULL);
3097 	} else if (strcmp(argv[optind], "pol_upd") == 0) {
3098 		if (argc - optind < 2) {
3099 			usage();
3100 			exit(0);
3101 		}
3102 		ret = cmd_pol_upd(&ctx, argc > 2 ? argv[optind + 1] : NULL,
3103 				  argc > optind + 2 ? argv[optind + 2] : NULL,
3104 				  argc > optind + 3 ? argv[optind + 3] : NULL);
3105 	} else if (strcmp(argv[optind], "prov") == 0) {
3106 		if (argc - optind < 2) {
3107 			usage();
3108 			exit(0);
3109 		}
3110 		ctx.ca_fname = argv[optind + 2];
3111 		cmd_prov(&ctx, argv[optind + 1]);
3112 	} else if (strcmp(argv[optind], "sim_prov") == 0) {
3113 		if (argc - optind < 2) {
3114 			usage();
3115 			exit(0);
3116 		}
3117 		ctx.ca_fname = argv[optind + 2];
3118 		cmd_sim_prov(&ctx, argv[optind + 1]);
3119 	} else if (strcmp(argv[optind], "dl_osu_ca") == 0) {
3120 		if (argc - optind < 2) {
3121 			usage();
3122 			exit(0);
3123 		}
3124 		cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]);
3125 	} else if (strcmp(argv[optind], "dl_polupd_ca") == 0) {
3126 		if (argc - optind < 2) {
3127 			usage();
3128 			exit(0);
3129 		}
3130 		cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]);
3131 	} else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
3132 		if (argc - optind < 2) {
3133 			usage();
3134 			exit(0);
3135 		}
3136 		cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
3137 	} else if (strcmp(argv[optind], "osu_select") == 0) {
3138 		if (argc - optind < 2) {
3139 			usage();
3140 			exit(0);
3141 		}
3142 		ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL;
3143 		cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL);
3144 	} else if (strcmp(argv[optind], "signup") == 0) {
3145 		ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL;
3146 		ret = cmd_signup(&ctx, no_prod_assoc, friendly_name);
3147 	} else if (strcmp(argv[optind], "set_pps") == 0) {
3148 		if (argc - optind < 2) {
3149 			usage();
3150 			exit(0);
3151 		}
3152 		cmd_set_pps(&ctx, argv[optind + 1]);
3153 	} else if (strcmp(argv[optind], "get_fqdn") == 0) {
3154 		if (argc - optind < 1) {
3155 			usage();
3156 			exit(0);
3157 		}
3158 		ret = cmd_get_fqdn(&ctx, argv[optind + 1]);
3159 	} else if (strcmp(argv[optind], "oma_dm_prov") == 0) {
3160 		if (argc - optind < 2) {
3161 			usage();
3162 			exit(0);
3163 		}
3164 		ctx.ca_fname = argv[optind + 2];
3165 		cmd_oma_dm_prov(&ctx, argv[optind + 1]);
3166 	} else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) {
3167 		if (argc - optind < 2) {
3168 			usage();
3169 			exit(0);
3170 		}
3171 		ctx.ca_fname = argv[optind + 2];
3172 		if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) {
3173 			write_summary(&ctx, "Failed to complete OMA DM SIM provisioning");
3174 			return -1;
3175 		}
3176 	} else if (strcmp(argv[optind], "oma_dm_add") == 0) {
3177 		if (argc - optind < 2) {
3178 			usage();
3179 			exit(0);
3180 		}
3181 		cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]);
3182 	} else if (strcmp(argv[optind], "oma_dm_replace") == 0) {
3183 		if (argc - optind < 2) {
3184 			usage();
3185 			exit(0);
3186 		}
3187 		cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]);
3188 	} else if (strcmp(argv[optind], "est_csr") == 0) {
3189 		if (argc - optind < 2) {
3190 			usage();
3191 			exit(0);
3192 		}
3193 		mkdir("Cert", S_IRWXU);
3194 		est_build_csr(&ctx, argv[optind + 1]);
3195 	} else if (strcmp(argv[optind], "browser") == 0) {
3196 		int ret;
3197 
3198 		if (argc - optind < 2) {
3199 			usage();
3200 			exit(0);
3201 		}
3202 
3203 		wpa_printf(MSG_INFO, "Launch web browser to URL %s",
3204 			   argv[optind + 1]);
3205 		ret = hs20_web_browser(argv[optind + 1]);
3206 		wpa_printf(MSG_INFO, "Web browser result: %d", ret);
3207 	} else if (strcmp(argv[optind], "parse_cert") == 0) {
3208 		if (argc - optind < 2) {
3209 			usage();
3210 			exit(0);
3211 		}
3212 
3213 		wpa_debug_level = MSG_MSGDUMP;
3214 		http_parse_x509_certificate(ctx.http, argv[optind + 1]);
3215 		wpa_debug_level = MSG_INFO;
3216 	} else {
3217 		wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]);
3218 	}
3219 
3220 	deinit_ctx(&ctx);
3221 	wpa_printf(MSG_DEBUG,
3222 		   "===[hs20-osu-client END ]======================");
3223 
3224 	wpa_debug_close_file();
3225 
3226 	return ret;
3227 }
3228