Cumulative patch from commit 39b420f7b12b152f879d1bb8a5267b9a838e04dc
39b420f HS 2.0R2: Add parse_cert command for debugging purposes
c0d701a HS 2.0R2: Add OSU client implementation
d2bb2b4 Add os_file_exists()
1cb3eda HS 2.0R2: Add wrapper functions for libcurl
2cb8f96 HS 2.0R2: Add wrapper functions for libxml2
da4ec2c HS 2.0R2: Add wrapper for system browser
db3ca20 HS 2.0R2: Add wrapper for wpadebug browser on Android
2a10e57 HS 2.0R2: Add wrapper for Android browser for user interaction
d0b79d7 HS 2.0R2: Add wrapper functions for WebKit
91728f2 Add wpa_ctrl helper functions for upper level functionality
b4c26ef Clean up hostapd_config_fill() parsers
b2e32cd Fix memory leaks on wpa_config_parse_string() error paths
a0b728b Simplify hostapd_config_fill() error reporting
599f40d Remove extra indentation level from hostapd_config_fill()
Change-Id: I17b7089210da7e083c9fe5814e79cef99bbee80b
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index b6f1d1b..f018f96 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1679,6 +1679,7 @@
str = wpa_config_parse_string(pos, &slen);
if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
+ os_free(str);
return -1;
}
@@ -1837,1389 +1838,1308 @@
struct hostapd_bss_config *bss,
char *buf, char *pos, int line)
{
- int errors = 0;
-
- {
- if (os_strcmp(buf, "interface") == 0) {
- os_strlcpy(conf->bss[0]->iface, pos,
- sizeof(conf->bss[0]->iface));
- } else if (os_strcmp(buf, "bridge") == 0) {
- os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
- } else if (os_strcmp(buf, "vlan_bridge") == 0) {
- os_strlcpy(bss->vlan_bridge, pos,
- sizeof(bss->vlan_bridge));
- } else if (os_strcmp(buf, "wds_bridge") == 0) {
- os_strlcpy(bss->wds_bridge, pos,
- sizeof(bss->wds_bridge));
- } else if (os_strcmp(buf, "driver") == 0) {
- int j;
- /* clear to get error below if setting is invalid */
- conf->driver = NULL;
- for (j = 0; wpa_drivers[j]; j++) {
- if (os_strcmp(pos, wpa_drivers[j]->name) == 0)
- {
- conf->driver = wpa_drivers[j];
- break;
- }
+ if (os_strcmp(buf, "interface") == 0) {
+ os_strlcpy(conf->bss[0]->iface, pos,
+ sizeof(conf->bss[0]->iface));
+ } else if (os_strcmp(buf, "bridge") == 0) {
+ os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
+ } else if (os_strcmp(buf, "vlan_bridge") == 0) {
+ os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
+ } else if (os_strcmp(buf, "wds_bridge") == 0) {
+ os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
+ } else if (os_strcmp(buf, "driver") == 0) {
+ int j;
+ /* clear to get error below if setting is invalid */
+ conf->driver = NULL;
+ for (j = 0; wpa_drivers[j]; j++) {
+ if (os_strcmp(pos, wpa_drivers[j]->name) == 0) {
+ conf->driver = wpa_drivers[j];
+ break;
}
- if (conf->driver == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: invalid/"
- "unknown driver '%s'", line, pos);
- errors++;
- }
- } else if (os_strcmp(buf, "debug") == 0) {
- wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' "
- "configuration variable is not used "
- "anymore", line);
- } else if (os_strcmp(buf, "logger_syslog_level") == 0) {
- bss->logger_syslog_level = atoi(pos);
- } else if (os_strcmp(buf, "logger_stdout_level") == 0) {
- bss->logger_stdout_level = atoi(pos);
- } else if (os_strcmp(buf, "logger_syslog") == 0) {
- bss->logger_syslog = atoi(pos);
- } else if (os_strcmp(buf, "logger_stdout") == 0) {
- bss->logger_stdout = atoi(pos);
- } else if (os_strcmp(buf, "dump_file") == 0) {
- wpa_printf(MSG_INFO, "Line %d: DEPRECATED: 'dump_file' configuration variable is not used anymore",
- line);
- } else if (os_strcmp(buf, "ssid") == 0) {
- bss->ssid.ssid_len = os_strlen(pos);
- if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN ||
- bss->ssid.ssid_len < 1) {
- wpa_printf(MSG_ERROR, "Line %d: invalid SSID "
- "'%s'", line, pos);
- errors++;
- } else {
- os_memcpy(bss->ssid.ssid, pos,
- bss->ssid.ssid_len);
- bss->ssid.ssid_set = 1;
- }
- } else if (os_strcmp(buf, "ssid2") == 0) {
- size_t slen;
- char *str = wpa_config_parse_string(pos, &slen);
- if (str == NULL || slen < 1 ||
- slen > HOSTAPD_MAX_SSID_LEN) {
- wpa_printf(MSG_ERROR, "Line %d: invalid SSID "
- "'%s'", line, pos);
- errors++;
- } else {
- os_memcpy(bss->ssid.ssid, str, slen);
- bss->ssid.ssid_len = slen;
- bss->ssid.ssid_set = 1;
- }
+ }
+ if (conf->driver == NULL) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid/unknown driver '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "debug") == 0) {
+ wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' configuration variable is not used anymore",
+ line);
+ } else if (os_strcmp(buf, "logger_syslog_level") == 0) {
+ bss->logger_syslog_level = atoi(pos);
+ } else if (os_strcmp(buf, "logger_stdout_level") == 0) {
+ bss->logger_stdout_level = atoi(pos);
+ } else if (os_strcmp(buf, "logger_syslog") == 0) {
+ bss->logger_syslog = atoi(pos);
+ } else if (os_strcmp(buf, "logger_stdout") == 0) {
+ bss->logger_stdout = atoi(pos);
+ } else if (os_strcmp(buf, "dump_file") == 0) {
+ wpa_printf(MSG_INFO, "Line %d: DEPRECATED: 'dump_file' configuration variable is not used anymore",
+ line);
+ } else if (os_strcmp(buf, "ssid") == 0) {
+ bss->ssid.ssid_len = os_strlen(pos);
+ if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN ||
+ bss->ssid.ssid_len < 1) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+ line, pos);
+ return 1;
+ }
+ os_memcpy(bss->ssid.ssid, pos, bss->ssid.ssid_len);
+ bss->ssid.ssid_set = 1;
+ } else if (os_strcmp(buf, "ssid2") == 0) {
+ size_t slen;
+ char *str = wpa_config_parse_string(pos, &slen);
+ if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+ line, pos);
os_free(str);
- } else if (os_strcmp(buf, "utf8_ssid") == 0) {
- bss->ssid.utf8_ssid = atoi(pos) > 0;
- } else if (os_strcmp(buf, "macaddr_acl") == 0) {
- bss->macaddr_acl = atoi(pos);
- if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
- bss->macaddr_acl != DENY_UNLESS_ACCEPTED &&
- bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
- wpa_printf(MSG_ERROR, "Line %d: unknown "
- "macaddr_acl %d",
- line, bss->macaddr_acl);
- }
- } else if (os_strcmp(buf, "accept_mac_file") == 0) {
- if (hostapd_config_read_maclist(pos, &bss->accept_mac,
- &bss->num_accept_mac))
- {
- wpa_printf(MSG_ERROR, "Line %d: Failed to "
- "read accept_mac_file '%s'",
- line, pos);
- errors++;
- }
- } else if (os_strcmp(buf, "deny_mac_file") == 0) {
- if (hostapd_config_read_maclist(pos, &bss->deny_mac,
- &bss->num_deny_mac)) {
- wpa_printf(MSG_ERROR, "Line %d: Failed to "
- "read deny_mac_file '%s'",
- line, pos);
- errors++;
- }
- } else if (os_strcmp(buf, "wds_sta") == 0) {
- bss->wds_sta = atoi(pos);
- } else if (os_strcmp(buf, "start_disabled") == 0) {
- bss->start_disabled = atoi(pos);
- } else if (os_strcmp(buf, "ap_isolate") == 0) {
- bss->isolate = atoi(pos);
- } else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
- bss->ap_max_inactivity = atoi(pos);
- } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
- bss->skip_inactivity_poll = atoi(pos);
- } else if (os_strcmp(buf, "country_code") == 0) {
- os_memcpy(conf->country, pos, 2);
- /* FIX: make this configurable */
- conf->country[2] = ' ';
- } else if (os_strcmp(buf, "ieee80211d") == 0) {
- conf->ieee80211d = atoi(pos);
- } else if (os_strcmp(buf, "ieee80211h") == 0) {
- conf->ieee80211h = atoi(pos);
- } else if (os_strcmp(buf, "ieee8021x") == 0) {
- bss->ieee802_1x = atoi(pos);
- } else if (os_strcmp(buf, "eapol_version") == 0) {
- bss->eapol_version = atoi(pos);
- if (bss->eapol_version < 1 ||
- bss->eapol_version > 2) {
- wpa_printf(MSG_ERROR, "Line %d: invalid EAPOL "
- "version (%d): '%s'.",
- line, bss->eapol_version, pos);
- errors++;
- } else
- wpa_printf(MSG_DEBUG, "eapol_version=%d",
- bss->eapol_version);
+ return 1;
+ }
+ os_memcpy(bss->ssid.ssid, str, slen);
+ bss->ssid.ssid_len = slen;
+ bss->ssid.ssid_set = 1;
+ os_free(str);
+ } else if (os_strcmp(buf, "utf8_ssid") == 0) {
+ bss->ssid.utf8_ssid = atoi(pos) > 0;
+ } else if (os_strcmp(buf, "macaddr_acl") == 0) {
+ bss->macaddr_acl = atoi(pos);
+ if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
+ bss->macaddr_acl != DENY_UNLESS_ACCEPTED &&
+ bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d",
+ line, bss->macaddr_acl);
+ }
+ } else if (os_strcmp(buf, "accept_mac_file") == 0) {
+ if (hostapd_config_read_maclist(pos, &bss->accept_mac,
+ &bss->num_accept_mac)) {
+ wpa_printf(MSG_ERROR, "Line %d: Failed to read accept_mac_file '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "deny_mac_file") == 0) {
+ if (hostapd_config_read_maclist(pos, &bss->deny_mac,
+ &bss->num_deny_mac)) {
+ wpa_printf(MSG_ERROR, "Line %d: Failed to read deny_mac_file '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wds_sta") == 0) {
+ bss->wds_sta = atoi(pos);
+ } else if (os_strcmp(buf, "start_disabled") == 0) {
+ bss->start_disabled = atoi(pos);
+ } else if (os_strcmp(buf, "ap_isolate") == 0) {
+ bss->isolate = atoi(pos);
+ } else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
+ bss->ap_max_inactivity = atoi(pos);
+ } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
+ bss->skip_inactivity_poll = atoi(pos);
+ } else if (os_strcmp(buf, "country_code") == 0) {
+ os_memcpy(conf->country, pos, 2);
+ /* FIX: make this configurable */
+ conf->country[2] = ' ';
+ } else if (os_strcmp(buf, "ieee80211d") == 0) {
+ conf->ieee80211d = atoi(pos);
+ } else if (os_strcmp(buf, "ieee80211h") == 0) {
+ conf->ieee80211h = atoi(pos);
+ } else if (os_strcmp(buf, "ieee8021x") == 0) {
+ bss->ieee802_1x = atoi(pos);
+ } else if (os_strcmp(buf, "eapol_version") == 0) {
+ bss->eapol_version = atoi(pos);
+ if (bss->eapol_version < 1 || bss->eapol_version > 2) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid EAPOL version (%d): '%s'.",
+ line, bss->eapol_version, pos);
+ return 1;
+ }
+ wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version);
#ifdef EAP_SERVER
- } else if (os_strcmp(buf, "eap_authenticator") == 0) {
- bss->eap_server = atoi(pos);
- wpa_printf(MSG_ERROR, "Line %d: obsolete "
- "eap_authenticator used; this has been "
- "renamed to eap_server", line);
- } else if (os_strcmp(buf, "eap_server") == 0) {
- bss->eap_server = atoi(pos);
- } else if (os_strcmp(buf, "eap_user_file") == 0) {
- if (hostapd_config_read_eap_user(pos, bss))
- errors++;
- } else if (os_strcmp(buf, "ca_cert") == 0) {
- os_free(bss->ca_cert);
- bss->ca_cert = os_strdup(pos);
- } else if (os_strcmp(buf, "server_cert") == 0) {
- os_free(bss->server_cert);
- bss->server_cert = os_strdup(pos);
- } else if (os_strcmp(buf, "private_key") == 0) {
- os_free(bss->private_key);
- bss->private_key = os_strdup(pos);
- } else if (os_strcmp(buf, "private_key_passwd") == 0) {
- os_free(bss->private_key_passwd);
- bss->private_key_passwd = os_strdup(pos);
- } else if (os_strcmp(buf, "check_crl") == 0) {
- bss->check_crl = atoi(pos);
- } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
- os_free(bss->ocsp_stapling_response);
- bss->ocsp_stapling_response = os_strdup(pos);
- } else if (os_strcmp(buf, "dh_file") == 0) {
- os_free(bss->dh_file);
- bss->dh_file = os_strdup(pos);
- } else if (os_strcmp(buf, "fragment_size") == 0) {
- bss->fragment_size = atoi(pos);
+ } else if (os_strcmp(buf, "eap_authenticator") == 0) {
+ bss->eap_server = atoi(pos);
+ wpa_printf(MSG_ERROR, "Line %d: obsolete eap_authenticator used; this has been renamed to eap_server", line);
+ } else if (os_strcmp(buf, "eap_server") == 0) {
+ bss->eap_server = atoi(pos);
+ } else if (os_strcmp(buf, "eap_user_file") == 0) {
+ if (hostapd_config_read_eap_user(pos, bss))
+ return 1;
+ } else if (os_strcmp(buf, "ca_cert") == 0) {
+ os_free(bss->ca_cert);
+ bss->ca_cert = os_strdup(pos);
+ } else if (os_strcmp(buf, "server_cert") == 0) {
+ os_free(bss->server_cert);
+ bss->server_cert = os_strdup(pos);
+ } else if (os_strcmp(buf, "private_key") == 0) {
+ os_free(bss->private_key);
+ bss->private_key = os_strdup(pos);
+ } else if (os_strcmp(buf, "private_key_passwd") == 0) {
+ os_free(bss->private_key_passwd);
+ bss->private_key_passwd = os_strdup(pos);
+ } else if (os_strcmp(buf, "check_crl") == 0) {
+ bss->check_crl = atoi(pos);
+ } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
+ os_free(bss->ocsp_stapling_response);
+ bss->ocsp_stapling_response = os_strdup(pos);
+ } else if (os_strcmp(buf, "dh_file") == 0) {
+ os_free(bss->dh_file);
+ bss->dh_file = os_strdup(pos);
+ } else if (os_strcmp(buf, "fragment_size") == 0) {
+ bss->fragment_size = atoi(pos);
#ifdef EAP_SERVER_FAST
- } else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) {
- os_free(bss->pac_opaque_encr_key);
- bss->pac_opaque_encr_key = os_malloc(16);
- if (bss->pac_opaque_encr_key == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: No memory for "
- "pac_opaque_encr_key", line);
- errors++;
- } else if (hexstr2bin(pos, bss->pac_opaque_encr_key,
- 16)) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "pac_opaque_encr_key", line);
- errors++;
- }
- } else if (os_strcmp(buf, "eap_fast_a_id") == 0) {
- size_t idlen = os_strlen(pos);
- if (idlen & 1) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "eap_fast_a_id", line);
- errors++;
- } else {
- os_free(bss->eap_fast_a_id);
- bss->eap_fast_a_id = os_malloc(idlen / 2);
- if (bss->eap_fast_a_id == NULL ||
- hexstr2bin(pos, bss->eap_fast_a_id,
- idlen / 2)) {
- wpa_printf(MSG_ERROR, "Line %d: "
- "Failed to parse "
- "eap_fast_a_id", line);
- errors++;
- } else
- bss->eap_fast_a_id_len = idlen / 2;
- }
- } else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) {
- os_free(bss->eap_fast_a_id_info);
- bss->eap_fast_a_id_info = os_strdup(pos);
- } else if (os_strcmp(buf, "eap_fast_prov") == 0) {
- bss->eap_fast_prov = atoi(pos);
- } else if (os_strcmp(buf, "pac_key_lifetime") == 0) {
- bss->pac_key_lifetime = atoi(pos);
- } else if (os_strcmp(buf, "pac_key_refresh_time") == 0) {
- bss->pac_key_refresh_time = atoi(pos);
+ } else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) {
+ os_free(bss->pac_opaque_encr_key);
+ bss->pac_opaque_encr_key = os_malloc(16);
+ if (bss->pac_opaque_encr_key == NULL) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: No memory for pac_opaque_encr_key",
+ line);
+ return 1;
+ } else if (hexstr2bin(pos, bss->pac_opaque_encr_key, 16)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid pac_opaque_encr_key",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "eap_fast_a_id") == 0) {
+ size_t idlen = os_strlen(pos);
+ if (idlen & 1) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid eap_fast_a_id",
+ line);
+ return 1;
+ }
+ os_free(bss->eap_fast_a_id);
+ bss->eap_fast_a_id = os_malloc(idlen / 2);
+ if (bss->eap_fast_a_id == NULL ||
+ hexstr2bin(pos, bss->eap_fast_a_id, idlen / 2)) {
+ wpa_printf(MSG_ERROR, "Line %d: Failed to parse eap_fast_a_id",
+ line);
+ os_free(bss->eap_fast_a_id);
+ bss->eap_fast_a_id = NULL;
+ return 1;
+ } else {
+ bss->eap_fast_a_id_len = idlen / 2;
+ }
+ } else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) {
+ os_free(bss->eap_fast_a_id_info);
+ bss->eap_fast_a_id_info = os_strdup(pos);
+ } else if (os_strcmp(buf, "eap_fast_prov") == 0) {
+ bss->eap_fast_prov = atoi(pos);
+ } else if (os_strcmp(buf, "pac_key_lifetime") == 0) {
+ bss->pac_key_lifetime = atoi(pos);
+ } else if (os_strcmp(buf, "pac_key_refresh_time") == 0) {
+ bss->pac_key_refresh_time = atoi(pos);
#endif /* EAP_SERVER_FAST */
#ifdef EAP_SERVER_SIM
- } else if (os_strcmp(buf, "eap_sim_db") == 0) {
- os_free(bss->eap_sim_db);
- bss->eap_sim_db = os_strdup(pos);
- } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
- bss->eap_sim_aka_result_ind = atoi(pos);
+ } else if (os_strcmp(buf, "eap_sim_db") == 0) {
+ os_free(bss->eap_sim_db);
+ bss->eap_sim_db = os_strdup(pos);
+ } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
+ bss->eap_sim_aka_result_ind = atoi(pos);
#endif /* EAP_SERVER_SIM */
#ifdef EAP_SERVER_TNC
- } else if (os_strcmp(buf, "tnc") == 0) {
- bss->tnc = atoi(pos);
+ } else if (os_strcmp(buf, "tnc") == 0) {
+ bss->tnc = atoi(pos);
#endif /* EAP_SERVER_TNC */
#ifdef EAP_SERVER_PWD
- } else if (os_strcmp(buf, "pwd_group") == 0) {
- bss->pwd_group = atoi(pos);
+ } else if (os_strcmp(buf, "pwd_group") == 0) {
+ bss->pwd_group = atoi(pos);
#endif /* EAP_SERVER_PWD */
#endif /* EAP_SERVER */
- } else if (os_strcmp(buf, "eap_message") == 0) {
- char *term;
- bss->eap_req_id_text = os_strdup(pos);
- if (bss->eap_req_id_text == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: Failed to "
- "allocate memory for "
- "eap_req_id_text", line);
- errors++;
- return errors;
- }
- bss->eap_req_id_text_len =
- os_strlen(bss->eap_req_id_text);
- term = os_strstr(bss->eap_req_id_text, "\\0");
- if (term) {
- *term++ = '\0';
- os_memmove(term, term + 1,
- bss->eap_req_id_text_len -
- (term - bss->eap_req_id_text) - 1);
- bss->eap_req_id_text_len--;
- }
- } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
- bss->default_wep_key_len = atoi(pos);
- if (bss->default_wep_key_len > 13) {
- wpa_printf(MSG_ERROR, "Line %d: invalid WEP "
- "key len %lu (= %lu bits)", line,
- (unsigned long)
- bss->default_wep_key_len,
- (unsigned long)
- bss->default_wep_key_len * 8);
- errors++;
- }
- } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) {
- bss->individual_wep_key_len = atoi(pos);
- if (bss->individual_wep_key_len < 0 ||
- bss->individual_wep_key_len > 13) {
- wpa_printf(MSG_ERROR, "Line %d: invalid WEP "
- "key len %d (= %d bits)", line,
- bss->individual_wep_key_len,
- bss->individual_wep_key_len * 8);
- errors++;
- }
- } else if (os_strcmp(buf, "wep_rekey_period") == 0) {
- bss->wep_rekeying_period = atoi(pos);
- if (bss->wep_rekeying_period < 0) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "period %d",
- line, bss->wep_rekeying_period);
- errors++;
- }
- } else if (os_strcmp(buf, "eap_reauth_period") == 0) {
- bss->eap_reauth_period = atoi(pos);
- if (bss->eap_reauth_period < 0) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "period %d",
- line, bss->eap_reauth_period);
- errors++;
- }
- } else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) {
- bss->eapol_key_index_workaround = atoi(pos);
+ } else if (os_strcmp(buf, "eap_message") == 0) {
+ char *term;
+ bss->eap_req_id_text = os_strdup(pos);
+ if (bss->eap_req_id_text == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Failed to allocate memory for eap_req_id_text",
+ line);
+ return 1;
+ }
+ bss->eap_req_id_text_len = os_strlen(bss->eap_req_id_text);
+ term = os_strstr(bss->eap_req_id_text, "\\0");
+ if (term) {
+ *term++ = '\0';
+ os_memmove(term, term + 1,
+ bss->eap_req_id_text_len -
+ (term - bss->eap_req_id_text) - 1);
+ bss->eap_req_id_text_len--;
+ }
+ } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
+ bss->default_wep_key_len = atoi(pos);
+ if (bss->default_wep_key_len > 13) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %lu (= %lu bits)",
+ line,
+ (unsigned long) bss->default_wep_key_len,
+ (unsigned long)
+ bss->default_wep_key_len * 8);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) {
+ bss->individual_wep_key_len = atoi(pos);
+ if (bss->individual_wep_key_len < 0 ||
+ bss->individual_wep_key_len > 13) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %d (= %d bits)",
+ line, bss->individual_wep_key_len,
+ bss->individual_wep_key_len * 8);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wep_rekey_period") == 0) {
+ bss->wep_rekeying_period = atoi(pos);
+ if (bss->wep_rekeying_period < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid period %d",
+ line, bss->wep_rekeying_period);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "eap_reauth_period") == 0) {
+ bss->eap_reauth_period = atoi(pos);
+ if (bss->eap_reauth_period < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid period %d",
+ line, bss->eap_reauth_period);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) {
+ bss->eapol_key_index_workaround = atoi(pos);
#ifdef CONFIG_IAPP
- } else if (os_strcmp(buf, "iapp_interface") == 0) {
- bss->ieee802_11f = 1;
- os_strlcpy(bss->iapp_iface, pos,
- sizeof(bss->iapp_iface));
+ } else if (os_strcmp(buf, "iapp_interface") == 0) {
+ bss->ieee802_11f = 1;
+ os_strlcpy(bss->iapp_iface, pos, sizeof(bss->iapp_iface));
#endif /* CONFIG_IAPP */
- } else if (os_strcmp(buf, "own_ip_addr") == 0) {
- if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid IP "
- "address '%s'", line, pos);
- errors++;
- }
- } else if (os_strcmp(buf, "nas_identifier") == 0) {
- bss->nas_identifier = os_strdup(pos);
+ } else if (os_strcmp(buf, "own_ip_addr") == 0) {
+ if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid IP address '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "nas_identifier") == 0) {
+ bss->nas_identifier = os_strdup(pos);
#ifndef CONFIG_NO_RADIUS
- } else if (os_strcmp(buf, "auth_server_addr") == 0) {
- if (hostapd_config_read_radius_addr(
- &bss->radius->auth_servers,
- &bss->radius->num_auth_servers, pos, 1812,
- &bss->radius->auth_server)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid IP "
- "address '%s'", line, pos);
- errors++;
- }
- } else if (bss->radius->auth_server &&
- os_strcmp(buf, "auth_server_port") == 0) {
- bss->radius->auth_server->port = atoi(pos);
- } else if (bss->radius->auth_server &&
- os_strcmp(buf, "auth_server_shared_secret") == 0) {
- int len = os_strlen(pos);
- if (len == 0) {
- /* RFC 2865, Ch. 3 */
- wpa_printf(MSG_ERROR, "Line %d: empty shared "
- "secret is not allowed.", line);
- errors++;
- }
- bss->radius->auth_server->shared_secret =
- (u8 *) os_strdup(pos);
- bss->radius->auth_server->shared_secret_len = len;
- } else if (os_strcmp(buf, "acct_server_addr") == 0) {
- if (hostapd_config_read_radius_addr(
- &bss->radius->acct_servers,
- &bss->radius->num_acct_servers, pos, 1813,
- &bss->radius->acct_server)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid IP "
- "address '%s'", line, pos);
- errors++;
- }
- } else if (bss->radius->acct_server &&
- os_strcmp(buf, "acct_server_port") == 0) {
- bss->radius->acct_server->port = atoi(pos);
- } else if (bss->radius->acct_server &&
- os_strcmp(buf, "acct_server_shared_secret") == 0) {
- int len = os_strlen(pos);
- if (len == 0) {
- /* RFC 2865, Ch. 3 */
- wpa_printf(MSG_ERROR, "Line %d: empty shared "
- "secret is not allowed.", line);
- errors++;
- }
- bss->radius->acct_server->shared_secret =
- (u8 *) os_strdup(pos);
- bss->radius->acct_server->shared_secret_len = len;
- } else if (os_strcmp(buf, "radius_retry_primary_interval") ==
- 0) {
- bss->radius->retry_primary_interval = atoi(pos);
- } else if (os_strcmp(buf, "radius_acct_interim_interval") == 0)
- {
- bss->acct_interim_interval = atoi(pos);
- } else if (os_strcmp(buf, "radius_request_cui") == 0) {
- bss->radius_request_cui = atoi(pos);
- } else if (os_strcmp(buf, "radius_auth_req_attr") == 0) {
- struct hostapd_radius_attr *attr, *a;
- attr = hostapd_parse_radius_attr(pos);
- if (attr == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "radius_auth_req_attr", line);
- errors++;
- } else if (bss->radius_auth_req_attr == NULL) {
- bss->radius_auth_req_attr = attr;
- } else {
- a = bss->radius_auth_req_attr;
- while (a->next)
- a = a->next;
- a->next = attr;
- }
- } else if (os_strcmp(buf, "radius_acct_req_attr") == 0) {
- struct hostapd_radius_attr *attr, *a;
- attr = hostapd_parse_radius_attr(pos);
- if (attr == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "radius_acct_req_attr", line);
- errors++;
- } else if (bss->radius_acct_req_attr == NULL) {
- bss->radius_acct_req_attr = attr;
- } else {
- a = bss->radius_acct_req_attr;
- while (a->next)
- a = a->next;
- a->next = attr;
- }
- } else if (os_strcmp(buf, "radius_das_port") == 0) {
- bss->radius_das_port = atoi(pos);
- } else if (os_strcmp(buf, "radius_das_client") == 0) {
- if (hostapd_parse_das_client(bss, pos) < 0) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "DAS client", line);
- errors++;
- }
- } else if (os_strcmp(buf, "radius_das_time_window") == 0) {
- bss->radius_das_time_window = atoi(pos);
- } else if (os_strcmp(buf, "radius_das_require_event_timestamp")
- == 0) {
- bss->radius_das_require_event_timestamp = atoi(pos);
+ } else if (os_strcmp(buf, "auth_server_addr") == 0) {
+ if (hostapd_config_read_radius_addr(
+ &bss->radius->auth_servers,
+ &bss->radius->num_auth_servers, pos, 1812,
+ &bss->radius->auth_server)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid IP address '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_port") == 0) {
+ bss->radius->auth_server->port = atoi(pos);
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_shared_secret") == 0) {
+ int len = os_strlen(pos);
+ if (len == 0) {
+ /* RFC 2865, Ch. 3 */
+ wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed",
+ line);
+ return 1;
+ }
+ bss->radius->auth_server->shared_secret = (u8 *) os_strdup(pos);
+ bss->radius->auth_server->shared_secret_len = len;
+ } else if (os_strcmp(buf, "acct_server_addr") == 0) {
+ if (hostapd_config_read_radius_addr(
+ &bss->radius->acct_servers,
+ &bss->radius->num_acct_servers, pos, 1813,
+ &bss->radius->acct_server)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid IP address '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_port") == 0) {
+ bss->radius->acct_server->port = atoi(pos);
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_shared_secret") == 0) {
+ int len = os_strlen(pos);
+ if (len == 0) {
+ /* RFC 2865, Ch. 3 */
+ wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed",
+ line);
+ return 1;
+ }
+ bss->radius->acct_server->shared_secret = (u8 *) os_strdup(pos);
+ bss->radius->acct_server->shared_secret_len = len;
+ } else if (os_strcmp(buf, "radius_retry_primary_interval") == 0) {
+ bss->radius->retry_primary_interval = atoi(pos);
+ } else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) {
+ bss->acct_interim_interval = atoi(pos);
+ } else if (os_strcmp(buf, "radius_request_cui") == 0) {
+ bss->radius_request_cui = atoi(pos);
+ } else if (os_strcmp(buf, "radius_auth_req_attr") == 0) {
+ struct hostapd_radius_attr *attr, *a;
+ attr = hostapd_parse_radius_attr(pos);
+ if (attr == NULL) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid radius_auth_req_attr",
+ line);
+ return 1;
+ } else if (bss->radius_auth_req_attr == NULL) {
+ bss->radius_auth_req_attr = attr;
+ } else {
+ a = bss->radius_auth_req_attr;
+ while (a->next)
+ a = a->next;
+ a->next = attr;
+ }
+ } else if (os_strcmp(buf, "radius_acct_req_attr") == 0) {
+ struct hostapd_radius_attr *attr, *a;
+ attr = hostapd_parse_radius_attr(pos);
+ if (attr == NULL) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid radius_acct_req_attr",
+ line);
+ return 1;
+ } else if (bss->radius_acct_req_attr == NULL) {
+ bss->radius_acct_req_attr = attr;
+ } else {
+ a = bss->radius_acct_req_attr;
+ while (a->next)
+ a = a->next;
+ a->next = attr;
+ }
+ } else if (os_strcmp(buf, "radius_das_port") == 0) {
+ bss->radius_das_port = atoi(pos);
+ } else if (os_strcmp(buf, "radius_das_client") == 0) {
+ if (hostapd_parse_das_client(bss, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid DAS client",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "radius_das_time_window") == 0) {
+ bss->radius_das_time_window = atoi(pos);
+ } else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) {
+ bss->radius_das_require_event_timestamp = atoi(pos);
#endif /* CONFIG_NO_RADIUS */
- } else if (os_strcmp(buf, "auth_algs") == 0) {
- bss->auth_algs = atoi(pos);
- if (bss->auth_algs == 0) {
- wpa_printf(MSG_ERROR, "Line %d: no "
- "authentication algorithms allowed",
- line);
- errors++;
- }
- } else if (os_strcmp(buf, "max_num_sta") == 0) {
- bss->max_num_sta = atoi(pos);
- if (bss->max_num_sta < 0 ||
- bss->max_num_sta > MAX_STA_COUNT) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "max_num_sta=%d; allowed range "
- "0..%d", line, bss->max_num_sta,
- MAX_STA_COUNT);
- errors++;
- }
- } else if (os_strcmp(buf, "wpa") == 0) {
- bss->wpa = atoi(pos);
- } else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
- bss->wpa_group_rekey = atoi(pos);
- } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
- bss->wpa_strict_rekey = atoi(pos);
- } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
- bss->wpa_gmk_rekey = atoi(pos);
- } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
- bss->wpa_ptk_rekey = atoi(pos);
- } else if (os_strcmp(buf, "wpa_passphrase") == 0) {
- int len = os_strlen(pos);
- if (len < 8 || len > 63) {
- wpa_printf(MSG_ERROR, "Line %d: invalid WPA "
- "passphrase length %d (expected "
- "8..63)", line, len);
- errors++;
- } else {
- os_free(bss->ssid.wpa_passphrase);
- bss->ssid.wpa_passphrase = os_strdup(pos);
- if (bss->ssid.wpa_passphrase) {
- os_free(bss->ssid.wpa_psk);
- bss->ssid.wpa_psk = NULL;
- bss->ssid.wpa_passphrase_set = 1;
- }
- }
- } else if (os_strcmp(buf, "wpa_psk") == 0) {
+ } else if (os_strcmp(buf, "auth_algs") == 0) {
+ bss->auth_algs = atoi(pos);
+ if (bss->auth_algs == 0) {
+ wpa_printf(MSG_ERROR, "Line %d: no authentication algorithms allowed",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "max_num_sta") == 0) {
+ bss->max_num_sta = atoi(pos);
+ if (bss->max_num_sta < 0 ||
+ bss->max_num_sta > MAX_STA_COUNT) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
+ line, bss->max_num_sta, MAX_STA_COUNT);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wpa") == 0) {
+ bss->wpa = atoi(pos);
+ } else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
+ bss->wpa_group_rekey = atoi(pos);
+ } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
+ bss->wpa_strict_rekey = atoi(pos);
+ } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
+ bss->wpa_gmk_rekey = atoi(pos);
+ } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
+ bss->wpa_ptk_rekey = atoi(pos);
+ } else if (os_strcmp(buf, "wpa_passphrase") == 0) {
+ int len = os_strlen(pos);
+ if (len < 8 || len > 63) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid WPA passphrase length %d (expected 8..63)",
+ line, len);
+ return 1;
+ }
+ os_free(bss->ssid.wpa_passphrase);
+ bss->ssid.wpa_passphrase = os_strdup(pos);
+ if (bss->ssid.wpa_passphrase) {
os_free(bss->ssid.wpa_psk);
- bss->ssid.wpa_psk =
- os_zalloc(sizeof(struct hostapd_wpa_psk));
- if (bss->ssid.wpa_psk == NULL)
- errors++;
- else if (hexstr2bin(pos, bss->ssid.wpa_psk->psk,
- PMK_LEN) ||
- pos[PMK_LEN * 2] != '\0') {
- wpa_printf(MSG_ERROR, "Line %d: Invalid PSK "
- "'%s'.", line, pos);
- errors++;
- } else {
- bss->ssid.wpa_psk->group = 1;
- os_free(bss->ssid.wpa_passphrase);
- bss->ssid.wpa_passphrase = NULL;
- bss->ssid.wpa_psk_set = 1;
- }
- } else if (os_strcmp(buf, "wpa_psk_file") == 0) {
- os_free(bss->ssid.wpa_psk_file);
- bss->ssid.wpa_psk_file = os_strdup(pos);
- if (!bss->ssid.wpa_psk_file) {
- wpa_printf(MSG_ERROR, "Line %d: allocation "
- "failed", line);
- errors++;
- }
- } else if (os_strcmp(buf, "wpa_key_mgmt") == 0) {
- bss->wpa_key_mgmt =
- hostapd_config_parse_key_mgmt(line, pos);
- if (bss->wpa_key_mgmt == -1)
- errors++;
- } else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
- bss->wpa_psk_radius = atoi(pos);
- if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
- bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
- bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
- wpa_printf(MSG_ERROR, "Line %d: unknown "
- "wpa_psk_radius %d",
- line, bss->wpa_psk_radius);
- errors++;
- }
- } else if (os_strcmp(buf, "wpa_pairwise") == 0) {
- bss->wpa_pairwise =
- hostapd_config_parse_cipher(line, pos);
- if (bss->wpa_pairwise == -1 ||
- bss->wpa_pairwise == 0)
- errors++;
- else if (bss->wpa_pairwise &
- (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 |
- WPA_CIPHER_WEP104)) {
- wpa_printf(MSG_ERROR, "Line %d: unsupported "
- "pairwise cipher suite '%s'",
- bss->wpa_pairwise, pos);
- errors++;
- }
- } else if (os_strcmp(buf, "rsn_pairwise") == 0) {
- bss->rsn_pairwise =
- hostapd_config_parse_cipher(line, pos);
- if (bss->rsn_pairwise == -1 ||
- bss->rsn_pairwise == 0)
- errors++;
- else if (bss->rsn_pairwise &
- (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 |
- WPA_CIPHER_WEP104)) {
- wpa_printf(MSG_ERROR, "Line %d: unsupported "
- "pairwise cipher suite '%s'",
- bss->rsn_pairwise, pos);
- errors++;
- }
+ bss->ssid.wpa_psk = NULL;
+ bss->ssid.wpa_passphrase_set = 1;
+ }
+ } else if (os_strcmp(buf, "wpa_psk") == 0) {
+ os_free(bss->ssid.wpa_psk);
+ bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
+ if (bss->ssid.wpa_psk == NULL)
+ return 1;
+ if (hexstr2bin(pos, bss->ssid.wpa_psk->psk, PMK_LEN) ||
+ pos[PMK_LEN * 2] != '\0') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+ line, pos);
+ os_free(bss->ssid.wpa_psk);
+ bss->ssid.wpa_psk = NULL;
+ return 1;
+ }
+ bss->ssid.wpa_psk->group = 1;
+ os_free(bss->ssid.wpa_passphrase);
+ bss->ssid.wpa_passphrase = NULL;
+ bss->ssid.wpa_psk_set = 1;
+ } else if (os_strcmp(buf, "wpa_psk_file") == 0) {
+ os_free(bss->ssid.wpa_psk_file);
+ bss->ssid.wpa_psk_file = os_strdup(pos);
+ if (!bss->ssid.wpa_psk_file) {
+ wpa_printf(MSG_ERROR, "Line %d: allocation failed",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wpa_key_mgmt") == 0) {
+ bss->wpa_key_mgmt = hostapd_config_parse_key_mgmt(line, pos);
+ if (bss->wpa_key_mgmt == -1)
+ return 1;
+ } else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
+ bss->wpa_psk_radius = atoi(pos);
+ if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+ bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
+ bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown wpa_psk_radius %d",
+ line, bss->wpa_psk_radius);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wpa_pairwise") == 0) {
+ bss->wpa_pairwise = hostapd_config_parse_cipher(line, pos);
+ if (bss->wpa_pairwise == -1 || bss->wpa_pairwise == 0)
+ return 1;
+ if (bss->wpa_pairwise &
+ (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+ wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
+ bss->wpa_pairwise, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "rsn_pairwise") == 0) {
+ bss->rsn_pairwise = hostapd_config_parse_cipher(line, pos);
+ if (bss->rsn_pairwise == -1 || bss->rsn_pairwise == 0)
+ return 1;
+ if (bss->rsn_pairwise &
+ (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+ wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
+ bss->rsn_pairwise, pos);
+ return 1;
+ }
#ifdef CONFIG_RSN_PREAUTH
- } else if (os_strcmp(buf, "rsn_preauth") == 0) {
- bss->rsn_preauth = atoi(pos);
- } else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) {
- bss->rsn_preauth_interfaces = os_strdup(pos);
+ } else if (os_strcmp(buf, "rsn_preauth") == 0) {
+ bss->rsn_preauth = atoi(pos);
+ } else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) {
+ bss->rsn_preauth_interfaces = os_strdup(pos);
#endif /* CONFIG_RSN_PREAUTH */
#ifdef CONFIG_PEERKEY
- } else if (os_strcmp(buf, "peerkey") == 0) {
- bss->peerkey = atoi(pos);
+ } else if (os_strcmp(buf, "peerkey") == 0) {
+ bss->peerkey = atoi(pos);
#endif /* CONFIG_PEERKEY */
#ifdef CONFIG_IEEE80211R
- } else if (os_strcmp(buf, "mobility_domain") == 0) {
- if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
- hexstr2bin(pos, bss->mobility_domain,
- MOBILITY_DOMAIN_ID_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "Line %d: Invalid "
- "mobility_domain '%s'", line, pos);
- errors++;
- return errors;
- }
- } else if (os_strcmp(buf, "r1_key_holder") == 0) {
- if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN ||
- hexstr2bin(pos, bss->r1_key_holder,
- FT_R1KH_ID_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "Line %d: Invalid "
- "r1_key_holder '%s'", line, pos);
- errors++;
- return errors;
- }
- } else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
- bss->r0_key_lifetime = atoi(pos);
- } else if (os_strcmp(buf, "reassociation_deadline") == 0) {
- bss->reassociation_deadline = atoi(pos);
- } else if (os_strcmp(buf, "r0kh") == 0) {
- if (add_r0kh(bss, pos) < 0) {
- wpa_printf(MSG_DEBUG, "Line %d: Invalid "
- "r0kh '%s'", line, pos);
- errors++;
- return errors;
- }
- } else if (os_strcmp(buf, "r1kh") == 0) {
- if (add_r1kh(bss, pos) < 0) {
- wpa_printf(MSG_DEBUG, "Line %d: Invalid "
- "r1kh '%s'", line, pos);
- errors++;
- return errors;
- }
- } else if (os_strcmp(buf, "pmk_r1_push") == 0) {
- bss->pmk_r1_push = atoi(pos);
- } else if (os_strcmp(buf, "ft_over_ds") == 0) {
- bss->ft_over_ds = atoi(pos);
+ } else if (os_strcmp(buf, "mobility_domain") == 0) {
+ if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
+ hexstr2bin(pos, bss->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid mobility_domain '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "r1_key_holder") == 0) {
+ if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN ||
+ hexstr2bin(pos, bss->r1_key_holder, FT_R1KH_ID_LEN) != 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid r1_key_holder '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
+ bss->r0_key_lifetime = atoi(pos);
+ } else if (os_strcmp(buf, "reassociation_deadline") == 0) {
+ bss->reassociation_deadline = atoi(pos);
+ } else if (os_strcmp(buf, "r0kh") == 0) {
+ if (add_r0kh(bss, pos) < 0) {
+ wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "r1kh") == 0) {
+ if (add_r1kh(bss, pos) < 0) {
+ wpa_printf(MSG_DEBUG, "Line %d: Invalid r1kh '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "pmk_r1_push") == 0) {
+ bss->pmk_r1_push = atoi(pos);
+ } else if (os_strcmp(buf, "ft_over_ds") == 0) {
+ bss->ft_over_ds = atoi(pos);
#endif /* CONFIG_IEEE80211R */
#ifndef CONFIG_NO_CTRL_IFACE
- } else if (os_strcmp(buf, "ctrl_interface") == 0) {
- os_free(bss->ctrl_interface);
- bss->ctrl_interface = os_strdup(pos);
- } else if (os_strcmp(buf, "ctrl_interface_group") == 0) {
+ } else if (os_strcmp(buf, "ctrl_interface") == 0) {
+ os_free(bss->ctrl_interface);
+ bss->ctrl_interface = os_strdup(pos);
+ } else if (os_strcmp(buf, "ctrl_interface_group") == 0) {
#ifndef CONFIG_NATIVE_WINDOWS
- struct group *grp;
- char *endp;
- const char *group = pos;
+ struct group *grp;
+ char *endp;
+ const char *group = pos;
- grp = getgrnam(group);
- if (grp) {
- bss->ctrl_interface_gid = grp->gr_gid;
- bss->ctrl_interface_gid_set = 1;
- wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
- " (from group name '%s')",
- bss->ctrl_interface_gid, group);
- return errors;
- }
-
- /* Group name not found - try to parse this as gid */
- bss->ctrl_interface_gid = strtol(group, &endp, 10);
- if (*group == '\0' || *endp != '\0') {
- wpa_printf(MSG_DEBUG, "Line %d: Invalid group "
- "'%s'", line, group);
- errors++;
- return errors;
- }
+ grp = getgrnam(group);
+ if (grp) {
+ bss->ctrl_interface_gid = grp->gr_gid;
bss->ctrl_interface_gid_set = 1;
- wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
- bss->ctrl_interface_gid);
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d (from group name '%s')",
+ bss->ctrl_interface_gid, group);
+ return 0;
+ }
+
+ /* Group name not found - try to parse this as gid */
+ bss->ctrl_interface_gid = strtol(group, &endp, 10);
+ if (*group == '\0' || *endp != '\0') {
+ wpa_printf(MSG_DEBUG, "Line %d: Invalid group '%s'",
+ line, group);
+ return 1;
+ }
+ bss->ctrl_interface_gid_set = 1;
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
+ bss->ctrl_interface_gid);
#endif /* CONFIG_NATIVE_WINDOWS */
#endif /* CONFIG_NO_CTRL_IFACE */
#ifdef RADIUS_SERVER
- } else if (os_strcmp(buf, "radius_server_clients") == 0) {
- os_free(bss->radius_server_clients);
- bss->radius_server_clients = os_strdup(pos);
- } else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
- bss->radius_server_auth_port = atoi(pos);
- } else if (os_strcmp(buf, "radius_server_acct_port") == 0) {
- bss->radius_server_acct_port = atoi(pos);
- } else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
- bss->radius_server_ipv6 = atoi(pos);
+ } else if (os_strcmp(buf, "radius_server_clients") == 0) {
+ os_free(bss->radius_server_clients);
+ bss->radius_server_clients = os_strdup(pos);
+ } else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
+ bss->radius_server_auth_port = atoi(pos);
+ } else if (os_strcmp(buf, "radius_server_acct_port") == 0) {
+ bss->radius_server_acct_port = atoi(pos);
+ } else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
+ bss->radius_server_ipv6 = atoi(pos);
#endif /* RADIUS_SERVER */
- } else if (os_strcmp(buf, "test_socket") == 0) {
- os_free(bss->test_socket);
- bss->test_socket = os_strdup(pos);
- } else if (os_strcmp(buf, "use_pae_group_addr") == 0) {
- bss->use_pae_group_addr = atoi(pos);
- } else if (os_strcmp(buf, "hw_mode") == 0) {
- if (os_strcmp(pos, "a") == 0)
- conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
- else if (os_strcmp(pos, "b") == 0)
- conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
- else if (os_strcmp(pos, "g") == 0)
- conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
- else if (os_strcmp(pos, "ad") == 0)
- conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
- else {
- wpa_printf(MSG_ERROR, "Line %d: unknown "
- "hw_mode '%s'", line, pos);
- errors++;
- }
- } else if (os_strcmp(buf, "wps_rf_bands") == 0) {
- if (os_strcmp(pos, "a") == 0)
- bss->wps_rf_bands = WPS_RF_50GHZ;
- else if (os_strcmp(pos, "g") == 0 ||
- os_strcmp(pos, "b") == 0)
- bss->wps_rf_bands = WPS_RF_24GHZ;
- else if (os_strcmp(pos, "ag") == 0 ||
- os_strcmp(pos, "ga") == 0)
- bss->wps_rf_bands =
- WPS_RF_24GHZ | WPS_RF_50GHZ;
- else {
- wpa_printf(MSG_ERROR, "Line %d: unknown "
- "wps_rf_band '%s'", line, pos);
- errors++;
- }
- } else if (os_strcmp(buf, "channel") == 0) {
- if (os_strcmp(pos, "acs_survey") == 0) {
+ } else if (os_strcmp(buf, "test_socket") == 0) {
+ os_free(bss->test_socket);
+ bss->test_socket = os_strdup(pos);
+ } else if (os_strcmp(buf, "use_pae_group_addr") == 0) {
+ bss->use_pae_group_addr = atoi(pos);
+ } else if (os_strcmp(buf, "hw_mode") == 0) {
+ if (os_strcmp(pos, "a") == 0)
+ conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
+ else if (os_strcmp(pos, "b") == 0)
+ conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+ else if (os_strcmp(pos, "g") == 0)
+ conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+ else if (os_strcmp(pos, "ad") == 0)
+ conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
+ else {
+ wpa_printf(MSG_ERROR, "Line %d: unknown hw_mode '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wps_rf_bands") == 0) {
+ if (os_strcmp(pos, "a") == 0)
+ bss->wps_rf_bands = WPS_RF_50GHZ;
+ else if (os_strcmp(pos, "g") == 0 ||
+ os_strcmp(pos, "b") == 0)
+ bss->wps_rf_bands = WPS_RF_24GHZ;
+ else if (os_strcmp(pos, "ag") == 0 ||
+ os_strcmp(pos, "ga") == 0)
+ bss->wps_rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
+ else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown wps_rf_band '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "channel") == 0) {
+ if (os_strcmp(pos, "acs_survey") == 0) {
#ifndef CONFIG_ACS
- wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled",
- line);
- errors++;
+ wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled",
+ line);
+ return 1;
#endif /* CONFIG_ACS */
- conf->channel = 0;
- } else
- conf->channel = atoi(pos);
- } else if (os_strcmp(buf, "chanlist") == 0) {
- if (hostapd_parse_intlist(&conf->chanlist, pos)) {
- wpa_printf(MSG_ERROR,
- "Line %d: invalid channel list",
- line);
- errors++;
- }
- } else if (os_strcmp(buf, "beacon_int") == 0) {
- int val = atoi(pos);
- /* MIB defines range as 1..65535, but very small values
- * cause problems with the current implementation.
- * Since it is unlikely that this small numbers are
- * useful in real life scenarios, do not allow beacon
- * period to be set below 15 TU. */
- if (val < 15 || val > 65535) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "beacon_int %d (expected "
- "15..65535)", line, val);
- errors++;
- } else
- conf->beacon_int = val;
+ conf->channel = 0;
+ } else
+ conf->channel = atoi(pos);
+ } else if (os_strcmp(buf, "chanlist") == 0) {
+ if (hostapd_parse_intlist(&conf->chanlist, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid channel list",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "beacon_int") == 0) {
+ int val = atoi(pos);
+ /* MIB defines range as 1..65535, but very small values
+ * cause problems with the current implementation.
+ * Since it is unlikely that this small numbers are
+ * useful in real life scenarios, do not allow beacon
+ * period to be set below 15 TU. */
+ if (val < 15 || val > 65535) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid beacon_int %d (expected 15..65535)",
+ line, val);
+ return 1;
+ }
+ conf->beacon_int = val;
#ifdef CONFIG_ACS
- } else if (os_strcmp(buf, "acs_num_scans") == 0) {
- int val = atoi(pos);
- if (val <= 0 || val > 100) {
- wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)",
- line, val);
- errors++;
- } else
- conf->acs_num_scans = val;
+ } else if (os_strcmp(buf, "acs_num_scans") == 0) {
+ int val = atoi(pos);
+ if (val <= 0 || val > 100) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)",
+ line, val);
+ return 1;
+ }
+ conf->acs_num_scans = val;
#endif /* CONFIG_ACS */
- } else if (os_strcmp(buf, "dtim_period") == 0) {
- bss->dtim_period = atoi(pos);
- if (bss->dtim_period < 1 || bss->dtim_period > 255) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "dtim_period %d",
- line, bss->dtim_period);
- errors++;
- }
- } else if (os_strcmp(buf, "rts_threshold") == 0) {
- conf->rts_threshold = atoi(pos);
- if (conf->rts_threshold < 0 ||
- conf->rts_threshold > 2347) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "rts_threshold %d",
- line, conf->rts_threshold);
- errors++;
- }
- } else if (os_strcmp(buf, "fragm_threshold") == 0) {
- conf->fragm_threshold = atoi(pos);
- if (conf->fragm_threshold < 256 ||
- conf->fragm_threshold > 2346) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "fragm_threshold %d",
- line, conf->fragm_threshold);
- errors++;
- }
- } else if (os_strcmp(buf, "send_probe_response") == 0) {
- int val = atoi(pos);
- if (val != 0 && val != 1) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "send_probe_response %d (expected "
- "0 or 1)", line, val);
- } else
- conf->send_probe_response = val;
- } else if (os_strcmp(buf, "supported_rates") == 0) {
- if (hostapd_parse_intlist(&conf->supported_rates, pos))
- {
- wpa_printf(MSG_ERROR, "Line %d: invalid rate "
- "list", line);
- errors++;
- }
- } else if (os_strcmp(buf, "basic_rates") == 0) {
- if (hostapd_parse_intlist(&conf->basic_rates, pos)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid rate "
- "list", line);
- errors++;
- }
- } else if (os_strcmp(buf, "preamble") == 0) {
- if (atoi(pos))
- conf->preamble = SHORT_PREAMBLE;
- else
- conf->preamble = LONG_PREAMBLE;
- } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
- bss->ignore_broadcast_ssid = atoi(pos);
- } else if (os_strcmp(buf, "wep_default_key") == 0) {
- bss->ssid.wep.idx = atoi(pos);
- if (bss->ssid.wep.idx > 3) {
- wpa_printf(MSG_ERROR, "Invalid "
- "wep_default_key index %d",
- bss->ssid.wep.idx);
- errors++;
- }
- } else if (os_strcmp(buf, "wep_key0") == 0 ||
- os_strcmp(buf, "wep_key1") == 0 ||
- os_strcmp(buf, "wep_key2") == 0 ||
- os_strcmp(buf, "wep_key3") == 0) {
- if (hostapd_config_read_wep(&bss->ssid.wep,
- buf[7] - '0', pos)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid WEP "
- "key '%s'", line, buf);
- errors++;
- }
+ } else if (os_strcmp(buf, "dtim_period") == 0) {
+ bss->dtim_period = atoi(pos);
+ if (bss->dtim_period < 1 || bss->dtim_period > 255) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d",
+ line, bss->dtim_period);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "rts_threshold") == 0) {
+ conf->rts_threshold = atoi(pos);
+ if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid rts_threshold %d",
+ line, conf->rts_threshold);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "fragm_threshold") == 0) {
+ conf->fragm_threshold = atoi(pos);
+ if (conf->fragm_threshold < 256 ||
+ conf->fragm_threshold > 2346) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid fragm_threshold %d",
+ line, conf->fragm_threshold);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "send_probe_response") == 0) {
+ int val = atoi(pos);
+ if (val != 0 && val != 1) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid send_probe_response %d (expected 0 or 1)",
+ line, val);
+ return 1;
+ }
+ conf->send_probe_response = val;
+ } else if (os_strcmp(buf, "supported_rates") == 0) {
+ if (hostapd_parse_intlist(&conf->supported_rates, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "basic_rates") == 0) {
+ if (hostapd_parse_intlist(&conf->basic_rates, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "preamble") == 0) {
+ if (atoi(pos))
+ conf->preamble = SHORT_PREAMBLE;
+ else
+ conf->preamble = LONG_PREAMBLE;
+ } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
+ bss->ignore_broadcast_ssid = atoi(pos);
+ } else if (os_strcmp(buf, "wep_default_key") == 0) {
+ bss->ssid.wep.idx = atoi(pos);
+ if (bss->ssid.wep.idx > 3) {
+ wpa_printf(MSG_ERROR,
+ "Invalid wep_default_key index %d",
+ bss->ssid.wep.idx);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wep_key0") == 0 ||
+ os_strcmp(buf, "wep_key1") == 0 ||
+ os_strcmp(buf, "wep_key2") == 0 ||
+ os_strcmp(buf, "wep_key3") == 0) {
+ if (hostapd_config_read_wep(&bss->ssid.wep,
+ buf[7] - '0', pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid WEP key '%s'",
+ line, buf);
+ return 1;
+ }
#ifndef CONFIG_NO_VLAN
- } else if (os_strcmp(buf, "dynamic_vlan") == 0) {
- bss->ssid.dynamic_vlan = atoi(pos);
- } else if (os_strcmp(buf, "vlan_file") == 0) {
- if (hostapd_config_read_vlan_file(bss, pos)) {
- wpa_printf(MSG_ERROR, "Line %d: failed to "
- "read VLAN file '%s'", line, pos);
- errors++;
- }
- } else if (os_strcmp(buf, "vlan_naming") == 0) {
- bss->ssid.vlan_naming = atoi(pos);
- if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END ||
- bss->ssid.vlan_naming < 0) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "naming scheme %d", line,
- bss->ssid.vlan_naming);
- errors++;
- }
+ } else if (os_strcmp(buf, "dynamic_vlan") == 0) {
+ bss->ssid.dynamic_vlan = atoi(pos);
+ } else if (os_strcmp(buf, "vlan_file") == 0) {
+ if (hostapd_config_read_vlan_file(bss, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to read VLAN file '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "vlan_naming") == 0) {
+ bss->ssid.vlan_naming = atoi(pos);
+ if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END ||
+ bss->ssid.vlan_naming < 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid naming scheme %d",
+ line, bss->ssid.vlan_naming);
+ return 1;
+ }
#ifdef CONFIG_FULL_DYNAMIC_VLAN
- } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
- bss->ssid.vlan_tagged_interface = os_strdup(pos);
+ } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
+ bss->ssid.vlan_tagged_interface = os_strdup(pos);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
#endif /* CONFIG_NO_VLAN */
- } else if (os_strcmp(buf, "ap_table_max_size") == 0) {
- conf->ap_table_max_size = atoi(pos);
- } else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
- conf->ap_table_expiration_time = atoi(pos);
- } else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
- if (hostapd_config_tx_queue(conf, buf, pos)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid TX "
- "queue item", line);
- errors++;
- }
- } else if (os_strcmp(buf, "wme_enabled") == 0 ||
- os_strcmp(buf, "wmm_enabled") == 0) {
- bss->wmm_enabled = atoi(pos);
- } else if (os_strcmp(buf, "uapsd_advertisement_enabled") == 0) {
- bss->wmm_uapsd = atoi(pos);
- } else if (os_strncmp(buf, "wme_ac_", 7) == 0 ||
- os_strncmp(buf, "wmm_ac_", 7) == 0) {
- if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf,
- pos)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
- "ac item", line);
- errors++;
- }
- } else if (os_strcmp(buf, "bss") == 0) {
- if (hostapd_config_bss(conf, pos)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid bss "
- "item", line);
- errors++;
- }
- } else if (os_strcmp(buf, "bssid") == 0) {
- if (hwaddr_aton(pos, bss->bssid)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid bssid "
- "item", line);
- errors++;
- }
+ } else if (os_strcmp(buf, "ap_table_max_size") == 0) {
+ conf->ap_table_max_size = atoi(pos);
+ } else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
+ conf->ap_table_expiration_time = atoi(pos);
+ } else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
+ if (hostapd_config_tx_queue(conf, buf, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid TX queue item",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wme_enabled") == 0 ||
+ os_strcmp(buf, "wmm_enabled") == 0) {
+ bss->wmm_enabled = atoi(pos);
+ } else if (os_strcmp(buf, "uapsd_advertisement_enabled") == 0) {
+ bss->wmm_uapsd = atoi(pos);
+ } else if (os_strncmp(buf, "wme_ac_", 7) == 0 ||
+ os_strncmp(buf, "wmm_ac_", 7) == 0) {
+ if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid WMM ac item",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "bss") == 0) {
+ if (hostapd_config_bss(conf, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid bss item",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "bssid") == 0) {
+ if (hwaddr_aton(pos, bss->bssid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid bssid item",
+ line);
+ return 1;
+ }
#ifdef CONFIG_IEEE80211W
- } else if (os_strcmp(buf, "ieee80211w") == 0) {
- bss->ieee80211w = atoi(pos);
- } else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) {
- bss->assoc_sa_query_max_timeout = atoi(pos);
- if (bss->assoc_sa_query_max_timeout == 0) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "assoc_sa_query_max_timeout", line);
- errors++;
- }
- } else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0)
- {
- bss->assoc_sa_query_retry_timeout = atoi(pos);
- if (bss->assoc_sa_query_retry_timeout == 0) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "assoc_sa_query_retry_timeout",
- line);
- errors++;
- }
+ } else if (os_strcmp(buf, "ieee80211w") == 0) {
+ bss->ieee80211w = atoi(pos);
+ } else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) {
+ bss->assoc_sa_query_max_timeout = atoi(pos);
+ if (bss->assoc_sa_query_max_timeout == 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_max_timeout",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0) {
+ bss->assoc_sa_query_retry_timeout = atoi(pos);
+ if (bss->assoc_sa_query_retry_timeout == 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_retry_timeout",
+ line);
+ return 1;
+ }
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211N
- } else if (os_strcmp(buf, "ieee80211n") == 0) {
- conf->ieee80211n = atoi(pos);
- } else if (os_strcmp(buf, "ht_capab") == 0) {
- if (hostapd_config_ht_capab(conf, pos) < 0) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "ht_capab", line);
- errors++;
- }
- } else if (os_strcmp(buf, "require_ht") == 0) {
- conf->require_ht = atoi(pos);
- } else if (os_strcmp(buf, "obss_interval") == 0) {
- conf->obss_interval = atoi(pos);
+ } else if (os_strcmp(buf, "ieee80211n") == 0) {
+ conf->ieee80211n = atoi(pos);
+ } else if (os_strcmp(buf, "ht_capab") == 0) {
+ if (hostapd_config_ht_capab(conf, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid ht_capab",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "require_ht") == 0) {
+ conf->require_ht = atoi(pos);
+ } else if (os_strcmp(buf, "obss_interval") == 0) {
+ conf->obss_interval = atoi(pos);
#endif /* CONFIG_IEEE80211N */
#ifdef CONFIG_IEEE80211AC
- } else if (os_strcmp(buf, "ieee80211ac") == 0) {
- conf->ieee80211ac = atoi(pos);
- } else if (os_strcmp(buf, "vht_capab") == 0) {
- if (hostapd_config_vht_capab(conf, pos) < 0) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "vht_capab", line);
- errors++;
- }
- } else if (os_strcmp(buf, "require_vht") == 0) {
- conf->require_vht = atoi(pos);
- } else if (os_strcmp(buf, "vht_oper_chwidth") == 0) {
- conf->vht_oper_chwidth = atoi(pos);
- } else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0)
- {
- conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
- } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0)
- {
- conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
+ } else if (os_strcmp(buf, "ieee80211ac") == 0) {
+ conf->ieee80211ac = atoi(pos);
+ } else if (os_strcmp(buf, "vht_capab") == 0) {
+ if (hostapd_config_vht_capab(conf, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid vht_capab",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "require_vht") == 0) {
+ conf->require_vht = atoi(pos);
+ } else if (os_strcmp(buf, "vht_oper_chwidth") == 0) {
+ conf->vht_oper_chwidth = atoi(pos);
+ } else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0) {
+ conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
+ } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) {
+ conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
#endif /* CONFIG_IEEE80211AC */
- } else if (os_strcmp(buf, "max_listen_interval") == 0) {
- bss->max_listen_interval = atoi(pos);
- } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
- bss->disable_pmksa_caching = atoi(pos);
- } else if (os_strcmp(buf, "okc") == 0) {
- bss->okc = atoi(pos);
+ } else if (os_strcmp(buf, "max_listen_interval") == 0) {
+ bss->max_listen_interval = atoi(pos);
+ } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
+ bss->disable_pmksa_caching = atoi(pos);
+ } else if (os_strcmp(buf, "okc") == 0) {
+ bss->okc = atoi(pos);
#ifdef CONFIG_WPS
- } else if (os_strcmp(buf, "wps_state") == 0) {
- bss->wps_state = atoi(pos);
- if (bss->wps_state < 0 || bss->wps_state > 2) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "wps_state", line);
- errors++;
- }
- } else if (os_strcmp(buf, "wps_independent") == 0) {
- bss->wps_independent = atoi(pos);
- } else if (os_strcmp(buf, "ap_setup_locked") == 0) {
- bss->ap_setup_locked = atoi(pos);
- } else if (os_strcmp(buf, "uuid") == 0) {
- if (uuid_str2bin(pos, bss->uuid)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid UUID",
- line);
- errors++;
- }
- } else if (os_strcmp(buf, "wps_pin_requests") == 0) {
- os_free(bss->wps_pin_requests);
- bss->wps_pin_requests = os_strdup(pos);
- } else if (os_strcmp(buf, "device_name") == 0) {
- if (os_strlen(pos) > 32) {
- wpa_printf(MSG_ERROR, "Line %d: Too long "
- "device_name", line);
- errors++;
- }
- os_free(bss->device_name);
- bss->device_name = os_strdup(pos);
- } else if (os_strcmp(buf, "manufacturer") == 0) {
- if (os_strlen(pos) > 64) {
- wpa_printf(MSG_ERROR, "Line %d: Too long "
- "manufacturer", line);
- errors++;
- }
- os_free(bss->manufacturer);
- bss->manufacturer = os_strdup(pos);
- } else if (os_strcmp(buf, "model_name") == 0) {
- if (os_strlen(pos) > 32) {
- wpa_printf(MSG_ERROR, "Line %d: Too long "
- "model_name", line);
- errors++;
- }
- os_free(bss->model_name);
- bss->model_name = os_strdup(pos);
- } else if (os_strcmp(buf, "model_number") == 0) {
- if (os_strlen(pos) > 32) {
- wpa_printf(MSG_ERROR, "Line %d: Too long "
- "model_number", line);
- errors++;
- }
- os_free(bss->model_number);
- bss->model_number = os_strdup(pos);
- } else if (os_strcmp(buf, "serial_number") == 0) {
- if (os_strlen(pos) > 32) {
- wpa_printf(MSG_ERROR, "Line %d: Too long "
- "serial_number", line);
- errors++;
- }
- os_free(bss->serial_number);
- bss->serial_number = os_strdup(pos);
- } else if (os_strcmp(buf, "device_type") == 0) {
- if (wps_dev_type_str2bin(pos, bss->device_type))
- errors++;
- } else if (os_strcmp(buf, "config_methods") == 0) {
- os_free(bss->config_methods);
- bss->config_methods = os_strdup(pos);
- } else if (os_strcmp(buf, "os_version") == 0) {
- if (hexstr2bin(pos, bss->os_version, 4)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "os_version", line);
- errors++;
- }
- } else if (os_strcmp(buf, "ap_pin") == 0) {
- os_free(bss->ap_pin);
- bss->ap_pin = os_strdup(pos);
- } else if (os_strcmp(buf, "skip_cred_build") == 0) {
- bss->skip_cred_build = atoi(pos);
- } else if (os_strcmp(buf, "extra_cred") == 0) {
- os_free(bss->extra_cred);
- bss->extra_cred =
- (u8 *) os_readfile(pos, &bss->extra_cred_len);
- if (bss->extra_cred == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: could not "
- "read Credentials from '%s'",
- line, pos);
- errors++;
- }
- } else if (os_strcmp(buf, "wps_cred_processing") == 0) {
- bss->wps_cred_processing = atoi(pos);
- } else if (os_strcmp(buf, "ap_settings") == 0) {
- os_free(bss->ap_settings);
- bss->ap_settings =
- (u8 *) os_readfile(pos, &bss->ap_settings_len);
- if (bss->ap_settings == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: could not "
- "read AP Settings from '%s'",
- line, pos);
- errors++;
- }
- } else if (os_strcmp(buf, "upnp_iface") == 0) {
- bss->upnp_iface = os_strdup(pos);
- } else if (os_strcmp(buf, "friendly_name") == 0) {
- os_free(bss->friendly_name);
- bss->friendly_name = os_strdup(pos);
- } else if (os_strcmp(buf, "manufacturer_url") == 0) {
- os_free(bss->manufacturer_url);
- bss->manufacturer_url = os_strdup(pos);
- } else if (os_strcmp(buf, "model_description") == 0) {
- os_free(bss->model_description);
- bss->model_description = os_strdup(pos);
- } else if (os_strcmp(buf, "model_url") == 0) {
- os_free(bss->model_url);
- bss->model_url = os_strdup(pos);
- } else if (os_strcmp(buf, "upc") == 0) {
- os_free(bss->upc);
- bss->upc = os_strdup(pos);
- } else if (os_strcmp(buf, "pbc_in_m1") == 0) {
- bss->pbc_in_m1 = atoi(pos);
- } else if (os_strcmp(buf, "server_id") == 0) {
- os_free(bss->server_id);
- bss->server_id = os_strdup(pos);
+ } else if (os_strcmp(buf, "wps_state") == 0) {
+ bss->wps_state = atoi(pos);
+ if (bss->wps_state < 0 || bss->wps_state > 2) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid wps_state",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wps_independent") == 0) {
+ bss->wps_independent = atoi(pos);
+ } else if (os_strcmp(buf, "ap_setup_locked") == 0) {
+ bss->ap_setup_locked = atoi(pos);
+ } else if (os_strcmp(buf, "uuid") == 0) {
+ if (uuid_str2bin(pos, bss->uuid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wps_pin_requests") == 0) {
+ os_free(bss->wps_pin_requests);
+ bss->wps_pin_requests = os_strdup(pos);
+ } else if (os_strcmp(buf, "device_name") == 0) {
+ if (os_strlen(pos) > 32) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long "
+ "device_name", line);
+ return 1;
+ }
+ os_free(bss->device_name);
+ bss->device_name = os_strdup(pos);
+ } else if (os_strcmp(buf, "manufacturer") == 0) {
+ if (os_strlen(pos) > 64) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long manufacturer",
+ line);
+ return 1;
+ }
+ os_free(bss->manufacturer);
+ bss->manufacturer = os_strdup(pos);
+ } else if (os_strcmp(buf, "model_name") == 0) {
+ if (os_strlen(pos) > 32) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long model_name",
+ line);
+ return 1;
+ }
+ os_free(bss->model_name);
+ bss->model_name = os_strdup(pos);
+ } else if (os_strcmp(buf, "model_number") == 0) {
+ if (os_strlen(pos) > 32) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long model_number",
+ line);
+ return 1;
+ }
+ os_free(bss->model_number);
+ bss->model_number = os_strdup(pos);
+ } else if (os_strcmp(buf, "serial_number") == 0) {
+ if (os_strlen(pos) > 32) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long serial_number",
+ line);
+ return 1;
+ }
+ os_free(bss->serial_number);
+ bss->serial_number = os_strdup(pos);
+ } else if (os_strcmp(buf, "device_type") == 0) {
+ if (wps_dev_type_str2bin(pos, bss->device_type))
+ return 1;
+ } else if (os_strcmp(buf, "config_methods") == 0) {
+ os_free(bss->config_methods);
+ bss->config_methods = os_strdup(pos);
+ } else if (os_strcmp(buf, "os_version") == 0) {
+ if (hexstr2bin(pos, bss->os_version, 4)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid os_version",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "ap_pin") == 0) {
+ os_free(bss->ap_pin);
+ bss->ap_pin = os_strdup(pos);
+ } else if (os_strcmp(buf, "skip_cred_build") == 0) {
+ bss->skip_cred_build = atoi(pos);
+ } else if (os_strcmp(buf, "extra_cred") == 0) {
+ os_free(bss->extra_cred);
+ bss->extra_cred = (u8 *) os_readfile(pos, &bss->extra_cred_len);
+ if (bss->extra_cred == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: could not read Credentials from '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wps_cred_processing") == 0) {
+ bss->wps_cred_processing = atoi(pos);
+ } else if (os_strcmp(buf, "ap_settings") == 0) {
+ os_free(bss->ap_settings);
+ bss->ap_settings =
+ (u8 *) os_readfile(pos, &bss->ap_settings_len);
+ if (bss->ap_settings == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: could not read AP Settings from '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "upnp_iface") == 0) {
+ bss->upnp_iface = os_strdup(pos);
+ } else if (os_strcmp(buf, "friendly_name") == 0) {
+ os_free(bss->friendly_name);
+ bss->friendly_name = os_strdup(pos);
+ } else if (os_strcmp(buf, "manufacturer_url") == 0) {
+ os_free(bss->manufacturer_url);
+ bss->manufacturer_url = os_strdup(pos);
+ } else if (os_strcmp(buf, "model_description") == 0) {
+ os_free(bss->model_description);
+ bss->model_description = os_strdup(pos);
+ } else if (os_strcmp(buf, "model_url") == 0) {
+ os_free(bss->model_url);
+ bss->model_url = os_strdup(pos);
+ } else if (os_strcmp(buf, "upc") == 0) {
+ os_free(bss->upc);
+ bss->upc = os_strdup(pos);
+ } else if (os_strcmp(buf, "pbc_in_m1") == 0) {
+ bss->pbc_in_m1 = atoi(pos);
+ } else if (os_strcmp(buf, "server_id") == 0) {
+ os_free(bss->server_id);
+ bss->server_id = os_strdup(pos);
#ifdef CONFIG_WPS_NFC
- } else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
- bss->wps_nfc_dev_pw_id = atoi(pos);
- if (bss->wps_nfc_dev_pw_id < 0x10 ||
- bss->wps_nfc_dev_pw_id > 0xffff) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "wps_nfc_dev_pw_id value", line);
- errors++;
- }
- bss->wps_nfc_pw_from_config = 1;
- } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
- wpabuf_free(bss->wps_nfc_dh_pubkey);
- bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
- bss->wps_nfc_pw_from_config = 1;
- } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
- wpabuf_free(bss->wps_nfc_dh_privkey);
- bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
- bss->wps_nfc_pw_from_config = 1;
- } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
- wpabuf_free(bss->wps_nfc_dev_pw);
- bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
- bss->wps_nfc_pw_from_config = 1;
+ } else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
+ bss->wps_nfc_dev_pw_id = atoi(pos);
+ if (bss->wps_nfc_dev_pw_id < 0x10 ||
+ bss->wps_nfc_dev_pw_id > 0xffff) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid wps_nfc_dev_pw_id value",
+ line);
+ return 1;
+ }
+ bss->wps_nfc_pw_from_config = 1;
+ } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
+ wpabuf_free(bss->wps_nfc_dh_pubkey);
+ bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
+ bss->wps_nfc_pw_from_config = 1;
+ } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
+ wpabuf_free(bss->wps_nfc_dh_privkey);
+ bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
+ bss->wps_nfc_pw_from_config = 1;
+ } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
+ wpabuf_free(bss->wps_nfc_dev_pw);
+ bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
+ bss->wps_nfc_pw_from_config = 1;
#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P_MANAGER
- } else if (os_strcmp(buf, "manage_p2p") == 0) {
- int manage = atoi(pos);
- if (manage)
- bss->p2p |= P2P_MANAGE;
- else
- bss->p2p &= ~P2P_MANAGE;
- } else if (os_strcmp(buf, "allow_cross_connection") == 0) {
- if (atoi(pos))
- bss->p2p |= P2P_ALLOW_CROSS_CONNECTION;
- else
- bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
+ } else if (os_strcmp(buf, "manage_p2p") == 0) {
+ if (atoi(pos))
+ bss->p2p |= P2P_MANAGE;
+ else
+ bss->p2p &= ~P2P_MANAGE;
+ } else if (os_strcmp(buf, "allow_cross_connection") == 0) {
+ if (atoi(pos))
+ bss->p2p |= P2P_ALLOW_CROSS_CONNECTION;
+ else
+ bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
#endif /* CONFIG_P2P_MANAGER */
- } else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
- bss->disassoc_low_ack = atoi(pos);
- } else if (os_strcmp(buf, "tdls_prohibit") == 0) {
- int val = atoi(pos);
- if (val)
- bss->tdls |= TDLS_PROHIBIT;
- else
- bss->tdls &= ~TDLS_PROHIBIT;
- } else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) {
- int val = atoi(pos);
- if (val)
- bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH;
- else
- bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH;
+ } else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
+ bss->disassoc_low_ack = atoi(pos);
+ } else if (os_strcmp(buf, "tdls_prohibit") == 0) {
+ if (atoi(pos))
+ bss->tdls |= TDLS_PROHIBIT;
+ else
+ bss->tdls &= ~TDLS_PROHIBIT;
+ } else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) {
+ if (atoi(pos))
+ bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH;
+ else
+ bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH;
#ifdef CONFIG_RSN_TESTING
- } else if (os_strcmp(buf, "rsn_testing") == 0) {
- extern int rsn_testing;
- rsn_testing = atoi(pos);
+ } else if (os_strcmp(buf, "rsn_testing") == 0) {
+ extern int rsn_testing;
+ rsn_testing = atoi(pos);
#endif /* CONFIG_RSN_TESTING */
- } else if (os_strcmp(buf, "time_advertisement") == 0) {
- bss->time_advertisement = atoi(pos);
- } else if (os_strcmp(buf, "time_zone") == 0) {
- size_t tz_len = os_strlen(pos);
- if (tz_len < 4 || tz_len > 255) {
- wpa_printf(MSG_DEBUG, "Line %d: invalid "
- "time_zone", line);
- errors++;
- return errors;
- }
- os_free(bss->time_zone);
- bss->time_zone = os_strdup(pos);
- if (bss->time_zone == NULL)
- errors++;
+ } else if (os_strcmp(buf, "time_advertisement") == 0) {
+ bss->time_advertisement = atoi(pos);
+ } else if (os_strcmp(buf, "time_zone") == 0) {
+ size_t tz_len = os_strlen(pos);
+ if (tz_len < 4 || tz_len > 255) {
+ wpa_printf(MSG_DEBUG, "Line %d: invalid time_zone",
+ line);
+ return 1;
+ }
+ os_free(bss->time_zone);
+ bss->time_zone = os_strdup(pos);
+ if (bss->time_zone == NULL)
+ return 1;
#ifdef CONFIG_WNM
- } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
- bss->wnm_sleep_mode = atoi(pos);
- } else if (os_strcmp(buf, "bss_transition") == 0) {
- bss->bss_transition = atoi(pos);
+ } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
+ bss->wnm_sleep_mode = atoi(pos);
+ } else if (os_strcmp(buf, "bss_transition") == 0) {
+ bss->bss_transition = atoi(pos);
#endif /* CONFIG_WNM */
#ifdef CONFIG_INTERWORKING
- } else if (os_strcmp(buf, "interworking") == 0) {
- bss->interworking = atoi(pos);
- } else if (os_strcmp(buf, "access_network_type") == 0) {
- bss->access_network_type = atoi(pos);
- if (bss->access_network_type < 0 ||
- bss->access_network_type > 15) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "access_network_type", line);
- errors++;
- }
- } else if (os_strcmp(buf, "internet") == 0) {
- bss->internet = atoi(pos);
- } else if (os_strcmp(buf, "asra") == 0) {
- bss->asra = atoi(pos);
- } else if (os_strcmp(buf, "esr") == 0) {
- bss->esr = atoi(pos);
- } else if (os_strcmp(buf, "uesa") == 0) {
- bss->uesa = atoi(pos);
- } else if (os_strcmp(buf, "venue_group") == 0) {
- bss->venue_group = atoi(pos);
- bss->venue_info_set = 1;
- } else if (os_strcmp(buf, "venue_type") == 0) {
- bss->venue_type = atoi(pos);
- bss->venue_info_set = 1;
- } else if (os_strcmp(buf, "hessid") == 0) {
- if (hwaddr_aton(pos, bss->hessid)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "hessid", line);
- errors++;
- }
- } else if (os_strcmp(buf, "roaming_consortium") == 0) {
- if (parse_roaming_consortium(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "venue_name") == 0) {
- if (parse_venue_name(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "network_auth_type") == 0) {
- u8 auth_type;
- u16 redirect_url_len;
- if (hexstr2bin(pos, &auth_type, 1)) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "network_auth_type '%s'",
- line, pos);
- errors++;
- return errors;
- }
- if (auth_type == 0 || auth_type == 2)
- redirect_url_len = os_strlen(pos + 2);
- else
- redirect_url_len = 0;
- os_free(bss->network_auth_type);
- bss->network_auth_type =
- os_malloc(redirect_url_len + 3 + 1);
- if (bss->network_auth_type == NULL) {
- errors++;
- return errors;
- }
- *bss->network_auth_type = auth_type;
- WPA_PUT_LE16(bss->network_auth_type + 1,
- redirect_url_len);
- if (redirect_url_len)
- os_memcpy(bss->network_auth_type + 3,
- pos + 2, redirect_url_len);
- bss->network_auth_type_len = 3 + redirect_url_len;
- } else if (os_strcmp(buf, "ipaddr_type_availability") == 0) {
- if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1))
- {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "ipaddr_type_availability '%s'",
- line, pos);
- bss->ipaddr_type_configured = 0;
- errors++;
- return errors;
- }
- bss->ipaddr_type_configured = 1;
- } else if (os_strcmp(buf, "domain_name") == 0) {
- int j, num_domains, domain_len, domain_list_len = 0;
- char *tok_start, *tok_prev;
- u8 *domain_list, *domain_ptr;
+ } else if (os_strcmp(buf, "interworking") == 0) {
+ bss->interworking = atoi(pos);
+ } else if (os_strcmp(buf, "access_network_type") == 0) {
+ bss->access_network_type = atoi(pos);
+ if (bss->access_network_type < 0 ||
+ bss->access_network_type > 15) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid access_network_type",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "internet") == 0) {
+ bss->internet = atoi(pos);
+ } else if (os_strcmp(buf, "asra") == 0) {
+ bss->asra = atoi(pos);
+ } else if (os_strcmp(buf, "esr") == 0) {
+ bss->esr = atoi(pos);
+ } else if (os_strcmp(buf, "uesa") == 0) {
+ bss->uesa = atoi(pos);
+ } else if (os_strcmp(buf, "venue_group") == 0) {
+ bss->venue_group = atoi(pos);
+ bss->venue_info_set = 1;
+ } else if (os_strcmp(buf, "venue_type") == 0) {
+ bss->venue_type = atoi(pos);
+ bss->venue_info_set = 1;
+ } else if (os_strcmp(buf, "hessid") == 0) {
+ if (hwaddr_aton(pos, bss->hessid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid hessid", line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "roaming_consortium") == 0) {
+ if (parse_roaming_consortium(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "venue_name") == 0) {
+ if (parse_venue_name(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "network_auth_type") == 0) {
+ u8 auth_type;
+ u16 redirect_url_len;
+ if (hexstr2bin(pos, &auth_type, 1)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid network_auth_type '%s'",
+ line, pos);
+ return 1;
+ }
+ if (auth_type == 0 || auth_type == 2)
+ redirect_url_len = os_strlen(pos + 2);
+ else
+ redirect_url_len = 0;
+ os_free(bss->network_auth_type);
+ bss->network_auth_type = os_malloc(redirect_url_len + 3 + 1);
+ if (bss->network_auth_type == NULL)
+ return 1;
+ *bss->network_auth_type = auth_type;
+ WPA_PUT_LE16(bss->network_auth_type + 1, redirect_url_len);
+ if (redirect_url_len)
+ os_memcpy(bss->network_auth_type + 3, pos + 2,
+ redirect_url_len);
+ bss->network_auth_type_len = 3 + redirect_url_len;
+ } else if (os_strcmp(buf, "ipaddr_type_availability") == 0) {
+ if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid ipaddr_type_availability '%s'",
+ line, pos);
+ bss->ipaddr_type_configured = 0;
+ return 1;
+ }
+ bss->ipaddr_type_configured = 1;
+ } else if (os_strcmp(buf, "domain_name") == 0) {
+ int j, num_domains, domain_len, domain_list_len = 0;
+ char *tok_start, *tok_prev;
+ u8 *domain_list, *domain_ptr;
- domain_list_len = os_strlen(pos) + 1;
- domain_list = os_malloc(domain_list_len);
- if (domain_list == NULL) {
- errors++;
- return errors;
- }
+ domain_list_len = os_strlen(pos) + 1;
+ domain_list = os_malloc(domain_list_len);
+ if (domain_list == NULL)
+ return 1;
- domain_ptr = domain_list;
- tok_prev = pos;
- num_domains = 1;
- while ((tok_prev = os_strchr(tok_prev, ','))) {
- num_domains++;
- tok_prev++;
+ domain_ptr = domain_list;
+ tok_prev = pos;
+ num_domains = 1;
+ while ((tok_prev = os_strchr(tok_prev, ','))) {
+ num_domains++;
+ tok_prev++;
+ }
+ tok_prev = pos;
+ for (j = 0; j < num_domains; j++) {
+ tok_start = os_strchr(tok_prev, ',');
+ if (tok_start) {
+ domain_len = tok_start - tok_prev;
+ *domain_ptr = domain_len;
+ os_memcpy(domain_ptr + 1, tok_prev, domain_len);
+ domain_ptr += domain_len + 1;
+ tok_prev = ++tok_start;
+ } else {
+ domain_len = os_strlen(tok_prev);
+ *domain_ptr = domain_len;
+ os_memcpy(domain_ptr + 1, tok_prev, domain_len);
+ domain_ptr += domain_len + 1;
}
- tok_prev = pos;
- for (j = 0; j < num_domains; j++) {
- tok_start = os_strchr(tok_prev, ',');
- if (tok_start) {
- domain_len = tok_start - tok_prev;
- *domain_ptr = domain_len;
- os_memcpy(domain_ptr + 1, tok_prev,
- domain_len);
- domain_ptr += domain_len + 1;
- tok_prev = ++tok_start;
- } else {
- domain_len = os_strlen(tok_prev);
- *domain_ptr = domain_len;
- os_memcpy(domain_ptr + 1, tok_prev,
- domain_len);
- domain_ptr += domain_len + 1;
- }
- }
+ }
- os_free(bss->domain_name);
- bss->domain_name = domain_list;
- bss->domain_name_len = domain_list_len;
- } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
- if (parse_3gpp_cell_net(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "nai_realm") == 0) {
- if (parse_nai_realm(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "gas_frag_limit") == 0) {
- bss->gas_frag_limit = atoi(pos);
- } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
- bss->gas_comeback_delay = atoi(pos);
- } else if (os_strcmp(buf, "qos_map_set") == 0) {
- if (parse_qos_map_set(bss, pos, line) < 0)
- errors++;
+ os_free(bss->domain_name);
+ bss->domain_name = domain_list;
+ bss->domain_name_len = domain_list_len;
+ } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
+ if (parse_3gpp_cell_net(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "nai_realm") == 0) {
+ if (parse_nai_realm(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "gas_frag_limit") == 0) {
+ bss->gas_frag_limit = atoi(pos);
+ } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
+ bss->gas_comeback_delay = atoi(pos);
+ } else if (os_strcmp(buf, "qos_map_set") == 0) {
+ if (parse_qos_map_set(bss, pos, line) < 0)
+ return 1;
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_RADIUS_TEST
- } else if (os_strcmp(buf, "dump_msk_file") == 0) {
- os_free(bss->dump_msk_file);
- bss->dump_msk_file = os_strdup(pos);
+ } else if (os_strcmp(buf, "dump_msk_file") == 0) {
+ os_free(bss->dump_msk_file);
+ bss->dump_msk_file = os_strdup(pos);
#endif /* CONFIG_RADIUS_TEST */
#ifdef CONFIG_HS20
- } else if (os_strcmp(buf, "hs20") == 0) {
- bss->hs20 = atoi(pos);
- } else if (os_strcmp(buf, "disable_dgaf") == 0) {
- bss->disable_dgaf = atoi(pos);
- } else if (os_strcmp(buf, "osen") == 0) {
- bss->osen = atoi(pos);
- } else if (os_strcmp(buf, "anqp_domain_id") == 0) {
- bss->anqp_domain_id = atoi(pos);
- } else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
- bss->hs20_deauth_req_timeout = atoi(pos);
- } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
- if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) {
- if (hs20_parse_wan_metrics(bss, pos, line) < 0) {
- errors++;
- return errors;
- }
- } else if (os_strcmp(buf, "hs20_conn_capab") == 0) {
- if (hs20_parse_conn_capab(bss, pos, line) < 0) {
- errors++;
- return errors;
- }
- } else if (os_strcmp(buf, "hs20_operating_class") == 0) {
- u8 *oper_class;
- size_t oper_class_len;
- oper_class_len = os_strlen(pos);
- if (oper_class_len < 2 || (oper_class_len & 0x01)) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "hs20_operating_class '%s'",
- line, pos);
- errors++;
- return errors;
- }
- oper_class_len /= 2;
- oper_class = os_malloc(oper_class_len);
- if (oper_class == NULL) {
- errors++;
- return errors;
- }
- if (hexstr2bin(pos, oper_class, oper_class_len)) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "hs20_operating_class '%s'",
- line, pos);
- os_free(oper_class);
- errors++;
- return errors;
- }
- os_free(bss->hs20_operating_class);
- bss->hs20_operating_class = oper_class;
- bss->hs20_operating_class_len = oper_class_len;
- } else if (os_strcmp(buf, "hs20_icon") == 0) {
- if (hs20_parse_icon(bss, pos) < 0) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "hs20_icon '%s'", line, pos);
- errors++;
- return errors;
- }
- } else if (os_strcmp(buf, "osu_ssid") == 0) {
- if (hs20_parse_osu_ssid(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "osu_server_uri") == 0) {
- if (hs20_parse_osu_server_uri(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "osu_friendly_name") == 0) {
- if (hs20_parse_osu_friendly_name(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "osu_nai") == 0) {
- if (hs20_parse_osu_nai(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "osu_method_list") == 0) {
- if (hs20_parse_osu_method_list(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "osu_icon") == 0) {
- if (hs20_parse_osu_icon(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "osu_service_desc") == 0) {
- if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
- errors++;
- } else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
- os_free(bss->subscr_remediation_url);
- bss->subscr_remediation_url = os_strdup(pos);
- } else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
- bss->subscr_remediation_method = atoi(pos);
+ } else if (os_strcmp(buf, "hs20") == 0) {
+ bss->hs20 = atoi(pos);
+ } else if (os_strcmp(buf, "disable_dgaf") == 0) {
+ bss->disable_dgaf = atoi(pos);
+ } else if (os_strcmp(buf, "osen") == 0) {
+ bss->osen = atoi(pos);
+ } else if (os_strcmp(buf, "anqp_domain_id") == 0) {
+ bss->anqp_domain_id = atoi(pos);
+ } else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
+ bss->hs20_deauth_req_timeout = atoi(pos);
+ } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
+ if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) {
+ if (hs20_parse_wan_metrics(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "hs20_conn_capab") == 0) {
+ if (hs20_parse_conn_capab(bss, pos, line) < 0) {
+ return 1;
+ }
+ } else if (os_strcmp(buf, "hs20_operating_class") == 0) {
+ u8 *oper_class;
+ size_t oper_class_len;
+ oper_class_len = os_strlen(pos);
+ if (oper_class_len < 2 || (oper_class_len & 0x01)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid hs20_operating_class '%s'",
+ line, pos);
+ return 1;
+ }
+ oper_class_len /= 2;
+ oper_class = os_malloc(oper_class_len);
+ if (oper_class == NULL)
+ return 1;
+ if (hexstr2bin(pos, oper_class, oper_class_len)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid hs20_operating_class '%s'",
+ line, pos);
+ os_free(oper_class);
+ return 1;
+ }
+ os_free(bss->hs20_operating_class);
+ bss->hs20_operating_class = oper_class;
+ bss->hs20_operating_class_len = oper_class_len;
+ } else if (os_strcmp(buf, "hs20_icon") == 0) {
+ if (hs20_parse_icon(bss, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_icon '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "osu_ssid") == 0) {
+ if (hs20_parse_osu_ssid(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_server_uri") == 0) {
+ if (hs20_parse_osu_server_uri(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_friendly_name") == 0) {
+ if (hs20_parse_osu_friendly_name(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_nai") == 0) {
+ if (hs20_parse_osu_nai(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_method_list") == 0) {
+ if (hs20_parse_osu_method_list(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_icon") == 0) {
+ if (hs20_parse_osu_icon(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_service_desc") == 0) {
+ if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
+ os_free(bss->subscr_remediation_url);
+ bss->subscr_remediation_url = os_strdup(pos);
+ } else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
+ bss->subscr_remediation_method = atoi(pos);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_TESTING_OPTIONS
-#define PARSE_TEST_PROBABILITY(_val) \
- } else if (os_strcmp(buf, #_val) == 0) { \
- char *end; \
- \
- conf->_val = strtod(pos, &end); \
- if (*end || conf->_val < 0.0d || \
- conf->_val > 1.0d) { \
- wpa_printf(MSG_ERROR, \
- "Line %d: Invalid value '%s'", \
- line, pos); \
- errors++; \
- return errors; \
- }
- PARSE_TEST_PROBABILITY(ignore_probe_probability)
- PARSE_TEST_PROBABILITY(ignore_auth_probability)
- PARSE_TEST_PROBABILITY(ignore_assoc_probability)
- PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
- PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
- } else if (os_strcmp(buf, "bss_load_test") == 0) {
- WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
- pos = os_strchr(pos, ':');
- if (pos == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "bss_load_test", line);
- return 1;
- }
- pos++;
- bss->bss_load_test[2] = atoi(pos);
- pos = os_strchr(pos, ':');
- if (pos == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "bss_load_test", line);
- return 1;
- }
- pos++;
- WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos));
- bss->bss_load_test_set = 1;
-#endif /* CONFIG_TESTING_OPTIONS */
- } else if (os_strcmp(buf, "vendor_elements") == 0) {
- struct wpabuf *elems;
- size_t len = os_strlen(pos);
- if (len & 0x01) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "vendor_elements '%s'", line, pos);
- return 1;
- }
- len /= 2;
- if (len == 0) {
- wpabuf_free(bss->vendor_elements);
- bss->vendor_elements = NULL;
- return 0;
- }
-
- elems = wpabuf_alloc(len);
- if (elems == NULL)
- return 1;
-
- if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
- wpabuf_free(elems);
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "vendor_elements '%s'", line, pos);
- return 1;
- }
-
- wpabuf_free(bss->vendor_elements);
- bss->vendor_elements = elems;
- } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
- bss->sae_anti_clogging_threshold = atoi(pos);
- } else if (os_strcmp(buf, "sae_groups") == 0) {
- if (hostapd_parse_intlist(&bss->sae_groups, pos)) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid "
- "sae_groups value '%s'", line, pos);
- return 1;
- }
- } else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
- int val = atoi(pos);
- if (val < 0 || val > 255) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid local_pwr_constraint %d (expected 0..255)",
- line, val);
- return 1;
- }
- conf->local_pwr_constraint = val;
- } else if (os_strcmp(buf, "spectrum_mgmt_required") == 0) {
- conf->spectrum_mgmt_required = atoi(pos);
- } else {
- wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
- "item '%s'", line, buf);
- errors++;
+#define PARSE_TEST_PROBABILITY(_val) \
+ } else if (os_strcmp(buf, #_val) == 0) { \
+ char *end; \
+ \
+ conf->_val = strtod(pos, &end); \
+ if (*end || conf->_val < 0.0d || \
+ conf->_val > 1.0d) { \
+ wpa_printf(MSG_ERROR, \
+ "Line %d: Invalid value '%s'", \
+ line, pos); \
+ return 1; \
}
+ PARSE_TEST_PROBABILITY(ignore_probe_probability)
+ PARSE_TEST_PROBABILITY(ignore_auth_probability)
+ PARSE_TEST_PROBABILITY(ignore_assoc_probability)
+ PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
+ PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
+ } else if (os_strcmp(buf, "bss_load_test") == 0) {
+ WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
+ pos = os_strchr(pos, ':');
+ if (pos == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid bss_load_test",
+ line);
+ return 1;
+ }
+ pos++;
+ bss->bss_load_test[2] = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid bss_load_test",
+ line);
+ return 1;
+ }
+ pos++;
+ WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos));
+ bss->bss_load_test_set = 1;
+#endif /* CONFIG_TESTING_OPTIONS */
+ } else if (os_strcmp(buf, "vendor_elements") == 0) {
+ struct wpabuf *elems;
+ size_t len = os_strlen(pos);
+ if (len & 0x01) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid vendor_elements '%s'",
+ line, pos);
+ return 1;
+ }
+ len /= 2;
+ if (len == 0) {
+ wpabuf_free(bss->vendor_elements);
+ bss->vendor_elements = NULL;
+ return 0;
+ }
+
+ elems = wpabuf_alloc(len);
+ if (elems == NULL)
+ return 1;
+
+ if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
+ wpabuf_free(elems);
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid vendor_elements '%s'",
+ line, pos);
+ return 1;
+ }
+
+ wpabuf_free(bss->vendor_elements);
+ bss->vendor_elements = elems;
+ } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
+ bss->sae_anti_clogging_threshold = atoi(pos);
+ } else if (os_strcmp(buf, "sae_groups") == 0) {
+ if (hostapd_parse_intlist(&bss->sae_groups, pos)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid sae_groups value '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
+ int val = atoi(pos);
+ if (val < 0 || val > 255) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid local_pwr_constraint %d (expected 0..255)",
+ line, val);
+ return 1;
+ }
+ conf->local_pwr_constraint = val;
+ } else if (os_strcmp(buf, "spectrum_mgmt_required") == 0) {
+ conf->spectrum_mgmt_required = atoi(pos);
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+ line, buf);
+ return 1;
}
- return errors;
+ return 0;
}
diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk
new file mode 100644
index 0000000..b7bd932
--- /dev/null
+++ b/hs20/client/Android.mk
@@ -0,0 +1,73 @@
+LOCAL_PATH := $(call my-dir)
+
+INCLUDES = $(LOCAL_PATH)
+INCLUDES += $(LOCAL_PATH)/../../src/utils
+INCLUDES += $(LOCAL_PATH)/../../src/common
+INCLUDES += $(LOCAL_PATH)/../../src
+INCLUDES += external/openssl/include
+INCLUDES += external/libxml2/include
+INCLUDES += external/curl/include
+INCLUDES += external/webkit/Source/WebKit/gtk
+INCLUDES += external/icu4c/common
+
+
+#GTKCFLAGS := $(shell pkg-config --cflags gtk+-2.0 webkit-1.0)
+#GTKLIBS := $(shell pkg-config --libs gtk+-2.0 webkit-1.0)
+#CFLAGS += $(GTKCFLAGS)
+#LIBS += $(GTKLIBS)
+
+L_CFLAGS += -DCONFIG_CTRL_IFACE
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
+L_CFLAGS += -DLIBXML_SCHEMAS_ENABLED
+L_CFLAGS += -DLIBXML_REGEXP_ENABLED
+
+OBJS = spp_client.c
+OBJS += oma_dm_client.c
+OBJS += osu_client.c
+OBJS += est.c
+OBJS += ../../src/common/wpa_ctrl.c
+OBJS += ../../src/common/wpa_helpers.c
+OBJS += ../../src/utils/xml-utils.c
+#OBJS += ../../src/utils/browser-android.c
+OBJS += ../../src/utils/browser-wpadebug.c
+OBJS += ../../src/utils/wpabuf.c
+OBJS += ../../src/utils/eloop.c
+OBJS += ../../src/wps/httpread.c
+OBJS += ../../src/wps/http_server.c
+OBJS += ../../src/utils/xml_libxml2.c
+OBJS += ../../src/utils/http_curl.c
+OBJS += ../../src/utils/base64.c
+OBJS += ../../src/utils/os_unix.c
+L_CFLAGS += -DCONFIG_DEBUG_FILE
+OBJS += ../../src/utils/wpa_debug.c
+OBJS += ../../src/utils/common.c
+OBJS += ../../src/crypto/crypto_internal.c
+OBJS += ../../src/crypto/md5-internal.c
+OBJS += ../../src/crypto/sha1-internal.c
+OBJS += ../../src/crypto/sha256-internal.c
+
+L_CFLAGS += -DEAP_TLS_OPENSSL
+
+#CFLAGS += $(shell xml2-config --cflags)
+#LIBS += $(shell xml2-config --libs)
+
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := hs20-osu-client
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_SHARED_LIBRARIES += libcrypto libssl
+#LOCAL_SHARED_LIBRARIES += libxml2
+LOCAL_STATIC_LIBRARIES += libxml2
+LOCAL_SHARED_LIBRARIES += libicuuc
+LOCAL_SHARED_LIBRARIES += libcurl
+
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS)
+LOCAL_C_INCLUDES := $(INCLUDES)
+include $(BUILD_EXECUTABLE)
+
+########################
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
new file mode 100644
index 0000000..ca67b54
--- /dev/null
+++ b/hs20/client/Makefile
@@ -0,0 +1,94 @@
+all: hs20-osu-client
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I../../src/utils
+CFLAGS += -I../../src/common
+CFLAGS += -I../../src
+
+ifndef CONFIG_NO_BROWSER
+ifndef CONFIG_BROWSER_SYSTEM
+GTKCFLAGS := $(shell pkg-config --cflags gtk+-3.0 webkitgtk-3.0)
+GTKLIBS := $(shell pkg-config --libs gtk+-3.0 webkitgtk-3.0)
+CFLAGS += $(GTKCFLAGS)
+LIBS += $(GTKLIBS)
+endif
+endif
+
+OBJS=spp_client.o
+OBJS += oma_dm_client.o
+OBJS += osu_client.o
+OBJS += est.o
+OBJS += ../../src/utils/xml-utils.o
+CFLAGS += -DCONFIG_CTRL_IFACE
+CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+OBJS += ../../src/common/wpa_ctrl.o ../../src/common/wpa_helpers.o
+ifdef CONFIG_NO_BROWSER
+CFLAGS += -DCONFIG_NO_BROWSER
+else
+ifdef CONFIG_BROWSER_SYSTEM
+OBJS += ../../src/utils/eloop.o
+OBJS += ../../src/utils/wpabuf.o
+OBJS += ../../src/wps/httpread.o
+OBJS += ../../src/wps/http_server.o
+OBJS += ../../src/utils/browser-system.o
+else
+OBJS += ../../src/utils/browser.o
+endif
+endif
+OBJS += ../../src/utils/xml_libxml2.o
+OBJS += ../../src/utils/http_curl.o
+OBJS += ../../src/utils/base64.o
+OBJS += ../../src/utils/os_unix.o
+CFLAGS += -DCONFIG_DEBUG_FILE
+OBJS += ../../src/utils/wpa_debug.o
+OBJS += ../../src/utils/common.o
+OBJS += ../../src/crypto/crypto_internal.o
+OBJS += ../../src/crypto/md5-internal.o
+OBJS += ../../src/crypto/sha1-internal.o
+OBJS += ../../src/crypto/sha256-internal.o
+
+CFLAGS += $(shell xml2-config --cflags)
+LIBS += $(shell xml2-config --libs)
+LIBS += -lcurl
+
+CFLAGS += -DEAP_TLS_OPENSSL
+LIBS += -lssl -lcrypto
+
+hs20-osu-client: $(OBJS)
+ $(Q)$(LDO) $(LDFLAGS) -o hs20-osu-client $(OBJS) $(LIBS)
+ @$(E) " LD " $@
+
+%.o: %.c
+ $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+ @$(E) " CC " $<
+
+clean:
+ rm -f core *~ *.o *.d hs20-osu-client
+ rm -f ../../src/utils/*.o
+ rm -f ../../src/utils/*.d
+ rm -f ../../src/common/*.o
+ rm -f ../../src/common/*.d
+ rm -f ../../src/crypto/*.o
+ rm -f ../../src/crypto/*.d
+ rm -f ../../src/wps/*.o
+ rm -f ../../src/wps/*.d
+
+-include $(OBJS:%.o=%.d)
diff --git a/hs20/client/devdetail.xml b/hs20/client/devdetail.xml
new file mode 100644
index 0000000..6d0389e
--- /dev/null
+++ b/hs20/client/devdetail.xml
@@ -0,0 +1,47 @@
+<DevDetail xmlns="urn:oma:mo:oma-dm-devdetail:1.0">
+ <Ext>
+ <org.wi-fi>
+ <Wi-Fi>
+ <EAPMethodList>
+ <EAPMethod1>
+ <EAPType>13</EAPType>
+ </EAPMethod1>
+ <EAPMethod2>
+ <EAPType>21</EAPType>
+ <InnerMethod>MS-CHAP-V2</InnerMethod>
+ </EAPMethod2>
+ <EAPMethod3>
+ <EAPType>18</EAPType>
+ </EAPMethod3>
+ <EAPMethod4>
+ <EAPType>23</EAPType>
+ </EAPMethod4>
+ <EAPMethod5>
+ <EAPType>50</EAPType>
+ </EAPMethod5>
+ </EAPMethodList>
+ <ManufacturingCertificate>false</ManufacturingCertificate>
+ <Wi-FiMACAddress>020102030405</Wi-FiMACAddress>
+ <IMSI>310026000000000</IMSI>
+ <IMEI_MEID>imei:490123456789012</IMEI_MEID>
+ <ClientTriggerRedirectURI>http://localhost:12345/</ClientTriggerRedirectURI>
+ <Ops>
+ <launchBrowserToURI></launchBrowserToURI>
+ <negotiateClientCertTLS></negotiateClientCertTLS>
+ <getCertificate></getCertificate>
+ </Ops>
+ </Wi-Fi>
+ </org.wi-fi>
+ </Ext>
+ <URI>
+ <MaxDepth>0</MaxDepth>
+ <MaxTotLen>0</MaxTotLen>
+ <MaxSegLen>0</MaxSegLen>
+ </URI>
+ <DevType>MobilePhone</DevType>
+ <OEM>Manufacturer</OEM>
+ <FwV>1.0</FwV>
+ <SwV>1.0</SwV>
+ <HwV>1.0</HwV>
+ <LrgObj>false</LrgObj>
+</DevDetail>
diff --git a/hs20/client/devinfo.xml b/hs20/client/devinfo.xml
new file mode 100644
index 0000000..d48a520
--- /dev/null
+++ b/hs20/client/devinfo.xml
@@ -0,0 +1,7 @@
+<DevInfo xmlns="urn:oma:mo:oma-dm-devinfo:1.0">
+ <DevId>urn:Example:HS20-station:123456</DevId>
+ <Man>Manufacturer</Man>
+ <Mod>HS20-station</Mod>
+ <DmV>1.2</DmV>
+ <Lang>en</Lang>
+</DevInfo>
diff --git a/hs20/client/est.c b/hs20/client/est.c
new file mode 100644
index 0000000..c1d514a
--- /dev/null
+++ b/hs20/client/est.c
@@ -0,0 +1,700 @@
+/*
+ * Hotspot 2.0 OSU client - EST client
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rsa.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "common.h"
+#include "utils/base64.h"
+#include "utils/xml-utils.h"
+#include "utils/http-utils.h"
+#include "osu_client.h"
+
+
+static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
+ size_t len, char *pem_file, char *der_file)
+{
+ PKCS7 *p7 = NULL;
+ const unsigned char *p = pkcs7;
+ STACK_OF(X509) *certs;
+ int i, num, ret = -1;
+ BIO *out = NULL;
+
+ p7 = d2i_PKCS7(NULL, &p, len);
+ if (p7 == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ write_result(ctx, "Could not parse PKCS#7 object from EST");
+ goto fail;
+ }
+
+ switch (OBJ_obj2nid(p7->type)) {
+ case NID_pkcs7_signed:
+ certs = p7->d.sign->cert;
+ break;
+ case NID_pkcs7_signedAndEnveloped:
+ certs = p7->d.signed_and_enveloped->cert;
+ break;
+ default:
+ certs = NULL;
+ break;
+ }
+
+ if (!certs || ((num = sk_X509_num(certs)) == 0)) {
+ wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
+ write_result(ctx, "No certificates found in PKCS#7 object");
+ goto fail;
+ }
+
+ if (der_file) {
+ FILE *f = fopen(der_file, "wb");
+ if (f == NULL)
+ goto fail;
+ i2d_X509_fp(f, sk_X509_value(certs, 0));
+ fclose(f);
+ }
+
+ if (pem_file) {
+ out = BIO_new(BIO_s_file());
+ if (out == NULL ||
+ BIO_write_filename(out, pem_file) <= 0)
+ goto fail;
+
+ for (i = 0; i < num; i++) {
+ X509 *cert = sk_X509_value(certs, i);
+ X509_print(out, cert);
+ PEM_write_bio_X509(out, cert);
+ BIO_puts(out, "\n");
+ }
+ }
+
+ ret = 0;
+
+fail:
+ PKCS7_free(p7);
+ if (out)
+ BIO_free_all(out);
+
+ return ret;
+}
+
+
+int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
+{
+ char *buf, *resp;
+ size_t buflen;
+ unsigned char *pkcs7;
+ size_t pkcs7_len, resp_len;
+ int res;
+
+ buflen = os_strlen(url) + 100;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return -1;
+
+ os_snprintf(buf, buflen, "%s/cacerts", url);
+ wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf);
+ write_summary(ctx, "Download EST cacerts from %s", buf);
+ res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
+ ctx->ca_fname);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s",
+ buf);
+ write_result(ctx, "Failed to download EST cacerts from %s",
+ buf);
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+
+ resp = os_readfile("Cert/est-cacerts.txt", &resp_len);
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt");
+ write_result(ctx, "Could not read EST cacerts");
+ return -1;
+ }
+
+ pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+ if (pkcs7 && pkcs7_len < resp_len / 2) {
+ wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
+ (unsigned int) pkcs7_len, (unsigned int) resp_len);
+ os_free(pkcs7);
+ pkcs7 = NULL;
+ }
+ if (pkcs7 == NULL) {
+ wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
+ pkcs7 = os_malloc(resp_len);
+ if (pkcs7) {
+ os_memcpy(pkcs7, resp, resp_len);
+ pkcs7_len = resp_len;
+ }
+ }
+ os_free(resp);
+
+ if (pkcs7 == NULL) {
+ wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts");
+ write_result(ctx, "Could not fetch EST PKCS#7 cacerts");
+ return -1;
+ }
+
+ res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem",
+ NULL);
+ os_free(pkcs7);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response");
+ write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response");
+ return -1;
+ }
+ unlink("Cert/est-cacerts.txt");
+
+ return 0;
+}
+
+
+/*
+ * CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID
+ *
+ * AttrOrOID ::= CHOICE {
+ * oid OBJECT IDENTIFIER,
+ * attribute Attribute }
+ *
+ * Attribute ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * values SET SIZE(1..MAX) OF OBJECT IDENTIFIER }
+ */
+
+typedef struct {
+ ASN1_OBJECT *type;
+ STACK_OF(ASN1_OBJECT) *values;
+} Attribute;
+
+typedef struct {
+ int type;
+ union {
+ ASN1_OBJECT *oid;
+ Attribute *attribute;
+ } d;
+} AttrOrOID;
+
+typedef struct {
+ int type;
+ STACK_OF(AttrOrOID) *attrs;
+} CsrAttrs;
+
+ASN1_SEQUENCE(Attribute) = {
+ ASN1_SIMPLE(Attribute, type, ASN1_OBJECT),
+ ASN1_SET_OF(Attribute, values, ASN1_OBJECT)
+} ASN1_SEQUENCE_END(Attribute);
+
+ASN1_CHOICE(AttrOrOID) = {
+ ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT),
+ ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute)
+} ASN1_CHOICE_END(AttrOrOID);
+
+ASN1_CHOICE(CsrAttrs) = {
+ ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID)
+} ASN1_CHOICE_END(CsrAttrs);
+
+IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs);
+
+
+static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid,
+ STACK_OF(X509_EXTENSION) *exts)
+{
+ char txt[100];
+ int res;
+
+ if (!oid)
+ return;
+
+ res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ return;
+
+ if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) {
+ wpa_printf(MSG_INFO, "TODO: csrattr challengePassword");
+ } else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) {
+ wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption");
+ } else {
+ wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt);
+ }
+}
+
+
+static void add_csrattrs_ext_req(struct hs20_osu_client *ctx,
+ STACK_OF(ASN1_OBJECT) *values,
+ STACK_OF(X509_EXTENSION) *exts)
+{
+ char txt[100];
+ int i, num, res;
+
+ num = sk_ASN1_OBJECT_num(values);
+ for (i = 0; i < num; i++) {
+ ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i);
+
+ res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ continue;
+
+ if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) {
+ wpa_printf(MSG_INFO, "TODO: extReq macAddress");
+ } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) {
+ wpa_printf(MSG_INFO, "TODO: extReq imei");
+ } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) {
+ wpa_printf(MSG_INFO, "TODO: extReq meid");
+ } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) {
+ wpa_printf(MSG_INFO, "TODO: extReq DevId");
+ } else {
+ wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s",
+ txt);
+ }
+ }
+}
+
+
+static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr,
+ STACK_OF(X509_EXTENSION) *exts)
+{
+ char txt[100], txt2[100];
+ int i, num, res;
+
+ if (!attr || !attr->type || !attr->values)
+ return;
+
+ res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ return;
+
+ if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) {
+ add_csrattrs_ext_req(ctx, attr->values, exts);
+ return;
+ }
+
+ num = sk_ASN1_OBJECT_num(attr->values);
+ for (i = 0; i < num; i++) {
+ ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i);
+
+ res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1);
+ if (res < 0 || res >= (int) sizeof(txt2))
+ continue;
+
+ wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s",
+ txt, txt2);
+ }
+}
+
+
+static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs,
+ STACK_OF(X509_EXTENSION) *exts)
+{
+ int i, num;
+
+ if (!csrattrs || ! csrattrs->attrs)
+ return;
+
+ num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
+ for (i = 0; i < num; i++) {
+ AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
+ switch (ao->type) {
+ case 0:
+ add_csrattrs_oid(ctx, ao->d.oid, exts);
+ break;
+ case 1:
+ add_csrattrs_attr(ctx, ao->d.attribute, exts);
+ break;
+ }
+ }
+}
+
+
+static int generate_csr(struct hs20_osu_client *ctx, char *key_pem,
+ char *csr_pem, char *est_req, char *old_cert,
+ CsrAttrs *csrattrs)
+{
+ EVP_PKEY_CTX *pctx = NULL;
+ EVP_PKEY *pkey = NULL;
+ RSA *rsa;
+ X509_REQ *req = NULL;
+ int ret = -1;
+ unsigned int val;
+ X509_NAME *subj = NULL;
+ char name[100];
+ STACK_OF(X509_EXTENSION) *exts = NULL;
+ X509_EXTENSION *ex;
+ BIO *out;
+
+ wpa_printf(MSG_INFO, "Generate RSA private key");
+ write_summary(ctx, "Generate RSA private key");
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+ if (!pctx)
+ return -1;
+
+ if (EVP_PKEY_keygen_init(pctx) <= 0)
+ goto fail;
+
+ if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0)
+ goto fail;
+
+ if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
+ goto fail;
+ EVP_PKEY_CTX_free(pctx);
+ pctx = NULL;
+
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa == NULL)
+ goto fail;
+
+ if (key_pem) {
+ FILE *f = fopen(key_pem, "wb");
+ if (f == NULL)
+ goto fail;
+ if (!PEM_write_RSAPrivateKey(f, rsa, NULL, NULL, 0, NULL,
+ NULL)) {
+ wpa_printf(MSG_INFO, "Could not write private key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ fclose(f);
+ goto fail;
+ }
+ fclose(f);
+ }
+
+ wpa_printf(MSG_INFO, "Generate CSR");
+ write_summary(ctx, "Generate CSR");
+ req = X509_REQ_new();
+ if (req == NULL)
+ goto fail;
+
+ if (old_cert) {
+ FILE *f;
+ X509 *cert;
+ int res;
+
+ f = fopen(old_cert, "r");
+ if (f == NULL)
+ goto fail;
+ cert = PEM_read_X509(f, NULL, NULL, NULL);
+ fclose(f);
+
+ if (cert == NULL)
+ goto fail;
+ res = X509_REQ_set_subject_name(req,
+ X509_get_subject_name(cert));
+ X509_free(cert);
+ if (!res)
+ goto fail;
+ } else {
+ os_get_random((u8 *) &val, sizeof(val));
+ os_snprintf(name, sizeof(name), "cert-user-%u", val);
+ subj = X509_NAME_new();
+ if (subj == NULL ||
+ !X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
+ (unsigned char *) name,
+ -1, -1, 0) ||
+ !X509_REQ_set_subject_name(req, subj))
+ goto fail;
+ X509_NAME_free(subj);
+ subj = NULL;
+ }
+
+ if (!X509_REQ_set_pubkey(req, pkey))
+ goto fail;
+
+ exts = sk_X509_EXTENSION_new_null();
+ if (!exts)
+ goto fail;
+
+ ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
+ "CA:FALSE");
+ if (ex == NULL ||
+ !sk_X509_EXTENSION_push(exts, ex))
+ goto fail;
+
+ ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
+ "nonRepudiation,digitalSignature,keyEncipherment");
+ if (ex == NULL ||
+ !sk_X509_EXTENSION_push(exts, ex))
+ goto fail;
+
+ ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage,
+ "1.3.6.1.4.1.40808.1.1.2");
+ if (ex == NULL ||
+ !sk_X509_EXTENSION_push(exts, ex))
+ goto fail;
+
+ add_csrattrs(ctx, csrattrs, exts);
+
+ if (!X509_REQ_add_extensions(req, exts))
+ goto fail;
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ exts = NULL;
+
+ if (!X509_REQ_sign(req, pkey, EVP_sha256()))
+ goto fail;
+
+ out = BIO_new(BIO_s_mem());
+ if (out) {
+ char *txt;
+ size_t rlen;
+
+ X509_REQ_print(out, req);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ int res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s",
+ txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+ }
+
+ if (csr_pem) {
+ FILE *f = fopen(csr_pem, "w");
+ if (f == NULL)
+ goto fail;
+ X509_REQ_print_fp(f, req);
+ if (!PEM_write_X509_REQ(f, req)) {
+ fclose(f);
+ goto fail;
+ }
+ fclose(f);
+ }
+
+ if (est_req) {
+ BIO *mem = BIO_new(BIO_s_mem());
+ BUF_MEM *ptr;
+ char *pos, *end, *buf_end;
+ FILE *f;
+
+ if (mem == NULL)
+ goto fail;
+ if (!PEM_write_bio_X509_REQ(mem, req)) {
+ BIO_free(mem);
+ goto fail;
+ }
+
+ BIO_get_mem_ptr(mem, &ptr);
+ pos = ptr->data;
+ buf_end = pos + ptr->length;
+
+ /* Remove START/END lines */
+ while (pos < buf_end && *pos != '\n')
+ pos++;
+ if (pos == buf_end) {
+ BIO_free(mem);
+ goto fail;
+ }
+ pos++;
+
+ end = pos;
+ while (end < buf_end && *end != '-')
+ end++;
+
+ f = fopen(est_req, "w");
+ if (f == NULL) {
+ BIO_free(mem);
+ goto fail;
+ }
+ fwrite(pos, end - pos, 1, f);
+ fclose(f);
+
+ BIO_free(mem);
+ }
+
+ ret = 0;
+fail:
+ if (exts)
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ if (subj)
+ X509_NAME_free(subj);
+ if (req)
+ X509_REQ_free(req);
+ if (pkey)
+ EVP_PKEY_free(pkey);
+ if (pctx)
+ EVP_PKEY_CTX_free(pctx);
+ return ret;
+}
+
+
+int est_build_csr(struct hs20_osu_client *ctx, const char *url)
+{
+ char *buf;
+ size_t buflen;
+ int res;
+ char old_cert_buf[200];
+ char *old_cert = NULL;
+ CsrAttrs *csrattrs = NULL;
+
+ buflen = os_strlen(url) + 100;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return -1;
+
+ os_snprintf(buf, buflen, "%s/csrattrs", url);
+ wpa_printf(MSG_INFO, "Download csrattrs from %s", buf);
+ write_summary(ctx, "Download EST csrattrs from %s", buf);
+ res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
+ ctx->ca_fname);
+ os_free(buf);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed");
+ } else {
+ size_t resp_len;
+ char *resp;
+ unsigned char *attrs;
+ const unsigned char *pos;
+ size_t attrs_len;
+
+ resp = os_readfile("Cert/est-csrattrs.txt", &resp_len);
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "Could not read csrattrs");
+ return -1;
+ }
+
+ attrs = base64_decode((unsigned char *) resp, resp_len,
+ &attrs_len);
+ os_free(resp);
+
+ if (attrs == NULL) {
+ wpa_printf(MSG_INFO, "Could not base64 decode csrattrs");
+ return -1;
+ }
+ unlink("Cert/est-csrattrs.txt");
+
+ pos = attrs;
+ csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len);
+ os_free(attrs);
+ if (csrattrs == NULL) {
+ wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1");
+ /* Continue assuming no additional requirements */
+ }
+ }
+
+ if (ctx->client_cert_present) {
+ os_snprintf(old_cert_buf, sizeof(old_cert_buf),
+ "SP/%s/client-cert.pem", ctx->fqdn);
+ old_cert = old_cert_buf;
+ }
+
+ res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem",
+ "Cert/est-req.b64", old_cert, csrattrs);
+ if (csrattrs)
+ CsrAttrs_free(csrattrs);
+
+ return res;
+}
+
+
+int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
+ const char *user, const char *pw)
+{
+ char *buf, *resp, *req, *req2;
+ size_t buflen, resp_len, len, pkcs7_len;
+ unsigned char *pkcs7;
+ FILE *f;
+ char client_cert_buf[200];
+ char client_key_buf[200];
+ const char *client_cert = NULL, *client_key = NULL;
+ int res;
+
+ req = os_readfile("Cert/est-req.b64", &len);
+ if (req == NULL) {
+ wpa_printf(MSG_INFO, "Could not read Cert/req.b64");
+ return -1;
+ }
+ req2 = os_realloc(req, len + 1);
+ if (req2 == NULL) {
+ os_free(req);
+ return -1;
+ }
+ req2[len] = '\0';
+ req = req2;
+ wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req);
+
+ buflen = os_strlen(url) + 100;
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ os_free(req);
+ return -1;
+ }
+
+ if (ctx->client_cert_present) {
+ os_snprintf(buf, buflen, "%s/simplereenroll", url);
+ os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+ "SP/%s/client-cert.pem", ctx->fqdn);
+ client_cert = client_cert_buf;
+ os_snprintf(client_key_buf, sizeof(client_key_buf),
+ "SP/%s/client-key.pem", ctx->fqdn);
+ client_key = client_key_buf;
+ } else
+ os_snprintf(buf, buflen, "%s/simpleenroll", url);
+ wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf);
+ write_summary(ctx, "EST simpleenroll URL: %s", buf);
+ resp = http_post(ctx->http, buf, req, "application/pkcs10",
+ "Content-Transfer-Encoding: base64",
+ ctx->ca_fname, user, pw, client_cert, client_key,
+ &resp_len);
+ os_free(buf);
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "EST certificate enrollment failed");
+ write_result(ctx, "EST certificate enrollment failed");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
+ f = fopen("Cert/est-resp.raw", "w");
+ if (f) {
+ fwrite(resp, resp_len, 1, f);
+ fclose(f);
+ }
+
+ pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+ if (pkcs7 == NULL) {
+ wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
+ pkcs7 = os_malloc(resp_len);
+ if (pkcs7) {
+ os_memcpy(pkcs7, resp, resp_len);
+ pkcs7_len = resp_len;
+ }
+ }
+ os_free(resp);
+
+ if (pkcs7 == NULL) {
+ wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response");
+ write_result(ctx, "Failed to parse EST simpleenroll base64 response");
+ return -1;
+ }
+
+ res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem",
+ "Cert/est_cert.der");
+ os_free(pkcs7);
+
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file");
+ write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "EST simple%senroll completed successfully",
+ ctx->client_cert_present ? "re" : "");
+ write_summary(ctx, "EST simple%senroll completed successfully",
+ ctx->client_cert_present ? "re" : "");
+
+ return 0;
+}
diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c
new file mode 100644
index 0000000..8fc350b
--- /dev/null
+++ b/hs20/client/oma_dm_client.c
@@ -0,0 +1,1374 @@
+/*
+ * Hotspot 2.0 - OMA DM client
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_helpers.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+#include "utils/browser.h"
+#include "osu_client.h"
+
+
+#define DM_SERVER_INITIATED_MGMT 1200
+#define DM_CLIENT_INITIATED_MGMT 1201
+#define DM_GENERIC_ALERT 1226
+
+/* OMA-TS-SyncML-RepPro-V1_2_2 - 10. Response Status Codes */
+#define DM_RESP_OK 200
+#define DM_RESP_AUTH_ACCEPTED 212
+#define DM_RESP_CHUNKED_ITEM_ACCEPTED 213
+#define DM_RESP_NOT_EXECUTED 215
+#define DM_RESP_ATOMIC_ROLL_BACK_OK 216
+#define DM_RESP_NOT_MODIFIED 304
+#define DM_RESP_BAD_REQUEST 400
+#define DM_RESP_UNAUTHORIZED 401
+#define DM_RESP_FORBIDDEN 403
+#define DM_RESP_NOT_FOUND 404
+#define DM_RESP_COMMAND_NOT_ALLOWED 405
+#define DM_RESP_OPTIONAL_FEATURE_NOT_SUPPORTED 406
+#define DM_RESP_MISSING_CREDENTIALS 407
+#define DM_RESP_CONFLICT 409
+#define DM_RESP_GONE 410
+#define DM_RESP_INCOMPLETE_COMMAND 412
+#define DM_RESP_REQ_ENTITY_TOO_LARGE 413
+#define DM_RESP_URI_TOO_LONG 414
+#define DM_RESP_UNSUPPORTED_MEDIA_TYPE_OR_FORMAT 415
+#define DM_RESP_REQ_TOO_BIG 416
+#define DM_RESP_ALREADY_EXISTS 418
+#define DM_RESP_DEVICE_FULL 420
+#define DM_RESP_SIZE_MISMATCH 424
+#define DM_RESP_PERMISSION_DENIED 425
+#define DM_RESP_COMMAND_FAILED 500
+#define DM_RESP_COMMAND_NOT_IMPLEMENTED 501
+#define DM_RESP_ATOMIC_ROLL_BACK_FAILED 516
+
+#define DM_HS20_SUBSCRIPTION_CREATION \
+ "org.wi-fi.hotspot2dot0.SubscriptionCreation"
+#define DM_HS20_SUBSCRIPTION_PROVISIONING \
+ "org.wi-fi.hotspot2dot0.SubscriptionProvisioning"
+#define DM_HS20_SUBSCRIPTION_REMEDIATION \
+ "org.wi-fi.hotspot2dot0.SubscriptionRemediation"
+#define DM_HS20_POLICY_UPDATE \
+ "org.wi-fi.hotspot2dot0.PolicyUpdate"
+
+#define DM_URI_PPS "./Wi-Fi/org.wi-fi/PerProviderSubscription"
+#define DM_URI_LAUNCH_BROWSER \
+ "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/launchBrowserToURI"
+
+
+static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
+ const char *locuri, const char *data);
+
+
+static const char * int2str(int val)
+{
+ static char buf[20];
+ snprintf(buf, sizeof(buf), "%d", val);
+ return buf;
+}
+
+
+static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx,
+ xml_node_t *node)
+{
+ xml_node_t *locuri;
+ char *uri, *ret = NULL;
+
+ locuri = get_node(ctx->xml, node, "Item/Target/LocURI");
+ if (locuri == NULL)
+ return NULL;
+
+ uri = xml_node_get_text(ctx->xml, locuri);
+ if (uri)
+ ret = os_strdup(uri);
+ xml_node_get_text_free(ctx->xml, uri);
+ return ret;
+}
+
+
+static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent,
+ const char *element, const char *uri)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, element);
+ if (node == NULL)
+ return;
+ xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri);
+}
+
+
+static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
+ const char *url, int msgid)
+{
+ xml_node_t *syncml, *synchdr;
+ xml_namespace_t *ns;
+
+ syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
+ "SyncML");
+
+ synchdr = xml_node_create(ctx->xml, syncml, NULL, "SyncHdr");
+ xml_node_create_text(ctx->xml, synchdr, NULL, "VerDTD", "1.2");
+ xml_node_create_text(ctx->xml, synchdr, NULL, "VerProto", "DM/1.2");
+ xml_node_create_text(ctx->xml, synchdr, NULL, "SessionID", "1");
+ xml_node_create_text(ctx->xml, synchdr, NULL, "MsgID", int2str(msgid));
+
+ oma_dm_add_locuri(ctx, synchdr, "Target", url);
+ oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid);
+
+ return syncml;
+}
+
+
+static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent,
+ int cmdid)
+{
+ xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid));
+}
+
+
+static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent,
+ int cmdid, int data)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "Alert");
+ if (node == NULL)
+ return NULL;
+ oma_dm_add_cmdid(ctx, node, cmdid);
+ xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
+
+ return node;
+}
+
+
+static xml_node_t * add_status(struct hs20_osu_client *ctx, xml_node_t *parent,
+ int msgref, int cmdref, int cmdid,
+ const char *cmd, int data, const char *targetref)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "Status");
+ if (node == NULL)
+ return NULL;
+ oma_dm_add_cmdid(ctx, node, cmdid);
+ xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
+ if (cmdref)
+ xml_node_create_text(ctx->xml, node, NULL, "CmdRef",
+ int2str(cmdref));
+ xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd);
+ xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
+ if (targetref) {
+ xml_node_create_text(ctx->xml, node, NULL, "TargetRef",
+ targetref);
+ }
+
+ return node;
+}
+
+
+static xml_node_t * add_results(struct hs20_osu_client *ctx, xml_node_t *parent,
+ int msgref, int cmdref, int cmdid,
+ const char *locuri, const char *data)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "Results");
+ if (node == NULL)
+ return NULL;
+
+ oma_dm_add_cmdid(ctx, node, cmdid);
+ xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
+ xml_node_create_text(ctx->xml, node, NULL, "CmdRef", int2str(cmdref));
+ add_item(ctx, node, locuri, data);
+
+ return node;
+}
+
+
+static char * mo_str(struct hs20_osu_client *ctx, const char *urn,
+ const char *fname)
+{
+ xml_node_t *fnode, *tnds;
+ char *str;
+
+ fnode = node_from_file(ctx->xml, fname);
+ if (!fnode)
+ return NULL;
+ tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2");
+ xml_node_free(ctx->xml, fnode);
+ if (!tnds)
+ return NULL;
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (str == NULL)
+ return NULL;
+ wpa_printf(MSG_INFO, "MgmtTree: %s", str);
+
+ return str;
+}
+
+
+static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
+ const char *locuri, const char *data)
+{
+ xml_node_t *item, *node;
+
+ item = xml_node_create(ctx->xml, parent, NULL, "Item");
+ oma_dm_add_locuri(ctx, item, "Source", locuri);
+ node = xml_node_create(ctx->xml, item, NULL, "Meta");
+ xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
+ "Chr");
+ xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type",
+ "text/plain");
+ xml_node_create_text(ctx->xml, item, NULL, "Data", data);
+}
+
+
+static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent,
+ int cmdid)
+{
+ xml_node_t *info, *child, *replace;
+ const char *name;
+ char locuri[200], *txt;
+
+ info = node_from_file(ctx->xml, "devinfo.xml");
+ if (info == NULL) {
+ wpa_printf(MSG_INFO, "Could not read devinfo.xml");
+ return;
+ }
+
+ replace = xml_node_create(ctx->xml, parent, NULL, "Replace");
+ if (replace == NULL) {
+ xml_node_free(ctx->xml, info);
+ return;
+ }
+ oma_dm_add_cmdid(ctx, replace, cmdid);
+
+ xml_node_for_each_child(ctx->xml, child, info) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ os_snprintf(locuri, sizeof(locuri), "./DevInfo/%s", name);
+ txt = xml_node_get_text(ctx->xml, child);
+ if (txt) {
+ add_item(ctx, replace, locuri, txt);
+ xml_node_get_text_free(ctx->xml, txt);
+ }
+ }
+
+ xml_node_free(ctx->xml, info);
+}
+
+
+static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client *ctx,
+ xml_node_t *syncbody,
+ int cmdid, const char *oper,
+ const char *data)
+{
+ xml_node_t *node, *item;
+ char buf[200];
+
+ node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT);
+
+ item = xml_node_create(ctx->xml, node, NULL, "Item");
+ oma_dm_add_locuri(ctx, item, "Source", DM_URI_PPS);
+ node = xml_node_create(ctx->xml, item, NULL, "Meta");
+ snprintf(buf, sizeof(buf), "Reversed-Domain-Name: %s", oper);
+ xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", buf);
+ xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
+ "xml");
+ xml_node_create_text(ctx->xml, item, NULL, "Data", data);
+}
+
+
+static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx,
+ const char *url, int msgid, const char *oper)
+{
+ xml_node_t *syncml, *syncbody;
+ char *str;
+ int cmdid = 0;
+
+ syncml = oma_dm_build_hdr(ctx, url, msgid);
+ if (syncml == NULL)
+ return NULL;
+
+ syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
+ if (syncbody == NULL) {
+ xml_node_free(ctx->xml, syncml);
+ return NULL;
+ }
+
+ cmdid++;
+ add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT);
+
+ str = mo_str(ctx, NULL, "devdetail.xml");
+ if (str == NULL) {
+ xml_node_free(ctx->xml, syncml);
+ return NULL;
+ }
+ cmdid++;
+ oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str);
+ os_free(str);
+
+ cmdid++;
+ add_replace_devinfo(ctx, syncbody, cmdid);
+
+ xml_node_create(ctx->xml, syncbody, NULL, "Final");
+
+ return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx,
+ const char *url, int msgid)
+{
+ xml_node_t *syncml;
+
+ syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION);
+ if (syncml)
+ debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml);
+
+ return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx,
+ const char *url, int msgid)
+{
+ xml_node_t *syncml;
+
+ syncml = build_oma_dm_1(ctx, url, msgid,
+ DM_HS20_SUBSCRIPTION_PROVISIONING);
+ if (syncml)
+ debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml);
+
+ return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx,
+ const char *url, int msgid)
+{
+ xml_node_t *syncml;
+
+ syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE);
+ if (syncml)
+ debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml);
+
+ return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx,
+ const char *url, int msgid)
+{
+ xml_node_t *syncml;
+
+ syncml = build_oma_dm_1(ctx, url, msgid,
+ DM_HS20_SUBSCRIPTION_REMEDIATION);
+ if (syncml)
+ debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml);
+
+ return syncml;
+}
+
+
+static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+ xml_node_t *node;
+ char *data;
+ int res;
+
+ node = get_node(ctx->xml, exec, "Item/Data");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Data node found");
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ data = xml_node_get_text(ctx->xml, node);
+ wpa_printf(MSG_INFO, "Data: %s", data);
+ wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
+ write_summary(ctx, "Launch browser to URI '%s'", data);
+ res = hs20_web_browser(data);
+ xml_node_get_text_free(ctx->xml, data);
+ if (res > 0) {
+ wpa_printf(MSG_INFO, "User response in browser completed successfully");
+ write_summary(ctx, "User response in browser completed successfully");
+ return DM_RESP_OK;
+ } else {
+ wpa_printf(MSG_INFO, "Failed to receive user response");
+ write_summary(ctx, "Failed to receive user response");
+ return DM_RESP_COMMAND_FAILED;
+ }
+}
+
+
+static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+ xml_node_t *node, *getcert;
+ char *data;
+ const char *name;
+ int res;
+
+ wpa_printf(MSG_INFO, "Client certificate enrollment");
+ write_summary(ctx, "Client certificate enrollment");
+
+ node = get_node(ctx->xml, exec, "Item/Data");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Data node found");
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ data = xml_node_get_text(ctx->xml, node);
+ wpa_printf(MSG_INFO, "Data: %s", data);
+ getcert = xml_node_from_buf(ctx->xml, data);
+ xml_node_get_text_free(ctx->xml, data);
+
+ if (getcert == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
+
+ name = xml_node_get_localname(ctx->xml, getcert);
+ if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
+ wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
+ name);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ res = osu_get_certificate(ctx, getcert);
+
+ xml_node_free(ctx->xml, getcert);
+
+ return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
+}
+
+
+static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+ char *locuri;
+ int ret;
+
+ locuri = oma_dm_get_target_locuri(ctx, exec);
+ if (locuri == NULL) {
+ wpa_printf(MSG_INFO, "No Target LocURI node found");
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
+
+ if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
+ "launchBrowserToURI") == 0) {
+ ret = oma_dm_exec_browser(ctx, exec);
+ } else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
+ "getCertificate") == 0) {
+ ret = oma_dm_exec_get_cert(ctx, exec);
+ } else {
+ wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
+ ret = DM_RESP_NOT_FOUND;
+ }
+ os_free(locuri);
+
+ return ret;
+}
+
+
+static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
+ xml_node_t *add, xml_node_t *pps,
+ const char *pps_fname)
+{
+ const char *pos;
+ size_t fqdn_len;
+ xml_node_t *node, *tnds, *unode, *pps_node;
+ char *data, *uri, *upos, *end;
+ int use_tnds = 0;
+ size_t uri_len;
+
+ wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
+
+ if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos = locuri + 8;
+
+ if (ctx->fqdn == NULL)
+ return DM_RESP_COMMAND_FAILED;
+ fqdn_len = os_strlen(ctx->fqdn);
+ if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+ pos[fqdn_len] != '/') {
+ wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
+ ctx->fqdn);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += fqdn_len + 1;
+
+ if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+ wpa_printf(MSG_INFO,
+ "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
+ ctx->fqdn);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += 24;
+
+ wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
+
+ pps_node = get_node(ctx->xml, pps, pos);
+ if (pps_node) {
+ wpa_printf(MSG_INFO, "Specified PPS node exists already");
+ return DM_RESP_ALREADY_EXISTS;
+ }
+
+ uri = os_strdup(pos);
+ if (uri == NULL)
+ return DM_RESP_COMMAND_FAILED;
+ while (!pps_node) {
+ upos = os_strrchr(uri, '/');
+ if (!upos)
+ break;
+ upos[0] = '\0';
+ pps_node = get_node(ctx->xml, pps, uri);
+ wpa_printf(MSG_INFO, "Node %s %s", uri,
+ pps_node ? "exists" : "does not exist");
+ }
+
+ wpa_printf(MSG_INFO, "Parent URI: %s", uri);
+
+ if (!pps_node) {
+ /* Add at root of PPS MO */
+ pps_node = pps;
+ }
+
+ uri_len = os_strlen(uri);
+ os_strlcpy(uri, pos + uri_len, os_strlen(pos));
+ upos = uri;
+ while (*upos == '/')
+ upos++;
+ wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
+
+ for (;;) {
+ end = os_strchr(upos, '/');
+ if (!end)
+ break;
+ *end = '\0';
+ wpa_printf(MSG_INFO, "Adding interim node %s", upos);
+ pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
+ if (pps_node == NULL) {
+ os_free(uri);
+ return DM_RESP_COMMAND_FAILED;
+ }
+ upos = end + 1;
+ }
+
+ wpa_printf(MSG_INFO, "Adding node %s", upos);
+
+ node = get_node(ctx->xml, add, "Item/Meta/Type");
+ if (node) {
+ char *type;
+ type = xml_node_get_text(ctx->xml, node);
+ use_tnds = node &&
+ os_strstr(type, "application/vnd.syncml.dmtnds+xml");
+ }
+
+ node = get_node(ctx->xml, add, "Item/Data");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Add/Item/Data found");
+ os_free(uri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ data = xml_node_get_text(ctx->xml, node);
+ if (data == NULL) {
+ wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
+ os_free(uri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
+
+ if (use_tnds) {
+ tnds = xml_node_from_buf(ctx->xml, data);
+ xml_node_get_text_free(ctx->xml, data);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO,
+ "Could not parse Add/Item/Data text");
+ os_free(uri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ unode = tnds_to_mo(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (unode == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse TNDS text");
+ os_free(uri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ debug_dump_node(ctx, "Parsed TNDS", unode);
+
+ xml_node_add_child(ctx->xml, pps_node, unode);
+ } else {
+ /* TODO: What to do here? */
+ os_free(uri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ os_free(uri);
+
+ if (update_pps_file(ctx, pps_fname, pps) < 0)
+ return DM_RESP_COMMAND_FAILED;
+
+ ctx->pps_updated = 1;
+
+ return DM_RESP_OK;
+}
+
+
+static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
+ xml_node_t *pps, const char *pps_fname)
+{
+ xml_node_t *node;
+ char *locuri;
+ char fname[300];
+ int ret;
+
+ node = get_node(ctx->xml, add, "Item/Target/LocURI");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Target LocURI node found");
+ return DM_RESP_BAD_REQUEST;
+ }
+ locuri = xml_node_get_text(ctx->xml, node);
+ wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
+ if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
+ xml_node_get_text_free(ctx->xml, locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+
+ node = get_node(ctx->xml, add, "Item/Data");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Data node found");
+ xml_node_get_text_free(ctx->xml, locuri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ if (pps_fname && os_file_exists(pps_fname)) {
+ ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
+ if (ret != DM_RESP_OK) {
+ xml_node_get_text_free(ctx->xml, locuri);
+ return ret;
+ }
+ ret = 0;
+ os_strlcpy(fname, pps_fname, sizeof(fname));
+ } else
+ ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
+ xml_node_get_text_free(ctx->xml, locuri);
+ if (ret < 0)
+ return ret == -2 ? DM_RESP_ALREADY_EXISTS :
+ DM_RESP_COMMAND_FAILED;
+
+ if (ctx->no_reconnect == 2) {
+ os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
+ fname);
+ ctx->pps_cred_set = 1;
+ return DM_RESP_OK;
+ }
+
+ wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+ cmd_set_pps(ctx, fname);
+
+ if (ctx->no_reconnect)
+ return DM_RESP_OK;
+
+ wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+ wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+
+ return DM_RESP_OK;
+}
+
+
+static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
+ xml_node_t *pps, const char *pps_fname)
+{
+ char *locuri, *pos;
+ size_t fqdn_len;
+ xml_node_t *node, *tnds, *unode, *pps_node, *parent;
+ char *data;
+ int use_tnds = 0;
+
+ locuri = oma_dm_get_target_locuri(ctx, replace);
+ if (locuri == NULL)
+ return DM_RESP_BAD_REQUEST;
+
+ wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
+ if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos = locuri + 8;
+
+ if (ctx->fqdn == NULL) {
+ os_free(locuri);
+ return DM_RESP_COMMAND_FAILED;
+ }
+ fqdn_len = os_strlen(ctx->fqdn);
+ if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+ pos[fqdn_len] != '/') {
+ wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
+ ctx->fqdn);
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += fqdn_len + 1;
+
+ if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+ wpa_printf(MSG_INFO,
+ "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
+ ctx->fqdn);
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += 24;
+
+ wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
+
+ pps_node = get_node(ctx->xml, pps, pos);
+ if (pps_node == NULL) {
+ wpa_printf(MSG_INFO, "Specified PPS node not found");
+ os_free(locuri);
+ return DM_RESP_NOT_FOUND;
+ }
+
+ node = get_node(ctx->xml, replace, "Item/Meta/Type");
+ if (node) {
+ char *type;
+ type = xml_node_get_text(ctx->xml, node);
+ use_tnds = node &&
+ os_strstr(type, "application/vnd.syncml.dmtnds+xml");
+ }
+
+ node = get_node(ctx->xml, replace, "Item/Data");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Replace/Item/Data found");
+ os_free(locuri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ data = xml_node_get_text(ctx->xml, node);
+ if (data == NULL) {
+ wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
+ os_free(locuri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
+
+ if (use_tnds) {
+ tnds = xml_node_from_buf(ctx->xml, data);
+ xml_node_get_text_free(ctx->xml, data);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO,
+ "Could not parse Replace/Item/Data text");
+ os_free(locuri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ unode = tnds_to_mo(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (unode == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse TNDS text");
+ os_free(locuri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ debug_dump_node(ctx, "Parsed TNDS", unode);
+
+ parent = xml_node_get_parent(ctx->xml, pps_node);
+ xml_node_detach(ctx->xml, pps_node);
+ xml_node_add_child(ctx->xml, parent, unode);
+ } else {
+ xml_node_set_text(ctx->xml, pps_node, data);
+ xml_node_get_text_free(ctx->xml, data);
+ }
+
+ os_free(locuri);
+
+ if (update_pps_file(ctx, pps_fname, pps) < 0)
+ return DM_RESP_COMMAND_FAILED;
+
+ ctx->pps_updated = 1;
+
+ return DM_RESP_OK;
+}
+
+
+static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
+ xml_node_t *pps, const char *pps_fname, char **value)
+{
+ char *locuri, *pos;
+ size_t fqdn_len;
+ xml_node_t *pps_node;
+ const char *name;
+
+ *value = NULL;
+
+ locuri = oma_dm_get_target_locuri(ctx, get);
+ if (locuri == NULL)
+ return DM_RESP_BAD_REQUEST;
+
+ wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
+ if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos = locuri + 8;
+
+ if (ctx->fqdn == NULL)
+ return DM_RESP_COMMAND_FAILED;
+ fqdn_len = os_strlen(ctx->fqdn);
+ if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+ pos[fqdn_len] != '/') {
+ wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
+ ctx->fqdn);
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += fqdn_len + 1;
+
+ if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+ wpa_printf(MSG_INFO,
+ "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
+ ctx->fqdn);
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += 24;
+
+ wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
+
+ pps_node = get_node(ctx->xml, pps, pos);
+ if (pps_node == NULL) {
+ wpa_printf(MSG_INFO, "Specified PPS node not found");
+ os_free(locuri);
+ return DM_RESP_NOT_FOUND;
+ }
+
+ name = xml_node_get_localname(ctx->xml, pps_node);
+ wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
+ if (os_strcasecmp(name, "Password") == 0) {
+ wpa_printf(MSG_INFO, "Do not allow Get for Password node");
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+
+ /*
+ * TODO: No support for DMTNDS, so if interior node, reply with a
+ * list of children node names in Results element. The child list type is
+ * defined in [DMTND].
+ */
+
+ *value = xml_node_get_text(ctx->xml, pps_node);
+ if (*value == NULL)
+ return DM_RESP_COMMAND_FAILED;
+
+ return DM_RESP_OK;
+}
+
+
+static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
+{
+ xml_node_t *cnode;
+ char *str;
+ int ret;
+
+ cnode = get_node(ctx->xml, node, "CmdID");
+ if (cnode == NULL)
+ return 0;
+
+ str = xml_node_get_text(ctx->xml, cnode);
+ if (str == NULL)
+ return 0;
+ ret = atoi(str);
+ xml_node_get_text_free(ctx->xml, str);
+ return ret;
+}
+
+
+static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
+ const char *url, xml_node_t *syncml,
+ const char *ext_hdr, const char *ca_fname,
+ const char *username, const char *password,
+ const char *client_cert,
+ const char *client_key)
+{
+ xml_node_t *resp;
+ char *str, *res;
+ char *resp_uri = NULL;
+
+ str = xml_node_to_str(ctx->xml, syncml);
+ xml_node_free(ctx->xml, syncml);
+ if (str == NULL)
+ return NULL;
+
+ wpa_printf(MSG_INFO, "Send OMA DM Package");
+ write_summary(ctx, "Send OMA DM Package");
+ os_free(ctx->server_url);
+ ctx->server_url = os_strdup(url);
+ res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
+ ext_hdr, ca_fname, username, password,
+ client_cert, client_key, NULL);
+ os_free(str);
+ os_free(resp_uri);
+ resp_uri = NULL;
+
+ if (res == NULL) {
+ const char *err = http_get_err(ctx->http);
+ if (err) {
+ wpa_printf(MSG_INFO, "HTTP error: %s", err);
+ write_result(ctx, "HTTP error: %s", err);
+ } else {
+ write_summary(ctx, "Failed to send OMA DM Package");
+ }
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "Server response: %s", res);
+
+ wpa_printf(MSG_INFO, "Process OMA DM Package");
+ write_summary(ctx, "Process received OMA DM Package");
+ resp = xml_node_from_buf(ctx->xml, res);
+ os_free(res);
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
+ return NULL;
+ }
+
+ debug_dump_node(ctx, "OMA DM Package", resp);
+
+ return resp;
+}
+
+
+static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
+ xml_node_t *resp, int msgid,
+ char **ret_resp_uri,
+ xml_node_t *pps, const char *pps_fname)
+{
+ xml_node_t *syncml, *syncbody, *hdr, *body, *child;
+ const char *name;
+ char *resp_uri = NULL;
+ int server_msgid = 0;
+ int cmdid = 0;
+ int server_cmdid;
+ int resp_needed = 0;
+ char *tmp;
+ int final = 0;
+ char *locuri;
+
+ *ret_resp_uri = NULL;
+
+ name = xml_node_get_localname(ctx->xml, resp);
+ if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
+ wpa_printf(MSG_INFO, "SyncML node not found");
+ return NULL;
+ }
+
+ hdr = get_node(ctx->xml, resp, "SyncHdr");
+ body = get_node(ctx->xml, resp, "SyncBody");
+ if (hdr == NULL || body == NULL) {
+ wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
+ return NULL;
+ }
+
+ xml_node_for_each_child(ctx->xml, child, hdr) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ wpa_printf(MSG_INFO, "SyncHdr %s", name);
+ if (os_strcasecmp(name, "RespURI") == 0) {
+ tmp = xml_node_get_text(ctx->xml, child);
+ if (tmp)
+ resp_uri = os_strdup(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ } else if (os_strcasecmp(name, "MsgID") == 0) {
+ tmp = xml_node_get_text(ctx->xml, child);
+ if (tmp)
+ server_msgid = atoi(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ }
+ }
+
+ wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
+ if (resp_uri)
+ wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
+
+ syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
+ if (syncml == NULL) {
+ os_free(resp_uri);
+ return NULL;
+ }
+
+ syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
+ cmdid++;
+ add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
+ DM_RESP_AUTH_ACCEPTED, NULL);
+
+ xml_node_for_each_child(ctx->xml, child, body) {
+ xml_node_for_each_check(ctx->xml, child);
+ server_cmdid = oma_dm_get_cmdid(ctx, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
+ server_cmdid, name);
+ if (os_strcasecmp(name, "Exec") == 0) {
+ int res = oma_dm_exec(ctx, child);
+ cmdid++;
+ locuri = oma_dm_get_target_locuri(ctx, child);
+ if (locuri == NULL)
+ res = DM_RESP_BAD_REQUEST;
+ add_status(ctx, syncbody, server_msgid, server_cmdid,
+ cmdid, name, res, locuri);
+ os_free(locuri);
+ resp_needed = 1;
+ } else if (os_strcasecmp(name, "Add") == 0) {
+ int res = oma_dm_add(ctx, child, pps, pps_fname);
+ cmdid++;
+ locuri = oma_dm_get_target_locuri(ctx, child);
+ if (locuri == NULL)
+ res = DM_RESP_BAD_REQUEST;
+ add_status(ctx, syncbody, server_msgid, server_cmdid,
+ cmdid, name, res, locuri);
+ os_free(locuri);
+ resp_needed = 1;
+ } else if (os_strcasecmp(name, "Replace") == 0) {
+ int res;
+ res = oma_dm_replace(ctx, child, pps, pps_fname);
+ cmdid++;
+ locuri = oma_dm_get_target_locuri(ctx, child);
+ if (locuri == NULL)
+ res = DM_RESP_BAD_REQUEST;
+ add_status(ctx, syncbody, server_msgid, server_cmdid,
+ cmdid, name, res, locuri);
+ os_free(locuri);
+ resp_needed = 1;
+ } else if (os_strcasecmp(name, "Status") == 0) {
+ /* TODO: Verify success */
+ } else if (os_strcasecmp(name, "Get") == 0) {
+ int res;
+ char *value;
+ res = oma_dm_get(ctx, child, pps, pps_fname, &value);
+ cmdid++;
+ locuri = oma_dm_get_target_locuri(ctx, child);
+ if (locuri == NULL)
+ res = DM_RESP_BAD_REQUEST;
+ add_status(ctx, syncbody, server_msgid, server_cmdid,
+ cmdid, name, res, locuri);
+ if (res == DM_RESP_OK && value) {
+ cmdid++;
+ add_results(ctx, syncbody, server_msgid,
+ server_cmdid, cmdid, locuri, value);
+ }
+ os_free(locuri);
+ xml_node_get_text_free(ctx->xml, value);
+ resp_needed = 1;
+#if 0 /* TODO: MUST support */
+ } else if (os_strcasecmp(name, "Delete") == 0) {
+#endif
+#if 0 /* TODO: MUST support */
+ } else if (os_strcasecmp(name, "Sequence") == 0) {
+#endif
+ } else if (os_strcasecmp(name, "Final") == 0) {
+ final = 1;
+ break;
+ } else {
+ locuri = oma_dm_get_target_locuri(ctx, child);
+ add_status(ctx, syncbody, server_msgid, server_cmdid,
+ cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
+ locuri);
+ os_free(locuri);
+ resp_needed = 1;
+ }
+ }
+
+ if (!final) {
+ wpa_printf(MSG_INFO, "Final node not found");
+ xml_node_free(ctx->xml, syncml);
+ os_free(resp_uri);
+ return NULL;
+ }
+
+ if (!resp_needed) {
+ wpa_printf(MSG_INFO, "Exchange completed - no response needed");
+ xml_node_free(ctx->xml, syncml);
+ os_free(resp_uri);
+ return NULL;
+ }
+
+ xml_node_create(ctx->xml, syncbody, NULL, "Final");
+
+ debug_dump_node(ctx, "OMA-DM Package 3", syncml);
+
+ *ret_resp_uri = resp_uri;
+ return syncml;
+}
+
+
+int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url,
+ const char *ca_fname)
+{
+ xml_node_t *syncml, *resp;
+ char *resp_uri = NULL;
+ int msgid = 0;
+
+ if (url == NULL) {
+ wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
+ write_summary(ctx, "OMA-DM credential provisioning");
+
+ msgid++;
+ syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
+ if (syncml == NULL)
+ return -1;
+
+ while (syncml) {
+ resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
+ syncml, NULL, ca_fname, NULL, NULL,
+ NULL, NULL);
+ if (resp == NULL)
+ return -1;
+
+ msgid++;
+ syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
+ NULL, NULL);
+ xml_node_free(ctx->xml, resp);
+ }
+
+ os_free(resp_uri);
+
+ return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url,
+ const char *ca_fname)
+{
+ xml_node_t *syncml, *resp;
+ char *resp_uri = NULL;
+ int msgid = 0;
+
+ if (url == NULL) {
+ wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
+ ctx->no_reconnect = 2;
+
+ wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
+ write_summary(ctx, "Wait for IP address before starting SIM provisioning");
+
+ if (wait_ip_addr(ctx->ifname, 15) < 0) {
+ wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+ }
+ write_summary(ctx, "OMA-DM SIM provisioning");
+
+ msgid++;
+ syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
+ if (syncml == NULL)
+ return -1;
+
+ while (syncml) {
+ resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
+ syncml, NULL, ca_fname, NULL, NULL,
+ NULL, NULL);
+ if (resp == NULL)
+ return -1;
+
+ msgid++;
+ syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
+ NULL, NULL);
+ xml_node_free(ctx->xml, resp);
+ }
+
+ os_free(resp_uri);
+
+ if (ctx->pps_cred_set) {
+ wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+ cmd_set_pps(ctx, ctx->pps_fname);
+
+ wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+ write_summary(ctx, "Requesting reconnection with updated configuration");
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+ wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+ write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+ return -1;
+ }
+ }
+
+ return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps)
+{
+ xml_node_t *syncml, *resp;
+ char *resp_uri = NULL;
+ int msgid = 0;
+
+ wpa_printf(MSG_INFO, "OMA-DM policy update");
+ write_summary(ctx, "OMA-DM policy update");
+
+ msgid++;
+ syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
+ if (syncml == NULL)
+ return;
+
+ while (syncml) {
+ resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
+ syncml, NULL, ca_fname, cred_username,
+ cred_password, client_cert, client_key);
+ if (resp == NULL)
+ return;
+
+ msgid++;
+ syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
+ pps, pps_fname);
+ xml_node_free(ctx->xml, resp);
+ }
+
+ os_free(resp_uri);
+
+ if (ctx->pps_updated) {
+ wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
+ write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
+ cmd_set_pps(ctx, pps_fname);
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+ wpa_printf(MSG_INFO,
+ "Failed to request wpa_supplicant to reconnect");
+ write_summary(ctx,
+ "Failed to request wpa_supplicant to reconnect");
+ }
+ }
+}
+
+
+void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps)
+{
+ xml_node_t *syncml, *resp;
+ char *resp_uri = NULL;
+ int msgid = 0;
+
+ wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
+ write_summary(ctx, "OMA-DM subscription remediation");
+
+ msgid++;
+ syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
+ if (syncml == NULL)
+ return;
+
+ while (syncml) {
+ resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
+ syncml, NULL, ca_fname, cred_username,
+ cred_password, client_cert, client_key);
+ if (resp == NULL)
+ return;
+
+ msgid++;
+ syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
+ pps, pps_fname);
+ xml_node_free(ctx->xml, resp);
+ }
+
+ os_free(resp_uri);
+
+ wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
+ write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
+ cmd_set_pps(ctx, pps_fname);
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+ wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+ write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+ }
+}
+
+
+void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *add_fname)
+{
+ xml_node_t *pps, *add;
+ int res;
+
+ ctx->fqdn = os_strdup("wi-fi.org");
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
+ pps_fname);
+ return;
+ }
+
+ add = node_from_file(ctx->xml, add_fname);
+ if (add == NULL) {
+ wpa_printf(MSG_INFO, "Add file %s could not be parsed",
+ add_fname);
+ xml_node_free(ctx->xml, pps);
+ return;
+ }
+
+ res = oma_dm_add(ctx, add, pps, pps_fname);
+ wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
+
+ xml_node_free(ctx->xml, pps);
+ xml_node_free(ctx->xml, add);
+}
+
+
+void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *replace_fname)
+{
+ xml_node_t *pps, *replace;
+ int res;
+
+ ctx->fqdn = os_strdup("wi-fi.org");
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
+ pps_fname);
+ return;
+ }
+
+ replace = node_from_file(ctx->xml, replace_fname);
+ if (replace == NULL) {
+ wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
+ replace_fname);
+ xml_node_free(ctx->xml, pps);
+ return;
+ }
+
+ res = oma_dm_replace(ctx, replace, pps, pps_fname);
+ wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
+
+ xml_node_free(ctx->xml, pps);
+ xml_node_free(ctx->xml, replace);
+}
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
new file mode 100644
index 0000000..2175fc3
--- /dev/null
+++ b/hs20/client/osu_client.c
@@ -0,0 +1,3196 @@
+/*
+ * Hotspot 2.0 OSU client
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <sys/stat.h>
+
+#include "common.h"
+#include "utils/browser.h"
+#include "utils/base64.h"
+#include "utils/xml-utils.h"
+#include "utils/http-utils.h"
+#include "common/wpa_ctrl.h"
+#include "common/wpa_helpers.h"
+#include "eap_common/eap_defs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "osu_client.h"
+
+
+void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ FILE *f;
+ char buf[500];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ write_summary(ctx, "%s", buf);
+
+ if (!ctx->result_file)
+ return;
+
+ f = fopen(ctx->result_file, "w");
+ if (f == NULL)
+ return;
+
+ va_start(ap, fmt);
+ vfprintf(f, fmt, ap);
+ va_end(ap);
+ fprintf(f, "\n");
+ fclose(f);
+}
+
+
+void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ FILE *f;
+
+ if (!ctx->summary_file)
+ return;
+
+ f = fopen(ctx->summary_file, "a");
+ if (f == NULL)
+ return;
+
+ va_start(ap, fmt);
+ vfprintf(f, fmt, ap);
+ va_end(ap);
+ fprintf(f, "\n");
+ fclose(f);
+}
+
+
+void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
+ xml_node_t *node)
+{
+ char *str = xml_node_to_str(ctx->xml, node);
+ wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str);
+ free(str);
+}
+
+
+static int valid_fqdn(const char *fqdn)
+{
+ const char *pos;
+
+ /* TODO: could make this more complete.. */
+ if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255)
+ return 0;
+ for (pos = fqdn; *pos; pos++) {
+ if (*pos >= 'a' && *pos <= 'z')
+ continue;
+ if (*pos >= 'A' && *pos <= 'Z')
+ continue;
+ if (*pos >= '0' && *pos <= '9')
+ continue;
+ if (*pos == '-' || *pos == '.' || *pos == '_')
+ continue;
+ return 0;
+ }
+ return 1;
+}
+
+
+int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
+{
+ xml_node_t *node;
+ char *url, *user = NULL, *pw = NULL;
+ char *proto;
+ int ret = -1;
+
+ proto = xml_node_get_attr_value(ctx->xml, getcert,
+ "enrollmentProtocol");
+ if (!proto)
+ return -1;
+ wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto);
+ write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto);
+ if (os_strcasecmp(proto, "EST") != 0) {
+ wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol");
+ xml_node_get_attr_value_free(ctx->xml, proto);
+ return -1;
+ }
+ xml_node_get_attr_value_free(ctx->xml, proto);
+
+ node = get_node(ctx->xml, getcert, "enrollmentServerURI");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node");
+ xml_node_get_attr_value_free(ctx->xml, proto);
+ return -1;
+ }
+ url = xml_node_get_text(ctx->xml, node);
+ if (url == NULL) {
+ wpa_printf(MSG_INFO, "Could not get URL text");
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url);
+ write_summary(ctx, "enrollmentServerURI: %s", url);
+
+ node = get_node(ctx->xml, getcert, "estUserID");
+ if (node == NULL && !ctx->client_cert_present) {
+ wpa_printf(MSG_INFO, "Could not find estUserID node");
+ goto fail;
+ }
+ if (node) {
+ user = xml_node_get_text(ctx->xml, node);
+ if (user == NULL) {
+ wpa_printf(MSG_INFO, "Could not get estUserID text");
+ goto fail;
+ }
+ wpa_printf(MSG_INFO, "estUserID: %s", user);
+ write_summary(ctx, "estUserID: %s", user);
+ }
+
+ node = get_node(ctx->xml, getcert, "estPassword");
+ if (node == NULL && !ctx->client_cert_present) {
+ wpa_printf(MSG_INFO, "Could not find estPassword node");
+ goto fail;
+ }
+ if (node) {
+ pw = xml_node_get_base64_text(ctx->xml, node, NULL);
+ if (pw == NULL) {
+ wpa_printf(MSG_INFO, "Could not get estPassword text");
+ goto fail;
+ }
+ wpa_printf(MSG_INFO, "estPassword: %s", pw);
+ }
+
+ mkdir("Cert", S_IRWXU);
+ if (est_load_cacerts(ctx, url) < 0 ||
+ est_build_csr(ctx, url) < 0 ||
+ est_simple_enroll(ctx, url, user, pw) < 0)
+ goto fail;
+
+ ret = 0;
+fail:
+ xml_node_get_text_free(ctx->xml, url);
+ xml_node_get_text_free(ctx->xml, user);
+ xml_node_get_text_free(ctx->xml, pw);
+
+ return ret;
+}
+
+
+static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert,
+ const char *fqdn)
+{
+ u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
+ char *der, *pem;
+ size_t der_len, pem_len;
+ char *fingerprint;
+ char buf[200];
+
+ wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn);
+
+ fingerprint = xml_node_get_text(ctx->xml, cert);
+ if (fingerprint == NULL)
+ return -1;
+ if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) {
+ wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
+ write_result(ctx, "Invalid client certificate SHA256 hash value in PPS");
+ xml_node_get_text_free(ctx->xml, fingerprint);
+ return -1;
+ }
+ xml_node_get_text_free(ctx->xml, fingerprint);
+
+ der = os_readfile("Cert/est_cert.der", &der_len);
+ if (der == NULL) {
+ wpa_printf(MSG_INFO, "Could not find client certificate from EST");
+ write_result(ctx, "Could not find client certificate from EST");
+ return -1;
+ }
+
+ if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) {
+ os_free(der);
+ return -1;
+ }
+ os_free(der);
+
+ if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
+ wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO");
+ write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO");
+ unlink("Cert/est_cert.der");
+
+ os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn);
+ if (rename("Cert/est-cacerts.pem", buf) < 0) {
+ wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s",
+ strerror(errno));
+ return -1;
+ }
+ pem = os_readfile(buf, &pem_len);
+
+ os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn);
+ if (rename("Cert/est_cert.pem", buf) < 0) {
+ wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s",
+ strerror(errno));
+ os_free(pem);
+ return -1;
+ }
+
+ if (pem) {
+ FILE *f = fopen(buf, "a");
+ if (f) {
+ fwrite(pem, pem_len, 1, f);
+ fclose(f);
+ }
+ os_free(pem);
+ }
+
+ os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn);
+ if (rename("Cert/privkey-plain.pem", buf) < 0) {
+ wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ unlink("Cert/est-req.b64");
+ unlink("Cert/est-req.pem");
+ unlink("Cert/est-resp.raw");
+ rmdir("Cert");
+
+ return 0;
+}
+
+
+#define TMP_CERT_DL_FILE "tmp-cert-download"
+
+static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
+ const char *fname)
+{
+ xml_node_t *url_node, *hash_node;
+ char *url, *hash;
+ char *cert;
+ size_t len;
+ u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
+ int res;
+ unsigned char *b64;
+ FILE *f;
+
+ url_node = get_node(ctx->xml, params, "CertURL");
+ hash_node = get_node(ctx->xml, params, "CertSHA256Fingerprint");
+ if (url_node == NULL || hash_node == NULL)
+ return -1;
+ url = xml_node_get_text(ctx->xml, url_node);
+ hash = xml_node_get_text(ctx->xml, hash_node);
+ if (url == NULL || hash == NULL) {
+ xml_node_get_text_free(ctx->xml, url);
+ xml_node_get_text_free(ctx->xml, hash);
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "CertURL: %s", url);
+ wpa_printf(MSG_INFO, "SHA256 hash: %s", hash);
+
+ if (hexstr2bin(hash, digest1, SHA256_MAC_LEN) < 0) {
+ wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
+ write_result(ctx, "Invalid SHA256 hash value for downloaded certificate");
+ xml_node_get_text_free(ctx->xml, hash);
+ return -1;
+ }
+ xml_node_get_text_free(ctx->xml, hash);
+
+ write_summary(ctx, "Download certificate from %s", url);
+ res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL);
+ xml_node_get_text_free(ctx->xml, url);
+ if (res < 0)
+ return -1;
+
+ cert = os_readfile(TMP_CERT_DL_FILE, &len);
+ remove(TMP_CERT_DL_FILE);
+ if (cert == NULL)
+ return -1;
+
+ if (sha256_vector(1, (const u8 **) &cert, &len, digest2) < 0) {
+ os_free(cert);
+ return -1;
+ }
+
+ if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
+ wpa_printf(MSG_INFO, "Downloaded certificate fingerprint did not match");
+ write_result(ctx, "Downloaded certificate fingerprint did not match");
+ os_free(cert);
+ return -1;
+ }
+
+ b64 = base64_encode((unsigned char *) cert, len, NULL);
+ os_free(cert);
+ if (b64 == NULL)
+ return -1;
+
+ f = fopen(fname, "wb");
+ if (f == NULL) {
+ os_free(b64);
+ return -1;
+ }
+
+ fprintf(f, "-----BEGIN CERTIFICATE-----\n"
+ "%s"
+ "-----END CERTIFICATE-----\n",
+ b64);
+
+ os_free(b64);
+ fclose(f);
+
+ wpa_printf(MSG_INFO, "Downloaded certificate into %s and validated fingerprint",
+ fname);
+ write_summary(ctx, "Downloaded certificate into %s and validated fingerprint",
+ fname);
+
+ return 0;
+}
+
+
+static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *ca_fname)
+{
+ xml_node_t *pps, *node;
+ int ret;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return -1;
+ }
+
+ node = get_child_node(ctx->xml, pps,
+ "SubscriptionUpdate/TrustRoot");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS");
+ xml_node_free(ctx->xml, pps);
+ return -1;
+ }
+
+ ret = download_cert(ctx, node, ca_fname);
+ xml_node_free(ctx->xml, pps);
+
+ return ret;
+}
+
+
+static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *ca_fname)
+{
+ xml_node_t *pps, *node;
+ int ret;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return -1;
+ }
+
+ node = get_child_node(ctx->xml, pps,
+ "PolicyUpdate/TrustRoot");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No PolicyUpdate/TrustRoot/CertURL found from PPS");
+ xml_node_free(ctx->xml, pps);
+ return -1;
+ }
+
+ ret = download_cert(ctx, node, ca_fname);
+ xml_node_free(ctx->xml, pps);
+
+ return ret;
+}
+
+
+static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *ca_fname)
+{
+ xml_node_t *pps, *node, *aaa;
+ int ret;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return -1;
+ }
+
+ node = get_child_node(ctx->xml, pps,
+ "AAAServerTrustRoot");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
+ xml_node_free(ctx->xml, pps);
+ return -1;
+ }
+
+ aaa = xml_node_first_child(ctx->xml, node);
+ if (aaa == NULL) {
+ wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
+ xml_node_free(ctx->xml, pps);
+ return -1;
+ }
+
+ ret = download_cert(ctx, aaa, ca_fname);
+ xml_node_free(ctx->xml, pps);
+
+ return ret;
+}
+
+
+static int download_trust_roots(struct hs20_osu_client *ctx,
+ const char *pps_fname)
+{
+ char *dir, *pos;
+ char fname[300];
+ int ret;
+
+ dir = os_strdup(pps_fname);
+ if (dir == NULL)
+ return -1;
+ pos = os_strrchr(dir, '/');
+ if (pos == NULL) {
+ os_free(dir);
+ return -1;
+ }
+ *pos = '\0';
+
+ snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
+ ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
+ snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
+ cmd_dl_polupd_ca(ctx, pps_fname, fname);
+ snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
+ cmd_dl_aaa_ca(ctx, pps_fname, fname);
+
+ os_free(dir);
+
+ return ret;
+}
+
+
+static int server_dnsname_suffix_match(struct hs20_osu_client *ctx,
+ const char *fqdn)
+{
+ size_t match_len, len, i;
+ const char *val;
+
+ match_len = os_strlen(fqdn);
+
+ for (i = 0; i < ctx->server_dnsname_count; i++) {
+ wpa_printf(MSG_INFO,
+ "Checking suffix match against server dNSName %s",
+ ctx->server_dnsname[i]);
+ val = ctx->server_dnsname[i];
+ len = os_strlen(val);
+
+ if (match_len > len)
+ continue;
+
+ if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0)
+ continue; /* no match */
+
+ if (match_len == len)
+ return 1; /* exact match */
+
+ if (val[len - match_len - 1] == '.')
+ return 1; /* full label match completes suffix match */
+
+ /* Reject due to incomplete label match */
+ }
+
+ /* None of the dNSName(s) matched */
+ return 0;
+}
+
+
+int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
+ xml_node_t *add_mo, char *fname, size_t fname_len)
+{
+ char *str;
+ char *fqdn, *pos;
+ xml_node_t *tnds, *mo, *cert;
+ const char *name;
+ int ret;
+
+ if (strncmp(uri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'",
+ uri);
+ write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'",
+ uri);
+ return -1;
+ }
+
+ fqdn = strdup(uri + 8);
+ if (fqdn == NULL)
+ return -1;
+ pos = strchr(fqdn, '/');
+ if (pos) {
+ if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) {
+ wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'",
+ uri);
+ write_result(ctx, "Unsupported location for addMO to "
+ "add PPS MO (extra directory): '%s'", uri);
+ return -1;
+ }
+ *pos = '\0'; /* remove trailing slash and PPS node name */
+ }
+ wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
+
+ if (!server_dnsname_suffix_match(ctx, fqdn)) {
+ wpa_printf(MSG_INFO, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
+ fqdn);
+ write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
+ fqdn);
+ free(fqdn);
+ return -1;
+ }
+
+ if (!valid_fqdn(fqdn)) {
+ wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn);
+ write_result(ctx, "Invalid FQDN '%s'", fqdn);
+ free(fqdn);
+ return -1;
+ }
+
+ mkdir("SP", S_IRWXU);
+ snprintf(fname, fname_len, "SP/%s", fqdn);
+ if (mkdir(fname, S_IRWXU) < 0) {
+ if (errno != EEXIST) {
+ int err = errno;
+ wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
+ fname, strerror(err));
+ free(fqdn);
+ return -1;
+ }
+ }
+
+ snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
+
+ if (os_file_exists(fname)) {
+ wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO",
+ fname);
+ write_result(ctx, "PPS file '%s' exists - reject addMO",
+ fname);
+ free(fqdn);
+ return -2;
+ }
+ wpa_printf(MSG_INFO, "Using PPS file: %s", fname);
+
+ str = xml_node_get_text(ctx->xml, add_mo);
+ if (str == NULL) {
+ wpa_printf(MSG_INFO, "Could not extract MO text");
+ free(fqdn);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str);
+
+ tnds = xml_node_from_buf(ctx->xml, str);
+ xml_node_get_text_free(ctx->xml, str);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text");
+ free(fqdn);
+ return -1;
+ }
+
+ mo = tnds_to_mo(ctx->xml, tnds);
+ if (mo == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text");
+ free(fqdn);
+ return -1;
+ }
+
+ debug_dump_node(ctx, "Parsed TNDS", mo);
+
+ name = xml_node_get_localname(ctx->xml, mo);
+ if (os_strcasecmp(name, "PerProviderSubscription") != 0) {
+ wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'",
+ name);
+ free(fqdn);
+ return -1;
+ }
+
+ cert = get_child_node(ctx->xml, mo,
+ "Credential/DigitalCertificate/"
+ "CertSHA256Fingerprint");
+ if (cert && process_est_cert(ctx, cert, fqdn) < 0) {
+ xml_node_free(ctx->xml, mo);
+ free(fqdn);
+ return -1;
+ }
+ free(fqdn);
+
+ if (node_to_file(ctx->xml, fname, mo) < 0) {
+ wpa_printf(MSG_INFO, "Could not write MO to file");
+ xml_node_free(ctx->xml, mo);
+ return -1;
+ }
+ xml_node_free(ctx->xml, mo);
+
+ wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname);
+ write_summary(ctx, "A new PPS MO added as '%s'", fname);
+
+ ret = download_trust_roots(ctx, fname);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO, "Remove invalid PPS MO file");
+ write_summary(ctx, "Remove invalid PPS MO file");
+ unlink(fname);
+ }
+
+ return ret;
+}
+
+
+int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
+ xml_node_t *pps)
+{
+ char *str;
+ FILE *f;
+ char backup[300];
+
+ if (ctx->client_cert_present) {
+ xml_node_t *cert;
+ cert = get_child_node(ctx->xml, pps,
+ "Credential/DigitalCertificate/"
+ "CertSHA256Fingerprint");
+ if (cert && os_file_exists("Cert/est_cert.der") &&
+ process_est_cert(ctx, cert, ctx->fqdn) < 0) {
+ wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update");
+ return -1;
+ }
+ }
+
+ wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname);
+
+ str = xml_node_to_str(ctx->xml, pps);
+ wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str);
+
+ snprintf(backup, sizeof(backup), "%s.bak", pps_fname);
+ rename(pps_fname, backup);
+ f = fopen(pps_fname, "w");
+ if (f == NULL) {
+ wpa_printf(MSG_INFO, "Could not write PPS");
+ rename(backup, pps_fname);
+ free(str);
+ return -1;
+ }
+ fprintf(f, "%s\n", str);
+ fclose(f);
+
+ free(str);
+
+ return 0;
+}
+
+
+void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
+ const char *alt_loc, char **user, char **pw)
+{
+ xml_node_t *node;
+
+ node = get_child_node(ctx->xml, pps,
+ "Credential/UsernamePassword/Username");
+ if (node)
+ *user = xml_node_get_text(ctx->xml, node);
+
+ node = get_child_node(ctx->xml, pps,
+ "Credential/UsernamePassword/Password");
+ if (node)
+ *pw = xml_node_get_base64_text(ctx->xml, node, NULL);
+
+ node = get_child_node(ctx->xml, pps, alt_loc);
+ if (node) {
+ xml_node_t *a;
+ a = get_node(ctx->xml, node, "Username");
+ if (a) {
+ xml_node_get_text_free(ctx->xml, *user);
+ *user = xml_node_get_text(ctx->xml, a);
+ wpa_printf(MSG_INFO, "Use OSU username '%s'", *user);
+ }
+
+ a = get_node(ctx->xml, node, "Password");
+ if (a) {
+ free(*pw);
+ *pw = xml_node_get_base64_text(ctx->xml, a, NULL);
+ wpa_printf(MSG_INFO, "Use OSU password");
+ }
+ }
+}
+
+
+/* Remove old credentials based on HomeSP/FQDN */
+static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
+{
+ char cmd[300];
+ os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED provisioning_sp=%s", fqdn);
+ if (wpa_command(ctx->ifname, cmd) < 0)
+ wpa_printf(MSG_INFO, "Failed to remove old credential(s)");
+}
+
+
+static void set_pps_cred_policy_spe(struct hs20_osu_client *ctx, int id,
+ xml_node_t *spe)
+{
+ xml_node_t *ssid;
+ char *txt;
+
+ ssid = get_node(ctx->xml, spe, "SSID");
+ if (ssid == NULL)
+ return;
+ txt = xml_node_get_text(ctx->xml, ssid);
+ if (txt == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "- Policy/SPExclusionList/<X+>/SSID = %s", txt);
+ if (set_cred_quoted(ctx->ifname, id, "excluded_ssid", txt) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred excluded_ssid");
+ xml_node_get_text_free(ctx->xml, txt);
+}
+
+
+static void set_pps_cred_policy_spel(struct hs20_osu_client *ctx, int id,
+ xml_node_t *spel)
+{
+ xml_node_t *child;
+
+ xml_node_for_each_child(ctx->xml, child, spel) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_policy_spe(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_policy_prp(struct hs20_osu_client *ctx, int id,
+ xml_node_t *prp)
+{
+ xml_node_t *node;
+ char *txt = NULL, *pos;
+ char *prio, *country_buf = NULL;
+ const char *country;
+ char val[200];
+ int priority;
+
+ node = get_node(ctx->xml, prp, "Priority");
+ if (node == NULL)
+ return;
+ prio = xml_node_get_text(ctx->xml, node);
+ if (prio == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s",
+ prio);
+ priority = atoi(prio);
+ xml_node_get_text_free(ctx->xml, prio);
+
+ node = get_node(ctx->xml, prp, "Country");
+ if (node) {
+ country_buf = xml_node_get_text(ctx->xml, node);
+ if (country_buf == NULL)
+ return;
+ country = country_buf;
+ wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s",
+ country);
+ } else {
+ country = "*";
+ }
+
+ node = get_node(ctx->xml, prp, "FQDN_Match");
+ if (node == NULL)
+ goto out;
+ txt = xml_node_get_text(ctx->xml, node);
+ if (txt == NULL)
+ goto out;
+ wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s",
+ txt);
+ pos = strrchr(txt, ',');
+ if (pos == NULL)
+ goto out;
+ *pos++ = '\0';
+
+ snprintf(val, sizeof(val), "%s,%d,%d,%s", txt,
+ strcmp(pos, "includeSubdomains") != 0, priority, country);
+ if (set_cred_quoted(ctx->ifname, id, "roaming_partner", val) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred roaming_partner");
+out:
+ xml_node_get_text_free(ctx->xml, country_buf);
+ xml_node_get_text_free(ctx->xml, txt);
+}
+
+
+static void set_pps_cred_policy_prpl(struct hs20_osu_client *ctx, int id,
+ xml_node_t *prpl)
+{
+ xml_node_t *child;
+
+ xml_node_for_each_child(ctx->xml, child, prpl) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_policy_prp(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client *ctx, int id,
+ xml_node_t *min_backhaul)
+{
+ xml_node_t *node;
+ char *type, *dl = NULL, *ul = NULL;
+ int home;
+
+ node = get_node(ctx->xml, min_backhaul, "NetworkType");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without mandatory NetworkType node");
+ return;
+ }
+
+ type = xml_node_get_text(ctx->xml, node);
+ if (type == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/NetworkType = %s",
+ type);
+ if (os_strcasecmp(type, "home") == 0)
+ home = 1;
+ else if (os_strcasecmp(type, "roaming") == 0)
+ home = 0;
+ else {
+ wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold with invalid NetworkType");
+ xml_node_get_text_free(ctx->xml, type);
+ return;
+ }
+ xml_node_get_text_free(ctx->xml, type);
+
+ node = get_node(ctx->xml, min_backhaul, "DLBandwidth");
+ if (node)
+ dl = xml_node_get_text(ctx->xml, node);
+
+ node = get_node(ctx->xml, min_backhaul, "ULBandwidth");
+ if (node)
+ ul = xml_node_get_text(ctx->xml, node);
+
+ if (dl == NULL && ul == NULL) {
+ wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without either DLBandwidth or ULBandwidth nodes");
+ return;
+ }
+
+ if (dl)
+ wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/DLBandwidth = %s",
+ dl);
+ if (ul)
+ wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/ULBandwidth = %s",
+ ul);
+
+ if (home) {
+ if (dl &&
+ set_cred(ctx->ifname, id, "min_dl_bandwidth_home", dl) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+ if (ul &&
+ set_cred(ctx->ifname, id, "min_ul_bandwidth_home", ul) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+ } else {
+ if (dl &&
+ set_cred(ctx->ifname, id, "min_dl_bandwidth_roaming", dl) <
+ 0)
+ wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+ if (ul &&
+ set_cred(ctx->ifname, id, "min_ul_bandwidth_roaming", ul) <
+ 0)
+ wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+ }
+
+ xml_node_get_text_free(ctx->xml, dl);
+ xml_node_get_text_free(ctx->xml, ul);
+}
+
+
+static void set_pps_cred_policy_min_backhaul_list(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ xml_node_t *child;
+
+ wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_policy_min_backhaul(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_policy_update(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ wpa_printf(MSG_INFO, "- Policy/PolicyUpdate");
+ /* Not used in wpa_supplicant */
+}
+
+
+static void set_pps_cred_policy_required_proto_port(struct hs20_osu_client *ctx,
+ int id, xml_node_t *tuple)
+{
+ xml_node_t *node;
+ char *proto, *port;
+ char *buf;
+ size_t buflen;
+
+ node = get_node(ctx->xml, tuple, "IPProtocol");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "Ignore RequiredProtoPortTuple without mandatory IPProtocol node");
+ return;
+ }
+
+ proto = xml_node_get_text(ctx->xml, node);
+ if (proto == NULL)
+ return;
+
+ wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/IPProtocol = %s",
+ proto);
+
+ node = get_node(ctx->xml, tuple, "PortNumber");
+ port = node ? xml_node_get_text(ctx->xml, node) : NULL;
+ if (port) {
+ wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/PortNumber = %s",
+ port);
+ buflen = os_strlen(proto) + os_strlen(port) + 10;
+ buf = os_malloc(buflen);
+ if (buf)
+ os_snprintf(buf, buflen, "%s:%s", proto, port);
+ xml_node_get_text_free(ctx->xml, port);
+ } else {
+ buflen = os_strlen(proto) + 10;
+ buf = os_malloc(buflen);
+ if (buf)
+ os_snprintf(buf, buflen, "%s", proto);
+ }
+
+ xml_node_get_text_free(ctx->xml, proto);
+
+ if (buf == NULL)
+ return;
+
+ if (set_cred(ctx->ifname, id, "req_conn_capab", buf) < 0)
+ wpa_printf(MSG_INFO, "Could not set req_conn_capab");
+
+ os_free(buf);
+}
+
+
+static void set_pps_cred_policy_required_proto_ports(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ xml_node_t *child;
+
+ wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_policy_required_proto_port(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_policy_max_bss_load(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Policy/MaximumBSSLoadValue - %s", str);
+ if (set_cred(ctx->ifname, id, "max_bss_load", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred max_bss_load limit");
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_policy(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+ const char *name;
+
+ wpa_printf(MSG_INFO, "- Policy");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "PreferredRoamingPartnerList") == 0)
+ set_pps_cred_policy_prpl(ctx, id, child);
+ else if (os_strcasecmp(name, "MinBackhaulThreshold") == 0)
+ set_pps_cred_policy_min_backhaul_list(ctx, id, child);
+ else if (os_strcasecmp(name, "PolicyUpdate") == 0)
+ set_pps_cred_policy_update(ctx, id, child);
+ else if (os_strcasecmp(name, "SPExclusionList") == 0)
+ set_pps_cred_policy_spel(ctx, id, child);
+ else if (os_strcasecmp(name, "RequiredProtoPortTuple") == 0)
+ set_pps_cred_policy_required_proto_ports(ctx, id, child);
+ else if (os_strcasecmp(name, "MaximumBSSLoadValue") == 0)
+ set_pps_cred_policy_max_bss_load(ctx, id, child);
+ else
+ wpa_printf(MSG_INFO, "Unknown Policy node '%s'", name);
+ }
+}
+
+
+static void set_pps_cred_priority(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- CredentialPriority = %s", str);
+ if (set_cred(ctx->ifname, id, "sp_priority", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred sp_priority");
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_aaa_server_trust_root(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ wpa_printf(MSG_INFO, "- AAAServerTrustRoot - TODO");
+}
+
+
+static void set_pps_cred_sub_update(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ wpa_printf(MSG_INFO, "- SubscriptionUpdate");
+ /* not used within wpa_supplicant */
+}
+
+
+static void set_pps_cred_home_sp_network_id(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ xml_node_t *ssid_node, *hessid_node;
+ char *ssid, *hessid;
+
+ ssid_node = get_node(ctx->xml, node, "SSID");
+ if (ssid_node == NULL) {
+ wpa_printf(MSG_INFO, "Ignore HomeSP/NetworkID without mandatory SSID node");
+ return;
+ }
+
+ hessid_node = get_node(ctx->xml, node, "HESSID");
+
+ ssid = xml_node_get_text(ctx->xml, ssid_node);
+ if (ssid == NULL)
+ return;
+ hessid = hessid_node ? xml_node_get_text(ctx->xml, hessid_node) : NULL;
+
+ wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/SSID = %s", ssid);
+ if (hessid)
+ wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/HESSID = %s",
+ hessid);
+
+ /* TODO: Configure to wpa_supplicant */
+
+ xml_node_get_text_free(ctx->xml, ssid);
+ xml_node_get_text_free(ctx->xml, hessid);
+}
+
+
+static void set_pps_cred_home_sp_network_ids(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ xml_node_t *child;
+
+ wpa_printf(MSG_INFO, "- HomeSP/NetworkID");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_home_sp_network_id(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_home_sp_friendly_name(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- HomeSP/FriendlyName = %s", str);
+ /* not used within wpa_supplicant(?) */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_icon_url(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- HomeSP/IconURL = %s", str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_fqdn(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- HomeSP/FQDN = %s", str);
+ if (set_cred_quoted(ctx->ifname, id, "domain", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred domain");
+ if (set_cred_quoted(ctx->ifname, id, "domain_suffix_match", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred domain_suffix_match");
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_oi(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+ const char *name;
+ char *homeoi = NULL;
+ int required = 0;
+ char *str;
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (strcasecmp(name, "HomeOI") == 0 && !homeoi) {
+ homeoi = xml_node_get_text(ctx->xml, child);
+ wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOI = %s",
+ homeoi);
+ } else if (strcasecmp(name, "HomeOIRequired") == 0) {
+ str = xml_node_get_text(ctx->xml, child);
+ wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOIRequired = '%s'",
+ str);
+ if (str == NULL)
+ continue;
+ required = strcasecmp(str, "true") == 0;
+ xml_node_get_text_free(ctx->xml, str);
+ } else
+ wpa_printf(MSG_INFO, "Unknown HomeOIList node '%s'",
+ name);
+ }
+
+ if (homeoi == NULL) {
+ wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> without HomeOI ignored");
+ return;
+ }
+
+ wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> '%s' required=%d",
+ homeoi, required);
+
+ if (required) {
+ if (set_cred(ctx->ifname, id, "required_roaming_consortium",
+ homeoi) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium");
+ } else {
+ if (set_cred_quoted(ctx->ifname, id, "roaming_consortium",
+ homeoi) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium");
+ }
+
+ xml_node_get_text_free(ctx->xml, homeoi);
+}
+
+
+static void set_pps_cred_home_sp_oi_list(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+
+ wpa_printf(MSG_INFO, "- HomeSP/HomeOIList");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_home_sp_oi(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_home_sp_other_partner(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ xml_node_t *child;
+ const char *name;
+ char *fqdn = NULL;
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "FQDN") == 0 && !fqdn) {
+ fqdn = xml_node_get_text(ctx->xml, child);
+ wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+>/FQDN = %s",
+ fqdn);
+ } else
+ wpa_printf(MSG_INFO, "Unknown OtherHomePartners node '%s'",
+ name);
+ }
+
+ if (fqdn == NULL) {
+ wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+> without FQDN ignored");
+ return;
+ }
+
+ if (set_cred_quoted(ctx->ifname, id, "domain", fqdn) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred domain for OtherHomePartners node");
+
+ xml_node_get_text_free(ctx->xml, fqdn);
+}
+
+
+static void set_pps_cred_home_sp_other_partners(struct hs20_osu_client *ctx,
+ int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+
+ wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_home_sp_other_partner(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_home_sp_roaming_consortium_oi(
+ struct hs20_osu_client *ctx, int id, xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str);
+ /* TODO: Set to wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+ const char *name;
+
+ wpa_printf(MSG_INFO, "- HomeSP");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "NetworkID") == 0)
+ set_pps_cred_home_sp_network_ids(ctx, id, child);
+ else if (os_strcasecmp(name, "FriendlyName") == 0)
+ set_pps_cred_home_sp_friendly_name(ctx, id, child);
+ else if (os_strcasecmp(name, "IconURL") == 0)
+ set_pps_cred_home_sp_icon_url(ctx, id, child);
+ else if (os_strcasecmp(name, "FQDN") == 0)
+ set_pps_cred_home_sp_fqdn(ctx, id, child);
+ else if (os_strcasecmp(name, "HomeOIList") == 0)
+ set_pps_cred_home_sp_oi_list(ctx, id, child);
+ else if (os_strcasecmp(name, "OtherHomePartners") == 0)
+ set_pps_cred_home_sp_other_partners(ctx, id, child);
+ else if (os_strcasecmp(name, "RoamingConsortiumOI") == 0)
+ set_pps_cred_home_sp_roaming_consortium_oi(ctx, id,
+ child);
+ else
+ wpa_printf(MSG_INFO, "Unknown HomeSP node '%s'", name);
+ }
+}
+
+
+static void set_pps_cred_sub_params(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ wpa_printf(MSG_INFO, "- SubscriptionParameters");
+ /* not used within wpa_supplicant */
+}
+
+
+static void set_pps_cred_creation_date(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/CreationDate = %s", str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_expiration_date(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/ExpirationDate = %s", str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_username(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Username = %s",
+ str);
+ if (set_cred_quoted(ctx->ifname, id, "username", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred username");
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_password(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ int len, i;
+ char *pw, *hex, *pos, *end;
+
+ pw = xml_node_get_base64_text(ctx->xml, node, &len);
+ if (pw == NULL)
+ return;
+
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Password = %s", pw);
+
+ hex = malloc(len * 2 + 1);
+ if (hex == NULL) {
+ free(pw);
+ return;
+ }
+ end = hex + len * 2 + 1;
+ pos = hex;
+ for (i = 0; i < len; i++) {
+ snprintf(pos, end - pos, "%02x", pw[i]);
+ pos += 2;
+ }
+ free(pw);
+
+ if (set_cred(ctx->ifname, id, "password", hex) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred password");
+ free(hex);
+}
+
+
+static void set_pps_cred_machine_managed(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/MachineManaged = %s",
+ str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_soft_token_app(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/SoftTokenApp = %s",
+ str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/AbleToShare = %s",
+ str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod - TODO");
+}
+
+
+static void set_pps_cred_username_password(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+ const char *name;
+
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "Username") == 0)
+ set_pps_cred_username(ctx, id, child);
+ else if (os_strcasecmp(name, "Password") == 0)
+ set_pps_cred_password(ctx, id, child);
+ else if (os_strcasecmp(name, "MachineManaged") == 0)
+ set_pps_cred_machine_managed(ctx, id, child);
+ else if (os_strcasecmp(name, "SoftTokenApp") == 0)
+ set_pps_cred_soft_token_app(ctx, id, child);
+ else if (os_strcasecmp(name, "AbleToShare") == 0)
+ set_pps_cred_able_to_share(ctx, id, child);
+ else if (os_strcasecmp(name, "EAPMethod") == 0)
+ set_pps_cred_eap_method(ctx, id, child);
+ else
+ wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword node '%s'",
+ name);
+ }
+}
+
+
+static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node, const char *fqdn)
+{
+ char buf[200], dir[200];
+
+ wpa_printf(MSG_INFO, "- Credential/DigitalCertificate");
+
+ if (getcwd(dir, sizeof(dir)) == NULL)
+ return;
+
+ /* TODO: could build username from Subject of Subject AltName */
+ if (set_cred_quoted(ctx->ifname, id, "username", "cert") < 0) {
+ wpa_printf(MSG_INFO, "Failed to set username");
+ }
+
+ snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, fqdn);
+ if (os_file_exists(buf)) {
+ if (set_cred_quoted(ctx->ifname, id, "client_cert", buf) < 0) {
+ wpa_printf(MSG_INFO, "Failed to set client_cert");
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir, fqdn);
+ if (os_file_exists(buf)) {
+ if (set_cred_quoted(ctx->ifname, id, "private_key", buf) < 0) {
+ wpa_printf(MSG_INFO, "Failed to set private_key");
+ }
+ }
+}
+
+
+static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node, const char *fqdn, int sim)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ char buf[200], dir[200];
+
+ if (str == NULL)
+ return;
+
+ wpa_printf(MSG_INFO, "- Credential/Realm = %s", str);
+ if (set_cred_quoted(ctx->ifname, id, "realm", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred realm");
+ xml_node_get_text_free(ctx->xml, str);
+
+ if (sim)
+ return;
+
+ if (getcwd(dir, sizeof(dir)) == NULL)
+ return;
+ snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn);
+ if (os_file_exists(buf)) {
+ if (set_cred_quoted(ctx->ifname, id, "ca_cert", buf) < 0) {
+ wpa_printf(MSG_INFO, "Failed to set CA cert");
+ }
+ }
+}
+
+
+static void set_pps_cred_check_aaa_cert_status(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+
+ if (str == NULL)
+ return;
+
+ wpa_printf(MSG_INFO, "- Credential/CheckAAAServerCertStatus = %s", str);
+ if (os_strcasecmp(str, "true") == 0 &&
+ set_cred(ctx->ifname, id, "ocsp", "2") < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred ocsp");
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_sim(struct hs20_osu_client *ctx, int id,
+ xml_node_t *sim, xml_node_t *realm)
+{
+ xml_node_t *node;
+ char *imsi, *eaptype, *str, buf[20];
+ int type;
+ int mnc_len = 3;
+ size_t imsi_len;
+
+ node = get_node(ctx->xml, sim, "EAPType");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No SIM/EAPType node in credential");
+ return;
+ }
+ eaptype = xml_node_get_text(ctx->xml, node);
+ if (eaptype == NULL) {
+ wpa_printf(MSG_INFO, "Could not extract SIM/EAPType");
+ return;
+ }
+ wpa_printf(MSG_INFO, " - Credential/SIM/EAPType = %s", eaptype);
+ type = atoi(eaptype);
+ xml_node_get_text_free(ctx->xml, eaptype);
+
+ switch (type) {
+ case EAP_TYPE_SIM:
+ if (set_cred(ctx->ifname, id, "eap", "SIM") < 0)
+ wpa_printf(MSG_INFO, "Could not set eap=SIM");
+ break;
+ case EAP_TYPE_AKA:
+ if (set_cred(ctx->ifname, id, "eap", "AKA") < 0)
+ wpa_printf(MSG_INFO, "Could not set eap=SIM");
+ break;
+ case EAP_TYPE_AKA_PRIME:
+ if (set_cred(ctx->ifname, id, "eap", "AKA'") < 0)
+ wpa_printf(MSG_INFO, "Could not set eap=SIM");
+ break;
+ default:
+ wpa_printf(MSG_INFO, "Unsupported SIM/EAPType %d", type);
+ return;
+ }
+
+ node = get_node(ctx->xml, sim, "IMSI");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No SIM/IMSI node in credential");
+ return;
+ }
+ imsi = xml_node_get_text(ctx->xml, node);
+ if (imsi == NULL) {
+ wpa_printf(MSG_INFO, "Could not extract SIM/IMSI");
+ return;
+ }
+ wpa_printf(MSG_INFO, " - Credential/SIM/IMSI = %s", imsi);
+ imsi_len = os_strlen(imsi);
+ if (imsi_len < 7 || imsi_len + 2 > sizeof(buf)) {
+ wpa_printf(MSG_INFO, "Invalid IMSI length");
+ xml_node_get_text_free(ctx->xml, imsi);
+ return;
+ }
+
+ str = xml_node_get_text(ctx->xml, node);
+ if (str) {
+ char *pos;
+ pos = os_strstr(str, "mnc");
+ if (pos && os_strlen(pos) >= 6) {
+ if (os_strncmp(imsi + 3, pos + 3, 3) == 0)
+ mnc_len = 3;
+ else if (os_strncmp(imsi + 3, pos + 4, 2) == 0)
+ mnc_len = 2;
+ }
+ xml_node_get_text_free(ctx->xml, str);
+ }
+
+ os_memcpy(buf, imsi, 3 + mnc_len);
+ buf[3 + mnc_len] = '-';
+ os_strlcpy(buf + 3 + mnc_len + 1, imsi + 3 + mnc_len,
+ sizeof(buf) - 3 - mnc_len - 1);
+
+ xml_node_get_text_free(ctx->xml, imsi);
+
+ if (set_cred_quoted(ctx->ifname, id, "imsi", buf) < 0)
+ wpa_printf(MSG_INFO, "Could not set IMSI");
+
+ if (set_cred_quoted(ctx->ifname, id, "milenage",
+ "90dca4eda45b53cf0f12d7c9c3bc6a89:"
+ "cb9cccc4b9258e6dca4760379fb82581:000000000123") <
+ 0)
+ wpa_printf(MSG_INFO, "Could not set Milenage parameters");
+}
+
+
+static void set_pps_cred_credential(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node, const char *fqdn)
+{
+ xml_node_t *child, *sim, *realm;
+ const char *name;
+
+ wpa_printf(MSG_INFO, "- Credential");
+
+ sim = get_node(ctx->xml, node, "SIM");
+ realm = get_node(ctx->xml, node, "Realm");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "CreationDate") == 0)
+ set_pps_cred_creation_date(ctx, id, child);
+ else if (os_strcasecmp(name, "ExpirationDate") == 0)
+ set_pps_cred_expiration_date(ctx, id, child);
+ else if (os_strcasecmp(name, "UsernamePassword") == 0)
+ set_pps_cred_username_password(ctx, id, child);
+ else if (os_strcasecmp(name, "DigitalCertificate") == 0)
+ set_pps_cred_digital_cert(ctx, id, child, fqdn);
+ else if (os_strcasecmp(name, "Realm") == 0)
+ set_pps_cred_realm(ctx, id, child, fqdn, sim != NULL);
+ else if (os_strcasecmp(name, "CheckAAAServerCertStatus") == 0)
+ set_pps_cred_check_aaa_cert_status(ctx, id, child);
+ else if (os_strcasecmp(name, "SIM") == 0)
+ set_pps_cred_sim(ctx, id, child, realm);
+ else
+ wpa_printf(MSG_INFO, "Unknown Credential node '%s'",
+ name);
+ }
+}
+
+
+static void set_pps_credential(struct hs20_osu_client *ctx, int id,
+ xml_node_t *cred, const char *fqdn)
+{
+ xml_node_t *child;
+ const char *name;
+
+ xml_node_for_each_child(ctx->xml, child, cred) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "Policy") == 0)
+ set_pps_cred_policy(ctx, id, child);
+ else if (os_strcasecmp(name, "CredentialPriority") == 0)
+ set_pps_cred_priority(ctx, id, child);
+ else if (os_strcasecmp(name, "AAAServerTrustRoot") == 0)
+ set_pps_cred_aaa_server_trust_root(ctx, id, child);
+ else if (os_strcasecmp(name, "SubscriptionUpdate") == 0)
+ set_pps_cred_sub_update(ctx, id, child);
+ else if (os_strcasecmp(name, "HomeSP") == 0)
+ set_pps_cred_home_sp(ctx, id, child);
+ else if (os_strcasecmp(name, "SubscriptionParameters") == 0)
+ set_pps_cred_sub_params(ctx, id, child);
+ else if (os_strcasecmp(name, "Credential") == 0)
+ set_pps_cred_credential(ctx, id, child, fqdn);
+ else
+ wpa_printf(MSG_INFO, "Unknown credential node '%s'",
+ name);
+ }
+}
+
+
+static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps,
+ const char *fqdn)
+{
+ xml_node_t *child;
+ const char *name;
+ int id;
+ char *update_identifier = NULL;
+
+ /*
+ * TODO: Could consider more complex mechanism that would remove
+ * credentials only if there are changes in the information sent to
+ * wpa_supplicant.
+ */
+ remove_sp_creds(ctx, fqdn);
+
+ xml_node_for_each_child(ctx->xml, child, pps) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "UpdateIdentifier") == 0) {
+ update_identifier = xml_node_get_text(ctx->xml, child);
+ if (update_identifier) {
+ wpa_printf(MSG_INFO, "- UpdateIdentifier = %s",
+ update_identifier);
+ break;
+ }
+ }
+ }
+
+ xml_node_for_each_child(ctx->xml, child, pps) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "UpdateIdentifier") == 0)
+ continue;
+ id = add_cred(ctx->ifname);
+ if (id < 0) {
+ wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant");
+ write_summary(ctx, "Failed to add credential to wpa_supplicant");
+ break;
+ }
+ write_summary(ctx, "Add a credential to wpa_supplicant");
+ if (update_identifier &&
+ set_cred(ctx->ifname, id, "update_identifier",
+ update_identifier) < 0)
+ wpa_printf(MSG_INFO, "Failed to set update_identifier");
+ if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) <
+ 0)
+ wpa_printf(MSG_INFO, "Failed to set provisioning_sp");
+ wpa_printf(MSG_INFO, "credential localname: '%s'", name);
+ set_pps_credential(ctx, id, child, fqdn);
+ ctx->pps_cred_set = 1;
+ }
+
+ xml_node_get_text_free(ctx->xml, update_identifier);
+}
+
+
+void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
+{
+ xml_node_t *pps;
+ const char *fqdn;
+ char *fqdn_buf = NULL, *pos;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return;
+ }
+
+ fqdn = os_strstr(pps_fname, "SP/");
+ if (fqdn) {
+ fqdn_buf = os_strdup(fqdn + 3);
+ if (fqdn_buf == NULL)
+ return;
+ pos = os_strchr(fqdn_buf, '/');
+ if (pos)
+ *pos = '\0';
+ fqdn = fqdn_buf;
+ } else
+ fqdn = "wi-fi.org";
+
+ wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s",
+ fqdn);
+ set_pps(ctx, pps, fqdn);
+
+ os_free(fqdn_buf);
+ xml_node_free(ctx->xml, pps);
+}
+
+
+static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname)
+{
+ xml_node_t *pps, *node;
+ char *fqdn = NULL;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return -1;
+ }
+
+ node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+ if (node)
+ fqdn = xml_node_get_text(ctx->xml, node);
+
+ xml_node_free(ctx->xml, pps);
+
+ if (fqdn) {
+ FILE *f = fopen("pps-fqdn", "w");
+ if (f) {
+ fprintf(f, "%s", fqdn);
+ fclose(f);
+ }
+ xml_node_get_text_free(ctx->xml, fqdn);
+ return 0;
+ }
+
+ xml_node_get_text_free(ctx->xml, fqdn);
+ return -1;
+}
+
+
+static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname,
+ const char *out_fname, const char *urn, int use_path)
+{
+ xml_node_t *mo, *node;
+
+ mo = node_from_file(ctx->xml, in_fname);
+ if (mo == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
+ return;
+ }
+
+ node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL);
+ if (node) {
+ node_to_file(ctx->xml, out_fname, node);
+ xml_node_free(ctx->xml, node);
+ }
+
+ xml_node_free(ctx->xml, mo);
+}
+
+
+static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname,
+ const char *out_fname)
+{
+ xml_node_t *tnds, *mo;
+
+ tnds = node_from_file(ctx->xml, in_fname);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
+ return;
+ }
+
+ mo = tnds_to_mo(ctx->xml, tnds);
+ if (mo) {
+ node_to_file(ctx->xml, out_fname, mo);
+ xml_node_free(ctx->xml, mo);
+ }
+
+ xml_node_free(ctx->xml, tnds);
+}
+
+
+struct osu_icon {
+ int id;
+ char lang[4];
+ char mime_type[256];
+ char filename[256];
+};
+
+struct osu_data {
+ char bssid[20];
+ char url[256];
+ unsigned int methods;
+ char osu_ssid[33];
+ char osu_nai[256];
+ struct osu_lang_text friendly_name[MAX_OSU_VALS];
+ size_t friendly_name_count;
+ struct osu_lang_text serv_desc[MAX_OSU_VALS];
+ size_t serv_desc_count;
+ struct osu_icon icon[MAX_OSU_VALS];
+ size_t icon_count;
+};
+
+
+static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
+{
+ FILE *f;
+ char buf[1000];
+ struct osu_data *osu = NULL, *last = NULL;
+ size_t osu_count = 0;
+ char *pos, *end;
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Could not open %s", fname);
+ return NULL;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ pos = strchr(buf, '\n');
+ if (pos)
+ *pos = '\0';
+
+ if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) {
+ last = realloc(osu, (osu_count + 1) * sizeof(*osu));
+ if (last == NULL)
+ break;
+ osu = last;
+ last = &osu[osu_count++];
+ memset(last, 0, sizeof(*last));
+ snprintf(last->bssid, sizeof(last->bssid), "%s",
+ buf + 13);
+ continue;
+ }
+ if (!last)
+ continue;
+
+ if (strncmp(buf, "uri=", 4) == 0) {
+ snprintf(last->url, sizeof(last->url), "%s", buf + 4);
+ continue;
+ }
+
+ if (strncmp(buf, "methods=", 8) == 0) {
+ last->methods = strtol(buf + 8, NULL, 16);
+ continue;
+ }
+
+ if (strncmp(buf, "osu_ssid=", 9) == 0) {
+ snprintf(last->osu_ssid, sizeof(last->osu_ssid),
+ "%s", buf + 9);
+ continue;
+ }
+
+ if (os_strncmp(buf, "osu_nai=", 8) == 0) {
+ os_snprintf(last->osu_nai, sizeof(last->osu_nai),
+ "%s", buf + 8);
+ continue;
+ }
+
+ if (strncmp(buf, "friendly_name=", 14) == 0) {
+ struct osu_lang_text *txt;
+ if (last->friendly_name_count == MAX_OSU_VALS)
+ continue;
+ pos = strchr(buf + 14, ':');
+ if (pos == NULL)
+ continue;
+ *pos++ = '\0';
+ txt = &last->friendly_name[last->friendly_name_count++];
+ snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 14);
+ snprintf(txt->text, sizeof(txt->text), "%s", pos);
+ }
+
+ if (strncmp(buf, "desc=", 5) == 0) {
+ struct osu_lang_text *txt;
+ if (last->serv_desc_count == MAX_OSU_VALS)
+ continue;
+ pos = strchr(buf + 5, ':');
+ if (pos == NULL)
+ continue;
+ *pos++ = '\0';
+ txt = &last->serv_desc[last->serv_desc_count++];
+ snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 5);
+ snprintf(txt->text, sizeof(txt->text), "%s", pos);
+ }
+
+ if (strncmp(buf, "icon=", 5) == 0) {
+ struct osu_icon *icon;
+ if (last->icon_count == MAX_OSU_VALS)
+ continue;
+ icon = &last->icon[last->icon_count++];
+ icon->id = atoi(buf + 5);
+ pos = strchr(buf, ':');
+ if (pos == NULL)
+ continue;
+ pos = strchr(pos + 1, ':');
+ if (pos == NULL)
+ continue;
+ pos = strchr(pos + 1, ':');
+ if (pos == NULL)
+ continue;
+ pos++;
+ end = strchr(pos, ':');
+ if (!end)
+ continue;
+ *end = '\0';
+ snprintf(icon->lang, sizeof(icon->lang), "%s", pos);
+ pos = end + 1;
+
+ end = strchr(pos, ':');
+ if (end)
+ *end = '\0';
+ snprintf(icon->mime_type, sizeof(icon->mime_type),
+ "%s", pos);
+ if (!pos)
+ continue;
+ pos = end + 1;
+
+ end = strchr(pos, ':');
+ if (end)
+ *end = '\0';
+ snprintf(icon->filename, sizeof(icon->filename),
+ "%s", pos);
+ continue;
+ }
+ }
+
+ fclose(f);
+
+ *count = osu_count;
+ return osu;
+}
+
+
+static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
+ const char *ssid, const char *url, const char *ca_fname,
+ unsigned int methods, int no_prod_assoc,
+ const char *osu_nai)
+{
+ int id;
+ const char *ifname = ctx->ifname;
+ char buf[200];
+ struct wpa_ctrl *mon;
+ int res;
+
+ id = add_network(ifname);
+ if (id < 0)
+ return -1;
+ if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
+ return -1;
+ if (osu_nai && os_strlen(osu_nai) > 0) {
+ char dir[255], fname[300];
+ if (getcwd(dir, sizeof(dir)) == NULL)
+ return -1;
+ os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
+
+ if (set_network(ifname, id, "proto", "OSEN") < 0 ||
+ set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
+ set_network(ifname, id, "pairwise", "CCMP") < 0 ||
+ set_network(ifname, id, "group", "GTK_NOT_USED") < 0 ||
+ set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 ||
+ set_network(ifname, id, "ocsp", "2") < 0 ||
+ set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
+ set_network_quoted(ifname, id, "ca_cert", fname) < 0)
+ return -1;
+ } else {
+ if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
+ return -1;
+ }
+
+ mon = open_wpa_mon(ifname);
+ if (mon == NULL)
+ return -1;
+
+ wpa_printf(MSG_INFO, "Associate with OSU SSID");
+ write_summary(ctx, "Associate with OSU SSID");
+ snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id);
+ if (wpa_command(ifname, buf) < 0)
+ return -1;
+
+ res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED",
+ buf, sizeof(buf));
+
+ wpa_ctrl_detach(mon);
+ wpa_ctrl_close(mon);
+
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "Could not connect");
+ write_summary(ctx, "Could not connect to OSU network");
+ wpa_printf(MSG_INFO, "Remove OSU network connection");
+ snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
+ wpa_command(ifname, buf);
+ return -1;
+ }
+
+ write_summary(ctx, "Waiting for IP address for subscription registration");
+ if (wait_ip_addr(ifname, 15) < 0) {
+ wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+ }
+
+ if (no_prod_assoc) {
+ if (res < 0)
+ return -1;
+ wpa_printf(MSG_INFO, "No production connection used for testing purposes");
+ write_summary(ctx, "No production connection used for testing purposes");
+ return 0;
+ }
+
+ ctx->no_reconnect = 1;
+ if (methods & 0x02)
+ res = cmd_prov(ctx, url, ca_fname);
+ else if (methods & 0x01)
+ res = cmd_oma_dm_prov(ctx, url, ca_fname);
+
+ wpa_printf(MSG_INFO, "Remove OSU network connection");
+ write_summary(ctx, "Remove OSU network connection");
+ snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
+ wpa_command(ifname, buf);
+
+ if (res < 0)
+ return -1;
+
+ wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+ write_summary(ctx, "Requesting reconnection with updated configuration");
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+ wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+ write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
+ int connect, const char *ca_fname, int no_prod_assoc,
+ const char *friendly_name)
+{
+ char fname[255];
+ FILE *f;
+ struct osu_data *osu = NULL, *last = NULL;
+ size_t osu_count, i, j;
+ int ret;
+
+ write_summary(ctx, "OSU provider selection");
+
+ if (dir == NULL) {
+ wpa_printf(MSG_INFO, "Missing dir parameter to osu_select");
+ return -1;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir);
+ osu = parse_osu_providers(fname, &osu_count);
+ if (osu == NULL) {
+ wpa_printf(MSG_INFO, "Could not any OSU providers from %s",
+ fname);
+ write_result(ctx, "No OSU providers available");
+ return -1;
+ }
+
+ if (friendly_name) {
+ for (i = 0; i < osu_count; i++) {
+ last = &osu[i];
+ for (j = 0; j < last->friendly_name_count; j++) {
+ if (os_strcmp(last->friendly_name[j].text,
+ friendly_name) == 0)
+ break;
+ }
+ if (j < last->friendly_name_count)
+ break;
+ }
+ if (i == osu_count) {
+ wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers",
+ friendly_name);
+ write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers",
+ friendly_name);
+ free(osu);
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'",
+ friendly_name);
+ write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'",
+ friendly_name);
+ ret = i + 1;
+ goto selected;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir);
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ wpa_printf(MSG_INFO, "Could not open %s", fname);
+ free(osu);
+ return -1;
+ }
+
+ fprintf(f, "<html><head>"
+ "<meta http-equiv=\"Content-type\" content=\"text/html; "
+ "charset=utf-8\"<title>Select service operator</title>"
+ "</head><body><h1>Select service operator</h1>\n");
+
+ if (osu_count == 0)
+ fprintf(f, "No online signup available\n");
+
+ for (i = 0; i < osu_count; i++) {
+ last = &osu[i];
+#ifdef ANDROID
+ fprintf(f, "<p>\n"
+ "<a href=\"http://localhost:12345/osu/%d\">"
+ "<table><tr><td>", (int) i + 1);
+#else /* ANDROID */
+ fprintf(f, "<p>\n"
+ "<a href=\"osu://%d\">"
+ "<table><tr><td>", (int) i + 1);
+#endif /* ANDROID */
+ for (j = 0; j < last->icon_count; j++) {
+ fprintf(f, "<img src=\"osu-icon-%d.%s\">\n",
+ last->icon[j].id,
+ strcasecmp(last->icon[j].mime_type,
+ "image/png") == 0 ? "png" : "icon");
+ }
+ fprintf(f, "<td>");
+ for (j = 0; j < last->friendly_name_count; j++) {
+ fprintf(f, "<small>[%s]</small> %s<br>\n",
+ last->friendly_name[j].lang,
+ last->friendly_name[j].text);
+ }
+ fprintf(f, "<tr><td colspan=2>");
+ for (j = 0; j < last->serv_desc_count; j++) {
+ fprintf(f, "<small>[%s]</small> %s<br>\n",
+ last->serv_desc[j].lang,
+ last->serv_desc[j].text);
+ }
+ fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
+ "SSID: %s<br>\n",
+ last->bssid, last->osu_ssid);
+ if (last->osu_nai)
+ fprintf(f, "NAI: %s<br>\n", last->osu_nai);
+ fprintf(f, "URL: %s<br>\n"
+ "methods:%s%s<br>\n"
+ "</small></p>\n",
+ last->url,
+ last->methods & 0x01 ? " OMA-DM" : "",
+ last->methods & 0x02 ? " SOAP-XML-SPP" : "");
+ }
+
+ fprintf(f, "</body></html>\n");
+
+ fclose(f);
+
+ snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir);
+ write_summary(ctx, "Start web browser with OSU provider selection page");
+ ret = hs20_web_browser(fname);
+
+selected:
+ if (ret > 0 && (size_t) ret <= osu_count) {
+ char *data;
+ size_t data_len;
+
+ wpa_printf(MSG_INFO, "Selected OSU id=%d", ret);
+ last = &osu[ret - 1];
+ ret = 0;
+ wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
+ wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
+ wpa_printf(MSG_INFO, "URL: %s", last->url);
+ write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
+ ret, last->bssid, last->osu_ssid, last->url);
+
+ ctx->friendly_name_count = last->friendly_name_count;
+ for (j = 0; j < last->friendly_name_count; j++) {
+ wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s",
+ last->friendly_name[j].lang,
+ last->friendly_name[j].text);
+ os_strlcpy(ctx->friendly_name[j].lang,
+ last->friendly_name[j].lang,
+ sizeof(ctx->friendly_name[j].lang));
+ os_strlcpy(ctx->friendly_name[j].text,
+ last->friendly_name[j].text,
+ sizeof(ctx->friendly_name[j].text));
+ }
+
+ ctx->icon_count = last->icon_count;
+ for (j = 0; j < last->icon_count; j++) {
+ char fname[256];
+
+ os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s",
+ dir, last->icon[j].id,
+ strcasecmp(last->icon[j].mime_type,
+ "image/png") == 0 ?
+ "png" : "icon");
+ wpa_printf(MSG_INFO, "ICON: %s (%s)",
+ fname, last->icon[j].filename);
+ os_strlcpy(ctx->icon_filename[j],
+ last->icon[j].filename,
+ sizeof(ctx->icon_filename[j]));
+
+ data = os_readfile(fname, &data_len);
+ if (data) {
+ sha256_vector(1, (const u8 **) &data, &data_len,
+ ctx->icon_hash[j]);
+ os_free(data);
+ }
+ }
+
+ if (connect == 2) {
+ if (last->methods & 0x02)
+ ret = cmd_prov(ctx, last->url, ca_fname);
+ else if (last->methods & 0x01)
+ ret = cmd_oma_dm_prov(ctx, last->url, ca_fname);
+ else
+ ret = -1;
+ } else if (connect)
+ ret = osu_connect(ctx, last->bssid, last->osu_ssid,
+ last->url, ca_fname, last->methods,
+ no_prod_assoc, last->osu_nai);
+ } else
+ ret = -1;
+
+ free(osu);
+
+ return ret;
+}
+
+
+static int cmd_signup(struct hs20_osu_client *ctx, const char *ca_fname,
+ int no_prod_assoc, const char *friendly_name)
+{
+ char dir[255];
+ char fname[300], buf[400];
+ struct wpa_ctrl *mon;
+ const char *ifname;
+ int res;
+
+ ifname = ctx->ifname;
+
+ if (getcwd(dir, sizeof(dir)) == NULL)
+ return -1;
+
+ snprintf(fname, sizeof(fname), "%s/osu-info", dir);
+ if (mkdir(fname, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) {
+ wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
+ fname, strerror(errno));
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "SET osu_dir %s", fname);
+ if (wpa_command(ifname, buf) < 0) {
+ wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant");
+ return -1;
+ }
+
+ mon = open_wpa_mon(ifname);
+ if (mon == NULL)
+ return -1;
+
+ wpa_printf(MSG_INFO, "Starting OSU fetch");
+ write_summary(ctx, "Starting OSU provider information fetch");
+ if (wpa_command(ifname, "FETCH_OSU") < 0) {
+ wpa_printf(MSG_INFO, "Could not start OSU fetch");
+ wpa_ctrl_detach(mon);
+ wpa_ctrl_close(mon);
+ return -1;
+ }
+ res = get_wpa_cli_event(mon, "OSU provider fetch completed",
+ buf, sizeof(buf));
+
+ wpa_ctrl_detach(mon);
+ wpa_ctrl_close(mon);
+
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "OSU fetch did not complete");
+ write_summary(ctx, "OSU fetch did not complete");
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "OSU provider fetch completed");
+
+ return cmd_osu_select(ctx, fname, 1, ca_fname, no_prod_assoc,
+ friendly_name);
+}
+
+
+static void cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname)
+{
+ xml_node_t *pps, *node;
+ char pps_fname_buf[300];
+ char ca_fname_buf[200];
+ char *cred_username = NULL;
+ char *cred_password = NULL;
+ char *sub_rem_uri = NULL;
+ char client_cert_buf[200];
+ char *client_cert = NULL;
+ char client_key_buf[200];
+ char *client_key = NULL;
+ int spp;
+
+ ctx->ca_fname = ca_fname;
+
+ wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s",
+ address);
+
+ if (!pps_fname) {
+ char buf[256];
+ wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
+ if (os_strncmp(address, "fqdn=", 5) == 0) {
+ wpa_printf(MSG_INFO, "Use requested FQDN from command line");
+ os_snprintf(buf, sizeof(buf), "%s", address + 5);
+ address = NULL;
+ } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
+ sizeof(buf)) < 0) {
+ wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
+ return;
+ }
+ os_free(ctx->fqdn);
+ ctx->fqdn = os_strdup(buf);
+ if (ctx->fqdn == NULL)
+ return;
+ wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
+ buf);
+ os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
+ "SP/%s/pps.xml", ctx->fqdn);
+ pps_fname = pps_fname_buf;
+
+ os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
+ ctx->fqdn);
+ ca_fname = ca_fname_buf;
+ }
+
+ if (!os_file_exists(pps_fname)) {
+ wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
+ pps_fname);
+ return;
+ }
+ wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
+
+ if (ca_fname && !os_file_exists(ca_fname)) {
+ wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
+ ca_fname);
+ return;
+ }
+ wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read PPS MO");
+ return;
+ }
+
+ if (!ctx->fqdn) {
+ char *tmp;
+ node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
+ return;
+ }
+ tmp = xml_node_get_text(ctx->xml, node);
+ if (tmp == NULL) {
+ wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
+ return;
+ }
+ ctx->fqdn = os_strdup(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ if (!ctx->fqdn) {
+ wpa_printf(MSG_INFO, "No FQDN known");
+ return;
+ }
+ }
+
+ node = get_child_node(ctx->xml, pps,
+ "SubscriptionUpdate/UpdateMethod");
+ if (node) {
+ char *tmp;
+ tmp = xml_node_get_text(ctx->xml, node);
+ if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
+ spp = 0;
+ else
+ spp = 1;
+ } else {
+ wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
+ spp = 1;
+ }
+
+ get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword",
+ &cred_username, &cred_password);
+ if (cred_username)
+ wpa_printf(MSG_INFO, "Using username: %s", cred_username);
+ if (cred_password)
+ wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
+
+ if (cred_username == NULL && cred_password == NULL &&
+ get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
+ wpa_printf(MSG_INFO, "Using client certificate");
+ os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+ "SP/%s/client-cert.pem", ctx->fqdn);
+ client_cert = client_cert_buf;
+ os_snprintf(client_key_buf, sizeof(client_key_buf),
+ "SP/%s/client-key.pem", ctx->fqdn);
+ client_key = client_key_buf;
+ ctx->client_cert_present = 1;
+ }
+
+ node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI");
+ if (node) {
+ sub_rem_uri = xml_node_get_text(ctx->xml, node);
+ if (sub_rem_uri &&
+ (!address || os_strcmp(address, sub_rem_uri) != 0)) {
+ wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s",
+ sub_rem_uri);
+ address = sub_rem_uri;
+ }
+ }
+ if (!address) {
+ wpa_printf(MSG_INFO, "Server URL not known");
+ return;
+ }
+
+ write_summary(ctx, "Wait for IP address for subscriptiom remediation");
+ wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation");
+
+ if (wait_ip_addr(ctx->ifname, 15) < 0) {
+ wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+ }
+
+ if (spp)
+ spp_sub_rem(ctx, address, pps_fname, ca_fname,
+ client_cert, client_key,
+ cred_username, cred_password, pps);
+ else
+ oma_dm_sub_rem(ctx, address, pps_fname, ca_fname,
+ client_cert, client_key,
+ cred_username, cred_password, pps);
+
+ xml_node_get_text_free(ctx->xml, sub_rem_uri);
+ xml_node_get_text_free(ctx->xml, cred_username);
+ os_free(cred_password);
+ xml_node_free(ctx->xml, pps);
+}
+
+
+static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname)
+{
+ xml_node_t *pps;
+ xml_node_t *node;
+ char pps_fname_buf[300];
+ char ca_fname_buf[200];
+ char *uri = NULL;
+ char *cred_username = NULL;
+ char *cred_password = NULL;
+ char client_cert_buf[200];
+ char *client_cert = NULL;
+ char client_key_buf[200];
+ char *client_key = NULL;
+ int spp;
+
+ wpa_printf(MSG_INFO, "Policy update requested");
+
+ if (!pps_fname) {
+ char buf[256];
+ wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
+ if (os_strncmp(address, "fqdn=", 5) == 0) {
+ wpa_printf(MSG_INFO, "Use requested FQDN from command line");
+ os_snprintf(buf, sizeof(buf), "%s", address + 5);
+ address = NULL;
+ } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
+ sizeof(buf)) < 0) {
+ wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
+ return -1;
+ }
+ os_free(ctx->fqdn);
+ ctx->fqdn = os_strdup(buf);
+ if (ctx->fqdn == NULL)
+ return -1;
+ wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
+ buf);
+ os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
+ "SP/%s/pps.xml", ctx->fqdn);
+ pps_fname = pps_fname_buf;
+
+ os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
+ buf);
+ ca_fname = ca_fname_buf;
+ }
+
+ if (!os_file_exists(pps_fname)) {
+ wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
+ pps_fname);
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
+
+ if (ca_fname && !os_file_exists(ca_fname)) {
+ wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
+ ca_fname);
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read PPS MO");
+ return -1;
+ }
+
+ if (!ctx->fqdn) {
+ char *tmp;
+ node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
+ return -1;
+ }
+ tmp = xml_node_get_text(ctx->xml, node);
+ if (tmp == NULL) {
+ wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
+ return -1;
+ }
+ ctx->fqdn = os_strdup(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ if (!ctx->fqdn) {
+ wpa_printf(MSG_INFO, "No FQDN known");
+ return -1;
+ }
+ }
+
+ node = get_child_node(ctx->xml, pps,
+ "Policy/PolicyUpdate/UpdateMethod");
+ if (node) {
+ char *tmp;
+ tmp = xml_node_get_text(ctx->xml, node);
+ if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
+ spp = 0;
+ else
+ spp = 1;
+ } else {
+ wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
+ spp = 1;
+ }
+
+ get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword",
+ &cred_username, &cred_password);
+ if (cred_username)
+ wpa_printf(MSG_INFO, "Using username: %s", cred_username);
+ if (cred_password)
+ wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
+
+ if (cred_username == NULL && cred_password == NULL &&
+ get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
+ wpa_printf(MSG_INFO, "Using client certificate");
+ os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+ "SP/%s/client-cert.pem", ctx->fqdn);
+ client_cert = client_cert_buf;
+ os_snprintf(client_key_buf, sizeof(client_key_buf),
+ "SP/%s/client-key.pem", ctx->fqdn);
+ client_key = client_key_buf;
+ }
+
+ if (!address) {
+ node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI");
+ if (node) {
+ uri = xml_node_get_text(ctx->xml, node);
+ wpa_printf(MSG_INFO, "URI based on PPS: %s", uri);
+ address = uri;
+ }
+ }
+ if (!address) {
+ wpa_printf(MSG_INFO, "Server URL not known");
+ return -1;
+ }
+
+ if (spp)
+ spp_pol_upd(ctx, address, pps_fname, ca_fname,
+ client_cert, client_key,
+ cred_username, cred_password, pps);
+ else
+ oma_dm_pol_upd(ctx, address, pps_fname, ca_fname,
+ client_cert, client_key,
+ cred_username, cred_password, pps);
+
+ xml_node_get_text_free(ctx->xml, uri);
+ xml_node_get_text_free(ctx->xml, cred_username);
+ os_free(cred_password);
+ xml_node_free(ctx->xml, pps);
+
+ return 0;
+}
+
+
+static char * get_hostname(const char *url)
+{
+ const char *pos, *end, *end2;
+ char *ret;
+
+ if (url == NULL)
+ return NULL;
+
+ pos = os_strchr(url, '/');
+ if (pos == NULL)
+ return NULL;
+ pos++;
+ if (*pos != '/')
+ return NULL;
+ pos++;
+
+ end = os_strchr(pos, '/');
+ end2 = os_strchr(pos, ':');
+ if (end && end2 && end2 < end)
+ end = end2;
+ if (end)
+ end--;
+ else {
+ end = pos;
+ while (*end)
+ end++;
+ if (end > pos)
+ end--;
+ }
+
+ ret = os_malloc(end - pos + 2);
+ if (ret == NULL)
+ return NULL;
+
+ os_memcpy(ret, pos, end - pos + 1);
+ ret[end - pos + 1] = '\0';
+
+ return ret;
+}
+
+
+static int osu_cert_cb(void *_ctx, struct http_cert *cert)
+{
+ struct hs20_osu_client *ctx = _ctx;
+ unsigned int i, j;
+ int found;
+ char *host = NULL;
+
+ wpa_printf(MSG_INFO, "osu_cert_cb");
+
+ host = get_hostname(ctx->server_url);
+
+ for (i = 0; i < ctx->server_dnsname_count; i++)
+ os_free(ctx->server_dnsname[i]);
+ os_free(ctx->server_dnsname);
+ ctx->server_dnsname = os_calloc(cert->num_dnsname, sizeof(char *));
+ ctx->server_dnsname_count = 0;
+
+ found = 0;
+ for (i = 0; i < cert->num_dnsname; i++) {
+ if (ctx->server_dnsname) {
+ ctx->server_dnsname[ctx->server_dnsname_count] =
+ os_strdup(cert->dnsname[i]);
+ if (ctx->server_dnsname[ctx->server_dnsname_count])
+ ctx->server_dnsname_count++;
+ }
+ if (host && os_strcasecmp(host, cert->dnsname[i]) == 0)
+ found = 1;
+ wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]);
+ }
+
+ if (host && !found) {
+ wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection",
+ host);
+ write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection",
+ host);
+ os_free(host);
+ return -1;
+ }
+
+ os_free(host);
+
+ for (i = 0; i < cert->num_othername; i++) {
+ if (os_strcmp(cert->othername[i].oid,
+ "1.3.6.1.4.1.40808.1.1.1") == 0) {
+ wpa_hexdump_ascii(MSG_INFO,
+ "id-wfa-hotspot-friendlyName",
+ cert->othername[i].data,
+ cert->othername[i].len);
+ }
+ }
+
+ for (j = 0; j < ctx->friendly_name_count; j++) {
+ int found = 0;
+ for (i = 0; i < cert->num_othername; i++) {
+ if (os_strcmp(cert->othername[i].oid,
+ "1.3.6.1.4.1.40808.1.1.1") != 0)
+ continue;
+ if (cert->othername[i].len < 3)
+ continue;
+ if (os_strncasecmp((char *) cert->othername[i].data,
+ ctx->friendly_name[j].lang, 3) != 0)
+ continue;
+ if (os_strncmp((char *) cert->othername[i].data + 3,
+ ctx->friendly_name[j].text,
+ cert->othername[i].len - 3) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'",
+ ctx->friendly_name[j].lang,
+ ctx->friendly_name[j].text);
+ write_result(ctx, "No friendly name match found for '[%s]%s'",
+ ctx->friendly_name[j].lang,
+ ctx->friendly_name[j].text);
+ return -1;
+ }
+ }
+
+ for (i = 0; i < cert->num_logo; i++) {
+ struct http_logo *logo = &cert->logo[i];
+
+ wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'",
+ logo->alg_oid, logo->uri);
+ wpa_hexdump_ascii(MSG_INFO, "hashValue",
+ logo->hash, logo->hash_len);
+ }
+
+ for (j = 0; j < ctx->icon_count; j++) {
+ int found = 0;
+ char *name = ctx->icon_filename[j];
+ size_t name_len = os_strlen(name);
+
+ wpa_printf(MSG_INFO, "Looking for icon file name '%s' match",
+ name);
+ for (i = 0; i < cert->num_logo; i++) {
+ struct http_logo *logo = &cert->logo[i];
+ size_t uri_len = os_strlen(logo->uri);
+ char *pos;
+
+ wpa_printf(MSG_INFO, "Comparing to '%s' uri_len=%d name_len=%d",
+ logo->uri, (int) uri_len, (int) name_len);
+ if (uri_len < 1 + name_len)
+ continue;
+ pos = &logo->uri[uri_len - name_len - 1];
+ if (*pos != '/')
+ continue;
+ pos++;
+ if (os_strcmp(pos, name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ wpa_printf(MSG_INFO, "No icon filename match found for '%s'",
+ name);
+ write_result(ctx,
+ "No icon filename match found for '%s'",
+ name);
+ return -1;
+ }
+ }
+
+ for (j = 0; j < ctx->icon_count; j++) {
+ int found = 0;
+
+ for (i = 0; i < cert->num_logo; i++) {
+ struct http_logo *logo = &cert->logo[i];
+
+ if (logo->hash_len != 32)
+ continue;
+ if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ wpa_printf(MSG_INFO, "No icon hash match found");
+ write_result(ctx, "No icon hash match found");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int init_ctx(struct hs20_osu_client *ctx)
+{
+ xml_node_t *devinfo, *devid;
+
+ os_memset(ctx, 0, sizeof(*ctx));
+ ctx->ifname = "wlan0";
+ ctx->xml = xml_node_init_ctx(ctx, NULL);
+ if (ctx->xml == NULL)
+ return -1;
+
+ devinfo = node_from_file(ctx->xml, "devinfo.xml");
+ if (!devinfo) {
+ wpa_printf(MSG_ERROR, "devinfo.xml not found");
+ return -1;
+ }
+
+ devid = get_node(ctx->xml, devinfo, "DevId");
+ if (devid) {
+ char *tmp = xml_node_get_text(ctx->xml, devid);
+ if (tmp) {
+ ctx->devid = os_strdup(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ }
+ }
+ xml_node_free(ctx->xml, devinfo);
+
+ if (ctx->devid == NULL) {
+ wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml");
+ return -1;
+ }
+
+ ctx->http = http_init_ctx(ctx, ctx->xml);
+ if (ctx->http == NULL) {
+ xml_node_deinit_ctx(ctx->xml);
+ return -1;
+ }
+ http_ocsp_set(ctx->http, 2);
+ http_set_cert_cb(ctx->http, osu_cert_cb, ctx);
+
+ return 0;
+}
+
+
+static void deinit_ctx(struct hs20_osu_client *ctx)
+{
+ size_t i;
+
+ http_deinit_ctx(ctx->http);
+ xml_node_deinit_ctx(ctx->xml);
+ os_free(ctx->fqdn);
+ os_free(ctx->server_url);
+ os_free(ctx->devid);
+
+ for (i = 0; i < ctx->server_dnsname_count; i++)
+ os_free(ctx->server_dnsname[i]);
+ os_free(ctx->server_dnsname);
+}
+
+
+static void check_workarounds(struct hs20_osu_client *ctx)
+{
+ FILE *f;
+ char buf[100];
+ unsigned long int val = 0;
+
+ f = fopen("hs20-osu-client.workarounds", "r");
+ if (f == NULL)
+ return;
+
+ if (fgets(buf, sizeof(buf), f))
+ val = strtoul(buf, NULL, 16);
+
+ fclose(f);
+
+ if (val) {
+ wpa_printf(MSG_INFO, "Workarounds enabled: 0x%lx", val);
+ ctx->workarounds = val;
+ if (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL)
+ http_ocsp_set(ctx->http, 1);
+ }
+}
+
+
+static void usage(void)
+{
+ printf("usage: hs20-osu-client [-dddqqKt] [-S<station ifname>] \\\n"
+ " [-w<wpa_supplicant ctrl_iface dir>] "
+ "[-r<result file>] [-f<debug file>] \\\n"
+ " [-s<summary file>] \\\n"
+ " <command> [arguments..]\n"
+ "commands:\n"
+ "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n"
+ "- to_tnds2 <XML MO> <XML MO in TNDS format (Path) "
+ "[URN]>\n"
+ "- from_tnds <XML MO in TNDS format> <XML MO>\n"
+ "- set_pps <PerProviderSubscription XML file name>\n"
+ "- get_fqdn <PerProviderSubscription XML file name>\n"
+ "- pol_upd [Server URL] [PPS] [CA cert]\n"
+ "- sub_rem <Server URL> [PPS] [CA cert]\n"
+ "- prov <Server URL> [CA cert]\n"
+ "- oma_dm_prov <Server URL> [CA cert]\n"
+ "- sim_prov <Server URL> [CA cert]\n"
+ "- oma_dm_sim_prov <Server URL> [CA cert]\n"
+ "- signup [CA cert]\n"
+ "- dl_osu_ca <PPS> <CA file>\n"
+ "- dl_polupd_ca <PPS> <CA file>\n"
+ "- dl_aaa_ca <PPS> <CA file>\n"
+ "- browser <URL>\n"
+ "- parse_cert <X.509 certificate (DER)>\n"
+ "- osu_select <OSU info directory> [CA cert]\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct hs20_osu_client ctx;
+ int c;
+ int ret = 0;
+ int no_prod_assoc = 0;
+ const char *friendly_name = NULL;
+ const char *wpa_debug_file_path = NULL;
+ extern char *wpas_ctrl_path;
+ extern int wpa_debug_level;
+ extern int wpa_debug_show_keys;
+ extern int wpa_debug_timestamp;
+
+ if (init_ctx(&ctx) < 0)
+ return -1;
+
+ for (;;) {
+ c = getopt(argc, argv, "df:hi:KNO:qr:s:S:tw:");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'd':
+ if (wpa_debug_level > 0)
+ wpa_debug_level--;
+ break;
+ case 'f':
+ wpa_debug_file_path = optarg;
+ break;
+ case 'K':
+ wpa_debug_show_keys++;
+ break;
+ case 'N':
+ no_prod_assoc = 1;
+ break;
+ case 'O':
+ friendly_name = optarg;
+ break;
+ case 'q':
+ wpa_debug_level++;
+ break;
+ case 'r':
+ ctx.result_file = optarg;
+ break;
+ case 's':
+ ctx.summary_file = optarg;
+ break;
+ case 'S':
+ ctx.ifname = optarg;
+ break;
+ case 't':
+ wpa_debug_timestamp++;
+ break;
+ case 'w':
+ wpas_ctrl_path = optarg;
+ break;
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ break;
+ }
+ }
+
+ if (argc - optind < 1) {
+ usage();
+ exit(0);
+ }
+
+ wpa_debug_open_file(wpa_debug_file_path);
+
+#ifdef __linux__
+ setlinebuf(stdout);
+#endif /* __linux__ */
+
+ if (ctx.result_file)
+ unlink(ctx.result_file);
+ wpa_printf(MSG_DEBUG, "===[hs20-osu-client START - command: %s ]======"
+ "================", argv[optind]);
+ check_workarounds(&ctx);
+
+ if (strcmp(argv[optind], "to_tnds") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
+ argc > optind + 3 ? argv[optind + 3] : NULL,
+ 0);
+ } else if (strcmp(argv[optind], "to_tnds2") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
+ argc > optind + 3 ? argv[optind + 3] : NULL,
+ 1);
+ } else if (strcmp(argv[optind], "from_tnds") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "sub_rem") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ if (argc - optind < 2)
+ wpa_printf(MSG_ERROR, "Server URL missing from command line");
+ else
+ cmd_sub_rem(&ctx, argv[optind + 1],
+ argc > optind + 2 ? argv[optind + 2] : NULL,
+ argc > optind + 3 ? argv[optind + 3] :
+ NULL);
+ } else if (strcmp(argv[optind], "pol_upd") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ ret = cmd_pol_upd(&ctx, argc > 2 ? argv[optind + 1] : NULL,
+ argc > optind + 2 ? argv[optind + 2] : NULL,
+ argc > optind + 3 ? argv[optind + 3] : NULL);
+ } else if (strcmp(argv[optind], "prov") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_prov(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "sim_prov") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_sim_prov(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "dl_osu_ca") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "dl_polupd_ca") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "osu_select") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_osu_select(&ctx, argv[optind + 1], 2,
+ argc > optind + 2 ? argv[optind + 2] : NULL,
+ 1, NULL);
+ } else if (strcmp(argv[optind], "signup") == 0) {
+ ret = cmd_signup(&ctx,
+ argc > optind + 1 ? argv[optind + 1] : NULL,
+ no_prod_assoc, friendly_name);
+ } else if (strcmp(argv[optind], "set_pps") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_set_pps(&ctx, argv[optind + 1]);
+ } else if (strcmp(argv[optind], "get_fqdn") == 0) {
+ if (argc - optind < 1) {
+ usage();
+ exit(0);
+ }
+ ret = cmd_get_fqdn(&ctx, argv[optind + 1]);
+ } else if (strcmp(argv[optind], "oma_dm_prov") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_oma_dm_prov(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1],
+ argv[optind + 2]) < 0) {
+ write_summary(&ctx, "Failed to complete OMA DM SIM provisioning");
+ return -1;
+ }
+ } else if (strcmp(argv[optind], "oma_dm_add") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "oma_dm_replace") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "est_csr") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ mkdir("Cert", S_IRWXU);
+ est_build_csr(&ctx, argv[optind + 1]);
+ } else if (strcmp(argv[optind], "browser") == 0) {
+ int ret;
+
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+
+ wpa_printf(MSG_INFO, "Launch web browser to URL %s",
+ argv[optind + 1]);
+ ret = hs20_web_browser(argv[optind + 1]);
+ wpa_printf(MSG_INFO, "Web browser result: %d", ret);
+ } else if (strcmp(argv[optind], "parse_cert") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+
+ wpa_debug_level = MSG_MSGDUMP;
+ http_parse_x509_certificate(ctx.http, argv[optind + 1]);
+ wpa_debug_level = MSG_INFO;
+ } else {
+ wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]);
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "===[hs20-osu-client END ]======================");
+
+ wpa_debug_close_file();
+ deinit_ctx(&ctx);
+
+ return ret;
+}
diff --git a/hs20/client/osu_client.h b/hs20/client/osu_client.h
new file mode 100644
index 0000000..ef568b4
--- /dev/null
+++ b/hs20/client/osu_client.h
@@ -0,0 +1,121 @@
+/*
+ * Hotspot 2.0 - OSU client
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OSU_CLIENT_H
+#define OSU_CLIENT_H
+
+#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
+
+#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
+#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
+#define URN_HS20_DEVDETAIL_EXT "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"
+#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
+
+
+#define MAX_OSU_VALS 10
+
+struct osu_lang_text {
+ char lang[4];
+ char text[253];
+};
+
+struct hs20_osu_client {
+ struct xml_node_ctx *xml;
+ struct http_ctx *http;
+ int no_reconnect;
+ char pps_fname[300];
+ char *devid;
+ const char *result_file;
+ const char *summary_file;
+ const char *ifname;
+ const char *ca_fname;
+ char *fqdn;
+ char *server_url;
+ struct osu_lang_text friendly_name[MAX_OSU_VALS];
+ size_t friendly_name_count;
+ size_t icon_count;
+ char icon_filename[MAX_OSU_VALS][256];
+ u8 icon_hash[MAX_OSU_VALS][32];
+ int pps_cred_set;
+ int pps_updated;
+ int client_cert_present;
+ char **server_dnsname;
+ size_t server_dnsname_count;
+#define WORKAROUND_OCSP_OPTIONAL 0x00000001
+ unsigned long int workarounds;
+};
+
+
+/* osu_client.c */
+
+void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
+ xml_node_t *node);
+int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert);
+int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
+ xml_node_t *add_mo, char *fname, size_t fname_len);
+void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
+ const char *alt_loc, char **user, char **pw);
+int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
+ xml_node_t *pps);
+void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname);
+
+
+/* spp_client.c */
+
+void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps);
+void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps);
+int cmd_prov(struct hs20_osu_client *ctx, const char *url,
+ const char *ca_fname);
+int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url,
+ const char *ca_fname);
+
+
+/* oma_dm_client.c */
+
+int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url,
+ const char *ca_fname);
+int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url,
+ const char *ca_fname);
+void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps);
+void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps);
+void cmd_oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname);
+void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *add_fname);
+void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *replace_fname);
+
+/* est.c */
+
+int est_load_cacerts(struct hs20_osu_client *ctx, const char *url);
+int est_build_csr(struct hs20_osu_client *ctx, const char *url);
+int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
+ const char *user, const char *pw);
+
+#endif /* OSU_CLIENT_H */
diff --git a/hs20/client/spp_client.c b/hs20/client/spp_client.c
new file mode 100644
index 0000000..8022ccb
--- /dev/null
+++ b/hs20/client/spp_client.c
@@ -0,0 +1,1001 @@
+/*
+ * Hotspot 2.0 SPP client
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/stat.h>
+
+#include "common.h"
+#include "browser.h"
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+#include "utils/base64.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "osu_client.h"
+
+
+static int hs20_spp_update_response(struct hs20_osu_client *ctx,
+ const char *session_id,
+ const char *spp_status,
+ const char *error_code);
+static void hs20_policy_update_complete(
+ struct hs20_osu_client *ctx, const char *pps_fname);
+
+
+static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+ char *attr_name)
+{
+ return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
+}
+
+
+static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
+ const char *expected_name)
+{
+ struct xml_node_ctx *xctx = ctx->xml;
+ const char *name;
+ char *err;
+ int ret;
+
+ if (!xml_node_is_element(xctx, node))
+ return -1;
+
+ name = xml_node_get_localname(xctx, node);
+ if (name == NULL)
+ return -1;
+
+ if (strcmp(expected_name, name) != 0) {
+ wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
+ name, expected_name);
+ write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
+ name, expected_name);
+ return -1;
+ }
+
+ ret = xml_validate(xctx, node, "spp.xsd", &err);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
+ write_summary(ctx, "SPP XML schema validation failed");
+ os_free(err);
+ }
+ return ret;
+}
+
+
+static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
+ xml_node_t *parent, const char *urn,
+ const char *fname)
+{
+ xml_node_t *node;
+ xml_node_t *fnode, *tnds;
+ char *str;
+
+ fnode = node_from_file(ctx, fname);
+ if (!fnode)
+ return;
+ tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
+ xml_node_free(ctx, fnode);
+ if (!tnds)
+ return;
+
+ str = xml_node_to_str(ctx, tnds);
+ xml_node_free(ctx, tnds);
+ if (str == NULL)
+ return;
+
+ node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
+ if (node)
+ xml_node_add_attr(ctx, node, ns, "moURN", urn);
+ os_free(str);
+}
+
+
+static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
+ xml_namespace_t **ret_ns,
+ const char *session_id,
+ const char *reason)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node;
+
+ write_summary(ctx, "Building sppPostDevData requestReason='%s'",
+ reason);
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppPostDevData");
+ if (spp_node == NULL)
+ return NULL;
+ if (ret_ns)
+ *ret_ns = ns;
+
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+ xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
+ if (session_id)
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
+ session_id);
+ xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
+ "http://localhost:12345/");
+
+ xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
+ "1.0");
+ xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
+ URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
+ URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
+
+ add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
+ "devinfo.xml");
+ add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
+ "devdetail.xml");
+
+ return spp_node;
+}
+
+
+static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
+ xml_node_t *update)
+{
+ xml_node_t *node, *parent, *tnds, *unode;
+ char *str;
+ const char *name;
+ char *uri, *pos;
+ char *cdata, *cdata_end;
+ size_t fqdn_len;
+
+ wpa_printf(MSG_INFO, "Processing updateNode");
+ debug_dump_node(ctx, "updateNode", update);
+
+ uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
+ if (uri == NULL) {
+ wpa_printf(MSG_INFO, "No managementTreeURI present");
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
+
+ name = os_strrchr(uri, '/');
+ if (name == NULL) {
+ wpa_printf(MSG_INFO, "Unexpected URI");
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ name++;
+ wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
+
+ str = xml_node_get_text(ctx->xml, update);
+ if (str == NULL) {
+ wpa_printf(MSG_INFO, "Could not extract MO text");
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
+ cdata = strstr(str, "<![CDATA[");
+ cdata_end = strstr(str, "]]>");
+ if (cdata && cdata_end && cdata_end > cdata &&
+ cdata < strstr(str, "MgmtTree") &&
+ cdata_end > strstr(str, "/MgmtTree")) {
+ char *tmp;
+ wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
+ tmp = strdup(cdata + 9);
+ if (tmp) {
+ cdata_end = strstr(tmp, "]]>");
+ if (cdata_end)
+ *cdata_end = '\0';
+ wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
+ tmp);
+ tnds = xml_node_from_buf(ctx->xml, tmp);
+ free(tmp);
+ } else
+ tnds = NULL;
+ } else
+ tnds = xml_node_from_buf(ctx->xml, str);
+ xml_node_get_text_free(ctx->xml, str);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+
+ unode = tnds_to_mo(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (unode == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+
+ debug_dump_node(ctx, "Parsed TNDS", unode);
+
+ if (get_node_uri(ctx->xml, unode, name) == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+
+ if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ pos = uri + 8;
+
+ if (ctx->fqdn == NULL) {
+ wpa_printf(MSG_INFO, "FQDN not known");
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ fqdn_len = os_strlen(ctx->fqdn);
+ if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+ pos[fqdn_len] != '/') {
+ wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
+ ctx->fqdn);
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ pos += fqdn_len + 1;
+
+ if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+ wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
+ ctx->fqdn);
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ pos += 24;
+
+ wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
+
+ node = get_node(ctx->xml, pps, pos);
+ if (node) {
+ parent = xml_node_get_parent(ctx->xml, node);
+ xml_node_detach(ctx->xml, node);
+ wpa_printf(MSG_INFO, "Replace '%s' node", name);
+ } else {
+ char *pos2;
+ pos2 = os_strrchr(pos, '/');
+ if (pos2 == NULL) {
+ parent = pps;
+ } else {
+ *pos2 = '\0';
+ parent = get_node(ctx->xml, pps, pos);
+ }
+ if (parent == NULL) {
+ wpa_printf(MSG_INFO, "Could not find parent %s", pos);
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "Add '%s' node", name);
+ }
+ xml_node_add_child(ctx->xml, parent, unode);
+
+ xml_node_get_attr_value_free(ctx->xml, uri);
+
+ return 0;
+}
+
+
+static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
+ const char *pps_fname, xml_node_t *pps)
+{
+ wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
+ xml_node_for_each_sibling(ctx->xml, update) {
+ xml_node_for_each_check(ctx->xml, update);
+ if (process_update_node(ctx, pps, update) < 0)
+ return -1;
+ }
+
+ return update_pps_file(ctx, pps_fname, pps);
+}
+
+
+static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
+ const char *pps_fname)
+{
+ /*
+ * Update wpa_supplicant credentials and reconnect using updated
+ * information.
+ */
+ wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+ cmd_set_pps(ctx, pps_fname);
+
+ if (ctx->no_reconnect)
+ return;
+
+ wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+ wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
+}
+
+
+static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
+ xml_node_t *cmd,
+ const char *session_id,
+ const char *pps_fname)
+{
+ xml_namespace_t *ns;
+ xml_node_t *node, *ret_node;
+ char *urn;
+
+ urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
+ if (!urn) {
+ wpa_printf(MSG_INFO, "No URN included");
+ return NULL;
+ }
+ wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
+ if (strcasecmp(urn, URN_HS20_PPS) != 0) {
+ wpa_printf(MSG_INFO, "Unsupported moURN");
+ xml_node_get_attr_value_free(ctx->xml, urn);
+ return NULL;
+ }
+ xml_node_get_attr_value_free(ctx->xml, urn);
+
+ if (!pps_fname) {
+ wpa_printf(MSG_INFO, "PPS file name no known");
+ return NULL;
+ }
+
+ node = build_spp_post_dev_data(ctx, &ns, session_id,
+ "MO upload");
+ if (node == NULL)
+ return NULL;
+ add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
+
+ ret_node = soap_send_receive(ctx->http, node);
+ if (ret_node == NULL)
+ return NULL;
+
+ debug_dump_node(ctx, "Received response to MO upload", ret_node);
+
+ if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+ wpa_printf(MSG_INFO, "SPP validation failed");
+ xml_node_free(ctx->xml, ret_node);
+ return NULL;
+ }
+
+ return ret_node;
+}
+
+
+static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
+ char *fname, size_t fname_len)
+{
+ char *uri, *urn;
+ int ret;
+
+ debug_dump_node(ctx, "Received addMO", add_mo);
+
+ urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
+ if (urn == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
+ if (strcasecmp(urn, URN_HS20_PPS) != 0) {
+ wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
+ xml_node_get_attr_value_free(ctx->xml, urn);
+ return -1;
+ }
+ xml_node_get_attr_value_free(ctx->xml, urn);
+
+ uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
+ if (uri == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
+
+ ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return ret;
+}
+
+
+static int process_spp_user_input_response(struct hs20_osu_client *ctx,
+ const char *session_id,
+ xml_node_t *add_mo)
+{
+ int ret;
+ char fname[300];
+
+ debug_dump_node(ctx, "addMO", add_mo);
+
+ wpa_printf(MSG_INFO, "Subscription registration completed");
+
+ if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
+ wpa_printf(MSG_INFO, "Could not add MO");
+ ret = hs20_spp_update_response(
+ ctx, session_id,
+ "Error occurred",
+ "MO addition or update failed");
+ return 0;
+ }
+
+ ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
+ if (ret == 0)
+ hs20_sub_rem_complete(ctx, fname);
+
+ return 0;
+}
+
+
+static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
+ const char *session_id)
+{
+ xml_node_t *node, *ret_node;
+
+ node = build_spp_post_dev_data(ctx, NULL, session_id,
+ "User input completed");
+ if (node == NULL)
+ return NULL;
+
+ ret_node = soap_send_receive(ctx->http, node);
+ if (!ret_node) {
+ if (soap_reinit_client(ctx->http) < 0)
+ return NULL;
+ wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
+ node = build_spp_post_dev_data(ctx, NULL, session_id,
+ "User input completed");
+ if (node == NULL)
+ return NULL;
+ ret_node = soap_send_receive(ctx->http, node);
+ if (ret_node == NULL)
+ return NULL;
+ wpa_printf(MSG_INFO, "Continue with new connection");
+ }
+
+ if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+ wpa_printf(MSG_INFO, "SPP validation failed");
+ xml_node_free(ctx->xml, ret_node);
+ return NULL;
+ }
+
+ return ret_node;
+}
+
+
+static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
+ xml_node_t *cmd,
+ const char *session_id,
+ const char *pps_fname)
+{
+ xml_namespace_t *ns;
+ xml_node_t *node, *ret_node;
+ int res;
+
+ wpa_printf(MSG_INFO, "Client certificate enrollment");
+
+ res = osu_get_certificate(ctx, cmd);
+ if (res < 0)
+ wpa_printf(MSG_INFO, "EST simpleEnroll failed");
+
+ node = build_spp_post_dev_data(ctx, &ns, session_id,
+ res == 0 ?
+ "Certificate enrollment completed" :
+ "Certificate enrollment failed");
+ if (node == NULL)
+ return NULL;
+
+ ret_node = soap_send_receive(ctx->http, node);
+ if (ret_node == NULL)
+ return NULL;
+
+ debug_dump_node(ctx, "Received response to certificate enrollment "
+ "completed", ret_node);
+
+ if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+ wpa_printf(MSG_INFO, "SPP validation failed");
+ xml_node_free(ctx->xml, ret_node);
+ return NULL;
+ }
+
+ return ret_node;
+}
+
+
+static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
+ const char *session_id, const char *pps_fname,
+ xml_node_t *pps, xml_node_t **ret_node)
+{
+ xml_node_t *cmd;
+ const char *name;
+ char *uri;
+ char *id = strdup(session_id);
+
+ if (id == NULL)
+ return -1;
+
+ *ret_node = NULL;
+
+ debug_dump_node(ctx, "exec", exec);
+
+ xml_node_for_each_child(ctx->xml, cmd, exec) {
+ xml_node_for_each_check(ctx->xml, cmd);
+ break;
+ }
+ if (!cmd) {
+ wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
+ cmd);
+ free(id);
+ return -1;
+ }
+
+ name = xml_node_get_localname(ctx->xml, cmd);
+
+ if (strcasecmp(name, "launchBrowserToURI") == 0) {
+ int res;
+ uri = xml_node_get_text(ctx->xml, cmd);
+ if (!uri) {
+ wpa_printf(MSG_INFO, "No URI found");
+ free(id);
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
+ write_summary(ctx, "Launch browser to URI '%s'", uri);
+ res = hs20_web_browser(uri);
+ xml_node_get_text_free(ctx->xml, uri);
+ if (res > 0) {
+ wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
+ id);
+ write_summary(ctx, "User response in browser completed successfully");
+ *ret_node = hs20_spp_user_input_completed(ctx, id);
+ free(id);
+ return *ret_node ? 0 : -1;
+ } else {
+ wpa_printf(MSG_INFO, "Failed to receive user response");
+ write_summary(ctx, "Failed to receive user response");
+ hs20_spp_update_response(
+ ctx, id, "Error occurred", "Other");
+ free(id);
+ return -1;
+ }
+ return 0;
+ }
+
+ if (strcasecmp(name, "uploadMO") == 0) {
+ if (pps_fname == NULL)
+ return -1;
+ *ret_node = hs20_spp_upload_mo(ctx, cmd, id,
+ pps_fname);
+ free(id);
+ return *ret_node ? 0 : -1;
+ }
+
+ if (strcasecmp(name, "getCertificate") == 0) {
+ *ret_node = hs20_spp_get_certificate(ctx, cmd, id,
+ pps_fname);
+ free(id);
+ return *ret_node ? 0 : -1;
+ }
+
+ wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
+ free(id);
+ return -1;
+}
+
+
+enum spp_post_dev_data_use {
+ SPP_SUBSCRIPTION_REMEDIATION,
+ SPP_POLICY_UPDATE,
+ SPP_SUBSCRIPTION_REGISTRATION,
+};
+
+static void process_spp_post_dev_data_response(
+ struct hs20_osu_client *ctx,
+ enum spp_post_dev_data_use use, xml_node_t *node,
+ const char *pps_fname, xml_node_t *pps)
+{
+ xml_node_t *child;
+ char *status = NULL;
+ xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
+ char *session_id = NULL;
+
+ debug_dump_node(ctx, "sppPostDevDataResponse node", node);
+
+ status = get_spp_attr_value(ctx->xml, node, "sppStatus");
+ if (status == NULL) {
+ wpa_printf(MSG_INFO, "No sppStatus attribute");
+ goto out;
+ }
+ write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
+ status);
+
+ session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
+ if (session_id == NULL) {
+ wpa_printf(MSG_INFO, "No sessionID attribute");
+ goto out;
+ }
+
+ wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s' sessionID: '%s'",
+ status, session_id);
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ const char *name;
+ xml_node_for_each_check(ctx->xml, child);
+ debug_dump_node(ctx, "child", child);
+ name = xml_node_get_localname(ctx->xml, child);
+ wpa_printf(MSG_INFO, "localname: '%s'", name);
+ if (!update && strcasecmp(name, "updateNode") == 0)
+ update = child;
+ if (!exec && strcasecmp(name, "exec") == 0)
+ exec = child;
+ if (!add_mo && strcasecmp(name, "addMO") == 0)
+ add_mo = child;
+ if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
+ no_mo = child;
+ }
+
+ if (use == SPP_SUBSCRIPTION_REMEDIATION &&
+ strcasecmp(status,
+ "Remediation complete, request sppUpdateResponse") == 0)
+ {
+ int res, ret;
+ if (!update && !no_mo) {
+ wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
+ goto out;
+ }
+ wpa_printf(MSG_INFO, "Subscription remediation completed");
+ res = update_pps(ctx, update, pps_fname, pps);
+ if (res < 0)
+ wpa_printf(MSG_INFO, "Failed to update PPS MO");
+ ret = hs20_spp_update_response(
+ ctx, session_id,
+ res < 0 ? "Error occurred" : "OK",
+ res < 0 ? "MO addition or update failed" : NULL);
+ if (res == 0 && ret == 0)
+ hs20_sub_rem_complete(ctx, pps_fname);
+ goto out;
+ }
+
+ if (use == SPP_SUBSCRIPTION_REMEDIATION &&
+ strcasecmp(status, "Exchange complete, release TLS connection") ==
+ 0) {
+ if (!no_mo) {
+ wpa_printf(MSG_INFO, "No noMOUpdate element");
+ goto out;
+ }
+ wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
+ goto out;
+ }
+
+ if (use == SPP_POLICY_UPDATE &&
+ strcasecmp(status, "Update complete, request sppUpdateResponse") ==
+ 0) {
+ int res, ret;
+ wpa_printf(MSG_INFO, "Policy update received - update PPS");
+ res = update_pps(ctx, update, pps_fname, pps);
+ ret = hs20_spp_update_response(
+ ctx, session_id,
+ res < 0 ? "Error occurred" : "OK",
+ res < 0 ? "MO addition or update failed" : NULL);
+ if (res == 0 && ret == 0)
+ hs20_policy_update_complete(ctx, pps_fname);
+ goto out;
+ }
+
+ if (use == SPP_SUBSCRIPTION_REGISTRATION &&
+ strcasecmp(status, "Provisioning complete, request "
+ "sppUpdateResponse") == 0) {
+ if (!add_mo) {
+ wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
+ goto out;
+ }
+ process_spp_user_input_response(ctx, session_id, add_mo);
+ node = NULL;
+ goto out;
+ }
+
+ if (strcasecmp(status, "No update available at this time") == 0) {
+ wpa_printf(MSG_INFO, "No update available at this time");
+ goto out;
+ }
+
+ if (strcasecmp(status, "OK") == 0) {
+ int res;
+ xml_node_t *ret;
+
+ if (!exec) {
+ wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
+ goto out;
+ }
+ res = hs20_spp_exec(ctx, exec, session_id,
+ pps_fname, pps, &ret);
+ /* xml_node_free(ctx->xml, node); */
+ node = NULL;
+ if (res == 0 && ret)
+ process_spp_post_dev_data_response(ctx, use,
+ ret, pps_fname, pps);
+ goto out;
+ }
+
+ if (strcasecmp(status, "Error occurred") == 0) {
+ xml_node_t *err;
+ char *code = NULL;
+ err = get_node(ctx->xml, node, "sppError");
+ if (err)
+ code = xml_node_get_attr_value(ctx->xml, err,
+ "errorCode");
+ wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
+ code ? code : "N/A");
+ xml_node_get_attr_value_free(ctx->xml, code);
+ goto out;
+ }
+
+ wpa_printf(MSG_INFO,
+ "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
+ status);
+out:
+ xml_node_get_attr_value_free(ctx->xml, status);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+ xml_node_free(ctx->xml, node);
+}
+
+
+static int spp_post_dev_data(struct hs20_osu_client *ctx,
+ enum spp_post_dev_data_use use,
+ const char *reason,
+ const char *pps_fname, xml_node_t *pps)
+{
+ xml_node_t *payload;
+ xml_node_t *ret_node;
+
+ payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
+ if (payload == NULL)
+ return -1;
+
+ ret_node = soap_send_receive(ctx->http, payload);
+ if (!ret_node) {
+ const char *err = http_get_err(ctx->http);
+ if (err) {
+ wpa_printf(MSG_INFO, "HTTP error: %s", err);
+ write_result(ctx, "HTTP error: %s", err);
+ } else {
+ write_summary(ctx, "Failed to send SOAP message");
+ }
+ return -1;
+ }
+
+ if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+ wpa_printf(MSG_INFO, "SPP validation failed");
+ xml_node_free(ctx->xml, ret_node);
+ return -1;
+ }
+
+ process_spp_post_dev_data_response(ctx, use, ret_node,
+ pps_fname, pps);
+ return 0;
+}
+
+
+void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps)
+{
+ wpa_printf(MSG_INFO, "SPP subscription remediation");
+ write_summary(ctx, "SPP subscription remediation");
+
+ os_free(ctx->server_url);
+ ctx->server_url = os_strdup(address);
+
+ if (soap_init_client(ctx->http, address, ca_fname,
+ cred_username, cred_password, client_cert,
+ client_key) == 0) {
+ spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
+ "Subscription remediation", pps_fname, pps);
+ }
+}
+
+
+static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
+ const char *pps_fname)
+{
+ wpa_printf(MSG_INFO, "Policy update completed");
+
+ /*
+ * Update wpa_supplicant credentials and reconnect using updated
+ * information.
+ */
+ wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+ cmd_set_pps(ctx, pps_fname);
+
+ wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+ wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
+}
+
+
+static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
+ xml_node_t *node)
+{
+ char *status, *session_id;
+
+ debug_dump_node(ctx, "sppExchangeComplete", node);
+
+ status = get_spp_attr_value(ctx->xml, node, "sppStatus");
+ if (status == NULL) {
+ wpa_printf(MSG_INFO, "No sppStatus attribute");
+ return -1;
+ }
+ write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
+ status);
+
+ session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
+ if (session_id == NULL) {
+ wpa_printf(MSG_INFO, "No sessionID attribute");
+ xml_node_get_attr_value_free(ctx->xml, status);
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s' sessionID: '%s'",
+ status, session_id);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+
+ if (strcasecmp(status, "Exchange complete, release TLS connection") ==
+ 0) {
+ xml_node_get_attr_value_free(ctx->xml, status);
+ return 0;
+ }
+
+ wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
+ write_summary(ctx, "Unexpected sppStatus '%s'", status);
+ xml_node_get_attr_value_free(ctx->xml, status);
+ return -1;
+}
+
+
+static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
+ const char *session_id,
+ const char *spp_status,
+ const char *error_code)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node;
+
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppUpdateResponse");
+ if (spp_node == NULL)
+ return NULL;
+
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
+
+ if (error_code) {
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ if (node)
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ error_code);
+ }
+
+ return spp_node;
+}
+
+
+static int hs20_spp_update_response(struct hs20_osu_client *ctx,
+ const char *session_id,
+ const char *spp_status,
+ const char *error_code)
+{
+ xml_node_t *node, *ret_node;
+ int ret;
+
+ write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
+ spp_status, error_code);
+ node = build_spp_update_response(ctx, session_id, spp_status,
+ error_code);
+ if (node == NULL)
+ return -1;
+ ret_node = soap_send_receive(ctx->http, node);
+ if (!ret_node) {
+ if (soap_reinit_client(ctx->http) < 0)
+ return -1;
+ wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
+ node = build_spp_update_response(ctx, session_id, spp_status,
+ error_code);
+ if (node == NULL)
+ return -1;
+ ret_node = soap_send_receive(ctx->http, node);
+ if (ret_node == NULL)
+ return -1;
+ wpa_printf(MSG_INFO, "Continue with new connection");
+ }
+
+ if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
+ wpa_printf(MSG_INFO, "SPP validation failed");
+ xml_node_free(ctx->xml, ret_node);
+ return -1;
+ }
+
+ ret = process_spp_exchange_complete(ctx, ret_node);
+ xml_node_free(ctx->xml, ret_node);
+ return ret;
+}
+
+
+void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps)
+{
+ wpa_printf(MSG_INFO, "SPP policy update");
+ write_summary(ctx, "SPP policy update");
+
+ os_free(ctx->server_url);
+ ctx->server_url = os_strdup(address);
+
+ if (soap_init_client(ctx->http, address, ca_fname, cred_username,
+ cred_password, client_cert, client_key) == 0) {
+ spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
+ pps_fname, pps);
+ }
+}
+
+
+int cmd_prov(struct hs20_osu_client *ctx, const char *url,
+ const char *ca_fname)
+{
+ unlink("Cert/est_cert.der");
+ unlink("Cert/est_cert.pem");
+
+ ctx->ca_fname = ca_fname;
+
+ if (url == NULL) {
+ wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "Credential provisioning requested");
+
+ os_free(ctx->server_url);
+ ctx->server_url = os_strdup(url);
+
+ if (soap_init_client(ctx->http, url, ca_fname, NULL, NULL, NULL, NULL) <
+ 0)
+ return -1;
+ spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
+ "Subscription registration", NULL, NULL);
+
+ return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url,
+ const char *ca_fname)
+{
+ ctx->ca_fname = ca_fname;
+
+ if (url == NULL) {
+ wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "SIM provisioning requested");
+
+ os_free(ctx->server_url);
+ ctx->server_url = os_strdup(url);
+
+ wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
+
+ if (wait_ip_addr(ctx->ifname, 15) < 0) {
+ wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+ }
+
+ if (soap_init_client(ctx->http, url, ca_fname, NULL, NULL, NULL, NULL) <
+ 0)
+ return -1;
+ spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
+ "Subscription provisioning", NULL, NULL);
+
+ return ctx->pps_cred_set ? 0 : -1;
+}
diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c
new file mode 100644
index 0000000..28913b9
--- /dev/null
+++ b/src/common/wpa_helpers.c
@@ -0,0 +1,292 @@
+/*
+ * wpa_supplicant ctrl_iface helpers
+ * Copyright (c) 2010-2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+
+#include "common.h"
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
+
+
+char *wpas_ctrl_path = "/var/run/wpa_supplicant/";
+static int default_timeout = 60;
+
+
+static struct wpa_ctrl * wpa_open_ctrl(const char *ifname)
+{
+ char buf[128];
+ struct wpa_ctrl *ctrl;
+
+ os_snprintf(buf, sizeof(buf), "%s%s", wpas_ctrl_path, ifname);
+ ctrl = wpa_ctrl_open(buf);
+ if (ctrl == NULL)
+ printf("wpa_command: wpa_ctrl_open(%s) failed\n", buf);
+ return ctrl;
+}
+
+
+int wpa_command(const char *ifname, const char *cmd)
+{
+ struct wpa_ctrl *ctrl;
+ char buf[128];
+ size_t len;
+
+ printf("wpa_command(ifname='%s', cmd='%s')\n", ifname, cmd);
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+ return -1;
+ len = sizeof(buf);
+ if (wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL) < 0) {
+ printf("wpa_command: wpa_ctrl_request failed\n");
+ wpa_ctrl_close(ctrl);
+ return -1;
+ }
+ wpa_ctrl_close(ctrl);
+ buf[len] = '\0';
+ if (strncmp(buf, "FAIL", 4) == 0) {
+ printf("wpa_command: Command failed (FAIL received)\n");
+ return -1;
+ }
+ return 0;
+}
+
+
+int wpa_command_resp(const char *ifname, const char *cmd,
+ char *resp, size_t resp_size)
+{
+ struct wpa_ctrl *ctrl;
+ size_t len;
+
+ printf("wpa_command(ifname='%s', cmd='%s')\n", ifname, cmd);
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+ return -1;
+ len = resp_size;
+ if (wpa_ctrl_request(ctrl, cmd, strlen(cmd), resp, &len, NULL) < 0) {
+ printf("wpa_command: wpa_ctrl_request failed\n");
+ wpa_ctrl_close(ctrl);
+ return -1;
+ }
+ wpa_ctrl_close(ctrl);
+ resp[len] = '\0';
+ return 0;
+}
+
+
+struct wpa_ctrl * open_wpa_mon(const char *ifname)
+{
+ struct wpa_ctrl *ctrl;
+
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+ return NULL;
+ if (wpa_ctrl_attach(ctrl) < 0) {
+ wpa_ctrl_close(ctrl);
+ return NULL;
+ }
+
+ return ctrl;
+}
+
+
+int get_wpa_cli_event2(struct wpa_ctrl *mon,
+ const char *event, const char *event2,
+ char *buf, size_t buf_size)
+{
+ int fd, ret;
+ fd_set rfd;
+ char *pos;
+ struct timeval tv;
+ time_t start, now;
+
+ printf("Waiting for wpa_cli event %s\n", event);
+ fd = wpa_ctrl_get_fd(mon);
+ if (fd < 0)
+ return -1;
+
+ time(&start);
+ while (1) {
+ size_t len;
+
+ FD_ZERO(&rfd);
+ FD_SET(fd, &rfd);
+ tv.tv_sec = default_timeout;
+ tv.tv_usec = 0;
+ ret = select(fd + 1, &rfd, NULL, NULL, &tv);
+ if (ret == 0) {
+ printf("Timeout on waiting for event %s\n", event);
+ return -1;
+ }
+ if (ret < 0) {
+ printf("select: %s\n", strerror(errno));
+ return -1;
+ }
+ len = buf_size;
+ if (wpa_ctrl_recv(mon, buf, &len) < 0) {
+ printf("Failure while waiting for event %s\n", event);
+ return -1;
+ }
+ if (len == buf_size)
+ len--;
+ buf[len] = '\0';
+
+ pos = strchr(buf, '>');
+ if (pos &&
+ (strncmp(pos + 1, event, strlen(event)) == 0 ||
+ (event2 &&
+ strncmp(pos + 1, event2, strlen(event2)) == 0)))
+ return 0; /* Event found */
+
+ time(&now);
+ if ((int) (now - start) > default_timeout) {
+ printf("Timeout on waiting for event %s\n", event);
+ return -1;
+ }
+ }
+}
+
+
+int get_wpa_cli_event(struct wpa_ctrl *mon,
+ const char *event, char *buf, size_t buf_size)
+{
+ return get_wpa_cli_event2(mon, event, NULL, buf, buf_size);
+}
+
+
+int get_wpa_status(const char *ifname, const char *field, char *obuf,
+ size_t obuf_size)
+{
+ struct wpa_ctrl *ctrl;
+ char buf[4096];
+ char *pos, *end;
+ size_t len, flen;
+
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+ return -1;
+ len = sizeof(buf);
+ if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) {
+ wpa_ctrl_close(ctrl);
+ return -1;
+ }
+ wpa_ctrl_close(ctrl);
+ buf[len] = '\0';
+
+ flen = strlen(field);
+ pos = buf;
+ while (pos + flen < buf + len) {
+ if (pos > buf) {
+ if (*pos != '\n') {
+ pos++;
+ continue;
+ }
+ pos++;
+ }
+ if (strncmp(pos, field, flen) != 0 || pos[flen] != '=') {
+ pos++;
+ continue;
+ }
+ pos += flen + 1;
+ end = strchr(pos, '\n');
+ if (end == NULL)
+ return -1;
+ *end++ = '\0';
+ if (end - pos > (int) obuf_size)
+ return -1;
+ memcpy(obuf, pos, end - pos);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int wait_ip_addr(const char *ifname, int timeout)
+{
+ char ip[30];
+ int count = timeout;
+ struct wpa_ctrl *ctrl;
+
+ while (count > 0) {
+ printf("%s: ifname='%s' - %d seconds remaining\n",
+ __func__, ifname, count);
+ count--;
+ if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0
+ && strlen(ip) > 0) {
+ printf("IP address found: '%s'\n", ip);
+ return 0;
+ }
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+ return -1;
+ wpa_ctrl_close(ctrl);
+ sleep(1);
+ }
+ printf("%s: Could not get IP address for ifname='%s'", __func__,
+ ifname);
+ return -1;
+}
+
+
+int add_network(const char *ifname)
+{
+ char res[30];
+
+ if (wpa_command_resp(ifname, "ADD_NETWORK", res, sizeof(res)) < 0)
+ return -1;
+ return atoi(res);
+}
+
+
+int set_network(const char *ifname, int id, const char *field,
+ const char *value)
+{
+ char buf[200];
+ snprintf(buf, sizeof(buf), "SET_NETWORK %d %s %s", id, field, value);
+ return wpa_command(ifname, buf);
+}
+
+
+int set_network_quoted(const char *ifname, int id, const char *field,
+ const char *value)
+{
+ char buf[200];
+ snprintf(buf, sizeof(buf), "SET_NETWORK %d %s \"%s\"",
+ id, field, value);
+ return wpa_command(ifname, buf);
+}
+
+
+int add_cred(const char *ifname)
+{
+ char res[30];
+
+ if (wpa_command_resp(ifname, "ADD_CRED", res, sizeof(res)) < 0)
+ return -1;
+ return atoi(res);
+}
+
+
+int set_cred(const char *ifname, int id, const char *field, const char *value)
+{
+ char buf[200];
+ snprintf(buf, sizeof(buf), "SET_CRED %d %s %s", id, field, value);
+ return wpa_command(ifname, buf);
+}
+
+
+int set_cred_quoted(const char *ifname, int id, const char *field,
+ const char *value)
+{
+ char buf[200];
+ snprintf(buf, sizeof(buf), "SET_CRED %d %s \"%s\"",
+ id, field, value);
+ return wpa_command(ifname, buf);
+}
diff --git a/src/common/wpa_helpers.h b/src/common/wpa_helpers.h
new file mode 100644
index 0000000..54c2872
--- /dev/null
+++ b/src/common/wpa_helpers.h
@@ -0,0 +1,37 @@
+/*
+ * wpa_supplicant ctrl_iface helpers
+ * Copyright (c) 2010-2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_HELPERS_H
+#define WPA_HELPERS_H
+
+int wpa_command(const char *ifname, const char *cmd);
+int wpa_command_resp(const char *ifname, const char *cmd,
+ char *resp, size_t resp_size);
+int get_wpa_status(const char *ifname, const char *field, char *obuf,
+ size_t obuf_size);
+
+struct wpa_ctrl * open_wpa_mon(const char *ifname);
+int wait_ip_addr(const char *ifname, int timeout);
+int get_wpa_cli_event(struct wpa_ctrl *mon,
+ const char *event, char *buf, size_t buf_size);
+int get_wpa_cli_event2(struct wpa_ctrl *mon,
+ const char *event, const char *event2,
+ char *buf, size_t buf_size);
+
+int add_network(const char *ifname);
+int set_network(const char *ifname, int id, const char *field,
+ const char *value);
+int set_network_quoted(const char *ifname, int id, const char *field,
+ const char *value);
+int add_cred(const char *ifname);
+int set_cred(const char *ifname, int id, const char *field, const char *value);
+int set_cred_quoted(const char *ifname, int id, const char *field,
+ const char *value);
+
+#endif /* WPA_HELPERS_H */
diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c
new file mode 100644
index 0000000..a066392
--- /dev/null
+++ b/src/utils/browser-android.c
@@ -0,0 +1,117 @@
+/*
+ * Hotspot 2.0 client - Web browser using Android browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+ int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+ wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+ "complete");
+ eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+ struct browser_data *data = ctx;
+ struct wpabuf *resp;
+ const char *url;
+ int done = 0;
+
+ url = http_request_get_uri(req);
+ wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+ if (os_strcmp(url, "/") == 0) {
+ data->success = 1;
+ done = 1;
+ } else if (os_strncmp(url, "/osu/", 5) == 0) {
+ data->success = atoi(url + 5);
+ done = 1;
+ }
+
+ resp = wpabuf_alloc(1);
+ if (resp == NULL) {
+ http_request_deinit(req);
+ if (done)
+ eloop_terminate();
+ return;
+ }
+
+ if (done) {
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+ }
+
+ http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+ char cmd[2000];
+ int ret;
+ struct http_server *http;
+ struct in_addr addr;
+ struct browser_data data;
+
+ wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
+
+ os_memset(&data, 0, sizeof(data));
+
+ ret = os_snprintf(cmd, sizeof(cmd),
+ "am start -a android.intent.action.VIEW -d '%s' "
+ "-n com.android.browser/.BrowserActivity", url);
+ if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
+ wpa_printf(MSG_ERROR, "Too long URL");
+ return -1;
+ }
+
+ if (eloop_init() < 0) {
+ wpa_printf(MSG_ERROR, "eloop_init failed");
+ return -1;
+ }
+ addr.s_addr = htonl((127 << 24) | 1);
+ http = http_server_init(&addr, 12345, http_req, &data);
+ if (http == NULL) {
+ wpa_printf(MSG_ERROR, "http_server_init failed");
+ eloop_destroy();
+ return -1;
+ }
+
+ if (system(cmd) != 0) {
+ wpa_printf(MSG_INFO, "Failed to launch Android browser");
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+ return -1;
+ }
+
+ eloop_register_timeout(30, 0, browser_timeout, &data, NULL);
+ eloop_run();
+ eloop_cancel_timeout(browser_timeout, &data, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+
+ wpa_printf(MSG_INFO, "Closing Android browser");
+ if (system("input keyevent 3") != 0) {
+ wpa_printf(MSG_INFO, "Failed to inject keyevent");
+ }
+
+ return data.success;
+}
diff --git a/src/utils/browser-system.c b/src/utils/browser-system.c
new file mode 100644
index 0000000..2884d34
--- /dev/null
+++ b/src/utils/browser-system.c
@@ -0,0 +1,112 @@
+/*
+ * Hotspot 2.0 client - Web browser using system browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+ int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+ wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+ "complete");
+ eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+ struct browser_data *data = ctx;
+ struct wpabuf *resp;
+ const char *url;
+ int done = 0;
+
+ url = http_request_get_uri(req);
+ wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+ if (os_strcmp(url, "/") == 0) {
+ data->success = 1;
+ done = 1;
+ } else if (os_strncmp(url, "/osu/", 5) == 0) {
+ data->success = atoi(url + 5);
+ done = 1;
+ }
+
+ resp = wpabuf_alloc(1);
+ if (resp == NULL) {
+ http_request_deinit(req);
+ if (done)
+ eloop_terminate();
+ return;
+ }
+
+ if (done) {
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+ }
+
+ http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+ char cmd[2000];
+ int ret;
+ struct http_server *http;
+ struct in_addr addr;
+ struct browser_data data;
+
+ wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
+
+ os_memset(&data, 0, sizeof(data));
+
+ ret = os_snprintf(cmd, sizeof(cmd), "x-www-browser '%s' &", url);
+ if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
+ wpa_printf(MSG_ERROR, "Too long URL");
+ return -1;
+ }
+
+ if (eloop_init() < 0) {
+ wpa_printf(MSG_ERROR, "eloop_init failed");
+ return -1;
+ }
+ addr.s_addr = htonl((127 << 24) | 1);
+ http = http_server_init(&addr, 12345, http_req, &data);
+ if (http == NULL) {
+ wpa_printf(MSG_ERROR, "http_server_init failed");
+ eloop_destroy();
+ return -1;
+ }
+
+ if (system(cmd) != 0) {
+ wpa_printf(MSG_INFO, "Failed to launch browser");
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+ return -1;
+ }
+
+ eloop_register_timeout(120, 0, browser_timeout, &data, NULL);
+ eloop_run();
+ eloop_cancel_timeout(browser_timeout, &data, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+
+ /* TODO: Close browser */
+
+ return data.success;
+}
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
new file mode 100644
index 0000000..eeb8f65
--- /dev/null
+++ b/src/utils/browser-wpadebug.c
@@ -0,0 +1,123 @@
+/*
+ * Hotspot 2.0 client - Web browser using wpadebug on Android
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+ int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+ wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+ "complete");
+ eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+ struct browser_data *data = ctx;
+ struct wpabuf *resp;
+ const char *url;
+ int done = 0;
+
+ url = http_request_get_uri(req);
+ wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+ if (os_strcmp(url, "/") == 0) {
+ data->success = 1;
+ done = 1;
+ } else if (os_strncmp(url, "/osu/", 5) == 0) {
+ data->success = atoi(url + 5);
+ done = 1;
+ }
+
+ resp = wpabuf_alloc(100);
+ if (resp == NULL) {
+ http_request_deinit(req);
+ if (done)
+ eloop_terminate();
+ return;
+ }
+ wpabuf_put_str(resp, "User input completed");
+
+ if (done) {
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+ }
+
+ http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+ char cmd[2000];
+ int ret;
+ struct http_server *http;
+ struct in_addr addr;
+ struct browser_data data;
+
+ wpa_printf(MSG_INFO, "Launching wpadebug browser to %s", url);
+
+ os_memset(&data, 0, sizeof(data));
+
+ ret = os_snprintf(cmd, sizeof(cmd),
+ "am start -a android.action.MAIN "
+ "-c android.intent.category.LAUNCHER "
+ "-n w1.fi.wpadebug/.WpaWebViewActivity "
+ "-e w1.fi.wpadebug.URL '%s'", url);
+ if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
+ wpa_printf(MSG_ERROR, "Too long URL");
+ return -1;
+ }
+
+ if (eloop_init() < 0) {
+ wpa_printf(MSG_ERROR, "eloop_init failed");
+ return -1;
+ }
+ addr.s_addr = htonl((127 << 24) | 1);
+ http = http_server_init(&addr, 12345, http_req, &data);
+ if (http == NULL) {
+ wpa_printf(MSG_ERROR, "http_server_init failed");
+ eloop_destroy();
+ return -1;
+ }
+
+ if (system(cmd) != 0) {
+ wpa_printf(MSG_INFO, "Failed to launch wpadebug browser");
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+ return -1;
+ }
+
+ eloop_register_timeout(300, 0, browser_timeout, &data, NULL);
+ eloop_run();
+ eloop_cancel_timeout(browser_timeout, &data, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+
+ wpa_printf(MSG_INFO, "Closing Android browser");
+ if (system("am start -a android.action.MAIN "
+ "-c android.intent.category.LAUNCHER "
+ "-n w1.fi.wpadebug/.WpaWebViewActivity "
+ "-e w1.fi.wpadebug.URL FINISH") != 0) {
+ wpa_printf(MSG_INFO, "Failed to close wpadebug browser");
+ }
+
+ return data.success;
+}
diff --git a/src/utils/browser.c b/src/utils/browser.c
new file mode 100644
index 0000000..9cf6152
--- /dev/null
+++ b/src/utils/browser.c
@@ -0,0 +1,219 @@
+/*
+ * Hotspot 2.0 client - Web browser using WebKit
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <webkit/webkit.h>
+
+#include "common.h"
+#include "browser.h"
+
+
+struct browser_context {
+ GtkWidget *win;
+ int success;
+ int progress;
+ char *hover_link;
+ char *title;
+};
+
+static void win_cb_destroy(GtkWidget *win, struct browser_context *ctx)
+{
+ wpa_printf(MSG_DEBUG, "BROWSER:%s", __func__);
+ gtk_main_quit();
+}
+
+
+static void browser_update_title(struct browser_context *ctx)
+{
+ char buf[100];
+
+ if (ctx->hover_link) {
+ gtk_window_set_title(GTK_WINDOW(ctx->win), ctx->hover_link);
+ return;
+ }
+
+ if (ctx->progress == 100) {
+ gtk_window_set_title(GTK_WINDOW(ctx->win),
+ ctx->title ? ctx->title :
+ "Hotspot 2.0 client");
+ return;
+ }
+
+ snprintf(buf, sizeof(buf), "[%d%%] %s", ctx->progress,
+ ctx->title ? ctx->title : "Hotspot 2.0 client");
+ gtk_window_set_title(GTK_WINDOW(ctx->win), buf);
+}
+
+
+static void view_cb_notify_progress(WebKitWebView *view, GParamSpec *pspec,
+ struct browser_context *ctx)
+{
+ ctx->progress = 100 * webkit_web_view_get_progress(view);
+ wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__,
+ ctx->progress);
+ browser_update_title(ctx);
+}
+
+
+static void view_cb_notify_load_status(WebKitWebView *view, GParamSpec *pspec,
+ struct browser_context *ctx)
+{
+ int status = webkit_web_view_get_load_status(view);
+ wpa_printf(MSG_DEBUG, "BROWSER:%s load-status=%d uri=%s",
+ __func__, status, webkit_web_view_get_uri(view));
+}
+
+
+static void view_cb_resource_request_starting(WebKitWebView *view,
+ WebKitWebFrame *frame,
+ WebKitWebResource *res,
+ WebKitNetworkRequest *req,
+ WebKitNetworkResponse *resp,
+ struct browser_context *ctx)
+{
+ const gchar *uri = webkit_network_request_get_uri(req);
+ wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+ if (g_str_has_suffix(uri, "/favicon.ico"))
+ webkit_network_request_set_uri(req, "about:blank");
+ if (g_str_has_prefix(uri, "osu://")) {
+ ctx->success = atoi(uri + 6);
+ gtk_main_quit();
+ }
+ if (g_str_has_prefix(uri, "http://localhost:12345")) {
+ /*
+ * This is used as a special trigger to indicate that the
+ * user exchange has been completed.
+ */
+ ctx->success = 1;
+ gtk_main_quit();
+ }
+}
+
+
+static gboolean view_cb_mime_type_policy_decision(
+ WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *req,
+ gchar *mime, WebKitWebPolicyDecision *policy,
+ struct browser_context *ctx)
+{
+ wpa_printf(MSG_DEBUG, "BROWSER:%s mime=%s", __func__, mime);
+
+ if (!webkit_web_view_can_show_mime_type(view, mime)) {
+ webkit_web_policy_decision_download(policy);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean view_cb_download_requested(WebKitWebView *view,
+ WebKitDownload *dl,
+ struct browser_context *ctx)
+{
+ const gchar *uri;
+ uri = webkit_download_get_uri(dl);
+ wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+ return FALSE;
+}
+
+
+static void view_cb_hovering_over_link(WebKitWebView *view, gchar *title,
+ gchar *uri, struct browser_context *ctx)
+{
+ wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s uri=%s", __func__, title,
+ uri);
+ os_free(ctx->hover_link);
+ if (uri)
+ ctx->hover_link = os_strdup(uri);
+ else
+ ctx->hover_link = NULL;
+
+ browser_update_title(ctx);
+}
+
+
+static void view_cb_title_changed(WebKitWebView *view, WebKitWebFrame *frame,
+ const char *title,
+ struct browser_context *ctx)
+{
+ wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title);
+ os_free(ctx->title);
+ ctx->title = os_strdup(title);
+ browser_update_title(ctx);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+ GtkWidget *scroll;
+ SoupSession *s;
+ WebKitWebView *view;
+ WebKitWebSettings *settings;
+ struct browser_context ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+ if (!gtk_init_check(NULL, NULL))
+ return -1;
+
+ s = webkit_get_default_session();
+ g_object_set(G_OBJECT(s), "ssl-ca-file",
+ "/etc/ssl/certs/ca-certificates.crt", NULL);
+ g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL);
+
+ ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_wmclass(GTK_WINDOW(ctx.win), "Hotspot 2.0 client",
+ "Hotspot 2.0 client");
+ gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600);
+
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+ GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+
+ g_signal_connect(G_OBJECT(ctx.win), "destroy",
+ G_CALLBACK(win_cb_destroy), &ctx);
+
+ view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+ g_signal_connect(G_OBJECT(view), "notify::progress",
+ G_CALLBACK(view_cb_notify_progress), &ctx);
+ g_signal_connect(G_OBJECT(view), "notify::load-status",
+ G_CALLBACK(view_cb_notify_load_status), &ctx);
+ g_signal_connect(G_OBJECT(view), "resource-request-starting",
+ G_CALLBACK(view_cb_resource_request_starting), &ctx);
+ g_signal_connect(G_OBJECT(view), "mime-type-policy-decision-requested",
+ G_CALLBACK(view_cb_mime_type_policy_decision), &ctx);
+ g_signal_connect(G_OBJECT(view), "download-requested",
+ G_CALLBACK(view_cb_download_requested), &ctx);
+ g_signal_connect(G_OBJECT(view), "hovering-over-link",
+ G_CALLBACK(view_cb_hovering_over_link), &ctx);
+ g_signal_connect(G_OBJECT(view), "title-changed",
+ G_CALLBACK(view_cb_title_changed), &ctx);
+
+ gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(ctx.win), GTK_WIDGET(scroll));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+ gtk_widget_show_all(ctx.win);
+
+ settings = webkit_web_view_get_settings(view);
+ g_object_set(G_OBJECT(settings), "user-agent",
+ "Mozilla/5.0 (X11; U; Unix; en-US) "
+ "AppleWebKit/537.15 (KHTML, like Gecko) "
+ "hs20-client/1.0", NULL);
+ g_object_set(G_OBJECT(settings), "auto-load-images", TRUE, NULL);
+
+ webkit_web_view_load_uri(view, url);
+
+ gtk_main();
+ gtk_widget_destroy(ctx.win);
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ free(ctx.hover_link);
+ free(ctx.title);
+ return ctx.success;
+}
diff --git a/src/utils/browser.h b/src/utils/browser.h
new file mode 100644
index 0000000..aaa0eed
--- /dev/null
+++ b/src/utils/browser.h
@@ -0,0 +1,21 @@
+/*
+ * Hotspot 2.0 client - Web browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BROWSER_H
+#define BROWSER_H
+
+#ifdef CONFIG_NO_BROWSER
+static inline int hs20_web_browser(const char *url)
+{
+ return -1;
+}
+#else /* CONFIG_NO_BROWSER */
+int hs20_web_browser(const char *url);
+#endif /* CONFIG_NO_BROWSER */
+
+#endif /* BROWSER_H */
diff --git a/src/utils/http-utils.h b/src/utils/http-utils.h
new file mode 100644
index 0000000..8d4399a
--- /dev/null
+++ b/src/utils/http-utils.h
@@ -0,0 +1,63 @@
+/*
+ * HTTP wrapper
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HTTP_UTILS_H
+#define HTTP_UTILS_H
+
+struct http_ctx;
+
+struct http_othername {
+ char *oid;
+ u8 *data;
+ size_t len;
+};
+
+#define HTTP_MAX_CERT_LOGO_HASH 32
+
+struct http_logo {
+ char *alg_oid;
+ u8 *hash;
+ size_t hash_len;
+ char *uri;
+};
+
+struct http_cert {
+ char **dnsname;
+ unsigned int num_dnsname;
+ struct http_othername *othername;
+ unsigned int num_othername;
+ struct http_logo *logo;
+ unsigned int num_logo;
+};
+
+int soap_init_client(struct http_ctx *ctx, const char *address,
+ const char *ca_fname, const char *username,
+ const char *password, const char *client_cert,
+ const char *client_key);
+int soap_reinit_client(struct http_ctx *ctx);
+xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node);
+
+struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx);
+void http_ocsp_set(struct http_ctx *ctx, int val);
+void http_deinit_ctx(struct http_ctx *ctx);
+
+int http_download_file(struct http_ctx *ctx, const char *url,
+ const char *fname, const char *ca_fname);
+char * http_post(struct http_ctx *ctx, const char *url, const char *data,
+ const char *content_type, const char *ext_hdr,
+ const char *ca_fname,
+ const char *username, const char *password,
+ const char *client_cert, const char *client_key,
+ size_t *resp_len);
+void http_set_cert_cb(struct http_ctx *ctx,
+ int (*cb)(void *ctx, struct http_cert *cert),
+ void *cb_ctx);
+const char * http_get_err(struct http_ctx *ctx);
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname);
+
+#endif /* HTTP_UTILS_H */
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
new file mode 100644
index 0000000..668c1a6
--- /dev/null
+++ b/src/utils/http_curl.c
@@ -0,0 +1,1642 @@
+/*
+ * HTTP wrapper for libcurl
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <curl/curl.h>
+#ifdef EAP_TLS_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509v3.h>
+
+#ifdef SSL_set_tlsext_status_type
+#ifndef OPENSSL_NO_TLSEXT
+#define HAVE_OCSP
+#include <openssl/err.h>
+#include <openssl/ocsp.h>
+#endif /* OPENSSL_NO_TLSEXT */
+#endif /* SSL_set_tlsext_status_type */
+#endif /* EAP_TLS_OPENSSL */
+
+#include "common.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+
+
+struct http_ctx {
+ void *ctx;
+ struct xml_node_ctx *xml;
+ CURL *curl;
+ struct curl_slist *curl_hdr;
+ char *svc_address;
+ char *svc_ca_fname;
+ char *svc_username;
+ char *svc_password;
+ char *svc_client_cert;
+ char *svc_client_key;
+ char *curl_buf;
+ size_t curl_buf_len;
+
+ int (*cert_cb)(void *ctx, struct http_cert *cert);
+ void *cert_cb_ctx;
+
+ enum {
+ NO_OCSP, OPTIONAL_OCSP, MANDATORY_OCSP
+ } ocsp;
+ X509 *peer_cert;
+ X509 *peer_issuer;
+ X509 *peer_issuer_issuer;
+
+ const char *last_err;
+};
+
+
+static void clear_curl(struct http_ctx *ctx)
+{
+ if (ctx->curl) {
+ curl_easy_cleanup(ctx->curl);
+ ctx->curl = NULL;
+ }
+ if (ctx->curl_hdr) {
+ curl_slist_free_all(ctx->curl_hdr);
+ ctx->curl_hdr = NULL;
+ }
+}
+
+
+static void clone_str(char **dst, const char *src)
+{
+ os_free(*dst);
+ if (src)
+ *dst = os_strdup(src);
+ else
+ *dst = NULL;
+}
+
+
+static void debug_dump(struct http_ctx *ctx, const char *title,
+ const char *buf, size_t len)
+{
+ char *txt = os_malloc(len + 1);
+ if (txt == NULL)
+ return;
+ os_memcpy(txt, buf, len);
+ txt[len] = '\0';
+ while (len > 0) {
+ len--;
+ if (txt[len] == '\r' || txt[len] == '\n')
+ txt[len] = '\0';
+ }
+ wpa_printf(MSG_MSGDUMP, "%s[%s]", title, txt);
+ os_free(txt);
+}
+
+
+static int curl_cb_debug(CURL *curl, curl_infotype info, char *buf, size_t len,
+ void *userdata)
+{
+ struct http_ctx *ctx = userdata;
+ switch (info) {
+ case CURLINFO_TEXT:
+ debug_dump(ctx, "CURLINFO_TEXT", buf, len);
+ break;
+ case CURLINFO_HEADER_IN:
+ debug_dump(ctx, "CURLINFO_HEADER_IN", buf, len);
+ break;
+ case CURLINFO_HEADER_OUT:
+ debug_dump(ctx, "CURLINFO_HEADER_OUT", buf, len);
+ break;
+ case CURLINFO_DATA_IN:
+ debug_dump(ctx, "CURLINFO_DATA_IN", buf, len);
+ break;
+ case CURLINFO_DATA_OUT:
+ debug_dump(ctx, "CURLINFO_DATA_OUT", buf, len);
+ break;
+ case CURLINFO_SSL_DATA_IN:
+ wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_IN - %d",
+ (int) len);
+ break;
+ case CURLINFO_SSL_DATA_OUT:
+ wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_OUT - %d",
+ (int) len);
+ break;
+ case CURLINFO_END:
+ wpa_printf(MSG_DEBUG, "debug - CURLINFO_END - %d",
+ (int) len);
+ break;
+ }
+ return 0;
+}
+
+
+static size_t curl_cb_header(void *ptr, size_t size, size_t nmemb,
+ void *userdata)
+{
+ struct http_ctx *ctx = userdata;
+ debug_dump(ctx, "curl header", ptr, size * nmemb);
+ return size * nmemb;
+}
+
+
+static size_t curl_cb_write(void *ptr, size_t size, size_t nmemb,
+ void *userdata)
+{
+ struct http_ctx *ctx = userdata;
+ char *n;
+ debug_dump(ctx, "curl write", ptr, size * nmemb);
+ n = os_realloc(ctx->curl_buf, ctx->curl_buf_len + size * nmemb + 1);
+ if (n == NULL)
+ return 0;
+ ctx->curl_buf = n;
+ os_memcpy(n + ctx->curl_buf_len, ptr, size * nmemb);
+ n[ctx->curl_buf_len + size * nmemb] = '\0';
+ ctx->curl_buf_len += size * nmemb;
+ return size * nmemb;
+}
+
+
+#ifdef EAP_TLS_OPENSSL
+
+static void debug_dump_cert(const char *title, X509 *cert)
+{
+ BIO *out;
+ char *txt;
+ size_t rlen;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return;
+
+ X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ int res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_MSGDUMP, "%s:\n%s", title, txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+}
+
+
+static void add_alt_name_othername(struct http_ctx *ctx, struct http_cert *cert,
+ OTHERNAME *o)
+{
+ char txt[100];
+ int res;
+ struct http_othername *on;
+ ASN1_TYPE *val;
+
+ on = os_realloc_array(cert->othername, cert->num_othername + 1,
+ sizeof(struct http_othername));
+ if (on == NULL)
+ return;
+ cert->othername = on;
+ on = &on[cert->num_othername];
+ os_memset(on, 0, sizeof(*on));
+
+ res = OBJ_obj2txt(txt, sizeof(txt), o->type_id, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ return;
+
+ on->oid = os_strdup(txt);
+ if (on->oid == NULL)
+ return;
+
+ val = o->value;
+ on->data = val->value.octet_string->data;
+ on->len = val->value.octet_string->length;
+
+ cert->num_othername++;
+}
+
+
+static void add_alt_name_dns(struct http_ctx *ctx, struct http_cert *cert,
+ ASN1_STRING *name)
+{
+ char *buf;
+ char **n;
+
+ buf = NULL;
+ if (ASN1_STRING_to_UTF8((unsigned char **) &buf, name) < 0)
+ return;
+
+ n = os_realloc_array(cert->dnsname, cert->num_dnsname + 1,
+ sizeof(char *));
+ if (n == NULL)
+ return;
+
+ cert->dnsname = n;
+ n[cert->num_dnsname] = buf;
+ cert->num_dnsname++;
+}
+
+
+static void add_alt_name(struct http_ctx *ctx, struct http_cert *cert,
+ const GENERAL_NAME *name)
+{
+ switch (name->type) {
+ case GEN_OTHERNAME:
+ add_alt_name_othername(ctx, cert, name->d.otherName);
+ break;
+ case GEN_DNS:
+ add_alt_name_dns(ctx, cert, name->d.dNSName);
+ break;
+ }
+}
+
+
+static void add_alt_names(struct http_ctx *ctx, struct http_cert *cert,
+ GENERAL_NAMES *names)
+{
+ int num, i;
+
+ num = sk_GENERAL_NAME_num(names);
+ for (i = 0; i < num; i++) {
+ const GENERAL_NAME *name;
+ name = sk_GENERAL_NAME_value(names, i);
+ add_alt_name(ctx, cert, name);
+ }
+}
+
+
+/* RFC 3709 */
+
+typedef struct {
+ X509_ALGOR *hashAlg;
+ ASN1_OCTET_STRING *hashValue;
+} HashAlgAndValue;
+
+typedef struct {
+ STACK_OF(HashAlgAndValue) *refStructHash;
+ STACK_OF(ASN1_IA5STRING) *refStructURI;
+} LogotypeReference;
+
+typedef struct {
+ ASN1_IA5STRING *mediaType;
+ STACK_OF(HashAlgAndValue) *logotypeHash;
+ STACK_OF(ASN1_IA5STRING) *logotypeURI;
+} LogotypeDetails;
+
+typedef struct {
+ int type;
+ union {
+ ASN1_INTEGER *numBits;
+ ASN1_INTEGER *tableSize;
+ } d;
+} LogotypeImageResolution;
+
+typedef struct {
+ ASN1_INTEGER *type; /* LogotypeImageType ::= INTEGER */
+ ASN1_INTEGER *fileSize;
+ ASN1_INTEGER *xSize;
+ ASN1_INTEGER *ySize;
+ LogotypeImageResolution *resolution;
+ ASN1_IA5STRING *language;
+} LogotypeImageInfo;
+
+typedef struct {
+ LogotypeDetails *imageDetails;
+ LogotypeImageInfo *imageInfo;
+} LogotypeImage;
+
+typedef struct {
+ ASN1_INTEGER *fileSize;
+ ASN1_INTEGER *playTime;
+ ASN1_INTEGER *channels;
+ ASN1_INTEGER *sampleRate;
+ ASN1_IA5STRING *language;
+} LogotypeAudioInfo;
+
+typedef struct {
+ LogotypeDetails *audioDetails;
+ LogotypeAudioInfo *audioInfo;
+} LogotypeAudio;
+
+typedef struct {
+ STACK_OF(LogotypeImage) *image;
+ STACK_OF(LogotypeAudio) *audio;
+} LogotypeData;
+
+typedef struct {
+ int type;
+ union {
+ LogotypeData *direct;
+ LogotypeReference *indirect;
+ } d;
+} LogotypeInfo;
+
+typedef struct {
+ ASN1_OBJECT *logotypeType;
+ LogotypeInfo *info;
+} OtherLogotypeInfo;
+
+typedef struct {
+ STACK_OF(LogotypeInfo) *communityLogos;
+ LogotypeInfo *issuerLogo;
+ LogotypeInfo *subjectLogo;
+ STACK_OF(OtherLogotypeInfo) *otherLogos;
+} LogotypeExtn;
+
+ASN1_SEQUENCE(HashAlgAndValue) = {
+ ASN1_SIMPLE(HashAlgAndValue, hashAlg, X509_ALGOR),
+ ASN1_SIMPLE(HashAlgAndValue, hashValue, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(HashAlgAndValue);
+
+ASN1_SEQUENCE(LogotypeReference) = {
+ ASN1_SEQUENCE_OF(LogotypeReference, refStructHash, HashAlgAndValue),
+ ASN1_SEQUENCE_OF(LogotypeReference, refStructURI, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(LogotypeReference);
+
+ASN1_SEQUENCE(LogotypeDetails) = {
+ ASN1_SIMPLE(LogotypeDetails, mediaType, ASN1_IA5STRING),
+ ASN1_SEQUENCE_OF(LogotypeDetails, logotypeHash, HashAlgAndValue),
+ ASN1_SEQUENCE_OF(LogotypeDetails, logotypeURI, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(LogotypeDetails);
+
+ASN1_CHOICE(LogotypeImageResolution) = {
+ ASN1_IMP(LogotypeImageResolution, d.numBits, ASN1_INTEGER, 1),
+ ASN1_IMP(LogotypeImageResolution, d.tableSize, ASN1_INTEGER, 2)
+} ASN1_CHOICE_END(LogotypeImageResolution);
+
+ASN1_SEQUENCE(LogotypeImageInfo) = {
+ ASN1_IMP_OPT(LogotypeImageInfo, type, ASN1_INTEGER, 0),
+ ASN1_SIMPLE(LogotypeImageInfo, fileSize, ASN1_INTEGER),
+ ASN1_SIMPLE(LogotypeImageInfo, xSize, ASN1_INTEGER),
+ ASN1_SIMPLE(LogotypeImageInfo, ySize, ASN1_INTEGER),
+ ASN1_OPT(LogotypeImageInfo, resolution, LogotypeImageResolution),
+ ASN1_IMP_OPT(LogotypeImageInfo, language, ASN1_IA5STRING, 4),
+} ASN1_SEQUENCE_END(LogotypeImageInfo);
+
+ASN1_SEQUENCE(LogotypeImage) = {
+ ASN1_SIMPLE(LogotypeImage, imageDetails, LogotypeDetails),
+ ASN1_OPT(LogotypeImage, imageInfo, LogotypeImageInfo)
+} ASN1_SEQUENCE_END(LogotypeImage);
+
+ASN1_SEQUENCE(LogotypeAudioInfo) = {
+ ASN1_SIMPLE(LogotypeAudioInfo, fileSize, ASN1_INTEGER),
+ ASN1_SIMPLE(LogotypeAudioInfo, playTime, ASN1_INTEGER),
+ ASN1_SIMPLE(LogotypeAudioInfo, channels, ASN1_INTEGER),
+ ASN1_IMP_OPT(LogotypeAudioInfo, sampleRate, ASN1_INTEGER, 3),
+ ASN1_IMP_OPT(LogotypeAudioInfo, language, ASN1_IA5STRING, 4)
+} ASN1_SEQUENCE_END(LogotypeAudioInfo);
+
+ASN1_SEQUENCE(LogotypeAudio) = {
+ ASN1_SIMPLE(LogotypeAudio, audioDetails, LogotypeDetails),
+ ASN1_OPT(LogotypeAudio, audioInfo, LogotypeAudioInfo)
+} ASN1_SEQUENCE_END(LogotypeAudio);
+
+ASN1_SEQUENCE(LogotypeData) = {
+ ASN1_SEQUENCE_OF_OPT(LogotypeData, image, LogotypeImage),
+ ASN1_IMP_SEQUENCE_OF_OPT(LogotypeData, audio, LogotypeAudio, 1)
+} ASN1_SEQUENCE_END(LogotypeData);
+
+ASN1_CHOICE(LogotypeInfo) = {
+ ASN1_IMP(LogotypeInfo, d.direct, LogotypeData, 0),
+ ASN1_IMP(LogotypeInfo, d.indirect, LogotypeReference, 1)
+} ASN1_CHOICE_END(LogotypeInfo);
+
+ASN1_SEQUENCE(OtherLogotypeInfo) = {
+ ASN1_SIMPLE(OtherLogotypeInfo, logotypeType, ASN1_OBJECT),
+ ASN1_SIMPLE(OtherLogotypeInfo, info, LogotypeInfo)
+} ASN1_SEQUENCE_END(OtherLogotypeInfo);
+
+ASN1_SEQUENCE(LogotypeExtn) = {
+ ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, communityLogos, LogotypeInfo, 0),
+ ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 1),
+ ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 2),
+ ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, otherLogos, OtherLogotypeInfo, 3)
+} ASN1_SEQUENCE_END(LogotypeExtn);
+
+IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn);
+
+#define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st))
+#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i))
+#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st))
+#define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i))
+#define sk_LogotypeAudio_num(st) SKM_sk_num(LogotypeAudio, (st))
+#define sk_LogotypeAudio_value(st, i) SKM_sk_value(LogotypeAudio, (st), (i))
+#define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st))
+#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i))
+#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
+#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
+
+
+static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
+ HashAlgAndValue *hash, ASN1_IA5STRING *uri)
+{
+ char txt[100];
+ int res, len;
+ struct http_logo *n;
+
+ if (hash == NULL || uri == NULL)
+ return;
+
+ res = OBJ_obj2txt(txt, sizeof(txt), hash->hashAlg->algorithm, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ return;
+
+ n = os_realloc_array(hcert->logo, hcert->num_logo + 1,
+ sizeof(struct http_logo));
+ if (n == NULL)
+ return;
+ hcert->logo = n;
+ n = &hcert->logo[hcert->num_logo];
+ os_memset(n, 0, sizeof(*n));
+
+ n->alg_oid = os_strdup(txt);
+ if (n->alg_oid == NULL)
+ return;
+
+ n->hash_len = ASN1_STRING_length(hash->hashValue);
+ n->hash = os_malloc(n->hash_len);
+ if (n->hash == NULL) {
+ os_free(n->alg_oid);
+ return;
+ }
+ os_memcpy(n->hash, ASN1_STRING_data(hash->hashValue), n->hash_len);
+
+ len = ASN1_STRING_length(uri);
+ n->uri = os_malloc(len + 1);
+ if (n->uri == NULL) {
+ os_free(n->alg_oid);
+ os_free(n->hash);
+ return;
+ }
+ os_memcpy(n->uri, ASN1_STRING_data(uri), len);
+ n->uri[len] = '\0';
+
+ hcert->num_logo++;
+}
+
+
+static void add_logo_direct(struct http_ctx *ctx, struct http_cert *hcert,
+ LogotypeData *data)
+{
+ int i, num;
+
+ if (data->image == NULL)
+ return;
+
+ num = sk_LogotypeImage_num(data->image);
+ for (i = 0; i < num; i++) {
+ LogotypeImage *image;
+ LogotypeDetails *details;
+ int j, hash_num, uri_num;
+ HashAlgAndValue *found_hash = NULL;
+
+ image = sk_LogotypeImage_value(data->image, i);
+ if (image == NULL)
+ continue;
+
+ details = image->imageDetails;
+ if (details == NULL)
+ continue;
+
+ hash_num = sk_HashAlgAndValue_num(details->logotypeHash);
+ for (j = 0; j < hash_num; j++) {
+ HashAlgAndValue *hash;
+ char txt[100];
+ int res;
+ hash = sk_HashAlgAndValue_value(details->logotypeHash,
+ j);
+ if (hash == NULL)
+ continue;
+ res = OBJ_obj2txt(txt, sizeof(txt),
+ hash->hashAlg->algorithm, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ continue;
+ if (os_strcmp(txt, "2.16.840.1.101.3.4.2.1") == 0) {
+ found_hash = hash;
+ break;
+ }
+ }
+
+ if (!found_hash) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: No SHA256 hash found for the logo");
+ continue;
+ }
+
+ uri_num = sk_ASN1_IA5STRING_num(details->logotypeURI);
+ for (j = 0; j < uri_num; j++) {
+ ASN1_IA5STRING *uri;
+ uri = sk_ASN1_IA5STRING_value(details->logotypeURI, j);
+ add_logo(ctx, hcert, found_hash, uri);
+ }
+ }
+}
+
+
+static void add_logo_indirect(struct http_ctx *ctx, struct http_cert *hcert,
+ LogotypeReference *ref)
+{
+ int j, hash_num, uri_num;
+
+ hash_num = sk_HashAlgAndValue_num(ref->refStructHash);
+ uri_num = sk_ASN1_IA5STRING_num(ref->refStructURI);
+ if (hash_num != uri_num) {
+ wpa_printf(MSG_INFO, "Unexpected LogotypeReference array size difference %d != %d",
+ hash_num, uri_num);
+ return;
+ }
+
+ for (j = 0; j < hash_num; j++) {
+ HashAlgAndValue *hash;
+ ASN1_IA5STRING *uri;
+ hash = sk_HashAlgAndValue_value(ref->refStructHash, j);
+ uri = sk_ASN1_IA5STRING_value(ref->refStructURI, j);
+ add_logo(ctx, hcert, hash, uri);
+ }
+}
+
+
+static void i2r_HashAlgAndValue(HashAlgAndValue *hash, BIO *out, int indent)
+{
+ int i;
+ const unsigned char *data;
+
+ BIO_printf(out, "%*shashAlg: ", indent, "");
+ i2a_ASN1_OBJECT(out, hash->hashAlg->algorithm);
+ BIO_printf(out, "\n");
+
+ BIO_printf(out, "%*shashValue: ", indent, "");
+ data = hash->hashValue->data;
+ for (i = 0; i < hash->hashValue->length; i++)
+ BIO_printf(out, "%s%02x", i > 0 ? ":" : "", data[i]);
+ BIO_printf(out, "\n");
+}
+
+static void i2r_LogotypeDetails(LogotypeDetails *details, BIO *out, int indent)
+{
+ int i, num;
+
+ BIO_printf(out, "%*sLogotypeDetails\n", indent, "");
+ if (details->mediaType) {
+ BIO_printf(out, "%*smediaType: ", indent, "");
+ ASN1_STRING_print(out, details->mediaType);
+ BIO_printf(out, "\n");
+ }
+
+ num = details->logotypeHash ?
+ sk_HashAlgAndValue_num(details->logotypeHash) : 0;
+ for (i = 0; i < num; i++) {
+ HashAlgAndValue *hash;
+ hash = sk_HashAlgAndValue_value(details->logotypeHash, i);
+ i2r_HashAlgAndValue(hash, out, indent);
+ }
+
+ num = details->logotypeURI ?
+ sk_ASN1_IA5STRING_num(details->logotypeURI) : 0;
+ for (i = 0; i < num; i++) {
+ ASN1_IA5STRING *uri;
+ uri = sk_ASN1_IA5STRING_value(details->logotypeURI, i);
+ BIO_printf(out, "%*slogotypeURI: ", indent, "");
+ ASN1_STRING_print(out, uri);
+ BIO_printf(out, "\n");
+ }
+}
+
+static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent)
+{
+ long val;
+
+ BIO_printf(out, "%*sLogotypeImageInfo\n", indent, "");
+ if (info->type) {
+ val = ASN1_INTEGER_get(info->type);
+ BIO_printf(out, "%*stype: %ld\n", indent, "", val);
+ } else {
+ BIO_printf(out, "%*stype: default (1)\n", indent, "");
+ }
+ val = ASN1_INTEGER_get(info->xSize);
+ BIO_printf(out, "%*sxSize: %ld\n", indent, "", val);
+ val = ASN1_INTEGER_get(info->ySize);
+ BIO_printf(out, "%*sySize: %ld\n", indent, "", val);
+ if (info->resolution) {
+ BIO_printf(out, "%*sresolution\n", indent, "");
+ /* TODO */
+ }
+ if (info->language) {
+ BIO_printf(out, "%*slanguage: ", indent, "");
+ ASN1_STRING_print(out, info->language);
+ BIO_printf(out, "\n");
+ }
+}
+
+static void i2r_LogotypeImage(LogotypeImage *image, BIO *out, int indent)
+{
+ BIO_printf(out, "%*sLogotypeImage\n", indent, "");
+ if (image->imageDetails) {
+ i2r_LogotypeDetails(image->imageDetails, out, indent + 4);
+ }
+ if (image->imageInfo) {
+ i2r_LogotypeImageInfo(image->imageInfo, out, indent + 4);
+ }
+}
+
+static void i2r_LogotypeData(LogotypeData *data, const char *title, BIO *out,
+ int indent)
+{
+ int i, num;
+
+ BIO_printf(out, "%*s%s - LogotypeData\n", indent, "", title);
+
+ num = data->image ? sk_LogotypeImage_num(data->image) : 0;
+ for (i = 0; i < num; i++) {
+ LogotypeImage *image = sk_LogotypeImage_value(data->image, i);
+ i2r_LogotypeImage(image, out, indent + 4);
+ }
+
+ num = data->audio ? sk_LogotypeAudio_num(data->audio) : 0;
+ for (i = 0; i < num; i++) {
+ BIO_printf(out, "%*saudio: TODO\n", indent, "");
+ }
+}
+
+static void i2r_LogotypeReference(LogotypeReference *ref, const char *title,
+ BIO *out, int indent)
+{
+ int i, hash_num, uri_num;
+
+ BIO_printf(out, "%*s%s - LogotypeReference\n", indent, "", title);
+
+ hash_num = ref->refStructHash ?
+ sk_HashAlgAndValue_num(ref->refStructHash) : 0;
+ uri_num = ref->refStructURI ?
+ sk_ASN1_IA5STRING_num(ref->refStructURI) : 0;
+ if (hash_num != uri_num) {
+ BIO_printf(out, "%*sUnexpected LogotypeReference array size difference %d != %d\n",
+ indent, "", hash_num, uri_num);
+ return;
+ }
+
+ for (i = 0; i < hash_num; i++) {
+ HashAlgAndValue *hash;
+ ASN1_IA5STRING *uri;
+
+ hash = sk_HashAlgAndValue_value(ref->refStructHash, i);
+ i2r_HashAlgAndValue(hash, out, indent);
+
+ uri = sk_ASN1_IA5STRING_value(ref->refStructURI, i);
+ BIO_printf(out, "%*srefStructURI: ", indent, "");
+ ASN1_STRING_print(out, uri);
+ BIO_printf(out, "\n");
+ }
+}
+
+static void i2r_LogotypeInfo(LogotypeInfo *info, const char *title, BIO *out,
+ int indent)
+{
+ switch (info->type) {
+ case 0:
+ i2r_LogotypeData(info->d.direct, title, out, indent);
+ break;
+ case 1:
+ i2r_LogotypeReference(info->d.indirect, title, out, indent);
+ break;
+ }
+}
+
+static void debug_print_logotypeext(LogotypeExtn *logo)
+{
+ BIO *out;
+ int i, num;
+ int indent = 0;
+
+ out = BIO_new_fp(stdout, BIO_NOCLOSE);
+ if (out == NULL)
+ return;
+
+ if (logo->communityLogos) {
+ num = sk_LogotypeInfo_num(logo->communityLogos);
+ for (i = 0; i < num; i++) {
+ LogotypeInfo *info;
+ info = sk_LogotypeInfo_value(logo->communityLogos, i);
+ i2r_LogotypeInfo(info, "communityLogo", out, indent);
+ }
+ }
+
+ if (logo->issuerLogo) {
+ i2r_LogotypeInfo(logo->issuerLogo, "issuerLogo", out, indent );
+ }
+
+ if (logo->subjectLogo) {
+ i2r_LogotypeInfo(logo->subjectLogo, "subjectLogo", out, indent);
+ }
+
+ if (logo->otherLogos) {
+ BIO_printf(out, "%*sotherLogos - TODO\n", indent, "");
+ }
+
+ BIO_free(out);
+}
+
+
+static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert,
+ X509 *cert)
+{
+ ASN1_OBJECT *obj;
+ int pos;
+ X509_EXTENSION *ext;
+ ASN1_OCTET_STRING *os;
+ LogotypeExtn *logo;
+ const unsigned char *data;
+ int i, num;
+
+ obj = OBJ_txt2obj("1.3.6.1.5.5.7.1.12", 0);
+ if (obj == NULL)
+ return;
+
+ pos = X509_get_ext_by_OBJ(cert, obj, -1);
+ if (pos < 0) {
+ wpa_printf(MSG_INFO, "No logotype extension included");
+ return;
+ }
+
+ wpa_printf(MSG_INFO, "Parsing logotype extension");
+ ext = X509_get_ext(cert, pos);
+ if (!ext) {
+ wpa_printf(MSG_INFO, "Could not get logotype extension");
+ return;
+ }
+
+ os = X509_EXTENSION_get_data(ext);
+ if (os == NULL) {
+ wpa_printf(MSG_INFO, "Could not get logotype extension data");
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "logotypeExtn",
+ ASN1_STRING_data(os), ASN1_STRING_length(os));
+
+ data = ASN1_STRING_data(os);
+ logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os));
+ if (logo == NULL) {
+ wpa_printf(MSG_INFO, "Failed to parse logotypeExtn");
+ return;
+ }
+
+ if (wpa_debug_level < MSG_INFO)
+ debug_print_logotypeext(logo);
+
+ if (!logo->communityLogos) {
+ wpa_printf(MSG_INFO, "No communityLogos included");
+ LogotypeExtn_free(logo);
+ return;
+ }
+
+ num = sk_LogotypeInfo_num(logo->communityLogos);
+ for (i = 0; i < num; i++) {
+ LogotypeInfo *info;
+ info = sk_LogotypeInfo_value(logo->communityLogos, i);
+ switch (info->type) {
+ case 0:
+ add_logo_direct(ctx, hcert, info->d.direct);
+ break;
+ case 1:
+ add_logo_indirect(ctx, hcert, info->d.indirect);
+ break;
+ }
+ }
+
+ LogotypeExtn_free(logo);
+}
+
+
+static void parse_cert(struct http_ctx *ctx, struct http_cert *hcert,
+ X509 *cert, GENERAL_NAMES **names)
+{
+ os_memset(hcert, 0, sizeof(*hcert));
+
+ *names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+ if (*names)
+ add_alt_names(ctx, hcert, *names);
+
+ add_logotype_ext(ctx, hcert, cert);
+}
+
+
+static void parse_cert_free(struct http_cert *hcert, GENERAL_NAMES *names)
+{
+ unsigned int i;
+
+ for (i = 0; i < hcert->num_dnsname; i++)
+ OPENSSL_free(hcert->dnsname[i]);
+ os_free(hcert->dnsname);
+
+ for (i = 0; i < hcert->num_othername; i++)
+ os_free(hcert->othername[i].oid);
+ os_free(hcert->othername);
+
+ for (i = 0; i < hcert->num_logo; i++) {
+ os_free(hcert->logo[i].alg_oid);
+ os_free(hcert->logo[i].hash);
+ os_free(hcert->logo[i].uri);
+ }
+ os_free(hcert->logo);
+
+ sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+}
+
+
+static int validate_server_cert(struct http_ctx *ctx, X509 *cert)
+{
+ GENERAL_NAMES *names;
+ struct http_cert hcert;
+ int ret;
+
+ if (ctx->cert_cb == NULL)
+ return 0;
+
+ if (0) {
+ BIO *out;
+ out = BIO_new_fp(stdout, BIO_NOCLOSE);
+ X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+ BIO_free(out);
+ }
+
+ parse_cert(ctx, &hcert, cert, &names);
+ ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert);
+ parse_cert_free(&hcert, names);
+
+ return ret;
+}
+
+
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname)
+{
+ BIO *in, *out;
+ X509 *cert;
+ GENERAL_NAMES *names;
+ struct http_cert hcert;
+ unsigned int i;
+
+ in = BIO_new_file(fname, "r");
+ if (in == NULL) {
+ wpa_printf(MSG_ERROR, "Could not read '%s'", fname);
+ return;
+ }
+
+ cert = d2i_X509_bio(in, NULL);
+ BIO_free(in);
+
+ if (cert == NULL) {
+ wpa_printf(MSG_ERROR, "Could not parse certificate");
+ return;
+ }
+
+ out = BIO_new_fp(stdout, BIO_NOCLOSE);
+ if (out) {
+ X509_print_ex(out, cert, XN_FLAG_COMPAT,
+ X509_FLAG_COMPAT);
+ BIO_free(out);
+ }
+
+ wpa_printf(MSG_INFO, "Additional parsing information:");
+ parse_cert(ctx, &hcert, cert, &names);
+ for (i = 0; i < hcert.num_othername; i++) {
+ if (os_strcmp(hcert.othername[i].oid,
+ "1.3.6.1.4.1.40808.1.1.1") == 0) {
+ char *name = os_zalloc(hcert.othername[i].len + 1);
+ if (name) {
+ os_memcpy(name, hcert.othername[i].data,
+ hcert.othername[i].len);
+ wpa_printf(MSG_INFO,
+ "id-wfa-hotspot-friendlyName: %s",
+ name);
+ os_free(name);
+ }
+ wpa_hexdump_ascii(MSG_INFO,
+ "id-wfa-hotspot-friendlyName",
+ hcert.othername[i].data,
+ hcert.othername[i].len);
+ } else {
+ wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s",
+ hcert.othername[i].oid);
+ wpa_hexdump_ascii(MSG_INFO, "unknown othername",
+ hcert.othername[i].data,
+ hcert.othername[i].len);
+ }
+ }
+ parse_cert_free(&hcert, names);
+
+ X509_free(cert);
+}
+
+
+static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+ struct http_ctx *ctx;
+ X509 *cert;
+ int err, depth;
+ char buf[256];
+ X509_NAME *name;
+ const char *err_str;
+ SSL *ssl;
+ SSL_CTX *ssl_ctx;
+
+ ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ ssl_ctx = ssl->ctx;
+ ctx = SSL_CTX_get_app_data(ssl_ctx);
+
+ wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify");
+
+ err = X509_STORE_CTX_get_error(x509_ctx);
+ err_str = X509_verify_cert_error_string(err);
+ depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+ cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+ if (!cert) {
+ wpa_printf(MSG_INFO, "No server certificate available");
+ ctx->last_err = "No server certificate available";
+ return 0;
+ }
+
+ if (depth == 0)
+ ctx->peer_cert = cert;
+ else if (depth == 1)
+ ctx->peer_issuer = cert;
+ else if (depth == 2)
+ ctx->peer_issuer_issuer = cert;
+
+ name = X509_get_subject_name(cert);
+ X509_NAME_oneline(name, buf, sizeof(buf));
+ wpa_printf(MSG_INFO, "Server certificate chain - depth=%d err=%d (%s) subject=%s",
+ depth, err, err_str, buf);
+ debug_dump_cert("Server certificate chain - certificate", cert);
+
+ if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0)
+ return 0;
+
+ if (!preverify_ok)
+ ctx->last_err = "TLS validation failed";
+
+ return preverify_ok;
+}
+
+
+#ifdef HAVE_OCSP
+
+static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
+{
+ BIO *out;
+ size_t rlen;
+ char *txt;
+ int res;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return;
+
+ OCSP_RESPONSE_print(out, rsp, 0);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (!txt) {
+ BIO_free(out);
+ return;
+ }
+
+ res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_MSGDUMP, "OpenSSL: OCSP Response\n%s", txt);
+ }
+ os_free(txt);
+ BIO_free(out);
+}
+
+
+static void tls_show_errors(const char *func, const char *txt)
+{
+ unsigned long err;
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - %s %s",
+ func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: pending error: %s",
+ ERR_error_string(err, NULL));
+ }
+}
+
+
+static int ocsp_resp_cb(SSL *s, void *arg)
+{
+ struct http_ctx *ctx = arg;
+ const unsigned char *p;
+ int len, status, reason;
+ OCSP_RESPONSE *rsp;
+ OCSP_BASICRESP *basic;
+ OCSP_CERTID *id;
+ ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update;
+ X509_STORE *store;
+ STACK_OF(X509) *certs = NULL;
+
+ len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+ if (!p) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
+ if (ctx->ocsp == MANDATORY_OCSP)
+ ctx->last_err = "No OCSP response received";
+ return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len);
+
+ rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+ if (!rsp) {
+ wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response");
+ ctx->last_err = "Failed to parse OCSP response";
+ return 0;
+ }
+
+ ocsp_debug_print_resp(rsp);
+
+ status = OCSP_response_status(rsp);
+ if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)",
+ status, OCSP_response_status_str(status));
+ ctx->last_err = "OCSP responder error";
+ return 0;
+ }
+
+ basic = OCSP_response_get1_basic(rsp);
+ if (!basic) {
+ wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
+ ctx->last_err = "Could not find BasicOCSPResponse";
+ return 0;
+ }
+
+ store = SSL_CTX_get_cert_store(s->ctx);
+ if (ctx->peer_issuer) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer");
+ debug_dump_cert("OpenSSL: Issuer certificate",
+ ctx->peer_issuer);
+
+ if (X509_STORE_add_cert(store, ctx->peer_issuer) != 1) {
+ tls_show_errors(__func__,
+ "OpenSSL: Could not add issuer to certificate store\n");
+ }
+ certs = sk_X509_new_null();
+ if (certs) {
+ X509 *cert;
+ cert = X509_dup(ctx->peer_issuer);
+ if (cert && !sk_X509_push(certs, cert)) {
+ tls_show_errors(
+ __func__,
+ "OpenSSL: Could not add issuer to OCSP responder trust store\n");
+ X509_free(cert);
+ sk_X509_free(certs);
+ certs = NULL;
+ }
+ if (ctx->peer_issuer_issuer) {
+ X509 *cert;
+ cert = X509_dup(ctx->peer_issuer_issuer);
+ if (cert && !sk_X509_push(certs, cert)) {
+ tls_show_errors(
+ __func__,
+ "OpenSSL: Could not add issuer to OCSP responder trust store\n");
+ X509_free(cert);
+ }
+ }
+ }
+ }
+
+ status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER);
+ sk_X509_pop_free(certs, X509_free);
+ if (status <= 0) {
+ tls_show_errors(__func__,
+ "OpenSSL: OCSP response failed verification");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err = "OCSP response failed verification";
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
+
+ if (!ctx->peer_cert) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err = "Peer certificate not available for OCSP status check";
+ return 0;
+ }
+
+ if (!ctx->peer_issuer) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err = "Peer issuer certificate not available for OCSP status check";
+ return 0;
+ }
+
+ id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+ if (!id) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err = "Could not create OCSP certificate identifier";
+ return 0;
+ }
+
+ if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+ &this_update, &next_update)) {
+ wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
+ (ctx->ocsp == MANDATORY_OCSP) ? "" :
+ " (OCSP not required)");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ if (ctx->ocsp == MANDATORY_OCSP)
+
+ ctx->last_err = "Could not find current server certificate from OCSP response";
+ return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1;
+ }
+
+ if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
+ tls_show_errors(__func__, "OpenSSL: OCSP status times invalid");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err = "OCSP status times invalid";
+ return 0;
+ }
+
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s",
+ OCSP_cert_status_str(status));
+
+ if (status == V_OCSP_CERTSTATUS_GOOD)
+ return 1;
+ if (status == V_OCSP_CERTSTATUS_REVOKED)
+ ctx->last_err = "Server certificate has been revoked";
+ return 0;
+ if (ctx->ocsp == MANDATORY_OCSP) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
+ ctx->last_err = "OCSP status unknown";
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
+ return 1;
+}
+
+
+static SSL_METHOD patch_ssl_method;
+static const SSL_METHOD *real_ssl_method;
+
+static int curl_patch_ssl_new(SSL *s)
+{
+ SSL_CTX *ssl = s->ctx;
+ int ret;
+
+ ssl->method = real_ssl_method;
+ s->method = real_ssl_method;
+
+ ret = s->method->ssl_new(s);
+ SSL_set_tlsext_status_type(s, TLSEXT_STATUSTYPE_ocsp);
+
+ return ret;
+}
+
+#endif /* HAVE_OCSP */
+
+
+static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm)
+{
+ struct http_ctx *ctx = parm;
+ SSL_CTX *ssl = sslctx;
+
+ wpa_printf(MSG_DEBUG, "curl_cb_ssl");
+ SSL_CTX_set_app_data(ssl, ctx);
+ SSL_CTX_set_verify(ssl, SSL_VERIFY_PEER, curl_cb_ssl_verify);
+
+#ifdef HAVE_OCSP
+ if (ctx->ocsp != NO_OCSP) {
+ SSL_CTX_set_tlsext_status_cb(ssl, ocsp_resp_cb);
+ SSL_CTX_set_tlsext_status_arg(ssl, ctx);
+
+ /*
+ * Use a temporary SSL_METHOD to get a callback on SSL_new()
+ * from libcurl since there is no proper callback registration
+ * available for this.
+ */
+ os_memset(&patch_ssl_method, 0, sizeof(patch_ssl_method));
+ patch_ssl_method.ssl_new = curl_patch_ssl_new;
+ real_ssl_method = ssl->method;
+ ssl->method = &patch_ssl_method;
+ }
+#endif /* HAVE_OCSP */
+
+ return CURLE_OK;
+}
+
+#endif /* EAP_TLS_OPENSSL */
+
+
+static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
+ const char *ca_fname, const char *username,
+ const char *password, const char *client_cert,
+ const char *client_key)
+{
+ CURL *curl;
+
+ wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s "
+ "username=%s", address, ca_fname, username);
+
+ curl = curl_easy_init();
+ if (curl == NULL)
+ return NULL;
+
+ curl_easy_setopt(curl, CURLOPT_URL, address);
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
+ if (ca_fname) {
+ curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+#ifdef EAP_TLS_OPENSSL
+ curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl);
+ curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx);
+#endif /* EAP_TLS_OPENSSL */
+ } else {
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+ }
+ if (client_cert && client_key) {
+ curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert);
+ curl_easy_setopt(curl, CURLOPT_SSLKEY, client_key);
+ }
+ /* TODO: use curl_easy_getinfo() with CURLINFO_CERTINFO to fetch
+ * information about the server certificate */
+ curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
+ curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug);
+ curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_cb_header);
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, ctx);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb_write);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+ if (username) {
+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
+ curl_easy_setopt(curl, CURLOPT_USERNAME, username);
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
+ }
+
+ return curl;
+}
+
+
+static int post_init_client(struct http_ctx *ctx, const char *address,
+ const char *ca_fname, const char *username,
+ const char *password, const char *client_cert,
+ const char *client_key)
+{
+ char *pos;
+ int count;
+
+ clone_str(&ctx->svc_address, address);
+ clone_str(&ctx->svc_ca_fname, ca_fname);
+ clone_str(&ctx->svc_username, username);
+ clone_str(&ctx->svc_password, password);
+ clone_str(&ctx->svc_client_cert, client_cert);
+ clone_str(&ctx->svc_client_key, client_key);
+
+ /*
+ * Workaround for Apache "Hostname 'FOO' provided via SNI and hostname
+ * 'foo' provided via HTTP are different.
+ */
+ for (count = 0, pos = ctx->svc_address; count < 3 && pos && *pos;
+ pos++) {
+ if (*pos == '/')
+ count++;
+ *pos = tolower(*pos);
+ }
+
+ ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username,
+ password, client_cert, client_key);
+ if (ctx->curl == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+int soap_init_client(struct http_ctx *ctx, const char *address,
+ const char *ca_fname, const char *username,
+ const char *password, const char *client_cert,
+ const char *client_key)
+{
+ if (post_init_client(ctx, address, ca_fname, username, password,
+ client_cert, client_key) < 0)
+ return -1;
+
+ ctx->curl_hdr = curl_slist_append(ctx->curl_hdr,
+ "Content-Type: application/soap+xml");
+ ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "SOAPAction: ");
+ ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "Expect:");
+ curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, ctx->curl_hdr);
+
+ return 0;
+}
+
+
+int soap_reinit_client(struct http_ctx *ctx)
+{
+ char *address = NULL;
+ char *ca_fname = NULL;
+ char *username = NULL;
+ char *password = NULL;
+ char *client_cert = NULL;
+ char *client_key = NULL;
+ int ret;
+
+ clear_curl(ctx);
+
+ clone_str(&address, ctx->svc_address);
+ clone_str(&ca_fname, ctx->svc_ca_fname);
+ clone_str(&username, ctx->svc_username);
+ clone_str(&password, ctx->svc_password);
+ clone_str(&client_cert, ctx->svc_client_cert);
+ clone_str(&client_key, ctx->svc_client_key);
+
+ ret = soap_init_client(ctx, address, ca_fname, username, password,
+ client_cert, client_key);
+ os_free(address);
+ os_free(ca_fname);
+ os_free(username);
+ os_free(password);
+ os_free(client_cert);
+ os_free(client_key);
+ return ret;
+}
+
+
+static void free_curl_buf(struct http_ctx *ctx)
+{
+ os_free(ctx->curl_buf);
+ ctx->curl_buf = NULL;
+ ctx->curl_buf_len = 0;
+}
+
+
+xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node)
+{
+ char *str;
+ xml_node_t *envelope, *ret, *resp, *n;
+ CURLcode res;
+ long http = 0;
+
+ ctx->last_err = NULL;
+
+ wpa_printf(MSG_DEBUG, "SOAP: Sending message");
+ envelope = soap_build_envelope(ctx->xml, node);
+ str = xml_node_to_str(ctx->xml, envelope);
+ xml_node_free(ctx->xml, envelope);
+ wpa_printf(MSG_MSGDUMP, "SOAP[%s]", str);
+
+ curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDS, str);
+ free_curl_buf(ctx);
+
+ res = curl_easy_perform(ctx->curl);
+ if (res != CURLE_OK) {
+ if (!ctx->last_err)
+ ctx->last_err = curl_easy_strerror(res);
+ wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+ ctx->last_err);
+ os_free(str);
+ free_curl_buf(ctx);
+ return NULL;
+ }
+ os_free(str);
+
+ curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &http);
+ wpa_printf(MSG_DEBUG, "SOAP: Server response code %ld", http);
+ if (http != 200) {
+ ctx->last_err = "HTTP download failed";
+ wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
+ free_curl_buf(ctx);
+ return NULL;
+ }
+
+ if (ctx->curl_buf == NULL)
+ return NULL;
+
+ wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ctx->curl_buf);
+ resp = xml_node_from_buf(ctx->xml, ctx->curl_buf);
+ free_curl_buf(ctx);
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse SOAP response");
+ ctx->last_err = "Could not parse SOAP response";
+ return NULL;
+ }
+
+ ret = soap_get_body(ctx->xml, resp);
+ if (ret == NULL) {
+ wpa_printf(MSG_INFO, "Could not get SOAP body");
+ ctx->last_err = "Could not get SOAP body";
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "SOAP body localname: '%s'",
+ xml_node_get_localname(ctx->xml, ret));
+ n = xml_node_copy(ctx->xml, ret);
+ xml_node_free(ctx->xml, resp);
+
+ return n;
+}
+
+
+struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx)
+{
+ struct http_ctx *ctx;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ ctx->ctx = upper_ctx;
+ ctx->xml = xml_ctx;
+ ctx->ocsp = OPTIONAL_OCSP;
+
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ return ctx;
+}
+
+
+void http_ocsp_set(struct http_ctx *ctx, int val)
+{
+ if (val == 0)
+ ctx->ocsp = NO_OCSP;
+ else if (val == 1)
+ ctx->ocsp = OPTIONAL_OCSP;
+ if (val == 2)
+ ctx->ocsp = MANDATORY_OCSP;
+}
+
+
+void http_deinit_ctx(struct http_ctx *ctx)
+{
+ clear_curl(ctx);
+ os_free(ctx->curl_buf);
+ curl_global_cleanup();
+
+ os_free(ctx->svc_address);
+ os_free(ctx->svc_ca_fname);
+ os_free(ctx->svc_username);
+ os_free(ctx->svc_password);
+ os_free(ctx->svc_client_cert);
+ os_free(ctx->svc_client_key);
+
+ os_free(ctx);
+}
+
+
+int http_download_file(struct http_ctx *ctx, const char *url,
+ const char *fname, const char *ca_fname)
+{
+ CURL *curl;
+ FILE *f;
+ CURLcode res;
+ long http = 0;
+
+ ctx->last_err = NULL;
+
+ wpa_printf(MSG_DEBUG, "curl: Download file from %s to %s (ca=%s)",
+ url, fname, ca_fname);
+ curl = curl_easy_init();
+ if (curl == NULL)
+ return -1;
+
+ f = fopen(fname, "wb");
+ if (f == NULL) {
+ curl_easy_cleanup(curl);
+ return -1;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ if (ca_fname) {
+ curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+ curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
+ } else {
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+ }
+ curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug);
+ curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_cb_header);
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, ctx);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK) {
+ if (!ctx->last_err)
+ ctx->last_err = curl_easy_strerror(res);
+ wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+ ctx->last_err);
+ curl_easy_cleanup(curl);
+ fclose(f);
+ return -1;
+ }
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
+ wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http);
+ if (http != 200) {
+ ctx->last_err = "HTTP download failed";
+ wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
+ curl_easy_cleanup(curl);
+ fclose(f);
+ return -1;
+ }
+
+ curl_easy_cleanup(curl);
+ fclose(f);
+
+ return 0;
+}
+
+
+char * http_post(struct http_ctx *ctx, const char *url, const char *data,
+ const char *content_type, const char *ext_hdr,
+ const char *ca_fname,
+ const char *username, const char *password,
+ const char *client_cert, const char *client_key,
+ size_t *resp_len)
+{
+ long http = 0;
+ CURLcode res;
+ char *ret;
+ CURL *curl;
+ struct curl_slist *curl_hdr = NULL;
+
+ ctx->last_err = NULL;
+ wpa_printf(MSG_DEBUG, "curl: HTTP POST to %s", url);
+ curl = setup_curl_post(ctx, url, ca_fname, username, password,
+ client_cert, client_key);
+ if (curl == NULL)
+ return NULL;
+
+ if (content_type) {
+ char ct[200];
+ snprintf(ct, sizeof(ct), "Content-Type: %s", content_type);
+ curl_hdr = curl_slist_append(curl_hdr, ct);
+ }
+ if (ext_hdr)
+ curl_hdr = curl_slist_append(curl_hdr, ext_hdr);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_hdr);
+
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
+ free_curl_buf(ctx);
+
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK) {
+ if (!ctx->last_err)
+ ctx->last_err = curl_easy_strerror(res);
+ wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+ ctx->last_err);
+ free_curl_buf(ctx);
+ return NULL;
+ }
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
+ wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http);
+ if (http != 200) {
+ ctx->last_err = "HTTP POST failed";
+ wpa_printf(MSG_INFO, "HTTP POST failed - code %ld", http);
+ free_curl_buf(ctx);
+ return NULL;
+ }
+
+ if (ctx->curl_buf == NULL)
+ return NULL;
+
+ ret = ctx->curl_buf;
+ if (resp_len)
+ *resp_len = ctx->curl_buf_len;
+ ctx->curl_buf = NULL;
+ ctx->curl_buf_len = 0;
+
+ wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ret);
+
+ return ret;
+}
+
+
+void http_set_cert_cb(struct http_ctx *ctx,
+ int (*cb)(void *ctx, struct http_cert *cert),
+ void *cb_ctx)
+{
+ ctx->cert_cb = cb;
+ ctx->cert_cb_ctx = cb_ctx;
+}
+
+
+const char * http_get_err(struct http_ctx *ctx)
+{
+ return ctx->last_err;
+}
diff --git a/src/utils/os.h b/src/utils/os.h
index d63ac29..f019e26 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -240,6 +240,13 @@
char * os_readfile(const char *name, size_t *len);
/**
+ * os_file_exists - Check whether the specified file exists
+ * @fname: Path and name of the file
+ * Returns: 1 if the file exists or 0 if not
+ */
+int os_file_exists(const char *fname);
+
+/**
* os_zalloc - Allocate and zero memory
* @size: Number of bytes to allocate
* Returns: Pointer to allocated and zeroed memory or %NULL on failure
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index fa67fdf..008ec6b 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -407,6 +407,16 @@
}
+int os_file_exists(const char *fname)
+{
+ FILE *f = fopen(fname, "rb");
+ if (f == NULL)
+ return 0;
+ fclose(f);
+ return 1;
+}
+
+
#ifndef WPA_TRACE
void * os_zalloc(size_t size)
{
diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c
new file mode 100644
index 0000000..4916d29
--- /dev/null
+++ b/src/utils/xml-utils.c
@@ -0,0 +1,471 @@
+/*
+ * Generic XML helper functions
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "xml-utils.h"
+
+
+static xml_node_t * get_node_uri_iter(struct xml_node_ctx *ctx,
+ xml_node_t *root, char *uri)
+{
+ char *end;
+ xml_node_t *node;
+ const char *name;
+
+ end = strchr(uri, '/');
+ if (end)
+ *end++ = '\0';
+
+ node = root;
+ xml_node_for_each_sibling(ctx, node) {
+ xml_node_for_each_check(ctx, node);
+ name = xml_node_get_localname(ctx, node);
+ if (strcasecmp(name, uri) == 0)
+ break;
+ }
+
+ if (node == NULL)
+ return NULL;
+
+ if (end) {
+ return get_node_uri_iter(ctx, xml_node_first_child(ctx, node),
+ end);
+ }
+
+ return node;
+}
+
+
+xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *uri)
+{
+ char *search;
+ xml_node_t *node;
+
+ search = os_strdup(uri);
+ if (search == NULL)
+ return NULL;
+
+ node = get_node_uri_iter(ctx, root, search);
+
+ os_free(search);
+ return node;
+}
+
+
+static xml_node_t * get_node_iter(struct xml_node_ctx *ctx,
+ xml_node_t *root, const char *path)
+{
+ char *end;
+ xml_node_t *node;
+ const char *name;
+
+ end = os_strchr(path, '/');
+ if (end)
+ *end++ = '\0';
+
+ xml_node_for_each_child(ctx, node, root) {
+ xml_node_for_each_check(ctx, node);
+ name = xml_node_get_localname(ctx, node);
+ if (os_strcasecmp(name, path) == 0)
+ break;
+ }
+
+ if (node == NULL)
+ return NULL;
+ if (end)
+ return get_node_iter(ctx, node, end);
+ return node;
+}
+
+
+xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *path)
+{
+ char *search;
+ xml_node_t *node;
+
+ search = os_strdup(path);
+ if (search == NULL)
+ return NULL;
+
+ node = get_node_iter(ctx, root, search);
+
+ os_free(search);
+ return node;
+}
+
+
+xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *path)
+{
+ xml_node_t *node;
+ xml_node_t *match;
+
+ xml_node_for_each_child(ctx, node, root) {
+ xml_node_for_each_check(ctx, node);
+ match = get_node(ctx, node, path);
+ if (match)
+ return match;
+ }
+
+ return NULL;
+}
+
+
+xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name)
+{
+ xml_node_t *node;
+ char *buf, *buf2, *start;
+ size_t len;
+
+ buf = os_readfile(name, &len);
+ if (buf == NULL)
+ return NULL;
+ buf2 = os_realloc(buf, len + 1);
+ if (buf2 == NULL) {
+ os_free(buf);
+ return NULL;
+ }
+ buf = buf2;
+ buf[len] = '\0';
+
+ start = os_strstr(buf, "<!DOCTYPE ");
+ if (start) {
+ char *pos = start + 1;
+ int count = 1;
+ while (*pos) {
+ if (*pos == '<')
+ count++;
+ else if (*pos == '>') {
+ count--;
+ if (count == 0) {
+ pos++;
+ break;
+ }
+ }
+ pos++;
+ }
+ if (count == 0) {
+ /* Remove DOCTYPE to allow the file to be parsed */
+ os_memset(start, ' ', pos - start);
+ }
+ }
+
+ node = xml_node_from_buf(ctx, buf);
+ os_free(buf);
+
+ return node;
+}
+
+
+int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node)
+{
+ FILE *f;
+ char *str;
+
+ str = xml_node_to_str(ctx, node);
+ if (str == NULL)
+ return -1;
+
+ f = fopen(fname, "w");
+ if (!f) {
+ os_free(str);
+ return -1;
+ }
+
+ fprintf(f, "%s\n", str);
+ os_free(str);
+ fclose(f);
+
+ return 0;
+}
+
+
+static char * get_val(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ char *val, *pos;
+
+ val = xml_node_get_text(ctx, node);
+ if (val == NULL)
+ return NULL;
+ pos = val;
+ while (*pos) {
+ if (*pos != ' ' && *pos != '\t' && *pos != '\r' && *pos != '\n')
+ return val;
+ pos++;
+ }
+
+ return NULL;
+}
+
+
+static char * add_path(const char *prev, const char *leaf)
+{
+ size_t len;
+ char *new_uri;
+
+ if (prev == NULL)
+ return NULL;
+
+ len = os_strlen(prev) + 1 + os_strlen(leaf) + 1;
+ new_uri = os_malloc(len);
+ if (new_uri)
+ os_snprintf(new_uri, len, "%s/%s", prev, leaf);
+
+ return new_uri;
+}
+
+
+static void node_to_tnds(struct xml_node_ctx *ctx, xml_node_t *out,
+ xml_node_t *in, const char *uri)
+{
+ xml_node_t *node;
+ xml_node_t *tnds;
+ const char *name;
+ char *val;
+ char *new_uri;
+
+ xml_node_for_each_child(ctx, node, in) {
+ xml_node_for_each_check(ctx, node);
+ name = xml_node_get_localname(ctx, node);
+
+ tnds = xml_node_create(ctx, out, NULL, "Node");
+ if (tnds == NULL)
+ return;
+ xml_node_create_text(ctx, tnds, NULL, "NodeName", name);
+
+ if (uri)
+ xml_node_create_text(ctx, tnds, NULL, "Path", uri);
+
+ val = get_val(ctx, node);
+ if (val) {
+ xml_node_create_text(ctx, tnds, NULL, "Value", val);
+ xml_node_get_text_free(ctx, val);
+ }
+
+ new_uri = add_path(uri, name);
+ node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri);
+ os_free(new_uri);
+ }
+}
+
+
+static int add_ddfname(struct xml_node_ctx *ctx, xml_node_t *parent,
+ const char *urn)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx, parent, NULL, "RTProperties");
+ if (node == NULL)
+ return -1;
+ node = xml_node_create(ctx, node, NULL, "Type");
+ if (node == NULL)
+ return -1;
+ xml_node_create_text(ctx, node, NULL, "DDFName", urn);
+ return 0;
+}
+
+
+xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
+ int use_path, const char *urn, const char *ns_uri)
+{
+ xml_node_t *root;
+ xml_node_t *node;
+ const char *name;
+
+ root = xml_node_create_root(ctx, ns_uri, NULL, NULL, "MgmtTree");
+ if (root == NULL)
+ return NULL;
+
+ xml_node_create_text(ctx, root, NULL, "VerDTD", "1.2");
+
+ name = xml_node_get_localname(ctx, mo);
+
+ node = xml_node_create(ctx, root, NULL, "Node");
+ if (node == NULL)
+ goto fail;
+ xml_node_create_text(ctx, node, NULL, "NodeName", name);
+ if (urn)
+ add_ddfname(ctx, node, urn);
+
+ node_to_tnds(ctx, use_path ? root : node, mo, use_path ? name : NULL);
+
+ return root;
+
+fail:
+ xml_node_free(ctx, root);
+ return NULL;
+}
+
+
+static xml_node_t * get_first_child_node(struct xml_node_ctx *ctx,
+ xml_node_t *node,
+ const char *name)
+{
+ const char *lname;
+ xml_node_t *child;
+
+ xml_node_for_each_child(ctx, child, node) {
+ xml_node_for_each_check(ctx, child);
+ lname = xml_node_get_localname(ctx, child);
+ if (os_strcasecmp(lname, name) == 0)
+ return child;
+ }
+
+ return NULL;
+}
+
+
+static char * get_node_text(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *node_name)
+{
+ node = get_first_child_node(ctx, node, node_name);
+ if (node == NULL)
+ return NULL;
+ return xml_node_get_text(ctx, node);
+}
+
+
+static xml_node_t * add_mo_node(struct xml_node_ctx *ctx, xml_node_t *root,
+ xml_node_t *node, const char *uri)
+{
+ char *nodename, *value, *path;
+ xml_node_t *parent;
+
+ nodename = get_node_text(ctx, node, "NodeName");
+ if (nodename == NULL)
+ return NULL;
+ value = get_node_text(ctx, node, "Value");
+
+ if (root == NULL) {
+ root = xml_node_create_root(ctx, NULL, NULL, NULL,
+ nodename);
+ if (root && value)
+ xml_node_set_text(ctx, root, value);
+ } else {
+ if (uri == NULL) {
+ xml_node_get_text_free(ctx, nodename);
+ xml_node_get_text_free(ctx, value);
+ return NULL;
+ }
+ path = get_node_text(ctx, node, "Path");
+ if (path)
+ uri = path;
+ parent = get_node_uri(ctx, root, uri);
+ xml_node_get_text_free(ctx, path);
+ if (parent == NULL) {
+ printf("Could not find URI '%s'\n", uri);
+ xml_node_get_text_free(ctx, nodename);
+ xml_node_get_text_free(ctx, value);
+ return NULL;
+ }
+ if (value)
+ xml_node_create_text(ctx, parent, NULL, nodename,
+ value);
+ else
+ xml_node_create(ctx, parent, NULL, nodename);
+ }
+
+ xml_node_get_text_free(ctx, nodename);
+ xml_node_get_text_free(ctx, value);
+
+ return root;
+}
+
+
+static xml_node_t * tnds_to_mo_iter(struct xml_node_ctx *ctx, xml_node_t *root,
+ xml_node_t *node, const char *uri)
+{
+ xml_node_t *child;
+ const char *name;
+ char *nodename;
+
+ xml_node_for_each_sibling(ctx, node) {
+ xml_node_for_each_check(ctx, node);
+
+ nodename = get_node_text(ctx, node, "NodeName");
+ if (nodename == NULL)
+ return NULL;
+
+ name = xml_node_get_localname(ctx, node);
+ if (strcmp(name, "Node") == 0) {
+ if (root && !uri) {
+ printf("Invalid TNDS tree structure - "
+ "multiple top level nodes\n");
+ xml_node_get_text_free(ctx, nodename);
+ return NULL;
+ }
+ root = add_mo_node(ctx, root, node, uri);
+ }
+
+ child = get_first_child_node(ctx, node, "Node");
+ if (child) {
+ if (uri == NULL)
+ tnds_to_mo_iter(ctx, root, child, nodename);
+ else {
+ char *new_uri;
+ new_uri = add_path(uri, nodename);
+ tnds_to_mo_iter(ctx, root, child, new_uri);
+ os_free(new_uri);
+ }
+ }
+ xml_node_get_text_free(ctx, nodename);
+ }
+
+ return root;
+}
+
+
+xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds)
+{
+ const char *name;
+ xml_node_t *node;
+
+ name = xml_node_get_localname(ctx, tnds);
+ if (name == NULL || os_strcmp(name, "MgmtTree") != 0)
+ return NULL;
+
+ node = get_first_child_node(ctx, tnds, "Node");
+ if (!node)
+ return NULL;
+ return tnds_to_mo_iter(ctx, NULL, node, NULL);
+}
+
+
+xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ xml_node_t *envelope, *body;
+ xml_namespace_t *ns;
+
+ envelope = xml_node_create_root(
+ ctx, "http://www.w3.org/2003/05/soap-envelope", "soap12", &ns,
+ "Envelope");
+ if (envelope == NULL)
+ return NULL;
+ body = xml_node_create(ctx, envelope, ns, "Body");
+ xml_node_add_child(ctx, body, node);
+ return envelope;
+}
+
+
+xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap)
+{
+ xml_node_t *body, *child;
+
+ body = get_node_uri(ctx, soap, "Envelope/Body");
+ if (body == NULL)
+ return NULL;
+ xml_node_for_each_child(ctx, child, body) {
+ xml_node_for_each_check(ctx, child);
+ return child;
+ }
+ return NULL;
+}
diff --git a/src/utils/xml-utils.h b/src/utils/xml-utils.h
new file mode 100644
index 0000000..0d8e0cb
--- /dev/null
+++ b/src/utils/xml-utils.h
@@ -0,0 +1,100 @@
+/*
+ * Generic XML helper functions
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef XML_UTILS_H
+#define XML_UTILS_H
+
+struct xml_node_ctx;
+typedef struct xml_node xml_node_t;
+typedef struct xml_namespace_foo xml_namespace_t;
+
+/* XML library wrappers */
+
+int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *xml_schema_fname, char **ret_err);
+int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *dtd_fname, char **ret_err);
+void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf);
+const char * xml_node_get_localname(struct xml_node_ctx *ctx,
+ xml_node_t *node);
+char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
+ xml_node_t *child);
+xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
+ const char *ns_prefix,
+ xml_namespace_t **ret_ns, const char *name);
+xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent,
+ xml_namespace_t *ns, const char *name);
+xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx,
+ xml_node_t *parent, xml_namespace_t *ns,
+ const char *name, const char *value);
+xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx,
+ xml_node_t *parent, const char *ns_uri,
+ const char *name, const char *value);
+void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *value);
+int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
+ xml_namespace_t *ns, const char *name, const char *value);
+char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+ char *name);
+char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *ns_uri, char *name);
+void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val);
+xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
+ xml_node_t *parent);
+xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
+ xml_node_t *node);
+int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node);
+char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val);
+char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
+ int *ret_len);
+xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node);
+
+#define xml_node_for_each_child(ctx, child, parent) \
+for (child = xml_node_first_child(ctx, parent); \
+ child; \
+ child = xml_node_next_sibling(ctx, child))
+
+#define xml_node_for_each_sibling(ctx, node) \
+for (; \
+ node; \
+ node = xml_node_next_sibling(ctx, node))
+
+#define xml_node_for_each_check(ctx, child) \
+if (!xml_node_is_element(ctx, child)) \
+ continue
+
+typedef void (*debug_print_func)(void *ctx, int print, const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4)));
+
+
+struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
+ const void *env);
+void xml_node_deinit_ctx(struct xml_node_ctx *ctx);
+
+
+xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *uri);
+xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *path);
+xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *path);
+xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name);
+int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node);
+xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
+ int use_path, const char *urn, const char *ns_uri);
+xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds);
+
+xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap);
+
+#endif /* XML_UTILS_H */
diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c
new file mode 100644
index 0000000..c928394
--- /dev/null
+++ b/src/utils/xml_libxml2.c
@@ -0,0 +1,457 @@
+/*
+ * XML wrapper for libxml2
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#define LIBXML_VALID_ENABLED
+#include <libxml/tree.h>
+#include <libxml/xmlschemastypes.h>
+
+#include "common.h"
+#include "base64.h"
+#include "xml-utils.h"
+
+
+struct xml_node_ctx {
+ void *ctx;
+};
+
+
+struct str_buf {
+ char *buf;
+ size_t len;
+};
+
+#define MAX_STR 1000
+
+static void add_str(void *ctx_ptr, const char *fmt, ...)
+{
+ struct str_buf *str = ctx_ptr;
+ va_list ap;
+ char *n;
+ int len;
+
+ n = os_realloc(str->buf, str->len + MAX_STR + 2);
+ if (n == NULL)
+ return;
+ str->buf = n;
+
+ va_start(ap, fmt);
+ len = vsnprintf(str->buf + str->len, MAX_STR, fmt, ap);
+ va_end(ap);
+ if (len >= MAX_STR)
+ len = MAX_STR - 1;
+ str->len += len;
+ str->buf[str->len] = '\0';
+}
+
+
+int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *xml_schema_fname, char **ret_err)
+{
+ xmlDocPtr doc;
+ xmlNodePtr n;
+ xmlSchemaParserCtxtPtr pctx;
+ xmlSchemaValidCtxtPtr vctx;
+ xmlSchemaPtr schema;
+ int ret;
+ struct str_buf errors;
+
+ if (ret_err)
+ *ret_err = NULL;
+
+ doc = xmlNewDoc((xmlChar *) "1.0");
+ if (doc == NULL)
+ return -1;
+ n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
+ if (n == NULL) {
+ xmlFreeDoc(doc);
+ return -1;
+ }
+ xmlDocSetRootElement(doc, n);
+
+ os_memset(&errors, 0, sizeof(errors));
+
+ pctx = xmlSchemaNewParserCtxt(xml_schema_fname);
+ xmlSchemaSetParserErrors(pctx, (xmlSchemaValidityErrorFunc) add_str,
+ (xmlSchemaValidityWarningFunc) add_str,
+ &errors);
+ schema = xmlSchemaParse(pctx);
+ xmlSchemaFreeParserCtxt(pctx);
+
+ vctx = xmlSchemaNewValidCtxt(schema);
+ xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc) add_str,
+ (xmlSchemaValidityWarningFunc) add_str,
+ &errors);
+
+ ret = xmlSchemaValidateDoc(vctx, doc);
+ xmlSchemaFreeValidCtxt(vctx);
+ xmlFreeDoc(doc);
+ xmlSchemaFree(schema);
+
+ if (ret == 0) {
+ os_free(errors.buf);
+ return 0;
+ } else if (ret > 0) {
+ if (ret_err)
+ *ret_err = errors.buf;
+ else
+ os_free(errors.buf);
+ return -1;
+ } else {
+ if (ret_err)
+ *ret_err = errors.buf;
+ else
+ os_free(errors.buf);
+ return -1;
+ }
+}
+
+
+int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *dtd_fname, char **ret_err)
+{
+ xmlDocPtr doc;
+ xmlNodePtr n;
+ xmlValidCtxt vctx;
+ xmlDtdPtr dtd;
+ int ret;
+ struct str_buf errors;
+
+ if (ret_err)
+ *ret_err = NULL;
+
+ doc = xmlNewDoc((xmlChar *) "1.0");
+ if (doc == NULL)
+ return -1;
+ n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
+ if (n == NULL) {
+ xmlFreeDoc(doc);
+ return -1;
+ }
+ xmlDocSetRootElement(doc, n);
+
+ os_memset(&errors, 0, sizeof(errors));
+
+ dtd = xmlParseDTD(NULL, (const xmlChar *) dtd_fname);
+ if (dtd == NULL) {
+ xmlFreeDoc(doc);
+ return -1;
+ }
+
+ os_memset(&vctx, 0, sizeof(vctx));
+ vctx.userData = &errors;
+ vctx.error = add_str;
+ vctx.warning = add_str;
+ ret = xmlValidateDtd(&vctx, doc, dtd);
+ xmlFreeDoc(doc);
+ xmlFreeDtd(dtd);
+
+ if (ret == 1) {
+ os_free(errors.buf);
+ return 0;
+ } else {
+ if (ret_err)
+ *ret_err = errors.buf;
+ else
+ os_free(errors.buf);
+ return -1;
+ }
+}
+
+
+void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ xmlFreeNode((xmlNodePtr) node);
+}
+
+
+xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ return (xml_node_t *) ((xmlNodePtr) node)->parent;
+}
+
+
+xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf)
+{
+ xmlDocPtr doc;
+ xmlNodePtr node;
+
+ doc = xmlParseMemory(buf, strlen(buf));
+ if (doc == NULL)
+ return NULL;
+ node = xmlDocGetRootElement(doc);
+ node = xmlCopyNode(node, 1);
+ xmlFreeDoc(doc);
+
+ return (xml_node_t *) node;
+}
+
+
+const char * xml_node_get_localname(struct xml_node_ctx *ctx,
+ xml_node_t *node)
+{
+ return (const char *) ((xmlNodePtr) node)->name;
+}
+
+
+char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ xmlChar *buf;
+ int bufsiz;
+ char *ret, *pos;
+ xmlNodePtr n = (xmlNodePtr) node;
+ xmlDocPtr doc;
+
+ doc = xmlNewDoc((xmlChar *) "1.0");
+ n = xmlDocCopyNode(n, doc, 1);
+ xmlDocSetRootElement(doc, n);
+ xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0);
+ xmlFreeDoc(doc);
+ pos = (char *) buf;
+ if (strncmp(pos, "<?xml", 5) == 0) {
+ pos = strchr(pos, '>');
+ if (pos)
+ pos++;
+ while (pos && (*pos == '\r' || *pos == '\n'))
+ pos++;
+ }
+ if (pos)
+ ret = os_strdup(pos);
+ else
+ ret = NULL;
+ xmlFree(buf);
+
+ if (ret) {
+ pos = ret;
+ if (pos[0]) {
+ while (pos[1])
+ pos++;
+ }
+ while (pos >= ret && *pos == '\n')
+ *pos-- = '\0';
+ }
+
+ return ret;
+}
+
+
+void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ xmlUnlinkNode((xmlNodePtr) node);
+}
+
+
+void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
+ xml_node_t *child)
+{
+ xmlAddChild((xmlNodePtr) parent, (xmlNodePtr) child);
+}
+
+
+xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
+ const char *ns_prefix,
+ xml_namespace_t **ret_ns, const char *name)
+{
+ xmlNodePtr node;
+ xmlNsPtr ns = NULL;
+
+ node = xmlNewNode(NULL, (const xmlChar *) name);
+ if (node == NULL)
+ return NULL;
+ if (ns_uri) {
+ ns = xmlNewNs(node, (const xmlChar *) ns_uri,
+ (const xmlChar *) ns_prefix);
+ xmlSetNs(node, ns);
+ }
+
+ if (ret_ns)
+ *ret_ns = (xml_namespace_t *) ns;
+
+ return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent,
+ xml_namespace_t *ns, const char *name)
+{
+ xmlNodePtr node;
+ node = xmlNewChild((xmlNodePtr) parent, (xmlNsPtr) ns,
+ (const xmlChar *) name, NULL);
+ return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx,
+ xml_node_t *parent, xml_namespace_t *ns,
+ const char *name, const char *value)
+{
+ xmlNodePtr node;
+ node = xmlNewTextChild((xmlNodePtr) parent, (xmlNsPtr) ns,
+ (const xmlChar *) name, (const xmlChar *) value);
+ return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx,
+ xml_node_t *parent, const char *ns_uri,
+ const char *name, const char *value)
+{
+ xmlNodePtr node;
+ xmlNsPtr ns;
+
+ node = xmlNewTextChild((xmlNodePtr) parent, NULL,
+ (const xmlChar *) name, (const xmlChar *) value);
+ ns = xmlNewNs(node, (const xmlChar *) ns_uri, NULL);
+ xmlSetNs(node, ns);
+ return (xml_node_t *) node;
+}
+
+
+void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *value)
+{
+ /* TODO: escape XML special chars in value */
+ xmlNodeSetContent((xmlNodePtr) node, (xmlChar *) value);
+}
+
+
+int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
+ xml_namespace_t *ns, const char *name, const char *value)
+{
+ xmlAttrPtr attr;
+
+ if (ns) {
+ attr = xmlNewNsProp((xmlNodePtr) node, (xmlNsPtr) ns,
+ (const xmlChar *) name,
+ (const xmlChar *) value);
+ } else {
+ attr = xmlNewProp((xmlNodePtr) node, (const xmlChar *) name,
+ (const xmlChar *) value);
+ }
+
+ return attr ? 0 : -1;
+}
+
+
+char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+ char *name)
+{
+ return (char *) xmlGetNoNsProp((xmlNodePtr) node,
+ (const xmlChar *) name);
+}
+
+
+char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *ns_uri, char *name)
+{
+ return (char *) xmlGetNsProp((xmlNodePtr) node, (const xmlChar *) name,
+ (const xmlChar *) ns_uri);
+}
+
+
+void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val)
+{
+ if (val)
+ xmlFree((xmlChar *) val);
+}
+
+
+xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
+ xml_node_t *parent)
+{
+ return (xml_node_t *) ((xmlNodePtr) parent)->children;
+}
+
+
+xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
+ xml_node_t *node)
+{
+ return (xml_node_t *) ((xmlNodePtr) node)->next;
+}
+
+
+int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ return ((xmlNodePtr) node)->type == XML_ELEMENT_NODE;
+}
+
+
+char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ if (xmlChildElementCount((xmlNodePtr) node) > 0)
+ return NULL;
+ return (char *) xmlNodeGetContent((xmlNodePtr) node);
+}
+
+
+void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val)
+{
+ if (val)
+ xmlFree((xmlChar *) val);
+}
+
+
+char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
+ int *ret_len)
+{
+ char *txt;
+ unsigned char *ret;
+ size_t len;
+
+ txt = xml_node_get_text(ctx, node);
+ if (txt == NULL)
+ return NULL;
+
+ ret = base64_decode((unsigned char *) txt, strlen(txt), &len);
+ if (ret_len)
+ *ret_len = len;
+ xml_node_get_text_free(ctx, txt);
+ if (ret == NULL)
+ return NULL;
+ txt = os_malloc(len + 1);
+ if (txt == NULL) {
+ os_free(ret);
+ return NULL;
+ }
+ os_memcpy(txt, ret, len);
+ txt[len] = '\0';
+ return txt;
+}
+
+
+xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ if (node == NULL)
+ return NULL;
+ return (xml_node_t *) xmlCopyNode((xmlNodePtr) node, 1);
+}
+
+
+struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
+ const void *env)
+{
+ struct xml_node_ctx *xctx;
+
+ xctx = os_zalloc(sizeof(*xctx));
+ if (xctx == NULL)
+ return NULL;
+ xctx->ctx = upper_ctx;
+
+ LIBXML_TEST_VERSION
+
+ return xctx;
+}
+
+
+void xml_node_deinit_ctx(struct xml_node_ctx *ctx)
+{
+ xmlSchemaCleanupTypes();
+ xmlCleanupParser();
+ xmlMemoryDump();
+ os_free(ctx);
+}