📄 wpa_ft.c
字号:
/* * hostapd - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */#include "includes.h"#include "common.h"#include "config.h"#include "wpa.h"#include "aes_wrap.h"#include "ieee802_11.h"#include "defs.h"#include "wpa_auth_i.h"#include "wpa_auth_ie.h"#ifdef CONFIG_IEEE80211Rstatic int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len){ if (wpa_auth->cb.send_ether == NULL) return -1; return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, data, data_len);}static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len){ if (wpa_auth->cb.send_ft_action == NULL) return -1; return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, data, data_len);}static struct wpa_state_machine *wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr){ if (wpa_auth->cb.add_sta == NULL) return NULL; return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr);}int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len){ u8 *pos = buf; u8 capab; if (len < 2 + sizeof(struct rsn_mdie)) return -1; *pos++ = WLAN_EID_MOBILITY_DOMAIN; *pos++ = MOBILITY_DOMAIN_ID_LEN + 1; os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); pos += MOBILITY_DOMAIN_ID_LEN; capab = RSN_FT_CAPAB_FT_OVER_DS; *pos++ = capab; return pos - buf;}static int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len){ u8 *pos = buf, *ielen; struct rsn_ftie *hdr; if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + subelem_len) return -1; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ielen = pos++; hdr = (struct rsn_ftie *) pos; os_memset(hdr, 0, sizeof(*hdr)); pos += sizeof(*hdr); WPA_PUT_LE16(hdr->mic_control, 0); if (anonce) os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); if (snonce) os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); /* Optional Parameters */ *pos++ = FTIE_SUBELEM_R1KH_ID; *pos++ = FT_R1KH_ID_LEN; os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN); pos += FT_R1KH_ID_LEN; if (r0kh_id) { *pos++ = FTIE_SUBELEM_R0KH_ID; *pos++ = r0kh_id_len; os_memcpy(pos, r0kh_id, r0kh_id_len); pos += r0kh_id_len; } if (subelem) { os_memcpy(pos, subelem, subelem_len); pos += subelem_len; } *ielen = pos - buf - 2; return pos - buf;}struct wpa_ft_pmk_r0_sa { struct wpa_ft_pmk_r0_sa *next; u8 pmk_r0[PMK_LEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ int pmk_r1_pushed;};struct wpa_ft_pmk_r1_sa { struct wpa_ft_pmk_r1_sa *next; u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */};struct wpa_ft_pmk_cache { struct wpa_ft_pmk_r0_sa *pmk_r0; struct wpa_ft_pmk_r1_sa *pmk_r1;};struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void){ struct wpa_ft_pmk_cache *cache; cache = os_zalloc(sizeof(*cache)); return cache;}void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache){ struct wpa_ft_pmk_r0_sa *r0, *r0prev; struct wpa_ft_pmk_r1_sa *r1, *r1prev; r0 = cache->pmk_r0; while (r0) { r0prev = r0; r0 = r0->next; os_memset(r0prev->pmk_r0, 0, PMK_LEN); os_free(r0prev); } r1 = cache->pmk_r1; while (r1) { r1prev = r1; r1 = r1->next; os_memset(r1prev->pmk_r1, 0, PMK_LEN); os_free(r1prev); } os_free(cache);}static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0, const u8 *pmk_r0_name){ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; /* TODO: add expiration and limit on number of entries in cache */ r0 = os_zalloc(sizeof(*r0)); if (r0 == NULL) return -1; os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(r0->spa, spa, ETH_ALEN); r0->next = cache->pmk_r0; cache->pmk_r0 = r0; return 0;}static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0_name, u8 *pmk_r0){ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; r0 = cache->pmk_r0; while (r0) { if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) == 0) { os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); return 0; } r0 = r0->next; } return -1;}static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1, const u8 *pmk_r1_name){ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; /* TODO: add expiration and limit on number of entries in cache */ r1 = os_zalloc(sizeof(*r1)); if (r1 == NULL) return -1; os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); os_memcpy(r1->spa, spa, ETH_ALEN); r1->next = cache->pmk_r1; cache->pmk_r1 = r1; return 0;}static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1_name, u8 *pmk_r1){ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; r1 = cache->pmk_r1; while (r1) { if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) == 0) { os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); return 0; } r1 = r1->next; } return -1;}static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *s1kh_id, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *pmk_r0_name){ struct ft_remote_r0kh *r0kh; struct ft_r0kh_r1kh_pull_frame frame, f; r0kh = wpa_auth->conf.r0kh_list; while (r0kh) { if (r0kh->id_len == r0kh_id_len && os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0) break; r0kh = r0kh->next; } if (r0kh == NULL) return -1; wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " "address " MACSTR, MAC2STR(r0kh->addr)); os_memset(&frame, 0, sizeof(frame)); frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ if (os_get_random(f.nonce, sizeof(f.nonce))) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; } os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, f.nonce, frame.nonce) < 0) return -1; wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); return 0;}int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk){ u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *mdid = sm->wpa_auth->conf.mobility_domain; const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len; const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; const u8 *ssid = sm->wpa_auth->conf.ssid; size_t ssid_len = sm->wpa_auth->conf.ssid_len; if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " "derivation"); return -1; } wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name); wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, pmk_r1, pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_name); wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, pmk_r1_name, (u8 *) ptk, sizeof(*ptk), ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, sizeof(*ptk)); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); return 0;}static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq){ if (wpa_auth->cb.get_seqnum == NULL) return -1; return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);}#ifdef CONFIG_IEEE80211Wstatic inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq){ if (wpa_auth->cb.get_seqnum_igtk == NULL) return -1; return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq);}#endif /* CONFIG_IEEE80211W */static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len){ u8 *subelem; struct wpa_group *gsm = sm->group; size_t subelem_len, pad_len; const u8 *key; size_t key_len; u8 keybuf[32]; key_len = gsm->GTK_len; if (key_len > sizeof(keybuf)) return NULL; /* * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less * than 16 bytes. */ pad_len = key_len % 8; if (pad_len) pad_len = 8 - pad_len; if (key_len + pad_len < 16) pad_len += 8; if (pad_len) { os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); os_memset(keybuf + key_len, 0, pad_len); keybuf[key_len] = 0xdd; key_len += pad_len; key = keybuf; } else key = gsm->GTK[gsm->GN - 1]; /* * Sub-elem ID[1] | Length[1] | Key Info[1] | Key Length[1] | RSC[8] | * Key[5..32]. */ subelem_len = 12 + key_len + 8; subelem = os_zalloc(subelem_len); if (subelem == NULL) return NULL; subelem[0] = FTIE_SUBELEM_GTK; subelem[1] = 10 + key_len + 8; subelem[2] = gsm->GN & 0x03; /* Key ID in B0-B1 of Key Info */ subelem[3] = gsm->GTK_len; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 4); if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 12)) { os_free(subelem); return NULL; } *len = subelem_len; return subelem;}#ifdef CONFIG_IEEE80211Wstatic u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len){ u8 *subelem, *pos; struct wpa_group *gsm = sm->group; size_t subelem_len; /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] | * Key[16+8] */ subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8; subelem = os_zalloc(subelem_len); if (subelem == NULL) return NULL; pos = subelem; *pos++ = FTIE_SUBELEM_IGTK; *pos++ = subelem_len - 2; WPA_PUT_LE16(pos, gsm->GN_igtk); pos += 2; wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN_igtk, pos); pos += 6; *pos++ = WPA_IGTK_LEN; if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, gsm->IGTK[gsm->GN_igtk - 4], pos)) { os_free(subelem); return NULL; } *len = subelem_len; return subelem;}#endif /* CONFIG_IEEE80211W */u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg){ u8 *end, *mdie, *ftie, *rsnie, *r0kh_id, *subelem = NULL; size_t mdie_len, ftie_len, rsnie_len, r0kh_id_len, subelem_len = 0; int res; struct wpa_auth_config *conf; struct rsn_ftie *_ftie; if (sm == NULL) return pos; conf = &sm->wpa_auth->conf; if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK) return pos; end = pos + max_len; /* RSN */ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); if (res < 0) return pos; rsnie = pos; rsnie_len = res; pos += res;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -