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