Browse Source

SAE: Allow enabled groups to be configured

hostapd.conf sae_groups parameter can now be used to limit the set of
groups that the AP allows for SAE. Similarly, sae_groups parameter is
wpa_supplicant.conf can be used to set the preferred order of groups. By
default, all implemented groups are enabled.

Signed-hostap: Jouni Malinen <j@w1.fi>
Jouni Malinen 12 years ago
parent
commit
625f202a74

+ 7 - 1
hostapd/config_file.c

@@ -1,6 +1,6 @@
 /*
 /*
  * hostapd / Configuration file parser
  * hostapd / Configuration file parser
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
  *
  *
  * This software may be distributed under the terms of the BSD license.
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
  * See README for more details.
@@ -2937,6 +2937,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 			bss->vendor_elements = elems;
 			bss->vendor_elements = elems;
 		} else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
 		} else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
 			bss->sae_anti_clogging_threshold = atoi(pos);
 			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 {
 		} else {
 			wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
 			wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
 				   "item '%s'", line, buf);
 				   "item '%s'", line, buf);

+ 9 - 0
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.
 # same time before the anti-clogging mechanism is taken into use.
 #sae_anti_clogging_threshold=5
 #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 ##############################################
 ##### IEEE 802.11r configuration ##############################################
 
 
 # Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
 # Mobility Domain identifier (dot11FTMobilityDomainID, MDID)

+ 2 - 0
src/ap/ap_config.c

@@ -521,6 +521,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 #endif /* CONFIG_HS20 */
 #endif /* CONFIG_HS20 */
 
 
 	wpabuf_free(conf->vendor_elements);
 	wpabuf_free(conf->vendor_elements);
+
+	os_free(conf->sae_groups);
 }
 }
 
 
 
 

+ 1 - 0
src/ap/ap_config.h

@@ -457,6 +457,7 @@ struct hostapd_bss_config {
 	struct wpabuf *vendor_elements;
 	struct wpabuf *vendor_elements;
 
 
 	unsigned int sae_anti_clogging_threshold;
 	unsigned int sae_anti_clogging_threshold;
+	int *sae_groups;
 };
 };
 
 
 
 

+ 1 - 1
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,
 		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
 					((const u8 *) mgmt) + len -
 					((const u8 *) mgmt) + len -
 					mgmt->u.auth.variable, &token,
 					mgmt->u.auth.variable, &token,
-					&token_len);
+					&token_len, hapd->conf->sae_groups);
 		if (token && check_sae_token(hapd, sta->addr, token, token_len)
 		if (token && check_sae_token(hapd, sta->addr, token, token_len)
 		    < 0) {
 		    < 0) {
 			wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
 			wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "

+ 14 - 1
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,
 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;
 	const u8 *pos = data, *end = data + len;
 	u16 group;
 	u16 group;
@@ -485,6 +485,19 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
 	if (pos + 2 > end)
 	if (pos + 2 > end)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	group = WPA_GET_LE16(pos);
 	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) {
 	if (sae->state == SAE_COMMITTED && group != sae->group) {
 		wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed");
 		wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed");
 		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
 		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;

+ 1 - 1
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,
 void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
 		      const struct wpabuf *token);
 		      const struct wpabuf *token);
 u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
 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);
 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);
 int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
 
 

+ 23 - 5
wpa_supplicant/config.c

@@ -916,9 +916,7 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data,
 #endif /* NO_CONFIG_WRITE */
 #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;
 	int *freqs;
 	size_t used, len;
 	size_t used, len;
@@ -965,7 +963,7 @@ static int wpa_config_parse_scan_freq(const struct parse_data *data,
 {
 {
 	int *freqs;
 	int *freqs;
 
 
-	freqs = wpa_config_parse_freqs(data, ssid, line, value);
+	freqs = wpa_config_parse_int_array(value);
 	if (freqs == NULL)
 	if (freqs == NULL)
 		return -1;
 		return -1;
 	os_free(ssid->scan_freq);
 	os_free(ssid->scan_freq);
@@ -981,7 +979,7 @@ static int wpa_config_parse_freq_list(const struct parse_data *data,
 {
 {
 	int *freqs;
 	int *freqs;
 
 
-	freqs = wpa_config_parse_freqs(data, ssid, line, value);
+	freqs = wpa_config_parse_int_array(value);
 	if (freqs == NULL)
 	if (freqs == NULL)
 		return -1;
 		return -1;
 	os_free(ssid->freq_list);
 	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_dh_privkey);
 	wpabuf_free(config->wps_nfc_dev_pw);
 	wpabuf_free(config->wps_nfc_dev_pw);
 	os_free(config->ext_password_backend);
 	os_free(config->ext_password_backend);
+	os_free(config->sae_groups);
 	os_free(config);
 	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
 #ifdef OFFSET
 #undef OFFSET
 #undef OFFSET
 #endif /* OFFSET */
 #endif /* OFFSET */
@@ -3070,6 +3087,7 @@ static const struct global_parse_data global_fields[] = {
 	{ INT_RANGE(auto_interworking, 0, 1), 0 },
 	{ INT_RANGE(auto_interworking, 0, 1), 0 },
 	{ INT(okc), 0 },
 	{ INT(okc), 0 },
 	{ INT(pmf), 0 },
 	{ INT(pmf), 0 },
+	{ FUNC(sae_groups), 0 },
 };
 };
 
 
 #undef FUNC
 #undef FUNC

+ 10 - 0
wpa_supplicant/config.h

@@ -797,6 +797,16 @@ struct wpa_config {
 	 * this default behavior.
 	 * this default behavior.
 	 */
 	 */
 	enum mfp_options pmf;
 	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;
 };
 };
 
 
 
 

+ 10 - 0
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);
 		fprintf(f, "okc=%d\n", config->okc);
 	if (config->pmf)
 	if (config->pmf)
 		fprintf(f, "pmf=%d\n", 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 */
 #endif /* CONFIG_NO_CONFIG_WRITE */

+ 58 - 2
wpa_supplicant/sme.c

@@ -42,6 +42,45 @@ static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
 
 
 #ifdef CONFIG_SAE
 #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,
 static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
 						 struct wpa_ssid *ssid,
 						 struct wpa_ssid *ssid,
 						 const u8 *bssid)
 						 const u8 *bssid)
@@ -54,8 +93,10 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
 		return NULL;
 		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;
 		return NULL;
+	}
 
 
 	if (sae_prepare_commit(wpa_s->own_addr, bssid,
 	if (sae_prepare_commit(wpa_s->own_addr, bssid,
 			       (u8 *) ssid->passphrase,
 			       (u8 *) ssid->passphrase,
@@ -424,6 +465,20 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 		return 0;
 		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)
 	if (status_code != WLAN_STATUS_SUCCESS)
 		return -1;
 		return -1;
 
 
@@ -434,7 +489,8 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 			return -1;
 			return -1;
 		if (wpa_s->sme.sae.state != SAE_COMMITTED)
 		if (wpa_s->sme.sae.state != SAE_COMMITTED)
 			return -1;
 			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)
 		    WLAN_STATUS_SUCCESS)
 			return -1;
 			return -1;
 
 

+ 8 - 0
wpa_supplicant/wpa_supplicant.conf

@@ -281,6 +281,14 @@ fast_reauth=1
 # ieee80211w parameter.
 # ieee80211w parameter.
 #pmf=0
 #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)
 # Interworking (IEEE 802.11u)
 
 
 # Enable Interworking
 # Enable Interworking

+ 1 - 0
wpa_supplicant/wpa_supplicant_i.h

@@ -517,6 +517,7 @@ struct wpa_supplicant {
 #ifdef CONFIG_SAE
 #ifdef CONFIG_SAE
 		struct sae_data sae;
 		struct sae_data sae;
 		struct wpabuf *sae_token;
 		struct wpabuf *sae_token;
+		int sae_group_index;
 #endif /* CONFIG_SAE */
 #endif /* CONFIG_SAE */
 	} sme;
 	} sme;
 #endif /* CONFIG_SME */
 #endif /* CONFIG_SME */