static int wpa_supplicant_process_smk_error( struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, size_t extra_len) { struct wpa_eapol_ie_parse kde; struct rsn_error_kde error; u8 peer[ETH_ALEN]; u16 error_type; wpa_printf(MSG_DEBUG, "RSN: Received SMK Error"); if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " "the current network"); return -1; } if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); return -1; } if (kde.error == NULL || kde.error_len < sizeof(error)) { wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error"); return -1; } if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN) os_memcpy(peer, kde.mac_addr, ETH_ALEN); else os_memset(peer, 0, ETH_ALEN); os_memcpy(&error, kde.error, sizeof(error)); error_type = be_to_host16(error.error_type); wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: SMK Error KDE received: MUI %d error_type %d peer " MACSTR, be_to_host16(error.mui), error_type, MAC2STR(peer)); if (kde.mac_addr && (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN || error_type == STK_ERR_CPHR_NS)) { struct wpa_peerkey *peerkey; for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0) break; } if (peerkey == NULL) { wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake " "found for SMK Error"); return -1; } /* TODO: abort SMK/STK handshake and remove all related keys */ } return 0; }
static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, u16 ver) { int i; struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; u8 buf[8]; lwip_log("WPA: RX message 1 of 4-Way Handshake from " MACSTR " (ver=%d)\n", MAC2STR(src_addr), ver); os_memset(&ie, 0, sizeof(ie)); if (sm->proto == WPA_PROTO_RSN) { /* RSN: msg 1/4 should contain PMKID for the selected PMK */ const u8 *_buf = (const u8 *) (key + 1); size_t len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len); wpa_supplicant_parse_ies(_buf, len, &ie); if (ie.pmkid) { wpa_hexdump(MSG_DEBUG, "RSN: PMKID from Authenticator", ie.pmkid, PMKID_LEN); } } if (sm->renew_snonce) { if (hostapd_get_rand(sm->snonce, WPA_NONCE_LEN)) { lwip_log("WPA: Failed to get random data for SNonce\n"); return; } sm->renew_snonce = 0; wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", sm->snonce, WPA_NONCE_LEN); } /* Calculate PTK which will be stored as a temporary PTK until it has * been verified when processing message 3/4. */ ptk = &sm->tptk; wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, (u8 *)ptk, sizeof(*ptk)); /* Supplicant: swap tx/rx Mic keys */ os_memcpy(buf, ptk->u.auth.tx_mic_key, 8); os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8); os_memcpy(ptk->u.auth.rx_mic_key, buf, 8); sm->tptk_set = 1; os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, ptk)) { return; } }
static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey, const struct wpa_eapol_key *key, u16 ver) { struct wpa_eapol_ie_parse kde; const u8 *keydata; size_t len; wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); os_memset(&kde, 0, sizeof(kde)); /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE * from the peer. It may also include Lifetime KDE. */ keydata = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len); if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 || kde.pmkid == NULL || kde.rsn_ie == NULL) { wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4"); return; } if (os_memcmp(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) { wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4", kde.pmkid, PMKID_LEN); return; } if (kde.rsn_ie_len != peerkey->rsnie_p_len || os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) { wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK " "handshakes did not match"); wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake", peerkey->rsnie_p, peerkey->rsnie_p_len); wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake", kde.rsn_ie, kde.rsn_ie_len); return; } wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); wpa_supplicant_send_stk_3_of_4(sm, peerkey); os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN); }
static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, const u8 *keydata, size_t keydatalen, u16 key_info, struct wpa_gtk_data *gd) { int maxkeylen; struct wpa_eapol_ie_parse ie; wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); wpa_supplicant_parse_ies(keydata, keydatalen, &ie); if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); return -1; } if (ie.gtk == NULL) { wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2"); return -1; } maxkeylen = gd->gtk_len = ie.gtk_len - 2; if (wpa_supplicant_check_group_cipher(sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake", ie.gtk, ie.gtk_len); gd->keyidx = ie.gtk[0] & 0x3; gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(ie.gtk[0] & BIT(2))); if (ie.gtk_len - 2 > sizeof(gd->gtk)) { wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE " "(len=%lu)", (unsigned long) ie.gtk_len - 2); return -1; } os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2); if (ieee80211w_set_keys(sm, &ie) < 0) wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK"); return 0; }
static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey, const struct wpa_eapol_key *key, u16 ver) { struct wpa_eapol_ie_parse kde; const u8 *keydata; size_t len, key_len; const u8 *_key; u8 key_buf[32], rsc[6]; wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); os_memset(&kde, 0, sizeof(kde)); /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include * Lifetime KDE. */ keydata = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len); if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) { wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in " "STK 3/4"); return; } if (kde.rsn_ie_len != peerkey->rsnie_i_len || os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) { wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK " "handshakes did not match"); wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK " "handshake", peerkey->rsnie_i, peerkey->rsnie_i_len); wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK " "handshake", kde.rsn_ie, kde.rsn_ie_len); return; } if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK " "4-Way Handshake differs from 3 of STK 4-Way " "Handshake - drop packet (src=" MACSTR ")", MAC2STR(peerkey->addr)); return; } wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver, WPA_GET_BE16(key->key_info), NULL, 0, &peerkey->stk)) return; _key = (u8 *) peerkey->stk.tk1; if (peerkey->cipher == WPA_CIPHER_TKIP) { /* Swap Tx/Rx keys for Michael MIC */ os_memcpy(key_buf, _key, 16); os_memcpy(key_buf + 16, peerkey->stk.u.auth.rx_mic_key, 8); os_memcpy(key_buf + 24, peerkey->stk.u.auth.tx_mic_key, 8); _key = key_buf; key_len = 32; } else key_len = 16; os_memset(rsc, 0, 6); if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, rsc, sizeof(rsc), _key, key_len) < 0) { wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " "driver."); return; } }
static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey, const struct wpa_eapol_key *key, u16 ver) { struct wpa_eapol_ie_parse ie; const u8 *kde; size_t len, kde_buf_len; struct wpa_ptk *stk; u8 buf[8], *kde_buf, *pos; be32 lifetime; wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); os_memset(&ie, 0, sizeof(ie)); /* RSN: msg 1/4 should contain SMKID for the selected SMK */ kde = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len); if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) { wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4"); return; } if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) { wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4", ie.pmkid, PMKID_LEN); return; } if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for PNonce"); return; } wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce", peerkey->pnonce, WPA_NONCE_LEN); /* Calculate STK which will be stored as a temporary STK until it has * been verified when processing message 3/4. */ stk = &peerkey->tstk; wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", sm->own_addr, peerkey->addr, peerkey->pnonce, key->key_nonce, (u8 *) stk, sizeof(*stk), peerkey->use_sha256); /* Supplicant: swap tx/rx Mic keys */ os_memcpy(buf, stk->u.auth.tx_mic_key, 8); os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8); os_memcpy(stk->u.auth.rx_mic_key, buf, 8); peerkey->tstk_set = 1; kde_buf_len = peerkey->rsnie_p_len + 2 + RSN_SELECTOR_LEN + sizeof(lifetime) + 2 + RSN_SELECTOR_LEN + PMKID_LEN; kde_buf = os_malloc(kde_buf_len); if (kde_buf == NULL) return; pos = kde_buf; pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); lifetime = host_to_be32(peerkey->lifetime); pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, (u8 *) &lifetime, sizeof(lifetime)); wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver, peerkey->pnonce, kde_buf, kde_buf_len, stk)) { os_free(kde_buf); return; } os_free(kde_buf); os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); }
static int wpa_supplicant_process_smk_m45( struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, size_t extra_len, int ver) { struct wpa_peerkey *peerkey; struct wpa_eapol_ie_parse kde; u32 lifetime; struct os_time now; if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " "the current network"); return -1; } if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5"); return -1; } if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN || kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN || kde.lifetime == NULL || kde.lifetime_len < 4) { wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or " "Lifetime KDE in SMK M4/M5"); return -1; } for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 && os_memcmp(peerkey->initiator ? peerkey->inonce : peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN) == 0) break; } if (peerkey == NULL) { wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found " "for SMK M4/M5: peer " MACSTR, MAC2STR(kde.mac_addr)); return -1; } if (peerkey->initiator) { if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver, peerkey, &kde) < 0) return -1; } else { if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0) return -1; } os_memcpy(peerkey->smk, kde.smk, PMK_LEN); peerkey->smk_complete = 1; wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN); lifetime = WPA_GET_BE32(kde.lifetime); wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime); if (lifetime > 1000000000) lifetime = 1000000000; /* avoid overflowing expiration time */ peerkey->lifetime = lifetime; os_get_time(&now); peerkey->expiration = now.sec + lifetime; eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, sm, peerkey); if (peerkey->initiator) { rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr, peerkey->inonce, sm->own_addr, peerkey->smkid, peerkey->use_sha256); wpa_supplicant_send_stk_1_of_4(sm, peerkey); } else { rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr, peerkey->inonce, peerkey->addr, peerkey->smkid, peerkey->use_sha256); } wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN); return 0; }
static int wpa_supplicant_process_smk_m2( struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, size_t extra_len, int ver) { struct wpa_peerkey *peerkey; struct wpa_eapol_ie_parse kde; struct wpa_ie_data ie; int cipher; struct rsn_ie_hdr *hdr; u8 *pos; wpa_printf(MSG_DEBUG, "RSN: Received SMK M2"); if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for " "the current network"); return -1; } if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2"); return -1; } if (kde.rsn_ie == NULL || kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN) { wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " "SMK M2"); return -1; } wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR, MAC2STR(kde.mac_addr)); if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) { wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK " "M2"); return -1; } if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2"); return -1; } cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; if (cipher & WPA_CIPHER_CCMP) { wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); cipher = WPA_CIPHER_CCMP; } else if (cipher & WPA_CIPHER_TKIP) { wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); cipher = WPA_CIPHER_TKIP; } else { wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2"); wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr, STK_MUI_SMK, STK_ERR_CPHR_NS, ver); return -1; } /* TODO: find existing entry and if found, use that instead of adding * a new one; how to handle the case where both ends initiate at the * same time? */ peerkey = os_zalloc(sizeof(*peerkey)); if (peerkey == NULL) return -1; os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN); os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); peerkey->rsnie_i_len = kde.rsn_ie_len; peerkey->cipher = cipher; #ifdef CONFIG_IEEE80211W if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 | WPA_KEY_MGMT_PSK_SHA256)) peerkey->use_sha256 = 1; #endif /* CONFIG_IEEE80211W */ if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for PNonce"); wpa_supplicant_peerkey_free(sm, peerkey); return -1; } hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); /* Group Suite can be anything for SMK RSN IE; receiver will just * ignore it. */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; /* Include only the selected cipher in pairwise cipher suite */ WPA_PUT_LE16(pos, 1); pos += 2; if (cipher == WPA_CIPHER_CCMP) RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); else if (cipher == WPA_CIPHER_TKIP) RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); pos += RSN_SELECTOR_LEN; hdr->len = (pos - peerkey->rsnie_p) - 2; peerkey->rsnie_p_len = pos - peerkey->rsnie_p; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", peerkey->rsnie_p, peerkey->rsnie_p_len); wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey); peerkey->next = sm->peerkey; sm->peerkey = peerkey; return 0; }
static void rx_data_eapol_key_3_of_4(struct wlantest *wt, const u8 *dst, const u8 *src, const u8 *data, size_t len) { struct wlantest_bss *bss; struct wlantest_sta *sta; const struct ieee802_1x_hdr *eapol; const struct wpa_eapol_key *hdr; const u8 *key_data, *kck; int recalc = 0; u16 key_info, ver; u8 *decrypted_buf = NULL; const u8 *decrypted; size_t decrypted_len = 0; struct wpa_eapol_ie_parse ie; wpa_printf(MSG_DEBUG, "EAPOL-Key 3/4 " MACSTR " -> " MACSTR, MAC2STR(src), MAC2STR(dst)); bss = bss_get(wt, src); if (bss == NULL) return; sta = sta_get(bss, dst); if (sta == NULL) return; eapol = (const struct ieee802_1x_hdr *) data; hdr = (const struct wpa_eapol_key *) (eapol + 1); key_info = WPA_GET_BE16(hdr->key_info); if (os_memcmp(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "EAPOL-Key ANonce mismatch between 1/4 " "and 3/4"); recalc = 1; } os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN); if (recalc) { derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK, data, len); } if (!sta->ptk_set && !sta->tptk_set) { wpa_printf(MSG_DEBUG, "No PTK known to process EAPOL-Key 3/4"); return; } kck = sta->ptk.kck; if (sta->tptk_set) { wpa_printf(MSG_DEBUG, "Use TPTK for validation EAPOL-Key MIC"); kck = sta->tptk.kck; } if (check_mic(kck, key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) { wpa_printf(MSG_INFO, "Mismatch in EAPOL-Key 3/4 MIC"); return; } wpa_printf(MSG_DEBUG, "Valid MIC found in EAPOL-Key 3/4"); key_data = (const u8 *) (hdr + 1); if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { if (sta->proto & WPA_PROTO_RSN) wpa_printf(MSG_INFO, "EAPOL-Key 3/4 without " "EncrKeyData bit"); decrypted = key_data; decrypted_len = WPA_GET_BE16(hdr->key_data_length); } else { ver = key_info & WPA_KEY_INFO_TYPE_MASK; decrypted_buf = decrypt_eapol_key_data(sta->ptk.kek, ver, hdr, &decrypted_len); if (decrypted_buf == NULL) { wpa_printf(MSG_INFO, "Failed to decrypt EAPOL-Key Key " "Data"); return; } decrypted = decrypted_buf; wpa_hexdump(MSG_DEBUG, "Decrypted EAPOL-Key Key Data", decrypted, decrypted_len); } if (wt->write_pcap_dumper && decrypted != key_data) { /* Fill in a dummy Data frame header */ u8 buf[24 + 8 + sizeof(*eapol) + sizeof(*hdr)]; struct ieee80211_hdr *h; struct wpa_eapol_key *k; const u8 *p; u8 *pos; size_t plain_len; plain_len = decrypted_len; p = decrypted; while (p + 1 < decrypted + decrypted_len) { if (p[0] == 0xdd && p[1] == 0x00) { /* Remove padding */ plain_len = p - decrypted; break; } p += 2 + p[1]; } os_memset(buf, 0, sizeof(buf)); h = (struct ieee80211_hdr *) buf; h->frame_control = host_to_le16(0x0208); os_memcpy(h->addr1, dst, ETH_ALEN); os_memcpy(h->addr2, src, ETH_ALEN); os_memcpy(h->addr3, src, ETH_ALEN); pos = (u8 *) (h + 1); os_memcpy(pos, "\xaa\xaa\x03\x00\x00\x00\x88\x8e", 8); pos += 8; os_memcpy(pos, eapol, sizeof(*eapol)); pos += sizeof(*eapol); os_memcpy(pos, hdr, sizeof(*hdr)); k = (struct wpa_eapol_key *) pos; WPA_PUT_BE16(k->key_info, key_info & ~WPA_KEY_INFO_ENCR_KEY_DATA); WPA_PUT_BE16(k->key_data_length, plain_len); write_pcap_decrypted(wt, buf, sizeof(buf), decrypted, plain_len); } if (wpa_supplicant_parse_ies(decrypted, decrypted_len, &ie) < 0) { wpa_printf(MSG_INFO, "Failed to parse EAPOL-Key Key Data"); os_free(decrypted_buf); return; } if ((ie.wpa_ie && os_memcmp(ie.wpa_ie, bss->wpaie, ie.wpa_ie_len) != 0) || (ie.wpa_ie == NULL && bss->wpaie[0])) { wpa_printf(MSG_INFO, "Mismatch in WPA IE between " "EAPOL-Key 3/4 and Beacon/Probe Response " "from " MACSTR, MAC2STR(bss->bssid)); wpa_hexdump(MSG_INFO, "WPA IE in EAPOL-Key", ie.wpa_ie, ie.wpa_ie_len); wpa_hexdump(MSG_INFO, "WPA IE in Beacon/Probe " "Response", bss->wpaie, bss->wpaie[0] ? 2 + bss->wpaie[1] : 0); } if ((ie.rsn_ie && os_memcmp(ie.rsn_ie, bss->rsnie, ie.rsn_ie_len) != 0) || (ie.rsn_ie == NULL && bss->rsnie[0])) { wpa_printf(MSG_INFO, "Mismatch in RSN IE between " "EAPOL-Key 3/4 and Beacon/Probe Response " "from " MACSTR, MAC2STR(bss->bssid)); wpa_hexdump(MSG_INFO, "RSN IE in EAPOL-Key", ie.rsn_ie, ie.rsn_ie_len); wpa_hexdump(MSG_INFO, "RSN IE in (Re)Association " "Request", bss->rsnie, bss->rsnie[0] ? 2 + bss->rsnie[1] : 0); } learn_kde_keys(bss, decrypted, decrypted_len, hdr->key_rsc); os_free(decrypted_buf); }
static void learn_kde_keys(struct wlantest_bss *bss, const u8 *buf, size_t len, const u8 *rsc) { struct wpa_eapol_ie_parse ie; if (wpa_supplicant_parse_ies(buf, len, &ie) < 0) { wpa_printf(MSG_INFO, "Failed to parse EAPOL-Key Key Data"); return; } if (ie.wpa_ie) { wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - WPA IE", ie.wpa_ie, ie.wpa_ie_len); } if (ie.rsn_ie) { wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - RSN IE", ie.rsn_ie, ie.rsn_ie_len); } if (ie.gtk) { wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - GTK KDE", ie.gtk, ie.gtk_len); if (ie.gtk_len >= 2 && ie.gtk_len <= 2 + 32) { int id; id = ie.gtk[0] & 0x03; wpa_printf(MSG_DEBUG, "GTK KeyID=%u tx=%u", id, !!(ie.gtk[0] & 0x04)); if ((ie.gtk[0] & 0xf8) || ie.gtk[1]) wpa_printf(MSG_INFO, "GTK KDE: Reserved field " "set: %02x %02x", ie.gtk[0], ie.gtk[1]); wpa_hexdump(MSG_DEBUG, "GTK", ie.gtk + 2, ie.gtk_len - 2); bss->gtk_len[id] = ie.gtk_len - 2; os_memcpy(bss->gtk[id], ie.gtk + 2, ie.gtk_len - 2); bss->rsc[id][0] = rsc[5]; bss->rsc[id][1] = rsc[4]; bss->rsc[id][2] = rsc[3]; bss->rsc[id][3] = rsc[2]; bss->rsc[id][4] = rsc[1]; bss->rsc[id][5] = rsc[0]; bss->gtk_idx = id; wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[id], 6); } else { wpa_printf(MSG_INFO, "Invalid GTK KDE length %u", (unsigned) ie.gtk_len); } } if (ie.igtk) { wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - IGTK KDE", ie.igtk, ie.igtk_len); if (ie.igtk_len == 24) { u16 id; id = WPA_GET_LE16(ie.igtk); if (id > 5) { wpa_printf(MSG_INFO, "Unexpected IGTK KeyID " "%u", id); } else { const u8 *ipn; wpa_printf(MSG_DEBUG, "IGTK KeyID %u", id); wpa_hexdump(MSG_DEBUG, "IPN", ie.igtk + 2, 6); wpa_hexdump(MSG_DEBUG, "IGTK", ie.igtk + 8, 16); os_memcpy(bss->igtk[id], ie.igtk + 8, 16); bss->igtk_set[id] = 1; ipn = ie.igtk + 2; bss->ipn[id][0] = ipn[5]; bss->ipn[id][1] = ipn[4]; bss->ipn[id][2] = ipn[3]; bss->ipn[id][3] = ipn[2]; bss->ipn[id][4] = ipn[1]; bss->ipn[id][5] = ipn[0]; bss->igtk_idx = id; } } else { wpa_printf(MSG_INFO, "Invalid IGTK KDE length %u", (unsigned) ie.igtk_len); } } }
static void rx_data_eapol_key_2_of_4(struct wlantest *wt, const u8 *dst, const u8 *src, const u8 *data, size_t len) { struct wlantest_bss *bss; struct wlantest_sta *sta; const struct ieee802_1x_hdr *eapol; const struct wpa_eapol_key *hdr; const u8 *key_data, *kck; u16 key_info, key_data_len; struct wpa_eapol_ie_parse ie; wpa_printf(MSG_DEBUG, "EAPOL-Key 2/4 " MACSTR " -> " MACSTR, MAC2STR(src), MAC2STR(dst)); bss = bss_get(wt, dst); if (bss == NULL) return; sta = sta_get(bss, src); if (sta == NULL) return; eapol = (const struct ieee802_1x_hdr *) data; hdr = (const struct wpa_eapol_key *) (eapol + 1); if (is_zero(hdr->key_nonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "EAPOL-Key 2/4 from " MACSTR " used " "zero nonce", MAC2STR(src)); } if (!is_zero(hdr->key_rsc, 8)) { wpa_printf(MSG_INFO, "EAPOL-Key 2/4 from " MACSTR " used " "non-zero Key RSC", MAC2STR(src)); } os_memcpy(sta->snonce, hdr->key_nonce, WPA_NONCE_LEN); key_info = WPA_GET_BE16(hdr->key_info); key_data_len = WPA_GET_BE16(hdr->key_data_length); derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK, data, len); if (!sta->ptk_set && !sta->tptk_set) { wpa_printf(MSG_DEBUG, "No PTK known to process EAPOL-Key 2/4"); return; } kck = sta->ptk.kck; if (sta->tptk_set) { wpa_printf(MSG_DEBUG, "Use TPTK for validation EAPOL-Key MIC"); kck = sta->tptk.kck; } if (check_mic(kck, key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) { wpa_printf(MSG_INFO, "Mismatch in EAPOL-Key 2/4 MIC"); return; } wpa_printf(MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/4"); key_data = (const u8 *) (hdr + 1); if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) { wpa_printf(MSG_INFO, "Failed to parse EAPOL-Key Key Data"); return; } if (ie.wpa_ie) { wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - WPA IE", ie.wpa_ie, ie.wpa_ie_len); if (os_memcmp(ie.wpa_ie, sta->rsnie, ie.wpa_ie_len) != 0) { wpa_printf(MSG_INFO, "Mismatch in WPA IE between " "EAPOL-Key 2/4 and (Re)Association " "Request from " MACSTR, MAC2STR(sta->addr)); wpa_hexdump(MSG_INFO, "WPA IE in EAPOL-Key", ie.wpa_ie, ie.wpa_ie_len); wpa_hexdump(MSG_INFO, "WPA IE in (Re)Association " "Request", sta->rsnie, sta->rsnie[0] ? 2 + sta->rsnie[1] : 0); } } if (ie.rsn_ie) { wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - RSN IE", ie.rsn_ie, ie.rsn_ie_len); if (os_memcmp(ie.rsn_ie, sta->rsnie, ie.rsn_ie_len) != 0) { wpa_printf(MSG_INFO, "Mismatch in RSN IE between " "EAPOL-Key 2/4 and (Re)Association " "Request from " MACSTR, MAC2STR(sta->addr)); wpa_hexdump(MSG_INFO, "RSN IE in EAPOL-Key", ie.rsn_ie, ie.rsn_ie_len); wpa_hexdump(MSG_INFO, "RSN IE in (Re)Association " "Request", sta->rsnie, sta->rsnie[0] ? 2 + sta->rsnie[1] : 0); } } }
static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, u16 ver) { struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; u8 buf[8]; int res; if (wpa_sm_get_network_ctx(sm) == NULL) { wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of " "4)."); return; } wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); os_memset(&ie, 0, sizeof(ie)); #ifndef CONFIG_NO_WPA2 if (sm->proto == WPA_PROTO_RSN) { /* RSN: msg 1/4 should contain PMKID for the selected PMK */ const u8 *_buf = (const u8 *) (key + 1); size_t len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len); wpa_supplicant_parse_ies(_buf, len, &ie); if (ie.pmkid) { wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " "Authenticator", ie.pmkid, PMKID_LEN); } } #endif /* CONFIG_NO_WPA2 */ res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); if (res == -2) { wpa_printf(MSG_DEBUG, "RSN: Do not reply to msg 1/4 - " "requesting full EAP authentication"); return; } if (res) goto failed; if (sm->renew_snonce) { if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for SNonce"); goto failed; } sm->renew_snonce = 0; wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", sm->snonce, WPA_NONCE_LEN); } /* Calculate PTK which will be stored as a temporary PTK until it has * been verified when processing message 3/4. */ ptk = &sm->tptk; wpa_derive_ptk(sm, src_addr, key, ptk); /* Supplicant: swap tx/rx Mic keys */ os_memcpy(buf, ptk->u.auth.tx_mic_key, 8); os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8); os_memcpy(ptk->u.auth.rx_mic_key, buf, 8); sm->tptk_set = 1; if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, ptk)) goto failed; os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); return; failed: wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); }
static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, u16 ver) { u16 key_info, keylen, len; const u8 *pos; struct wpa_eapol_ie_parse ie; wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver); key_info = WPA_GET_BE16(key->key_info); pos = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); wpa_supplicant_parse_ies(pos, len, &ie); if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); goto failed; } #ifdef CONFIG_IEEE80211W if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_printf(MSG_WARNING, "WPA: IGTK KDE in unencrypted key " "data"); goto failed; } if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) { wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KDE length %lu", (unsigned long) ie.igtk_len); goto failed; } #endif /* CONFIG_IEEE80211W */ if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) goto failed; if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way " "Handshake differs from 3 of 4-Way Handshake - drop" " packet (src=" MACSTR ")", MAC2STR(sm->bssid)); goto failed; } keylen = WPA_GET_BE16(key->key_length); switch (sm->pairwise_cipher) { case WPA_CIPHER_CCMP: if (keylen != 16) { wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length " "%d (src=" MACSTR ")", keylen, MAC2STR(sm->bssid)); goto failed; } break; case WPA_CIPHER_TKIP: if (keylen != 32) { wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length " "%d (src=" MACSTR ")", keylen, MAC2STR(sm->bssid)); goto failed; } break; } if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, NULL, 0, &sm->ptk)) { goto failed; } /* SNonce was successfully used in msg 3/4, so mark it to be renewed * for the next 4-Way Handshake. If msg 3 is received again, the old * SNonce will still be used to avoid changing PTK. */ sm->renew_snonce = 1; if (key_info & WPA_KEY_INFO_INSTALL) { if (wpa_supplicant_install_ptk(sm, key)) goto failed; } if (key_info & WPA_KEY_INFO_SECURE) { wpa_sm_mlme_setprotection( sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); eapol_sm_notify_portValid(sm->eapol, TRUE); } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to configure GTK"); goto failed; } if (ieee80211w_set_keys(sm, &ie) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK"); goto failed; } return; failed: wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); }
static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, //size 95 u16 ver) { u16 key_info, keylen, len; const u8 *pos; struct wpa_eapol_ie_parse ie; lwip_log("WPA: RX message 3 of 4-Way Handshake from " MACSTR " (ver=%d)\n", MAC2STR(sm->bssid), ver); key_info = WPA_GET_BE16(key->key_info); pos = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); wpa_supplicant_parse_ies(pos, len, &ie); if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { lwip_log("WPA: GTK IE in unencrypted key data\n"); return; } /* if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) { return; } */ if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { lwip_log("WPA: ANonce from message 1 of 4-Way Handshake differs from 3 of 4-Way Handshake - drop" " packet (src=" MACSTR ")\n", MAC2STR(sm->bssid)); return; } keylen = WPA_GET_BE16(key->key_length); switch (sm->pairwise_cipher) { case WPA_CIPHER_CCMP: if (keylen != 16) { lwip_log("WPA: Invalid CCMP key length %d (src=" MACSTR ")\n", keylen, MAC2STR(sm->bssid)); return; } break; case WPA_CIPHER_TKIP: if (keylen != 32) { lwip_log("WPA: Invalid TKIP key length %d (src=" MACSTR ")\n", keylen, MAC2STR(sm->bssid)); return; } break; } if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, NULL, 0, &sm->ptk)) { return; } /* SNonce was successfully used in msg 3/4, so mark it to be renewed * for the next 4-Way Handshake. If msg 3 is received again, the old * SNonce will still be used to avoid changing PTK. */ sm->renew_snonce = 1; if (key_info & WPA_KEY_INFO_INSTALL) { wpa_supplicant_install_ptk(sm, key); } if (key_info & WPA_KEY_INFO_SECURE) { // wpa_sm_mlme_setprotection( // sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX, // MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); // eapol_sm_notify_portValid(sm->eapol, TRUE); } // wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { lwip_log("RSN: Failed to configure GTK\n"); } #if 1 MSG_SET_WPA_SH(); #else if (ieee80211w_set_keys(sm, &ie) < 0) { lwip_log("RSN: Failed to configure DHV/IGTK\n"); } else { MSG_SET_WPA_SH(); } #endif }