]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * WPA Supplicant - IEEE 802.11r - Fast BSS Transition | |
c22bb5bb | 3 | * Copyright (c) 2006-2018, Jouni Malinen <j@w1.fi> |
6fc6879b | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
03da66bd | 12 | #include "crypto/aes_wrap.h" |
994eac7e | 13 | #include "crypto/sha384.h" |
3642c431 | 14 | #include "crypto/random.h" |
03da66bd JM |
15 | #include "common/ieee802_11_defs.h" |
16 | #include "common/ieee802_11_common.h" | |
dd8df6af MV |
17 | #include "common/ocv.h" |
18 | #include "drivers/driver.h" | |
6fc6879b JM |
19 | #include "wpa.h" |
20 | #include "wpa_i.h" | |
f73dd0a6 | 21 | #include "wpa_ie.h" |
0c46b1a5 | 22 | #include "pmksa_cache.h" |
6fc6879b JM |
23 | |
24 | #ifdef CONFIG_IEEE80211R | |
25 | ||
26 | int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, | |
98cd3d1c | 27 | const struct wpa_eapol_key *key, struct wpa_ptk *ptk) |
6fc6879b | 28 | { |
6fc6879b JM |
29 | u8 ptk_name[WPA_PMK_NAME_LEN]; |
30 | const u8 *anonce = key->key_nonce; | |
994eac7e | 31 | int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); |
0c46b1a5 JM |
32 | const u8 *mpmk; |
33 | size_t mpmk_len; | |
6fc6879b | 34 | |
0c46b1a5 JM |
35 | if (sm->xxkey_len > 0) { |
36 | mpmk = sm->xxkey; | |
37 | mpmk_len = sm->xxkey_len; | |
38 | } else if (sm->cur_pmksa) { | |
39 | mpmk = sm->cur_pmksa->pmk; | |
40 | mpmk_len = sm->cur_pmksa->pmk_len; | |
41 | } else { | |
6fc6879b JM |
42 | wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " |
43 | "derivation"); | |
44 | return -1; | |
45 | } | |
46 | ||
994eac7e | 47 | sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; |
0c46b1a5 | 48 | if (wpa_derive_pmk_r0(mpmk, mpmk_len, sm->ssid, |
a3e18dbb JM |
49 | sm->ssid_len, sm->mobility_domain, |
50 | sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, | |
994eac7e | 51 | sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) |
a3e18dbb JM |
52 | return -1; |
53 | wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, sm->pmk_r0_len); | |
6fc6879b JM |
54 | wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", |
55 | sm->pmk_r0_name, WPA_PMK_NAME_LEN); | |
a3e18dbb JM |
56 | sm->pmk_r1_len = sm->pmk_r0_len; |
57 | if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name, | |
58 | sm->r1kh_id, sm->own_addr, sm->pmk_r1, | |
59 | sm->pmk_r1_name) < 0) | |
60 | return -1; | |
61 | wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len); | |
26e23750 JM |
62 | wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, |
63 | WPA_PMK_NAME_LEN); | |
a3e18dbb JM |
64 | return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce, |
65 | sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk, | |
66 | ptk_name, sm->key_mgmt, sm->pairwise_cipher); | |
6fc6879b JM |
67 | } |
68 | ||
69 | ||
70 | /** | |
71 | * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters | |
72 | * @sm: Pointer to WPA state machine data from wpa_sm_init() | |
e7846b68 JM |
73 | * @ies: Association Response IEs or %NULL to clear FT parameters |
74 | * @ies_len: Length of ies buffer in octets | |
6fc6879b JM |
75 | * Returns: 0 on success, -1 on failure |
76 | */ | |
e7846b68 | 77 | int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) |
6fc6879b | 78 | { |
e7846b68 | 79 | struct wpa_ft_ies ft; |
9a33737a | 80 | int use_sha384; |
e7846b68 JM |
81 | |
82 | if (sm == NULL) | |
83 | return 0; | |
84 | ||
785f99b6 JM |
85 | if (!get_ie(ies, ies_len, WLAN_EID_MOBILITY_DOMAIN)) { |
86 | os_free(sm->assoc_resp_ies); | |
87 | sm->assoc_resp_ies = NULL; | |
88 | sm->assoc_resp_ies_len = 0; | |
89 | os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN); | |
90 | os_memset(sm->r0kh_id, 0, FT_R0KH_ID_MAX_LEN); | |
91 | sm->r0kh_id_len = 0; | |
92 | os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN); | |
93 | return 0; | |
94 | } | |
95 | ||
9a33737a JM |
96 | use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); |
97 | if (wpa_ft_parse_ies(ies, ies_len, &ft, use_sha384) < 0) | |
e7846b68 JM |
98 | return -1; |
99 | ||
785f99b6 | 100 | if (ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) |
e7846b68 JM |
101 | return -1; |
102 | ||
785f99b6 JM |
103 | wpa_hexdump(MSG_DEBUG, "FT: Mobility domain", |
104 | ft.mdie, MOBILITY_DOMAIN_ID_LEN); | |
105 | os_memcpy(sm->mobility_domain, ft.mdie, MOBILITY_DOMAIN_ID_LEN); | |
106 | sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN]; | |
107 | wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x", | |
108 | sm->mdie_ft_capab); | |
6fc6879b | 109 | |
e7846b68 JM |
110 | if (ft.r0kh_id) { |
111 | wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", | |
112 | ft.r0kh_id, ft.r0kh_id_len); | |
113 | os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len); | |
114 | sm->r0kh_id_len = ft.r0kh_id_len; | |
115 | } else { | |
6fc6879b JM |
116 | /* FIX: When should R0KH-ID be cleared? We need to keep the |
117 | * old R0KH-ID in order to be able to use this during FT. */ | |
118 | /* | |
119 | * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN); | |
120 | * sm->r0kh_id_len = 0; | |
121 | */ | |
122 | } | |
123 | ||
e7846b68 JM |
124 | if (ft.r1kh_id) { |
125 | wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", | |
126 | ft.r1kh_id, FT_R1KH_ID_LEN); | |
127 | os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN); | |
128 | } else | |
6fc6879b JM |
129 | os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN); |
130 | ||
55046414 JM |
131 | os_free(sm->assoc_resp_ies); |
132 | sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2); | |
133 | if (sm->assoc_resp_ies) { | |
134 | u8 *pos = sm->assoc_resp_ies; | |
785f99b6 JM |
135 | |
136 | os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2); | |
137 | pos += ft.mdie_len + 2; | |
138 | ||
55046414 JM |
139 | if (ft.ftie) { |
140 | os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2); | |
141 | pos += ft.ftie_len + 2; | |
142 | } | |
143 | sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies; | |
144 | wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from " | |
145 | "(Re)Association Response", | |
146 | sm->assoc_resp_ies, sm->assoc_resp_ies_len); | |
147 | } | |
148 | ||
6fc6879b JM |
149 | return 0; |
150 | } | |
151 | ||
152 | ||
153 | /** | |
babfbf15 | 154 | * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request |
6fc6879b JM |
155 | * @sm: Pointer to WPA state machine data from wpa_sm_init() |
156 | * @len: Buffer for returning the length of the IEs | |
157 | * @anonce: ANonce or %NULL if not yet available | |
158 | * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List | |
159 | * @kck: 128-bit KCK for MIC or %NULL if no MIC is used | |
98cd3d1c | 160 | * @kck_len: KCK length in octets |
6fc6879b | 161 | * @target_ap: Target AP address |
babfbf15 JM |
162 | * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL |
163 | * @ric_ies_len: Length of ric_ies buffer in octets | |
76b7981d | 164 | * @ap_mdie: Mobility Domain IE from the target AP |
6fc6879b JM |
165 | * Returns: Pointer to buffer with IEs or %NULL on failure |
166 | * | |
167 | * Caller is responsible for freeing the returned buffer with os_free(); | |
168 | */ | |
169 | static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, | |
170 | const u8 *anonce, const u8 *pmk_name, | |
98cd3d1c JM |
171 | const u8 *kck, size_t kck_len, |
172 | const u8 *target_ap, | |
76b7981d JM |
173 | const u8 *ric_ies, size_t ric_ies_len, |
174 | const u8 *ap_mdie) | |
6fc6879b JM |
175 | { |
176 | size_t buf_len; | |
06f12866 | 177 | u8 *buf, *pos, *ftie_len, *ftie_pos, *fte_mic, *elem_count; |
6fc6879b | 178 | struct rsn_mdie *mdie; |
6fc6879b JM |
179 | struct rsn_ie_hdr *rsnie; |
180 | u16 capab; | |
3dc3afe2 | 181 | int mdie_len; |
f73dd0a6 JM |
182 | u8 rsnxe[10]; |
183 | size_t rsnxe_len; | |
184 | int res; | |
6fc6879b JM |
185 | |
186 | sm->ft_completed = 0; | |
2a9c5217 | 187 | sm->ft_reassoc_completed = 0; |
6fc6879b | 188 | |
06f12866 JM |
189 | buf_len = 2 + sizeof(struct rsn_mdie) + 2 + |
190 | sizeof(struct rsn_ftie_sha384) + | |
babfbf15 | 191 | 2 + sm->r0kh_id_len + ric_ies_len + 100; |
6fc6879b JM |
192 | buf = os_zalloc(buf_len); |
193 | if (buf == NULL) | |
194 | return NULL; | |
195 | pos = buf; | |
196 | ||
babfbf15 | 197 | /* RSNIE[PMKR0Name/PMKR1Name] */ |
6fc6879b JM |
198 | rsnie = (struct rsn_ie_hdr *) pos; |
199 | rsnie->elem_id = WLAN_EID_RSN; | |
200 | WPA_PUT_LE16(rsnie->version, RSN_VERSION); | |
201 | pos = (u8 *) (rsnie + 1); | |
202 | ||
203 | /* Group Suite Selector */ | |
05a90d78 | 204 | if (!wpa_cipher_valid_group(sm->group_cipher)) { |
6fc6879b JM |
205 | wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", |
206 | sm->group_cipher); | |
207 | os_free(buf); | |
208 | return NULL; | |
209 | } | |
c3550295 JM |
210 | RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, |
211 | sm->group_cipher)); | |
6fc6879b JM |
212 | pos += RSN_SELECTOR_LEN; |
213 | ||
214 | /* Pairwise Suite Count */ | |
215 | WPA_PUT_LE16(pos, 1); | |
216 | pos += 2; | |
217 | ||
218 | /* Pairwise Suite List */ | |
c3550295 | 219 | if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { |
6fc6879b JM |
220 | wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", |
221 | sm->pairwise_cipher); | |
222 | os_free(buf); | |
223 | return NULL; | |
224 | } | |
c3550295 JM |
225 | RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, |
226 | sm->pairwise_cipher)); | |
6fc6879b JM |
227 | pos += RSN_SELECTOR_LEN; |
228 | ||
229 | /* Authenticated Key Management Suite Count */ | |
230 | WPA_PUT_LE16(pos, 1); | |
231 | pos += 2; | |
232 | ||
233 | /* Authenticated Key Management Suite List */ | |
234 | if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) | |
235 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); | |
c22bb5bb JM |
236 | #ifdef CONFIG_SHA384 |
237 | else if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) | |
238 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); | |
239 | #endif /* CONFIG_SHA384 */ | |
6fc6879b JM |
240 | else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) |
241 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); | |
aa189ac9 JM |
242 | else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE) |
243 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); | |
5aa08153 JM |
244 | #ifdef CONFIG_FILS |
245 | else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256) | |
246 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); | |
247 | else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) | |
248 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); | |
249 | #endif /* CONFIG_FILS */ | |
6fc6879b JM |
250 | else { |
251 | wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", | |
252 | sm->key_mgmt); | |
253 | os_free(buf); | |
254 | return NULL; | |
255 | } | |
256 | pos += RSN_SELECTOR_LEN; | |
257 | ||
258 | /* RSN Capabilities */ | |
259 | capab = 0; | |
ded56f2f | 260 | if (sm->mfp) |
0b60b0aa | 261 | capab |= WPA_CAPABILITY_MFPC; |
ded56f2f JM |
262 | if (sm->mfp == 2) |
263 | capab |= WPA_CAPABILITY_MFPR; | |
55c12f5d MV |
264 | if (sm->ocv) |
265 | capab |= WPA_CAPABILITY_OCVC; | |
6fc6879b JM |
266 | WPA_PUT_LE16(pos, capab); |
267 | pos += 2; | |
268 | ||
269 | /* PMKID Count */ | |
270 | WPA_PUT_LE16(pos, 1); | |
271 | pos += 2; | |
272 | ||
273 | /* PMKID List [PMKR0Name/PMKR1Name] */ | |
274 | os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN); | |
275 | pos += WPA_PMK_NAME_LEN; | |
276 | ||
ef13b33a JM |
277 | /* Management Group Cipher Suite */ |
278 | switch (sm->mgmt_group_cipher) { | |
279 | case WPA_CIPHER_AES_128_CMAC: | |
6fc6879b JM |
280 | RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); |
281 | pos += RSN_SELECTOR_LEN; | |
ef13b33a JM |
282 | break; |
283 | case WPA_CIPHER_BIP_GMAC_128: | |
284 | RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128); | |
285 | pos += RSN_SELECTOR_LEN; | |
286 | break; | |
287 | case WPA_CIPHER_BIP_GMAC_256: | |
288 | RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256); | |
289 | pos += RSN_SELECTOR_LEN; | |
290 | break; | |
291 | case WPA_CIPHER_BIP_CMAC_256: | |
292 | RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256); | |
293 | pos += RSN_SELECTOR_LEN; | |
294 | break; | |
6fc6879b | 295 | } |
6fc6879b JM |
296 | |
297 | rsnie->len = (pos - (u8 *) rsnie) - 2; | |
298 | ||
299 | /* MDIE */ | |
3dc3afe2 AM |
300 | mdie_len = wpa_ft_add_mdie(sm, pos, buf_len - (pos - buf), ap_mdie); |
301 | if (mdie_len <= 0) { | |
302 | os_free(buf); | |
303 | return NULL; | |
304 | } | |
305 | mdie = (struct rsn_mdie *) (pos + 2); | |
306 | pos += mdie_len; | |
6fc6879b | 307 | |
3db65314 | 308 | /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */ |
ccfab35a | 309 | ftie_pos = pos; |
6fc6879b JM |
310 | *pos++ = WLAN_EID_FAST_BSS_TRANSITION; |
311 | ftie_len = pos++; | |
06f12866 JM |
312 | if (wpa_key_mgmt_sha384(sm->key_mgmt)) { |
313 | struct rsn_ftie_sha384 *ftie; | |
314 | ||
315 | ftie = (struct rsn_ftie_sha384 *) pos; | |
316 | fte_mic = ftie->mic; | |
317 | elem_count = &ftie->mic_control[1]; | |
318 | pos += sizeof(*ftie); | |
319 | os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); | |
320 | if (anonce) | |
321 | os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); | |
322 | } else { | |
323 | struct rsn_ftie *ftie; | |
324 | ||
325 | ftie = (struct rsn_ftie *) pos; | |
326 | fte_mic = ftie->mic; | |
327 | elem_count = &ftie->mic_control[1]; | |
328 | pos += sizeof(*ftie); | |
329 | os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); | |
330 | if (anonce) | |
331 | os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); | |
332 | } | |
3db65314 JM |
333 | if (kck) { |
334 | /* R1KH-ID sub-element in third FT message */ | |
335 | *pos++ = FTIE_SUBELEM_R1KH_ID; | |
336 | *pos++ = FT_R1KH_ID_LEN; | |
337 | os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN); | |
338 | pos += FT_R1KH_ID_LEN; | |
339 | } | |
6fc6879b JM |
340 | /* R0KH-ID sub-element */ |
341 | *pos++ = FTIE_SUBELEM_R0KH_ID; | |
342 | *pos++ = sm->r0kh_id_len; | |
343 | os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len); | |
344 | pos += sm->r0kh_id_len; | |
dd8df6af MV |
345 | #ifdef CONFIG_OCV |
346 | if (kck && wpa_sm_ocv_enabled(sm)) { | |
347 | /* OCI sub-element in the third FT message */ | |
348 | struct wpa_channel_info ci; | |
349 | ||
350 | if (wpa_sm_channel_info(sm, &ci) != 0) { | |
351 | wpa_printf(MSG_WARNING, | |
352 | "Failed to get channel info for OCI element in FTE"); | |
353 | os_free(buf); | |
354 | return NULL; | |
355 | } | |
356 | ||
357 | *pos++ = FTIE_SUBELEM_OCI; | |
358 | *pos++ = OCV_OCI_LEN; | |
359 | if (ocv_insert_oci(&ci, &pos) < 0) { | |
360 | os_free(buf); | |
361 | return NULL; | |
362 | } | |
363 | } | |
364 | #endif /* CONFIG_OCV */ | |
6fc6879b JM |
365 | *ftie_len = pos - ftie_len - 1; |
366 | ||
babfbf15 JM |
367 | if (ric_ies) { |
368 | /* RIC Request */ | |
369 | os_memcpy(pos, ric_ies, ric_ies_len); | |
370 | pos += ric_ies_len; | |
371 | } | |
372 | ||
f73dd0a6 JM |
373 | res = wpa_gen_rsnxe(sm, rsnxe, sizeof(rsnxe)); |
374 | if (res < 0) { | |
375 | os_free(buf); | |
376 | return NULL; | |
377 | } | |
378 | rsnxe_len = res; | |
379 | ||
6fc6879b JM |
380 | if (kck) { |
381 | /* | |
c1e033b0 | 382 | * IEEE Std 802.11r-2008, 11A.8.4 |
6fc6879b JM |
383 | * MIC shall be calculated over: |
384 | * non-AP STA MAC address | |
385 | * Target AP MAC address | |
386 | * Transaction seq number (5 for ReassocReq, 3 otherwise) | |
387 | * RSN IE | |
388 | * MDIE | |
389 | * FTIE (with MIC field set to 0) | |
390 | * RIC-Request (if present) | |
f73dd0a6 | 391 | * RSNXE (if present) |
6fc6879b | 392 | */ |
babfbf15 | 393 | /* Information element count */ |
06f12866 | 394 | *elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len); |
f73dd0a6 JM |
395 | if (rsnxe_len) |
396 | *elem_count += 1; | |
98cd3d1c | 397 | if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5, |
6fc6879b | 398 | ((u8 *) mdie) - 2, 2 + sizeof(*mdie), |
ccfab35a | 399 | ftie_pos, 2 + *ftie_len, |
babfbf15 | 400 | (u8 *) rsnie, 2 + rsnie->len, ric_ies, |
f73dd0a6 JM |
401 | ric_ies_len, rsnxe_len ? rsnxe : NULL, rsnxe_len, |
402 | fte_mic) < 0) { | |
6fc6879b JM |
403 | wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); |
404 | os_free(buf); | |
405 | return NULL; | |
406 | } | |
407 | } | |
408 | ||
409 | *len = pos - buf; | |
410 | ||
411 | return buf; | |
412 | } | |
413 | ||
414 | ||
6fc6879b JM |
415 | static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) |
416 | { | |
417 | int keylen; | |
71934751 | 418 | enum wpa_alg alg; |
6fc6879b JM |
419 | u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 }; |
420 | ||
421 | wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); | |
422 | ||
c3550295 | 423 | if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { |
6fc6879b JM |
424 | wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d", |
425 | sm->pairwise_cipher); | |
426 | return -1; | |
427 | } | |
428 | ||
c3550295 JM |
429 | alg = wpa_cipher_to_alg(sm->pairwise_cipher); |
430 | keylen = wpa_cipher_key_len(sm->pairwise_cipher); | |
431 | ||
a919a260 AW |
432 | if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc), |
433 | (u8 *) sm->ptk.tk, keylen, | |
434 | KEY_FLAG_PAIRWISE_RX_TX) < 0) { | |
6fc6879b JM |
435 | wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver"); |
436 | return -1; | |
437 | } | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
442 | ||
443 | /** | |
444 | * wpa_ft_prepare_auth_request - Generate over-the-air auth request | |
445 | * @sm: Pointer to WPA state machine data from wpa_sm_init() | |
76b7981d | 446 | * @mdie: Target AP MDIE |
6fc6879b JM |
447 | * Returns: 0 on success, -1 on failure |
448 | */ | |
76b7981d | 449 | int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) |
6fc6879b JM |
450 | { |
451 | u8 *ft_ies; | |
452 | size_t ft_ies_len; | |
453 | ||
454 | /* Generate a new SNonce */ | |
3642c431 | 455 | if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { |
6fc6879b JM |
456 | wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); |
457 | return -1; | |
458 | } | |
459 | ||
460 | ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, | |
98cd3d1c | 461 | NULL, 0, sm->bssid, NULL, 0, mdie); |
6fc6879b JM |
462 | if (ft_ies) { |
463 | wpa_sm_update_ft_ies(sm, sm->mobility_domain, | |
464 | ft_ies, ft_ies_len); | |
465 | os_free(ft_ies); | |
466 | } | |
467 | ||
468 | return 0; | |
469 | } | |
470 | ||
471 | ||
3dc3afe2 AM |
472 | int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *buf, size_t buf_len, |
473 | const u8 *ap_mdie) | |
474 | { | |
475 | u8 *pos = buf; | |
476 | struct rsn_mdie *mdie; | |
477 | ||
478 | if (buf_len < 2 + sizeof(*mdie)) { | |
479 | wpa_printf(MSG_INFO, | |
480 | "FT: Failed to add MDIE: short buffer, length=%zu", | |
481 | buf_len); | |
482 | return 0; | |
483 | } | |
484 | ||
485 | *pos++ = WLAN_EID_MOBILITY_DOMAIN; | |
486 | *pos++ = sizeof(*mdie); | |
487 | mdie = (struct rsn_mdie *) pos; | |
488 | os_memcpy(mdie->mobility_domain, sm->mobility_domain, | |
489 | MOBILITY_DOMAIN_ID_LEN); | |
490 | mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : | |
491 | sm->mdie_ft_capab; | |
492 | ||
493 | return 2 + sizeof(*mdie); | |
494 | } | |
495 | ||
496 | ||
497 | const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm) | |
498 | { | |
499 | return sm->mobility_domain; | |
500 | } | |
501 | ||
502 | ||
6fc6879b | 503 | int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, |
babfbf15 JM |
504 | int ft_action, const u8 *target_ap, |
505 | const u8 *ric_ies, size_t ric_ies_len) | |
6fc6879b JM |
506 | { |
507 | u8 *ft_ies; | |
98cd3d1c | 508 | size_t ft_ies_len; |
6fc6879b JM |
509 | struct wpa_ft_ies parse; |
510 | struct rsn_mdie *mdie; | |
6fc6879b JM |
511 | u8 ptk_name[WPA_PMK_NAME_LEN]; |
512 | int ret; | |
513 | const u8 *bssid; | |
2f373878 JM |
514 | const u8 *kck; |
515 | size_t kck_len; | |
9a33737a JM |
516 | int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); |
517 | const u8 *anonce, *snonce; | |
6fc6879b JM |
518 | |
519 | wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); | |
babfbf15 | 520 | wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len); |
6fc6879b JM |
521 | |
522 | if (ft_action) { | |
523 | if (!sm->over_the_ds_in_progress) { | |
524 | wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " | |
525 | "- drop FT Action Response"); | |
526 | return -1; | |
527 | } | |
528 | ||
529 | if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) { | |
530 | wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " | |
531 | "with this Target AP - drop FT Action " | |
532 | "Response"); | |
533 | return -1; | |
534 | } | |
535 | } | |
536 | ||
aa189ac9 | 537 | if (!wpa_key_mgmt_ft(sm->key_mgmt)) { |
6fc6879b JM |
538 | wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " |
539 | "enabled for this connection"); | |
540 | return -1; | |
541 | } | |
542 | ||
9a33737a | 543 | if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) { |
6fc6879b JM |
544 | wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); |
545 | return -1; | |
546 | } | |
547 | ||
548 | mdie = (struct rsn_mdie *) parse.mdie; | |
549 | if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || | |
550 | os_memcmp(mdie->mobility_domain, sm->mobility_domain, | |
551 | MOBILITY_DOMAIN_ID_LEN) != 0) { | |
552 | wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); | |
553 | return -1; | |
554 | } | |
555 | ||
9a33737a JM |
556 | if (use_sha384) { |
557 | struct rsn_ftie_sha384 *ftie; | |
558 | ||
559 | ftie = (struct rsn_ftie_sha384 *) parse.ftie; | |
560 | if (!ftie || parse.ftie_len < sizeof(*ftie)) { | |
561 | wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); | |
562 | return -1; | |
563 | } | |
564 | ||
565 | anonce = ftie->anonce; | |
566 | snonce = ftie->snonce; | |
567 | } else { | |
568 | struct rsn_ftie *ftie; | |
569 | ||
570 | ftie = (struct rsn_ftie *) parse.ftie; | |
571 | if (!ftie || parse.ftie_len < sizeof(*ftie)) { | |
572 | wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); | |
573 | return -1; | |
574 | } | |
575 | ||
576 | anonce = ftie->anonce; | |
577 | snonce = ftie->snonce; | |
6fc6879b JM |
578 | } |
579 | ||
9a33737a | 580 | if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) { |
148fb67d JM |
581 | wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); |
582 | wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", | |
9a33737a | 583 | snonce, WPA_NONCE_LEN); |
148fb67d JM |
584 | wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", |
585 | sm->snonce, WPA_NONCE_LEN); | |
586 | return -1; | |
587 | } | |
588 | ||
6fc6879b JM |
589 | if (parse.r0kh_id == NULL) { |
590 | wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); | |
591 | return -1; | |
592 | } | |
593 | ||
594 | if (parse.r0kh_id_len != sm->r0kh_id_len || | |
0d15b69f JM |
595 | os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) |
596 | { | |
6fc6879b JM |
597 | wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " |
598 | "the current R0KH-ID"); | |
599 | wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", | |
600 | parse.r0kh_id, parse.r0kh_id_len); | |
601 | wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", | |
602 | sm->r0kh_id, sm->r0kh_id_len); | |
603 | return -1; | |
604 | } | |
605 | ||
606 | if (parse.r1kh_id == NULL) { | |
607 | wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); | |
608 | return -1; | |
609 | } | |
610 | ||
611 | if (parse.rsn_pmkid == NULL || | |
0d15b69f JM |
612 | os_memcmp_const(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) |
613 | { | |
6fc6879b JM |
614 | wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in " |
615 | "RSNIE"); | |
616 | return -1; | |
617 | } | |
618 | ||
528f263c JM |
619 | if (sm->mfp == 2 && !(parse.rsn_capab & WPA_CAPABILITY_MFPC)) { |
620 | wpa_printf(MSG_INFO, | |
621 | "FT: Target AP does not support PMF, but local configuration requires that"); | |
622 | return -1; | |
623 | } | |
528f263c | 624 | |
6fc6879b JM |
625 | os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); |
626 | wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); | |
627 | wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); | |
9a33737a JM |
628 | wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN); |
629 | os_memcpy(sm->anonce, anonce, WPA_NONCE_LEN); | |
a3e18dbb JM |
630 | if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name, |
631 | sm->r1kh_id, sm->own_addr, sm->pmk_r1, | |
632 | sm->pmk_r1_name) < 0) | |
633 | return -1; | |
634 | sm->pmk_r1_len = sm->pmk_r0_len; | |
635 | wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len); | |
6fc6879b JM |
636 | wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", |
637 | sm->pmk_r1_name, WPA_PMK_NAME_LEN); | |
638 | ||
658d1662 | 639 | bssid = target_ap; |
a3e18dbb | 640 | if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, |
9a33737a | 641 | anonce, sm->own_addr, bssid, |
a3e18dbb JM |
642 | sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt, |
643 | sm->pairwise_cipher) < 0) | |
98cd3d1c | 644 | return -1; |
6fc6879b | 645 | |
2f373878 JM |
646 | if (wpa_key_mgmt_fils(sm->key_mgmt)) { |
647 | kck = sm->ptk.kck2; | |
648 | kck_len = sm->ptk.kck2_len; | |
649 | } else { | |
650 | kck = sm->ptk.kck; | |
651 | kck_len = sm->ptk.kck_len; | |
652 | } | |
9a33737a | 653 | ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, anonce, |
98cd3d1c | 654 | sm->pmk_r1_name, |
2f373878 | 655 | kck, kck_len, bssid, |
76b7981d JM |
656 | ric_ies, ric_ies_len, |
657 | parse.mdie ? parse.mdie - 2 : NULL); | |
6fc6879b JM |
658 | if (ft_ies) { |
659 | wpa_sm_update_ft_ies(sm, sm->mobility_domain, | |
660 | ft_ies, ft_ies_len); | |
661 | os_free(ft_ies); | |
662 | } | |
663 | ||
2a7e7f4e | 664 | wpa_sm_mark_authenticated(sm, bssid); |
6fc6879b | 665 | ret = wpa_ft_install_ptk(sm, bssid); |
86f7b62a JM |
666 | if (ret) { |
667 | /* | |
668 | * Some drivers do not support key configuration when we are | |
669 | * not associated with the target AP. Work around this by | |
670 | * trying again after the following reassociation gets | |
671 | * completed. | |
672 | */ | |
673 | wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to " | |
674 | "association - try again after reassociation"); | |
675 | sm->set_ptk_after_assoc = 1; | |
676 | } else | |
677 | sm->set_ptk_after_assoc = 0; | |
6fc6879b | 678 | |
86f7b62a JM |
679 | sm->ft_completed = 1; |
680 | if (ft_action) { | |
681 | /* | |
682 | * The caller is expected trigger re-association with the | |
683 | * Target AP. | |
684 | */ | |
685 | os_memcpy(sm->bssid, target_ap, ETH_ALEN); | |
6fc6879b JM |
686 | } |
687 | ||
86f7b62a | 688 | return 0; |
6fc6879b JM |
689 | } |
690 | ||
691 | ||
692 | int wpa_ft_is_completed(struct wpa_sm *sm) | |
693 | { | |
694 | if (sm == NULL) | |
695 | return 0; | |
696 | ||
aa189ac9 | 697 | if (!wpa_key_mgmt_ft(sm->key_mgmt)) |
6fc6879b JM |
698 | return 0; |
699 | ||
700 | return sm->ft_completed; | |
701 | } | |
702 | ||
703 | ||
02db75b6 DG |
704 | void wpa_reset_ft_completed(struct wpa_sm *sm) |
705 | { | |
706 | if (sm != NULL) | |
707 | sm->ft_completed = 0; | |
708 | } | |
709 | ||
710 | ||
b27f13ed JM |
711 | static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, |
712 | size_t gtk_elem_len) | |
713 | { | |
714 | u8 gtk[32]; | |
715 | int keyidx; | |
71934751 | 716 | enum wpa_alg alg; |
b27f13ed | 717 | size_t gtk_len, keylen, rsc_len; |
657b4c45 JM |
718 | const u8 *kek; |
719 | size_t kek_len; | |
720 | ||
721 | if (wpa_key_mgmt_fils(sm->key_mgmt)) { | |
722 | kek = sm->ptk.kek2; | |
723 | kek_len = sm->ptk.kek2_len; | |
724 | } else { | |
725 | kek = sm->ptk.kek; | |
726 | kek_len = sm->ptk.kek_len; | |
727 | } | |
b27f13ed JM |
728 | |
729 | if (gtk_elem == NULL) { | |
730 | wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE"); | |
731 | return 0; | |
732 | } | |
733 | ||
734 | wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp", | |
735 | gtk_elem, gtk_elem_len); | |
736 | ||
39eb4d08 JM |
737 | if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 || |
738 | gtk_elem_len - 19 > sizeof(gtk)) { | |
b27f13ed JM |
739 | wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem " |
740 | "length %lu", (unsigned long) gtk_elem_len); | |
741 | return -1; | |
742 | } | |
39eb4d08 | 743 | gtk_len = gtk_elem_len - 19; |
657b4c45 | 744 | if (aes_unwrap(kek, kek_len, gtk_len / 8, gtk_elem + 11, gtk)) { |
b27f13ed JM |
745 | wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " |
746 | "decrypt GTK"); | |
747 | return -1; | |
748 | } | |
749 | ||
c3550295 JM |
750 | keylen = wpa_cipher_key_len(sm->group_cipher); |
751 | rsc_len = wpa_cipher_rsc_len(sm->group_cipher); | |
752 | alg = wpa_cipher_to_alg(sm->group_cipher); | |
753 | if (alg == WPA_ALG_NONE) { | |
b27f13ed JM |
754 | wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", |
755 | sm->group_cipher); | |
756 | return -1; | |
757 | } | |
758 | ||
759 | if (gtk_len < keylen) { | |
760 | wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE"); | |
761 | return -1; | |
762 | } | |
763 | ||
39eb4d08 | 764 | /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */ |
b27f13ed | 765 | |
39eb4d08 | 766 | keyidx = WPA_GET_LE16(gtk_elem) & 0x03; |
b27f13ed | 767 | |
39eb4d08 | 768 | if (gtk_elem[2] != keylen) { |
b27f13ed JM |
769 | wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d " |
770 | "negotiated %lu", | |
39eb4d08 | 771 | gtk_elem[2], (unsigned long) keylen); |
b27f13ed JM |
772 | return -1; |
773 | } | |
774 | ||
775 | wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); | |
b54c9ff9 JM |
776 | if (sm->group_cipher == WPA_CIPHER_TKIP) { |
777 | /* Swap Tx/Rx keys for Michael MIC */ | |
778 | u8 tmp[8]; | |
779 | os_memcpy(tmp, gtk + 16, 8); | |
780 | os_memcpy(gtk + 16, gtk + 24, 8); | |
781 | os_memcpy(gtk + 24, tmp, 8); | |
782 | } | |
0382097e | 783 | if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0, |
a919a260 AW |
784 | gtk_elem + 3, rsc_len, gtk, keylen, |
785 | KEY_FLAG_GROUP_RX) < 0) { | |
b27f13ed JM |
786 | wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " |
787 | "driver."); | |
788 | return -1; | |
789 | } | |
790 | ||
791 | return 0; | |
792 | } | |
793 | ||
794 | ||
b27f13ed JM |
795 | static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, |
796 | size_t igtk_elem_len) | |
797 | { | |
ef13b33a JM |
798 | u8 igtk[WPA_IGTK_MAX_LEN]; |
799 | size_t igtk_len; | |
b27f13ed | 800 | u16 keyidx; |
657b4c45 JM |
801 | const u8 *kek; |
802 | size_t kek_len; | |
803 | ||
804 | if (wpa_key_mgmt_fils(sm->key_mgmt)) { | |
805 | kek = sm->ptk.kek2; | |
806 | kek_len = sm->ptk.kek2_len; | |
807 | } else { | |
808 | kek = sm->ptk.kek; | |
809 | kek_len = sm->ptk.kek_len; | |
810 | } | |
b27f13ed | 811 | |
ef13b33a JM |
812 | if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC && |
813 | sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 && | |
814 | sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 && | |
815 | sm->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256) | |
b27f13ed JM |
816 | return 0; |
817 | ||
818 | if (igtk_elem == NULL) { | |
819 | wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE"); | |
820 | return 0; | |
821 | } | |
822 | ||
823 | wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp", | |
824 | igtk_elem, igtk_elem_len); | |
825 | ||
ef13b33a JM |
826 | igtk_len = wpa_cipher_key_len(sm->mgmt_group_cipher); |
827 | if (igtk_elem_len != 2 + 6 + 1 + igtk_len + 8) { | |
b27f13ed JM |
828 | wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem " |
829 | "length %lu", (unsigned long) igtk_elem_len); | |
830 | return -1; | |
831 | } | |
ef13b33a | 832 | if (igtk_elem[8] != igtk_len) { |
ff89afb7 JM |
833 | wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length " |
834 | "%d", igtk_elem[8]); | |
835 | return -1; | |
836 | } | |
837 | ||
ef13b33a | 838 | if (aes_unwrap(kek, kek_len, igtk_len / 8, igtk_elem + 9, igtk)) { |
b27f13ed JM |
839 | wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " |
840 | "decrypt IGTK"); | |
841 | return -1; | |
842 | } | |
843 | ||
ff89afb7 | 844 | /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */ |
b27f13ed JM |
845 | |
846 | keyidx = WPA_GET_LE16(igtk_elem); | |
847 | ||
848 | wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, | |
ef13b33a JM |
849 | igtk_len); |
850 | if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), | |
851 | broadcast_ether_addr, keyidx, 0, | |
a919a260 AW |
852 | igtk_elem + 2, 6, igtk, igtk_len, |
853 | KEY_FLAG_GROUP_RX) < 0) { | |
b27f13ed JM |
854 | wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " |
855 | "driver."); | |
31bc66e4 | 856 | forced_memzero(igtk, sizeof(igtk)); |
b27f13ed JM |
857 | return -1; |
858 | } | |
31bc66e4 | 859 | forced_memzero(igtk, sizeof(igtk)); |
b27f13ed JM |
860 | |
861 | return 0; | |
862 | } | |
b27f13ed JM |
863 | |
864 | ||
2d4c78ae JM |
865 | static int wpa_ft_process_bigtk_subelem(struct wpa_sm *sm, const u8 *bigtk_elem, |
866 | size_t bigtk_elem_len) | |
867 | { | |
868 | u8 bigtk[WPA_BIGTK_MAX_LEN]; | |
869 | size_t bigtk_len; | |
870 | u16 keyidx; | |
871 | const u8 *kek; | |
872 | size_t kek_len; | |
873 | ||
874 | if (!sm->beacon_prot || !bigtk_elem || | |
875 | (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC && | |
876 | sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 && | |
877 | sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 && | |
878 | sm->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256)) | |
879 | return 0; | |
880 | ||
881 | if (wpa_key_mgmt_fils(sm->key_mgmt)) { | |
882 | kek = sm->ptk.kek2; | |
883 | kek_len = sm->ptk.kek2_len; | |
884 | } else { | |
885 | kek = sm->ptk.kek; | |
886 | kek_len = sm->ptk.kek_len; | |
887 | } | |
888 | ||
889 | wpa_hexdump_key(MSG_DEBUG, "FT: Received BIGTK in Reassoc Resp", | |
890 | bigtk_elem, bigtk_elem_len); | |
891 | ||
892 | bigtk_len = wpa_cipher_key_len(sm->mgmt_group_cipher); | |
893 | if (bigtk_elem_len != 2 + 6 + 1 + bigtk_len + 8) { | |
894 | wpa_printf(MSG_DEBUG, | |
895 | "FT: Invalid BIGTK sub-elem length %lu", | |
896 | (unsigned long) bigtk_elem_len); | |
897 | return -1; | |
898 | } | |
899 | if (bigtk_elem[8] != bigtk_len) { | |
900 | wpa_printf(MSG_DEBUG, | |
901 | "FT: Invalid BIGTK sub-elem Key Length %d", | |
902 | bigtk_elem[8]); | |
903 | return -1; | |
904 | } | |
905 | ||
906 | if (aes_unwrap(kek, kek_len, bigtk_len / 8, bigtk_elem + 9, bigtk)) { | |
907 | wpa_printf(MSG_WARNING, | |
908 | "FT: AES unwrap failed - could not decrypt BIGTK"); | |
909 | return -1; | |
910 | } | |
911 | ||
912 | /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */ | |
913 | ||
914 | keyidx = WPA_GET_LE16(bigtk_elem); | |
915 | ||
916 | wpa_hexdump_key(MSG_DEBUG, "FT: BIGTK from Reassoc Resp", bigtk, | |
917 | bigtk_len); | |
918 | if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), | |
919 | broadcast_ether_addr, keyidx, 0, | |
920 | bigtk_elem + 2, 6, bigtk, bigtk_len, | |
921 | KEY_FLAG_GROUP_RX) < 0) { | |
922 | wpa_printf(MSG_WARNING, | |
923 | "WPA: Failed to set BIGTK to the driver"); | |
924 | forced_memzero(bigtk, sizeof(bigtk)); | |
925 | return -1; | |
926 | } | |
927 | forced_memzero(bigtk, sizeof(bigtk)); | |
928 | ||
929 | return 0; | |
930 | } | |
931 | ||
932 | ||
6fc6879b | 933 | int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, |
658d1662 | 934 | size_t ies_len, const u8 *src_addr) |
6fc6879b JM |
935 | { |
936 | struct wpa_ft_ies parse; | |
937 | struct rsn_mdie *mdie; | |
8aaf894d | 938 | unsigned int count; |
98cd3d1c | 939 | u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; |
2f373878 JM |
940 | const u8 *kck; |
941 | size_t kck_len; | |
9a33737a JM |
942 | int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); |
943 | const u8 *anonce, *snonce, *fte_mic; | |
944 | u8 fte_elem_count; | |
6fc6879b JM |
945 | |
946 | wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); | |
947 | ||
aa189ac9 | 948 | if (!wpa_key_mgmt_ft(sm->key_mgmt)) { |
6fc6879b JM |
949 | wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " |
950 | "enabled for this connection"); | |
951 | return -1; | |
952 | } | |
953 | ||
2a9c5217 JM |
954 | if (sm->ft_reassoc_completed) { |
955 | wpa_printf(MSG_DEBUG, "FT: Reassociation has already been completed for this FT protocol instance - ignore unexpected retransmission"); | |
956 | return 0; | |
957 | } | |
958 | ||
9a33737a | 959 | if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) { |
6fc6879b JM |
960 | wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); |
961 | return -1; | |
962 | } | |
963 | ||
964 | mdie = (struct rsn_mdie *) parse.mdie; | |
965 | if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || | |
966 | os_memcmp(mdie->mobility_domain, sm->mobility_domain, | |
967 | MOBILITY_DOMAIN_ID_LEN) != 0) { | |
968 | wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); | |
969 | return -1; | |
970 | } | |
971 | ||
9a33737a JM |
972 | if (use_sha384) { |
973 | struct rsn_ftie_sha384 *ftie; | |
974 | ||
975 | ftie = (struct rsn_ftie_sha384 *) parse.ftie; | |
976 | if (!ftie || parse.ftie_len < sizeof(*ftie)) { | |
977 | wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); | |
978 | return -1; | |
979 | } | |
980 | ||
981 | anonce = ftie->anonce; | |
982 | snonce = ftie->snonce; | |
983 | fte_elem_count = ftie->mic_control[1]; | |
984 | fte_mic = ftie->mic; | |
985 | } else { | |
986 | struct rsn_ftie *ftie; | |
987 | ||
988 | ftie = (struct rsn_ftie *) parse.ftie; | |
989 | if (!ftie || parse.ftie_len < sizeof(*ftie)) { | |
990 | wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); | |
991 | return -1; | |
992 | } | |
993 | ||
994 | anonce = ftie->anonce; | |
995 | snonce = ftie->snonce; | |
996 | fte_elem_count = ftie->mic_control[1]; | |
997 | fte_mic = ftie->mic; | |
6fc6879b JM |
998 | } |
999 | ||
9a33737a | 1000 | if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) { |
148fb67d JM |
1001 | wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); |
1002 | wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", | |
9a33737a | 1003 | snonce, WPA_NONCE_LEN); |
148fb67d JM |
1004 | wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", |
1005 | sm->snonce, WPA_NONCE_LEN); | |
1006 | return -1; | |
1007 | } | |
1008 | ||
9a33737a | 1009 | if (os_memcmp(anonce, sm->anonce, WPA_NONCE_LEN) != 0) { |
148fb67d JM |
1010 | wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); |
1011 | wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", | |
9a33737a | 1012 | anonce, WPA_NONCE_LEN); |
148fb67d JM |
1013 | wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", |
1014 | sm->anonce, WPA_NONCE_LEN); | |
1015 | return -1; | |
1016 | } | |
1017 | ||
6fc6879b JM |
1018 | if (parse.r0kh_id == NULL) { |
1019 | wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); | |
1020 | return -1; | |
1021 | } | |
1022 | ||
1023 | if (parse.r0kh_id_len != sm->r0kh_id_len || | |
0d15b69f JM |
1024 | os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) |
1025 | { | |
6fc6879b JM |
1026 | wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " |
1027 | "the current R0KH-ID"); | |
1028 | wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", | |
1029 | parse.r0kh_id, parse.r0kh_id_len); | |
1030 | wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", | |
1031 | sm->r0kh_id, sm->r0kh_id_len); | |
1032 | return -1; | |
1033 | } | |
1034 | ||
1035 | if (parse.r1kh_id == NULL) { | |
1036 | wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); | |
1037 | return -1; | |
1038 | } | |
1039 | ||
0d15b69f | 1040 | if (os_memcmp_const(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) { |
6fc6879b JM |
1041 | wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " |
1042 | "ReassocResp"); | |
1043 | return -1; | |
1044 | } | |
1045 | ||
1046 | if (parse.rsn_pmkid == NULL || | |
0d15b69f JM |
1047 | os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) |
1048 | { | |
6fc6879b JM |
1049 | wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " |
1050 | "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); | |
1051 | return -1; | |
1052 | } | |
1053 | ||
1054 | count = 3; | |
c284b461 HW |
1055 | if (parse.ric) |
1056 | count += ieee802_11_ie_count(parse.ric, parse.ric_len); | |
f73dd0a6 JM |
1057 | if (parse.rsnxe) |
1058 | count++; | |
9a33737a | 1059 | if (fte_elem_count != count) { |
8aaf894d JM |
1060 | wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " |
1061 | "Control: received %u expected %u", | |
9a33737a | 1062 | fte_elem_count, count); |
8aaf894d JM |
1063 | return -1; |
1064 | } | |
6fc6879b | 1065 | |
2f373878 JM |
1066 | if (wpa_key_mgmt_fils(sm->key_mgmt)) { |
1067 | kck = sm->ptk.kck2; | |
1068 | kck_len = sm->ptk.kck2_len; | |
1069 | } else { | |
1070 | kck = sm->ptk.kck; | |
1071 | kck_len = sm->ptk.kck_len; | |
1072 | } | |
1073 | ||
1074 | if (wpa_ft_mic(kck, kck_len, sm->own_addr, src_addr, 6, | |
6fc6879b JM |
1075 | parse.mdie - 2, parse.mdie_len + 2, |
1076 | parse.ftie - 2, parse.ftie_len + 2, | |
f238cf9f JM |
1077 | parse.rsn - 2, parse.rsn_len + 2, |
1078 | parse.ric, parse.ric_len, | |
f73dd0a6 JM |
1079 | parse.rsnxe ? parse.rsnxe - 2 : NULL, |
1080 | parse.rsnxe ? parse.rsnxe_len + 2 : 0, | |
6fc6879b JM |
1081 | mic) < 0) { |
1082 | wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); | |
1083 | return -1; | |
1084 | } | |
1085 | ||
9a33737a | 1086 | if (os_memcmp_const(mic, fte_mic, 16) != 0) { |
6fc6879b | 1087 | wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); |
9a33737a | 1088 | wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", fte_mic, 16); |
6fc6879b JM |
1089 | wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); |
1090 | return -1; | |
1091 | } | |
1092 | ||
1a8e9334 JM |
1093 | if (!sm->ap_rsn_ie) { |
1094 | wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, | |
1095 | "FT: No RSNE for this AP known - trying to get from scan results"); | |
1096 | if (wpa_sm_get_beacon_ie(sm) < 0) { | |
1097 | wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, | |
1098 | "FT: Could not find AP from the scan results"); | |
1099 | return -1; | |
1100 | } | |
1101 | wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, | |
1102 | "FT: Found the current AP from updated scan results"); | |
1103 | } | |
1104 | ||
1105 | if (sm->ap_rsn_ie && | |
1106 | wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), | |
1107 | sm->ap_rsn_ie, sm->ap_rsn_ie_len, | |
1108 | parse.rsn - 2, parse.rsn_len + 2)) { | |
1109 | wpa_msg(sm->ctx->msg_ctx, MSG_INFO, | |
1110 | "FT: RSNE mismatch between Beacon/ProbeResp and FT protocol Reassociation Response frame"); | |
1111 | wpa_hexdump(MSG_INFO, "RSNE in Beacon/ProbeResp", | |
1112 | sm->ap_rsn_ie, sm->ap_rsn_ie_len); | |
1113 | wpa_hexdump(MSG_INFO, | |
1114 | "RSNE in FT protocol Reassociation Response frame", | |
1115 | parse.rsn ? parse.rsn - 2 : NULL, | |
1116 | parse.rsn ? parse.rsn_len + 2 : 0); | |
1117 | return -1; | |
1118 | } | |
1119 | ||
1120 | if ((sm->ap_rsnxe && !parse.rsnxe) || | |
1121 | (!sm->ap_rsnxe && parse.rsnxe) || | |
1122 | (sm->ap_rsnxe && parse.rsnxe && | |
1123 | (sm->ap_rsnxe_len != 2 + parse.rsnxe_len || | |
1124 | os_memcmp(sm->ap_rsnxe, parse.rsnxe - 2, | |
1125 | sm->ap_rsnxe_len) != 0))) { | |
1126 | wpa_msg(sm->ctx->msg_ctx, MSG_INFO, | |
1127 | "FT: RSNXE mismatch between Beacon/ProbeResp and FT protocol Reassociation Response frame"); | |
1128 | wpa_hexdump(MSG_INFO, "RSNXE in Beacon/ProbeResp", | |
1129 | sm->ap_rsnxe, sm->ap_rsnxe_len); | |
1130 | wpa_hexdump(MSG_INFO, | |
1131 | "RSNXE in FT protocol Reassociation Response frame", | |
1132 | parse.rsnxe ? parse.rsnxe - 2 : NULL, | |
1133 | parse.rsnxe ? parse.rsnxe_len + 2 : 0); | |
1134 | return -1; | |
1135 | } | |
1136 | ||
dd8df6af MV |
1137 | #ifdef CONFIG_OCV |
1138 | if (wpa_sm_ocv_enabled(sm)) { | |
1139 | struct wpa_channel_info ci; | |
1140 | ||
1141 | if (wpa_sm_channel_info(sm, &ci) != 0) { | |
1142 | wpa_printf(MSG_WARNING, | |
1143 | "Failed to get channel info to validate received OCI in (Re)Assoc Response"); | |
1144 | return -1; | |
1145 | } | |
1146 | ||
1147 | if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci, | |
1148 | channel_width_to_int(ci.chanwidth), | |
1149 | ci.seg1_idx) != 0) { | |
1150 | wpa_printf(MSG_WARNING, "%s", ocv_errorstr); | |
1151 | return -1; | |
1152 | } | |
1153 | } | |
1154 | #endif /* CONFIG_OCV */ | |
1155 | ||
2a9c5217 JM |
1156 | sm->ft_reassoc_completed = 1; |
1157 | ||
2d4c78ae JM |
1158 | if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0 || |
1159 | wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0 || | |
1160 | wpa_ft_process_bigtk_subelem(sm, parse.bigtk, parse.bigtk_len) < 0) | |
6fc6879b | 1161 | return -1; |
6fc6879b | 1162 | |
86f7b62a JM |
1163 | if (sm->set_ptk_after_assoc) { |
1164 | wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we " | |
1165 | "are associated"); | |
1166 | if (wpa_ft_install_ptk(sm, src_addr) < 0) | |
1167 | return -1; | |
1168 | sm->set_ptk_after_assoc = 0; | |
1169 | } | |
1170 | ||
f238cf9f JM |
1171 | if (parse.ric) { |
1172 | wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", | |
1173 | parse.ric, parse.ric_len); | |
6a1ce395 DG |
1174 | /* TODO: parse response and inform driver about results when |
1175 | * using wpa_supplicant SME */ | |
f238cf9f JM |
1176 | } |
1177 | ||
6a1ce395 DG |
1178 | wpa_printf(MSG_DEBUG, "FT: Completed successfully"); |
1179 | ||
6fc6879b JM |
1180 | return 0; |
1181 | } | |
1182 | ||
1183 | ||
1184 | /** | |
1185 | * wpa_ft_start_over_ds - Generate over-the-DS auth request | |
1186 | * @sm: Pointer to WPA state machine data from wpa_sm_init() | |
76b7981d JM |
1187 | * @target_ap: Target AP Address |
1188 | * @mdie: Mobility Domain IE from the target AP | |
6fc6879b JM |
1189 | * Returns: 0 on success, -1 on failure |
1190 | */ | |
76b7981d JM |
1191 | int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, |
1192 | const u8 *mdie) | |
6fc6879b JM |
1193 | { |
1194 | u8 *ft_ies; | |
1195 | size_t ft_ies_len; | |
1196 | ||
1197 | wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR, | |
1198 | MAC2STR(target_ap)); | |
1199 | ||
1200 | /* Generate a new SNonce */ | |
3642c431 | 1201 | if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { |
6fc6879b JM |
1202 | wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); |
1203 | return -1; | |
1204 | } | |
1205 | ||
1206 | ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, | |
98cd3d1c | 1207 | NULL, 0, target_ap, NULL, 0, mdie); |
6fc6879b JM |
1208 | if (ft_ies) { |
1209 | sm->over_the_ds_in_progress = 1; | |
1210 | os_memcpy(sm->target_ap, target_ap, ETH_ALEN); | |
1211 | wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len); | |
1212 | os_free(ft_ies); | |
1213 | } | |
1214 | ||
1215 | return 0; | |
1216 | } | |
1217 | ||
1218 | #endif /* CONFIG_IEEE80211R */ |