diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 3032919c9..7b2c41c23 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1,6 +1,6 @@ /* * hostapd / Configuration file parser - * Copyright (c) 2003-2012, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -2937,6 +2937,12 @@ static int hostapd_config_fill(struct hostapd_config *conf, 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_rates(&bss->sae_groups, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "sae_groups value '%s'", line, pos); + return 1; + } } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration " "item '%s'", line, buf); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index a25fe378d..eca399604 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1042,6 +1042,15 @@ own_ip_addr=127.0.0.1 # same time before the anti-clogging mechanism is taken into use. #sae_anti_clogging_threshold=5 +# Enabled SAE finite cyclic groups +# SAE implementation are required to support group 19 (ECC group defined over a +# 256-bit prime order field). All groups that are supported by the +# implementation are enabled by default. This configuration parameter can be +# used to specify a limited set of allowed groups. The group values are listed +# in the IANA registry: +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 +#sae_groups=19 20 21 25 26 + ##### IEEE 802.11r configuration ############################################## # Mobility Domain identifier (dot11FTMobilityDomainID, MDID) diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index c9bf02b23..922f56469 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -521,6 +521,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) #endif /* CONFIG_HS20 */ wpabuf_free(conf->vendor_elements); + + os_free(conf->sae_groups); } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index b3efff76d..4742107d4 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -457,6 +457,7 @@ struct hostapd_bss_config { struct wpabuf *vendor_elements; unsigned int sae_anti_clogging_threshold; + int *sae_groups; }; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 4a04aa906..ea19cdde6 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -460,7 +460,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, - &token_len); + &token_len, hapd->conf->sae_groups); if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " diff --git a/src/common/sae.c b/src/common/sae.c index d9d6467e0..a182cf50f 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -470,7 +470,7 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, - const u8 **token, size_t *token_len) + const u8 **token, size_t *token_len, int *allowed_groups) { const u8 *pos = data, *end = data + len; u16 group; @@ -485,6 +485,19 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, if (pos + 2 > end) return WLAN_STATUS_UNSPECIFIED_FAILURE; group = WPA_GET_LE16(pos); + if (allowed_groups) { + int i; + for (i = 0; allowed_groups[i] >= 0; i++) { + if (allowed_groups[i] == group) + break; + } + if (allowed_groups[i] != group) { + wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " + "enabled in the current configuration", + group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + } if (sae->state == SAE_COMMITTED && group != sae->group) { wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; diff --git a/src/common/sae.h b/src/common/sae.h index a30fb625a..b2bb60538 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -43,7 +43,7 @@ int sae_process_commit(struct sae_data *sae); void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, const struct wpabuf *token); u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, - const u8 **token, size_t *token_len); + const u8 **token, size_t *token_len, int *allowed_groups); void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf); int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len); diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 0fab07aed..136deea49 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -916,9 +916,7 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ -static int * wpa_config_parse_freqs(const struct parse_data *data, - struct wpa_ssid *ssid, int line, - const char *value) +static int * wpa_config_parse_int_array(const char *value) { int *freqs; size_t used, len; @@ -965,7 +963,7 @@ static int wpa_config_parse_scan_freq(const struct parse_data *data, { int *freqs; - freqs = wpa_config_parse_freqs(data, ssid, line, value); + freqs = wpa_config_parse_int_array(value); if (freqs == NULL) return -1; os_free(ssid->scan_freq); @@ -981,7 +979,7 @@ static int wpa_config_parse_freq_list(const struct parse_data *data, { int *freqs; - freqs = wpa_config_parse_freqs(data, ssid, line, value); + freqs = wpa_config_parse_int_array(value); if (freqs == NULL) return -1; os_free(ssid->freq_list); @@ -1903,6 +1901,7 @@ void wpa_config_free(struct wpa_config *config) wpabuf_free(config->wps_nfc_dh_privkey); wpabuf_free(config->wps_nfc_dev_pw); os_free(config->ext_password_backend); + os_free(config->sae_groups); os_free(config); } @@ -2978,6 +2977,24 @@ static int wpa_config_process_hessid( } +static int wpa_config_process_sae_groups( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + int *groups = wpa_config_parse_int_array(pos); + if (groups == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid sae_groups '%s'", + line, pos); + return -1; + } + + os_free(config->sae_groups); + config->sae_groups = groups; + + return 0; +} + + #ifdef OFFSET #undef OFFSET #endif /* OFFSET */ @@ -3070,6 +3087,7 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(auto_interworking, 0, 1), 0 }, { INT(okc), 0 }, { INT(pmf), 0 }, + { FUNC(sae_groups), 0 }, }; #undef FUNC diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index bb70b9c55..0c3cb9a93 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -797,6 +797,16 @@ struct wpa_config { * this default behavior. */ enum mfp_options pmf; + + /** + * sae_groups - Preference list of enabled groups for SAE + * + * By default (if this parameter is not set), the mandatory group 19 + * (ECC group defined over a 256-bit prime order field) is preferred, + * but other groups are also enabled. If this parameter is set, the + * groups will be tried in the indicated order. + */ + int *sae_groups; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 8f32cc8d9..50c35333e 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -970,6 +970,16 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "okc=%d\n", config->okc); if (config->pmf) fprintf(f, "pmf=%d\n", config->pmf); + + if (config->sae_groups) { + int i; + fprintf(f, "sae_groups="); + for (i = 0; config->sae_groups[i] >= 0; i++) { + fprintf(f, "%s%d", i > 0 ? " " : "", + config->sae_groups[i]); + } + fprintf(f, "\n"); + } } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 112c80f78..6fbb9e119 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -42,6 +42,45 @@ static void sme_stop_sa_query(struct wpa_supplicant *wpa_s); #ifdef CONFIG_SAE +static int index_within_array(const int *array, int idx) +{ + int i; + for (i = 0; i < idx; i++) { + if (array[i] == -1) + return 0; + } + return 1; +} + + +static int sme_set_sae_group(struct wpa_supplicant *wpa_s) +{ + int *groups = wpa_s->conf->sae_groups; + int default_groups[] = { 19, 20, 21, 25, 26 }; + + if (!groups) + groups = default_groups; + + /* Configuration may have changed, so validate current index */ + if (!index_within_array(groups, wpa_s->sme.sae_group_index)) + return -1; + + for (;;) { + int group = groups[wpa_s->sme.sae_group_index]; + if (group < 0) + break; + if (sae_set_group(&wpa_s->sme.sae, group) == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d", + wpa_s->sme.sae.group); + return 0; + } + wpa_s->sme.sae_group_index++; + } + + return -1; +} + + static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const u8 *bssid) @@ -54,8 +93,10 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, return NULL; } - if (sae_set_group(&wpa_s->sme.sae, 19) < 0) + if (sme_set_sae_group(wpa_s) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to select group"); return NULL; + } if (sae_prepare_commit(wpa_s->own_addr, bssid, (u8 *) ssid->passphrase, @@ -424,6 +465,20 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return 0; } + if (auth_transaction == 1 && + status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && + wpa_s->sme.sae.state == SAE_COMMITTED && + wpa_s->current_bss && wpa_s->current_ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported"); + wpa_s->sme.sae_group_index++; + if (sme_set_sae_group(wpa_s) < 0) + return -1; /* no other groups enabled */ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group"); + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 1); + return 0; + } + if (status_code != WLAN_STATUS_SUCCESS) return -1; @@ -434,7 +489,8 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return -1; if (wpa_s->sme.sae.state != SAE_COMMITTED) return -1; - if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL) != + if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL, + wpa_s->conf->sae_groups) != WLAN_STATUS_SUCCESS) return -1; diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 381ab47cc..18460b89e 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -281,6 +281,14 @@ fast_reauth=1 # ieee80211w parameter. #pmf=0 +# Enabled SAE finite cyclic groups in preference order +# By default (if this parameter is not set), the mandatory group 19 (ECC group +# defined over a 256-bit prime order field) is preferred, but other groups are +# also enabled. If this parameter is set, the groups will be tried in the +# indicated order. The group values are listed in the IANA registry: +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 +#sae_groups=21 20 19 26 25 + # Interworking (IEEE 802.11u) # Enable Interworking diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 081928ab4..8408a4a7e 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -517,6 +517,7 @@ struct wpa_supplicant { #ifdef CONFIG_SAE struct sae_data sae; struct wpabuf *sae_token; + int sae_group_index; #endif /* CONFIG_SAE */ } sme; #endif /* CONFIG_SME */