1214501Srpaulo/* 2214501Srpaulo * hostapd - IEEE 802.11r - Fast BSS Transition 3214501Srpaulo * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "utils/includes.h" 10214501Srpaulo 11214501Srpaulo#include "utils/common.h" 12214501Srpaulo#include "common/ieee802_11_defs.h" 13214501Srpaulo#include "common/ieee802_11_common.h" 14214501Srpaulo#include "crypto/aes_wrap.h" 15252726Srpaulo#include "crypto/random.h" 16214501Srpaulo#include "ap_config.h" 17214501Srpaulo#include "ieee802_11.h" 18214501Srpaulo#include "wmm.h" 19214501Srpaulo#include "wpa_auth.h" 20214501Srpaulo#include "wpa_auth_i.h" 21214501Srpaulo 22214501Srpaulo 23214501Srpaulo#ifdef CONFIG_IEEE80211R 24214501Srpaulo 25214501Srpaulostatic int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, 26214501Srpaulo const u8 *data, size_t data_len) 27214501Srpaulo{ 28214501Srpaulo if (wpa_auth->cb.send_ether == NULL) 29214501Srpaulo return -1; 30214501Srpaulo wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst)); 31214501Srpaulo return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, 32214501Srpaulo data, data_len); 33214501Srpaulo} 34214501Srpaulo 35214501Srpaulo 36214501Srpaulostatic int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, 37214501Srpaulo const u8 *dst, const u8 *data, size_t data_len) 38214501Srpaulo{ 39214501Srpaulo if (wpa_auth->cb.send_ft_action == NULL) 40214501Srpaulo return -1; 41214501Srpaulo return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, 42214501Srpaulo data, data_len); 43214501Srpaulo} 44214501Srpaulo 45214501Srpaulo 46214501Srpaulostatic struct wpa_state_machine * 47214501Srpaulowpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) 48214501Srpaulo{ 49214501Srpaulo if (wpa_auth->cb.add_sta == NULL) 50214501Srpaulo return NULL; 51214501Srpaulo return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); 52214501Srpaulo} 53214501Srpaulo 54214501Srpaulo 55252726Srpaulostatic int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, 56252726Srpaulo const u8 *sta_addr, 57252726Srpaulo u8 *tspec_ie, size_t tspec_ielen) 58252726Srpaulo{ 59252726Srpaulo if (wpa_auth->cb.add_tspec == NULL) { 60252726Srpaulo wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); 61252726Srpaulo return -1; 62252726Srpaulo } 63252726Srpaulo return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, 64252726Srpaulo tspec_ielen); 65252726Srpaulo} 66252726Srpaulo 67252726Srpaulo 68214501Srpauloint wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) 69214501Srpaulo{ 70214501Srpaulo u8 *pos = buf; 71214501Srpaulo u8 capab; 72214501Srpaulo if (len < 2 + sizeof(struct rsn_mdie)) 73214501Srpaulo return -1; 74214501Srpaulo 75214501Srpaulo *pos++ = WLAN_EID_MOBILITY_DOMAIN; 76214501Srpaulo *pos++ = MOBILITY_DOMAIN_ID_LEN + 1; 77214501Srpaulo os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); 78214501Srpaulo pos += MOBILITY_DOMAIN_ID_LEN; 79252726Srpaulo capab = 0; 80252726Srpaulo if (conf->ft_over_ds) 81252726Srpaulo capab |= RSN_FT_CAPAB_FT_OVER_DS; 82214501Srpaulo *pos++ = capab; 83214501Srpaulo 84214501Srpaulo return pos - buf; 85214501Srpaulo} 86214501Srpaulo 87214501Srpaulo 88214501Srpauloint wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, 89214501Srpaulo size_t r0kh_id_len, 90214501Srpaulo const u8 *anonce, const u8 *snonce, 91214501Srpaulo u8 *buf, size_t len, const u8 *subelem, 92214501Srpaulo size_t subelem_len) 93214501Srpaulo{ 94214501Srpaulo u8 *pos = buf, *ielen; 95214501Srpaulo struct rsn_ftie *hdr; 96214501Srpaulo 97214501Srpaulo if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + 98214501Srpaulo subelem_len) 99214501Srpaulo return -1; 100214501Srpaulo 101214501Srpaulo *pos++ = WLAN_EID_FAST_BSS_TRANSITION; 102214501Srpaulo ielen = pos++; 103214501Srpaulo 104214501Srpaulo hdr = (struct rsn_ftie *) pos; 105214501Srpaulo os_memset(hdr, 0, sizeof(*hdr)); 106214501Srpaulo pos += sizeof(*hdr); 107214501Srpaulo WPA_PUT_LE16(hdr->mic_control, 0); 108214501Srpaulo if (anonce) 109214501Srpaulo os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); 110214501Srpaulo if (snonce) 111214501Srpaulo os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); 112214501Srpaulo 113214501Srpaulo /* Optional Parameters */ 114214501Srpaulo *pos++ = FTIE_SUBELEM_R1KH_ID; 115214501Srpaulo *pos++ = FT_R1KH_ID_LEN; 116214501Srpaulo os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN); 117214501Srpaulo pos += FT_R1KH_ID_LEN; 118214501Srpaulo 119214501Srpaulo if (r0kh_id) { 120214501Srpaulo *pos++ = FTIE_SUBELEM_R0KH_ID; 121214501Srpaulo *pos++ = r0kh_id_len; 122214501Srpaulo os_memcpy(pos, r0kh_id, r0kh_id_len); 123214501Srpaulo pos += r0kh_id_len; 124214501Srpaulo } 125214501Srpaulo 126214501Srpaulo if (subelem) { 127214501Srpaulo os_memcpy(pos, subelem, subelem_len); 128214501Srpaulo pos += subelem_len; 129214501Srpaulo } 130214501Srpaulo 131214501Srpaulo *ielen = pos - buf - 2; 132214501Srpaulo 133214501Srpaulo return pos - buf; 134214501Srpaulo} 135214501Srpaulo 136214501Srpaulo 137214501Srpaulostruct wpa_ft_pmk_r0_sa { 138214501Srpaulo struct wpa_ft_pmk_r0_sa *next; 139214501Srpaulo u8 pmk_r0[PMK_LEN]; 140214501Srpaulo u8 pmk_r0_name[WPA_PMK_NAME_LEN]; 141214501Srpaulo u8 spa[ETH_ALEN]; 142214501Srpaulo int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ 143214501Srpaulo /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ 144214501Srpaulo int pmk_r1_pushed; 145214501Srpaulo}; 146214501Srpaulo 147214501Srpaulostruct wpa_ft_pmk_r1_sa { 148214501Srpaulo struct wpa_ft_pmk_r1_sa *next; 149214501Srpaulo u8 pmk_r1[PMK_LEN]; 150214501Srpaulo u8 pmk_r1_name[WPA_PMK_NAME_LEN]; 151214501Srpaulo u8 spa[ETH_ALEN]; 152214501Srpaulo int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ 153214501Srpaulo /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ 154214501Srpaulo}; 155214501Srpaulo 156214501Srpaulostruct wpa_ft_pmk_cache { 157214501Srpaulo struct wpa_ft_pmk_r0_sa *pmk_r0; 158214501Srpaulo struct wpa_ft_pmk_r1_sa *pmk_r1; 159214501Srpaulo}; 160214501Srpaulo 161214501Srpaulostruct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) 162214501Srpaulo{ 163214501Srpaulo struct wpa_ft_pmk_cache *cache; 164214501Srpaulo 165214501Srpaulo cache = os_zalloc(sizeof(*cache)); 166214501Srpaulo 167214501Srpaulo return cache; 168214501Srpaulo} 169214501Srpaulo 170214501Srpaulo 171214501Srpaulovoid wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) 172214501Srpaulo{ 173214501Srpaulo struct wpa_ft_pmk_r0_sa *r0, *r0prev; 174214501Srpaulo struct wpa_ft_pmk_r1_sa *r1, *r1prev; 175214501Srpaulo 176214501Srpaulo r0 = cache->pmk_r0; 177214501Srpaulo while (r0) { 178214501Srpaulo r0prev = r0; 179214501Srpaulo r0 = r0->next; 180214501Srpaulo os_memset(r0prev->pmk_r0, 0, PMK_LEN); 181214501Srpaulo os_free(r0prev); 182214501Srpaulo } 183214501Srpaulo 184214501Srpaulo r1 = cache->pmk_r1; 185214501Srpaulo while (r1) { 186214501Srpaulo r1prev = r1; 187214501Srpaulo r1 = r1->next; 188214501Srpaulo os_memset(r1prev->pmk_r1, 0, PMK_LEN); 189214501Srpaulo os_free(r1prev); 190214501Srpaulo } 191214501Srpaulo 192214501Srpaulo os_free(cache); 193214501Srpaulo} 194214501Srpaulo 195214501Srpaulo 196214501Srpaulostatic int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, 197214501Srpaulo const u8 *spa, const u8 *pmk_r0, 198214501Srpaulo const u8 *pmk_r0_name, int pairwise) 199214501Srpaulo{ 200214501Srpaulo struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; 201214501Srpaulo struct wpa_ft_pmk_r0_sa *r0; 202214501Srpaulo 203214501Srpaulo /* TODO: add expiration and limit on number of entries in cache */ 204214501Srpaulo 205214501Srpaulo r0 = os_zalloc(sizeof(*r0)); 206214501Srpaulo if (r0 == NULL) 207214501Srpaulo return -1; 208214501Srpaulo 209214501Srpaulo os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); 210214501Srpaulo os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); 211214501Srpaulo os_memcpy(r0->spa, spa, ETH_ALEN); 212214501Srpaulo r0->pairwise = pairwise; 213214501Srpaulo 214214501Srpaulo r0->next = cache->pmk_r0; 215214501Srpaulo cache->pmk_r0 = r0; 216214501Srpaulo 217214501Srpaulo return 0; 218214501Srpaulo} 219214501Srpaulo 220214501Srpaulo 221214501Srpaulostatic int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, 222214501Srpaulo const u8 *spa, const u8 *pmk_r0_name, 223214501Srpaulo u8 *pmk_r0, int *pairwise) 224214501Srpaulo{ 225214501Srpaulo struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; 226214501Srpaulo struct wpa_ft_pmk_r0_sa *r0; 227214501Srpaulo 228214501Srpaulo r0 = cache->pmk_r0; 229214501Srpaulo while (r0) { 230214501Srpaulo if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && 231214501Srpaulo os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) 232214501Srpaulo == 0) { 233214501Srpaulo os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); 234214501Srpaulo if (pairwise) 235214501Srpaulo *pairwise = r0->pairwise; 236214501Srpaulo return 0; 237214501Srpaulo } 238214501Srpaulo 239214501Srpaulo r0 = r0->next; 240214501Srpaulo } 241214501Srpaulo 242214501Srpaulo return -1; 243214501Srpaulo} 244214501Srpaulo 245214501Srpaulo 246214501Srpaulostatic int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, 247214501Srpaulo const u8 *spa, const u8 *pmk_r1, 248214501Srpaulo const u8 *pmk_r1_name, int pairwise) 249214501Srpaulo{ 250214501Srpaulo struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; 251214501Srpaulo struct wpa_ft_pmk_r1_sa *r1; 252214501Srpaulo 253214501Srpaulo /* TODO: add expiration and limit on number of entries in cache */ 254214501Srpaulo 255214501Srpaulo r1 = os_zalloc(sizeof(*r1)); 256214501Srpaulo if (r1 == NULL) 257214501Srpaulo return -1; 258214501Srpaulo 259214501Srpaulo os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); 260214501Srpaulo os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); 261214501Srpaulo os_memcpy(r1->spa, spa, ETH_ALEN); 262214501Srpaulo r1->pairwise = pairwise; 263214501Srpaulo 264214501Srpaulo r1->next = cache->pmk_r1; 265214501Srpaulo cache->pmk_r1 = r1; 266214501Srpaulo 267214501Srpaulo return 0; 268214501Srpaulo} 269214501Srpaulo 270214501Srpaulo 271214501Srpaulostatic int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, 272214501Srpaulo const u8 *spa, const u8 *pmk_r1_name, 273214501Srpaulo u8 *pmk_r1, int *pairwise) 274214501Srpaulo{ 275214501Srpaulo struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; 276214501Srpaulo struct wpa_ft_pmk_r1_sa *r1; 277214501Srpaulo 278214501Srpaulo r1 = cache->pmk_r1; 279214501Srpaulo while (r1) { 280214501Srpaulo if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && 281214501Srpaulo os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) 282214501Srpaulo == 0) { 283214501Srpaulo os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); 284214501Srpaulo if (pairwise) 285214501Srpaulo *pairwise = r1->pairwise; 286214501Srpaulo return 0; 287214501Srpaulo } 288214501Srpaulo 289214501Srpaulo r1 = r1->next; 290214501Srpaulo } 291214501Srpaulo 292214501Srpaulo return -1; 293214501Srpaulo} 294214501Srpaulo 295214501Srpaulo 296214501Srpaulostatic int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, 297214501Srpaulo const u8 *s1kh_id, const u8 *r0kh_id, 298214501Srpaulo size_t r0kh_id_len, const u8 *pmk_r0_name) 299214501Srpaulo{ 300214501Srpaulo struct ft_remote_r0kh *r0kh; 301214501Srpaulo struct ft_r0kh_r1kh_pull_frame frame, f; 302214501Srpaulo 303214501Srpaulo r0kh = wpa_auth->conf.r0kh_list; 304214501Srpaulo while (r0kh) { 305214501Srpaulo if (r0kh->id_len == r0kh_id_len && 306214501Srpaulo os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0) 307214501Srpaulo break; 308214501Srpaulo r0kh = r0kh->next; 309214501Srpaulo } 310214501Srpaulo if (r0kh == NULL) 311214501Srpaulo return -1; 312214501Srpaulo 313214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " 314214501Srpaulo "address " MACSTR, MAC2STR(r0kh->addr)); 315214501Srpaulo 316214501Srpaulo os_memset(&frame, 0, sizeof(frame)); 317214501Srpaulo frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; 318214501Srpaulo frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; 319214501Srpaulo frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); 320214501Srpaulo os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); 321214501Srpaulo 322214501Srpaulo /* aes_wrap() does not support inplace encryption, so use a temporary 323214501Srpaulo * buffer for the data. */ 324252726Srpaulo if (random_get_bytes(f.nonce, sizeof(f.nonce))) { 325214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " 326214501Srpaulo "nonce"); 327214501Srpaulo return -1; 328214501Srpaulo } 329214501Srpaulo os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); 330214501Srpaulo os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); 331214501Srpaulo os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); 332214501Srpaulo 333214501Srpaulo if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, 334214501Srpaulo f.nonce, frame.nonce) < 0) 335214501Srpaulo return -1; 336214501Srpaulo 337214501Srpaulo wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); 338214501Srpaulo 339214501Srpaulo return 0; 340214501Srpaulo} 341214501Srpaulo 342214501Srpaulo 343214501Srpauloint wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, 344214501Srpaulo struct wpa_ptk *ptk, size_t ptk_len) 345214501Srpaulo{ 346214501Srpaulo u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; 347214501Srpaulo u8 pmk_r1[PMK_LEN]; 348214501Srpaulo u8 ptk_name[WPA_PMK_NAME_LEN]; 349214501Srpaulo const u8 *mdid = sm->wpa_auth->conf.mobility_domain; 350214501Srpaulo const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; 351214501Srpaulo size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len; 352214501Srpaulo const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; 353214501Srpaulo const u8 *ssid = sm->wpa_auth->conf.ssid; 354214501Srpaulo size_t ssid_len = sm->wpa_auth->conf.ssid_len; 355214501Srpaulo 356214501Srpaulo 357214501Srpaulo if (sm->xxkey_len == 0) { 358214501Srpaulo wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " 359214501Srpaulo "derivation"); 360214501Srpaulo return -1; 361214501Srpaulo } 362214501Srpaulo 363214501Srpaulo wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, 364214501Srpaulo r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); 365214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); 366214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); 367214501Srpaulo wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name, 368214501Srpaulo sm->pairwise); 369214501Srpaulo 370214501Srpaulo wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, 371214501Srpaulo pmk_r1, sm->pmk_r1_name); 372214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); 373214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, 374214501Srpaulo WPA_PMK_NAME_LEN); 375214501Srpaulo wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name, 376214501Srpaulo sm->pairwise); 377214501Srpaulo 378214501Srpaulo wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, 379214501Srpaulo sm->wpa_auth->addr, sm->pmk_r1_name, 380214501Srpaulo (u8 *) ptk, ptk_len, ptk_name); 381214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); 382214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); 383214501Srpaulo 384214501Srpaulo return 0; 385214501Srpaulo} 386214501Srpaulo 387214501Srpaulo 388214501Srpaulostatic inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, 389214501Srpaulo const u8 *addr, int idx, u8 *seq) 390214501Srpaulo{ 391214501Srpaulo if (wpa_auth->cb.get_seqnum == NULL) 392214501Srpaulo return -1; 393214501Srpaulo return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); 394214501Srpaulo} 395214501Srpaulo 396214501Srpaulo 397214501Srpaulostatic u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) 398214501Srpaulo{ 399214501Srpaulo u8 *subelem; 400214501Srpaulo struct wpa_group *gsm = sm->group; 401214501Srpaulo size_t subelem_len, pad_len; 402214501Srpaulo const u8 *key; 403214501Srpaulo size_t key_len; 404214501Srpaulo u8 keybuf[32]; 405214501Srpaulo 406214501Srpaulo key_len = gsm->GTK_len; 407214501Srpaulo if (key_len > sizeof(keybuf)) 408214501Srpaulo return NULL; 409214501Srpaulo 410214501Srpaulo /* 411214501Srpaulo * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less 412214501Srpaulo * than 16 bytes. 413214501Srpaulo */ 414214501Srpaulo pad_len = key_len % 8; 415214501Srpaulo if (pad_len) 416214501Srpaulo pad_len = 8 - pad_len; 417214501Srpaulo if (key_len + pad_len < 16) 418214501Srpaulo pad_len += 8; 419214501Srpaulo if (pad_len) { 420214501Srpaulo os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); 421214501Srpaulo os_memset(keybuf + key_len, 0, pad_len); 422214501Srpaulo keybuf[key_len] = 0xdd; 423214501Srpaulo key_len += pad_len; 424214501Srpaulo key = keybuf; 425214501Srpaulo } else 426214501Srpaulo key = gsm->GTK[gsm->GN - 1]; 427214501Srpaulo 428214501Srpaulo /* 429214501Srpaulo * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | 430214501Srpaulo * Key[5..32]. 431214501Srpaulo */ 432214501Srpaulo subelem_len = 13 + key_len + 8; 433214501Srpaulo subelem = os_zalloc(subelem_len); 434214501Srpaulo if (subelem == NULL) 435214501Srpaulo return NULL; 436214501Srpaulo 437214501Srpaulo subelem[0] = FTIE_SUBELEM_GTK; 438214501Srpaulo subelem[1] = 11 + key_len + 8; 439214501Srpaulo /* Key ID in B0-B1 of Key Info */ 440214501Srpaulo WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); 441214501Srpaulo subelem[4] = gsm->GTK_len; 442214501Srpaulo wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5); 443214501Srpaulo if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) { 444214501Srpaulo os_free(subelem); 445214501Srpaulo return NULL; 446214501Srpaulo } 447214501Srpaulo 448214501Srpaulo *len = subelem_len; 449214501Srpaulo return subelem; 450214501Srpaulo} 451214501Srpaulo 452214501Srpaulo 453214501Srpaulo#ifdef CONFIG_IEEE80211W 454214501Srpaulostatic u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) 455214501Srpaulo{ 456214501Srpaulo u8 *subelem, *pos; 457214501Srpaulo struct wpa_group *gsm = sm->group; 458214501Srpaulo size_t subelem_len; 459214501Srpaulo 460214501Srpaulo /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] | 461214501Srpaulo * Key[16+8] */ 462214501Srpaulo subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8; 463214501Srpaulo subelem = os_zalloc(subelem_len); 464214501Srpaulo if (subelem == NULL) 465214501Srpaulo return NULL; 466214501Srpaulo 467214501Srpaulo pos = subelem; 468214501Srpaulo *pos++ = FTIE_SUBELEM_IGTK; 469214501Srpaulo *pos++ = subelem_len - 2; 470214501Srpaulo WPA_PUT_LE16(pos, gsm->GN_igtk); 471214501Srpaulo pos += 2; 472214501Srpaulo wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos); 473214501Srpaulo pos += 6; 474214501Srpaulo *pos++ = WPA_IGTK_LEN; 475214501Srpaulo if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, 476214501Srpaulo gsm->IGTK[gsm->GN_igtk - 4], pos)) { 477214501Srpaulo os_free(subelem); 478214501Srpaulo return NULL; 479214501Srpaulo } 480214501Srpaulo 481214501Srpaulo *len = subelem_len; 482214501Srpaulo return subelem; 483214501Srpaulo} 484214501Srpaulo#endif /* CONFIG_IEEE80211W */ 485214501Srpaulo 486214501Srpaulo 487252726Srpaulostatic u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, 488252726Srpaulo u8 *pos, u8 *end, u8 id, u8 descr_count, 489214501Srpaulo const u8 *ies, size_t ies_len) 490214501Srpaulo{ 491214501Srpaulo struct ieee802_11_elems parse; 492214501Srpaulo struct rsn_rdie *rdie; 493214501Srpaulo 494214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d", 495214501Srpaulo id, descr_count); 496214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)", 497214501Srpaulo ies, ies_len); 498214501Srpaulo 499214501Srpaulo if (end - pos < (int) sizeof(*rdie)) { 500214501Srpaulo wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE"); 501214501Srpaulo return pos; 502214501Srpaulo } 503214501Srpaulo 504214501Srpaulo *pos++ = WLAN_EID_RIC_DATA; 505214501Srpaulo *pos++ = sizeof(*rdie); 506214501Srpaulo rdie = (struct rsn_rdie *) pos; 507214501Srpaulo rdie->id = id; 508214501Srpaulo rdie->descr_count = 0; 509214501Srpaulo rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS); 510214501Srpaulo pos += sizeof(*rdie); 511214501Srpaulo 512214501Srpaulo if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) == 513214501Srpaulo ParseFailed) { 514214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs"); 515214501Srpaulo rdie->status_code = 516214501Srpaulo host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); 517214501Srpaulo return pos; 518214501Srpaulo } 519214501Srpaulo 520214501Srpaulo#ifdef NEED_AP_MLME 521252726Srpaulo if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { 522214501Srpaulo struct wmm_tspec_element *tspec; 523214501Srpaulo int res; 524214501Srpaulo 525214501Srpaulo if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) { 526214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE " 527214501Srpaulo "(%d)", (int) parse.wmm_tspec_len); 528214501Srpaulo rdie->status_code = 529214501Srpaulo host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); 530214501Srpaulo return pos; 531214501Srpaulo } 532214501Srpaulo if (end - pos < (int) sizeof(*tspec)) { 533214501Srpaulo wpa_printf(MSG_ERROR, "FT: Not enough room for " 534214501Srpaulo "response TSPEC"); 535214501Srpaulo rdie->status_code = 536214501Srpaulo host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); 537214501Srpaulo return pos; 538214501Srpaulo } 539214501Srpaulo tspec = (struct wmm_tspec_element *) pos; 540214501Srpaulo os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); 541214501Srpaulo res = wmm_process_tspec(tspec); 542214501Srpaulo wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res); 543214501Srpaulo if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS) 544214501Srpaulo rdie->status_code = 545214501Srpaulo host_to_le16(WLAN_STATUS_INVALID_PARAMETERS); 546214501Srpaulo else if (res == WMM_ADDTS_STATUS_REFUSED) 547214501Srpaulo rdie->status_code = 548214501Srpaulo host_to_le16(WLAN_STATUS_REQUEST_DECLINED); 549214501Srpaulo else { 550214501Srpaulo /* TSPEC accepted; include updated TSPEC in response */ 551214501Srpaulo rdie->descr_count = 1; 552214501Srpaulo pos += sizeof(*tspec); 553214501Srpaulo } 554214501Srpaulo return pos; 555214501Srpaulo } 556214501Srpaulo#endif /* NEED_AP_MLME */ 557214501Srpaulo 558252726Srpaulo if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { 559252726Srpaulo struct wmm_tspec_element *tspec; 560252726Srpaulo int res; 561252726Srpaulo 562252726Srpaulo tspec = (struct wmm_tspec_element *) pos; 563252726Srpaulo os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); 564252726Srpaulo res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, 565252726Srpaulo sizeof(*tspec)); 566252726Srpaulo if (res >= 0) { 567252726Srpaulo if (res) 568252726Srpaulo rdie->status_code = host_to_le16(res); 569252726Srpaulo else { 570252726Srpaulo /* TSPEC accepted; include updated TSPEC in 571252726Srpaulo * response */ 572252726Srpaulo rdie->descr_count = 1; 573252726Srpaulo pos += sizeof(*tspec); 574252726Srpaulo } 575252726Srpaulo return pos; 576252726Srpaulo } 577252726Srpaulo } 578252726Srpaulo 579214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No supported resource requested"); 580214501Srpaulo rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); 581214501Srpaulo return pos; 582214501Srpaulo} 583214501Srpaulo 584214501Srpaulo 585252726Srpaulostatic u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end, 586252726Srpaulo const u8 *ric, size_t ric_len) 587214501Srpaulo{ 588214501Srpaulo const u8 *rpos, *start; 589214501Srpaulo const struct rsn_rdie *rdie; 590214501Srpaulo 591214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len); 592214501Srpaulo 593214501Srpaulo rpos = ric; 594214501Srpaulo while (rpos + sizeof(*rdie) < ric + ric_len) { 595214501Srpaulo if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) || 596214501Srpaulo rpos + 2 + rpos[1] > ric + ric_len) 597214501Srpaulo break; 598214501Srpaulo rdie = (const struct rsn_rdie *) (rpos + 2); 599214501Srpaulo rpos += 2 + rpos[1]; 600214501Srpaulo start = rpos; 601214501Srpaulo 602214501Srpaulo while (rpos + 2 <= ric + ric_len && 603214501Srpaulo rpos + 2 + rpos[1] <= ric + ric_len) { 604214501Srpaulo if (rpos[0] == WLAN_EID_RIC_DATA) 605214501Srpaulo break; 606214501Srpaulo rpos += 2 + rpos[1]; 607214501Srpaulo } 608252726Srpaulo pos = wpa_ft_process_rdie(sm, pos, end, rdie->id, 609214501Srpaulo rdie->descr_count, 610214501Srpaulo start, rpos - start); 611214501Srpaulo } 612214501Srpaulo 613214501Srpaulo return pos; 614214501Srpaulo} 615214501Srpaulo 616214501Srpaulo 617214501Srpaulou8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, 618214501Srpaulo size_t max_len, int auth_alg, 619214501Srpaulo const u8 *req_ies, size_t req_ies_len) 620214501Srpaulo{ 621214501Srpaulo u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL; 622214501Srpaulo size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0; 623214501Srpaulo int res; 624214501Srpaulo struct wpa_auth_config *conf; 625214501Srpaulo struct rsn_ftie *_ftie; 626214501Srpaulo struct wpa_ft_ies parse; 627214501Srpaulo u8 *ric_start; 628214501Srpaulo u8 *anonce, *snonce; 629214501Srpaulo 630214501Srpaulo if (sm == NULL) 631214501Srpaulo return pos; 632214501Srpaulo 633214501Srpaulo conf = &sm->wpa_auth->conf; 634214501Srpaulo 635214501Srpaulo if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && 636214501Srpaulo sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK) 637214501Srpaulo return pos; 638214501Srpaulo 639214501Srpaulo end = pos + max_len; 640214501Srpaulo 641214501Srpaulo if (auth_alg == WLAN_AUTH_FT) { 642214501Srpaulo /* 643214501Srpaulo * RSN (only present if this is a Reassociation Response and 644214501Srpaulo * part of a fast BSS transition) 645214501Srpaulo */ 646214501Srpaulo res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); 647214501Srpaulo if (res < 0) 648214501Srpaulo return pos; 649214501Srpaulo rsnie = pos; 650214501Srpaulo rsnie_len = res; 651214501Srpaulo pos += res; 652214501Srpaulo } 653214501Srpaulo 654214501Srpaulo /* Mobility Domain Information */ 655214501Srpaulo res = wpa_write_mdie(conf, pos, end - pos); 656214501Srpaulo if (res < 0) 657214501Srpaulo return pos; 658214501Srpaulo mdie = pos; 659214501Srpaulo mdie_len = res; 660214501Srpaulo pos += res; 661214501Srpaulo 662214501Srpaulo /* Fast BSS Transition Information */ 663214501Srpaulo if (auth_alg == WLAN_AUTH_FT) { 664214501Srpaulo subelem = wpa_ft_gtk_subelem(sm, &subelem_len); 665214501Srpaulo r0kh_id = sm->r0kh_id; 666214501Srpaulo r0kh_id_len = sm->r0kh_id_len; 667214501Srpaulo anonce = sm->ANonce; 668214501Srpaulo snonce = sm->SNonce; 669214501Srpaulo#ifdef CONFIG_IEEE80211W 670214501Srpaulo if (sm->mgmt_frame_prot) { 671214501Srpaulo u8 *igtk; 672214501Srpaulo size_t igtk_len; 673214501Srpaulo u8 *nbuf; 674214501Srpaulo igtk = wpa_ft_igtk_subelem(sm, &igtk_len); 675214501Srpaulo if (igtk == NULL) { 676214501Srpaulo os_free(subelem); 677214501Srpaulo return pos; 678214501Srpaulo } 679214501Srpaulo nbuf = os_realloc(subelem, subelem_len + igtk_len); 680214501Srpaulo if (nbuf == NULL) { 681214501Srpaulo os_free(subelem); 682214501Srpaulo os_free(igtk); 683214501Srpaulo return pos; 684214501Srpaulo } 685214501Srpaulo subelem = nbuf; 686214501Srpaulo os_memcpy(subelem + subelem_len, igtk, igtk_len); 687214501Srpaulo subelem_len += igtk_len; 688214501Srpaulo os_free(igtk); 689214501Srpaulo } 690214501Srpaulo#endif /* CONFIG_IEEE80211W */ 691214501Srpaulo } else { 692214501Srpaulo r0kh_id = conf->r0_key_holder; 693214501Srpaulo r0kh_id_len = conf->r0_key_holder_len; 694214501Srpaulo anonce = NULL; 695214501Srpaulo snonce = NULL; 696214501Srpaulo } 697214501Srpaulo res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos, 698214501Srpaulo end - pos, subelem, subelem_len); 699214501Srpaulo os_free(subelem); 700214501Srpaulo if (res < 0) 701214501Srpaulo return pos; 702214501Srpaulo ftie = pos; 703214501Srpaulo ftie_len = res; 704214501Srpaulo pos += res; 705214501Srpaulo 706214501Srpaulo os_free(sm->assoc_resp_ftie); 707214501Srpaulo sm->assoc_resp_ftie = os_malloc(ftie_len); 708214501Srpaulo if (sm->assoc_resp_ftie) 709214501Srpaulo os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); 710214501Srpaulo 711214501Srpaulo _ftie = (struct rsn_ftie *) (ftie + 2); 712214501Srpaulo if (auth_alg == WLAN_AUTH_FT) 713214501Srpaulo _ftie->mic_control[1] = 3; /* Information element count */ 714214501Srpaulo 715214501Srpaulo ric_start = pos; 716214501Srpaulo if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { 717252726Srpaulo pos = wpa_ft_process_ric(sm, pos, end, parse.ric, 718252726Srpaulo parse.ric_len); 719214501Srpaulo if (auth_alg == WLAN_AUTH_FT) 720214501Srpaulo _ftie->mic_control[1] += 721214501Srpaulo ieee802_11_ie_count(ric_start, 722214501Srpaulo pos - ric_start); 723214501Srpaulo } 724214501Srpaulo if (ric_start == pos) 725214501Srpaulo ric_start = NULL; 726214501Srpaulo 727214501Srpaulo if (auth_alg == WLAN_AUTH_FT && 728214501Srpaulo wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6, 729214501Srpaulo mdie, mdie_len, ftie, ftie_len, 730214501Srpaulo rsnie, rsnie_len, 731214501Srpaulo ric_start, ric_start ? pos - ric_start : 0, 732214501Srpaulo _ftie->mic) < 0) 733214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); 734214501Srpaulo 735214501Srpaulo return pos; 736214501Srpaulo} 737214501Srpaulo 738214501Srpaulo 739214501Srpaulostatic inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, 740214501Srpaulo int vlan_id, 741214501Srpaulo enum wpa_alg alg, const u8 *addr, int idx, 742214501Srpaulo u8 *key, size_t key_len) 743214501Srpaulo{ 744214501Srpaulo if (wpa_auth->cb.set_key == NULL) 745214501Srpaulo return -1; 746214501Srpaulo return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, 747214501Srpaulo key, key_len); 748214501Srpaulo} 749214501Srpaulo 750214501Srpaulo 751214501Srpaulovoid wpa_ft_install_ptk(struct wpa_state_machine *sm) 752214501Srpaulo{ 753214501Srpaulo enum wpa_alg alg; 754214501Srpaulo int klen; 755214501Srpaulo 756214501Srpaulo /* MLME-SETKEYS.request(PTK) */ 757252726Srpaulo alg = wpa_cipher_to_alg(sm->pairwise); 758252726Srpaulo klen = wpa_cipher_key_len(sm->pairwise); 759252726Srpaulo if (!wpa_cipher_valid_pairwise(sm->pairwise)) { 760214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip " 761214501Srpaulo "PTK configuration", sm->pairwise); 762214501Srpaulo return; 763214501Srpaulo } 764214501Srpaulo 765324739Sgordon if (sm->tk_already_set) { 766324739Sgordon /* Must avoid TK reconfiguration to prevent clearing of TX/RX 767324739Sgordon * PN in the driver */ 768324739Sgordon wpa_printf(MSG_DEBUG, 769324739Sgordon "FT: Do not re-install same PTK to the driver"); 770324739Sgordon return; 771324739Sgordon } 772324739Sgordon 773214501Srpaulo /* FIX: add STA entry to kernel/driver here? The set_key will fail 774214501Srpaulo * most likely without this.. At the moment, STA entry is added only 775214501Srpaulo * after association has been completed. This function will be called 776214501Srpaulo * again after association to get the PTK configured, but that could be 777214501Srpaulo * optimized by adding the STA entry earlier. 778214501Srpaulo */ 779214501Srpaulo if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, 780214501Srpaulo sm->PTK.tk1, klen)) 781214501Srpaulo return; 782214501Srpaulo 783214501Srpaulo /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ 784214501Srpaulo sm->pairwise_set = TRUE; 785324739Sgordon sm->tk_already_set = TRUE; 786214501Srpaulo} 787214501Srpaulo 788214501Srpaulo 789214501Srpaulostatic u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, 790214501Srpaulo const u8 *ies, size_t ies_len, 791214501Srpaulo u8 **resp_ies, size_t *resp_ies_len) 792214501Srpaulo{ 793214501Srpaulo struct rsn_mdie *mdie; 794214501Srpaulo struct rsn_ftie *ftie; 795214501Srpaulo u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; 796214501Srpaulo u8 ptk_name[WPA_PMK_NAME_LEN]; 797214501Srpaulo struct wpa_auth_config *conf; 798214501Srpaulo struct wpa_ft_ies parse; 799214501Srpaulo size_t buflen, ptk_len; 800214501Srpaulo int ret; 801214501Srpaulo u8 *pos, *end; 802214501Srpaulo int pairwise; 803214501Srpaulo 804214501Srpaulo *resp_ies = NULL; 805214501Srpaulo *resp_ies_len = 0; 806214501Srpaulo 807214501Srpaulo sm->pmk_r1_name_valid = 0; 808214501Srpaulo conf = &sm->wpa_auth->conf; 809214501Srpaulo 810214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs", 811214501Srpaulo ies, ies_len); 812214501Srpaulo 813214501Srpaulo if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { 814214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); 815214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 816214501Srpaulo } 817214501Srpaulo 818214501Srpaulo mdie = (struct rsn_mdie *) parse.mdie; 819214501Srpaulo if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || 820214501Srpaulo os_memcmp(mdie->mobility_domain, 821214501Srpaulo sm->wpa_auth->conf.mobility_domain, 822214501Srpaulo MOBILITY_DOMAIN_ID_LEN) != 0) { 823214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); 824214501Srpaulo return WLAN_STATUS_INVALID_MDIE; 825214501Srpaulo } 826214501Srpaulo 827214501Srpaulo ftie = (struct rsn_ftie *) parse.ftie; 828214501Srpaulo if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { 829214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); 830214501Srpaulo return WLAN_STATUS_INVALID_FTIE; 831214501Srpaulo } 832214501Srpaulo 833214501Srpaulo os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); 834214501Srpaulo 835214501Srpaulo if (parse.r0kh_id == NULL) { 836214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); 837214501Srpaulo return WLAN_STATUS_INVALID_FTIE; 838214501Srpaulo } 839214501Srpaulo 840214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID", 841214501Srpaulo parse.r0kh_id, parse.r0kh_id_len); 842214501Srpaulo os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); 843214501Srpaulo sm->r0kh_id_len = parse.r0kh_id_len; 844214501Srpaulo 845214501Srpaulo if (parse.rsn_pmkid == NULL) { 846214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); 847214501Srpaulo return WLAN_STATUS_INVALID_PMKID; 848214501Srpaulo } 849214501Srpaulo 850214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name", 851214501Srpaulo parse.rsn_pmkid, WPA_PMK_NAME_LEN); 852214501Srpaulo wpa_derive_pmk_r1_name(parse.rsn_pmkid, 853214501Srpaulo sm->wpa_auth->conf.r1_key_holder, sm->addr, 854214501Srpaulo pmk_r1_name); 855214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", 856214501Srpaulo pmk_r1_name, WPA_PMK_NAME_LEN); 857214501Srpaulo 858214501Srpaulo if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1, 859214501Srpaulo &pairwise) < 0) { 860214501Srpaulo if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id, 861214501Srpaulo sm->r0kh_id_len, parse.rsn_pmkid) < 0) { 862214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Did not have matching " 863214501Srpaulo "PMK-R1 and unknown R0KH-ID"); 864214501Srpaulo return WLAN_STATUS_INVALID_PMKID; 865214501Srpaulo } 866214501Srpaulo 867214501Srpaulo /* 868214501Srpaulo * TODO: Should return "status pending" (and the caller should 869214501Srpaulo * not send out response now). The real response will be sent 870214501Srpaulo * once the response from R0KH is received. 871214501Srpaulo */ 872214501Srpaulo return WLAN_STATUS_INVALID_PMKID; 873214501Srpaulo } 874214501Srpaulo 875214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); 876214501Srpaulo sm->pmk_r1_name_valid = 1; 877214501Srpaulo os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); 878214501Srpaulo 879252726Srpaulo if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { 880214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " 881214501Srpaulo "ANonce"); 882214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 883214501Srpaulo } 884214501Srpaulo 885214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", 886214501Srpaulo sm->SNonce, WPA_NONCE_LEN); 887214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", 888214501Srpaulo sm->ANonce, WPA_NONCE_LEN); 889214501Srpaulo 890252726Srpaulo ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48; 891214501Srpaulo wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, 892214501Srpaulo sm->wpa_auth->addr, pmk_r1_name, 893214501Srpaulo (u8 *) &sm->PTK, ptk_len, ptk_name); 894214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "FT: PTK", 895214501Srpaulo (u8 *) &sm->PTK, ptk_len); 896214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); 897214501Srpaulo 898214501Srpaulo sm->pairwise = pairwise; 899324739Sgordon sm->tk_already_set = FALSE; 900214501Srpaulo wpa_ft_install_ptk(sm); 901214501Srpaulo 902214501Srpaulo buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 903214501Srpaulo 2 + FT_R1KH_ID_LEN + 200; 904214501Srpaulo *resp_ies = os_zalloc(buflen); 905214501Srpaulo if (*resp_ies == NULL) { 906214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 907214501Srpaulo } 908214501Srpaulo 909214501Srpaulo pos = *resp_ies; 910214501Srpaulo end = *resp_ies + buflen; 911214501Srpaulo 912214501Srpaulo ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid); 913214501Srpaulo if (ret < 0) { 914214501Srpaulo os_free(*resp_ies); 915214501Srpaulo *resp_ies = NULL; 916214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 917214501Srpaulo } 918214501Srpaulo pos += ret; 919214501Srpaulo 920214501Srpaulo ret = wpa_write_mdie(conf, pos, end - pos); 921214501Srpaulo if (ret < 0) { 922214501Srpaulo os_free(*resp_ies); 923214501Srpaulo *resp_ies = NULL; 924214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 925214501Srpaulo } 926214501Srpaulo pos += ret; 927214501Srpaulo 928214501Srpaulo ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, 929214501Srpaulo sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0); 930214501Srpaulo if (ret < 0) { 931214501Srpaulo os_free(*resp_ies); 932214501Srpaulo *resp_ies = NULL; 933214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 934214501Srpaulo } 935214501Srpaulo pos += ret; 936214501Srpaulo 937214501Srpaulo *resp_ies_len = pos - *resp_ies; 938214501Srpaulo 939214501Srpaulo return WLAN_STATUS_SUCCESS; 940214501Srpaulo} 941214501Srpaulo 942214501Srpaulo 943214501Srpaulovoid wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, 944214501Srpaulo u16 auth_transaction, const u8 *ies, size_t ies_len, 945214501Srpaulo void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, 946214501Srpaulo u16 auth_transaction, u16 status, 947214501Srpaulo const u8 *ies, size_t ies_len), 948214501Srpaulo void *ctx) 949214501Srpaulo{ 950214501Srpaulo u16 status; 951214501Srpaulo u8 *resp_ies; 952214501Srpaulo size_t resp_ies_len; 953214501Srpaulo 954214501Srpaulo if (sm == NULL) { 955214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but " 956214501Srpaulo "WPA SM not available"); 957214501Srpaulo return; 958214501Srpaulo } 959214501Srpaulo 960214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR 961214501Srpaulo " BSSID=" MACSTR " transaction=%d", 962214501Srpaulo MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction); 963214501Srpaulo status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, 964214501Srpaulo &resp_ies_len); 965214501Srpaulo 966214501Srpaulo wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR 967214501Srpaulo " auth_transaction=%d status=%d", 968214501Srpaulo MAC2STR(sm->addr), auth_transaction + 1, status); 969214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); 970214501Srpaulo cb(ctx, sm->addr, bssid, auth_transaction + 1, status, 971214501Srpaulo resp_ies, resp_ies_len); 972214501Srpaulo os_free(resp_ies); 973214501Srpaulo} 974214501Srpaulo 975214501Srpaulo 976214501Srpaulou16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, 977214501Srpaulo size_t ies_len) 978214501Srpaulo{ 979214501Srpaulo struct wpa_ft_ies parse; 980214501Srpaulo struct rsn_mdie *mdie; 981214501Srpaulo struct rsn_ftie *ftie; 982214501Srpaulo u8 mic[16]; 983214501Srpaulo unsigned int count; 984214501Srpaulo 985214501Srpaulo if (sm == NULL) 986214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 987214501Srpaulo 988214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len); 989214501Srpaulo 990214501Srpaulo if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { 991214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); 992214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 993214501Srpaulo } 994214501Srpaulo 995214501Srpaulo if (parse.rsn == NULL) { 996214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req"); 997214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 998214501Srpaulo } 999214501Srpaulo 1000214501Srpaulo if (parse.rsn_pmkid == NULL) { 1001214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); 1002214501Srpaulo return WLAN_STATUS_INVALID_PMKID; 1003214501Srpaulo } 1004214501Srpaulo 1005214501Srpaulo if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) 1006214501Srpaulo { 1007214501Srpaulo wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match " 1008214501Srpaulo "with the PMKR1Name derived from auth request"); 1009214501Srpaulo return WLAN_STATUS_INVALID_PMKID; 1010214501Srpaulo } 1011214501Srpaulo 1012214501Srpaulo mdie = (struct rsn_mdie *) parse.mdie; 1013214501Srpaulo if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || 1014214501Srpaulo os_memcmp(mdie->mobility_domain, 1015214501Srpaulo sm->wpa_auth->conf.mobility_domain, 1016214501Srpaulo MOBILITY_DOMAIN_ID_LEN) != 0) { 1017214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); 1018214501Srpaulo return WLAN_STATUS_INVALID_MDIE; 1019214501Srpaulo } 1020214501Srpaulo 1021214501Srpaulo ftie = (struct rsn_ftie *) parse.ftie; 1022214501Srpaulo if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { 1023214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); 1024214501Srpaulo return WLAN_STATUS_INVALID_FTIE; 1025214501Srpaulo } 1026214501Srpaulo 1027214501Srpaulo if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { 1028214501Srpaulo wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); 1029214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", 1030214501Srpaulo ftie->snonce, WPA_NONCE_LEN); 1031214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", 1032214501Srpaulo sm->SNonce, WPA_NONCE_LEN); 1033214501Srpaulo return -1; 1034214501Srpaulo } 1035214501Srpaulo 1036214501Srpaulo if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { 1037214501Srpaulo wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); 1038214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", 1039214501Srpaulo ftie->anonce, WPA_NONCE_LEN); 1040214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", 1041214501Srpaulo sm->ANonce, WPA_NONCE_LEN); 1042214501Srpaulo return -1; 1043214501Srpaulo } 1044214501Srpaulo 1045214501Srpaulo 1046214501Srpaulo if (parse.r0kh_id == NULL) { 1047214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); 1048214501Srpaulo return -1; 1049214501Srpaulo } 1050214501Srpaulo 1051214501Srpaulo if (parse.r0kh_id_len != sm->r0kh_id_len || 1052214501Srpaulo os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { 1053214501Srpaulo wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " 1054214501Srpaulo "the current R0KH-ID"); 1055214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", 1056214501Srpaulo parse.r0kh_id, parse.r0kh_id_len); 1057214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", 1058214501Srpaulo sm->r0kh_id, sm->r0kh_id_len); 1059214501Srpaulo return -1; 1060214501Srpaulo } 1061214501Srpaulo 1062214501Srpaulo if (parse.r1kh_id == NULL) { 1063214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); 1064214501Srpaulo return -1; 1065214501Srpaulo } 1066214501Srpaulo 1067214501Srpaulo if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, 1068214501Srpaulo FT_R1KH_ID_LEN) != 0) { 1069214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " 1070214501Srpaulo "ReassocReq"); 1071214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE", 1072214501Srpaulo parse.r1kh_id, FT_R1KH_ID_LEN); 1073214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID", 1074214501Srpaulo sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); 1075214501Srpaulo return -1; 1076214501Srpaulo } 1077214501Srpaulo 1078214501Srpaulo if (parse.rsn_pmkid == NULL || 1079214501Srpaulo os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { 1080214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " 1081214501Srpaulo "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); 1082214501Srpaulo return -1; 1083214501Srpaulo } 1084214501Srpaulo 1085214501Srpaulo count = 3; 1086214501Srpaulo if (parse.ric) 1087252726Srpaulo count += ieee802_11_ie_count(parse.ric, parse.ric_len); 1088214501Srpaulo if (ftie->mic_control[1] != count) { 1089214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " 1090214501Srpaulo "Control: received %u expected %u", 1091214501Srpaulo ftie->mic_control[1], count); 1092214501Srpaulo return -1; 1093214501Srpaulo } 1094214501Srpaulo 1095214501Srpaulo if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5, 1096214501Srpaulo parse.mdie - 2, parse.mdie_len + 2, 1097214501Srpaulo parse.ftie - 2, parse.ftie_len + 2, 1098214501Srpaulo parse.rsn - 2, parse.rsn_len + 2, 1099214501Srpaulo parse.ric, parse.ric_len, 1100214501Srpaulo mic) < 0) { 1101214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); 1102214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 1103214501Srpaulo } 1104214501Srpaulo 1105214501Srpaulo if (os_memcmp(mic, ftie->mic, 16) != 0) { 1106214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); 1107252726Srpaulo wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, 1108252726Srpaulo MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); 1109214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); 1110214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); 1111252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", 1112252726Srpaulo parse.mdie - 2, parse.mdie_len + 2); 1113252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "FT: FTIE", 1114252726Srpaulo parse.ftie - 2, parse.ftie_len + 2); 1115252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "FT: RSN", 1116252726Srpaulo parse.rsn - 2, parse.rsn_len + 2); 1117214501Srpaulo return WLAN_STATUS_INVALID_FTIE; 1118214501Srpaulo } 1119214501Srpaulo 1120214501Srpaulo return WLAN_STATUS_SUCCESS; 1121214501Srpaulo} 1122214501Srpaulo 1123214501Srpaulo 1124214501Srpauloint wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) 1125214501Srpaulo{ 1126214501Srpaulo const u8 *sta_addr, *target_ap; 1127214501Srpaulo const u8 *ies; 1128214501Srpaulo size_t ies_len; 1129214501Srpaulo u8 action; 1130214501Srpaulo struct ft_rrb_frame *frame; 1131214501Srpaulo 1132214501Srpaulo if (sm == NULL) 1133214501Srpaulo return -1; 1134214501Srpaulo 1135214501Srpaulo /* 1136214501Srpaulo * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] 1137214501Srpaulo * FT Request action frame body[variable] 1138214501Srpaulo */ 1139214501Srpaulo 1140214501Srpaulo if (len < 14) { 1141214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame " 1142214501Srpaulo "(len=%lu)", (unsigned long) len); 1143214501Srpaulo return -1; 1144214501Srpaulo } 1145214501Srpaulo 1146214501Srpaulo action = data[1]; 1147214501Srpaulo sta_addr = data + 2; 1148214501Srpaulo target_ap = data + 8; 1149214501Srpaulo ies = data + 14; 1150214501Srpaulo ies_len = len - 14; 1151214501Srpaulo 1152214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR 1153214501Srpaulo " Target AP=" MACSTR " Action=%d)", 1154214501Srpaulo MAC2STR(sta_addr), MAC2STR(target_ap), action); 1155214501Srpaulo 1156214501Srpaulo if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) { 1157214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: " 1158214501Srpaulo "STA=" MACSTR " STA-Address=" MACSTR, 1159214501Srpaulo MAC2STR(sm->addr), MAC2STR(sta_addr)); 1160214501Srpaulo return -1; 1161214501Srpaulo } 1162214501Srpaulo 1163214501Srpaulo /* 1164214501Srpaulo * Do some sanity checking on the target AP address (not own and not 1165214501Srpaulo * broadcast. This could be extended to filter based on a list of known 1166214501Srpaulo * APs in the MD (if such a list were configured). 1167214501Srpaulo */ 1168214501Srpaulo if ((target_ap[0] & 0x01) || 1169214501Srpaulo os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) { 1170214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action " 1171214501Srpaulo "frame"); 1172214501Srpaulo return -1; 1173214501Srpaulo } 1174214501Srpaulo 1175214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); 1176214501Srpaulo 1177214501Srpaulo /* RRB - Forward action frame to the target AP */ 1178214501Srpaulo frame = os_malloc(sizeof(*frame) + len); 1179214501Srpaulo frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; 1180214501Srpaulo frame->packet_type = FT_PACKET_REQUEST; 1181214501Srpaulo frame->action_length = host_to_le16(len); 1182214501Srpaulo os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN); 1183214501Srpaulo os_memcpy(frame + 1, data, len); 1184214501Srpaulo 1185214501Srpaulo wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame, 1186214501Srpaulo sizeof(*frame) + len); 1187214501Srpaulo os_free(frame); 1188214501Srpaulo 1189214501Srpaulo return 0; 1190214501Srpaulo} 1191214501Srpaulo 1192214501Srpaulo 1193214501Srpaulostatic int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, 1194214501Srpaulo const u8 *current_ap, const u8 *sta_addr, 1195214501Srpaulo const u8 *body, size_t len) 1196214501Srpaulo{ 1197214501Srpaulo struct wpa_state_machine *sm; 1198214501Srpaulo u16 status; 1199214501Srpaulo u8 *resp_ies, *pos; 1200214501Srpaulo size_t resp_ies_len, rlen; 1201214501Srpaulo struct ft_rrb_frame *frame; 1202214501Srpaulo 1203214501Srpaulo sm = wpa_ft_add_sta(wpa_auth, sta_addr); 1204214501Srpaulo if (sm == NULL) { 1205214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on " 1206214501Srpaulo "RRB Request"); 1207214501Srpaulo return -1; 1208214501Srpaulo } 1209214501Srpaulo 1210214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len); 1211214501Srpaulo 1212214501Srpaulo status = wpa_ft_process_auth_req(sm, body, len, &resp_ies, 1213214501Srpaulo &resp_ies_len); 1214214501Srpaulo 1215214501Srpaulo wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR 1216214501Srpaulo " CurrentAP=" MACSTR " status=%d", 1217214501Srpaulo MAC2STR(sm->addr), MAC2STR(current_ap), status); 1218214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); 1219214501Srpaulo 1220214501Srpaulo /* RRB - Forward action frame response to the Current AP */ 1221214501Srpaulo 1222214501Srpaulo /* 1223214501Srpaulo * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] 1224214501Srpaulo * Status_Code[2] FT Request action frame body[variable] 1225214501Srpaulo */ 1226214501Srpaulo rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len; 1227214501Srpaulo 1228214501Srpaulo frame = os_malloc(sizeof(*frame) + rlen); 1229214501Srpaulo frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; 1230214501Srpaulo frame->packet_type = FT_PACKET_RESPONSE; 1231214501Srpaulo frame->action_length = host_to_le16(rlen); 1232214501Srpaulo os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN); 1233214501Srpaulo pos = (u8 *) (frame + 1); 1234214501Srpaulo *pos++ = WLAN_ACTION_FT; 1235214501Srpaulo *pos++ = 2; /* Action: Response */ 1236214501Srpaulo os_memcpy(pos, sta_addr, ETH_ALEN); 1237214501Srpaulo pos += ETH_ALEN; 1238214501Srpaulo os_memcpy(pos, wpa_auth->addr, ETH_ALEN); 1239214501Srpaulo pos += ETH_ALEN; 1240214501Srpaulo WPA_PUT_LE16(pos, status); 1241214501Srpaulo pos += 2; 1242214501Srpaulo if (resp_ies) { 1243214501Srpaulo os_memcpy(pos, resp_ies, resp_ies_len); 1244214501Srpaulo os_free(resp_ies); 1245214501Srpaulo } 1246214501Srpaulo 1247214501Srpaulo wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame, 1248214501Srpaulo sizeof(*frame) + rlen); 1249214501Srpaulo os_free(frame); 1250214501Srpaulo 1251214501Srpaulo return 0; 1252214501Srpaulo} 1253214501Srpaulo 1254214501Srpaulo 1255214501Srpaulostatic int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, 1256214501Srpaulo const u8 *src_addr, 1257214501Srpaulo const u8 *data, size_t data_len) 1258214501Srpaulo{ 1259214501Srpaulo struct ft_r0kh_r1kh_pull_frame *frame, f; 1260214501Srpaulo struct ft_remote_r1kh *r1kh; 1261214501Srpaulo struct ft_r0kh_r1kh_resp_frame resp, r; 1262214501Srpaulo u8 pmk_r0[PMK_LEN]; 1263214501Srpaulo int pairwise; 1264214501Srpaulo 1265214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); 1266214501Srpaulo 1267214501Srpaulo if (data_len < sizeof(*frame)) 1268214501Srpaulo return -1; 1269214501Srpaulo 1270214501Srpaulo r1kh = wpa_auth->conf.r1kh_list; 1271214501Srpaulo while (r1kh) { 1272214501Srpaulo if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) 1273214501Srpaulo break; 1274214501Srpaulo r1kh = r1kh->next; 1275214501Srpaulo } 1276214501Srpaulo if (r1kh == NULL) { 1277214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " 1278214501Srpaulo "PMK-R1 pull source address " MACSTR, 1279214501Srpaulo MAC2STR(src_addr)); 1280214501Srpaulo return -1; 1281214501Srpaulo } 1282214501Srpaulo 1283214501Srpaulo frame = (struct ft_r0kh_r1kh_pull_frame *) data; 1284214501Srpaulo /* aes_unwrap() does not support inplace decryption, so use a temporary 1285214501Srpaulo * buffer for the data. */ 1286214501Srpaulo if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, 1287214501Srpaulo frame->nonce, f.nonce) < 0) { 1288214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " 1289214501Srpaulo "request from " MACSTR, MAC2STR(src_addr)); 1290214501Srpaulo return -1; 1291214501Srpaulo } 1292214501Srpaulo 1293214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", 1294214501Srpaulo f.nonce, sizeof(f.nonce)); 1295214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", 1296214501Srpaulo f.pmk_r0_name, WPA_PMK_NAME_LEN); 1297214501Srpaulo wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" 1298214501Srpaulo MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); 1299214501Srpaulo 1300214501Srpaulo os_memset(&resp, 0, sizeof(resp)); 1301214501Srpaulo resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; 1302214501Srpaulo resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; 1303214501Srpaulo resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); 1304214501Srpaulo os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); 1305214501Srpaulo 1306214501Srpaulo /* aes_wrap() does not support inplace encryption, so use a temporary 1307214501Srpaulo * buffer for the data. */ 1308214501Srpaulo os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); 1309214501Srpaulo os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); 1310214501Srpaulo os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); 1311214501Srpaulo if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0, 1312214501Srpaulo &pairwise) < 0) { 1313214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " 1314214501Srpaulo "PMK-R1 pull"); 1315214501Srpaulo return -1; 1316214501Srpaulo } 1317214501Srpaulo 1318214501Srpaulo wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, 1319214501Srpaulo r.pmk_r1, r.pmk_r1_name); 1320214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); 1321214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, 1322214501Srpaulo WPA_PMK_NAME_LEN); 1323214501Srpaulo r.pairwise = host_to_le16(pairwise); 1324214501Srpaulo 1325214501Srpaulo if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, 1326214501Srpaulo r.nonce, resp.nonce) < 0) { 1327214501Srpaulo os_memset(pmk_r0, 0, PMK_LEN); 1328214501Srpaulo return -1; 1329214501Srpaulo } 1330214501Srpaulo 1331214501Srpaulo os_memset(pmk_r0, 0, PMK_LEN); 1332214501Srpaulo 1333214501Srpaulo wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); 1334214501Srpaulo 1335214501Srpaulo return 0; 1336214501Srpaulo} 1337214501Srpaulo 1338214501Srpaulo 1339214501Srpaulostatic int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, 1340214501Srpaulo const u8 *src_addr, 1341214501Srpaulo const u8 *data, size_t data_len) 1342214501Srpaulo{ 1343214501Srpaulo struct ft_r0kh_r1kh_resp_frame *frame, f; 1344214501Srpaulo struct ft_remote_r0kh *r0kh; 1345214501Srpaulo int pairwise; 1346214501Srpaulo 1347214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); 1348214501Srpaulo 1349214501Srpaulo if (data_len < sizeof(*frame)) 1350214501Srpaulo return -1; 1351214501Srpaulo 1352214501Srpaulo r0kh = wpa_auth->conf.r0kh_list; 1353214501Srpaulo while (r0kh) { 1354214501Srpaulo if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) 1355214501Srpaulo break; 1356214501Srpaulo r0kh = r0kh->next; 1357214501Srpaulo } 1358214501Srpaulo if (r0kh == NULL) { 1359214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " 1360214501Srpaulo "PMK-R0 pull response source address " MACSTR, 1361214501Srpaulo MAC2STR(src_addr)); 1362214501Srpaulo return -1; 1363214501Srpaulo } 1364214501Srpaulo 1365214501Srpaulo frame = (struct ft_r0kh_r1kh_resp_frame *) data; 1366214501Srpaulo /* aes_unwrap() does not support inplace decryption, so use a temporary 1367214501Srpaulo * buffer for the data. */ 1368214501Srpaulo if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, 1369214501Srpaulo frame->nonce, f.nonce) < 0) { 1370214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " 1371214501Srpaulo "response from " MACSTR, MAC2STR(src_addr)); 1372214501Srpaulo return -1; 1373214501Srpaulo } 1374214501Srpaulo 1375214501Srpaulo if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) 1376214501Srpaulo != 0) { 1377214501Srpaulo wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " 1378214501Srpaulo "matching R1KH-ID"); 1379214501Srpaulo return -1; 1380214501Srpaulo } 1381214501Srpaulo 1382214501Srpaulo /* TODO: verify that <nonce,s1kh_id> matches with a pending request 1383214501Srpaulo * and call this requests callback function to finish request 1384214501Srpaulo * processing */ 1385214501Srpaulo 1386214501Srpaulo pairwise = le_to_host16(f.pairwise); 1387214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", 1388214501Srpaulo f.nonce, sizeof(f.nonce)); 1389214501Srpaulo wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" 1390214501Srpaulo MACSTR " pairwise=0x%x", 1391214501Srpaulo MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); 1392214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", 1393214501Srpaulo f.pmk_r1, PMK_LEN); 1394214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", 1395214501Srpaulo f.pmk_r1_name, WPA_PMK_NAME_LEN); 1396214501Srpaulo 1397214501Srpaulo wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, 1398214501Srpaulo pairwise); 1399214501Srpaulo os_memset(f.pmk_r1, 0, PMK_LEN); 1400214501Srpaulo 1401214501Srpaulo return 0; 1402214501Srpaulo} 1403214501Srpaulo 1404214501Srpaulo 1405214501Srpaulostatic int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, 1406214501Srpaulo const u8 *src_addr, 1407214501Srpaulo const u8 *data, size_t data_len) 1408214501Srpaulo{ 1409214501Srpaulo struct ft_r0kh_r1kh_push_frame *frame, f; 1410214501Srpaulo struct ft_remote_r0kh *r0kh; 1411214501Srpaulo struct os_time now; 1412214501Srpaulo os_time_t tsend; 1413214501Srpaulo int pairwise; 1414214501Srpaulo 1415214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); 1416214501Srpaulo 1417214501Srpaulo if (data_len < sizeof(*frame)) 1418214501Srpaulo return -1; 1419214501Srpaulo 1420214501Srpaulo r0kh = wpa_auth->conf.r0kh_list; 1421214501Srpaulo while (r0kh) { 1422214501Srpaulo if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) 1423214501Srpaulo break; 1424214501Srpaulo r0kh = r0kh->next; 1425214501Srpaulo } 1426214501Srpaulo if (r0kh == NULL) { 1427214501Srpaulo wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " 1428214501Srpaulo "PMK-R0 push source address " MACSTR, 1429214501Srpaulo MAC2STR(src_addr)); 1430214501Srpaulo return -1; 1431214501Srpaulo } 1432214501Srpaulo 1433214501Srpaulo frame = (struct ft_r0kh_r1kh_push_frame *) data; 1434214501Srpaulo /* aes_unwrap() does not support inplace decryption, so use a temporary 1435214501Srpaulo * buffer for the data. */ 1436214501Srpaulo if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, 1437214501Srpaulo frame->timestamp, f.timestamp) < 0) { 1438214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " 1439214501Srpaulo MACSTR, MAC2STR(src_addr)); 1440214501Srpaulo return -1; 1441214501Srpaulo } 1442214501Srpaulo 1443214501Srpaulo os_get_time(&now); 1444214501Srpaulo tsend = WPA_GET_LE32(f.timestamp); 1445214501Srpaulo if ((now.sec > tsend && now.sec - tsend > 60) || 1446214501Srpaulo (now.sec < tsend && tsend - now.sec > 60)) { 1447214501Srpaulo wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " 1448214501Srpaulo "timestamp: sender time %d own time %d\n", 1449214501Srpaulo (int) tsend, (int) now.sec); 1450214501Srpaulo return -1; 1451214501Srpaulo } 1452214501Srpaulo 1453214501Srpaulo if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) 1454214501Srpaulo != 0) { 1455214501Srpaulo wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " 1456214501Srpaulo "R1KH-ID (received " MACSTR " own " MACSTR ")", 1457214501Srpaulo MAC2STR(f.r1kh_id), 1458214501Srpaulo MAC2STR(wpa_auth->conf.r1_key_holder)); 1459214501Srpaulo return -1; 1460214501Srpaulo } 1461214501Srpaulo 1462214501Srpaulo pairwise = le_to_host16(f.pairwise); 1463214501Srpaulo wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" 1464214501Srpaulo MACSTR " pairwise=0x%x", 1465214501Srpaulo MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); 1466214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", 1467214501Srpaulo f.pmk_r1, PMK_LEN); 1468214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", 1469214501Srpaulo f.pmk_r1_name, WPA_PMK_NAME_LEN); 1470214501Srpaulo 1471214501Srpaulo wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, 1472214501Srpaulo pairwise); 1473214501Srpaulo os_memset(f.pmk_r1, 0, PMK_LEN); 1474214501Srpaulo 1475214501Srpaulo return 0; 1476214501Srpaulo} 1477214501Srpaulo 1478214501Srpaulo 1479214501Srpauloint wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, 1480214501Srpaulo const u8 *data, size_t data_len) 1481214501Srpaulo{ 1482214501Srpaulo struct ft_rrb_frame *frame; 1483214501Srpaulo u16 alen; 1484214501Srpaulo const u8 *pos, *end, *start; 1485214501Srpaulo u8 action; 1486214501Srpaulo const u8 *sta_addr, *target_ap_addr; 1487214501Srpaulo 1488214501Srpaulo wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR, 1489214501Srpaulo MAC2STR(src_addr)); 1490214501Srpaulo 1491214501Srpaulo if (data_len < sizeof(*frame)) { 1492214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)", 1493214501Srpaulo (unsigned long) data_len); 1494214501Srpaulo return -1; 1495214501Srpaulo } 1496214501Srpaulo 1497214501Srpaulo pos = data; 1498214501Srpaulo frame = (struct ft_rrb_frame *) pos; 1499214501Srpaulo pos += sizeof(*frame); 1500214501Srpaulo 1501214501Srpaulo alen = le_to_host16(frame->action_length); 1502214501Srpaulo wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d " 1503214501Srpaulo "action_length=%d ap_address=" MACSTR, 1504214501Srpaulo frame->frame_type, frame->packet_type, alen, 1505214501Srpaulo MAC2STR(frame->ap_address)); 1506214501Srpaulo 1507214501Srpaulo if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) { 1508214501Srpaulo /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */ 1509214501Srpaulo wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with " 1510214501Srpaulo "unrecognized type %d", frame->frame_type); 1511214501Srpaulo return -1; 1512214501Srpaulo } 1513214501Srpaulo 1514214501Srpaulo if (alen > data_len - sizeof(*frame)) { 1515214501Srpaulo wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action " 1516214501Srpaulo "frame"); 1517214501Srpaulo return -1; 1518214501Srpaulo } 1519214501Srpaulo 1520214501Srpaulo if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL) 1521214501Srpaulo return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len); 1522214501Srpaulo if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP) 1523214501Srpaulo return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len); 1524214501Srpaulo if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH) 1525214501Srpaulo return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len); 1526214501Srpaulo 1527214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen); 1528214501Srpaulo 1529214501Srpaulo if (alen < 1 + 1 + 2 * ETH_ALEN) { 1530214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough " 1531214501Srpaulo "room for Action Frame body); alen=%lu", 1532214501Srpaulo (unsigned long) alen); 1533214501Srpaulo return -1; 1534214501Srpaulo } 1535214501Srpaulo start = pos; 1536214501Srpaulo end = pos + alen; 1537214501Srpaulo 1538214501Srpaulo if (*pos != WLAN_ACTION_FT) { 1539214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category " 1540214501Srpaulo "%d", *pos); 1541214501Srpaulo return -1; 1542214501Srpaulo } 1543214501Srpaulo 1544214501Srpaulo pos++; 1545214501Srpaulo action = *pos++; 1546214501Srpaulo sta_addr = pos; 1547214501Srpaulo pos += ETH_ALEN; 1548214501Srpaulo target_ap_addr = pos; 1549214501Srpaulo pos += ETH_ALEN; 1550214501Srpaulo wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr=" 1551214501Srpaulo MACSTR " target_ap_addr=" MACSTR, 1552214501Srpaulo action, MAC2STR(sta_addr), MAC2STR(target_ap_addr)); 1553214501Srpaulo 1554214501Srpaulo if (frame->packet_type == FT_PACKET_REQUEST) { 1555214501Srpaulo wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request"); 1556214501Srpaulo 1557214501Srpaulo if (action != 1) { 1558214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in " 1559214501Srpaulo "RRB Request", action); 1560214501Srpaulo return -1; 1561214501Srpaulo } 1562214501Srpaulo 1563214501Srpaulo if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) { 1564214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Target AP address in the " 1565214501Srpaulo "RRB Request does not match with own " 1566214501Srpaulo "address"); 1567214501Srpaulo return -1; 1568214501Srpaulo } 1569214501Srpaulo 1570214501Srpaulo if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address, 1571214501Srpaulo sta_addr, pos, end - pos) < 0) 1572214501Srpaulo return -1; 1573214501Srpaulo } else if (frame->packet_type == FT_PACKET_RESPONSE) { 1574214501Srpaulo u16 status_code; 1575214501Srpaulo 1576214501Srpaulo if (end - pos < 2) { 1577214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Not enough room for status " 1578214501Srpaulo "code in RRB Response"); 1579214501Srpaulo return -1; 1580214501Srpaulo } 1581214501Srpaulo status_code = WPA_GET_LE16(pos); 1582214501Srpaulo pos += 2; 1583214501Srpaulo 1584214501Srpaulo wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response " 1585214501Srpaulo "(status_code=%d)", status_code); 1586214501Srpaulo 1587214501Srpaulo if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0) 1588214501Srpaulo return -1; 1589214501Srpaulo } else { 1590214501Srpaulo wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown " 1591214501Srpaulo "packet_type %d", frame->packet_type); 1592214501Srpaulo return -1; 1593214501Srpaulo } 1594214501Srpaulo 1595214501Srpaulo return 0; 1596214501Srpaulo} 1597214501Srpaulo 1598214501Srpaulo 1599214501Srpaulostatic void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, 1600214501Srpaulo struct wpa_ft_pmk_r0_sa *pmk_r0, 1601214501Srpaulo struct ft_remote_r1kh *r1kh, 1602214501Srpaulo const u8 *s1kh_id, int pairwise) 1603214501Srpaulo{ 1604214501Srpaulo struct ft_r0kh_r1kh_push_frame frame, f; 1605214501Srpaulo struct os_time now; 1606214501Srpaulo 1607214501Srpaulo os_memset(&frame, 0, sizeof(frame)); 1608214501Srpaulo frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; 1609214501Srpaulo frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; 1610214501Srpaulo frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); 1611214501Srpaulo os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); 1612214501Srpaulo 1613214501Srpaulo /* aes_wrap() does not support inplace encryption, so use a temporary 1614214501Srpaulo * buffer for the data. */ 1615214501Srpaulo os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); 1616214501Srpaulo os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); 1617214501Srpaulo os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); 1618214501Srpaulo wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, 1619214501Srpaulo s1kh_id, f.pmk_r1, f.pmk_r1_name); 1620214501Srpaulo wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); 1621214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); 1622214501Srpaulo wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, 1623214501Srpaulo WPA_PMK_NAME_LEN); 1624214501Srpaulo os_get_time(&now); 1625214501Srpaulo WPA_PUT_LE32(f.timestamp, now.sec); 1626214501Srpaulo f.pairwise = host_to_le16(pairwise); 1627214501Srpaulo if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, 1628214501Srpaulo f.timestamp, frame.timestamp) < 0) 1629214501Srpaulo return; 1630214501Srpaulo 1631214501Srpaulo wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); 1632214501Srpaulo} 1633214501Srpaulo 1634214501Srpaulo 1635214501Srpaulovoid wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) 1636214501Srpaulo{ 1637214501Srpaulo struct wpa_ft_pmk_r0_sa *r0; 1638214501Srpaulo struct ft_remote_r1kh *r1kh; 1639214501Srpaulo 1640214501Srpaulo if (!wpa_auth->conf.pmk_r1_push) 1641214501Srpaulo return; 1642214501Srpaulo 1643214501Srpaulo r0 = wpa_auth->ft_pmk_cache->pmk_r0; 1644214501Srpaulo while (r0) { 1645214501Srpaulo if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) 1646214501Srpaulo break; 1647214501Srpaulo r0 = r0->next; 1648214501Srpaulo } 1649214501Srpaulo 1650214501Srpaulo if (r0 == NULL || r0->pmk_r1_pushed) 1651214501Srpaulo return; 1652214501Srpaulo r0->pmk_r1_pushed = 1; 1653214501Srpaulo 1654214501Srpaulo wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " 1655214501Srpaulo "for STA " MACSTR, MAC2STR(addr)); 1656214501Srpaulo 1657214501Srpaulo r1kh = wpa_auth->conf.r1kh_list; 1658214501Srpaulo while (r1kh) { 1659214501Srpaulo wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise); 1660214501Srpaulo r1kh = r1kh->next; 1661214501Srpaulo } 1662214501Srpaulo} 1663214501Srpaulo 1664214501Srpaulo#endif /* CONFIG_IEEE80211R */ 1665