X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=wpa_supplicant%2Fmesh_rsn.c;h=27ab8cb3645863dbd40b478ab1fa00fc9a761e29;hb=1ac388633a3639d55ed9aaf91f6cd7863faba40d;hp=ecc6393124d3ebbc6f8f41c942253a12b8ddb40f;hpb=0f950df0298a91d9696da15933ef6e7bc291cfa4;p=mech_eap.git diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c index ecc6393..27ab8cb 100644 --- a/wpa_supplicant/mesh_rsn.c +++ b/wpa_supplicant/mesh_rsn.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "crypto/aes.h" @@ -17,12 +18,50 @@ #include "ap/hostapd.h" #include "ap/wpa_auth.h" #include "ap/sta_info.h" +#include "ap/ieee802_11.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "wpas_glue.h" #include "mesh_mpm.h" #include "mesh_rsn.h" +#define MESH_AUTH_TIMEOUT 10 +#define MESH_AUTH_RETRY 3 + +void mesh_auth_timer(void *eloop_ctx, void *user_data) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct sta_info *sta = user_data; + struct hostapd_data *hapd; + + if (sta->sae->state != SAE_ACCEPTED) { + wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR + " (attempt %d) ", + MAC2STR(sta->addr), sta->sae_auth_retry); + wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_FAILURE "addr=" MACSTR, + MAC2STR(sta->addr)); + if (sta->sae_auth_retry < MESH_AUTH_RETRY) { + mesh_rsn_auth_sae_sta(wpa_s, sta); + } else { + hapd = wpa_s->ifmsh->bss[0]; + + if (sta->sae_auth_retry > MESH_AUTH_RETRY) { + ap_free_sta(hapd, sta); + return; + } + + /* block the STA if exceeded the number of attempts */ + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED); + sta->sae->state = SAE_NOTHING; + wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_BLOCKED "addr=" + MACSTR " duration=%d", + MAC2STR(sta->addr), + hapd->conf->ap_max_inactivity); + } + sta->sae_auth_retry++; + } +} + static void auth_logger(void *ctx, const u8 *addr, logger_level level, const char *txt) @@ -81,16 +120,24 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, static int auth_start_ampe(void *ctx, const u8 *addr) { struct mesh_rsn *mesh_rsn = ctx; + struct hostapd_data *hapd; + struct sta_info *sta; if (mesh_rsn->wpa_s->current_ssid->mode != WPAS_MODE_MESH) return -1; + hapd = mesh_rsn->wpa_s->ifmsh->bss[0]; + sta = ap_get_sta(hapd, addr); + if (sta) + eloop_cancel_timeout(mesh_auth_timer, mesh_rsn->wpa_s, sta); + mesh_mpm_auth_peer(mesh_rsn->wpa_s, addr); return 0; } -static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) +static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr, + enum mfp_options ieee80211w) { struct wpa_auth_config conf; struct wpa_auth_callbacks cb; @@ -99,13 +146,18 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); os_memset(&conf, 0, sizeof(conf)); - conf.wpa = 2; + conf.wpa = WPA_PROTO_RSN; conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE; - conf.wpa_pairwise = WPA_CIPHER_CCMP; - conf.rsn_pairwise = WPA_CIPHER_CCMP; - conf.wpa_group = WPA_CIPHER_CCMP; + conf.wpa_pairwise = rsn->pairwise_cipher; + conf.rsn_pairwise = rsn->pairwise_cipher; + conf.wpa_group = rsn->group_cipher; conf.eapol_version = 0; conf.wpa_group_rekey = -1; +#ifdef CONFIG_IEEE80211W + conf.ieee80211w = ieee80211w; + if (ieee80211w != NO_MGMT_FRAME_PROTECTION) + conf.group_mgmt_cipher = rsn->mgmt_group_cipher; +#endif /* CONFIG_IEEE80211W */ os_memset(&cb, 0, sizeof(cb)); cb.ctx = rsn; @@ -121,18 +173,34 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) } /* TODO: support rekeying */ - if (random_get_bytes(rsn->mgtk, 16) < 0) { - wpa_deinit(rsn->auth); + rsn->mgtk_len = wpa_cipher_key_len(conf.wpa_group); + if (random_get_bytes(rsn->mgtk, rsn->mgtk_len) < 0) return -1; - } + rsn->mgtk_key_id = 1; - /* group mgmt */ - wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1, - seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk)); +#ifdef CONFIG_IEEE80211W + if (ieee80211w != NO_MGMT_FRAME_PROTECTION) { + rsn->igtk_len = wpa_cipher_key_len(conf.group_mgmt_cipher); + if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0) + return -1; + rsn->igtk_key_id = 4; + + /* group mgmt */ + wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK", + rsn->igtk, rsn->igtk_len); + wpa_drv_set_key(rsn->wpa_s, + wpa_cipher_to_alg(rsn->mgmt_group_cipher), NULL, + rsn->igtk_key_id, 1, + seq, sizeof(seq), rsn->igtk, rsn->igtk_len); + } +#endif /* CONFIG_IEEE80211W */ /* group privacy / data frames */ - wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1, - seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk)); + wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK", + rsn->mgtk, rsn->mgtk_len); + wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher), NULL, + rsn->mgtk_key_id, 1, seq, sizeof(seq), + rsn->mgtk, rsn->mgtk_len); return 0; } @@ -141,7 +209,11 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) static void mesh_rsn_deinit(struct mesh_rsn *rsn) { os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk)); - wpa_deinit(rsn->auth); + rsn->mgtk_len = 0; + os_memset(rsn->igtk, 0, sizeof(rsn->igtk)); + rsn->igtk_len = 0; + if (rsn->auth) + wpa_deinit(rsn->auth); } @@ -157,17 +229,22 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, if (mesh_rsn == NULL) return NULL; mesh_rsn->wpa_s = wpa_s; + mesh_rsn->pairwise_cipher = conf->pairwise_cipher; + mesh_rsn->group_cipher = conf->group_cipher; + mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher; - if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) { + if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr, + conf->ieee80211w) < 0) { mesh_rsn_deinit(mesh_rsn); + os_free(mesh_rsn); return NULL; } bss->wpa_auth = mesh_rsn->auth; ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len); - conf->ies = (u8 *) ie; - conf->ie_len = ie_len; + conf->rsn_ie = (u8 *) ie; + conf->rsn_ie_len = ie_len; wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); @@ -214,79 +291,23 @@ static int mesh_rsn_sae_group(struct wpa_supplicant *wpa_s, } -struct wpabuf * -mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, struct sta_info *sta) +static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct sta_info *sta) { - struct wpabuf *buf; - int len; - if (ssid->passphrase == NULL) { wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available"); - return NULL; + return -1; } if (mesh_rsn_sae_group(wpa_s, sta->sae) < 0) { wpa_msg(wpa_s, MSG_DEBUG, "SAE: Failed to select group"); - return NULL; - } - - if (sae_prepare_commit(wpa_s->own_addr, sta->addr, - (u8 *) ssid->passphrase, - os_strlen(ssid->passphrase), sta->sae) < 0) { - wpa_msg(wpa_s, MSG_DEBUG, "SAE: Could not pick PWE"); - return NULL; + return -1; } - len = wpa_s->mesh_rsn->sae_token ? - wpabuf_len(wpa_s->mesh_rsn->sae_token) : 0; - buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len); - if (buf == NULL) - return NULL; - - sae_write_commit(sta->sae, buf, wpa_s->mesh_rsn->sae_token); - - return buf; -} - - -static void mesh_rsn_send_auth(struct wpa_supplicant *wpa_s, - const u8 *dst, const u8 *src, - u16 auth_transaction, u16 resp, - struct wpabuf *data) -{ - struct ieee80211_mgmt *auth; - u8 *buf; - size_t len, ielen = 0; - - if (data) - ielen = wpabuf_len(data); - len = IEEE80211_HDRLEN + sizeof(auth->u.auth) + ielen; - buf = os_zalloc(len); - if (buf == NULL) - return; - - auth = (struct ieee80211_mgmt *) buf; - auth->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_AUTH); - os_memcpy(auth->da, dst, ETH_ALEN); - os_memcpy(auth->sa, src, ETH_ALEN); - os_memcpy(auth->bssid, src, ETH_ALEN); - - auth->u.auth.auth_alg = host_to_le16(WLAN_AUTH_SAE); - auth->u.auth.auth_transaction = host_to_le16(auth_transaction); - auth->u.auth.status_code = host_to_le16(resp); - - if (data) - os_memcpy(auth->u.auth.variable, wpabuf_head(data), ielen); - - wpa_msg(wpa_s, MSG_DEBUG, "authentication frame: STA=" MACSTR - " auth_transaction=%d resp=%d (IE len=%lu)", - MAC2STR(dst), auth_transaction, resp, (unsigned long) ielen); - if (wpa_drv_send_mlme(wpa_s, buf, len, 0) < 0) - perror("send_auth_reply: send"); - - os_free(buf); + return sae_prepare_commit(wpa_s->own_addr, sta->addr, + (u8 *) ssid->passphrase, + os_strlen(ssid->passphrase), sta->sae); } @@ -294,8 +315,17 @@ static void mesh_rsn_send_auth(struct wpa_supplicant *wpa_s, int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta) { + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; struct wpa_ssid *ssid = wpa_s->current_ssid; - struct wpabuf *buf; + struct rsn_pmksa_cache_entry *pmksa; + unsigned int rnd; + int ret; + + if (!ssid) { + wpa_msg(wpa_s, MSG_DEBUG, + "AUTH: No current_ssid known to initiate new SAE"); + return -1; + } if (!sta->sae) { sta->sae = os_zalloc(sizeof(*sta->sae)); @@ -303,32 +333,51 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, return -1; } - buf = mesh_rsn_build_sae_commit(wpa_s, ssid, sta); - if (!buf) + pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr); + if (pmksa) { + if (!sta->wpa_sm) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, NULL); + if (!sta->wpa_sm) { + wpa_printf(MSG_ERROR, + "mesh: Failed to initialize RSN state machine"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "AUTH: Mesh PMKSA cache entry found for " MACSTR + " - try to use PMKSA caching instead of new SAE authentication", + MAC2STR(sta->addr)); + wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth, + sta->sae->pmkid, sta->sae->pmk); + sae_accept_sta(hapd, sta); + sta->mesh_sae_pmksa_caching = 1; + return 0; + } + sta->mesh_sae_pmksa_caching = 0; + + if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta)) return -1; wpa_msg(wpa_s, MSG_DEBUG, "AUTH: started authentication with SAE peer: " MACSTR, MAC2STR(sta->addr)); - sta->sae->state = SAE_COMMITTED; - wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); - - mesh_rsn_send_auth(wpa_s, sta->addr, wpa_s->own_addr, - 1, WLAN_STATUS_SUCCESS, buf); - - wpabuf_free(buf); + ret = auth_sae_init_committed(hapd, sta); + if (ret) + return ret; + eloop_cancel_timeout(mesh_auth_timer, wpa_s, sta); + rnd = rand() % MESH_AUTH_TIMEOUT; + eloop_register_timeout(MESH_AUTH_TIMEOUT + rnd, 0, mesh_auth_timer, + wpa_s, sta); return 0; } void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid) { - /* don't expect wpa auth to cache the pmkid for now */ - rsn_pmkid(sta->sae->pmk, PMK_LEN, rsn->wpa_s->own_addr, - sta->addr, pmkid, - wpa_key_mgmt_sha256(wpa_auth_sta_key_mgmt(sta->wpa_sm))); + os_memcpy(pmkid, sta->sae->pmkid, SAE_PMKID_LEN); } @@ -337,18 +386,27 @@ mesh_rsn_derive_aek(struct mesh_rsn *rsn, struct sta_info *sta) { u8 *myaddr = rsn->wpa_s->own_addr; u8 *peer = sta->addr; - u8 *addr1 = peer, *addr2 = myaddr; - u8 context[AES_BLOCK_SIZE]; + u8 *addr1, *addr2; + u8 context[RSN_SELECTOR_LEN + 2 * ETH_ALEN], *ptr = context; - /* SAE */ - RSN_SELECTOR_PUT(context, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP)); + /* + * AEK = KDF-Hash-256(PMK, "AEK Derivation", Selected AKM Suite || + * min(localMAC, peerMAC) || max(localMAC, peerMAC)) + */ + /* Selected AKM Suite: SAE */ + RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE); + ptr += RSN_SELECTOR_LEN; if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { addr1 = myaddr; addr2 = peer; + } else { + addr1 = peer; + addr2 = myaddr; } - os_memcpy(context + 4, addr1, ETH_ALEN); - os_memcpy(context + 10, addr2, ETH_ALEN); + os_memcpy(ptr, addr1, ETH_ALEN); + ptr += ETH_ALEN; + os_memcpy(ptr, addr2, ETH_ALEN); sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation", context, sizeof(context), sta->aek, sizeof(sta->aek)); @@ -360,40 +418,44 @@ int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta) { u8 *ptr; u8 *min, *max; - u16 min_lid, max_lid; - size_t nonce_len = sizeof(sta->my_nonce); - size_t lid_len = sizeof(sta->my_lid); u8 *myaddr = wpa_s->own_addr; u8 *peer = sta->addr; - /* 2 nonces, 2 linkids, akm suite, 2 mac addrs */ - u8 context[64 + 4 + 4 + 12]; - + u8 context[2 * WPA_NONCE_LEN + 2 * 2 + RSN_SELECTOR_LEN + 2 * ETH_ALEN]; + + /* + * MTK = KDF-Hash-Length(PMK, "Temporal Key Derivation", min(localNonce, + * peerNonce) || max(localNonce, peerNonce) || min(localLinkID, + * peerLinkID) || max(localLinkID, peerLinkID) || Selected AKM Suite || + * min(localMAC, peerMAC) || max(localMAC, peerMAC)) + */ ptr = context; - if (os_memcmp(sta->my_nonce, sta->peer_nonce, nonce_len) < 0) { + if (os_memcmp(sta->my_nonce, sta->peer_nonce, WPA_NONCE_LEN) < 0) { min = sta->my_nonce; max = sta->peer_nonce; } else { min = sta->peer_nonce; max = sta->my_nonce; } - os_memcpy(ptr, min, nonce_len); - os_memcpy(ptr + nonce_len, max, nonce_len); - ptr += 2 * nonce_len; + os_memcpy(ptr, min, WPA_NONCE_LEN); + ptr += WPA_NONCE_LEN; + os_memcpy(ptr, max, WPA_NONCE_LEN); + ptr += WPA_NONCE_LEN; if (sta->my_lid < sta->peer_lid) { - min_lid = host_to_le16(sta->my_lid); - max_lid = host_to_le16(sta->peer_lid); + WPA_PUT_LE16(ptr, sta->my_lid); + ptr += 2; + WPA_PUT_LE16(ptr, sta->peer_lid); + ptr += 2; } else { - min_lid = host_to_le16(sta->peer_lid); - max_lid = host_to_le16(sta->my_lid); + WPA_PUT_LE16(ptr, sta->peer_lid); + ptr += 2; + WPA_PUT_LE16(ptr, sta->my_lid); + ptr += 2; } - os_memcpy(ptr, &min_lid, lid_len); - os_memcpy(ptr + lid_len, &max_lid, lid_len); - ptr += 2 * lid_len; - /* SAE */ - RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP)); - ptr += 4; + /* Selected AKM Suite: SAE */ + RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE); + ptr += RSN_SELECTOR_LEN; if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { min = myaddr; @@ -403,22 +465,24 @@ int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta) max = myaddr; } os_memcpy(ptr, min, ETH_ALEN); - os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN); + ptr += ETH_ALEN; + os_memcpy(ptr, max, ETH_ALEN); - sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), + sta->mtk_len = wpa_cipher_key_len(wpa_s->mesh_rsn->pairwise_cipher); + sha256_prf(sta->sae->pmk, SAE_PMK_LEN, "Temporal Key Derivation", context, sizeof(context), - sta->mtk, sizeof(sta->mtk)); + sta->mtk, sta->mtk_len); return 0; } void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta) { - if (random_get_bytes(sta->my_nonce, 32) < 0) { + if (random_get_bytes(sta->my_nonce, WPA_NONCE_LEN) < 0) { wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce"); /* TODO: How to handle this more cleanly? */ } - os_memset(sta->peer_nonce, 0, 32); + os_memset(sta->peer_nonce, 0, WPA_NONCE_LEN); mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta); } @@ -434,65 +498,94 @@ int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, { struct ieee80211_ampe_ie *ampe; u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf); - u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload; + u8 *ampe_ie, *pos, *mic_payload; const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat }; const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat }; int ret = 0; + size_t len; - if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) { + len = sizeof(*ampe); + if (cat[1] == PLINK_OPEN) + len += rsn->mgtk_len + WPA_KEY_RSC_LEN + 4; +#ifdef CONFIG_IEEE80211W + if (cat[1] == PLINK_OPEN && rsn->igtk_len) + len += 2 + 6 + rsn->igtk_len; +#endif /* CONFIG_IEEE80211W */ + + if (2 + AES_BLOCK_SIZE + 2 + len > wpabuf_tailroom(buf)) { wpa_printf(MSG_ERROR, "protect frame: buffer too small"); return -EINVAL; } - ampe_ie = os_zalloc(2 + sizeof(*ampe)); + ampe_ie = os_zalloc(2 + len); if (!ampe_ie) { wpa_printf(MSG_ERROR, "protect frame: out of memory"); return -ENOMEM; } - mic_ie = os_zalloc(2 + AES_BLOCK_SIZE); - if (!mic_ie) { - wpa_printf(MSG_ERROR, "protect frame: out of memory"); - ret = -ENOMEM; - goto free; - } - /* IE: AMPE */ ampe_ie[0] = WLAN_EID_AMPE; - ampe_ie[1] = sizeof(*ampe); + ampe_ie[1] = len; ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2); RSN_SELECTOR_PUT(ampe->selected_pairwise_suite, - wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP)); - os_memcpy(ampe->local_nonce, sta->my_nonce, 32); - os_memcpy(ampe->peer_nonce, sta->peer_nonce, 32); - /* incomplete: see 13.5.4 */ + RSN_CIPHER_SUITE_CCMP); + os_memcpy(ampe->local_nonce, sta->my_nonce, WPA_NONCE_LEN); + os_memcpy(ampe->peer_nonce, sta->peer_nonce, WPA_NONCE_LEN); + + pos = (u8 *) (ampe + 1); + if (cat[1] != PLINK_OPEN) + goto skip_keys; + + /* TODO: Key Replay Counter[8] optionally for + * Mesh Group Key Inform/Acknowledge frames */ + /* TODO: static mgtk for now since we don't support rekeying! */ - os_memcpy(ampe->mgtk, rsn->mgtk, 16); - /* TODO: Populate Key RSC */ - /* expire in 13 decades or so */ - os_memset(ampe->key_expiration, 0xff, 4); + /* + * GTKdata[variable]: + * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4] + */ + os_memcpy(pos, rsn->mgtk, rsn->mgtk_len); + pos += rsn->mgtk_len; + wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->mgtk_key_id, pos); + pos += WPA_KEY_RSC_LEN; + /* Use fixed GTKExpirationTime for now */ + WPA_PUT_LE32(pos, 0xffffffff); + pos += 4; + +#ifdef CONFIG_IEEE80211W + /* + * IGTKdata[variable]: + * Key ID[2], IPN[6], IGTK[variable] + */ + if (rsn->igtk_len) { + WPA_PUT_LE16(pos, rsn->igtk_key_id); + pos += 2; + wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->igtk_key_id, pos); + pos += 6; + os_memcpy(pos, rsn->igtk, rsn->igtk_len); + } +#endif /* CONFIG_IEEE80211W */ + +skip_keys: + wpa_hexdump_key(MSG_DEBUG, "mesh: Plaintext AMPE element", + ampe_ie, 2 + len); /* IE: MIC */ - mic_ie[0] = WLAN_EID_MIC; - mic_ie[1] = AES_BLOCK_SIZE; - wpabuf_put_data(buf, mic_ie, 2); + wpabuf_put_u8(buf, WLAN_EID_MIC); + wpabuf_put_u8(buf, AES_BLOCK_SIZE); /* MIC field is output ciphertext */ /* encrypt after MIC */ - mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) + - AES_BLOCK_SIZE); + mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE); - if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3, + if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + len, 3, aad, aad_len, mic_payload)) { wpa_printf(MSG_ERROR, "protect frame: failed to encrypt"); ret = -ENOMEM; - goto free; } -free: os_free(ampe_ie); - os_free(mic_ie); return ret; } @@ -500,18 +593,37 @@ free: int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, struct ieee802_11_elems *elems, const u8 *cat, + const u8 *chosen_pmk, const u8 *start, size_t elems_len) { int ret = 0; struct ieee80211_ampe_ie *ampe; - u8 null_nonce[32] = {}; + u8 null_nonce[WPA_NONCE_LEN] = {}; u8 ampe_eid; u8 ampe_ie_len; - u8 *ampe_buf, *crypt = NULL; + u8 *ampe_buf, *crypt = NULL, *pos, *end; size_t crypt_len; const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat }; const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, (elems->mic - 2) - cat }; + size_t key_len; + + if (!sta->sae) { + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) { + wpa_printf(MSG_INFO, + "Mesh RSN: SAE is not prepared yet"); + return -1; + } + mesh_rsn_auth_sae_sta(wpa_s, sta); + } + + if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) { + wpa_msg(wpa_s, MSG_DEBUG, + "Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)"); + return -1; + } if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie"); @@ -523,7 +635,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, return -1; crypt_len = elems_len - (elems->mic - start); - if (crypt_len < 2) { + if (crypt_len < 2 + AES_BLOCK_SIZE) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie"); return -1; } @@ -541,14 +653,19 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3, aad, aad_len, ampe_buf)) { wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!"); - ret = -1; + ret = -2; goto free; } + crypt_len -= AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "mesh: Decrypted AMPE element", + ampe_buf, crypt_len); + ampe_eid = *ampe_buf++; ampe_ie_len = *ampe_buf++; if (ampe_eid != WLAN_EID_AMPE || + (size_t) 2 + ampe_ie_len > crypt_len || ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie"); ret = -1; @@ -556,17 +673,89 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, } ampe = (struct ieee80211_ampe_ie *) ampe_buf; - if (os_memcmp(ampe->peer_nonce, null_nonce, 32) != 0 && - os_memcmp(ampe->peer_nonce, sta->my_nonce, 32) != 0) { + pos = (u8 *) (ampe + 1); + end = ampe_buf + ampe_ie_len; + if (os_memcmp(ampe->peer_nonce, null_nonce, WPA_NONCE_LEN) != 0 && + os_memcmp(ampe->peer_nonce, sta->my_nonce, WPA_NONCE_LEN) != 0) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce"); ret = -1; goto free; } os_memcpy(sta->peer_nonce, ampe->local_nonce, sizeof(ampe->local_nonce)); - os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk)); - /* todo parse mgtk expiration */ + /* TODO: Key Replay Counter[8] in Mesh Group Key Inform/Acknowledge + * frames */ + + /* + * GTKdata shall not be included in Mesh Peering Confirm. While the + * standard does not state the same about IGTKdata, that same constraint + * needs to apply for it. It makes no sense to include the keys in Mesh + * Peering Close frames either, so while the standard does not seem to + * have a shall statement for these, they are described without + * mentioning GTKdata. + * + * An earlier implementation used to add GTKdata to both Mesh Peering + * Open and Mesh Peering Confirm frames, so ignore the possibly present + * GTKdata frame without rejecting the frame as a backwards + * compatibility mechanism. + */ + if (cat[1] != PLINK_OPEN) { + if (end > pos) { + wpa_hexdump_key(MSG_DEBUG, + "mesh: Ignore unexpected GTKdata(etc.) fields in the end of AMPE element in Mesh Peering Confirm/Close", + pos, end - pos); + } + goto free; + } + + /* + * GTKdata[variable]: + * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4] + */ + sta->mgtk_key_id = 1; /* FIX: Where to get Key ID? */ + key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->group_cipher); + if ((int) key_len + WPA_KEY_RSC_LEN + 4 > end - pos) { + wpa_dbg(wpa_s, MSG_DEBUG, "mesh: Truncated AMPE element"); + ret = -1; + goto free; + } + sta->mgtk_len = key_len; + os_memcpy(sta->mgtk, pos, sta->mgtk_len); + wpa_hexdump_key(MSG_DEBUG, "mesh: GTKdata - MGTK", + sta->mgtk, sta->mgtk_len); + pos += sta->mgtk_len; + wpa_hexdump(MSG_DEBUG, "mesh: GTKdata - MGTK - Key RSC", + pos, WPA_KEY_RSC_LEN); + os_memcpy(sta->mgtk_rsc, pos, sizeof(sta->mgtk_rsc)); + pos += WPA_KEY_RSC_LEN; + wpa_printf(MSG_DEBUG, + "mesh: GTKdata - MGTK - GTKExpirationTime: %u seconds", + WPA_GET_LE32(pos)); + pos += 4; + +#ifdef CONFIG_IEEE80211W + /* + * IGTKdata[variable]: + * Key ID[2], IPN[6], IGTK[variable] + */ + key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->mgmt_group_cipher); + if (end - pos >= (int) (2 + 6 + key_len)) { + sta->igtk_key_id = WPA_GET_LE16(pos); + wpa_printf(MSG_DEBUG, "mesh: IGTKdata - Key ID %u", + sta->igtk_key_id); + pos += 2; + os_memcpy(sta->igtk_rsc, pos, sizeof(sta->igtk_rsc)); + wpa_hexdump(MSG_DEBUG, "mesh: IGTKdata - IPN", + sta->igtk_rsc, sizeof(sta->igtk_rsc)); + pos += 6; + os_memcpy(sta->igtk, pos, key_len); + sta->igtk_len = key_len; + wpa_hexdump_key(MSG_DEBUG, "mesh: IGTKdata - IGTK", + sta->igtk, sta->igtk_len); + } +#endif /* CONFIG_IEEE80211W */ + free: os_free(crypt); return ret;