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  static void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...);
26  
write_result(struct hs20_osu_client * ctx,const char * fmt,...)27  static void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
28  {
29  	va_list ap;
30  	FILE *f;
31  	char buf[500];
32  
33  	va_start(ap, fmt);
34  	vsnprintf(buf, sizeof(buf), fmt, ap);
35  	va_end(ap);
36  	write_summary(ctx, "%s", buf);
37  
38  	if (!ctx->result_file)
39  		return;
40  
41  	f = fopen(ctx->result_file, "w");
42  	if (f == NULL)
43  		return;
44  
45  	va_start(ap, fmt);
46  	vfprintf(f, fmt, ap);
47  	va_end(ap);
48  	fprintf(f, "\n");
49  	fclose(f);
50  }
51  
52  
write_summary(struct hs20_osu_client * ctx,const char * fmt,...)53  static void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
54  {
55  	va_list ap;
56  	FILE *f;
57  
58  	if (!ctx->summary_file)
59  		return;
60  
61  	f = fopen(ctx->summary_file, "a");
62  	if (f == NULL)
63  		return;
64  
65  	va_start(ap, fmt);
66  	vfprintf(f, fmt, ap);
67  	va_end(ap);
68  	fprintf(f, "\n");
69  	fclose(f);
70  }
71  
72  
73  #define TMP_CERT_DL_FILE "tmp-cert-download"
74  
download_cert(struct hs20_osu_client * ctx,xml_node_t * params,const char * fname)75  static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
76  			 const char *fname)
77  {
78  	xml_node_t *url_node, *hash_node;
79  	char *url, *hash;
80  	char *cert;
81  	size_t len;
82  	u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
83  	int res;
84  	char *b64;
85  	FILE *f;
86  
87  	url_node = get_node(ctx->xml, params, "CertURL");
88  	hash_node = get_node(ctx->xml, params, "CertSHA256Fingerprint");
89  	if (url_node == NULL || hash_node == NULL)
90  		return -1;
91  	url = xml_node_get_text(ctx->xml, url_node);
92  	hash = xml_node_get_text(ctx->xml, hash_node);
93  	if (url == NULL || hash == NULL) {
94  		xml_node_get_text_free(ctx->xml, url);
95  		xml_node_get_text_free(ctx->xml, hash);
96  		return -1;
97  	}
98  
99  	wpa_printf(MSG_INFO, "CertURL: %s", url);
100  	wpa_printf(MSG_INFO, "SHA256 hash: %s", hash);
101  
102  	if (hexstr2bin(hash, digest1, SHA256_MAC_LEN) < 0) {
103  		wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
104  		write_result(ctx, "Invalid SHA256 hash value for downloaded certificate");
105  		xml_node_get_text_free(ctx->xml, hash);
106  		return -1;
107  	}
108  	xml_node_get_text_free(ctx->xml, hash);
109  
110  	write_summary(ctx, "Download certificate from %s", url);
111  	http_ocsp_set(ctx->http, 1);
112  	res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL);
113  	http_ocsp_set(ctx->http,
114  		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
115  	xml_node_get_text_free(ctx->xml, url);
116  	if (res < 0)
117  		return -1;
118  
119  	cert = os_readfile(TMP_CERT_DL_FILE, &len);
120  	remove(TMP_CERT_DL_FILE);
121  	if (cert == NULL)
122  		return -1;
123  
124  	if (sha256_vector(1, (const u8 **) &cert, &len, digest2) < 0) {
125  		os_free(cert);
126  		return -1;
127  	}
128  
129  	if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
130  		wpa_printf(MSG_INFO, "Downloaded certificate fingerprint did not match");
131  		write_result(ctx, "Downloaded certificate fingerprint did not match");
132  		os_free(cert);
133  		return -1;
134  	}
135  
136  	b64 = base64_encode(cert, len, NULL);
137  	os_free(cert);
138  	if (b64 == NULL)
139  		return -1;
140  
141  	f = fopen(fname, "wb");
142  	if (f == NULL) {
143  		os_free(b64);
144  		return -1;
145  	}
146  
147  	fprintf(f, "-----BEGIN CERTIFICATE-----\n"
148  		"%s"
149  		"-----END CERTIFICATE-----\n",
150  		b64);
151  
152  	os_free(b64);
153  	fclose(f);
154  
155  	wpa_printf(MSG_INFO, "Downloaded certificate into %s and validated fingerprint",
156  		   fname);
157  	write_summary(ctx, "Downloaded certificate into %s and validated fingerprint",
158  		      fname);
159  
160  	return 0;
161  }
162  
163  
cmd_dl_aaa_ca(struct hs20_osu_client * ctx,const char * pps_fname,const char * ca_fname)164  static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
165  			 const char *ca_fname)
166  {
167  	xml_node_t *pps, *node, *aaa;
168  	int ret;
169  
170  	pps = node_from_file(ctx->xml, pps_fname);
171  	if (pps == NULL) {
172  		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
173  		return -1;
174  	}
175  
176  	node = get_child_node(ctx->xml, pps,
177  			      "AAAServerTrustRoot");
178  	if (node == NULL) {
179  		wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
180  		xml_node_free(ctx->xml, pps);
181  		return -2;
182  	}
183  
184  	aaa = xml_node_first_child(ctx->xml, node);
185  	if (aaa == NULL) {
186  		wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
187  		xml_node_free(ctx->xml, pps);
188  		return -1;
189  	}
190  
191  	ret = download_cert(ctx, aaa, ca_fname);
192  	xml_node_free(ctx->xml, pps);
193  
194  	return ret;
195  }
196  
197  
198  /* Remove old credentials based on HomeSP/FQDN */
remove_sp_creds(struct hs20_osu_client * ctx,const char * fqdn)199  static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
200  {
201  	char cmd[300];
202  	os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED provisioning_sp=%s", fqdn);
203  	if (wpa_command(ctx->ifname, cmd) < 0)
204  		wpa_printf(MSG_INFO, "Failed to remove old credential(s)");
205  }
206  
207  
set_pps_cred_policy_spe(struct hs20_osu_client * ctx,int id,xml_node_t * spe)208  static void set_pps_cred_policy_spe(struct hs20_osu_client *ctx, int id,
209  				    xml_node_t *spe)
210  {
211  	xml_node_t *ssid;
212  	char *txt;
213  
214  	ssid = get_node(ctx->xml, spe, "SSID");
215  	if (ssid == NULL)
216  		return;
217  	txt = xml_node_get_text(ctx->xml, ssid);
218  	if (txt == NULL)
219  		return;
220  	wpa_printf(MSG_DEBUG, "- Policy/SPExclusionList/<X+>/SSID = %s", txt);
221  	if (set_cred_quoted(ctx->ifname, id, "excluded_ssid", txt) < 0)
222  		wpa_printf(MSG_INFO, "Failed to set cred excluded_ssid");
223  	xml_node_get_text_free(ctx->xml, txt);
224  }
225  
226  
set_pps_cred_policy_spel(struct hs20_osu_client * ctx,int id,xml_node_t * spel)227  static void set_pps_cred_policy_spel(struct hs20_osu_client *ctx, int id,
228  				     xml_node_t *spel)
229  {
230  	xml_node_t *child;
231  
232  	xml_node_for_each_child(ctx->xml, child, spel) {
233  		xml_node_for_each_check(ctx->xml, child);
234  		set_pps_cred_policy_spe(ctx, id, child);
235  	}
236  }
237  
238  
set_pps_cred_policy_prp(struct hs20_osu_client * ctx,int id,xml_node_t * prp)239  static void set_pps_cred_policy_prp(struct hs20_osu_client *ctx, int id,
240  				    xml_node_t *prp)
241  {
242  	xml_node_t *node;
243  	char *txt = NULL, *pos;
244  	char *prio, *country_buf = NULL;
245  	const char *country;
246  	char val[200];
247  	int priority;
248  
249  	node = get_node(ctx->xml, prp, "Priority");
250  	if (node == NULL)
251  		return;
252  	prio = xml_node_get_text(ctx->xml, node);
253  	if (prio == NULL)
254  		return;
255  	wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s",
256  		   prio);
257  	priority = atoi(prio);
258  	xml_node_get_text_free(ctx->xml, prio);
259  
260  	node = get_node(ctx->xml, prp, "Country");
261  	if (node) {
262  		country_buf = xml_node_get_text(ctx->xml, node);
263  		if (country_buf == NULL)
264  			return;
265  		country = country_buf;
266  		wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s",
267  			   country);
268  	} else {
269  		country = "*";
270  	}
271  
272  	node = get_node(ctx->xml, prp, "FQDN_Match");
273  	if (node == NULL)
274  		goto out;
275  	txt = xml_node_get_text(ctx->xml, node);
276  	if (txt == NULL)
277  		goto out;
278  	wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s",
279  		   txt);
280  	pos = strrchr(txt, ',');
281  	if (pos == NULL)
282  		goto out;
283  	*pos++ = '\0';
284  
285  	snprintf(val, sizeof(val), "%s,%d,%d,%s", txt,
286  		 strcmp(pos, "includeSubdomains") != 0, priority, country);
287  	if (set_cred_quoted(ctx->ifname, id, "roaming_partner", val) < 0)
288  		wpa_printf(MSG_INFO, "Failed to set cred roaming_partner");
289  out:
290  	xml_node_get_text_free(ctx->xml, country_buf);
291  	xml_node_get_text_free(ctx->xml, txt);
292  }
293  
294  
set_pps_cred_policy_prpl(struct hs20_osu_client * ctx,int id,xml_node_t * prpl)295  static void set_pps_cred_policy_prpl(struct hs20_osu_client *ctx, int id,
296  				     xml_node_t *prpl)
297  {
298  	xml_node_t *child;
299  
300  	xml_node_for_each_child(ctx->xml, child, prpl) {
301  		xml_node_for_each_check(ctx->xml, child);
302  		set_pps_cred_policy_prp(ctx, id, child);
303  	}
304  }
305  
306  
set_pps_cred_policy_min_backhaul(struct hs20_osu_client * ctx,int id,xml_node_t * min_backhaul)307  static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client *ctx, int id,
308  					     xml_node_t *min_backhaul)
309  {
310  	xml_node_t *node;
311  	char *type, *dl = NULL, *ul = NULL;
312  	int home;
313  
314  	node = get_node(ctx->xml, min_backhaul, "NetworkType");
315  	if (node == NULL) {
316  		wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without mandatory NetworkType node");
317  		return;
318  	}
319  
320  	type = xml_node_get_text(ctx->xml, node);
321  	if (type == NULL)
322  		return;
323  	wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/NetworkType = %s",
324  		   type);
325  	if (os_strcasecmp(type, "home") == 0)
326  		home = 1;
327  	else if (os_strcasecmp(type, "roaming") == 0)
328  		home = 0;
329  	else {
330  		wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold with invalid NetworkType");
331  		xml_node_get_text_free(ctx->xml, type);
332  		return;
333  	}
334  	xml_node_get_text_free(ctx->xml, type);
335  
336  	node = get_node(ctx->xml, min_backhaul, "DLBandwidth");
337  	if (node)
338  		dl = xml_node_get_text(ctx->xml, node);
339  
340  	node = get_node(ctx->xml, min_backhaul, "ULBandwidth");
341  	if (node)
342  		ul = xml_node_get_text(ctx->xml, node);
343  
344  	if (dl == NULL && ul == NULL) {
345  		wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without either DLBandwidth or ULBandwidth nodes");
346  		return;
347  	}
348  
349  	if (dl)
350  		wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/DLBandwidth = %s",
351  			   dl);
352  	if (ul)
353  		wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/ULBandwidth = %s",
354  			   ul);
355  
356  	if (home) {
357  		if (dl &&
358  		    set_cred(ctx->ifname, id, "min_dl_bandwidth_home", dl) < 0)
359  			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
360  		if (ul &&
361  		    set_cred(ctx->ifname, id, "min_ul_bandwidth_home", ul) < 0)
362  			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
363  	} else {
364  		if (dl &&
365  		    set_cred(ctx->ifname, id, "min_dl_bandwidth_roaming", dl) <
366  		    0)
367  			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
368  		if (ul &&
369  		    set_cred(ctx->ifname, id, "min_ul_bandwidth_roaming", ul) <
370  		    0)
371  			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
372  	}
373  
374  	xml_node_get_text_free(ctx->xml, dl);
375  	xml_node_get_text_free(ctx->xml, ul);
376  }
377  
378  
set_pps_cred_policy_min_backhaul_list(struct hs20_osu_client * ctx,int id,xml_node_t * node)379  static void set_pps_cred_policy_min_backhaul_list(struct hs20_osu_client *ctx,
380  						  int id, xml_node_t *node)
381  {
382  	xml_node_t *child;
383  
384  	wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold");
385  
386  	xml_node_for_each_child(ctx->xml, child, node) {
387  		xml_node_for_each_check(ctx->xml, child);
388  		set_pps_cred_policy_min_backhaul(ctx, id, child);
389  	}
390  }
391  
392  
set_pps_cred_policy_update(struct hs20_osu_client * ctx,int id,xml_node_t * node)393  static void set_pps_cred_policy_update(struct hs20_osu_client *ctx, int id,
394  				       xml_node_t *node)
395  {
396  	wpa_printf(MSG_INFO, "- Policy/PolicyUpdate");
397  	/* Not used in wpa_supplicant */
398  }
399  
400  
set_pps_cred_policy_required_proto_port(struct hs20_osu_client * ctx,int id,xml_node_t * tuple)401  static void set_pps_cred_policy_required_proto_port(struct hs20_osu_client *ctx,
402  						    int id, xml_node_t *tuple)
403  {
404  	xml_node_t *node;
405  	char *proto, *port;
406  	char *buf;
407  	size_t buflen;
408  
409  	node = get_node(ctx->xml, tuple, "IPProtocol");
410  	if (node == NULL) {
411  		wpa_printf(MSG_INFO, "Ignore RequiredProtoPortTuple without mandatory IPProtocol node");
412  		return;
413  	}
414  
415  	proto = xml_node_get_text(ctx->xml, node);
416  	if (proto == NULL)
417  		return;
418  
419  	wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/IPProtocol = %s",
420  		   proto);
421  
422  	node = get_node(ctx->xml, tuple, "PortNumber");
423  	port = node ? xml_node_get_text(ctx->xml, node) : NULL;
424  	if (port) {
425  		wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/PortNumber = %s",
426  			   port);
427  		buflen = os_strlen(proto) + os_strlen(port) + 10;
428  		buf = os_malloc(buflen);
429  		if (buf)
430  			os_snprintf(buf, buflen, "%s:%s", proto, port);
431  		xml_node_get_text_free(ctx->xml, port);
432  	} else {
433  		buflen = os_strlen(proto) + 10;
434  		buf = os_malloc(buflen);
435  		if (buf)
436  			os_snprintf(buf, buflen, "%s", proto);
437  	}
438  
439  	xml_node_get_text_free(ctx->xml, proto);
440  
441  	if (buf == NULL)
442  		return;
443  
444  	if (set_cred(ctx->ifname, id, "req_conn_capab", buf) < 0)
445  		wpa_printf(MSG_INFO, "Could not set req_conn_capab");
446  
447  	os_free(buf);
448  }
449  
450  
set_pps_cred_policy_required_proto_ports(struct hs20_osu_client * ctx,int id,xml_node_t * node)451  static void set_pps_cred_policy_required_proto_ports(struct hs20_osu_client *ctx,
452  						     int id, xml_node_t *node)
453  {
454  	xml_node_t *child;
455  
456  	wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple");
457  
458  	xml_node_for_each_child(ctx->xml, child, node) {
459  		xml_node_for_each_check(ctx->xml, child);
460  		set_pps_cred_policy_required_proto_port(ctx, id, child);
461  	}
462  }
463  
464  
set_pps_cred_policy_max_bss_load(struct hs20_osu_client * ctx,int id,xml_node_t * node)465  static void set_pps_cred_policy_max_bss_load(struct hs20_osu_client *ctx, int id,
466  					     xml_node_t *node)
467  {
468  	char *str = xml_node_get_text(ctx->xml, node);
469  	if (str == NULL)
470  		return;
471  	wpa_printf(MSG_INFO, "- Policy/MaximumBSSLoadValue - %s", str);
472  	if (set_cred(ctx->ifname, id, "max_bss_load", str) < 0)
473  		wpa_printf(MSG_INFO, "Failed to set cred max_bss_load limit");
474  	xml_node_get_text_free(ctx->xml, str);
475  }
476  
477  
set_pps_cred_policy(struct hs20_osu_client * ctx,int id,xml_node_t * node)478  static void set_pps_cred_policy(struct hs20_osu_client *ctx, int id,
479  				xml_node_t *node)
480  {
481  	xml_node_t *child;
482  	const char *name;
483  
484  	wpa_printf(MSG_INFO, "- Policy");
485  
486  	xml_node_for_each_child(ctx->xml, child, node) {
487  		xml_node_for_each_check(ctx->xml, child);
488  		name = xml_node_get_localname(ctx->xml, child);
489  		if (os_strcasecmp(name, "PreferredRoamingPartnerList") == 0)
490  			set_pps_cred_policy_prpl(ctx, id, child);
491  		else if (os_strcasecmp(name, "MinBackhaulThreshold") == 0)
492  			set_pps_cred_policy_min_backhaul_list(ctx, id, child);
493  		else if (os_strcasecmp(name, "PolicyUpdate") == 0)
494  			set_pps_cred_policy_update(ctx, id, child);
495  		else if (os_strcasecmp(name, "SPExclusionList") == 0)
496  			set_pps_cred_policy_spel(ctx, id, child);
497  		else if (os_strcasecmp(name, "RequiredProtoPortTuple") == 0)
498  			set_pps_cred_policy_required_proto_ports(ctx, id, child);
499  		else if (os_strcasecmp(name, "MaximumBSSLoadValue") == 0)
500  			set_pps_cred_policy_max_bss_load(ctx, id, child);
501  		else
502  			wpa_printf(MSG_INFO, "Unknown Policy node '%s'", name);
503  	}
504  }
505  
506  
set_pps_cred_priority(struct hs20_osu_client * ctx,int id,xml_node_t * node)507  static void set_pps_cred_priority(struct hs20_osu_client *ctx, int id,
508  				  xml_node_t *node)
509  {
510  	char *str = xml_node_get_text(ctx->xml, node);
511  	if (str == NULL)
512  		return;
513  	wpa_printf(MSG_INFO, "- CredentialPriority = %s", str);
514  	if (set_cred(ctx->ifname, id, "sp_priority", str) < 0)
515  		wpa_printf(MSG_INFO, "Failed to set cred sp_priority");
516  	xml_node_get_text_free(ctx->xml, str);
517  }
518  
519  
set_pps_cred_aaa_server_trust_root(struct hs20_osu_client * ctx,int id,xml_node_t * node)520  static void set_pps_cred_aaa_server_trust_root(struct hs20_osu_client *ctx,
521  					       int id, xml_node_t *node)
522  {
523  	wpa_printf(MSG_INFO, "- AAAServerTrustRoot - TODO");
524  }
525  
526  
set_pps_cred_sub_update(struct hs20_osu_client * ctx,int id,xml_node_t * node)527  static void set_pps_cred_sub_update(struct hs20_osu_client *ctx, int id,
528  				    xml_node_t *node)
529  {
530  	wpa_printf(MSG_INFO, "- SubscriptionUpdate");
531  	/* not used within wpa_supplicant */
532  }
533  
534  
set_pps_cred_home_sp_network_id(struct hs20_osu_client * ctx,int id,xml_node_t * node)535  static void set_pps_cred_home_sp_network_id(struct hs20_osu_client *ctx,
536  					    int id, xml_node_t *node)
537  {
538  	xml_node_t *ssid_node, *hessid_node;
539  	char *ssid, *hessid;
540  
541  	ssid_node = get_node(ctx->xml, node, "SSID");
542  	if (ssid_node == NULL) {
543  		wpa_printf(MSG_INFO, "Ignore HomeSP/NetworkID without mandatory SSID node");
544  		return;
545  	}
546  
547  	hessid_node = get_node(ctx->xml, node, "HESSID");
548  
549  	ssid = xml_node_get_text(ctx->xml, ssid_node);
550  	if (ssid == NULL)
551  		return;
552  	hessid = hessid_node ? xml_node_get_text(ctx->xml, hessid_node) : NULL;
553  
554  	wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/SSID = %s", ssid);
555  	if (hessid)
556  		wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/HESSID = %s",
557  			   hessid);
558  
559  	/* TODO: Configure to wpa_supplicant */
560  
561  	xml_node_get_text_free(ctx->xml, ssid);
562  	xml_node_get_text_free(ctx->xml, hessid);
563  }
564  
565  
set_pps_cred_home_sp_network_ids(struct hs20_osu_client * ctx,int id,xml_node_t * node)566  static void set_pps_cred_home_sp_network_ids(struct hs20_osu_client *ctx,
567  					     int id, xml_node_t *node)
568  {
569  	xml_node_t *child;
570  
571  	wpa_printf(MSG_INFO, "- HomeSP/NetworkID");
572  
573  	xml_node_for_each_child(ctx->xml, child, node) {
574  		xml_node_for_each_check(ctx->xml, child);
575  		set_pps_cred_home_sp_network_id(ctx, id, child);
576  	}
577  }
578  
579  
set_pps_cred_home_sp_friendly_name(struct hs20_osu_client * ctx,int id,xml_node_t * node)580  static void set_pps_cred_home_sp_friendly_name(struct hs20_osu_client *ctx,
581  					       int id, xml_node_t *node)
582  {
583  	char *str = xml_node_get_text(ctx->xml, node);
584  	if (str == NULL)
585  		return;
586  	wpa_printf(MSG_INFO, "- HomeSP/FriendlyName = %s", str);
587  	/* not used within wpa_supplicant(?) */
588  	xml_node_get_text_free(ctx->xml, str);
589  }
590  
591  
set_pps_cred_home_sp_icon_url(struct hs20_osu_client * ctx,int id,xml_node_t * node)592  static void set_pps_cred_home_sp_icon_url(struct hs20_osu_client *ctx,
593  					  int id, xml_node_t *node)
594  {
595  	char *str = xml_node_get_text(ctx->xml, node);
596  	if (str == NULL)
597  		return;
598  	wpa_printf(MSG_INFO, "- HomeSP/IconURL = %s", str);
599  	/* not used within wpa_supplicant */
600  	xml_node_get_text_free(ctx->xml, str);
601  }
602  
603  
set_pps_cred_home_sp_fqdn(struct hs20_osu_client * ctx,int id,xml_node_t * node)604  static void set_pps_cred_home_sp_fqdn(struct hs20_osu_client *ctx, int id,
605  				      xml_node_t *node)
606  {
607  	char *str = xml_node_get_text(ctx->xml, node);
608  	if (str == NULL)
609  		return;
610  	wpa_printf(MSG_INFO, "- HomeSP/FQDN = %s", str);
611  	if (set_cred_quoted(ctx->ifname, id, "domain", str) < 0)
612  		wpa_printf(MSG_INFO, "Failed to set cred domain");
613  	if (set_cred_quoted(ctx->ifname, id, "domain_suffix_match", str) < 0)
614  		wpa_printf(MSG_INFO, "Failed to set cred domain_suffix_match");
615  	xml_node_get_text_free(ctx->xml, str);
616  }
617  
618  
set_pps_cred_home_sp_oi(struct hs20_osu_client * ctx,int id,xml_node_t * node)619  static void set_pps_cred_home_sp_oi(struct hs20_osu_client *ctx, int id,
620  				    xml_node_t *node)
621  {
622  	xml_node_t *child;
623  	const char *name;
624  	char *homeoi = NULL;
625  	int required = 0;
626  	char *str;
627  
628  	xml_node_for_each_child(ctx->xml, child, node) {
629  		xml_node_for_each_check(ctx->xml, child);
630  		name = xml_node_get_localname(ctx->xml, child);
631  		if (strcasecmp(name, "HomeOI") == 0 && !homeoi) {
632  			homeoi = xml_node_get_text(ctx->xml, child);
633  			wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOI = %s",
634  				   homeoi);
635  		} else if (strcasecmp(name, "HomeOIRequired") == 0) {
636  			str = xml_node_get_text(ctx->xml, child);
637  			wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOIRequired = '%s'",
638  				   str);
639  			if (str == NULL)
640  				continue;
641  			required = strcasecmp(str, "true") == 0;
642  			xml_node_get_text_free(ctx->xml, str);
643  		} else
644  			wpa_printf(MSG_INFO, "Unknown HomeOIList node '%s'",
645  				   name);
646  	}
647  
648  	if (homeoi == NULL) {
649  		wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> without HomeOI ignored");
650  		return;
651  	}
652  
653  	wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> '%s' required=%d",
654  		   homeoi, required);
655  
656  	if (required) {
657  		if (set_cred_quoted(ctx->ifname, id, "required_home_ois",
658  				    homeoi) < 0)
659  			wpa_printf(MSG_INFO,
660  				   "Failed to set cred required_home_ois");
661  	} else {
662  		if (set_cred_quoted(ctx->ifname, id, "home_ois", homeoi) < 0)
663  			wpa_printf(MSG_INFO, "Failed to set cred home_ois");
664  	}
665  
666  	xml_node_get_text_free(ctx->xml, homeoi);
667  }
668  
669  
set_pps_cred_home_sp_oi_list(struct hs20_osu_client * ctx,int id,xml_node_t * node)670  static void set_pps_cred_home_sp_oi_list(struct hs20_osu_client *ctx, int id,
671  					 xml_node_t *node)
672  {
673  	xml_node_t *child;
674  
675  	wpa_printf(MSG_INFO, "- HomeSP/HomeOIList");
676  
677  	xml_node_for_each_child(ctx->xml, child, node) {
678  		xml_node_for_each_check(ctx->xml, child);
679  		set_pps_cred_home_sp_oi(ctx, id, child);
680  	}
681  }
682  
683  
set_pps_cred_home_sp_other_partner(struct hs20_osu_client * ctx,int id,xml_node_t * node)684  static void set_pps_cred_home_sp_other_partner(struct hs20_osu_client *ctx,
685  					       int id, xml_node_t *node)
686  {
687  	xml_node_t *child;
688  	const char *name;
689  	char *fqdn = NULL;
690  
691  	xml_node_for_each_child(ctx->xml, child, node) {
692  		xml_node_for_each_check(ctx->xml, child);
693  		name = xml_node_get_localname(ctx->xml, child);
694  		if (os_strcasecmp(name, "FQDN") == 0 && !fqdn) {
695  			fqdn = xml_node_get_text(ctx->xml, child);
696  			wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+>/FQDN = %s",
697  				   fqdn);
698  		} else
699  			wpa_printf(MSG_INFO, "Unknown OtherHomePartners node '%s'",
700  				   name);
701  	}
702  
703  	if (fqdn == NULL) {
704  		wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+> without FQDN ignored");
705  		return;
706  	}
707  
708  	if (set_cred_quoted(ctx->ifname, id, "domain", fqdn) < 0)
709  		wpa_printf(MSG_INFO, "Failed to set cred domain for OtherHomePartners node");
710  
711  	xml_node_get_text_free(ctx->xml, fqdn);
712  }
713  
714  
set_pps_cred_home_sp_other_partners(struct hs20_osu_client * ctx,int id,xml_node_t * node)715  static void set_pps_cred_home_sp_other_partners(struct hs20_osu_client *ctx,
716  						int id,
717  						xml_node_t *node)
718  {
719  	xml_node_t *child;
720  
721  	wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners");
722  
723  	xml_node_for_each_child(ctx->xml, child, node) {
724  		xml_node_for_each_check(ctx->xml, child);
725  		set_pps_cred_home_sp_other_partner(ctx, id, child);
726  	}
727  }
728  
729  
set_pps_cred_home_sp_roaming_consortium_oi(struct hs20_osu_client * ctx,int id,xml_node_t * node)730  static void set_pps_cred_home_sp_roaming_consortium_oi(
731  	struct hs20_osu_client *ctx, int id, xml_node_t *node)
732  {
733  	char *str = xml_node_get_text(ctx->xml, node);
734  	if (str == NULL)
735  		return;
736  	wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str);
737  	if (set_cred_quoted(ctx->ifname, id, "roaming_consortiums",
738  			    str) < 0)
739  		wpa_printf(MSG_INFO, "Failed to set cred roaming_consortiums");
740  	xml_node_get_text_free(ctx->xml, str);
741  }
742  
743  
set_pps_cred_home_sp(struct hs20_osu_client * ctx,int id,xml_node_t * node)744  static void set_pps_cred_home_sp(struct hs20_osu_client *ctx, int id,
745  				 xml_node_t *node)
746  {
747  	xml_node_t *child;
748  	const char *name;
749  
750  	wpa_printf(MSG_INFO, "- HomeSP");
751  
752  	xml_node_for_each_child(ctx->xml, child, node) {
753  		xml_node_for_each_check(ctx->xml, child);
754  		name = xml_node_get_localname(ctx->xml, child);
755  		if (os_strcasecmp(name, "NetworkID") == 0)
756  			set_pps_cred_home_sp_network_ids(ctx, id, child);
757  		else if (os_strcasecmp(name, "FriendlyName") == 0)
758  			set_pps_cred_home_sp_friendly_name(ctx, id, child);
759  		else if (os_strcasecmp(name, "IconURL") == 0)
760  			set_pps_cred_home_sp_icon_url(ctx, id, child);
761  		else if (os_strcasecmp(name, "FQDN") == 0)
762  			set_pps_cred_home_sp_fqdn(ctx, id, child);
763  		else if (os_strcasecmp(name, "HomeOIList") == 0)
764  			set_pps_cred_home_sp_oi_list(ctx, id, child);
765  		else if (os_strcasecmp(name, "OtherHomePartners") == 0)
766  			set_pps_cred_home_sp_other_partners(ctx, id, child);
767  		else if (os_strcasecmp(name, "RoamingConsortiumOI") == 0)
768  			set_pps_cred_home_sp_roaming_consortium_oi(ctx, id,
769  								   child);
770  		else
771  			wpa_printf(MSG_INFO, "Unknown HomeSP node '%s'", name);
772  	}
773  }
774  
775  
set_pps_cred_sub_params(struct hs20_osu_client * ctx,int id,xml_node_t * node)776  static void set_pps_cred_sub_params(struct hs20_osu_client *ctx, int id,
777  				    xml_node_t *node)
778  {
779  	wpa_printf(MSG_INFO, "- SubscriptionParameters");
780  	/* not used within wpa_supplicant */
781  }
782  
783  
set_pps_cred_creation_date(struct hs20_osu_client * ctx,int id,xml_node_t * node)784  static void set_pps_cred_creation_date(struct hs20_osu_client *ctx, int id,
785  				       xml_node_t *node)
786  {
787  	char *str = xml_node_get_text(ctx->xml, node);
788  	if (str == NULL)
789  		return;
790  	wpa_printf(MSG_INFO, "- Credential/CreationDate = %s", str);
791  	/* not used within wpa_supplicant */
792  	xml_node_get_text_free(ctx->xml, str);
793  }
794  
795  
set_pps_cred_expiration_date(struct hs20_osu_client * ctx,int id,xml_node_t * node)796  static void set_pps_cred_expiration_date(struct hs20_osu_client *ctx, int id,
797  					 xml_node_t *node)
798  {
799  	char *str = xml_node_get_text(ctx->xml, node);
800  	if (str == NULL)
801  		return;
802  	wpa_printf(MSG_INFO, "- Credential/ExpirationDate = %s", str);
803  	/* not used within wpa_supplicant */
804  	xml_node_get_text_free(ctx->xml, str);
805  }
806  
807  
set_pps_cred_username(struct hs20_osu_client * ctx,int id,xml_node_t * node)808  static void set_pps_cred_username(struct hs20_osu_client *ctx, int id,
809  				  xml_node_t *node)
810  {
811  	char *str = xml_node_get_text(ctx->xml, node);
812  	if (str == NULL)
813  		return;
814  	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Username = %s",
815  		   str);
816  	if (set_cred_quoted(ctx->ifname, id, "username", str) < 0)
817  		wpa_printf(MSG_INFO, "Failed to set cred username");
818  	xml_node_get_text_free(ctx->xml, str);
819  }
820  
821  
set_pps_cred_password(struct hs20_osu_client * ctx,int id,xml_node_t * node)822  static void set_pps_cred_password(struct hs20_osu_client *ctx, int id,
823  				  xml_node_t *node)
824  {
825  	int len, i;
826  	char *pw, *hex, *pos, *end;
827  
828  	pw = xml_node_get_base64_text(ctx->xml, node, &len);
829  	if (pw == NULL)
830  		return;
831  
832  	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Password = %s", pw);
833  
834  	hex = malloc(len * 2 + 1);
835  	if (hex == NULL) {
836  		free(pw);
837  		return;
838  	}
839  	end = hex + len * 2 + 1;
840  	pos = hex;
841  	for (i = 0; i < len; i++) {
842  		snprintf(pos, end - pos, "%02x", pw[i]);
843  		pos += 2;
844  	}
845  	free(pw);
846  
847  	if (set_cred(ctx->ifname, id, "password", hex) < 0)
848  		wpa_printf(MSG_INFO, "Failed to set cred password");
849  	free(hex);
850  }
851  
852  
set_pps_cred_machine_managed(struct hs20_osu_client * ctx,int id,xml_node_t * node)853  static void set_pps_cred_machine_managed(struct hs20_osu_client *ctx, int id,
854  					 xml_node_t *node)
855  {
856  	char *str = xml_node_get_text(ctx->xml, node);
857  	if (str == NULL)
858  		return;
859  	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/MachineManaged = %s",
860  		   str);
861  	/* not used within wpa_supplicant */
862  	xml_node_get_text_free(ctx->xml, str);
863  }
864  
865  
set_pps_cred_soft_token_app(struct hs20_osu_client * ctx,int id,xml_node_t * node)866  static void set_pps_cred_soft_token_app(struct hs20_osu_client *ctx, int id,
867  					xml_node_t *node)
868  {
869  	char *str = xml_node_get_text(ctx->xml, node);
870  	if (str == NULL)
871  		return;
872  	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/SoftTokenApp = %s",
873  		   str);
874  	/* not used within wpa_supplicant */
875  	xml_node_get_text_free(ctx->xml, str);
876  }
877  
878  
set_pps_cred_able_to_share(struct hs20_osu_client * ctx,int id,xml_node_t * node)879  static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id,
880  				       xml_node_t *node)
881  {
882  	char *str = xml_node_get_text(ctx->xml, node);
883  	if (str == NULL)
884  		return;
885  	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/AbleToShare = %s",
886  		   str);
887  	/* not used within wpa_supplicant */
888  	xml_node_get_text_free(ctx->xml, str);
889  }
890  
891  
set_pps_cred_eap_method_eap_type(struct hs20_osu_client * ctx,int id,xml_node_t * node)892  static void set_pps_cred_eap_method_eap_type(struct hs20_osu_client *ctx,
893  					     int id, xml_node_t *node)
894  {
895  	char *str = xml_node_get_text(ctx->xml, node);
896  	int type;
897  	const char *eap_method = NULL;
898  
899  	if (!str)
900  		return;
901  	wpa_printf(MSG_INFO,
902  		   "- Credential/UsernamePassword/EAPMethod/EAPType = %s", str);
903  	type = atoi(str);
904  	switch (type) {
905  	case EAP_TYPE_TLS:
906  		eap_method = "TLS";
907  		break;
908  	case EAP_TYPE_TTLS:
909  		eap_method = "TTLS";
910  		break;
911  	case EAP_TYPE_PEAP:
912  		eap_method = "PEAP";
913  		break;
914  	case EAP_TYPE_PWD:
915  		eap_method = "PWD";
916  		break;
917  	}
918  	xml_node_get_text_free(ctx->xml, str);
919  	if (!eap_method) {
920  		wpa_printf(MSG_INFO, "Unknown EAPType value");
921  		return;
922  	}
923  
924  	if (set_cred(ctx->ifname, id, "eap", eap_method) < 0)
925  		wpa_printf(MSG_INFO, "Failed to set cred eap");
926  }
927  
928  
set_pps_cred_eap_method_inner_method(struct hs20_osu_client * ctx,int id,xml_node_t * node)929  static void set_pps_cred_eap_method_inner_method(struct hs20_osu_client *ctx,
930  						 int id, xml_node_t *node)
931  {
932  	char *str = xml_node_get_text(ctx->xml, node);
933  	const char *phase2 = NULL;
934  
935  	if (!str)
936  		return;
937  	wpa_printf(MSG_INFO,
938  		   "- Credential/UsernamePassword/EAPMethod/InnerMethod = %s",
939  		   str);
940  	if (os_strcmp(str, "PAP") == 0)
941  		phase2 = "auth=PAP";
942  	else if (os_strcmp(str, "CHAP") == 0)
943  		phase2 = "auth=CHAP";
944  	else if (os_strcmp(str, "MS-CHAP") == 0)
945  		phase2 = "auth=MSCHAP";
946  	else if (os_strcmp(str, "MS-CHAP-V2") == 0)
947  		phase2 = "auth=MSCHAPV2";
948  	xml_node_get_text_free(ctx->xml, str);
949  	if (!phase2) {
950  		wpa_printf(MSG_INFO, "Unknown InnerMethod value");
951  		return;
952  	}
953  
954  	if (set_cred_quoted(ctx->ifname, id, "phase2", phase2) < 0)
955  		wpa_printf(MSG_INFO, "Failed to set cred phase2");
956  }
957  
958  
set_pps_cred_eap_method(struct hs20_osu_client * ctx,int id,xml_node_t * node)959  static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id,
960  				    xml_node_t *node)
961  {
962  	xml_node_t *child;
963  	const char *name;
964  
965  	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod");
966  
967  	xml_node_for_each_child(ctx->xml, child, node) {
968  		xml_node_for_each_check(ctx->xml, child);
969  		name = xml_node_get_localname(ctx->xml, child);
970  		if (os_strcasecmp(name, "EAPType") == 0)
971  			set_pps_cred_eap_method_eap_type(ctx, id, child);
972  		else if (os_strcasecmp(name, "InnerMethod") == 0)
973  			set_pps_cred_eap_method_inner_method(ctx, id, child);
974  		else
975  			wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword/EAPMethod node '%s'",
976  				   name);
977  	}
978  }
979  
980  
set_pps_cred_username_password(struct hs20_osu_client * ctx,int id,xml_node_t * node)981  static void set_pps_cred_username_password(struct hs20_osu_client *ctx, int id,
982  					   xml_node_t *node)
983  {
984  	xml_node_t *child;
985  	const char *name;
986  
987  	wpa_printf(MSG_INFO, "- Credential/UsernamePassword");
988  
989  	xml_node_for_each_child(ctx->xml, child, node) {
990  		xml_node_for_each_check(ctx->xml, child);
991  		name = xml_node_get_localname(ctx->xml, child);
992  		if (os_strcasecmp(name, "Username") == 0)
993  			set_pps_cred_username(ctx, id, child);
994  		else if (os_strcasecmp(name, "Password") == 0)
995  			set_pps_cred_password(ctx, id, child);
996  		else if (os_strcasecmp(name, "MachineManaged") == 0)
997  			set_pps_cred_machine_managed(ctx, id, child);
998  		else if (os_strcasecmp(name, "SoftTokenApp") == 0)
999  			set_pps_cred_soft_token_app(ctx, id, child);
1000  		else if (os_strcasecmp(name, "AbleToShare") == 0)
1001  			set_pps_cred_able_to_share(ctx, id, child);
1002  		else if (os_strcasecmp(name, "EAPMethod") == 0)
1003  			set_pps_cred_eap_method(ctx, id, child);
1004  		else
1005  			wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword node '%s'",
1006  				   name);
1007  	}
1008  }
1009  
1010  
set_pps_cred_digital_cert(struct hs20_osu_client * ctx,int id,xml_node_t * node,const char * fqdn)1011  static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id,
1012  				      xml_node_t *node, const char *fqdn)
1013  {
1014  	char buf[200], dir[200];
1015  	int res;
1016  
1017  	wpa_printf(MSG_INFO, "- Credential/DigitalCertificate");
1018  
1019  	if (getcwd(dir, sizeof(dir)) == NULL)
1020  		return;
1021  
1022  	/* TODO: could build username from Subject of Subject AltName */
1023  	if (set_cred_quoted(ctx->ifname, id, "username", "cert") < 0) {
1024  		wpa_printf(MSG_INFO, "Failed to set username");
1025  	}
1026  
1027  	res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir,
1028  			  fqdn);
1029  	if (os_snprintf_error(sizeof(buf), res))
1030  		return;
1031  	if (os_file_exists(buf)) {
1032  		if (set_cred_quoted(ctx->ifname, id, "client_cert", buf) < 0) {
1033  			wpa_printf(MSG_INFO, "Failed to set client_cert");
1034  		}
1035  	}
1036  
1037  	res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir,
1038  			  fqdn);
1039  	if (os_snprintf_error(sizeof(buf), res))
1040  		return;
1041  	if (os_file_exists(buf)) {
1042  		if (set_cred_quoted(ctx->ifname, id, "private_key", buf) < 0) {
1043  			wpa_printf(MSG_INFO, "Failed to set private_key");
1044  		}
1045  	}
1046  }
1047  
1048  
set_pps_cred_realm(struct hs20_osu_client * ctx,int id,xml_node_t * node,const char * fqdn,int sim)1049  static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id,
1050  			       xml_node_t *node, const char *fqdn, int sim)
1051  {
1052  	char *str = xml_node_get_text(ctx->xml, node);
1053  	char buf[200], dir[200];
1054  	int res;
1055  
1056  	if (str == NULL)
1057  		return;
1058  
1059  	wpa_printf(MSG_INFO, "- Credential/Realm = %s", str);
1060  	if (set_cred_quoted(ctx->ifname, id, "realm", str) < 0)
1061  		wpa_printf(MSG_INFO, "Failed to set cred realm");
1062  	xml_node_get_text_free(ctx->xml, str);
1063  
1064  	if (sim)
1065  		return;
1066  
1067  	if (getcwd(dir, sizeof(dir)) == NULL)
1068  		return;
1069  	res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn);
1070  	if (os_snprintf_error(sizeof(buf), res))
1071  		return;
1072  	if (os_file_exists(buf)) {
1073  		if (set_cred_quoted(ctx->ifname, id, "ca_cert", buf) < 0) {
1074  			wpa_printf(MSG_INFO, "Failed to set CA cert");
1075  		}
1076  	}
1077  }
1078  
1079  
set_pps_cred_check_aaa_cert_status(struct hs20_osu_client * ctx,int id,xml_node_t * node)1080  static void set_pps_cred_check_aaa_cert_status(struct hs20_osu_client *ctx,
1081  					       int id, xml_node_t *node)
1082  {
1083  	char *str = xml_node_get_text(ctx->xml, node);
1084  
1085  	if (str == NULL)
1086  		return;
1087  
1088  	wpa_printf(MSG_INFO, "- Credential/CheckAAAServerCertStatus = %s", str);
1089  	if (os_strcasecmp(str, "true") == 0 &&
1090  	    set_cred(ctx->ifname, id, "ocsp", "2") < 0)
1091  		wpa_printf(MSG_INFO, "Failed to set cred ocsp");
1092  	xml_node_get_text_free(ctx->xml, str);
1093  }
1094  
1095  
set_pps_cred_sim(struct hs20_osu_client * ctx,int id,xml_node_t * sim,xml_node_t * realm)1096  static void set_pps_cred_sim(struct hs20_osu_client *ctx, int id,
1097  			     xml_node_t *sim, xml_node_t *realm)
1098  {
1099  	xml_node_t *node;
1100  	char *imsi, *eaptype, *str, buf[20];
1101  	int type;
1102  	int mnc_len = 3;
1103  	size_t imsi_len;
1104  
1105  	node = get_node(ctx->xml, sim, "EAPType");
1106  	if (node == NULL) {
1107  		wpa_printf(MSG_INFO, "No SIM/EAPType node in credential");
1108  		return;
1109  	}
1110  	eaptype = xml_node_get_text(ctx->xml, node);
1111  	if (eaptype == NULL) {
1112  		wpa_printf(MSG_INFO, "Could not extract SIM/EAPType");
1113  		return;
1114  	}
1115  	wpa_printf(MSG_INFO, " - Credential/SIM/EAPType = %s", eaptype);
1116  	type = atoi(eaptype);
1117  	xml_node_get_text_free(ctx->xml, eaptype);
1118  
1119  	switch (type) {
1120  	case EAP_TYPE_SIM:
1121  		if (set_cred(ctx->ifname, id, "eap", "SIM") < 0)
1122  			wpa_printf(MSG_INFO, "Could not set eap=SIM");
1123  		break;
1124  	case EAP_TYPE_AKA:
1125  		if (set_cred(ctx->ifname, id, "eap", "AKA") < 0)
1126  			wpa_printf(MSG_INFO, "Could not set eap=SIM");
1127  		break;
1128  	case EAP_TYPE_AKA_PRIME:
1129  		if (set_cred(ctx->ifname, id, "eap", "AKA'") < 0)
1130  			wpa_printf(MSG_INFO, "Could not set eap=SIM");
1131  		break;
1132  	default:
1133  		wpa_printf(MSG_INFO, "Unsupported SIM/EAPType %d", type);
1134  		return;
1135  	}
1136  
1137  	node = get_node(ctx->xml, sim, "IMSI");
1138  	if (node == NULL) {
1139  		wpa_printf(MSG_INFO, "No SIM/IMSI node in credential");
1140  		return;
1141  	}
1142  	imsi = xml_node_get_text(ctx->xml, node);
1143  	if (imsi == NULL) {
1144  		wpa_printf(MSG_INFO, "Could not extract SIM/IMSI");
1145  		return;
1146  	}
1147  	wpa_printf(MSG_INFO, " - Credential/SIM/IMSI = %s", imsi);
1148  	imsi_len = os_strlen(imsi);
1149  	if (imsi_len < 7 || imsi_len + 2 > sizeof(buf)) {
1150  		wpa_printf(MSG_INFO, "Invalid IMSI length");
1151  		xml_node_get_text_free(ctx->xml, imsi);
1152  		return;
1153  	}
1154  
1155  	str = xml_node_get_text(ctx->xml, node);
1156  	if (str) {
1157  		char *pos;
1158  		pos = os_strstr(str, "mnc");
1159  		if (pos && os_strlen(pos) >= 6) {
1160  			if (os_strncmp(imsi + 3, pos + 3, 3) == 0)
1161  				mnc_len = 3;
1162  			else if (os_strncmp(imsi + 3, pos + 4, 2) == 0)
1163  				mnc_len = 2;
1164  		}
1165  		xml_node_get_text_free(ctx->xml, str);
1166  	}
1167  
1168  	os_memcpy(buf, imsi, 3 + mnc_len);
1169  	buf[3 + mnc_len] = '-';
1170  	os_strlcpy(buf + 3 + mnc_len + 1, imsi + 3 + mnc_len,
1171  		   sizeof(buf) - 3 - mnc_len - 1);
1172  
1173  	xml_node_get_text_free(ctx->xml, imsi);
1174  
1175  	if (set_cred_quoted(ctx->ifname, id, "imsi", buf) < 0)
1176  		wpa_printf(MSG_INFO, "Could not set IMSI");
1177  
1178  	if (set_cred_quoted(ctx->ifname, id, "milenage",
1179  			    "90dca4eda45b53cf0f12d7c9c3bc6a89:"
1180  			    "cb9cccc4b9258e6dca4760379fb82581:000000000123") <
1181  	    0)
1182  		wpa_printf(MSG_INFO, "Could not set Milenage parameters");
1183  }
1184  
1185  
set_pps_cred_credential(struct hs20_osu_client * ctx,int id,xml_node_t * node,const char * fqdn)1186  static void set_pps_cred_credential(struct hs20_osu_client *ctx, int id,
1187  				    xml_node_t *node, const char *fqdn)
1188  {
1189  	xml_node_t *child, *sim, *realm;
1190  	const char *name;
1191  
1192  	wpa_printf(MSG_INFO, "- Credential");
1193  
1194  	sim = get_node(ctx->xml, node, "SIM");
1195  	realm = get_node(ctx->xml, node, "Realm");
1196  
1197  	xml_node_for_each_child(ctx->xml, child, node) {
1198  		xml_node_for_each_check(ctx->xml, child);
1199  		name = xml_node_get_localname(ctx->xml, child);
1200  		if (os_strcasecmp(name, "CreationDate") == 0)
1201  			set_pps_cred_creation_date(ctx, id, child);
1202  		else if (os_strcasecmp(name, "ExpirationDate") == 0)
1203  			set_pps_cred_expiration_date(ctx, id, child);
1204  		else if (os_strcasecmp(name, "UsernamePassword") == 0)
1205  			set_pps_cred_username_password(ctx, id, child);
1206  		else if (os_strcasecmp(name, "DigitalCertificate") == 0)
1207  			set_pps_cred_digital_cert(ctx, id, child, fqdn);
1208  		else if (os_strcasecmp(name, "Realm") == 0)
1209  			set_pps_cred_realm(ctx, id, child, fqdn, sim != NULL);
1210  		else if (os_strcasecmp(name, "CheckAAAServerCertStatus") == 0)
1211  			set_pps_cred_check_aaa_cert_status(ctx, id, child);
1212  		else if (os_strcasecmp(name, "SIM") == 0)
1213  			set_pps_cred_sim(ctx, id, child, realm);
1214  		else
1215  			wpa_printf(MSG_INFO, "Unknown Credential node '%s'",
1216  				   name);
1217  	}
1218  }
1219  
1220  
set_pps_credential(struct hs20_osu_client * ctx,int id,xml_node_t * cred,const char * fqdn)1221  static void set_pps_credential(struct hs20_osu_client *ctx, int id,
1222  			       xml_node_t *cred, const char *fqdn)
1223  {
1224  	xml_node_t *child;
1225  	const char *name;
1226  
1227  	xml_node_for_each_child(ctx->xml, child, cred) {
1228  		xml_node_for_each_check(ctx->xml, child);
1229  		name = xml_node_get_localname(ctx->xml, child);
1230  		if (os_strcasecmp(name, "Policy") == 0)
1231  			set_pps_cred_policy(ctx, id, child);
1232  		else if (os_strcasecmp(name, "CredentialPriority") == 0)
1233  			set_pps_cred_priority(ctx, id, child);
1234  		else if (os_strcasecmp(name, "AAAServerTrustRoot") == 0)
1235  			set_pps_cred_aaa_server_trust_root(ctx, id, child);
1236  		else if (os_strcasecmp(name, "SubscriptionUpdate") == 0)
1237  			set_pps_cred_sub_update(ctx, id, child);
1238  		else if (os_strcasecmp(name, "HomeSP") == 0)
1239  			set_pps_cred_home_sp(ctx, id, child);
1240  		else if (os_strcasecmp(name, "SubscriptionParameters") == 0)
1241  			set_pps_cred_sub_params(ctx, id, child);
1242  		else if (os_strcasecmp(name, "Credential") == 0)
1243  			set_pps_cred_credential(ctx, id, child, fqdn);
1244  		else
1245  			wpa_printf(MSG_INFO, "Unknown credential node '%s'",
1246  				   name);
1247  	}
1248  }
1249  
1250  
set_pps(struct hs20_osu_client * ctx,xml_node_t * pps,const char * fqdn)1251  static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps,
1252  		    const char *fqdn)
1253  {
1254  	xml_node_t *child;
1255  	const char *name;
1256  	int id;
1257  	char *update_identifier = NULL;
1258  
1259  	/*
1260  	 * TODO: Could consider more complex mechanism that would remove
1261  	 * credentials only if there are changes in the information sent to
1262  	 * wpa_supplicant.
1263  	 */
1264  	remove_sp_creds(ctx, fqdn);
1265  
1266  	xml_node_for_each_child(ctx->xml, child, pps) {
1267  		xml_node_for_each_check(ctx->xml, child);
1268  		name = xml_node_get_localname(ctx->xml, child);
1269  		if (os_strcasecmp(name, "UpdateIdentifier") == 0) {
1270  			update_identifier = xml_node_get_text(ctx->xml, child);
1271  			if (update_identifier) {
1272  				wpa_printf(MSG_INFO, "- UpdateIdentifier = %s",
1273  					   update_identifier);
1274  				break;
1275  			}
1276  		}
1277  	}
1278  
1279  	xml_node_for_each_child(ctx->xml, child, pps) {
1280  		xml_node_for_each_check(ctx->xml, child);
1281  		name = xml_node_get_localname(ctx->xml, child);
1282  		if (os_strcasecmp(name, "UpdateIdentifier") == 0)
1283  			continue;
1284  		id = add_cred(ctx->ifname);
1285  		if (id < 0) {
1286  			wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant");
1287  			write_summary(ctx, "Failed to add credential to wpa_supplicant");
1288  			break;
1289  		}
1290  		write_summary(ctx, "Add a credential to wpa_supplicant");
1291  		if (update_identifier &&
1292  		    set_cred(ctx->ifname, id, "update_identifier",
1293  			     update_identifier) < 0)
1294  			wpa_printf(MSG_INFO, "Failed to set update_identifier");
1295  		if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) <
1296  		    0)
1297  			wpa_printf(MSG_INFO, "Failed to set provisioning_sp");
1298  		wpa_printf(MSG_INFO, "credential localname: '%s'", name);
1299  		set_pps_credential(ctx, id, child, fqdn);
1300  	}
1301  
1302  	xml_node_get_text_free(ctx->xml, update_identifier);
1303  }
1304  
1305  
cmd_set_pps(struct hs20_osu_client * ctx,const char * pps_fname)1306  static void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
1307  {
1308  	xml_node_t *pps;
1309  	const char *fqdn;
1310  	char *fqdn_buf = NULL, *pos;
1311  
1312  	pps = node_from_file(ctx->xml, pps_fname);
1313  	if (pps == NULL) {
1314  		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
1315  		return;
1316  	}
1317  
1318  	fqdn = os_strstr(pps_fname, "SP/");
1319  	if (fqdn) {
1320  		fqdn_buf = os_strdup(fqdn + 3);
1321  		if (fqdn_buf == NULL)
1322  			return;
1323  		pos = os_strchr(fqdn_buf, '/');
1324  		if (pos)
1325  			*pos = '\0';
1326  		fqdn = fqdn_buf;
1327  	} else
1328  		fqdn = "wi-fi.org";
1329  
1330  	wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s",
1331  		   fqdn);
1332  	set_pps(ctx, pps, fqdn);
1333  
1334  	os_free(fqdn_buf);
1335  	xml_node_free(ctx->xml, pps);
1336  }
1337  
1338  
cmd_get_fqdn(struct hs20_osu_client * ctx,const char * pps_fname)1339  static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname)
1340  {
1341  	xml_node_t *pps, *node;
1342  	char *fqdn = NULL;
1343  
1344  	pps = node_from_file(ctx->xml, pps_fname);
1345  	if (pps == NULL) {
1346  		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
1347  		return -1;
1348  	}
1349  
1350  	node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
1351  	if (node)
1352  		fqdn = xml_node_get_text(ctx->xml, node);
1353  
1354  	xml_node_free(ctx->xml, pps);
1355  
1356  	if (fqdn) {
1357  		FILE *f = fopen("pps-fqdn", "w");
1358  		if (f) {
1359  			fprintf(f, "%s", fqdn);
1360  			fclose(f);
1361  		}
1362  		xml_node_get_text_free(ctx->xml, fqdn);
1363  		return 0;
1364  	}
1365  
1366  	xml_node_get_text_free(ctx->xml, fqdn);
1367  	return -1;
1368  }
1369  
1370  
cmd_to_tnds(struct hs20_osu_client * ctx,const char * in_fname,const char * out_fname,const char * urn,int use_path)1371  static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname,
1372  			const char *out_fname, const char *urn, int use_path)
1373  {
1374  	xml_node_t *mo, *node;
1375  
1376  	mo = node_from_file(ctx->xml, in_fname);
1377  	if (mo == NULL) {
1378  		wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
1379  		return;
1380  	}
1381  
1382  	node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL);
1383  	if (node) {
1384  		node_to_file(ctx->xml, out_fname, node);
1385  		xml_node_free(ctx->xml, node);
1386  	}
1387  
1388  	xml_node_free(ctx->xml, mo);
1389  }
1390  
1391  
cmd_from_tnds(struct hs20_osu_client * ctx,const char * in_fname,const char * out_fname)1392  static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname,
1393  			  const char *out_fname)
1394  {
1395  	xml_node_t *tnds, *mo;
1396  
1397  	tnds = node_from_file(ctx->xml, in_fname);
1398  	if (tnds == NULL) {
1399  		wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
1400  		return;
1401  	}
1402  
1403  	mo = tnds_to_mo(ctx->xml, tnds);
1404  	if (mo) {
1405  		node_to_file(ctx->xml, out_fname, mo);
1406  		xml_node_free(ctx->xml, mo);
1407  	}
1408  
1409  	xml_node_free(ctx->xml, tnds);
1410  }
1411  
1412  
init_ctx(struct hs20_osu_client * ctx)1413  static int init_ctx(struct hs20_osu_client *ctx)
1414  {
1415  	os_memset(ctx, 0, sizeof(*ctx));
1416  	ctx->ifname = "wlan0";
1417  	ctx->xml = xml_node_init_ctx(ctx, NULL);
1418  	if (ctx->xml == NULL)
1419  		return -1;
1420  
1421  	ctx->http = http_init_ctx(ctx, ctx->xml);
1422  	if (ctx->http == NULL) {
1423  		xml_node_deinit_ctx(ctx->xml);
1424  		return -1;
1425  	}
1426  	http_ocsp_set(ctx->http, 2);
1427  
1428  	return 0;
1429  }
1430  
1431  
deinit_ctx(struct hs20_osu_client * ctx)1432  static void deinit_ctx(struct hs20_osu_client *ctx)
1433  {
1434  	http_deinit_ctx(ctx->http);
1435  	xml_node_deinit_ctx(ctx->xml);
1436  }
1437  
1438  
check_workarounds(struct hs20_osu_client * ctx)1439  static void check_workarounds(struct hs20_osu_client *ctx)
1440  {
1441  	FILE *f;
1442  	char buf[100];
1443  	unsigned long int val = 0;
1444  
1445  	f = fopen("hs20-osu-client.workarounds", "r");
1446  	if (f == NULL)
1447  		return;
1448  
1449  	if (fgets(buf, sizeof(buf), f))
1450  		val = strtoul(buf, NULL, 16);
1451  
1452  	fclose(f);
1453  
1454  	if (val) {
1455  		wpa_printf(MSG_INFO, "Workarounds enabled: 0x%lx", val);
1456  		ctx->workarounds = val;
1457  		if (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL)
1458  			http_ocsp_set(ctx->http, 1);
1459  	}
1460  }
1461  
1462  
usage(void)1463  static void usage(void)
1464  {
1465  	printf("usage: hs20-osu-client [-dddqqKtT] [-S<station ifname>] \\\n"
1466  	       "    [-w<wpa_supplicant ctrl_iface dir>] "
1467  	       "[-r<result file>] [-f<debug file>] \\\n"
1468  	       "    [-s<summary file>] \\\n"
1469  	       "    [-x<spp.xsd file name>] \\\n"
1470  	       "    <command> [arguments..]\n"
1471  	       "commands:\n"
1472  	       "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n"
1473  	       "- to_tnds2 <XML MO> <XML MO in TNDS format (Path) "
1474  	       "[URN]>\n"
1475  	       "- from_tnds <XML MO in TNDS format> <XML MO>\n"
1476  	       "- set_pps <PerProviderSubscription XML file name>\n"
1477  	       "- get_fqdn <PerProviderSubscription XML file name>\n"
1478  	       "- dl_aaa_ca <PPS> <CA file>\n"
1479  	       "- browser <URL>\n");
1480  }
1481  
1482  
main(int argc,char * argv[])1483  int main(int argc, char *argv[])
1484  {
1485  	struct hs20_osu_client ctx;
1486  	int c;
1487  	int ret = 0;
1488  	const char *wpa_debug_file_path = NULL;
1489  	extern char *wpas_ctrl_path;
1490  	extern int wpa_debug_level;
1491  	extern int wpa_debug_show_keys;
1492  	extern int wpa_debug_timestamp;
1493  
1494  	if (init_ctx(&ctx) < 0)
1495  		return -1;
1496  
1497  	for (;;) {
1498  		c = getopt(argc, argv, "df:hKqr:s:S:tTw:");
1499  		if (c < 0)
1500  			break;
1501  		switch (c) {
1502  		case 'd':
1503  			if (wpa_debug_level > 0)
1504  				wpa_debug_level--;
1505  			break;
1506  		case 'f':
1507  			wpa_debug_file_path = optarg;
1508  			break;
1509  		case 'K':
1510  			wpa_debug_show_keys++;
1511  			break;
1512  		case 'q':
1513  			wpa_debug_level++;
1514  			break;
1515  		case 'r':
1516  			ctx.result_file = optarg;
1517  			break;
1518  		case 's':
1519  			ctx.summary_file = optarg;
1520  			break;
1521  		case 'S':
1522  			ctx.ifname = optarg;
1523  			break;
1524  		case 't':
1525  			wpa_debug_timestamp++;
1526  			break;
1527  		case 'T':
1528  			ctx.ignore_tls = 1;
1529  			break;
1530  		case 'w':
1531  			wpas_ctrl_path = optarg;
1532  			break;
1533  		case 'h':
1534  		default:
1535  			usage();
1536  			exit(0);
1537  		}
1538  	}
1539  
1540  	if (argc - optind < 1) {
1541  		usage();
1542  		exit(0);
1543  	}
1544  
1545  	wpa_debug_open_file(wpa_debug_file_path);
1546  
1547  #ifdef __linux__
1548  	setlinebuf(stdout);
1549  #endif /* __linux__ */
1550  
1551  	if (ctx.result_file)
1552  		unlink(ctx.result_file);
1553  	wpa_printf(MSG_DEBUG, "===[hs20-osu-client START - command: %s ]======"
1554  		   "================", argv[optind]);
1555  	check_workarounds(&ctx);
1556  
1557  	if (strcmp(argv[optind], "to_tnds") == 0) {
1558  		if (argc - optind < 2) {
1559  			usage();
1560  			exit(0);
1561  		}
1562  		cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
1563  			    argc > optind + 3 ? argv[optind + 3] : NULL,
1564  			    0);
1565  	} else if (strcmp(argv[optind], "to_tnds2") == 0) {
1566  		if (argc - optind < 2) {
1567  			usage();
1568  			exit(0);
1569  		}
1570  		cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
1571  			    argc > optind + 3 ? argv[optind + 3] : NULL,
1572  			    1);
1573  	} else if (strcmp(argv[optind], "from_tnds") == 0) {
1574  		if (argc - optind < 2) {
1575  			usage();
1576  			exit(0);
1577  		}
1578  		cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]);
1579  	} else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
1580  		if (argc - optind < 2) {
1581  			usage();
1582  			exit(0);
1583  		}
1584  		cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
1585  	} else if (strcmp(argv[optind], "set_pps") == 0) {
1586  		if (argc - optind < 2) {
1587  			usage();
1588  			exit(0);
1589  		}
1590  		cmd_set_pps(&ctx, argv[optind + 1]);
1591  	} else if (strcmp(argv[optind], "get_fqdn") == 0) {
1592  		if (argc - optind < 1) {
1593  			usage();
1594  			exit(0);
1595  		}
1596  		ret = cmd_get_fqdn(&ctx, argv[optind + 1]);
1597  	} else if (strcmp(argv[optind], "browser") == 0) {
1598  		int ret;
1599  
1600  		if (argc - optind < 2) {
1601  			usage();
1602  			exit(0);
1603  		}
1604  
1605  		wpa_printf(MSG_INFO, "Launch web browser to URL %s",
1606  			   argv[optind + 1]);
1607  		ret = hs20_web_browser(argv[optind + 1], ctx.ignore_tls);
1608  		wpa_printf(MSG_INFO, "Web browser result: %d", ret);
1609  	} else {
1610  		wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]);
1611  	}
1612  
1613  	deinit_ctx(&ctx);
1614  	wpa_printf(MSG_DEBUG,
1615  		   "===[hs20-osu-client END ]======================");
1616  
1617  	wpa_debug_close_file();
1618  
1619  	return ret;
1620  }
1621