2 * Hotspot 2.0 OSU client
3 * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
13 #include "private/android_filesystem_config.h"
17 #include "utils/browser.h"
18 #include "utils/base64.h"
19 #include "utils/xml-utils.h"
20 #include "utils/http-utils.h"
21 #include "common/wpa_ctrl.h"
22 #include "common/wpa_helpers.h"
23 #include "eap_common/eap_defs.h"
24 #include "crypto/crypto.h"
25 #include "crypto/sha256.h"
26 #include "osu_client.h"
28 const char *spp_xsd_fname
= "spp.xsd";
31 void write_result(struct hs20_osu_client
*ctx
, const char *fmt
, ...)
38 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
40 write_summary(ctx
, "%s", buf
);
42 if (!ctx
->result_file
)
45 f
= fopen(ctx
->result_file
, "w");
57 void write_summary(struct hs20_osu_client
*ctx
, const char *fmt
, ...)
62 if (!ctx
->summary_file
)
65 f
= fopen(ctx
->summary_file
, "a");
77 void debug_dump_node(struct hs20_osu_client
*ctx
, const char *title
,
80 char *str
= xml_node_to_str(ctx
->xml
, node
);
81 wpa_printf(MSG_DEBUG
, "[hs20] %s: '%s'", title
, str
);
86 static int valid_fqdn(const char *fqdn
)
90 /* TODO: could make this more complete.. */
91 if (strchr(fqdn
, '.') == 0 || strlen(fqdn
) > 255)
93 for (pos
= fqdn
; *pos
; pos
++) {
94 if (*pos
>= 'a' && *pos
<= 'z')
96 if (*pos
>= 'A' && *pos
<= 'Z')
98 if (*pos
>= '0' && *pos
<= '9')
100 if (*pos
== '-' || *pos
== '.' || *pos
== '_')
108 static int android_update_permission(const char *path
, mode_t mode
)
111 /* we need to change file/folder permission for Android */
114 wpa_printf(MSG_ERROR
, "file path null");
118 /* Allow processes running with Group ID as AID_WIFI,
119 * to read files from SP, SP/<fqdn>, Cert and osu-info directories */
120 if (lchown(path
, -1, AID_WIFI
)) {
121 wpa_printf(MSG_INFO
, "CTRL: Could not lchown directory: %s",
126 if (chmod(path
, mode
) < 0) {
127 wpa_printf(MSG_INFO
, "CTRL: Could not chmod directory: %s",
137 int osu_get_certificate(struct hs20_osu_client
*ctx
, xml_node_t
*getcert
)
140 char *url
, *user
= NULL
, *pw
= NULL
;
144 proto
= xml_node_get_attr_value(ctx
->xml
, getcert
,
145 "enrollmentProtocol");
148 wpa_printf(MSG_INFO
, "getCertificate - enrollmentProtocol=%s", proto
);
149 write_summary(ctx
, "getCertificate - enrollmentProtocol=%s", proto
);
150 if (os_strcasecmp(proto
, "EST") != 0) {
151 wpa_printf(MSG_INFO
, "Unsupported enrollmentProtocol");
152 xml_node_get_attr_value_free(ctx
->xml
, proto
);
155 xml_node_get_attr_value_free(ctx
->xml
, proto
);
157 node
= get_node(ctx
->xml
, getcert
, "enrollmentServerURI");
159 wpa_printf(MSG_INFO
, "Could not find enrollmentServerURI node");
160 xml_node_get_attr_value_free(ctx
->xml
, proto
);
163 url
= xml_node_get_text(ctx
->xml
, node
);
165 wpa_printf(MSG_INFO
, "Could not get URL text");
168 wpa_printf(MSG_INFO
, "enrollmentServerURI: %s", url
);
169 write_summary(ctx
, "enrollmentServerURI: %s", url
);
171 node
= get_node(ctx
->xml
, getcert
, "estUserID");
172 if (node
== NULL
&& !ctx
->client_cert_present
) {
173 wpa_printf(MSG_INFO
, "Could not find estUserID node");
177 user
= xml_node_get_text(ctx
->xml
, node
);
179 wpa_printf(MSG_INFO
, "Could not get estUserID text");
182 wpa_printf(MSG_INFO
, "estUserID: %s", user
);
183 write_summary(ctx
, "estUserID: %s", user
);
186 node
= get_node(ctx
->xml
, getcert
, "estPassword");
187 if (node
== NULL
&& !ctx
->client_cert_present
) {
188 wpa_printf(MSG_INFO
, "Could not find estPassword node");
192 pw
= xml_node_get_base64_text(ctx
->xml
, node
, NULL
);
194 wpa_printf(MSG_INFO
, "Could not get estPassword text");
197 wpa_printf(MSG_INFO
, "estPassword: %s", pw
);
200 mkdir("Cert", S_IRWXU
);
201 android_update_permission("Cert", S_IRWXU
| S_IRWXG
);
203 if (est_load_cacerts(ctx
, url
) < 0 ||
204 est_build_csr(ctx
, url
) < 0 ||
205 est_simple_enroll(ctx
, url
, user
, pw
) < 0)
210 xml_node_get_text_free(ctx
->xml
, url
);
211 xml_node_get_text_free(ctx
->xml
, user
);
212 xml_node_get_text_free(ctx
->xml
, pw
);
218 static int process_est_cert(struct hs20_osu_client
*ctx
, xml_node_t
*cert
,
221 u8 digest1
[SHA256_MAC_LEN
], digest2
[SHA256_MAC_LEN
];
223 size_t der_len
, pem_len
;
227 wpa_printf(MSG_INFO
, "PPS for certificate credential - fqdn=%s", fqdn
);
229 fingerprint
= xml_node_get_text(ctx
->xml
, cert
);
230 if (fingerprint
== NULL
)
232 if (hexstr2bin(fingerprint
, digest1
, SHA256_MAC_LEN
) < 0) {
233 wpa_printf(MSG_INFO
, "Invalid SHA256 hash value");
234 write_result(ctx
, "Invalid client certificate SHA256 hash value in PPS");
235 xml_node_get_text_free(ctx
->xml
, fingerprint
);
238 xml_node_get_text_free(ctx
->xml
, fingerprint
);
240 der
= os_readfile("Cert/est_cert.der", &der_len
);
242 wpa_printf(MSG_INFO
, "Could not find client certificate from EST");
243 write_result(ctx
, "Could not find client certificate from EST");
247 if (sha256_vector(1, (const u8
**) &der
, &der_len
, digest2
) < 0) {
253 if (os_memcmp(digest1
, digest2
, sizeof(digest1
)) != 0) {
254 wpa_printf(MSG_INFO
, "Client certificate from EST does not match fingerprint from PPS MO");
255 write_result(ctx
, "Client certificate from EST does not match fingerprint from PPS MO");
259 wpa_printf(MSG_INFO
, "Client certificate from EST matches PPS MO");
260 unlink("Cert/est_cert.der");
262 os_snprintf(buf
, sizeof(buf
), "SP/%s/client-ca.pem", fqdn
);
263 if (rename("Cert/est-cacerts.pem", buf
) < 0) {
264 wpa_printf(MSG_INFO
, "Could not move est-cacerts.pem to client-ca.pem: %s",
268 pem
= os_readfile(buf
, &pem_len
);
270 os_snprintf(buf
, sizeof(buf
), "SP/%s/client-cert.pem", fqdn
);
271 if (rename("Cert/est_cert.pem", buf
) < 0) {
272 wpa_printf(MSG_INFO
, "Could not move est_cert.pem to client-cert.pem: %s",
279 FILE *f
= fopen(buf
, "a");
281 fwrite(pem
, pem_len
, 1, f
);
287 os_snprintf(buf
, sizeof(buf
), "SP/%s/client-key.pem", fqdn
);
288 if (rename("Cert/privkey-plain.pem", buf
) < 0) {
289 wpa_printf(MSG_INFO
, "Could not move privkey-plain.pem to client-key.pem: %s",
294 unlink("Cert/est-req.b64");
295 unlink("Cert/est-req.pem");
302 #define TMP_CERT_DL_FILE "tmp-cert-download"
304 static int download_cert(struct hs20_osu_client
*ctx
, xml_node_t
*params
,
307 xml_node_t
*url_node
, *hash_node
;
311 u8 digest1
[SHA256_MAC_LEN
], digest2
[SHA256_MAC_LEN
];
316 url_node
= get_node(ctx
->xml
, params
, "CertURL");
317 hash_node
= get_node(ctx
->xml
, params
, "CertSHA256Fingerprint");
318 if (url_node
== NULL
|| hash_node
== NULL
)
320 url
= xml_node_get_text(ctx
->xml
, url_node
);
321 hash
= xml_node_get_text(ctx
->xml
, hash_node
);
322 if (url
== NULL
|| hash
== NULL
) {
323 xml_node_get_text_free(ctx
->xml
, url
);
324 xml_node_get_text_free(ctx
->xml
, hash
);
328 wpa_printf(MSG_INFO
, "CertURL: %s", url
);
329 wpa_printf(MSG_INFO
, "SHA256 hash: %s", hash
);
331 if (hexstr2bin(hash
, digest1
, SHA256_MAC_LEN
) < 0) {
332 wpa_printf(MSG_INFO
, "Invalid SHA256 hash value");
333 write_result(ctx
, "Invalid SHA256 hash value for downloaded certificate");
334 xml_node_get_text_free(ctx
->xml
, hash
);
337 xml_node_get_text_free(ctx
->xml
, hash
);
339 write_summary(ctx
, "Download certificate from %s", url
);
340 ctx
->no_osu_cert_validation
= 1;
341 http_ocsp_set(ctx
->http
, 1);
342 res
= http_download_file(ctx
->http
, url
, TMP_CERT_DL_FILE
, NULL
);
343 http_ocsp_set(ctx
->http
,
344 (ctx
->workarounds
& WORKAROUND_OCSP_OPTIONAL
) ? 1 : 2);
345 ctx
->no_osu_cert_validation
= 0;
346 xml_node_get_text_free(ctx
->xml
, url
);
350 cert
= os_readfile(TMP_CERT_DL_FILE
, &len
);
351 remove(TMP_CERT_DL_FILE
);
355 if (sha256_vector(1, (const u8
**) &cert
, &len
, digest2
) < 0) {
360 if (os_memcmp(digest1
, digest2
, sizeof(digest1
)) != 0) {
361 wpa_printf(MSG_INFO
, "Downloaded certificate fingerprint did not match");
362 write_result(ctx
, "Downloaded certificate fingerprint did not match");
367 b64
= base64_encode(cert
, len
, NULL
);
372 f
= fopen(fname
, "wb");
378 fprintf(f
, "-----BEGIN CERTIFICATE-----\n"
380 "-----END CERTIFICATE-----\n",
386 wpa_printf(MSG_INFO
, "Downloaded certificate into %s and validated fingerprint",
388 write_summary(ctx
, "Downloaded certificate into %s and validated fingerprint",
395 static int cmd_dl_osu_ca(struct hs20_osu_client
*ctx
, const char *pps_fname
,
396 const char *ca_fname
)
398 xml_node_t
*pps
, *node
;
401 pps
= node_from_file(ctx
->xml
, pps_fname
);
403 wpa_printf(MSG_INFO
, "Could not read or parse '%s'", pps_fname
);
407 node
= get_child_node(ctx
->xml
, pps
,
408 "SubscriptionUpdate/TrustRoot");
410 wpa_printf(MSG_INFO
, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS");
411 xml_node_free(ctx
->xml
, pps
);
415 ret
= download_cert(ctx
, node
, ca_fname
);
416 xml_node_free(ctx
->xml
, pps
);
422 static int cmd_dl_polupd_ca(struct hs20_osu_client
*ctx
, const char *pps_fname
,
423 const char *ca_fname
)
425 xml_node_t
*pps
, *node
;
428 pps
= node_from_file(ctx
->xml
, pps_fname
);
430 wpa_printf(MSG_INFO
, "Could not read or parse '%s'", pps_fname
);
434 node
= get_child_node(ctx
->xml
, pps
,
435 "Policy/PolicyUpdate/TrustRoot");
437 wpa_printf(MSG_INFO
, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
438 xml_node_free(ctx
->xml
, pps
);
442 ret
= download_cert(ctx
, node
, ca_fname
);
443 xml_node_free(ctx
->xml
, pps
);
449 static int cmd_dl_aaa_ca(struct hs20_osu_client
*ctx
, const char *pps_fname
,
450 const char *ca_fname
)
452 xml_node_t
*pps
, *node
, *aaa
;
455 pps
= node_from_file(ctx
->xml
, pps_fname
);
457 wpa_printf(MSG_INFO
, "Could not read or parse '%s'", pps_fname
);
461 node
= get_child_node(ctx
->xml
, pps
,
462 "AAAServerTrustRoot");
464 wpa_printf(MSG_INFO
, "No AAAServerTrustRoot/CertURL found from PPS");
465 xml_node_free(ctx
->xml
, pps
);
469 aaa
= xml_node_first_child(ctx
->xml
, node
);
471 wpa_printf(MSG_INFO
, "No AAAServerTrustRoot/CertURL found from PPS");
472 xml_node_free(ctx
->xml
, pps
);
476 ret
= download_cert(ctx
, aaa
, ca_fname
);
477 xml_node_free(ctx
->xml
, pps
);
483 static int download_trust_roots(struct hs20_osu_client
*ctx
,
484 const char *pps_fname
)
490 dir
= os_strdup(pps_fname
);
493 pos
= os_strrchr(dir
, '/');
500 snprintf(fname
, sizeof(fname
), "%s/ca.pem", dir
);
501 ret
= cmd_dl_osu_ca(ctx
, pps_fname
, fname
);
502 snprintf(fname
, sizeof(fname
), "%s/polupd-ca.pem", dir
);
503 ret1
= cmd_dl_polupd_ca(ctx
, pps_fname
, fname
);
504 if (ret
== 0 && ret1
== -1)
506 snprintf(fname
, sizeof(fname
), "%s/aaa-ca.pem", dir
);
507 ret1
= cmd_dl_aaa_ca(ctx
, pps_fname
, fname
);
508 if (ret
== 0 && ret1
== -1)
517 static int server_dnsname_suffix_match(struct hs20_osu_client
*ctx
,
520 size_t match_len
, len
, i
;
523 match_len
= os_strlen(fqdn
);
525 for (i
= 0; i
< ctx
->server_dnsname_count
; i
++) {
527 "Checking suffix match against server dNSName %s",
528 ctx
->server_dnsname
[i
]);
529 val
= ctx
->server_dnsname
[i
];
530 len
= os_strlen(val
);
535 if (os_strncasecmp(val
+ len
- match_len
, fqdn
, match_len
) != 0)
536 continue; /* no match */
538 if (match_len
== len
)
539 return 1; /* exact match */
541 if (val
[len
- match_len
- 1] == '.')
542 return 1; /* full label match completes suffix match */
544 /* Reject due to incomplete label match */
547 /* None of the dNSName(s) matched */
552 int hs20_add_pps_mo(struct hs20_osu_client
*ctx
, const char *uri
,
553 xml_node_t
*add_mo
, char *fname
, size_t fname_len
)
557 xml_node_t
*tnds
, *mo
, *cert
;
561 if (strncmp(uri
, "./Wi-Fi/", 8) != 0) {
562 wpa_printf(MSG_INFO
, "Unsupported location for addMO to add PPS MO: '%s'",
564 write_result(ctx
, "Unsupported location for addMO to add PPS MO: '%s'",
569 fqdn
= strdup(uri
+ 8);
572 pos
= strchr(fqdn
, '/');
574 if (os_strcasecmp(pos
, "/PerProviderSubscription") != 0) {
575 wpa_printf(MSG_INFO
, "Unsupported location for addMO to add PPS MO (extra directory): '%s'",
577 write_result(ctx
, "Unsupported location for addMO to "
578 "add PPS MO (extra directory): '%s'", uri
);
582 *pos
= '\0'; /* remove trailing slash and PPS node name */
584 wpa_printf(MSG_INFO
, "SP FQDN: %s", fqdn
);
586 if (!server_dnsname_suffix_match(ctx
, fqdn
)) {
588 "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d",
589 fqdn
, (int) ctx
->server_dnsname_count
);
590 write_result(ctx
, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
596 if (!valid_fqdn(fqdn
)) {
597 wpa_printf(MSG_INFO
, "Invalid FQDN '%s'", fqdn
);
598 write_result(ctx
, "Invalid FQDN '%s'", fqdn
);
603 mkdir("SP", S_IRWXU
);
604 snprintf(fname
, fname_len
, "SP/%s", fqdn
);
605 if (mkdir(fname
, S_IRWXU
) < 0) {
606 if (errno
!= EEXIST
) {
608 wpa_printf(MSG_INFO
, "mkdir(%s) failed: %s",
609 fname
, strerror(err
));
615 android_update_permission("SP", S_IRWXU
| S_IRWXG
);
616 android_update_permission(fname
, S_IRWXU
| S_IRWXG
);
618 snprintf(fname
, fname_len
, "SP/%s/pps.xml", fqdn
);
620 if (os_file_exists(fname
)) {
621 wpa_printf(MSG_INFO
, "PPS file '%s' exists - reject addMO",
623 write_result(ctx
, "PPS file '%s' exists - reject addMO",
628 wpa_printf(MSG_INFO
, "Using PPS file: %s", fname
);
630 str
= xml_node_get_text(ctx
->xml
, add_mo
);
632 wpa_printf(MSG_INFO
, "Could not extract MO text");
636 wpa_printf(MSG_DEBUG
, "[hs20] addMO text: '%s'", str
);
638 tnds
= xml_node_from_buf(ctx
->xml
, str
);
639 xml_node_get_text_free(ctx
->xml
, str
);
641 wpa_printf(MSG_INFO
, "[hs20] Could not parse addMO text");
646 mo
= tnds_to_mo(ctx
->xml
, tnds
);
648 wpa_printf(MSG_INFO
, "[hs20] Could not parse addMO TNDS text");
653 debug_dump_node(ctx
, "Parsed TNDS", mo
);
655 name
= xml_node_get_localname(ctx
->xml
, mo
);
656 if (os_strcasecmp(name
, "PerProviderSubscription") != 0) {
657 wpa_printf(MSG_INFO
, "[hs20] Unexpected PPS MO root node name '%s'",
663 cert
= get_child_node(ctx
->xml
, mo
,
664 "Credential/DigitalCertificate/"
665 "CertSHA256Fingerprint");
666 if (cert
&& process_est_cert(ctx
, cert
, fqdn
) < 0) {
667 xml_node_free(ctx
->xml
, mo
);
673 if (node_to_file(ctx
->xml
, fname
, mo
) < 0) {
674 wpa_printf(MSG_INFO
, "Could not write MO to file");
675 xml_node_free(ctx
->xml
, mo
);
678 xml_node_free(ctx
->xml
, mo
);
680 wpa_printf(MSG_INFO
, "A new PPS MO added as '%s'", fname
);
681 write_summary(ctx
, "A new PPS MO added as '%s'", fname
);
683 ret
= download_trust_roots(ctx
, fname
);
685 wpa_printf(MSG_INFO
, "Remove invalid PPS MO file");
686 write_summary(ctx
, "Remove invalid PPS MO file");
694 int update_pps_file(struct hs20_osu_client
*ctx
, const char *pps_fname
,
701 if (ctx
->client_cert_present
) {
703 cert
= get_child_node(ctx
->xml
, pps
,
704 "Credential/DigitalCertificate/"
705 "CertSHA256Fingerprint");
706 if (cert
&& os_file_exists("Cert/est_cert.der") &&
707 process_est_cert(ctx
, cert
, ctx
->fqdn
) < 0) {
708 wpa_printf(MSG_INFO
, "EST certificate update processing failed on PPS MO update");
713 wpa_printf(MSG_INFO
, "Updating PPS MO %s", pps_fname
);
715 str
= xml_node_to_str(ctx
->xml
, pps
);
717 wpa_printf(MSG_ERROR
, "No node found");
720 wpa_printf(MSG_MSGDUMP
, "[hs20] Updated PPS: '%s'", str
);
722 snprintf(backup
, sizeof(backup
), "%s.bak", pps_fname
);
723 rename(pps_fname
, backup
);
724 f
= fopen(pps_fname
, "w");
726 wpa_printf(MSG_INFO
, "Could not write PPS");
727 rename(backup
, pps_fname
);
731 fprintf(f
, "%s\n", str
);
740 void get_user_pw(struct hs20_osu_client
*ctx
, xml_node_t
*pps
,
741 const char *alt_loc
, char **user
, char **pw
)
745 node
= get_child_node(ctx
->xml
, pps
,
746 "Credential/UsernamePassword/Username");
748 *user
= xml_node_get_text(ctx
->xml
, node
);
750 node
= get_child_node(ctx
->xml
, pps
,
751 "Credential/UsernamePassword/Password");
753 *pw
= xml_node_get_base64_text(ctx
->xml
, node
, NULL
);
755 node
= get_child_node(ctx
->xml
, pps
, alt_loc
);
758 a
= get_node(ctx
->xml
, node
, "Username");
760 xml_node_get_text_free(ctx
->xml
, *user
);
761 *user
= xml_node_get_text(ctx
->xml
, a
);
762 wpa_printf(MSG_INFO
, "Use OSU username '%s'", *user
);
765 a
= get_node(ctx
->xml
, node
, "Password");
768 *pw
= xml_node_get_base64_text(ctx
->xml
, a
, NULL
);
769 wpa_printf(MSG_INFO
, "Use OSU password");
775 /* Remove old credentials based on HomeSP/FQDN */
776 static void remove_sp_creds(struct hs20_osu_client
*ctx
, const char *fqdn
)
779 os_snprintf(cmd
, sizeof(cmd
), "REMOVE_CRED provisioning_sp=%s", fqdn
);
780 if (wpa_command(ctx
->ifname
, cmd
) < 0)
781 wpa_printf(MSG_INFO
, "Failed to remove old credential(s)");
785 static void set_pps_cred_policy_spe(struct hs20_osu_client
*ctx
, int id
,
791 ssid
= get_node(ctx
->xml
, spe
, "SSID");
794 txt
= xml_node_get_text(ctx
->xml
, ssid
);
797 wpa_printf(MSG_DEBUG
, "- Policy/SPExclusionList/<X+>/SSID = %s", txt
);
798 if (set_cred_quoted(ctx
->ifname
, id
, "excluded_ssid", txt
) < 0)
799 wpa_printf(MSG_INFO
, "Failed to set cred excluded_ssid");
800 xml_node_get_text_free(ctx
->xml
, txt
);
804 static void set_pps_cred_policy_spel(struct hs20_osu_client
*ctx
, int id
,
809 xml_node_for_each_child(ctx
->xml
, child
, spel
) {
810 xml_node_for_each_check(ctx
->xml
, child
);
811 set_pps_cred_policy_spe(ctx
, id
, child
);
816 static void set_pps_cred_policy_prp(struct hs20_osu_client
*ctx
, int id
,
820 char *txt
= NULL
, *pos
;
821 char *prio
, *country_buf
= NULL
;
826 node
= get_node(ctx
->xml
, prp
, "Priority");
829 prio
= xml_node_get_text(ctx
->xml
, node
);
832 wpa_printf(MSG_INFO
, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s",
834 priority
= atoi(prio
);
835 xml_node_get_text_free(ctx
->xml
, prio
);
837 node
= get_node(ctx
->xml
, prp
, "Country");
839 country_buf
= xml_node_get_text(ctx
->xml
, node
);
840 if (country_buf
== NULL
)
842 country
= country_buf
;
843 wpa_printf(MSG_INFO
, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s",
849 node
= get_node(ctx
->xml
, prp
, "FQDN_Match");
852 txt
= xml_node_get_text(ctx
->xml
, node
);
855 wpa_printf(MSG_INFO
, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s",
857 pos
= strrchr(txt
, ',');
862 snprintf(val
, sizeof(val
), "%s,%d,%d,%s", txt
,
863 strcmp(pos
, "includeSubdomains") != 0, priority
, country
);
864 if (set_cred_quoted(ctx
->ifname
, id
, "roaming_partner", val
) < 0)
865 wpa_printf(MSG_INFO
, "Failed to set cred roaming_partner");
867 xml_node_get_text_free(ctx
->xml
, country_buf
);
868 xml_node_get_text_free(ctx
->xml
, txt
);
872 static void set_pps_cred_policy_prpl(struct hs20_osu_client
*ctx
, int id
,
877 xml_node_for_each_child(ctx
->xml
, child
, prpl
) {
878 xml_node_for_each_check(ctx
->xml
, child
);
879 set_pps_cred_policy_prp(ctx
, id
, child
);
884 static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client
*ctx
, int id
,
885 xml_node_t
*min_backhaul
)
888 char *type
, *dl
= NULL
, *ul
= NULL
;
891 node
= get_node(ctx
->xml
, min_backhaul
, "NetworkType");
893 wpa_printf(MSG_INFO
, "Ignore MinBackhaulThreshold without mandatory NetworkType node");
897 type
= xml_node_get_text(ctx
->xml
, node
);
900 wpa_printf(MSG_INFO
, "- Policy/MinBackhaulThreshold/<X+>/NetworkType = %s",
902 if (os_strcasecmp(type
, "home") == 0)
904 else if (os_strcasecmp(type
, "roaming") == 0)
907 wpa_printf(MSG_INFO
, "Ignore MinBackhaulThreshold with invalid NetworkType");
908 xml_node_get_text_free(ctx
->xml
, type
);
911 xml_node_get_text_free(ctx
->xml
, type
);
913 node
= get_node(ctx
->xml
, min_backhaul
, "DLBandwidth");
915 dl
= xml_node_get_text(ctx
->xml
, node
);
917 node
= get_node(ctx
->xml
, min_backhaul
, "ULBandwidth");
919 ul
= xml_node_get_text(ctx
->xml
, node
);
921 if (dl
== NULL
&& ul
== NULL
) {
922 wpa_printf(MSG_INFO
, "Ignore MinBackhaulThreshold without either DLBandwidth or ULBandwidth nodes");
927 wpa_printf(MSG_INFO
, "- Policy/MinBackhaulThreshold/<X+>/DLBandwidth = %s",
930 wpa_printf(MSG_INFO
, "- Policy/MinBackhaulThreshold/<X+>/ULBandwidth = %s",
935 set_cred(ctx
->ifname
, id
, "min_dl_bandwidth_home", dl
) < 0)
936 wpa_printf(MSG_INFO
, "Failed to set cred bandwidth limit");
938 set_cred(ctx
->ifname
, id
, "min_ul_bandwidth_home", ul
) < 0)
939 wpa_printf(MSG_INFO
, "Failed to set cred bandwidth limit");
942 set_cred(ctx
->ifname
, id
, "min_dl_bandwidth_roaming", dl
) <
944 wpa_printf(MSG_INFO
, "Failed to set cred bandwidth limit");
946 set_cred(ctx
->ifname
, id
, "min_ul_bandwidth_roaming", ul
) <
948 wpa_printf(MSG_INFO
, "Failed to set cred bandwidth limit");
951 xml_node_get_text_free(ctx
->xml
, dl
);
952 xml_node_get_text_free(ctx
->xml
, ul
);
956 static void set_pps_cred_policy_min_backhaul_list(struct hs20_osu_client
*ctx
,
957 int id
, xml_node_t
*node
)
961 wpa_printf(MSG_INFO
, "- Policy/MinBackhaulThreshold");
963 xml_node_for_each_child(ctx
->xml
, child
, node
) {
964 xml_node_for_each_check(ctx
->xml
, child
);
965 set_pps_cred_policy_min_backhaul(ctx
, id
, child
);
970 static void set_pps_cred_policy_update(struct hs20_osu_client
*ctx
, int id
,
973 wpa_printf(MSG_INFO
, "- Policy/PolicyUpdate");
974 /* Not used in wpa_supplicant */
978 static void set_pps_cred_policy_required_proto_port(struct hs20_osu_client
*ctx
,
979 int id
, xml_node_t
*tuple
)
986 node
= get_node(ctx
->xml
, tuple
, "IPProtocol");
988 wpa_printf(MSG_INFO
, "Ignore RequiredProtoPortTuple without mandatory IPProtocol node");
992 proto
= xml_node_get_text(ctx
->xml
, node
);
996 wpa_printf(MSG_INFO
, "- Policy/RequiredProtoPortTuple/<X+>/IPProtocol = %s",
999 node
= get_node(ctx
->xml
, tuple
, "PortNumber");
1000 port
= node
? xml_node_get_text(ctx
->xml
, node
) : NULL
;
1002 wpa_printf(MSG_INFO
, "- Policy/RequiredProtoPortTuple/<X+>/PortNumber = %s",
1004 buflen
= os_strlen(proto
) + os_strlen(port
) + 10;
1005 buf
= os_malloc(buflen
);
1007 os_snprintf(buf
, buflen
, "%s:%s", proto
, port
);
1008 xml_node_get_text_free(ctx
->xml
, port
);
1010 buflen
= os_strlen(proto
) + 10;
1011 buf
= os_malloc(buflen
);
1013 os_snprintf(buf
, buflen
, "%s", proto
);
1016 xml_node_get_text_free(ctx
->xml
, proto
);
1021 if (set_cred(ctx
->ifname
, id
, "req_conn_capab", buf
) < 0)
1022 wpa_printf(MSG_INFO
, "Could not set req_conn_capab");
1028 static void set_pps_cred_policy_required_proto_ports(struct hs20_osu_client
*ctx
,
1029 int id
, xml_node_t
*node
)
1033 wpa_printf(MSG_INFO
, "- Policy/RequiredProtoPortTuple");
1035 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1036 xml_node_for_each_check(ctx
->xml
, child
);
1037 set_pps_cred_policy_required_proto_port(ctx
, id
, child
);
1042 static void set_pps_cred_policy_max_bss_load(struct hs20_osu_client
*ctx
, int id
,
1045 char *str
= xml_node_get_text(ctx
->xml
, node
);
1048 wpa_printf(MSG_INFO
, "- Policy/MaximumBSSLoadValue - %s", str
);
1049 if (set_cred(ctx
->ifname
, id
, "max_bss_load", str
) < 0)
1050 wpa_printf(MSG_INFO
, "Failed to set cred max_bss_load limit");
1051 xml_node_get_text_free(ctx
->xml
, str
);
1055 static void set_pps_cred_policy(struct hs20_osu_client
*ctx
, int id
,
1061 wpa_printf(MSG_INFO
, "- Policy");
1063 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1064 xml_node_for_each_check(ctx
->xml
, child
);
1065 name
= xml_node_get_localname(ctx
->xml
, child
);
1066 if (os_strcasecmp(name
, "PreferredRoamingPartnerList") == 0)
1067 set_pps_cred_policy_prpl(ctx
, id
, child
);
1068 else if (os_strcasecmp(name
, "MinBackhaulThreshold") == 0)
1069 set_pps_cred_policy_min_backhaul_list(ctx
, id
, child
);
1070 else if (os_strcasecmp(name
, "PolicyUpdate") == 0)
1071 set_pps_cred_policy_update(ctx
, id
, child
);
1072 else if (os_strcasecmp(name
, "SPExclusionList") == 0)
1073 set_pps_cred_policy_spel(ctx
, id
, child
);
1074 else if (os_strcasecmp(name
, "RequiredProtoPortTuple") == 0)
1075 set_pps_cred_policy_required_proto_ports(ctx
, id
, child
);
1076 else if (os_strcasecmp(name
, "MaximumBSSLoadValue") == 0)
1077 set_pps_cred_policy_max_bss_load(ctx
, id
, child
);
1079 wpa_printf(MSG_INFO
, "Unknown Policy node '%s'", name
);
1084 static void set_pps_cred_priority(struct hs20_osu_client
*ctx
, int id
,
1087 char *str
= xml_node_get_text(ctx
->xml
, node
);
1090 wpa_printf(MSG_INFO
, "- CredentialPriority = %s", str
);
1091 if (set_cred(ctx
->ifname
, id
, "sp_priority", str
) < 0)
1092 wpa_printf(MSG_INFO
, "Failed to set cred sp_priority");
1093 xml_node_get_text_free(ctx
->xml
, str
);
1097 static void set_pps_cred_aaa_server_trust_root(struct hs20_osu_client
*ctx
,
1098 int id
, xml_node_t
*node
)
1100 wpa_printf(MSG_INFO
, "- AAAServerTrustRoot - TODO");
1104 static void set_pps_cred_sub_update(struct hs20_osu_client
*ctx
, int id
,
1107 wpa_printf(MSG_INFO
, "- SubscriptionUpdate");
1108 /* not used within wpa_supplicant */
1112 static void set_pps_cred_home_sp_network_id(struct hs20_osu_client
*ctx
,
1113 int id
, xml_node_t
*node
)
1115 xml_node_t
*ssid_node
, *hessid_node
;
1116 char *ssid
, *hessid
;
1118 ssid_node
= get_node(ctx
->xml
, node
, "SSID");
1119 if (ssid_node
== NULL
) {
1120 wpa_printf(MSG_INFO
, "Ignore HomeSP/NetworkID without mandatory SSID node");
1124 hessid_node
= get_node(ctx
->xml
, node
, "HESSID");
1126 ssid
= xml_node_get_text(ctx
->xml
, ssid_node
);
1129 hessid
= hessid_node
? xml_node_get_text(ctx
->xml
, hessid_node
) : NULL
;
1131 wpa_printf(MSG_INFO
, "- HomeSP/NetworkID/<X+>/SSID = %s", ssid
);
1133 wpa_printf(MSG_INFO
, "- HomeSP/NetworkID/<X+>/HESSID = %s",
1136 /* TODO: Configure to wpa_supplicant */
1138 xml_node_get_text_free(ctx
->xml
, ssid
);
1139 xml_node_get_text_free(ctx
->xml
, hessid
);
1143 static void set_pps_cred_home_sp_network_ids(struct hs20_osu_client
*ctx
,
1144 int id
, xml_node_t
*node
)
1148 wpa_printf(MSG_INFO
, "- HomeSP/NetworkID");
1150 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1151 xml_node_for_each_check(ctx
->xml
, child
);
1152 set_pps_cred_home_sp_network_id(ctx
, id
, child
);
1157 static void set_pps_cred_home_sp_friendly_name(struct hs20_osu_client
*ctx
,
1158 int id
, xml_node_t
*node
)
1160 char *str
= xml_node_get_text(ctx
->xml
, node
);
1163 wpa_printf(MSG_INFO
, "- HomeSP/FriendlyName = %s", str
);
1164 /* not used within wpa_supplicant(?) */
1165 xml_node_get_text_free(ctx
->xml
, str
);
1169 static void set_pps_cred_home_sp_icon_url(struct hs20_osu_client
*ctx
,
1170 int id
, xml_node_t
*node
)
1172 char *str
= xml_node_get_text(ctx
->xml
, node
);
1175 wpa_printf(MSG_INFO
, "- HomeSP/IconURL = %s", str
);
1176 /* not used within wpa_supplicant */
1177 xml_node_get_text_free(ctx
->xml
, str
);
1181 static void set_pps_cred_home_sp_fqdn(struct hs20_osu_client
*ctx
, int id
,
1184 char *str
= xml_node_get_text(ctx
->xml
, node
);
1187 wpa_printf(MSG_INFO
, "- HomeSP/FQDN = %s", str
);
1188 if (set_cred_quoted(ctx
->ifname
, id
, "domain", str
) < 0)
1189 wpa_printf(MSG_INFO
, "Failed to set cred domain");
1190 if (set_cred_quoted(ctx
->ifname
, id
, "domain_suffix_match", str
) < 0)
1191 wpa_printf(MSG_INFO
, "Failed to set cred domain_suffix_match");
1192 xml_node_get_text_free(ctx
->xml
, str
);
1196 static void set_pps_cred_home_sp_oi(struct hs20_osu_client
*ctx
, int id
,
1201 char *homeoi
= NULL
;
1205 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1206 xml_node_for_each_check(ctx
->xml
, child
);
1207 name
= xml_node_get_localname(ctx
->xml
, child
);
1208 if (strcasecmp(name
, "HomeOI") == 0 && !homeoi
) {
1209 homeoi
= xml_node_get_text(ctx
->xml
, child
);
1210 wpa_printf(MSG_INFO
, "- HomeSP/HomeOIList/<X+>/HomeOI = %s",
1212 } else if (strcasecmp(name
, "HomeOIRequired") == 0) {
1213 str
= xml_node_get_text(ctx
->xml
, child
);
1214 wpa_printf(MSG_INFO
, "- HomeSP/HomeOIList/<X+>/HomeOIRequired = '%s'",
1218 required
= strcasecmp(str
, "true") == 0;
1219 xml_node_get_text_free(ctx
->xml
, str
);
1221 wpa_printf(MSG_INFO
, "Unknown HomeOIList node '%s'",
1225 if (homeoi
== NULL
) {
1226 wpa_printf(MSG_INFO
, "- HomeSP/HomeOIList/<X+> without HomeOI ignored");
1230 wpa_printf(MSG_INFO
, "- HomeSP/HomeOIList/<X+> '%s' required=%d",
1234 if (set_cred(ctx
->ifname
, id
, "required_roaming_consortium",
1236 wpa_printf(MSG_INFO
, "Failed to set cred required_roaming_consortium");
1238 if (set_cred(ctx
->ifname
, id
, "roaming_consortium", homeoi
) < 0)
1239 wpa_printf(MSG_INFO
, "Failed to set cred roaming_consortium");
1242 xml_node_get_text_free(ctx
->xml
, homeoi
);
1246 static void set_pps_cred_home_sp_oi_list(struct hs20_osu_client
*ctx
, int id
,
1251 wpa_printf(MSG_INFO
, "- HomeSP/HomeOIList");
1253 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1254 xml_node_for_each_check(ctx
->xml
, child
);
1255 set_pps_cred_home_sp_oi(ctx
, id
, child
);
1260 static void set_pps_cred_home_sp_other_partner(struct hs20_osu_client
*ctx
,
1261 int id
, xml_node_t
*node
)
1267 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1268 xml_node_for_each_check(ctx
->xml
, child
);
1269 name
= xml_node_get_localname(ctx
->xml
, child
);
1270 if (os_strcasecmp(name
, "FQDN") == 0 && !fqdn
) {
1271 fqdn
= xml_node_get_text(ctx
->xml
, child
);
1272 wpa_printf(MSG_INFO
, "- HomeSP/OtherHomePartners/<X+>/FQDN = %s",
1275 wpa_printf(MSG_INFO
, "Unknown OtherHomePartners node '%s'",
1280 wpa_printf(MSG_INFO
, "- HomeSP/OtherHomePartners/<X+> without FQDN ignored");
1284 if (set_cred_quoted(ctx
->ifname
, id
, "domain", fqdn
) < 0)
1285 wpa_printf(MSG_INFO
, "Failed to set cred domain for OtherHomePartners node");
1287 xml_node_get_text_free(ctx
->xml
, fqdn
);
1291 static void set_pps_cred_home_sp_other_partners(struct hs20_osu_client
*ctx
,
1297 wpa_printf(MSG_INFO
, "- HomeSP/OtherHomePartners");
1299 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1300 xml_node_for_each_check(ctx
->xml
, child
);
1301 set_pps_cred_home_sp_other_partner(ctx
, id
, child
);
1306 static void set_pps_cred_home_sp_roaming_consortium_oi(
1307 struct hs20_osu_client
*ctx
, int id
, xml_node_t
*node
)
1309 char *str
= xml_node_get_text(ctx
->xml
, node
);
1312 wpa_printf(MSG_INFO
, "- HomeSP/RoamingConsortiumOI = %s", str
);
1313 if (set_cred_quoted(ctx
->ifname
, id
, "roaming_consortiums",
1315 wpa_printf(MSG_INFO
, "Failed to set cred roaming_consortiums");
1316 xml_node_get_text_free(ctx
->xml
, str
);
1320 static void set_pps_cred_home_sp(struct hs20_osu_client
*ctx
, int id
,
1326 wpa_printf(MSG_INFO
, "- HomeSP");
1328 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1329 xml_node_for_each_check(ctx
->xml
, child
);
1330 name
= xml_node_get_localname(ctx
->xml
, child
);
1331 if (os_strcasecmp(name
, "NetworkID") == 0)
1332 set_pps_cred_home_sp_network_ids(ctx
, id
, child
);
1333 else if (os_strcasecmp(name
, "FriendlyName") == 0)
1334 set_pps_cred_home_sp_friendly_name(ctx
, id
, child
);
1335 else if (os_strcasecmp(name
, "IconURL") == 0)
1336 set_pps_cred_home_sp_icon_url(ctx
, id
, child
);
1337 else if (os_strcasecmp(name
, "FQDN") == 0)
1338 set_pps_cred_home_sp_fqdn(ctx
, id
, child
);
1339 else if (os_strcasecmp(name
, "HomeOIList") == 0)
1340 set_pps_cred_home_sp_oi_list(ctx
, id
, child
);
1341 else if (os_strcasecmp(name
, "OtherHomePartners") == 0)
1342 set_pps_cred_home_sp_other_partners(ctx
, id
, child
);
1343 else if (os_strcasecmp(name
, "RoamingConsortiumOI") == 0)
1344 set_pps_cred_home_sp_roaming_consortium_oi(ctx
, id
,
1347 wpa_printf(MSG_INFO
, "Unknown HomeSP node '%s'", name
);
1352 static void set_pps_cred_sub_params(struct hs20_osu_client
*ctx
, int id
,
1355 wpa_printf(MSG_INFO
, "- SubscriptionParameters");
1356 /* not used within wpa_supplicant */
1360 static void set_pps_cred_creation_date(struct hs20_osu_client
*ctx
, int id
,
1363 char *str
= xml_node_get_text(ctx
->xml
, node
);
1366 wpa_printf(MSG_INFO
, "- Credential/CreationDate = %s", str
);
1367 /* not used within wpa_supplicant */
1368 xml_node_get_text_free(ctx
->xml
, str
);
1372 static void set_pps_cred_expiration_date(struct hs20_osu_client
*ctx
, int id
,
1375 char *str
= xml_node_get_text(ctx
->xml
, node
);
1378 wpa_printf(MSG_INFO
, "- Credential/ExpirationDate = %s", str
);
1379 /* not used within wpa_supplicant */
1380 xml_node_get_text_free(ctx
->xml
, str
);
1384 static void set_pps_cred_username(struct hs20_osu_client
*ctx
, int id
,
1387 char *str
= xml_node_get_text(ctx
->xml
, node
);
1390 wpa_printf(MSG_INFO
, "- Credential/UsernamePassword/Username = %s",
1392 if (set_cred_quoted(ctx
->ifname
, id
, "username", str
) < 0)
1393 wpa_printf(MSG_INFO
, "Failed to set cred username");
1394 xml_node_get_text_free(ctx
->xml
, str
);
1398 static void set_pps_cred_password(struct hs20_osu_client
*ctx
, int id
,
1402 char *pw
, *hex
, *pos
, *end
;
1404 pw
= xml_node_get_base64_text(ctx
->xml
, node
, &len
);
1408 wpa_printf(MSG_INFO
, "- Credential/UsernamePassword/Password = %s", pw
);
1410 hex
= malloc(len
* 2 + 1);
1415 end
= hex
+ len
* 2 + 1;
1417 for (i
= 0; i
< len
; i
++) {
1418 snprintf(pos
, end
- pos
, "%02x", pw
[i
]);
1423 if (set_cred(ctx
->ifname
, id
, "password", hex
) < 0)
1424 wpa_printf(MSG_INFO
, "Failed to set cred password");
1429 static void set_pps_cred_machine_managed(struct hs20_osu_client
*ctx
, int id
,
1432 char *str
= xml_node_get_text(ctx
->xml
, node
);
1435 wpa_printf(MSG_INFO
, "- Credential/UsernamePassword/MachineManaged = %s",
1437 /* not used within wpa_supplicant */
1438 xml_node_get_text_free(ctx
->xml
, str
);
1442 static void set_pps_cred_soft_token_app(struct hs20_osu_client
*ctx
, int id
,
1445 char *str
= xml_node_get_text(ctx
->xml
, node
);
1448 wpa_printf(MSG_INFO
, "- Credential/UsernamePassword/SoftTokenApp = %s",
1450 /* not used within wpa_supplicant */
1451 xml_node_get_text_free(ctx
->xml
, str
);
1455 static void set_pps_cred_able_to_share(struct hs20_osu_client
*ctx
, int id
,
1458 char *str
= xml_node_get_text(ctx
->xml
, node
);
1461 wpa_printf(MSG_INFO
, "- Credential/UsernamePassword/AbleToShare = %s",
1463 /* not used within wpa_supplicant */
1464 xml_node_get_text_free(ctx
->xml
, str
);
1468 static void set_pps_cred_eap_method_eap_type(struct hs20_osu_client
*ctx
,
1469 int id
, xml_node_t
*node
)
1471 char *str
= xml_node_get_text(ctx
->xml
, node
);
1473 const char *eap_method
= NULL
;
1477 wpa_printf(MSG_INFO
,
1478 "- Credential/UsernamePassword/EAPMethod/EAPType = %s", str
);
1485 eap_method
= "TTLS";
1488 eap_method
= "PEAP";
1494 xml_node_get_text_free(ctx
->xml
, str
);
1496 wpa_printf(MSG_INFO
, "Unknown EAPType value");
1500 if (set_cred(ctx
->ifname
, id
, "eap", eap_method
) < 0)
1501 wpa_printf(MSG_INFO
, "Failed to set cred eap");
1505 static void set_pps_cred_eap_method_inner_method(struct hs20_osu_client
*ctx
,
1506 int id
, xml_node_t
*node
)
1508 char *str
= xml_node_get_text(ctx
->xml
, node
);
1509 const char *phase2
= NULL
;
1513 wpa_printf(MSG_INFO
,
1514 "- Credential/UsernamePassword/EAPMethod/InnerMethod = %s",
1516 if (os_strcmp(str
, "PAP") == 0)
1517 phase2
= "auth=PAP";
1518 else if (os_strcmp(str
, "CHAP") == 0)
1519 phase2
= "auth=CHAP";
1520 else if (os_strcmp(str
, "MS-CHAP") == 0)
1521 phase2
= "auth=MSCHAP";
1522 else if (os_strcmp(str
, "MS-CHAP-V2") == 0)
1523 phase2
= "auth=MSCHAPV2";
1524 xml_node_get_text_free(ctx
->xml
, str
);
1526 wpa_printf(MSG_INFO
, "Unknown InnerMethod value");
1530 if (set_cred_quoted(ctx
->ifname
, id
, "phase2", phase2
) < 0)
1531 wpa_printf(MSG_INFO
, "Failed to set cred phase2");
1535 static void set_pps_cred_eap_method(struct hs20_osu_client
*ctx
, int id
,
1541 wpa_printf(MSG_INFO
, "- Credential/UsernamePassword/EAPMethod");
1543 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1544 xml_node_for_each_check(ctx
->xml
, child
);
1545 name
= xml_node_get_localname(ctx
->xml
, child
);
1546 if (os_strcasecmp(name
, "EAPType") == 0)
1547 set_pps_cred_eap_method_eap_type(ctx
, id
, child
);
1548 else if (os_strcasecmp(name
, "InnerMethod") == 0)
1549 set_pps_cred_eap_method_inner_method(ctx
, id
, child
);
1551 wpa_printf(MSG_INFO
, "Unknown Credential/UsernamePassword/EAPMethod node '%s'",
1557 static void set_pps_cred_username_password(struct hs20_osu_client
*ctx
, int id
,
1563 wpa_printf(MSG_INFO
, "- Credential/UsernamePassword");
1565 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1566 xml_node_for_each_check(ctx
->xml
, child
);
1567 name
= xml_node_get_localname(ctx
->xml
, child
);
1568 if (os_strcasecmp(name
, "Username") == 0)
1569 set_pps_cred_username(ctx
, id
, child
);
1570 else if (os_strcasecmp(name
, "Password") == 0)
1571 set_pps_cred_password(ctx
, id
, child
);
1572 else if (os_strcasecmp(name
, "MachineManaged") == 0)
1573 set_pps_cred_machine_managed(ctx
, id
, child
);
1574 else if (os_strcasecmp(name
, "SoftTokenApp") == 0)
1575 set_pps_cred_soft_token_app(ctx
, id
, child
);
1576 else if (os_strcasecmp(name
, "AbleToShare") == 0)
1577 set_pps_cred_able_to_share(ctx
, id
, child
);
1578 else if (os_strcasecmp(name
, "EAPMethod") == 0)
1579 set_pps_cred_eap_method(ctx
, id
, child
);
1581 wpa_printf(MSG_INFO
, "Unknown Credential/UsernamePassword node '%s'",
1587 static void set_pps_cred_digital_cert(struct hs20_osu_client
*ctx
, int id
,
1588 xml_node_t
*node
, const char *fqdn
)
1590 char buf
[200], dir
[200];
1593 wpa_printf(MSG_INFO
, "- Credential/DigitalCertificate");
1595 if (getcwd(dir
, sizeof(dir
)) == NULL
)
1598 /* TODO: could build username from Subject of Subject AltName */
1599 if (set_cred_quoted(ctx
->ifname
, id
, "username", "cert") < 0) {
1600 wpa_printf(MSG_INFO
, "Failed to set username");
1603 res
= os_snprintf(buf
, sizeof(buf
), "%s/SP/%s/client-cert.pem", dir
,
1605 if (os_snprintf_error(sizeof(buf
), res
))
1607 if (os_file_exists(buf
)) {
1608 if (set_cred_quoted(ctx
->ifname
, id
, "client_cert", buf
) < 0) {
1609 wpa_printf(MSG_INFO
, "Failed to set client_cert");
1613 res
= os_snprintf(buf
, sizeof(buf
), "%s/SP/%s/client-key.pem", dir
,
1615 if (os_snprintf_error(sizeof(buf
), res
))
1617 if (os_file_exists(buf
)) {
1618 if (set_cred_quoted(ctx
->ifname
, id
, "private_key", buf
) < 0) {
1619 wpa_printf(MSG_INFO
, "Failed to set private_key");
1625 static void set_pps_cred_realm(struct hs20_osu_client
*ctx
, int id
,
1626 xml_node_t
*node
, const char *fqdn
, int sim
)
1628 char *str
= xml_node_get_text(ctx
->xml
, node
);
1629 char buf
[200], dir
[200];
1635 wpa_printf(MSG_INFO
, "- Credential/Realm = %s", str
);
1636 if (set_cred_quoted(ctx
->ifname
, id
, "realm", str
) < 0)
1637 wpa_printf(MSG_INFO
, "Failed to set cred realm");
1638 xml_node_get_text_free(ctx
->xml
, str
);
1643 if (getcwd(dir
, sizeof(dir
)) == NULL
)
1645 res
= os_snprintf(buf
, sizeof(buf
), "%s/SP/%s/aaa-ca.pem", dir
, fqdn
);
1646 if (os_snprintf_error(sizeof(buf
), res
))
1648 if (os_file_exists(buf
)) {
1649 if (set_cred_quoted(ctx
->ifname
, id
, "ca_cert", buf
) < 0) {
1650 wpa_printf(MSG_INFO
, "Failed to set CA cert");
1656 static void set_pps_cred_check_aaa_cert_status(struct hs20_osu_client
*ctx
,
1657 int id
, xml_node_t
*node
)
1659 char *str
= xml_node_get_text(ctx
->xml
, node
);
1664 wpa_printf(MSG_INFO
, "- Credential/CheckAAAServerCertStatus = %s", str
);
1665 if (os_strcasecmp(str
, "true") == 0 &&
1666 set_cred(ctx
->ifname
, id
, "ocsp", "2") < 0)
1667 wpa_printf(MSG_INFO
, "Failed to set cred ocsp");
1668 xml_node_get_text_free(ctx
->xml
, str
);
1672 static void set_pps_cred_sim(struct hs20_osu_client
*ctx
, int id
,
1673 xml_node_t
*sim
, xml_node_t
*realm
)
1676 char *imsi
, *eaptype
, *str
, buf
[20];
1681 node
= get_node(ctx
->xml
, sim
, "EAPType");
1683 wpa_printf(MSG_INFO
, "No SIM/EAPType node in credential");
1686 eaptype
= xml_node_get_text(ctx
->xml
, node
);
1687 if (eaptype
== NULL
) {
1688 wpa_printf(MSG_INFO
, "Could not extract SIM/EAPType");
1691 wpa_printf(MSG_INFO
, " - Credential/SIM/EAPType = %s", eaptype
);
1692 type
= atoi(eaptype
);
1693 xml_node_get_text_free(ctx
->xml
, eaptype
);
1697 if (set_cred(ctx
->ifname
, id
, "eap", "SIM") < 0)
1698 wpa_printf(MSG_INFO
, "Could not set eap=SIM");
1701 if (set_cred(ctx
->ifname
, id
, "eap", "AKA") < 0)
1702 wpa_printf(MSG_INFO
, "Could not set eap=SIM");
1704 case EAP_TYPE_AKA_PRIME
:
1705 if (set_cred(ctx
->ifname
, id
, "eap", "AKA'") < 0)
1706 wpa_printf(MSG_INFO
, "Could not set eap=SIM");
1709 wpa_printf(MSG_INFO
, "Unsupported SIM/EAPType %d", type
);
1713 node
= get_node(ctx
->xml
, sim
, "IMSI");
1715 wpa_printf(MSG_INFO
, "No SIM/IMSI node in credential");
1718 imsi
= xml_node_get_text(ctx
->xml
, node
);
1720 wpa_printf(MSG_INFO
, "Could not extract SIM/IMSI");
1723 wpa_printf(MSG_INFO
, " - Credential/SIM/IMSI = %s", imsi
);
1724 imsi_len
= os_strlen(imsi
);
1725 if (imsi_len
< 7 || imsi_len
+ 2 > sizeof(buf
)) {
1726 wpa_printf(MSG_INFO
, "Invalid IMSI length");
1727 xml_node_get_text_free(ctx
->xml
, imsi
);
1731 str
= xml_node_get_text(ctx
->xml
, node
);
1734 pos
= os_strstr(str
, "mnc");
1735 if (pos
&& os_strlen(pos
) >= 6) {
1736 if (os_strncmp(imsi
+ 3, pos
+ 3, 3) == 0)
1738 else if (os_strncmp(imsi
+ 3, pos
+ 4, 2) == 0)
1741 xml_node_get_text_free(ctx
->xml
, str
);
1744 os_memcpy(buf
, imsi
, 3 + mnc_len
);
1745 buf
[3 + mnc_len
] = '-';
1746 os_strlcpy(buf
+ 3 + mnc_len
+ 1, imsi
+ 3 + mnc_len
,
1747 sizeof(buf
) - 3 - mnc_len
- 1);
1749 xml_node_get_text_free(ctx
->xml
, imsi
);
1751 if (set_cred_quoted(ctx
->ifname
, id
, "imsi", buf
) < 0)
1752 wpa_printf(MSG_INFO
, "Could not set IMSI");
1754 if (set_cred_quoted(ctx
->ifname
, id
, "milenage",
1755 "90dca4eda45b53cf0f12d7c9c3bc6a89:"
1756 "cb9cccc4b9258e6dca4760379fb82581:000000000123") <
1758 wpa_printf(MSG_INFO
, "Could not set Milenage parameters");
1762 static void set_pps_cred_credential(struct hs20_osu_client
*ctx
, int id
,
1763 xml_node_t
*node
, const char *fqdn
)
1765 xml_node_t
*child
, *sim
, *realm
;
1768 wpa_printf(MSG_INFO
, "- Credential");
1770 sim
= get_node(ctx
->xml
, node
, "SIM");
1771 realm
= get_node(ctx
->xml
, node
, "Realm");
1773 xml_node_for_each_child(ctx
->xml
, child
, node
) {
1774 xml_node_for_each_check(ctx
->xml
, child
);
1775 name
= xml_node_get_localname(ctx
->xml
, child
);
1776 if (os_strcasecmp(name
, "CreationDate") == 0)
1777 set_pps_cred_creation_date(ctx
, id
, child
);
1778 else if (os_strcasecmp(name
, "ExpirationDate") == 0)
1779 set_pps_cred_expiration_date(ctx
, id
, child
);
1780 else if (os_strcasecmp(name
, "UsernamePassword") == 0)
1781 set_pps_cred_username_password(ctx
, id
, child
);
1782 else if (os_strcasecmp(name
, "DigitalCertificate") == 0)
1783 set_pps_cred_digital_cert(ctx
, id
, child
, fqdn
);
1784 else if (os_strcasecmp(name
, "Realm") == 0)
1785 set_pps_cred_realm(ctx
, id
, child
, fqdn
, sim
!= NULL
);
1786 else if (os_strcasecmp(name
, "CheckAAAServerCertStatus") == 0)
1787 set_pps_cred_check_aaa_cert_status(ctx
, id
, child
);
1788 else if (os_strcasecmp(name
, "SIM") == 0)
1789 set_pps_cred_sim(ctx
, id
, child
, realm
);
1791 wpa_printf(MSG_INFO
, "Unknown Credential node '%s'",
1797 static void set_pps_credential(struct hs20_osu_client
*ctx
, int id
,
1798 xml_node_t
*cred
, const char *fqdn
)
1803 xml_node_for_each_child(ctx
->xml
, child
, cred
) {
1804 xml_node_for_each_check(ctx
->xml
, child
);
1805 name
= xml_node_get_localname(ctx
->xml
, child
);
1806 if (os_strcasecmp(name
, "Policy") == 0)
1807 set_pps_cred_policy(ctx
, id
, child
);
1808 else if (os_strcasecmp(name
, "CredentialPriority") == 0)
1809 set_pps_cred_priority(ctx
, id
, child
);
1810 else if (os_strcasecmp(name
, "AAAServerTrustRoot") == 0)
1811 set_pps_cred_aaa_server_trust_root(ctx
, id
, child
);
1812 else if (os_strcasecmp(name
, "SubscriptionUpdate") == 0)
1813 set_pps_cred_sub_update(ctx
, id
, child
);
1814 else if (os_strcasecmp(name
, "HomeSP") == 0)
1815 set_pps_cred_home_sp(ctx
, id
, child
);
1816 else if (os_strcasecmp(name
, "SubscriptionParameters") == 0)
1817 set_pps_cred_sub_params(ctx
, id
, child
);
1818 else if (os_strcasecmp(name
, "Credential") == 0)
1819 set_pps_cred_credential(ctx
, id
, child
, fqdn
);
1821 wpa_printf(MSG_INFO
, "Unknown credential node '%s'",
1827 static void set_pps(struct hs20_osu_client
*ctx
, xml_node_t
*pps
,
1833 char *update_identifier
= NULL
;
1836 * TODO: Could consider more complex mechanism that would remove
1837 * credentials only if there are changes in the information sent to
1840 remove_sp_creds(ctx
, fqdn
);
1842 xml_node_for_each_child(ctx
->xml
, child
, pps
) {
1843 xml_node_for_each_check(ctx
->xml
, child
);
1844 name
= xml_node_get_localname(ctx
->xml
, child
);
1845 if (os_strcasecmp(name
, "UpdateIdentifier") == 0) {
1846 update_identifier
= xml_node_get_text(ctx
->xml
, child
);
1847 if (update_identifier
) {
1848 wpa_printf(MSG_INFO
, "- UpdateIdentifier = %s",
1855 xml_node_for_each_child(ctx
->xml
, child
, pps
) {
1856 xml_node_for_each_check(ctx
->xml
, child
);
1857 name
= xml_node_get_localname(ctx
->xml
, child
);
1858 if (os_strcasecmp(name
, "UpdateIdentifier") == 0)
1860 id
= add_cred(ctx
->ifname
);
1862 wpa_printf(MSG_INFO
, "Failed to add credential to wpa_supplicant");
1863 write_summary(ctx
, "Failed to add credential to wpa_supplicant");
1866 write_summary(ctx
, "Add a credential to wpa_supplicant");
1867 if (update_identifier
&&
1868 set_cred(ctx
->ifname
, id
, "update_identifier",
1869 update_identifier
) < 0)
1870 wpa_printf(MSG_INFO
, "Failed to set update_identifier");
1871 if (set_cred_quoted(ctx
->ifname
, id
, "provisioning_sp", fqdn
) <
1873 wpa_printf(MSG_INFO
, "Failed to set provisioning_sp");
1874 wpa_printf(MSG_INFO
, "credential localname: '%s'", name
);
1875 set_pps_credential(ctx
, id
, child
, fqdn
);
1876 ctx
->pps_cred_set
= 1;
1879 xml_node_get_text_free(ctx
->xml
, update_identifier
);
1883 void cmd_set_pps(struct hs20_osu_client
*ctx
, const char *pps_fname
)
1887 char *fqdn_buf
= NULL
, *pos
;
1889 pps
= node_from_file(ctx
->xml
, pps_fname
);
1891 wpa_printf(MSG_INFO
, "Could not read or parse '%s'", pps_fname
);
1895 fqdn
= os_strstr(pps_fname
, "SP/");
1897 fqdn_buf
= os_strdup(fqdn
+ 3);
1898 if (fqdn_buf
== NULL
)
1900 pos
= os_strchr(fqdn_buf
, '/');
1907 wpa_printf(MSG_INFO
, "Set PPS MO info to wpa_supplicant - SP FQDN %s",
1909 set_pps(ctx
, pps
, fqdn
);
1912 xml_node_free(ctx
->xml
, pps
);
1916 static int cmd_get_fqdn(struct hs20_osu_client
*ctx
, const char *pps_fname
)
1918 xml_node_t
*pps
, *node
;
1921 pps
= node_from_file(ctx
->xml
, pps_fname
);
1923 wpa_printf(MSG_INFO
, "Could not read or parse '%s'", pps_fname
);
1927 node
= get_child_node(ctx
->xml
, pps
, "HomeSP/FQDN");
1929 fqdn
= xml_node_get_text(ctx
->xml
, node
);
1931 xml_node_free(ctx
->xml
, pps
);
1934 FILE *f
= fopen("pps-fqdn", "w");
1936 fprintf(f
, "%s", fqdn
);
1939 xml_node_get_text_free(ctx
->xml
, fqdn
);
1943 xml_node_get_text_free(ctx
->xml
, fqdn
);
1948 static void cmd_to_tnds(struct hs20_osu_client
*ctx
, const char *in_fname
,
1949 const char *out_fname
, const char *urn
, int use_path
)
1951 xml_node_t
*mo
, *node
;
1953 mo
= node_from_file(ctx
->xml
, in_fname
);
1955 wpa_printf(MSG_INFO
, "Could not read or parse '%s'", in_fname
);
1959 node
= mo_to_tnds(ctx
->xml
, mo
, use_path
, urn
, NULL
);
1961 node_to_file(ctx
->xml
, out_fname
, node
);
1962 xml_node_free(ctx
->xml
, node
);
1965 xml_node_free(ctx
->xml
, mo
);
1969 static void cmd_from_tnds(struct hs20_osu_client
*ctx
, const char *in_fname
,
1970 const char *out_fname
)
1972 xml_node_t
*tnds
, *mo
;
1974 tnds
= node_from_file(ctx
->xml
, in_fname
);
1976 wpa_printf(MSG_INFO
, "Could not read or parse '%s'", in_fname
);
1980 mo
= tnds_to_mo(ctx
->xml
, tnds
);
1982 node_to_file(ctx
->xml
, out_fname
, mo
);
1983 xml_node_free(ctx
->xml
, mo
);
1986 xml_node_free(ctx
->xml
, tnds
);
1993 char mime_type
[256];
2000 unsigned int methods
;
2005 struct osu_lang_text friendly_name
[MAX_OSU_VALS
];
2006 size_t friendly_name_count
;
2007 struct osu_lang_text serv_desc
[MAX_OSU_VALS
];
2008 size_t serv_desc_count
;
2009 struct osu_icon icon
[MAX_OSU_VALS
];
2014 static struct osu_data
* parse_osu_providers(const char *fname
, size_t *count
)
2018 struct osu_data
*osu
= NULL
, *last
= NULL
;
2019 size_t osu_count
= 0;
2022 f
= fopen(fname
, "r");
2024 wpa_printf(MSG_ERROR
, "Could not open %s", fname
);
2028 while (fgets(buf
, sizeof(buf
), f
)) {
2029 pos
= strchr(buf
, '\n');
2033 if (strncmp(buf
, "OSU-PROVIDER ", 13) == 0) {
2034 last
= realloc(osu
, (osu_count
+ 1) * sizeof(*osu
));
2038 last
= &osu
[osu_count
++];
2039 memset(last
, 0, sizeof(*last
));
2040 snprintf(last
->bssid
, sizeof(last
->bssid
), "%s",
2047 if (strncmp(buf
, "uri=", 4) == 0) {
2048 snprintf(last
->url
, sizeof(last
->url
), "%s", buf
+ 4);
2052 if (strncmp(buf
, "methods=", 8) == 0) {
2053 last
->methods
= strtol(buf
+ 8, NULL
, 16);
2057 if (strncmp(buf
, "osu_ssid=", 9) == 0) {
2058 snprintf(last
->osu_ssid
, sizeof(last
->osu_ssid
),
2063 if (strncmp(buf
, "osu_ssid2=", 10) == 0) {
2064 snprintf(last
->osu_ssid2
, sizeof(last
->osu_ssid2
),
2069 if (os_strncmp(buf
, "osu_nai=", 8) == 0) {
2070 os_snprintf(last
->osu_nai
, sizeof(last
->osu_nai
),
2075 if (os_strncmp(buf
, "osu_nai2=", 9) == 0) {
2076 os_snprintf(last
->osu_nai2
, sizeof(last
->osu_nai2
),
2081 if (strncmp(buf
, "friendly_name=", 14) == 0) {
2082 struct osu_lang_text
*txt
;
2083 if (last
->friendly_name_count
== MAX_OSU_VALS
)
2085 pos
= strchr(buf
+ 14, ':');
2089 txt
= &last
->friendly_name
[last
->friendly_name_count
++];
2090 snprintf(txt
->lang
, sizeof(txt
->lang
), "%s", buf
+ 14);
2091 snprintf(txt
->text
, sizeof(txt
->text
), "%s", pos
);
2094 if (strncmp(buf
, "desc=", 5) == 0) {
2095 struct osu_lang_text
*txt
;
2096 if (last
->serv_desc_count
== MAX_OSU_VALS
)
2098 pos
= strchr(buf
+ 5, ':');
2102 txt
= &last
->serv_desc
[last
->serv_desc_count
++];
2103 snprintf(txt
->lang
, sizeof(txt
->lang
), "%s", buf
+ 5);
2104 snprintf(txt
->text
, sizeof(txt
->text
), "%s", pos
);
2107 if (strncmp(buf
, "icon=", 5) == 0) {
2108 struct osu_icon
*icon
;
2109 if (last
->icon_count
== MAX_OSU_VALS
)
2111 icon
= &last
->icon
[last
->icon_count
++];
2112 icon
->id
= atoi(buf
+ 5);
2113 pos
= strchr(buf
, ':');
2116 pos
= strchr(pos
+ 1, ':');
2119 pos
= strchr(pos
+ 1, ':');
2123 end
= strchr(pos
, ':');
2127 snprintf(icon
->lang
, sizeof(icon
->lang
), "%s", pos
);
2130 end
= strchr(pos
, ':');
2133 snprintf(icon
->mime_type
, sizeof(icon
->mime_type
),
2139 end
= strchr(pos
, ':');
2142 snprintf(icon
->filename
, sizeof(icon
->filename
),
2155 static int osu_connect(struct hs20_osu_client
*ctx
, const char *bssid
,
2156 const char *ssid
, const char *ssid2
, const char *url
,
2157 unsigned int methods
, int no_prod_assoc
,
2158 const char *osu_nai
, const char *osu_nai2
)
2161 const char *ifname
= ctx
->ifname
;
2163 struct wpa_ctrl
*mon
;
2166 if (ssid2
&& ssid2
[0] == '\0')
2169 if (ctx
->osu_ssid
) {
2170 if (os_strcmp(ssid
, ctx
->osu_ssid
) == 0) {
2171 wpa_printf(MSG_DEBUG
,
2172 "Enforced OSU SSID matches ANQP info");
2174 } else if (ssid2
&& os_strcmp(ssid2
, ctx
->osu_ssid
) == 0) {
2175 wpa_printf(MSG_DEBUG
,
2176 "Enforced OSU SSID matches RSN[OSEN] info");
2179 wpa_printf(MSG_INFO
, "Enforced OSU SSID did not match");
2180 write_summary(ctx
, "Enforced OSU SSID did not match");
2185 id
= add_network(ifname
);
2188 if (set_network_quoted(ifname
, id
, "ssid", ssid
) < 0)
2192 if (osu_nai
&& os_strlen(osu_nai
) > 0) {
2193 char dir
[255], fname
[300];
2194 if (getcwd(dir
, sizeof(dir
)) == NULL
)
2196 os_snprintf(fname
, sizeof(fname
), "%s/osu-ca.pem", dir
);
2198 if (ssid2
&& set_network_quoted(ifname
, id
, "ssid", ssid2
) < 0)
2201 if (set_network(ifname
, id
, "proto", "OSEN") < 0 ||
2202 set_network(ifname
, id
, "key_mgmt", "OSEN") < 0 ||
2203 set_network(ifname
, id
, "pairwise", "CCMP") < 0 ||
2204 set_network(ifname
, id
, "group", "GTK_NOT_USED CCMP") < 0 ||
2205 set_network(ifname
, id
, "eap", "WFA-UNAUTH-TLS") < 0 ||
2206 set_network(ifname
, id
, "ocsp", "2") < 0 ||
2207 set_network_quoted(ifname
, id
, "identity", osu_nai
) < 0 ||
2208 set_network_quoted(ifname
, id
, "ca_cert", fname
) < 0)
2211 wpa_printf(MSG_INFO
, "No OSU_NAI set for RSN[OSEN]");
2212 write_summary(ctx
, "No OSU_NAI set for RSN[OSEN]");
2215 if (set_network(ifname
, id
, "key_mgmt", "NONE") < 0)
2219 mon
= open_wpa_mon(ifname
);
2223 wpa_printf(MSG_INFO
, "Associate with OSU SSID");
2224 write_summary(ctx
, "Associate with OSU SSID");
2225 snprintf(buf
, sizeof(buf
), "SELECT_NETWORK %d", id
);
2226 if (wpa_command(ifname
, buf
) < 0)
2229 res
= get_wpa_cli_event(mon
, "CTRL-EVENT-CONNECTED",
2232 wpa_ctrl_detach(mon
);
2233 wpa_ctrl_close(mon
);
2236 wpa_printf(MSG_INFO
, "Could not connect");
2237 write_summary(ctx
, "Could not connect to OSU network");
2238 wpa_printf(MSG_INFO
, "Remove OSU network connection");
2239 snprintf(buf
, sizeof(buf
), "REMOVE_NETWORK %d", id
);
2240 wpa_command(ifname
, buf
);
2244 write_summary(ctx
, "Waiting for IP address for subscription registration");
2245 if (wait_ip_addr(ifname
, 15) < 0) {
2246 wpa_printf(MSG_INFO
, "Could not get IP address for WLAN - try connection anyway");
2249 if (no_prod_assoc
) {
2252 wpa_printf(MSG_INFO
, "No production connection used for testing purposes");
2253 write_summary(ctx
, "No production connection used for testing purposes");
2257 ctx
->no_reconnect
= 1;
2258 if (methods
& 0x02) {
2259 wpa_printf(MSG_DEBUG
, "Calling cmd_prov from osu_connect");
2260 res
= cmd_prov(ctx
, url
);
2261 } else if (methods
& 0x01) {
2262 wpa_printf(MSG_DEBUG
,
2263 "Calling cmd_oma_dm_prov from osu_connect");
2264 res
= cmd_oma_dm_prov(ctx
, url
);
2267 wpa_printf(MSG_INFO
, "Remove OSU network connection");
2268 write_summary(ctx
, "Remove OSU network connection");
2269 snprintf(buf
, sizeof(buf
), "REMOVE_NETWORK %d", id
);
2270 wpa_command(ifname
, buf
);
2275 wpa_printf(MSG_INFO
, "Requesting reconnection with updated configuration");
2276 write_summary(ctx
, "Requesting reconnection with updated configuration");
2277 if (wpa_command(ctx
->ifname
, "INTERWORKING_SELECT auto") < 0) {
2278 wpa_printf(MSG_INFO
, "Failed to request wpa_supplicant to reconnect");
2279 write_summary(ctx
, "Failed to request wpa_supplicant to reconnect");
2287 static int cmd_osu_select(struct hs20_osu_client
*ctx
, const char *dir
,
2288 int connect
, int no_prod_assoc
,
2289 const char *friendly_name
)
2293 struct osu_data
*osu
= NULL
, *last
= NULL
;
2294 size_t osu_count
= 0, i
, j
;
2297 write_summary(ctx
, "OSU provider selection");
2300 wpa_printf(MSG_INFO
, "Missing dir parameter to osu_select");
2304 snprintf(fname
, sizeof(fname
), "%s/osu-providers.txt", dir
);
2305 osu
= parse_osu_providers(fname
, &osu_count
);
2307 wpa_printf(MSG_INFO
, "Could not find any OSU providers from %s",
2309 write_result(ctx
, "No OSU providers available");
2313 if (friendly_name
) {
2314 for (i
= 0; i
< osu_count
; i
++) {
2316 for (j
= 0; j
< last
->friendly_name_count
; j
++) {
2317 if (os_strcmp(last
->friendly_name
[j
].text
,
2318 friendly_name
) == 0)
2321 if (j
< last
->friendly_name_count
)
2324 if (i
== osu_count
) {
2325 wpa_printf(MSG_INFO
, "Requested operator friendly name '%s' not found in the list of available providers",
2327 write_summary(ctx
, "Requested operator friendly name '%s' not found in the list of available providers",
2333 wpa_printf(MSG_INFO
, "OSU Provider selected based on requested operator friendly name '%s'",
2335 write_summary(ctx
, "OSU Provider selected based on requested operator friendly name '%s'",
2341 snprintf(fname
, sizeof(fname
), "%s/osu-providers.html", dir
);
2342 f
= fopen(fname
, "w");
2344 wpa_printf(MSG_INFO
, "Could not open %s", fname
);
2349 fprintf(f
, "<html><head>"
2350 "<meta http-equiv=\"Content-type\" content=\"text/html; "
2351 "charset=utf-8\"<title>Select service operator</title>"
2352 "</head><body><h1>Select service operator</h1>\n");
2355 fprintf(f
, "No online signup available\n");
2357 for (i
= 0; i
< osu_count
; i
++) {
2361 "<a href=\"http://localhost:12345/osu/%d\">"
2362 "<table><tr><td>", (int) i
+ 1);
2365 "<a href=\"osu://%d\">"
2366 "<table><tr><td>", (int) i
+ 1);
2367 #endif /* ANDROID */
2368 for (j
= 0; j
< last
->icon_count
; j
++) {
2369 fprintf(f
, "<img src=\"osu-icon-%d.%s\">\n",
2371 strcasecmp(last
->icon
[j
].mime_type
,
2372 "image/png") == 0 ? "png" : "icon");
2375 for (j
= 0; j
< last
->friendly_name_count
; j
++) {
2376 fprintf(f
, "<small>[%s]</small> %s<br>\n",
2377 last
->friendly_name
[j
].lang
,
2378 last
->friendly_name
[j
].text
);
2380 fprintf(f
, "<tr><td colspan=2>");
2381 for (j
= 0; j
< last
->serv_desc_count
; j
++) {
2382 fprintf(f
, "<small>[%s]</small> %s<br>\n",
2383 last
->serv_desc
[j
].lang
,
2384 last
->serv_desc
[j
].text
);
2386 fprintf(f
, "</table></a><br><small>BSSID: %s<br>\n"
2388 last
->bssid
, last
->osu_ssid
);
2389 if (last
->osu_ssid2
[0])
2390 fprintf(f
, "SSID2: %s<br>\n", last
->osu_ssid2
);
2391 if (last
->osu_nai
[0])
2392 fprintf(f
, "NAI: %s<br>\n", last
->osu_nai
);
2393 if (last
->osu_nai2
[0])
2394 fprintf(f
, "NAI2: %s<br>\n", last
->osu_nai2
);
2395 fprintf(f
, "URL: %s<br>\n"
2396 "methods:%s%s<br>\n"
2399 last
->methods
& 0x01 ? " OMA-DM" : "",
2400 last
->methods
& 0x02 ? " SOAP-XML-SPP" : "");
2403 fprintf(f
, "</body></html>\n");
2407 snprintf(fname
, sizeof(fname
), "file://%s/osu-providers.html", dir
);
2408 write_summary(ctx
, "Start web browser with OSU provider selection page");
2409 ret
= hs20_web_browser(fname
, 0);
2412 if (ret
> 0 && (size_t) ret
<= osu_count
) {
2416 wpa_printf(MSG_INFO
, "Selected OSU id=%d", ret
);
2417 last
= &osu
[ret
- 1];
2419 wpa_printf(MSG_INFO
, "BSSID: %s", last
->bssid
);
2420 wpa_printf(MSG_INFO
, "SSID: %s", last
->osu_ssid
);
2421 if (last
->osu_ssid2
[0])
2422 wpa_printf(MSG_INFO
, "SSID2: %s", last
->osu_ssid2
);
2423 wpa_printf(MSG_INFO
, "URL: %s", last
->url
);
2424 write_summary(ctx
, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
2425 ret
, last
->bssid
, last
->osu_ssid
, last
->url
);
2427 ctx
->friendly_name_count
= last
->friendly_name_count
;
2428 for (j
= 0; j
< last
->friendly_name_count
; j
++) {
2429 wpa_printf(MSG_INFO
, "FRIENDLY_NAME: [%s]%s",
2430 last
->friendly_name
[j
].lang
,
2431 last
->friendly_name
[j
].text
);
2432 os_strlcpy(ctx
->friendly_name
[j
].lang
,
2433 last
->friendly_name
[j
].lang
,
2434 sizeof(ctx
->friendly_name
[j
].lang
));
2435 os_strlcpy(ctx
->friendly_name
[j
].text
,
2436 last
->friendly_name
[j
].text
,
2437 sizeof(ctx
->friendly_name
[j
].text
));
2440 ctx
->icon_count
= last
->icon_count
;
2441 for (j
= 0; j
< last
->icon_count
; j
++) {
2444 os_snprintf(fname
, sizeof(fname
), "%s/osu-icon-%d.%s",
2445 dir
, last
->icon
[j
].id
,
2446 strcasecmp(last
->icon
[j
].mime_type
,
2449 wpa_printf(MSG_INFO
, "ICON: %s (%s)",
2450 fname
, last
->icon
[j
].filename
);
2451 os_strlcpy(ctx
->icon_filename
[j
],
2452 last
->icon
[j
].filename
,
2453 sizeof(ctx
->icon_filename
[j
]));
2455 data
= os_readfile(fname
, &data_len
);
2457 sha256_vector(1, (const u8
**) &data
, &data_len
,
2464 if (last
->methods
& 0x02) {
2465 wpa_printf(MSG_DEBUG
,
2466 "Calling cmd_prov from cmd_osu_select");
2467 ret
= cmd_prov(ctx
, last
->url
);
2468 } else if (last
->methods
& 0x01) {
2469 wpa_printf(MSG_DEBUG
,
2470 "Calling cmd_oma_dm_prov from cmd_osu_select");
2471 ret
= cmd_oma_dm_prov(ctx
, last
->url
);
2473 wpa_printf(MSG_DEBUG
,
2474 "No supported OSU provisioning method");
2477 } else if (connect
) {
2478 ret
= osu_connect(ctx
, last
->bssid
, last
->osu_ssid
,
2480 last
->url
, last
->methods
,
2481 no_prod_assoc
, last
->osu_nai
,
2493 static int cmd_signup(struct hs20_osu_client
*ctx
, int no_prod_assoc
,
2494 const char *friendly_name
)
2497 char fname
[300], buf
[400];
2498 struct wpa_ctrl
*mon
;
2502 ifname
= ctx
->ifname
;
2504 if (getcwd(dir
, sizeof(dir
)) == NULL
)
2507 snprintf(fname
, sizeof(fname
), "%s/osu-info", dir
);
2508 if (mkdir(fname
, S_IRWXU
| S_IRWXG
| S_IROTH
| S_IXOTH
) < 0 &&
2510 wpa_printf(MSG_INFO
, "mkdir(%s) failed: %s",
2511 fname
, strerror(errno
));
2515 android_update_permission(fname
, S_IRWXU
| S_IRWXG
| S_IROTH
| S_IXOTH
);
2517 snprintf(buf
, sizeof(buf
), "SET osu_dir %s", fname
);
2518 if (wpa_command(ifname
, buf
) < 0) {
2519 wpa_printf(MSG_INFO
, "Failed to configure osu_dir to wpa_supplicant");
2523 mon
= open_wpa_mon(ifname
);
2527 wpa_printf(MSG_INFO
, "Starting OSU fetch");
2528 write_summary(ctx
, "Starting OSU provider information fetch");
2529 if (wpa_command(ifname
, "FETCH_OSU") < 0) {
2530 wpa_printf(MSG_INFO
, "Could not start OSU fetch");
2531 wpa_ctrl_detach(mon
);
2532 wpa_ctrl_close(mon
);
2535 res
= get_wpa_cli_event(mon
, "OSU provider fetch completed",
2538 wpa_ctrl_detach(mon
);
2539 wpa_ctrl_close(mon
);
2542 wpa_printf(MSG_INFO
, "OSU fetch did not complete");
2543 write_summary(ctx
, "OSU fetch did not complete");
2546 wpa_printf(MSG_INFO
, "OSU provider fetch completed");
2548 return cmd_osu_select(ctx
, fname
, 1, no_prod_assoc
, friendly_name
);
2552 static int cmd_sub_rem(struct hs20_osu_client
*ctx
, const char *address
,
2553 const char *pps_fname
, const char *ca_fname
)
2555 xml_node_t
*pps
, *node
;
2556 char pps_fname_buf
[300];
2557 char ca_fname_buf
[200];
2558 char *cred_username
= NULL
;
2559 char *cred_password
= NULL
;
2560 char *sub_rem_uri
= NULL
;
2561 char client_cert_buf
[200];
2562 char *client_cert
= NULL
;
2563 char client_key_buf
[200];
2564 char *client_key
= NULL
;
2567 wpa_printf(MSG_INFO
, "Subscription remediation requested with Server URL: %s",
2572 wpa_printf(MSG_INFO
, "Determining PPS file based on Home SP information");
2573 if (os_strncmp(address
, "fqdn=", 5) == 0) {
2574 wpa_printf(MSG_INFO
, "Use requested FQDN from command line");
2575 os_snprintf(buf
, sizeof(buf
), "%s", address
+ 5);
2577 } else if (get_wpa_status(ctx
->ifname
, "provisioning_sp", buf
,
2579 wpa_printf(MSG_INFO
, "Could not get provisioning Home SP FQDN from wpa_supplicant");
2583 ctx
->fqdn
= os_strdup(buf
);
2584 if (ctx
->fqdn
== NULL
)
2586 wpa_printf(MSG_INFO
, "Home SP FQDN for current credential: %s",
2588 os_snprintf(pps_fname_buf
, sizeof(pps_fname_buf
),
2589 "SP/%s/pps.xml", ctx
->fqdn
);
2590 pps_fname
= pps_fname_buf
;
2592 os_snprintf(ca_fname_buf
, sizeof(ca_fname_buf
), "SP/%s/ca.pem",
2594 ca_fname
= ca_fname_buf
;
2597 if (!os_file_exists(pps_fname
)) {
2598 wpa_printf(MSG_INFO
, "PPS file '%s' does not exist or is not accessible",
2602 wpa_printf(MSG_INFO
, "Using PPS file: %s", pps_fname
);
2604 if (ca_fname
&& !os_file_exists(ca_fname
)) {
2605 wpa_printf(MSG_INFO
, "CA file '%s' does not exist or is not accessible",
2609 wpa_printf(MSG_INFO
, "Using server trust root: %s", ca_fname
);
2610 ctx
->ca_fname
= ca_fname
;
2612 pps
= node_from_file(ctx
->xml
, pps_fname
);
2614 wpa_printf(MSG_INFO
, "Could not read PPS MO");
2620 node
= get_child_node(ctx
->xml
, pps
, "HomeSP/FQDN");
2622 wpa_printf(MSG_INFO
, "No HomeSP/FQDN found from PPS");
2625 tmp
= xml_node_get_text(ctx
->xml
, node
);
2627 wpa_printf(MSG_INFO
, "No HomeSP/FQDN text found from PPS");
2630 ctx
->fqdn
= os_strdup(tmp
);
2631 xml_node_get_text_free(ctx
->xml
, tmp
);
2633 wpa_printf(MSG_INFO
, "No FQDN known");
2638 node
= get_child_node(ctx
->xml
, pps
,
2639 "SubscriptionUpdate/UpdateMethod");
2642 tmp
= xml_node_get_text(ctx
->xml
, node
);
2643 if (tmp
&& os_strcasecmp(tmp
, "OMA-DM-ClientInitiated") == 0)
2648 wpa_printf(MSG_INFO
, "No UpdateMethod specified - assume SPP");
2652 get_user_pw(ctx
, pps
, "SubscriptionUpdate/UsernamePassword",
2653 &cred_username
, &cred_password
);
2655 wpa_printf(MSG_INFO
, "Using username: %s", cred_username
);
2657 wpa_printf(MSG_DEBUG
, "Using password: %s", cred_password
);
2659 if (cred_username
== NULL
&& cred_password
== NULL
&&
2660 get_child_node(ctx
->xml
, pps
, "Credential/DigitalCertificate")) {
2661 wpa_printf(MSG_INFO
, "Using client certificate");
2662 os_snprintf(client_cert_buf
, sizeof(client_cert_buf
),
2663 "SP/%s/client-cert.pem", ctx
->fqdn
);
2664 client_cert
= client_cert_buf
;
2665 os_snprintf(client_key_buf
, sizeof(client_key_buf
),
2666 "SP/%s/client-key.pem", ctx
->fqdn
);
2667 client_key
= client_key_buf
;
2668 ctx
->client_cert_present
= 1;
2671 node
= get_child_node(ctx
->xml
, pps
, "SubscriptionUpdate/URI");
2673 sub_rem_uri
= xml_node_get_text(ctx
->xml
, node
);
2675 (!address
|| os_strcmp(address
, sub_rem_uri
) != 0)) {
2676 wpa_printf(MSG_INFO
, "Override sub rem URI based on PPS: %s",
2678 address
= sub_rem_uri
;
2682 wpa_printf(MSG_INFO
, "Server URL not known");
2686 write_summary(ctx
, "Wait for IP address for subscriptiom remediation");
2687 wpa_printf(MSG_INFO
, "Wait for IP address before starting subscription remediation");
2689 if (wait_ip_addr(ctx
->ifname
, 15) < 0) {
2690 wpa_printf(MSG_INFO
, "Could not get IP address for WLAN - try connection anyway");
2694 spp_sub_rem(ctx
, address
, pps_fname
,
2695 client_cert
, client_key
,
2696 cred_username
, cred_password
, pps
);
2698 oma_dm_sub_rem(ctx
, address
, pps_fname
,
2699 client_cert
, client_key
,
2700 cred_username
, cred_password
, pps
);
2702 xml_node_get_text_free(ctx
->xml
, sub_rem_uri
);
2703 xml_node_get_text_free(ctx
->xml
, cred_username
);
2704 str_clear_free(cred_password
);
2705 xml_node_free(ctx
->xml
, pps
);
2710 static int cmd_pol_upd(struct hs20_osu_client
*ctx
, const char *address
,
2711 const char *pps_fname
, const char *ca_fname
)
2715 char pps_fname_buf
[300];
2716 char ca_fname_buf
[200];
2718 char *cred_username
= NULL
;
2719 char *cred_password
= NULL
;
2720 char client_cert_buf
[200];
2721 char *client_cert
= NULL
;
2722 char client_key_buf
[200];
2723 char *client_key
= NULL
;
2726 wpa_printf(MSG_INFO
, "Policy update requested");
2732 wpa_printf(MSG_INFO
, "Determining PPS file based on Home SP information");
2733 if (address
&& os_strncmp(address
, "fqdn=", 5) == 0) {
2734 wpa_printf(MSG_INFO
, "Use requested FQDN from command line");
2735 os_snprintf(buf
, sizeof(buf
), "%s", address
+ 5);
2737 } else if (get_wpa_status(ctx
->ifname
, "provisioning_sp", buf
,
2739 wpa_printf(MSG_INFO
, "Could not get provisioning Home SP FQDN from wpa_supplicant");
2743 ctx
->fqdn
= os_strdup(buf
);
2744 if (ctx
->fqdn
== NULL
)
2746 wpa_printf(MSG_INFO
, "Home SP FQDN for current credential: %s",
2748 os_snprintf(pps_fname_buf
, sizeof(pps_fname_buf
),
2749 "SP/%s/pps.xml", ctx
->fqdn
);
2750 pps_fname
= pps_fname_buf
;
2752 res
= os_snprintf(ca_fname_buf
, sizeof(ca_fname_buf
),
2753 "SP/%s/ca.pem", buf
);
2754 if (os_snprintf_error(sizeof(ca_fname_buf
), res
)) {
2759 ca_fname
= ca_fname_buf
;
2762 if (!os_file_exists(pps_fname
)) {
2763 wpa_printf(MSG_INFO
, "PPS file '%s' does not exist or is not accessible",
2767 wpa_printf(MSG_INFO
, "Using PPS file: %s", pps_fname
);
2769 if (ca_fname
&& !os_file_exists(ca_fname
)) {
2770 wpa_printf(MSG_INFO
, "CA file '%s' does not exist or is not accessible",
2774 wpa_printf(MSG_INFO
, "Using server trust root: %s", ca_fname
);
2775 ctx
->ca_fname
= ca_fname
;
2777 pps
= node_from_file(ctx
->xml
, pps_fname
);
2779 wpa_printf(MSG_INFO
, "Could not read PPS MO");
2785 node
= get_child_node(ctx
->xml
, pps
, "HomeSP/FQDN");
2787 wpa_printf(MSG_INFO
, "No HomeSP/FQDN found from PPS");
2790 tmp
= xml_node_get_text(ctx
->xml
, node
);
2792 wpa_printf(MSG_INFO
, "No HomeSP/FQDN text found from PPS");
2795 ctx
->fqdn
= os_strdup(tmp
);
2796 xml_node_get_text_free(ctx
->xml
, tmp
);
2798 wpa_printf(MSG_INFO
, "No FQDN known");
2803 node
= get_child_node(ctx
->xml
, pps
,
2804 "Policy/PolicyUpdate/UpdateMethod");
2807 tmp
= xml_node_get_text(ctx
->xml
, node
);
2808 if (tmp
&& os_strcasecmp(tmp
, "OMA-DM-ClientInitiated") == 0)
2813 wpa_printf(MSG_INFO
, "No UpdateMethod specified - assume SPP");
2817 get_user_pw(ctx
, pps
, "Policy/PolicyUpdate/UsernamePassword",
2818 &cred_username
, &cred_password
);
2820 wpa_printf(MSG_INFO
, "Using username: %s", cred_username
);
2822 wpa_printf(MSG_DEBUG
, "Using password: %s", cred_password
);
2824 if (cred_username
== NULL
&& cred_password
== NULL
&&
2825 get_child_node(ctx
->xml
, pps
, "Credential/DigitalCertificate")) {
2826 wpa_printf(MSG_INFO
, "Using client certificate");
2827 os_snprintf(client_cert_buf
, sizeof(client_cert_buf
),
2828 "SP/%s/client-cert.pem", ctx
->fqdn
);
2829 client_cert
= client_cert_buf
;
2830 os_snprintf(client_key_buf
, sizeof(client_key_buf
),
2831 "SP/%s/client-key.pem", ctx
->fqdn
);
2832 client_key
= client_key_buf
;
2836 node
= get_child_node(ctx
->xml
, pps
, "Policy/PolicyUpdate/URI");
2838 uri
= xml_node_get_text(ctx
->xml
, node
);
2839 wpa_printf(MSG_INFO
, "URI based on PPS: %s", uri
);
2844 wpa_printf(MSG_INFO
, "Server URL not known");
2849 spp_pol_upd(ctx
, address
, pps_fname
,
2850 client_cert
, client_key
,
2851 cred_username
, cred_password
, pps
);
2853 oma_dm_pol_upd(ctx
, address
, pps_fname
,
2854 client_cert
, client_key
,
2855 cred_username
, cred_password
, pps
);
2857 xml_node_get_text_free(ctx
->xml
, uri
);
2858 xml_node_get_text_free(ctx
->xml
, cred_username
);
2859 str_clear_free(cred_password
);
2860 xml_node_free(ctx
->xml
, pps
);
2866 static char * get_hostname(const char *url
)
2868 const char *pos
, *end
, *end2
;
2874 pos
= os_strchr(url
, '/');
2882 end
= os_strchr(pos
, '/');
2883 end2
= os_strchr(pos
, ':');
2884 if ((end
&& end2
&& end2
< end
) || (!end
&& end2
))
2896 ret
= os_malloc(end
- pos
+ 2);
2900 os_memcpy(ret
, pos
, end
- pos
+ 1);
2901 ret
[end
- pos
+ 1] = '\0';
2907 static int osu_cert_cb(void *_ctx
, struct http_cert
*cert
)
2909 struct hs20_osu_client
*ctx
= _ctx
;
2914 wpa_printf(MSG_INFO
, "osu_cert_cb(osu_cert_validation=%d, url=%s)",
2915 !ctx
->no_osu_cert_validation
, ctx
->server_url
);
2917 host
= get_hostname(ctx
->server_url
);
2919 for (i
= 0; i
< ctx
->server_dnsname_count
; i
++)
2920 os_free(ctx
->server_dnsname
[i
]);
2921 os_free(ctx
->server_dnsname
);
2922 ctx
->server_dnsname
= os_calloc(cert
->num_dnsname
, sizeof(char *));
2923 ctx
->server_dnsname_count
= 0;
2926 for (i
= 0; i
< cert
->num_dnsname
; i
++) {
2927 if (ctx
->server_dnsname
) {
2928 ctx
->server_dnsname
[ctx
->server_dnsname_count
] =
2929 os_strdup(cert
->dnsname
[i
]);
2930 if (ctx
->server_dnsname
[ctx
->server_dnsname_count
])
2931 ctx
->server_dnsname_count
++;
2933 if (host
&& os_strcasecmp(host
, cert
->dnsname
[i
]) == 0)
2935 wpa_printf(MSG_INFO
, "dNSName '%s'", cert
->dnsname
[i
]);
2938 if (host
&& !found
) {
2939 wpa_printf(MSG_INFO
, "Server name from URL (%s) did not match any dNSName - abort connection",
2941 write_result(ctx
, "Server name from URL (%s) did not match any dNSName - abort connection",
2949 for (i
= 0; i
< cert
->num_othername
; i
++) {
2950 if (os_strcmp(cert
->othername
[i
].oid
,
2951 "1.3.6.1.4.1.40808.1.1.1") == 0) {
2952 wpa_hexdump_ascii(MSG_INFO
,
2953 "id-wfa-hotspot-friendlyName",
2954 cert
->othername
[i
].data
,
2955 cert
->othername
[i
].len
);
2959 for (j
= 0; !ctx
->no_osu_cert_validation
&&
2960 j
< ctx
->friendly_name_count
; j
++) {
2962 for (i
= 0; i
< cert
->num_othername
; i
++) {
2963 if (os_strcmp(cert
->othername
[i
].oid
,
2964 "1.3.6.1.4.1.40808.1.1.1") != 0)
2966 if (cert
->othername
[i
].len
< 3)
2968 if (os_strncasecmp((char *) cert
->othername
[i
].data
,
2969 ctx
->friendly_name
[j
].lang
, 3) != 0)
2971 if (os_strncmp((char *) cert
->othername
[i
].data
+ 3,
2972 ctx
->friendly_name
[j
].text
,
2973 cert
->othername
[i
].len
- 3) == 0) {
2980 wpa_printf(MSG_INFO
, "No friendly name match found for '[%s]%s'",
2981 ctx
->friendly_name
[j
].lang
,
2982 ctx
->friendly_name
[j
].text
);
2983 write_result(ctx
, "No friendly name match found for '[%s]%s'",
2984 ctx
->friendly_name
[j
].lang
,
2985 ctx
->friendly_name
[j
].text
);
2990 for (i
= 0; i
< cert
->num_logo
; i
++) {
2991 struct http_logo
*logo
= &cert
->logo
[i
];
2993 wpa_printf(MSG_INFO
, "logo hash alg %s uri '%s'",
2994 logo
->alg_oid
, logo
->uri
);
2995 wpa_hexdump_ascii(MSG_INFO
, "hashValue",
2996 logo
->hash
, logo
->hash_len
);
2999 for (j
= 0; !ctx
->no_osu_cert_validation
&& j
< ctx
->icon_count
; j
++) {
3001 char *name
= ctx
->icon_filename
[j
];
3002 size_t name_len
= os_strlen(name
);
3004 wpa_printf(MSG_INFO
,
3005 "[%i] Looking for icon file name '%s' match",
3007 for (i
= 0; i
< cert
->num_logo
; i
++) {
3008 struct http_logo
*logo
= &cert
->logo
[i
];
3009 size_t uri_len
= os_strlen(logo
->uri
);
3012 wpa_printf(MSG_INFO
,
3013 "[%i] Comparing to '%s' uri_len=%d name_len=%d",
3014 i
, logo
->uri
, (int) uri_len
, (int) name_len
);
3015 if (uri_len
< 1 + name_len
) {
3016 wpa_printf(MSG_INFO
, "URI Length is too short");
3019 pos
= &logo
->uri
[uri_len
- name_len
- 1];
3023 if (os_strcmp(pos
, name
) == 0) {
3030 wpa_printf(MSG_INFO
, "No icon filename match found for '%s'",
3033 "No icon filename match found for '%s'",
3039 for (j
= 0; !ctx
->no_osu_cert_validation
&& j
< ctx
->icon_count
; j
++) {
3042 for (i
= 0; i
< cert
->num_logo
; i
++) {
3043 struct http_logo
*logo
= &cert
->logo
[i
];
3045 if (logo
->hash_len
!= 32) {
3046 wpa_printf(MSG_INFO
,
3047 "[%i][%i] Icon hash length invalid (should be 32): %d",
3048 j
, i
, (int) logo
->hash_len
);
3051 if (os_memcmp(logo
->hash
, ctx
->icon_hash
[j
], 32) == 0) {
3056 wpa_printf(MSG_DEBUG
,
3057 "[%u][%u] Icon hash did not match", j
, i
);
3058 wpa_hexdump_ascii(MSG_DEBUG
, "logo->hash",
3060 wpa_hexdump_ascii(MSG_DEBUG
, "ctx->icon_hash[j]",
3061 ctx
->icon_hash
[j
], 32);
3065 wpa_printf(MSG_INFO
,
3066 "No icon hash match (by hash) found");
3068 "No icon hash match (by hash) found");
3077 static int init_ctx(struct hs20_osu_client
*ctx
)
3079 xml_node_t
*devinfo
, *devid
;
3081 os_memset(ctx
, 0, sizeof(*ctx
));
3082 ctx
->ifname
= "wlan0";
3083 ctx
->xml
= xml_node_init_ctx(ctx
, NULL
);
3084 if (ctx
->xml
== NULL
)
3087 devinfo
= node_from_file(ctx
->xml
, "devinfo.xml");
3089 devid
= get_node(ctx
->xml
, devinfo
, "DevId");
3091 char *tmp
= xml_node_get_text(ctx
->xml
, devid
);
3094 ctx
->devid
= os_strdup(tmp
);
3095 xml_node_get_text_free(ctx
->xml
, tmp
);
3098 xml_node_free(ctx
->xml
, devinfo
);
3101 ctx
->http
= http_init_ctx(ctx
, ctx
->xml
);
3102 if (ctx
->http
== NULL
) {
3103 xml_node_deinit_ctx(ctx
->xml
);
3106 http_ocsp_set(ctx
->http
, 2);
3107 http_set_cert_cb(ctx
->http
, osu_cert_cb
, ctx
);
3113 static void deinit_ctx(struct hs20_osu_client
*ctx
)
3117 http_deinit_ctx(ctx
->http
);
3118 xml_node_deinit_ctx(ctx
->xml
);
3120 os_free(ctx
->server_url
);
3121 os_free(ctx
->devid
);
3123 for (i
= 0; i
< ctx
->server_dnsname_count
; i
++)
3124 os_free(ctx
->server_dnsname
[i
]);
3125 os_free(ctx
->server_dnsname
);
3129 static void check_workarounds(struct hs20_osu_client
*ctx
)
3133 unsigned long int val
= 0;
3135 f
= fopen("hs20-osu-client.workarounds", "r");
3139 if (fgets(buf
, sizeof(buf
), f
))
3140 val
= strtoul(buf
, NULL
, 16);
3145 wpa_printf(MSG_INFO
, "Workarounds enabled: 0x%lx", val
);
3146 ctx
->workarounds
= val
;
3147 if (ctx
->workarounds
& WORKAROUND_OCSP_OPTIONAL
)
3148 http_ocsp_set(ctx
->http
, 1);
3153 static void usage(void)
3155 printf("usage: hs20-osu-client [-dddqqKtT] [-S<station ifname>] \\\n"
3156 " [-w<wpa_supplicant ctrl_iface dir>] "
3157 "[-r<result file>] [-f<debug file>] \\\n"
3158 " [-s<summary file>] \\\n"
3159 " [-x<spp.xsd file name>] \\\n"
3160 " <command> [arguments..]\n"
3162 "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n"
3163 "- to_tnds2 <XML MO> <XML MO in TNDS format (Path) "
3165 "- from_tnds <XML MO in TNDS format> <XML MO>\n"
3166 "- set_pps <PerProviderSubscription XML file name>\n"
3167 "- get_fqdn <PerProviderSubscription XML file name>\n"
3168 "- pol_upd [Server URL] [PPS] [CA cert]\n"
3169 "- sub_rem <Server URL> [PPS] [CA cert]\n"
3170 "- prov <Server URL> [CA cert]\n"
3171 "- oma_dm_prov <Server URL> [CA cert]\n"
3172 "- sim_prov <Server URL> [CA cert]\n"
3173 "- oma_dm_sim_prov <Server URL> [CA cert]\n"
3174 "- signup [CA cert]\n"
3175 "- dl_osu_ca <PPS> <CA file>\n"
3176 "- dl_polupd_ca <PPS> <CA file>\n"
3177 "- dl_aaa_ca <PPS> <CA file>\n"
3179 "- parse_cert <X.509 certificate (DER)>\n"
3180 "- osu_select <OSU info directory> [CA cert]\n");
3184 int main(int argc
, char *argv
[])
3186 struct hs20_osu_client ctx
;
3189 int no_prod_assoc
= 0;
3190 const char *friendly_name
= NULL
;
3191 const char *wpa_debug_file_path
= NULL
;
3192 extern char *wpas_ctrl_path
;
3193 extern int wpa_debug_level
;
3194 extern int wpa_debug_show_keys
;
3195 extern int wpa_debug_timestamp
;
3197 if (init_ctx(&ctx
) < 0)
3201 c
= getopt(argc
, argv
, "df:hKNo:O:qr:s:S:tTw:x:");
3206 if (wpa_debug_level
> 0)
3210 wpa_debug_file_path
= optarg
;
3213 wpa_debug_show_keys
++;
3219 ctx
.osu_ssid
= optarg
;
3222 friendly_name
= optarg
;
3228 ctx
.result_file
= optarg
;
3231 ctx
.summary_file
= optarg
;
3234 ctx
.ifname
= optarg
;
3237 wpa_debug_timestamp
++;
3243 wpas_ctrl_path
= optarg
;
3246 spp_xsd_fname
= optarg
;
3256 if (argc
- optind
< 1) {
3261 wpa_debug_open_file(wpa_debug_file_path
);
3265 #endif /* __linux__ */
3267 if (ctx
.result_file
)
3268 unlink(ctx
.result_file
);
3269 wpa_printf(MSG_DEBUG
, "===[hs20-osu-client START - command: %s ]======"
3270 "================", argv
[optind
]);
3271 check_workarounds(&ctx
);
3273 if (strcmp(argv
[optind
], "to_tnds") == 0) {
3274 if (argc
- optind
< 2) {
3278 cmd_to_tnds(&ctx
, argv
[optind
+ 1], argv
[optind
+ 2],
3279 argc
> optind
+ 3 ? argv
[optind
+ 3] : NULL
,
3281 } else if (strcmp(argv
[optind
], "to_tnds2") == 0) {
3282 if (argc
- optind
< 2) {
3286 cmd_to_tnds(&ctx
, argv
[optind
+ 1], argv
[optind
+ 2],
3287 argc
> optind
+ 3 ? argv
[optind
+ 3] : NULL
,
3289 } else if (strcmp(argv
[optind
], "from_tnds") == 0) {
3290 if (argc
- optind
< 2) {
3294 cmd_from_tnds(&ctx
, argv
[optind
+ 1], argv
[optind
+ 2]);
3295 } else if (strcmp(argv
[optind
], "sub_rem") == 0) {
3296 if (argc
- optind
< 2) {
3300 ret
= cmd_sub_rem(&ctx
, argv
[optind
+ 1],
3301 argc
> optind
+ 2 ? argv
[optind
+ 2] : NULL
,
3302 argc
> optind
+ 3 ? argv
[optind
+ 3] : NULL
);
3303 } else if (strcmp(argv
[optind
], "pol_upd") == 0) {
3304 ret
= cmd_pol_upd(&ctx
,
3305 argc
> optind
+ 1 ? argv
[optind
+ 1] : NULL
,
3306 argc
> optind
+ 2 ? argv
[optind
+ 2] : NULL
,
3307 argc
> optind
+ 3 ? argv
[optind
+ 3] : NULL
);
3308 } else if (strcmp(argv
[optind
], "prov") == 0) {
3309 if (argc
- optind
< 2) {
3313 ctx
.ca_fname
= argv
[optind
+ 2];
3314 wpa_printf(MSG_DEBUG
, "Calling cmd_prov from main");
3315 cmd_prov(&ctx
, argv
[optind
+ 1]);
3316 } else if (strcmp(argv
[optind
], "sim_prov") == 0) {
3317 if (argc
- optind
< 2) {
3321 ctx
.ca_fname
= argv
[optind
+ 2];
3322 cmd_sim_prov(&ctx
, argv
[optind
+ 1]);
3323 } else if (strcmp(argv
[optind
], "dl_osu_ca") == 0) {
3324 if (argc
- optind
< 2) {
3328 cmd_dl_osu_ca(&ctx
, argv
[optind
+ 1], argv
[optind
+ 2]);
3329 } else if (strcmp(argv
[optind
], "dl_polupd_ca") == 0) {
3330 if (argc
- optind
< 2) {
3334 cmd_dl_polupd_ca(&ctx
, argv
[optind
+ 1], argv
[optind
+ 2]);
3335 } else if (strcmp(argv
[optind
], "dl_aaa_ca") == 0) {
3336 if (argc
- optind
< 2) {
3340 cmd_dl_aaa_ca(&ctx
, argv
[optind
+ 1], argv
[optind
+ 2]);
3341 } else if (strcmp(argv
[optind
], "osu_select") == 0) {
3342 if (argc
- optind
< 2) {
3346 ctx
.ca_fname
= argc
> optind
+ 2 ? argv
[optind
+ 2] : NULL
;
3347 cmd_osu_select(&ctx
, argv
[optind
+ 1], 2, 1, NULL
);
3348 } else if (strcmp(argv
[optind
], "signup") == 0) {
3349 ctx
.ca_fname
= argc
> optind
+ 1 ? argv
[optind
+ 1] : NULL
;
3350 ret
= cmd_signup(&ctx
, no_prod_assoc
, friendly_name
);
3351 } else if (strcmp(argv
[optind
], "set_pps") == 0) {
3352 if (argc
- optind
< 2) {
3356 cmd_set_pps(&ctx
, argv
[optind
+ 1]);
3357 } else if (strcmp(argv
[optind
], "get_fqdn") == 0) {
3358 if (argc
- optind
< 1) {
3362 ret
= cmd_get_fqdn(&ctx
, argv
[optind
+ 1]);
3363 } else if (strcmp(argv
[optind
], "oma_dm_prov") == 0) {
3364 if (argc
- optind
< 2) {
3368 ctx
.ca_fname
= argv
[optind
+ 2];
3369 cmd_oma_dm_prov(&ctx
, argv
[optind
+ 1]);
3370 } else if (strcmp(argv
[optind
], "oma_dm_sim_prov") == 0) {
3371 if (argc
- optind
< 2) {
3375 ctx
.ca_fname
= argv
[optind
+ 2];
3376 if (cmd_oma_dm_sim_prov(&ctx
, argv
[optind
+ 1]) < 0) {
3377 write_summary(&ctx
, "Failed to complete OMA DM SIM provisioning");
3380 } else if (strcmp(argv
[optind
], "oma_dm_add") == 0) {
3381 if (argc
- optind
< 2) {
3385 cmd_oma_dm_add(&ctx
, argv
[optind
+ 1], argv
[optind
+ 2]);
3386 } else if (strcmp(argv
[optind
], "oma_dm_replace") == 0) {
3387 if (argc
- optind
< 2) {
3391 cmd_oma_dm_replace(&ctx
, argv
[optind
+ 1], argv
[optind
+ 2]);
3392 } else if (strcmp(argv
[optind
], "est_csr") == 0) {
3393 if (argc
- optind
< 2) {
3397 mkdir("Cert", S_IRWXU
);
3398 est_build_csr(&ctx
, argv
[optind
+ 1]);
3399 } else if (strcmp(argv
[optind
], "browser") == 0) {
3402 if (argc
- optind
< 2) {
3407 wpa_printf(MSG_INFO
, "Launch web browser to URL %s",
3409 ret
= hs20_web_browser(argv
[optind
+ 1], ctx
.ignore_tls
);
3410 wpa_printf(MSG_INFO
, "Web browser result: %d", ret
);
3411 } else if (strcmp(argv
[optind
], "parse_cert") == 0) {
3412 if (argc
- optind
< 2) {
3417 wpa_debug_level
= MSG_MSGDUMP
;
3418 http_parse_x509_certificate(ctx
.http
, argv
[optind
+ 1]);
3419 wpa_debug_level
= MSG_INFO
;
3421 wpa_printf(MSG_INFO
, "Unknown command '%s'", argv
[optind
]);
3425 wpa_printf(MSG_DEBUG
,
3426 "===[hs20-osu-client END ]======================");
3428 wpa_debug_close_file();