From 61a56c14800922328e4dc29bf4b70ff0ea51c7d3 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 26 Sep 2017 17:36:33 +0300 Subject: [PATCH] Add group_mgmt network parameter for PMF cipher selection The new wpa_supplicant network parameter group_mgmt can be used to specify which group management ciphers (AES-128-CMAC, BIP-GMAC-128, BIP-GMAC-256, BIP-CMAC-256) are allowed for the network. If not specified, the current behavior is maintained (i.e., follow what the AP advertises). The parameter can list multiple space separate ciphers. Signed-off-by: Jouni Malinen --- src/common/wpa_common.c | 36 ++++++++++++++++++++++++++++++ src/common/wpa_common.h | 3 +++ src/drivers/driver.h | 9 +++++++- tests/hwsim/wpasupplicant.py | 3 ++- wpa_supplicant/config.c | 35 +++++++++++++++++++++++++++++ wpa_supplicant/config_file.c | 17 ++++++++++++++ wpa_supplicant/config_ssid.h | 9 ++++++++ wpa_supplicant/events.c | 8 +++++++ wpa_supplicant/sme.c | 1 + wpa_supplicant/wpa_supplicant.c | 27 ++++++++++++++++++---- wpa_supplicant/wpa_supplicant.conf | 8 +++++++ 11 files changed, 150 insertions(+), 6 deletions(-) diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 4efb04604..13323bf9f 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -2072,6 +2072,14 @@ int wpa_parse_cipher(const char *value) val |= WPA_CIPHER_NONE; else if (os_strcmp(start, "GTK_NOT_USED") == 0) val |= WPA_CIPHER_GTK_NOT_USED; + else if (os_strcmp(start, "AES-128-CMAC") == 0) + val |= WPA_CIPHER_AES_128_CMAC; + else if (os_strcmp(start, "BIP-GMAC-128") == 0) + val |= WPA_CIPHER_BIP_GMAC_128; + else if (os_strcmp(start, "BIP-GMAC-256") == 0) + val |= WPA_CIPHER_BIP_GMAC_256; + else if (os_strcmp(start, "BIP-CMAC-256") == 0) + val |= WPA_CIPHER_BIP_CMAC_256; else { os_free(buf); return -1; @@ -2127,6 +2135,34 @@ int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) return -1; pos += ret; } + if (ciphers & WPA_CIPHER_AES_128_CMAC) { + ret = os_snprintf(pos, end - pos, "%sAES-128-CMAC", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_BIP_GMAC_128) { + ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-128", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_BIP_GMAC_256) { + ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-256", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_BIP_CMAC_256) { + ret = os_snprintf(pos, end - pos, "%sBIP-CMAC-256", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } if (ciphers & WPA_CIPHER_NONE) { ret = os_snprintf(pos, end - pos, "%sNONE", pos == start ? "" : delim); diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 2f11d2f34..cc8edf82d 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -29,6 +29,9 @@ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ WPA_CIPHER_GTK_NOT_USED) +#define WPA_ALLOWED_GROUP_MGMT_CIPHERS \ +(WPA_CIPHER_AES_128_CMAC | WPA_CIPHER_BIP_GMAC_128 | WPA_CIPHER_BIP_GMAC_256 | \ +WPA_CIPHER_BIP_CMAC_256) #define WPA_SELECTOR_LEN 4 #define WPA_VERSION 1 diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 6797a5a84..f8631c7ba 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -814,7 +814,7 @@ struct wpa_driver_associate_params { * WPA information element to be included in (Re)Association * Request (including information element id and length). Use * of this WPA IE is optional. If the driver generates the WPA - * IE, it can use pairwise_suite, group_suite, and + * IE, it can use pairwise_suite, group_suite, group_mgmt_suite, and * key_mgmt_suite to select proper algorithms. In this case, * the driver has to notify wpa_supplicant about the used WPA * IE by generating an event that the interface code will @@ -853,6 +853,13 @@ struct wpa_driver_associate_params { */ unsigned int group_suite; + /** + * mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*) + * + * This is usually ignored if @wpa_ie is used. + */ + unsigned int mgmt_group_suite; + /** * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*) * diff --git a/tests/hwsim/wpasupplicant.py b/tests/hwsim/wpasupplicant.py index 6ca45abcf..7f182d12f 100644 --- a/tests/hwsim/wpasupplicant.py +++ b/tests/hwsim/wpasupplicant.py @@ -1027,7 +1027,8 @@ class WpaSupplicant: "bssid_whitelist", "mem_only_psk", "eap_workaround", "engine", "fils_dh_group", "bssid_hint", "dpp_csign", "dpp_csign_expiry", - "dpp_netaccesskey", "dpp_netaccesskey_expiry" ] + "dpp_netaccesskey", "dpp_netaccesskey_expiry", + "group_mgmt" ] for field in not_quoted: if field in kwargs and kwargs[field]: self.set_network(id, field, kwargs[field]) diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 7e6022d57..0ea34e0ce 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1148,6 +1148,40 @@ static char * wpa_config_write_group(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +static int wpa_config_parse_group_mgmt(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val; + + val = wpa_config_parse_cipher(line, value); + if (val == -1) + return -1; + + if (val & ~WPA_ALLOWED_GROUP_MGMT_CIPHERS) { + wpa_printf(MSG_ERROR, + "Line %d: not allowed group management cipher (0x%x).", + line, val); + return -1; + } + + if (ssid->group_mgmt_cipher == val) + return 1; + wpa_printf(MSG_MSGDUMP, "group_mgmt: 0x%x", val); + ssid->group_mgmt_cipher = val; + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_group_mgmt(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_cipher(ssid->group_mgmt_cipher); +} +#endif /* NO_CONFIG_WRITE */ + + static int wpa_config_parse_auth_alg(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -2086,6 +2120,7 @@ static const struct parse_data ssid_fields[] = { { INT(bg_scan_period) }, { FUNC(pairwise) }, { FUNC(group) }, + { FUNC(group_mgmt) }, { FUNC(auth_alg) }, { FUNC(scan_freq) }, { FUNC(freq_list) }, diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 5ccd00387..ab11af959 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -593,6 +593,22 @@ static void write_group(FILE *f, struct wpa_ssid *ssid) } +static void write_group_mgmt(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (!ssid->group_mgmt_cipher) + return; + + value = wpa_config_get(ssid, "group_mgmt"); + if (!value) + return; + if (value[0]) + fprintf(f, "\tgroup_mgmt=%s\n", value); + os_free(value); +} + + static void write_auth_alg(FILE *f, struct wpa_ssid *ssid) { char *value; @@ -734,6 +750,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD); write_pairwise(f, ssid); write_group(f, ssid); + write_group_mgmt(f, ssid); write_auth_alg(f, ssid); STR(bgscan); STR(autoscan); diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 81f64a5f4..737ef42ff 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -209,6 +209,15 @@ struct wpa_ssid { */ int group_cipher; + /** + * group_mgmt_cipher - Bitfield of allowed group management ciphers + * + * This is a bitfield of WPA_CIPHER_AES_128_CMAC and WPA_CIPHER_BIP_* + * values. If 0, no constraint is used for the cipher, i.e., whatever + * the AP uses is accepted. + */ + int group_mgmt_cipher; + /** * key_mgmt - Bitfield of allowed key management protocols * diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index db7de89ed..500e66c78 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -565,6 +565,14 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, break; } + if (ssid->group_mgmt_cipher && + !(ie.mgmt_group_cipher & ssid->group_mgmt_cipher)) { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - group mgmt cipher mismatch"); + break; + } + if (!(ie.key_mgmt & ssid->key_mgmt)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index a4d5f75b9..a92fb4573 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -1227,6 +1227,7 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; params.pairwise_suite = wpa_s->pairwise_cipher; params.group_suite = wpa_s->group_cipher; + params.mgmt_group_suite = wpa_s->mgmt_group_cipher; params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; #ifdef CONFIG_HT_OVERRIDES diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index cdff003d8..eca9b2555 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1232,9 +1232,24 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ie.pairwise_cipher = ssid->pairwise_cipher; ie.key_mgmt = ssid->key_mgmt; #ifdef CONFIG_IEEE80211W - ie.mgmt_group_cipher = - ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ? - WPA_CIPHER_AES_128_CMAC : 0; + ie.mgmt_group_cipher = 0; + if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (ssid->group_mgmt_cipher & + WPA_CIPHER_BIP_GMAC_256) + ie.mgmt_group_cipher = + WPA_CIPHER_BIP_GMAC_256; + else if (ssid->group_mgmt_cipher & + WPA_CIPHER_BIP_CMAC_256) + ie.mgmt_group_cipher = + WPA_CIPHER_BIP_CMAC_256; + else if (ssid->group_mgmt_cipher & + WPA_CIPHER_BIP_GMAC_128) + ie.mgmt_group_cipher = + WPA_CIPHER_BIP_GMAC_128; + else + ie.mgmt_group_cipher = + WPA_CIPHER_AES_128_CMAC; + } #endif /* CONFIG_IEEE80211W */ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites " "based on configuration"); @@ -1387,6 +1402,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211W sel = ie.mgmt_group_cipher; + if (ssid->group_mgmt_cipher) + sel &= ssid->group_mgmt_cipher; if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || !(ie.capabilities & WPA_CAPABILITY_MFPC)) sel = 0; @@ -2301,7 +2318,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) size_t wpa_ie_len; int use_crypt, ret, i, bssid_changed; int algs = WPA_AUTH_ALG_OPEN; - unsigned int cipher_pairwise, cipher_group; + unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt; struct wpa_driver_associate_params params; int wep_keys_set = 0; int assoc_failed = 0; @@ -2665,6 +2682,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) use_crypt = 1; cipher_pairwise = wpa_s->pairwise_cipher; cipher_group = wpa_s->group_cipher; + cipher_group_mgmt = wpa_s->mgmt_group_cipher; if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) @@ -2748,6 +2766,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.wpa_ie_len = wpa_ie_len; params.pairwise_suite = cipher_pairwise; params.group_suite = cipher_group; + params.mgmt_group_suite = cipher_group_mgmt; params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; params.auth_alg = algs; diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 3430be0cf..fbf3e72c9 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -879,6 +879,14 @@ fast_reauth=1 # WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11] # If not set, this defaults to: CCMP TKIP WEP104 WEP40 # +# group_mgmt: list of accepted group management ciphers for RSN (PMF) +# AES-128-CMAC = BIP-CMAC-128 +# BIP-GMAC-128 +# BIP-GMAC-256 +# BIP-CMAC-256 +# If not set, no constraint on the cipher, i.e., accept whichever cipher the AP +# indicates. +# # psk: WPA preshared key; 256-bit pre-shared key # The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e., # 32 bytes or as an ASCII passphrase (in which case, the real PSK will be