]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
SAE: Add support for using the optional Password Identifier
authorJouni Malinen <jouni@codeaurora.org>
Sat, 19 May 2018 14:28:01 +0000 (17:28 +0300)
committerJouni Malinen <j@w1.fi>
Sat, 19 May 2018 14:30:29 +0000 (17:30 +0300)
This extends the SAE implementation in both infrastructure and mesh BSS
cases to allow an optional Password Identifier to be used. This uses the
mechanism added in P802.11REVmd/D1.0. The Password Identifier is
configured in a wpa_supplicant network profile as a new string parameter
sae_password_id. In hostapd configuration, the existing sae_password
parameter has been extended to allow the password identifier (and also a
peer MAC address) to be set. In addition, multiple sae_password entries
can now be provided to hostapd to allow multiple per-peer and
per-identifier passwords to be set.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
18 files changed:
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/ieee802_11.c
src/common/ieee802_11_common.c
src/common/ieee802_11_common.h
src/common/ieee802_11_defs.h
src/common/sae.c
src/common/sae.h
src/common/wpa_ctrl.h
wpa_supplicant/config.c
wpa_supplicant/config_file.c
wpa_supplicant/config_ssid.h
wpa_supplicant/config_winreg.c
wpa_supplicant/mesh_rsn.c
wpa_supplicant/sme.c
wpa_supplicant/wpa_supplicant.conf

index 151b9fc5c901edc3e593d8a6253d760c25cd710a..502ea3d942e826031a7d39161ce78075c4249f81 100644 (file)
@@ -2169,6 +2169,61 @@ static unsigned int parse_tls_flags(const char *val)
 #endif /* EAP_SERVER */
 
 
+#ifdef CONFIG_SAE
+static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
+{
+       struct sae_password_entry *pw;
+       const char *pos = val, *pos2, *end = NULL;
+
+       pw = os_zalloc(sizeof(*pw));
+       if (!pw)
+               return -1;
+       os_memset(pw->peer_addr, 0xff, ETH_ALEN); /* default to wildcard */
+
+       pos2 = os_strstr(pos, "|mac=");
+       if (pos2) {
+               end = pos2;
+               pos2 += 5;
+               if (hwaddr_aton(pos2, pw->peer_addr) < 0)
+                       goto fail;
+               pos = pos2 + ETH_ALEN * 3 - 1;
+       }
+
+       pos2 = os_strstr(pos, "|id=");
+       if (pos2) {
+               if (!end)
+                       end = pos2;
+               pos2 += 4;
+               pw->identifier = os_strdup(pos2);
+               if (!pw->identifier)
+                       goto fail;
+       }
+
+       if (!end) {
+               pw->password = os_strdup(val);
+               if (!pw->password)
+                       goto fail;
+       } else {
+               pw->password = os_malloc(end - val + 1);
+               if (!pw->password)
+                       goto fail;
+               os_memcpy(pw->password, val, end - val);
+               pw->password[end - val] = '\0';
+       }
+
+       pw->next = bss->sae_passwords;
+       bss->sae_passwords = pw;
+
+       return 0;
+fail:
+       str_clear_free(pw->password);
+       os_free(pw->identifier);
+       os_free(pw);
+       return -1;
+}
+#endif /* CONFIG_SAE */
+
+
 static int hostapd_config_fill(struct hostapd_config *conf,
                               struct hostapd_bss_config *bss,
                               const char *buf, char *pos, int line)
@@ -3727,9 +3782,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
        } else if (os_strcmp(buf, "sae_commit_override") == 0) {
                wpabuf_free(bss->sae_commit_override);
                bss->sae_commit_override = wpabuf_parse_bin(pos);
+#ifdef CONFIG_SAE
        } else if (os_strcmp(buf, "sae_password") == 0) {
-               os_free(bss->sae_password);
-               bss->sae_password = os_strdup(pos);
+               if (parse_sae_password(bss, pos) < 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid sae_password",
+                                  line);
+                       return 1;
+               }
+#endif /* CONFIG_SAE */
 #endif /* CONFIG_TESTING_OPTIONS */
        } else if (os_strcmp(buf, "vendor_elements") == 0) {
                if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
index b5a271866720601061830cac297e37516bf0d4ce..64dd8c28b637b29604b5c5a34bd7f763cec99092 100644 (file)
@@ -1416,13 +1416,32 @@ own_ip_addr=127.0.0.1
 #okc=1
 
 # SAE password
-# This parameter can be used to set a password for SAE. By default, the
+# This parameter can be used to set passwords for SAE. By default, the
 # wpa_passphrase value is used if this separate parameter is not used, but
 # wpa_passphrase follows the WPA-PSK constraints (8..63 characters) even though
 # SAE passwords do not have such constraints. If the BSS enabled both SAE and
-# WPA-PSK and both values are set, SAE uses the sae_password value and WPA-PSK
+# WPA-PSK and both values are set, SAE uses the sae_password values and WPA-PSK
 # uses the wpa_passphrase value.
+#
+# Each sae_password entry is added to a list of available passwords. This
+# corresponds to the dot11RSNAConfigPasswordValueEntry. sae_password value
+# starts with the password (dot11RSNAConfigPasswordCredential). That value can
+# be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and
+# by optional password identifier (dot11RSNAConfigPasswordIdentifier). If the
+# peer MAC address is not included or is set to the wildcard address
+# (ff:ff:ff:ff:ff:ff), the entry is available for any station to use. If a
+# specific peer MAC address is included, only a station with that MAC address
+# is allowed to use the entry. If the password identifier (with non-zero length)
+# is included, the entry is limited to be used only with that specified
+# identifier. The last matching (based on peer MAC address and identifier) entry
+# is used to select which password to use. Setting sae_password to an empty
+# string has a special meaning of removing all previously added entries.
+# sae_password uses the following encoding:
+#<password/credential>[|mac=<peer mac>][|id=<identifier>]
+# Examples:
 #sae_password=secret
+#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff
+#sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier
 
 # SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
 # This parameter defines how many open SAE instances can be in progress at the
index 056a7903582602cfc6f0c27d69ca5ef086a8eb60..1c28d662a5c471a10d9abc0c7fd4b1bebf911e56 100644 (file)
@@ -481,6 +481,22 @@ static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf)
 }
 
 
+static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf)
+{
+       struct sae_password_entry *pw, *tmp;
+
+       pw = conf->sae_passwords;
+       conf->sae_passwords = NULL;
+       while (pw) {
+               tmp = pw;
+               pw = pw->next;
+               str_clear_free(tmp->password);
+               os_free(tmp->identifier);
+               os_free(tmp);
+       }
+}
+
+
 void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 {
        if (conf == NULL)
@@ -658,7 +674,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
        wpabuf_free(conf->dpp_csign);
 #endif /* CONFIG_DPP */
 
-       os_free(conf->sae_password);
+       hostapd_config_free_sae_passwords(conf);
 
        os_free(conf);
 }
index bc6489a84497374b90e10c138cf242e21c955f36..7d47cb5ac59029b96f8edb34cdf656b91d6c45f1 100644 (file)
@@ -237,6 +237,12 @@ struct fils_realm {
        char realm[];
 };
 
+struct sae_password_entry {
+       struct sae_password_entry *next;
+       char *password;
+       char *identifier;
+       u8 peer_addr[ETH_ALEN];
+};
 
 /**
  * struct hostapd_bss_config - Per-BSS configuration
@@ -604,7 +610,7 @@ struct hostapd_bss_config {
        unsigned int sae_sync;
        int sae_require_mfp;
        int *sae_groups;
-       char *sae_password;
+       struct sae_password_entry *sae_passwords;
 
        char *wowlan_triggers; /* Wake-on-WLAN triggers */
 
index 68c0da24d0e16e10d2f483d370a57d81be778061..9f6d24420560398d6b953cfcedf3f1b8c3984440 100644 (file)
@@ -369,9 +369,25 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
                                             struct sta_info *sta, int update)
 {
        struct wpabuf *buf;
-       const char *password;
+       const char *password = NULL;
+       struct sae_password_entry *pw;
+       const char *rx_id = NULL;
 
-       password = hapd->conf->sae_password;
+       if (sta->sae->tmp)
+               rx_id = sta->sae->tmp->pw_id;
+
+       for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+               if (!is_broadcast_ether_addr(pw->peer_addr) &&
+                   os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0)
+                       continue;
+               if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
+                       continue;
+               if (rx_id && pw->identifier &&
+                   os_strcmp(rx_id, pw->identifier) != 0)
+                       continue;
+               password = pw->password;
+               break;
+       }
        if (!password)
                password = hapd->conf->ssid.wpa_passphrase;
        if (!password) {
@@ -381,17 +397,18 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
 
        if (update &&
            sae_prepare_commit(hapd->own_addr, sta->addr,
-                              (u8 *) password, os_strlen(password),
+                              (u8 *) password, os_strlen(password), rx_id,
                               sta->sae) < 0) {
                wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
                return NULL;
        }
 
-       buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
+       buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
+                          (rx_id ? 3 + os_strlen(rx_id) : 0));
        if (buf == NULL)
                return NULL;
        sae_write_commit(sta->sae, buf, sta->sae->tmp ?
-                        sta->sae->tmp->anti_clogging_token : NULL);
+                        sta->sae->tmp->anti_clogging_token : NULL, rx_id);
 
        return buf;
 }
@@ -420,6 +437,8 @@ static int auth_sae_send_commit(struct hostapd_data *hapd,
        int reply_res;
 
        data = auth_build_sae_commit(hapd, sta, update);
+       if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
+               return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
        if (data == NULL)
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
@@ -932,6 +951,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
                                   MAC2STR(sta->addr));
                        goto remove_sta;
                }
+
+               if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
+                       wpa_msg(hapd->msg_ctx, MSG_INFO,
+                               WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
+                               MACSTR, MAC2STR(sta->addr));
+                       sae_clear_retransmit_timer(hapd, sta);
+                       sae_set_state(sta, SAE_NOTHING,
+                                     "Unknown Password Identifier");
+                       goto remove_sta;
+               }
+
                if (token && check_sae_token(hapd, sta->addr, token, token_len)
                    < 0) {
                        wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
index dcae84ce0392b6e6e188bb8e9ce767a6ca1157aa..e1ef27795b99a0eb0d75c75e9ee234b0a7105fcc 100644 (file)
@@ -262,6 +262,10 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
                elems->owe_dh = pos;
                elems->owe_dh_len = elen;
                break;
+       case WLAN_EID_EXT_PASSWORD_IDENTIFIER:
+               elems->password_id = pos;
+               elems->password_id_len = elen;
+               break;
        default:
                if (show_errors) {
                        wpa_printf(MSG_MSGDUMP,
index 5b0893bba606f1bcfa489b7f3664fdbc1a0fb4cd..ff7e51de3dc9bebbf6a8e9b1694b87466080749e 100644 (file)
@@ -83,6 +83,7 @@ struct ieee802_11_elems {
        const u8 *owe_dh;
        const u8 *power_capab;
        const u8 *roaming_cons_sel;
+       const u8 *password_id;
 
        u8 ssid_len;
        u8 supp_rates_len;
@@ -128,6 +129,7 @@ struct ieee802_11_elems {
        u8 owe_dh_len;
        u8 power_capab_len;
        u8 roaming_cons_sel_len;
+       u8 password_id_len;
 
        struct mb_ies_info mb_ies;
 };
index ff2912518042dc27365117a8805d75506be5cb84..7e3d8dbc116c3361958d58b18732afb7699730bd 100644 (file)
 #define WLAN_STATUS_AUTHORIZATION_DEENABLED 107
 #define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
 #define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
+#define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123
 
 /* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
 #define WLAN_REASON_UNSPECIFIED 1
 #define WLAN_EID_EXT_FILS_NONCE 13
 #define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14
 #define WLAN_EID_EXT_OWE_DH_PARAM 32
+#define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33
 #define WLAN_EID_EXT_HE_CAPABILITIES 35
 #define WLAN_EID_EXT_HE_OPERATION 36
 
index fa28dfb1d9cf6659250960c6a1966d41bd8a4559..981e788dc7518cc7986fc58e3646fc80f0688b82 100644 (file)
@@ -94,6 +94,7 @@ void sae_clear_temp_data(struct sae_data *sae)
        crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
        crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
        wpabuf_free(tmp->anti_clogging_token);
+       os_free(tmp->pw_id);
        bin_clear_free(tmp, sizeof(*tmp));
        sae->tmp = NULL;
 }
@@ -423,12 +424,13 @@ static int get_random_qr_qnr(const u8 *prime, size_t prime_len,
 
 static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
                              const u8 *addr2, const u8 *password,
-                             size_t password_len)
+                             size_t password_len, const char *identifier)
 {
        u8 counter, k = 40;
        u8 addrs[2 * ETH_ALEN];
-       const u8 *addr[2];
-       size_t len[2];
+       const u8 *addr[3];
+       size_t len[3];
+       size_t num_elem;
        u8 dummy_password[32];
        size_t dummy_password_len;
        int pwd_seed_odd = 0;
@@ -460,10 +462,13 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
 
        wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
                              password, password_len);
+       if (identifier)
+               wpa_printf(MSG_DEBUG, "SAE: password identifier: %s",
+                          identifier);
 
        /*
         * H(salt, ikm) = HMAC-SHA256(salt, ikm)
-        * base = password
+        * base = password [|| identifier]
         * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
         *              base || counter)
         */
@@ -471,8 +476,15 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
 
        addr[0] = password;
        len[0] = password_len;
-       addr[1] = &counter;
-       len[1] = sizeof(counter);
+       num_elem = 1;
+       if (identifier) {
+               addr[num_elem] = (const u8 *) identifier;
+               len[num_elem] = os_strlen(identifier);
+               num_elem++;
+       }
+       addr[num_elem] = &counter;
+       len[num_elem] = sizeof(counter);
+       num_elem++;
 
        /*
         * Continue for at least k iterations to protect against side-channel
@@ -490,8 +502,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
                }
 
                wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
-               if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
-                                      pwd_seed) < 0)
+               if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+                                      addr, len, pwd_seed) < 0)
                        break;
 
                res = sae_test_pwd_seed_ecc(sae, pwd_seed,
@@ -550,12 +562,13 @@ fail:
 
 static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
                              const u8 *addr2, const u8 *password,
-                             size_t password_len)
+                             size_t password_len, const char *identifier)
 {
        u8 counter;
        u8 addrs[2 * ETH_ALEN];
-       const u8 *addr[2];
-       size_t len[2];
+       const u8 *addr[3];
+       size_t len[3];
+       size_t num_elem;
        int found = 0;
 
        if (sae->tmp->pwe_ffc == NULL) {
@@ -570,14 +583,21 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
        /*
         * H(salt, ikm) = HMAC-SHA256(salt, ikm)
         * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
-        *              password || counter)
+        *              password [|| identifier] || counter)
         */
        sae_pwd_seed_key(addr1, addr2, addrs);
 
        addr[0] = password;
        len[0] = password_len;
-       addr[1] = &counter;
-       len[1] = sizeof(counter);
+       num_elem = 1;
+       if (identifier) {
+               addr[num_elem] = (const u8 *) identifier;
+               len[num_elem] = os_strlen(identifier);
+               num_elem++;
+       }
+       addr[num_elem] = &counter;
+       len[num_elem] = sizeof(counter);
+       num_elem++;
 
        for (counter = 1; !found; counter++) {
                u8 pwd_seed[SHA256_MAC_LEN];
@@ -590,8 +610,8 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
                }
 
                wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
-               if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
-                                      pwd_seed) < 0)
+               if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+                                      addr, len, pwd_seed) < 0)
                        break;
                res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
                if (res < 0)
@@ -702,13 +722,15 @@ fail:
 
 int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
                       const u8 *password, size_t password_len,
-                      struct sae_data *sae)
+                      const char *identifier, struct sae_data *sae)
 {
        if (sae->tmp == NULL ||
            (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
-                                               password_len) < 0) ||
+                                               password_len,
+                                               identifier) < 0) ||
            (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
-                                               password_len) < 0) ||
+                                               password_len,
+                                               identifier) < 0) ||
            sae_derive_commit(sae) < 0)
                return -1;
        return 0;
@@ -848,7 +870,7 @@ int sae_process_commit(struct sae_data *sae)
 
 
 void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
-                     const struct wpabuf *token)
+                     const struct wpabuf *token, const char *identifier)
 {
        u8 *pos;
 
@@ -882,6 +904,16 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
                wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
                            pos, sae->tmp->prime_len);
        }
+
+       if (identifier) {
+               /* Password Identifier element */
+               wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+               wpabuf_put_u8(buf, 1 + os_strlen(identifier));
+               wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER);
+               wpabuf_put_str(buf, identifier);
+               wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s",
+                          identifier);
+       }
 }
 
 
@@ -927,25 +959,70 @@ u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group)
 }
 
 
+static int sae_is_password_id_elem(const u8 *pos, const u8 *end)
+{
+       return end - pos >= 3 &&
+               pos[0] == WLAN_EID_EXTENSION &&
+               pos[1] >= 1 &&
+               end - pos - 2 >= pos[1] &&
+               pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER;
+}
+
+
 static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
                                   const u8 *end, const u8 **token,
                                   size_t *token_len)
 {
-       if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) {
-               size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
-                                    sae->tmp->prime_len);
-               wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
-               if (token)
-                       *token = *pos;
-               if (token_len)
-                       *token_len = tlen;
-               *pos += tlen;
-       } else {
-               if (token)
-                       *token = NULL;
-               if (token_len)
-                       *token_len = 0;
+       size_t scalar_elem_len, tlen;
+       const u8 *elem;
+
+       if (token)
+               *token = NULL;
+       if (token_len)
+               *token_len = 0;
+
+       scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len;
+       if (scalar_elem_len >= (size_t) (end - *pos))
+               return; /* No extra data beyond peer scalar and element */
+
+       /* It is a bit difficult to parse this now that there is an
+        * optional variable length Anti-Clogging Token field and
+        * optional variable length Password Identifier element in the
+        * frame. We are sending out fixed length Anti-Clogging Token
+        * fields, so use that length as a requirement for the received
+        * token and check for the presence of possible Password
+        * Identifier element based on the element header information.
+        */
+       tlen = end - (*pos + scalar_elem_len);
+
+       if (tlen < SHA256_MAC_LEN) {
+               wpa_printf(MSG_DEBUG,
+                          "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token",
+                          (unsigned int) tlen);
+               return;
+       }
+
+       elem = *pos + scalar_elem_len;
+       if (sae_is_password_id_elem(elem, end)) {
+                /* Password Identifier element takes out all available
+                 * extra octets, so there can be no Anti-Clogging token in
+                 * this frame. */
+               return;
        }
+
+       elem += SHA256_MAC_LEN;
+       if (sae_is_password_id_elem(elem, end)) {
+                /* Password Identifier element is included in the end, so
+                 * remove its length from the Anti-Clogging token field. */
+               tlen -= 2 + elem[1];
+       }
+
+       wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
+       if (token)
+               *token = *pos;
+       if (token_len)
+               *token_len = tlen;
+       *pos += tlen;
 }
 
 
@@ -997,12 +1074,12 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos,
 }
 
 
-static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
+static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos,
                                        const u8 *end)
 {
        u8 prime[SAE_MAX_ECC_PRIME_LEN];
 
-       if (2 * sae->tmp->prime_len > end - pos) {
+       if (2 * sae->tmp->prime_len > end - *pos) {
                wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
                           "commit-element");
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1013,8 +1090,8 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
        /* element x and y coordinates < p */
-       if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
-           os_memcmp(pos + sae->tmp->prime_len, prime,
+       if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 ||
+           os_memcmp(*pos + sae->tmp->prime_len, prime,
                      sae->tmp->prime_len) >= 0) {
                wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
                           "element");
@@ -1022,13 +1099,13 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
        }
 
        wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
-                   pos, sae->tmp->prime_len);
+                   *pos, sae->tmp->prime_len);
        wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
-                   pos + sae->tmp->prime_len, sae->tmp->prime_len);
+                   *pos + sae->tmp->prime_len, sae->tmp->prime_len);
 
        crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
        sae->tmp->peer_commit_element_ecc =
-               crypto_ec_point_from_bin(sae->tmp->ec, pos);
+               crypto_ec_point_from_bin(sae->tmp->ec, *pos);
        if (sae->tmp->peer_commit_element_ecc == NULL)
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
@@ -1038,27 +1115,29 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
        }
 
+       *pos += 2 * sae->tmp->prime_len;
+
        return WLAN_STATUS_SUCCESS;
 }
 
 
-static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
+static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos,
                                        const u8 *end)
 {
        struct crypto_bignum *res, *one;
        const u8 one_bin[1] = { 0x01 };
 
-       if (sae->tmp->prime_len > end - pos) {
+       if (sae->tmp->prime_len > end - *pos) {
                wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
                           "commit-element");
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
        }
-       wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos,
+       wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos,
                    sae->tmp->prime_len);
 
        crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
        sae->tmp->peer_commit_element_ffc =
-               crypto_bignum_init_set(pos, sae->tmp->prime_len);
+               crypto_bignum_init_set(*pos, sae->tmp->prime_len);
        if (sae->tmp->peer_commit_element_ffc == NULL)
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
        /* 1 < element < p - 1 */
@@ -1086,11 +1165,13 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
        }
        crypto_bignum_deinit(res, 0);
 
+       *pos += sae->tmp->prime_len;
+
        return WLAN_STATUS_SUCCESS;
 }
 
 
-static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
+static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos,
                                    const u8 *end)
 {
        if (sae->tmp->dh)
@@ -1099,6 +1180,44 @@ static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
 }
 
 
+static int sae_parse_password_identifier(struct sae_data *sae,
+                                        const u8 *pos, const u8 *end)
+{
+       wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
+                   pos, end - pos);
+       if (!sae_is_password_id_elem(pos, end)) {
+               if (sae->tmp->pw_id) {
+                       wpa_printf(MSG_DEBUG,
+                                  "SAE: No Password Identifier included, but expected one (%s)",
+                                  sae->tmp->pw_id);
+                       return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+               }
+               os_free(sae->tmp->pw_id);
+               sae->tmp->pw_id = NULL;
+               return WLAN_STATUS_SUCCESS; /* No Password Identifier */
+       }
+
+       if (sae->tmp->pw_id &&
+           (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) ||
+            os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) {
+               wpa_printf(MSG_DEBUG,
+                          "SAE: The included Password Identifier does not match the expected one (%s)",
+                          sae->tmp->pw_id);
+               return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+       }
+
+       os_free(sae->tmp->pw_id);
+       sae->tmp->pw_id = os_malloc(pos[1]);
+       if (!sae->tmp->pw_id)
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1);
+       sae->tmp->pw_id[pos[1] - 1] = '\0';
+       wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier",
+                         sae->tmp->pw_id, pos[1] -  1);
+       return WLAN_STATUS_SUCCESS;
+}
+
+
 u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
                     const u8 **token, size_t *token_len, int *allowed_groups)
 {
@@ -1122,7 +1241,12 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
                return res;
 
        /* commit-element */
-       res = sae_parse_commit_element(sae, pos, end);
+       res = sae_parse_commit_element(sae, &pos, end);
+       if (res != WLAN_STATUS_SUCCESS)
+               return res;
+
+       /* Optional Password Identifier element */
+       res = sae_parse_password_identifier(sae, pos, end);
        if (res != WLAN_STATUS_SUCCESS)
                return res;
 
index 7c07bfba28181bd22700dc68c4316612f43c84fd..3fbcb58d155a0d6580b94fc7cc87f6d4584a958b 100644 (file)
@@ -39,6 +39,7 @@ struct sae_temporary_data {
        struct crypto_bignum *prime_buf;
        struct crypto_bignum *order_buf;
        struct wpabuf *anti_clogging_token;
+       char *pw_id;
 };
 
 enum sae_state {
@@ -63,10 +64,10 @@ void sae_clear_data(struct sae_data *sae);
 
 int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
                       const u8 *password, size_t password_len,
-                      struct sae_data *sae);
+                      const char *identifier, struct sae_data *sae);
 int sae_process_commit(struct sae_data *sae);
 void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
-                     const struct wpabuf *token);
+                     const struct wpabuf *token, const char *identifier);
 u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
                     const u8 **token, size_t *token_len, int *allowed_groups);
 void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
index c7a7c1e275c9a3ffe83af86c811f2c0688037420..4eb7356fbd6fe203c0886e258bcc9b21f658d6b4 100644 (file)
@@ -89,6 +89,9 @@ extern "C" {
 #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
 /** Channel switch (followed by freq=<MHz> and other channel parameters) */
 #define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH "
+/** SAE authentication failed due to unknown password identifier */
+#define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \
+       "CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
 
 /** IP subnet status change notification
  *
index f65bbb02f83d46450bee3aedac1c72a12f599893..8ec60629bb9e2749168da47f8056c5274109dba7 100644 (file)
@@ -2134,6 +2134,7 @@ static const struct parse_data ssid_fields[] = {
        { FUNC_KEY(psk) },
        { INT(mem_only_psk) },
        { STR_KEY(sae_password) },
+       { STR(sae_password_id) },
        { FUNC(proto) },
        { FUNC(key_mgmt) },
        { INT(bg_scan_period) },
@@ -2474,6 +2475,7 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
        str_clear_free(ssid->passphrase);
        os_free(ssid->ext_psk);
        str_clear_free(ssid->sae_password);
+       os_free(ssid->sae_password_id);
 #ifdef IEEE8021X_EAPOL
        eap_peer_config_free(&ssid->eap);
 #endif /* IEEE8021X_EAPOL */
index d186f78eb87ce05c6f71c38ea05f0c777b5e6382..aa73f9df6dfac513cebda50fa34904962b382920 100644 (file)
@@ -749,6 +749,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        write_psk(f, ssid);
        INT(mem_only_psk);
        STR(sae_password);
+       STR(sae_password_id);
        write_proto(f, ssid);
        write_key_mgmt(f, ssid);
        INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
index 9fd56c32f670d57bfc056e5d26a04cc861089f7f..5b872fac0ce7bdbc624eb4e15baf9d1749c76b3b 100644 (file)
@@ -193,6 +193,14 @@ struct wpa_ssid {
         */
        char *sae_password;
 
+       /**
+        * sae_password_id - SAE password identifier
+        *
+        * This parameter can be used to identify a specific SAE password. If
+        * not included, the default SAE password is used instead.
+        */
+       char *sae_password_id;
+
        /**
         * ext_psk - PSK/passphrase name in external storage
         *
index 8b8992bd7f6661b1e324c6af1d6c7a91a014582c..0ce1830b4ef2b1a8c20fdecd37e963d7ace7fa3d 100644 (file)
@@ -873,6 +873,7 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id)
        write_bssid(netw, ssid);
        write_psk(netw, ssid);
        STR(sae_password);
+       STR(sae_password_id);
        write_proto(netw, ssid);
        write_key_mgmt(netw, ssid);
        write_pairwise(netw, ssid);
index 25dcde5c63c3d6a64f32a50d0845ccfa1dcaa8ee..e74cb16b072518fe912fa2e93c7f19f1b8af2a09 100644 (file)
@@ -332,8 +332,14 @@ static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s,
                return -1;
        }
 
+       if (sta->sae->tmp && !sta->sae->tmp->pw_id && ssid->sae_password_id) {
+               sta->sae->tmp->pw_id = os_strdup(ssid->sae_password_id);
+               if (!sta->sae->tmp->pw_id)
+                       return -1;
+       }
        return sae_prepare_commit(wpa_s->own_addr, sta->addr,
                                  (u8 *) password, os_strlen(password),
+                                 ssid->sae_password_id,
                                  sta->sae);
 }
 
index 1348e1c29c243cff6af83db53057bd4907214f69..d57195f1531739a29f839b3abf8fcf498646bae0 100644 (file)
@@ -117,12 +117,15 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
 
        if (sae_prepare_commit(wpa_s->own_addr, bssid,
                               (u8 *) password, os_strlen(password),
+                              ssid->sae_password_id,
                               &wpa_s->sme.sae) < 0) {
                wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
                return NULL;
        }
 
        len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0;
+       if (ssid->sae_password_id)
+               len += 4 + os_strlen(ssid->sae_password_id);
        buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
        if (buf == NULL)
                return NULL;
@@ -130,7 +133,8 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
                wpabuf_put_le16(buf, 1); /* Transaction seq# */
                wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
        }
-       sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token);
+       sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
+                        ssid->sae_password_id);
 
        return buf;
 }
@@ -1005,6 +1009,16 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                return 0;
        }
 
+       if (auth_transaction == 1 &&
+           status_code == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
+               const u8 *bssid = sa ? sa : wpa_s->pending_bssid;
+
+               wpa_msg(wpa_s, MSG_INFO,
+                       WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER MACSTR,
+                       MAC2STR(bssid));
+               return -1;
+       }
+
        if (status_code != WLAN_STATUS_SUCCESS)
                return -1;
 
index 892e73501c5cbd10590d12483b97590ddd625fc5..a235ea0bfa77294c1e627a12dcb984783e387dab 100644 (file)
@@ -954,6 +954,11 @@ fast_reauth=1
 # used, but psk follows the WPA-PSK constraints (8..63 characters) even though
 # SAE passwords do not have such constraints.
 #
+# sae_password_id: SAE password identifier
+# This parameter can be used to set an identifier for the SAE password. By
+# default, no such identifier is used. If set, the specified identifier value
+# is used by the other peer to select which password to use for authentication.
+#
 # eapol_flags: IEEE 802.1X/EAPOL options (bit field)
 # Dynamic WEP key required for non-WPA mode
 # bit0 (1): require dynamically generated unicast WEP key