]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
SAE: Add RSNXE in Association Request and EAPOL-Key msg 2/4
authorJouni Malinen <jouni@codeaurora.org>
Thu, 17 Oct 2019 13:54:05 +0000 (16:54 +0300)
committerJouni Malinen <j@w1.fi>
Thu, 17 Oct 2019 13:54:05 +0000 (16:54 +0300)
Add the new RSNXE into (Re)Association Request frames and EAPOL-Key msg
2/4 when using SAE with hash-to-element mechanism enabled. This allows
the AP to verify that there was no downgrade attack when both PWE
derivation mechanisms are enabled.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
src/rsn_supp/wpa_i.h
src/rsn_supp/wpa_ie.c
src/rsn_supp/wpa_ie.h
wpa_supplicant/events.c
wpa_supplicant/sme.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index f5231c138b908dbf16d39e6ffa695a129d51b5a9..504feaf2a5eb9d564abdfda12fdab037f459eea1 100644 (file)
@@ -655,51 +655,51 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
 
        kde = sm->assoc_wpa_ie;
        kde_len = sm->assoc_wpa_ie_len;
+       kde_buf = os_malloc(kde_len +
+                           2 + RSN_SELECTOR_LEN + 3 +
+                           sm->assoc_rsnxe_len +
+                           2 + RSN_SELECTOR_LEN + 1);
+       if (!kde_buf)
+               goto failed;
+       os_memcpy(kde_buf, kde, kde_len);
+       kde = kde_buf;
 
 #ifdef CONFIG_OCV
        if (wpa_sm_ocv_enabled(sm)) {
                struct wpa_channel_info ci;
                u8 *pos;
 
+               pos = kde + kde_len;
                if (wpa_sm_channel_info(sm, &ci) != 0) {
                        wpa_printf(MSG_WARNING,
                                   "Failed to get channel info for OCI element in EAPOL-Key 2/4");
                        goto failed;
                }
 
-               kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 3);
-               if (!kde_buf) {
-                       wpa_printf(MSG_WARNING,
-                                  "Failed to allocate memory for KDE with OCI in EAPOL-Key 2/4");
-                       goto failed;
-               }
-
-               os_memcpy(kde_buf, kde, kde_len);
-               kde = kde_buf;
-               pos = kde + kde_len;
                if (ocv_insert_oci_kde(&ci, &pos) < 0)
                        goto failed;
                kde_len = pos - kde;
        }
 #endif /* CONFIG_OCV */
 
+       if (sm->assoc_rsnxe && sm->assoc_rsnxe_len) {
+               os_memcpy(kde + kde_len, sm->assoc_rsnxe, sm->assoc_rsnxe_len);
+               kde_len += sm->assoc_rsnxe_len;
+       }
+
 #ifdef CONFIG_P2P
        if (sm->p2p) {
-               kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1);
-               if (kde_buf) {
-                       u8 *pos;
-                       wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE "
-                                  "into EAPOL-Key 2/4");
-                       os_memcpy(kde_buf, kde, kde_len);
-                       kde = kde_buf;
-                       pos = kde + kde_len;
-                       *pos++ = WLAN_EID_VENDOR_SPECIFIC;
-                       *pos++ = RSN_SELECTOR_LEN + 1;
-                       RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ);
-                       pos += RSN_SELECTOR_LEN;
-                       *pos++ = 0x01;
-                       kde_len = pos - kde;
-               }
+               u8 *pos;
+
+               wpa_printf(MSG_DEBUG,
+                          "P2P: Add IP Address Request KDE into EAPOL-Key 2/4");
+               pos = kde + kde_len;
+               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+               *pos++ = RSN_SELECTOR_LEN + 1;
+               RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ);
+               pos += RSN_SELECTOR_LEN;
+               *pos++ = 0x01;
+               kde_len = pos - kde;
        }
 #endif /* CONFIG_P2P */
 
@@ -2672,6 +2672,7 @@ void wpa_sm_deinit(struct wpa_sm *sm)
        eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
        eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
        os_free(sm->assoc_wpa_ie);
+       os_free(sm->assoc_rsnxe);
        os_free(sm->ap_wpa_ie);
        os_free(sm->ap_rsn_ie);
        os_free(sm->ap_rsnxe);
@@ -3049,6 +3050,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
        case WPA_PARAM_OCV:
                sm->ocv = value;
                break;
+       case WPA_PARAM_SAE_PWE:
+               sm->sae_pwe = value;
+               break;
        default:
                break;
        }
@@ -3226,6 +3230,83 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
 }
 
 
+/**
+ * wpa_sm_set_assoc_rsnxe_default - Generate own RSNXE from configuration
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @rsnxe: Pointer to buffer for RSNXE
+ * @rsnxe_len: Pointer to the length of the rsne buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe,
+                                  size_t *rsnxe_len)
+{
+       int res;
+
+       if (!sm)
+               return -1;
+
+       res = wpa_gen_rsnxe(sm, rsnxe, *rsnxe_len);
+       if (res < 0)
+               return -1;
+       *rsnxe_len = res;
+
+       wpa_hexdump(MSG_DEBUG, "RSN: Set own RSNXE default", rsnxe, *rsnxe_len);
+
+       if (sm->assoc_rsnxe) {
+               wpa_hexdump(MSG_DEBUG,
+                           "RSN: Leave previously set RSNXE default",
+                           sm->assoc_rsnxe, sm->assoc_rsnxe_len);
+       } else if (*rsnxe_len > 0) {
+               /*
+                * Make a copy of the RSNXE so that 4-Way Handshake gets the
+                * correct version of the IE even if it gets changed.
+                */
+               sm->assoc_rsnxe = os_memdup(rsnxe, *rsnxe_len);
+               if (!sm->assoc_rsnxe)
+                       return -1;
+
+               sm->assoc_rsnxe_len = *rsnxe_len;
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_sm_set_assoc_rsnxe - Set own RSNXE from (Re)AssocReq
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the RSNXE used in (Re)Association Request
+ * frame. The IE will be used to override the default value generated
+ * with wpa_sm_set_assoc_rsnxe_default().
+ */
+int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+       if (!sm)
+               return -1;
+
+       os_free(sm->assoc_rsnxe);
+       if (!ie || len == 0) {
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "RSN: clearing own RSNXE");
+               sm->assoc_rsnxe = NULL;
+               sm->assoc_rsnxe_len = 0;
+       } else {
+               wpa_hexdump(MSG_DEBUG, "RSN: set own RSNXE", ie, len);
+               sm->assoc_rsnxe = os_memdup(ie, len);
+               if (!sm->assoc_rsnxe)
+                       return -1;
+
+               sm->assoc_rsnxe_len = len;
+       }
+
+       return 0;
+}
+
+
 /**
  * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
index 125d359ef6284e4e798cd5e5489a366fd2303e13..f1fbb1bb563757eca6f241f27763004fadd30ad5 100644 (file)
@@ -98,7 +98,8 @@ enum wpa_sm_conf_params {
        WPA_PARAM_MGMT_GROUP,
        WPA_PARAM_RSN_ENABLED,
        WPA_PARAM_MFP,
-       WPA_PARAM_OCV
+       WPA_PARAM_OCV,
+       WPA_PARAM_SAE_PWE,
 };
 
 struct rsn_supp_config {
@@ -134,6 +135,9 @@ void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol);
 int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
 int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
                                    size_t *wpa_ie_len);
+int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe,
+                                  size_t *rsnxe_len);
+int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len);
 int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
 int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
 int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len);
index 16224c9ef65fd5957bf783d852b64a0aecae8579..2a433425c75ff0885d095ee574c34c2d95c3620a 100644 (file)
@@ -85,9 +85,12 @@ struct wpa_sm {
        int rsn_enabled; /* Whether RSN is enabled in configuration */
        int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
        int ocv; /* Operating Channel Validation */
+       int sae_pwe; /* SAE PWE generation options */
 
        u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
        size_t assoc_wpa_ie_len;
+       u8 *assoc_rsnxe; /* Own RSNXE from (Re)AssocReq */
+       size_t assoc_rsnxe_len;
        u8 *ap_wpa_ie, *ap_rsn_ie, *ap_rsnxe;
        size_t ap_wpa_ie_len, ap_rsn_ie_len, ap_rsnxe_len;
 
index e6af6c10e7009ae6715329d827c31867393aa895..f4ac25c05accb3a9f0e7b0ae5f5c9d33dfd48583 100644 (file)
@@ -342,6 +342,28 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
 }
 
 
+int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len)
+{
+       u8 *pos = rsnxe;
+
+       if (!wpa_key_mgmt_sae(sm->key_mgmt))
+               return 0; /* SAE not in use */
+       if (sm->sae_pwe != 1 && sm->sae_pwe != 2)
+               return 0; /* no supported extended RSN capabilities */
+
+       if (rsnxe_len < 3)
+               return -1;
+
+       *pos++ = WLAN_EID_RSNX;
+       *pos++ = 1;
+       /* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
+        * used for now */
+       *pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+
+       return pos - rsnxe;
+}
+
+
 /**
  * wpa_parse_vendor_specific - Parse Vendor Specific IEs
  * @pos: Pointer to the IE header
index 6f18bbe2ae58cece4c33ffee880dd2839921faa5..fa7d0a317a42e30b5bf4ba9965a161a5d53a6ef7 100644 (file)
@@ -62,5 +62,6 @@ struct wpa_eapol_ie_parse {
 int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
                             struct wpa_eapol_ie_parse *ie);
 int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len);
+int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len);
 
 #endif /* WPA_IE_H */
index 9ab5ebc92a64c19802a2c57ef3bd9b9336390811..ac4274cfbcdbb7f9f20dafb70f97427d3622db00 100644 (file)
@@ -2411,7 +2411,7 @@ static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s,
 static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
                                          union wpa_event_data *data)
 {
-       int l, len, found = 0, wpa_found, rsn_found;
+       int l, len, found = 0, found_x = 0, wpa_found, rsn_found;
        const u8 *p;
 #if defined(CONFIG_IEEE80211R) || defined(CONFIG_OWE)
        u8 bssid[ETH_ALEN];
@@ -2483,22 +2483,29 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
                                    p, l);
                        break;
                }
-               if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
-                    (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
-                   (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 &&
-                    (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) ||
-                   (p[0] == WLAN_EID_RSN && p[1] >= 2)) {
+               if (!found &&
+                   ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+                     (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
+                    (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 &&
+                     (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) ||
+                    (p[0] == WLAN_EID_RSN && p[1] >= 2))) {
                        if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
                                break;
                        found = 1;
                        wpa_find_assoc_pmkid(wpa_s);
-                       break;
+               }
+               if (!found_x && p[0] == WLAN_EID_RSNX) {
+                       if (wpa_sm_set_assoc_rsnxe(wpa_s->wpa, p, len))
+                               break;
+                       found_x = 1;
                }
                l -= len;
                p += len;
        }
        if (!found && data->assoc_info.req_ies)
                wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+       if (!found_x && data->assoc_info.req_ies)
+               wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
 
 #ifdef CONFIG_FILS
 #ifdef CONFIG_SME
index fbec8967f8027994781ac43c1a302a578425b5ac..c8a5d4b8506a19cecb0ddefd6c41bdaee3f3c156 100644 (file)
@@ -591,6 +591,14 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                os_memcpy(pos, ext_capab, ext_capab_len);
        }
 
+       if (wpa_s->rsnxe_len > 0 &&
+           wpa_s->rsnxe_len <=
+           sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len) {
+               os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+                         wpa_s->rsnxe, wpa_s->rsnxe_len);
+               wpa_s->sme.assoc_req_ie_len += wpa_s->rsnxe_len;
+       }
+
 #ifdef CONFIG_HS20
        if (is_hs20_network(wpa_s, ssid, bss)) {
                struct wpabuf *hs20;
@@ -884,6 +892,8 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
        /* Starting new connection, so clear the possibly used WPA IE from the
         * previous association. */
        wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+       wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
+       wpa_s->rsnxe_len = 0;
 
        sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
 }
@@ -1899,6 +1909,11 @@ pfs_fail:
                                        elems.osen_len + 2);
        } else
                wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+       if (elems.rsnxe)
+               wpa_sm_set_assoc_rsnxe(wpa_s->wpa, elems.rsnxe - 2,
+                                      elems.rsnxe_len + 2);
+       else
+               wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
        if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
                params.p2p = 1;
 
index 29f482038c37ddac80d8a7a83416e32491a597c1..6688d71af9544e2e3b9bc5dd6c647f62afc72148 100644 (file)
@@ -404,6 +404,8 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
        wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
        wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0);
        wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+       wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
+       wpa_s->rsnxe_len = 0;
        wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
        wpa_s->group_cipher = WPA_CIPHER_NONE;
        wpa_s->mgmt_group_cipher = 0;
@@ -1578,12 +1580,20 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 #ifdef CONFIG_OCV
        wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv);
 #endif /* CONFIG_OCV */
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, wpa_s->conf->sae_pwe);
 
        if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
                wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
                return -1;
        }
 
+       wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe);
+       if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe,
+                                          &wpa_s->rsnxe_len)) {
+               wpa_msg(wpa_s, MSG_WARNING, "RSN: Failed to generate RSNXE");
+               return -1;
+       }
+
        if (0) {
 #ifdef CONFIG_DPP
        } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) {
@@ -2995,6 +3005,12 @@ pfs_fail:
        }
 #endif /* CONFIG_IEEE80211R */
 
+       if (wpa_s->rsnxe_len > 0 &&
+           wpa_s->rsnxe_len <= max_wpa_ie_len - wpa_ie_len) {
+               os_memcpy(wpa_ie + wpa_ie_len, wpa_s->rsnxe, wpa_s->rsnxe_len);
+               wpa_ie_len += wpa_s->rsnxe_len;
+       }
+
        if (ssid->multi_ap_backhaul_sta) {
                size_t multi_ap_ie_len;
 
@@ -3305,6 +3321,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
        /* Starting new association, so clear the possibly used WPA IE from the
         * previous association. */
        wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+       wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
+       wpa_s->rsnxe_len = 0;
 
        wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, &params, NULL);
        if (!wpa_ie) {
index 1b29617ca1e34a71cc219d8af6005ff552651426..8c5defc3bc7fe6859c0201c1d62d3fbb50aae5bc 100644 (file)
@@ -614,6 +614,9 @@ struct wpa_supplicant {
        int eapol_received; /* number of EAPOL packets received after the
                             * previous association event */
 
+       u8 rsnxe[20];
+       size_t rsnxe_len;
+
        struct scard_data *scard;
        char imsi[20];
        int mnc_len;