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