wpa_ft.c revision 252726
1132718Skan/* 2132718Skan * WPA Supplicant - IEEE 802.11r - Fast BSS Transition 3169689Skan * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> 4132718Skan * 5132718Skan * This software may be distributed under the terms of the BSD license. 6132718Skan * See README for more details. 7132718Skan */ 8132718Skan 9132718Skan#include "includes.h" 10132718Skan 11132718Skan#include "common.h" 12132718Skan#include "crypto/aes_wrap.h" 13132718Skan#include "crypto/random.h" 14132718Skan#include "common/ieee802_11_defs.h" 15132718Skan#include "common/ieee802_11_common.h" 16132718Skan#include "wpa.h" 17132718Skan#include "wpa_i.h" 18132718Skan 19132718Skan#ifdef CONFIG_IEEE80211R 20132718Skan 21132718Skanint wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, 22132718Skan const struct wpa_eapol_key *key, 23169689Skan struct wpa_ptk *ptk, size_t ptk_len) 24169689Skan{ 25132718Skan u8 ptk_name[WPA_PMK_NAME_LEN]; 26132718Skan const u8 *anonce = key->key_nonce; 27132718Skan 28132718Skan if (sm->xxkey_len == 0) { 29132718Skan wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " 30132718Skan "derivation"); 31132718Skan return -1; 32132718Skan } 33132718Skan 34132718Skan wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid, 35132718Skan sm->ssid_len, sm->mobility_domain, 36132718Skan sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, 37132718Skan sm->pmk_r0, sm->pmk_r0_name); 38132718Skan wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN); 39132718Skan wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", 40132718Skan sm->pmk_r0_name, WPA_PMK_NAME_LEN); 41132718Skan wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, 42132718Skan sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); 43132718Skan wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); 44132718Skan wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, 45169689Skan WPA_PMK_NAME_LEN); 46169689Skan wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, 47132718Skan sm->bssid, sm->pmk_r1_name, 48132718Skan (u8 *) ptk, ptk_len, ptk_name); 49132718Skan wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); 50132718Skan wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); 51132718Skan 52132718Skan return 0; 53132718Skan} 54132718Skan 55132718Skan 56132718Skan/** 57132718Skan * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters 58132718Skan * @sm: Pointer to WPA state machine data from wpa_sm_init() 59132718Skan * @ies: Association Response IEs or %NULL to clear FT parameters 60132718Skan * @ies_len: Length of ies buffer in octets 61132718Skan * Returns: 0 on success, -1 on failure 62132718Skan */ 63132718Skanint wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) 64132718Skan{ 65132718Skan struct wpa_ft_ies ft; 66132718Skan 67132718Skan if (sm == NULL) 68132718Skan return 0; 69132718Skan 70132718Skan if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0) 71132718Skan return -1; 72132718Skan 73132718Skan if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) 74132718Skan return -1; 75132718Skan 76132718Skan if (ft.mdie) { 77132718Skan wpa_hexdump(MSG_DEBUG, "FT: Mobility domain", 78132718Skan ft.mdie, MOBILITY_DOMAIN_ID_LEN); 79132718Skan os_memcpy(sm->mobility_domain, ft.mdie, 80132718Skan MOBILITY_DOMAIN_ID_LEN); 81132718Skan sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN]; 82132718Skan wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x", 83132718Skan sm->mdie_ft_capab); 84132718Skan } else 85132718Skan os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN); 86132718Skan 87132718Skan if (ft.r0kh_id) { 88132718Skan wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", 89132718Skan ft.r0kh_id, ft.r0kh_id_len); 90132718Skan os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len); 91132718Skan sm->r0kh_id_len = ft.r0kh_id_len; 92132718Skan } else { 93132718Skan /* FIX: When should R0KH-ID be cleared? We need to keep the 94132718Skan * old R0KH-ID in order to be able to use this during FT. */ 95132718Skan /* 96132718Skan * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN); 97132718Skan * sm->r0kh_id_len = 0; 98132718Skan */ 99169689Skan } 100169689Skan 101169689Skan if (ft.r1kh_id) { 102169689Skan wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", 103169689Skan ft.r1kh_id, FT_R1KH_ID_LEN); 104132718Skan os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN); 105132718Skan } else 106132718Skan os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN); 107132718Skan 108132718Skan os_free(sm->assoc_resp_ies); 109132718Skan sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2); 110132718Skan if (sm->assoc_resp_ies) { 111132718Skan u8 *pos = sm->assoc_resp_ies; 112132718Skan if (ft.mdie) { 113132718Skan os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2); 114132718Skan pos += ft.mdie_len + 2; 115132718Skan } 116132718Skan if (ft.ftie) { 117132718Skan os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2); 118132718Skan pos += ft.ftie_len + 2; 119132718Skan } 120132718Skan sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies; 121132718Skan wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from " 122132718Skan "(Re)Association Response", 123169689Skan sm->assoc_resp_ies, sm->assoc_resp_ies_len); 124169689Skan } 125132718Skan 126169689Skan return 0; 127169689Skan} 128169689Skan 129169689Skan 130169689Skan/** 131169689Skan * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request 132169689Skan * @sm: Pointer to WPA state machine data from wpa_sm_init() 133169689Skan * @len: Buffer for returning the length of the IEs 134169689Skan * @anonce: ANonce or %NULL if not yet available 135169689Skan * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List 136169689Skan * @kck: 128-bit KCK for MIC or %NULL if no MIC is used 137169689Skan * @target_ap: Target AP address 138169689Skan * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL 139132718Skan * @ric_ies_len: Length of ric_ies buffer in octets 140132718Skan * @ap_mdie: Mobility Domain IE from the target AP 141132718Skan * Returns: Pointer to buffer with IEs or %NULL on failure 142132718Skan * 143132718Skan * Caller is responsible for freeing the returned buffer with os_free(); 144132718Skan */ 145132718Skanstatic u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, 146132718Skan const u8 *anonce, const u8 *pmk_name, 147132718Skan const u8 *kck, const u8 *target_ap, 148132718Skan const u8 *ric_ies, size_t ric_ies_len, 149132718Skan const u8 *ap_mdie) 150132718Skan{ 151132718Skan size_t buf_len; 152132718Skan u8 *buf, *pos, *ftie_len, *ftie_pos; 153132718Skan struct rsn_mdie *mdie; 154132718Skan struct rsn_ftie *ftie; 155132718Skan struct rsn_ie_hdr *rsnie; 156132718Skan u16 capab; 157132718Skan 158132718Skan sm->ft_completed = 0; 159132718Skan 160132718Skan buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 161132718Skan 2 + sm->r0kh_id_len + ric_ies_len + 100; 162132718Skan buf = os_zalloc(buf_len); 163132718Skan if (buf == NULL) 164132718Skan return NULL; 165132718Skan pos = buf; 166132718Skan 167132718Skan /* RSNIE[PMKR0Name/PMKR1Name] */ 168132718Skan rsnie = (struct rsn_ie_hdr *) pos; 169132718Skan rsnie->elem_id = WLAN_EID_RSN; 170132718Skan WPA_PUT_LE16(rsnie->version, RSN_VERSION); 171132718Skan pos = (u8 *) (rsnie + 1); 172132718Skan 173132718Skan /* Group Suite Selector */ 174132718Skan if (sm->group_cipher != WPA_CIPHER_CCMP && 175132718Skan sm->group_cipher != WPA_CIPHER_GCMP && 176132718Skan sm->group_cipher != WPA_CIPHER_TKIP) { 177132718Skan wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", 178132718Skan sm->group_cipher); 179132718Skan os_free(buf); 180132718Skan return NULL; 181132718Skan } 182132718Skan RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, 183169689Skan sm->group_cipher)); 184132718Skan pos += RSN_SELECTOR_LEN; 185132718Skan 186132718Skan /* Pairwise Suite Count */ 187132718Skan WPA_PUT_LE16(pos, 1); 188132718Skan pos += 2; 189132718Skan 190132718Skan /* Pairwise Suite List */ 191132718Skan if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { 192132718Skan wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", 193132718Skan sm->pairwise_cipher); 194169689Skan os_free(buf); 195161651Skan return NULL; 196132718Skan } 197132718Skan RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, 198132718Skan sm->pairwise_cipher)); 199132718Skan pos += RSN_SELECTOR_LEN; 200132718Skan 201132718Skan /* Authenticated Key Management Suite Count */ 202132718Skan WPA_PUT_LE16(pos, 1); 203132718Skan pos += 2; 204132718Skan 205132718Skan /* Authenticated Key Management Suite List */ 206132718Skan if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) 207132718Skan RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); 208132718Skan else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) 209132718Skan RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); 210132718Skan else { 211132718Skan wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", 212132718Skan sm->key_mgmt); 213132718Skan os_free(buf); 214132718Skan return NULL; 215132718Skan } 216132718Skan pos += RSN_SELECTOR_LEN; 217132718Skan 218132718Skan /* RSN Capabilities */ 219132718Skan capab = 0; 220132718Skan#ifdef CONFIG_IEEE80211W 221132718Skan if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) 222132718Skan capab |= WPA_CAPABILITY_MFPC; 223132718Skan#endif /* CONFIG_IEEE80211W */ 224132718Skan WPA_PUT_LE16(pos, capab); 225132718Skan pos += 2; 226132718Skan 227132718Skan /* PMKID Count */ 228132718Skan WPA_PUT_LE16(pos, 1); 229132718Skan pos += 2; 230132718Skan 231132718Skan /* PMKID List [PMKR0Name/PMKR1Name] */ 232132718Skan os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN); 233132718Skan pos += WPA_PMK_NAME_LEN; 234132718Skan 235132718Skan#ifdef CONFIG_IEEE80211W 236132718Skan if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { 237132718Skan /* Management Group Cipher Suite */ 238132718Skan RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); 239132718Skan pos += RSN_SELECTOR_LEN; 240132718Skan } 241132718Skan#endif /* CONFIG_IEEE80211W */ 242132718Skan 243132718Skan rsnie->len = (pos - (u8 *) rsnie) - 2; 244132718Skan 245132718Skan /* MDIE */ 246132718Skan *pos++ = WLAN_EID_MOBILITY_DOMAIN; 247132718Skan *pos++ = sizeof(*mdie); 248132718Skan mdie = (struct rsn_mdie *) pos; 249132718Skan pos += sizeof(*mdie); 250132718Skan os_memcpy(mdie->mobility_domain, sm->mobility_domain, 251132718Skan MOBILITY_DOMAIN_ID_LEN); 252132718Skan mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : 253132718Skan sm->mdie_ft_capab; 254132718Skan 255132718Skan /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */ 256132718Skan ftie_pos = pos; 257132718Skan *pos++ = WLAN_EID_FAST_BSS_TRANSITION; 258132718Skan ftie_len = pos++; 259132718Skan ftie = (struct rsn_ftie *) pos; 260132718Skan pos += sizeof(*ftie); 261132718Skan os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); 262132718Skan if (anonce) 263132718Skan os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); 264132718Skan if (kck) { 265169689Skan /* R1KH-ID sub-element in third FT message */ 266132718Skan *pos++ = FTIE_SUBELEM_R1KH_ID; 267132718Skan *pos++ = FT_R1KH_ID_LEN; 268132718Skan os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN); 269132718Skan pos += FT_R1KH_ID_LEN; 270169689Skan } 271132718Skan /* R0KH-ID sub-element */ 272132718Skan *pos++ = FTIE_SUBELEM_R0KH_ID; 273132718Skan *pos++ = sm->r0kh_id_len; 274169689Skan os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len); 275132718Skan pos += sm->r0kh_id_len; 276132718Skan *ftie_len = pos - ftie_len - 1; 277132718Skan 278132718Skan if (ric_ies) { 279132718Skan /* RIC Request */ 280132718Skan os_memcpy(pos, ric_ies, ric_ies_len); 281132718Skan pos += ric_ies_len; 282169689Skan } 283132718Skan 284132718Skan if (kck) { 285132718Skan /* 286132718Skan * IEEE Std 802.11r-2008, 11A.8.4 287132718Skan * MIC shall be calculated over: 288132718Skan * non-AP STA MAC address 289132718Skan * Target AP MAC address 290132718Skan * Transaction seq number (5 for ReassocReq, 3 otherwise) 291132718Skan * RSN IE 292132718Skan * MDIE 293132718Skan * FTIE (with MIC field set to 0) 294132718Skan * RIC-Request (if present) 295132718Skan */ 296132718Skan /* Information element count */ 297132718Skan ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, 298132718Skan ric_ies_len); 299132718Skan if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5, 300132718Skan ((u8 *) mdie) - 2, 2 + sizeof(*mdie), 301132718Skan ftie_pos, 2 + *ftie_len, 302132718Skan (u8 *) rsnie, 2 + rsnie->len, ric_ies, 303132718Skan ric_ies_len, ftie->mic) < 0) { 304132718Skan wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); 305132718Skan os_free(buf); 306132718Skan return NULL; 307132718Skan } 308132718Skan } 309132718Skan 310169689Skan *len = pos - buf; 311169689Skan 312169689Skan return buf; 313169689Skan} 314169689Skan 315169689Skan 316132718Skanstatic int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) 317132718Skan{ 318132718Skan int keylen; 319132718Skan enum wpa_alg alg; 320132718Skan u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 }; 321132718Skan 322132718Skan wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); 323132718Skan 324132718Skan if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { 325132718Skan wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d", 326132718Skan sm->pairwise_cipher); 327132718Skan return -1; 328132718Skan } 329132718Skan 330132718Skan alg = wpa_cipher_to_alg(sm->pairwise_cipher); 331132718Skan keylen = wpa_cipher_key_len(sm->pairwise_cipher); 332132718Skan 333132718Skan if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, 334132718Skan sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) { 335132718Skan wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver"); 336169689Skan return -1; 337169689Skan } 338169689Skan 339132718Skan return 0; 340132718Skan} 341132718Skan 342132718Skan 343132718Skan/** 344132718Skan * wpa_ft_prepare_auth_request - Generate over-the-air auth request 345132718Skan * @sm: Pointer to WPA state machine data from wpa_sm_init() 346132718Skan * @mdie: Target AP MDIE 347132718Skan * Returns: 0 on success, -1 on failure 348169689Skan */ 349132718Skanint wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) 350132718Skan{ 351132718Skan u8 *ft_ies; 352132718Skan size_t ft_ies_len; 353132718Skan 354132718Skan /* Generate a new SNonce */ 355132718Skan if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { 356169689Skan wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); 357132718Skan return -1; 358132718Skan } 359132718Skan 360132718Skan ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, 361132718Skan NULL, sm->bssid, NULL, 0, mdie); 362132718Skan if (ft_ies) { 363132718Skan wpa_sm_update_ft_ies(sm, sm->mobility_domain, 364169689Skan ft_ies, ft_ies_len); 365132718Skan os_free(ft_ies); 366132718Skan } 367132718Skan 368132718Skan return 0; 369132718Skan} 370132718Skan 371132718Skan 372132718Skanint wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, 373132718Skan int ft_action, const u8 *target_ap, 374132718Skan const u8 *ric_ies, size_t ric_ies_len) 375132718Skan{ 376132718Skan u8 *ft_ies; 377132718Skan size_t ft_ies_len, ptk_len; 378132718Skan struct wpa_ft_ies parse; 379132718Skan struct rsn_mdie *mdie; 380132718Skan struct rsn_ftie *ftie; 381132718Skan u8 ptk_name[WPA_PMK_NAME_LEN]; 382132718Skan int ret; 383132718Skan const u8 *bssid; 384132718Skan 385132718Skan wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); 386132718Skan wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len); 387132718Skan 388132718Skan if (ft_action) { 389169689Skan if (!sm->over_the_ds_in_progress) { 390132718Skan wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " 391169689Skan "- drop FT Action Response"); 392169689Skan return -1; 393169689Skan } 394132718Skan 395169689Skan if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) { 396169689Skan wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " 397169689Skan "with this Target AP - drop FT Action " 398169689Skan "Response"); 399169689Skan return -1; 400169689Skan } 401132718Skan } 402169689Skan 403169689Skan if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && 404132718Skan sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { 405132718Skan wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " 406132718Skan "enabled for this connection"); 407132718Skan return -1; 408132718Skan } 409132718Skan 410132718Skan if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { 411169689Skan wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); 412132718Skan return -1; 413169689Skan } 414169689Skan 415132718Skan mdie = (struct rsn_mdie *) parse.mdie; 416169689Skan if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || 417132718Skan os_memcmp(mdie->mobility_domain, sm->mobility_domain, 418169689Skan MOBILITY_DOMAIN_ID_LEN) != 0) { 419132718Skan wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); 420132718Skan return -1; 421169689Skan } 422169689Skan 423169689Skan ftie = (struct rsn_ftie *) parse.ftie; 424132718Skan if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { 425132718Skan wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); 426132718Skan return -1; 427132718Skan } 428132718Skan 429132718Skan if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { 430132718Skan wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); 431132718Skan wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", 432132718Skan ftie->snonce, WPA_NONCE_LEN); 433259405Spfg wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", 434132718Skan sm->snonce, WPA_NONCE_LEN); 435132718Skan return -1; 436259563Spfg } 437132718Skan 438132718Skan if (parse.r0kh_id == NULL) { 439259405Spfg wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); 440259405Spfg return -1; 441259405Spfg } 442259405Spfg 443132718Skan if (parse.r0kh_id_len != sm->r0kh_id_len || 444259405Spfg os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { 445259405Spfg wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " 446132718Skan "the current R0KH-ID"); 447259405Spfg wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", 448259405Spfg parse.r0kh_id, parse.r0kh_id_len); 449259405Spfg wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", 450259405Spfg sm->r0kh_id, sm->r0kh_id_len); 451169689Skan return -1; 452259405Spfg } 453259405Spfg 454259405Spfg if (parse.r1kh_id == NULL) { 455259405Spfg wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); 456259405Spfg return -1; 457259405Spfg } 458259405Spfg 459259405Spfg if (parse.rsn_pmkid == NULL || 460259405Spfg os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) { 461259405Spfg wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in " 462259405Spfg "RSNIE"); 463259405Spfg return -1; 464259405Spfg } 465259405Spfg 466259405Spfg os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); 467259405Spfg wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); 468259405Spfg wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); 469259405Spfg wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN); 470259405Spfg os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN); 471259405Spfg wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, 472259405Spfg sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); 473259405Spfg wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); 474259405Spfg wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", 475259405Spfg sm->pmk_r1_name, WPA_PMK_NAME_LEN); 476259405Spfg 477259405Spfg bssid = target_ap; 478259405Spfg ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; 479259405Spfg wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, 480259405Spfg bssid, sm->pmk_r1_name, 481259405Spfg (u8 *) &sm->ptk, ptk_len, ptk_name); 482259405Spfg wpa_hexdump_key(MSG_DEBUG, "FT: PTK", 483259405Spfg (u8 *) &sm->ptk, ptk_len); 484259405Spfg wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); 485259405Spfg 486259405Spfg ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, 487259405Spfg sm->pmk_r1_name, sm->ptk.kck, bssid, 488259405Spfg ric_ies, ric_ies_len, 489259405Spfg parse.mdie ? parse.mdie - 2 : NULL); 490259405Spfg if (ft_ies) { 491259405Spfg wpa_sm_update_ft_ies(sm, sm->mobility_domain, 492259405Spfg ft_ies, ft_ies_len); 493259405Spfg os_free(ft_ies); 494259405Spfg } 495132718Skan 496132718Skan wpa_sm_mark_authenticated(sm, bssid); 497259405Spfg ret = wpa_ft_install_ptk(sm, bssid); 498259405Spfg if (ret) { 499132718Skan /* 500259405Spfg * Some drivers do not support key configuration when we are 501132718Skan * not associated with the target AP. Work around this by 502132718Skan * trying again after the following reassociation gets 503132718Skan * completed. 504132718Skan */ 505132718Skan wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to " 506132718Skan "association - try again after reassociation"); 507132718Skan sm->set_ptk_after_assoc = 1; 508132718Skan } else 509132718Skan sm->set_ptk_after_assoc = 0; 510169689Skan 511169689Skan sm->ft_completed = 1; 512169689Skan if (ft_action) { 513132718Skan /* 514169689Skan * The caller is expected trigger re-association with the 515132718Skan * Target AP. 516132718Skan */ 517132718Skan os_memcpy(sm->bssid, target_ap, ETH_ALEN); 518132718Skan } 519132718Skan 520132718Skan return 0; 521132718Skan} 522132718Skan 523132718Skan 524132718Skanint wpa_ft_is_completed(struct wpa_sm *sm) 525132718Skan{ 526132718Skan if (sm == NULL) 527132718Skan return 0; 528132718Skan 529132718Skan if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && 530132718Skan sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) 531132718Skan return 0; 532132718Skan 533132718Skan return sm->ft_completed; 534169689Skan} 535169689Skan 536132718Skan 537132718Skanstatic int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, 538132718Skan size_t gtk_elem_len) 539132718Skan{ 540132718Skan u8 gtk[32]; 541132718Skan int keyidx; 542132718Skan enum wpa_alg alg; 543132718Skan size_t gtk_len, keylen, rsc_len; 544132718Skan 545132718Skan if (gtk_elem == NULL) { 546132718Skan wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE"); 547132718Skan return 0; 548132718Skan } 549132718Skan 550132718Skan wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp", 551132718Skan gtk_elem, gtk_elem_len); 552132718Skan 553132718Skan if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 || 554132718Skan gtk_elem_len - 19 > sizeof(gtk)) { 555132718Skan wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem " 556132718Skan "length %lu", (unsigned long) gtk_elem_len); 557169689Skan return -1; 558169689Skan } 559132718Skan gtk_len = gtk_elem_len - 19; 560132718Skan if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) { 561132718Skan wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " 562132718Skan "decrypt GTK"); 563132718Skan return -1; 564132718Skan } 565132718Skan 566132718Skan keylen = wpa_cipher_key_len(sm->group_cipher); 567132718Skan rsc_len = wpa_cipher_rsc_len(sm->group_cipher); 568132718Skan alg = wpa_cipher_to_alg(sm->group_cipher); 569132718Skan if (alg == WPA_ALG_NONE) { 570132718Skan wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", 571132718Skan sm->group_cipher); 572132718Skan return -1; 573132718Skan } 574132718Skan 575132718Skan if (gtk_len < keylen) { 576169689Skan wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE"); 577132718Skan return -1; 578132718Skan } 579132718Skan 580132718Skan /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */ 581132718Skan 582132718Skan keyidx = WPA_GET_LE16(gtk_elem) & 0x03; 583132718Skan 584169689Skan if (gtk_elem[2] != keylen) { 585132718Skan wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d " 586132718Skan "negotiated %lu", 587132718Skan gtk_elem[2], (unsigned long) keylen); 588132718Skan return -1; 589132718Skan } 590132718Skan 591132718Skan wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); 592132718Skan if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0, 593132718Skan gtk_elem + 3, rsc_len, gtk, keylen) < 0) { 594132718Skan wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " 595132718Skan "driver."); 596132718Skan return -1; 597132718Skan } 598132718Skan 599132718Skan return 0; 600132718Skan} 601132718Skan 602132718Skan 603132718Skan#ifdef CONFIG_IEEE80211W 604132718Skanstatic int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, 605132718Skan size_t igtk_elem_len) 606132718Skan{ 607132718Skan u8 igtk[WPA_IGTK_LEN]; 608132718Skan u16 keyidx; 609169689Skan 610132718Skan if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) 611132718Skan return 0; 612132718Skan 613132718Skan if (igtk_elem == NULL) { 614169689Skan wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE"); 615132718Skan return 0; 616132718Skan } 617169689Skan 618132718Skan wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp", 619132718Skan igtk_elem, igtk_elem_len); 620132718Skan 621169689Skan if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) { 622169689Skan wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem " 623169689Skan "length %lu", (unsigned long) igtk_elem_len); 624132718Skan return -1; 625132718Skan } 626132718Skan if (igtk_elem[8] != WPA_IGTK_LEN) { 627132718Skan wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length " 628132718Skan "%d", igtk_elem[8]); 629132718Skan return -1; 630132718Skan } 631132718Skan 632132718Skan if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) { 633132718Skan wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " 634132718Skan "decrypt IGTK"); 635132718Skan return -1; 636132718Skan } 637132718Skan 638132718Skan /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */ 639132718Skan 640132718Skan keyidx = WPA_GET_LE16(igtk_elem); 641132718Skan 642132718Skan wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, 643132718Skan WPA_IGTK_LEN); 644132718Skan if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, 645132718Skan igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) { 646132718Skan wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " 647132718Skan "driver."); 648169689Skan return -1; 649169689Skan } 650132718Skan 651132718Skan return 0; 652132718Skan} 653169689Skan#endif /* CONFIG_IEEE80211W */ 654169689Skan 655132718Skan 656132718Skanint wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, 657132718Skan size_t ies_len, const u8 *src_addr) 658132718Skan{ 659132718Skan struct wpa_ft_ies parse; 660132718Skan struct rsn_mdie *mdie; 661169689Skan struct rsn_ftie *ftie; 662169689Skan unsigned int count; 663132718Skan u8 mic[16]; 664132718Skan 665132718Skan wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); 666132718Skan 667169689Skan if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && 668169689Skan sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { 669169689Skan wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " 670132718Skan "enabled for this connection"); 671132718Skan return -1; 672169689Skan } 673169689Skan 674132718Skan if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { 675132718Skan wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); 676132718Skan return -1; 677132718Skan } 678132718Skan 679132718Skan mdie = (struct rsn_mdie *) parse.mdie; 680132718Skan if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || 681132718Skan os_memcmp(mdie->mobility_domain, sm->mobility_domain, 682132718Skan MOBILITY_DOMAIN_ID_LEN) != 0) { 683169689Skan wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); 684132718Skan return -1; 685169689Skan } 686132718Skan 687132718Skan ftie = (struct rsn_ftie *) parse.ftie; 688132718Skan if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { 689169689Skan wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); 690132718Skan return -1; 691132718Skan } 692132718Skan 693132718Skan if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { 694132718Skan wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); 695132718Skan wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", 696132718Skan ftie->snonce, WPA_NONCE_LEN); 697132718Skan wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", 698132718Skan sm->snonce, WPA_NONCE_LEN); 699132718Skan return -1; 700132718Skan } 701169689Skan 702132718Skan if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) { 703132718Skan wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); 704132718Skan wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", 705132718Skan ftie->anonce, WPA_NONCE_LEN); 706132718Skan wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", 707132718Skan sm->anonce, WPA_NONCE_LEN); 708132718Skan return -1; 709132718Skan } 710132718Skan 711132718Skan if (parse.r0kh_id == NULL) { 712132718Skan wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); 713132718Skan return -1; 714132718Skan } 715132718Skan 716132718Skan if (parse.r0kh_id_len != sm->r0kh_id_len || 717132718Skan os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { 718132718Skan wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " 719132718Skan "the current R0KH-ID"); 720132718Skan wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", 721132718Skan parse.r0kh_id, parse.r0kh_id_len); 722132718Skan wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", 723132718Skan sm->r0kh_id, sm->r0kh_id_len); 724132718Skan return -1; 725132718Skan } 726169689Skan 727169689Skan if (parse.r1kh_id == NULL) { 728132718Skan wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); 729132718Skan return -1; 730132718Skan } 731132718Skan 732132718Skan if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) { 733169689Skan wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " 734132718Skan "ReassocResp"); 735169689Skan return -1; 736169689Skan } 737169689Skan 738132718Skan if (parse.rsn_pmkid == NULL || 739132718Skan os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { 740132718Skan wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " 741169689Skan "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); 742169689Skan return -1; 743169689Skan } 744169689Skan 745132718Skan count = 3; 746132718Skan if (parse.ric) 747169689Skan count += ieee802_11_ie_count(parse.ric, parse.ric_len); 748169689Skan if (ftie->mic_control[1] != count) { 749132718Skan wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " 750132718Skan "Control: received %u expected %u", 751132718Skan ftie->mic_control[1], count); 752132718Skan return -1; 753132718Skan } 754132718Skan 755132718Skan if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6, 756132718Skan parse.mdie - 2, parse.mdie_len + 2, 757132718Skan parse.ftie - 2, parse.ftie_len + 2, 758132718Skan parse.rsn - 2, parse.rsn_len + 2, 759132718Skan parse.ric, parse.ric_len, 760132718Skan mic) < 0) { 761132718Skan wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); 762132718Skan return -1; 763132718Skan } 764132718Skan 765132718Skan if (os_memcmp(mic, ftie->mic, 16) != 0) { 766169689Skan wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); 767169689Skan wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); 768132718Skan wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); 769132718Skan return -1; 770132718Skan } 771132718Skan 772132718Skan if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) 773132718Skan return -1; 774132718Skan 775132718Skan#ifdef CONFIG_IEEE80211W 776132718Skan if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0) 777132718Skan return -1; 778132718Skan#endif /* CONFIG_IEEE80211W */ 779132718Skan 780132718Skan if (sm->set_ptk_after_assoc) { 781132718Skan wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we " 782132718Skan "are associated"); 783132718Skan if (wpa_ft_install_ptk(sm, src_addr) < 0) 784132718Skan return -1; 785132718Skan sm->set_ptk_after_assoc = 0; 786132718Skan } 787132718Skan 788132718Skan if (parse.ric) { 789132718Skan wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", 790132718Skan parse.ric, parse.ric_len); 791132718Skan /* TODO: parse response and inform driver about results */ 792132718Skan } 793132718Skan 794132718Skan return 0; 795132718Skan} 796132718Skan 797169689Skan 798132718Skan/** 799132718Skan * wpa_ft_start_over_ds - Generate over-the-DS auth request 800132718Skan * @sm: Pointer to WPA state machine data from wpa_sm_init() 801169689Skan * @target_ap: Target AP Address 802132718Skan * @mdie: Mobility Domain IE from the target AP 803132718Skan * Returns: 0 on success, -1 on failure 804169689Skan */ 805132718Skanint wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, 806132718Skan const u8 *mdie) 807132718Skan{ 808132718Skan u8 *ft_ies; 809132718Skan size_t ft_ies_len; 810132718Skan 811132718Skan wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR, 812132718Skan MAC2STR(target_ap)); 813132718Skan 814169689Skan /* Generate a new SNonce */ 815132718Skan if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { 816132718Skan wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); 817169689Skan return -1; 818132718Skan } 819132718Skan 820132718Skan ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, 821132718Skan NULL, target_ap, NULL, 0, mdie); 822132718Skan if (ft_ies) { 823132718Skan sm->over_the_ds_in_progress = 1; 824132718Skan os_memcpy(sm->target_ap, target_ap, ETH_ALEN); 825132718Skan wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len); 826132718Skan os_free(ft_ies); 827132718Skan } 828132718Skan 829132718Skan return 0; 830132718Skan} 831132718Skan 832132718Skan#endif /* CONFIG_IEEE80211R */ 833132718Skan