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