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