]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * wpa_supplicant - WPA/RSN IE and KDE processing | |
56586197 | 3 | * Copyright (c) 2003-2008, 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" | |
12 | #include "wpa.h" | |
13 | #include "pmksa_cache.h" | |
90973fb2 | 14 | #include "common/ieee802_11_defs.h" |
6fc6879b JM |
15 | #include "wpa_i.h" |
16 | #include "wpa_ie.h" | |
17 | ||
18 | ||
6fc6879b JM |
19 | /** |
20 | * wpa_parse_wpa_ie - Parse WPA/RSN IE | |
21 | * @wpa_ie: Pointer to WPA or RSN IE | |
22 | * @wpa_ie_len: Length of the WPA/RSN IE | |
23 | * @data: Pointer to data area for parsing results | |
24 | * Returns: 0 on success, -1 on failure | |
25 | * | |
26 | * Parse the contents of WPA or RSN IE and write the parsed data into data. | |
27 | */ | |
28 | int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, | |
29 | struct wpa_ie_data *data) | |
30 | { | |
31 | if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) | |
32 | return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); | |
33 | else | |
34 | return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); | |
35 | } | |
36 | ||
37 | ||
38 | static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, | |
39 | int pairwise_cipher, int group_cipher, | |
40 | int key_mgmt) | |
41 | { | |
42 | u8 *pos; | |
43 | struct wpa_ie_hdr *hdr; | |
c3550295 | 44 | u32 suite; |
6fc6879b JM |
45 | |
46 | if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + | |
47 | 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) | |
48 | return -1; | |
49 | ||
50 | hdr = (struct wpa_ie_hdr *) wpa_ie; | |
51 | hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; | |
52 | RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); | |
53 | WPA_PUT_LE16(hdr->version, WPA_VERSION); | |
54 | pos = (u8 *) (hdr + 1); | |
55 | ||
c3550295 JM |
56 | suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher); |
57 | if (suite == 0) { | |
6fc6879b JM |
58 | wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", |
59 | group_cipher); | |
60 | return -1; | |
61 | } | |
c3550295 | 62 | RSN_SELECTOR_PUT(pos, suite); |
6fc6879b JM |
63 | pos += WPA_SELECTOR_LEN; |
64 | ||
65 | *pos++ = 1; | |
66 | *pos++ = 0; | |
c3550295 JM |
67 | suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher); |
68 | if (suite == 0 || | |
69 | (!wpa_cipher_valid_pairwise(pairwise_cipher) && | |
70 | pairwise_cipher != WPA_CIPHER_NONE)) { | |
6fc6879b JM |
71 | wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", |
72 | pairwise_cipher); | |
73 | return -1; | |
74 | } | |
c3550295 | 75 | RSN_SELECTOR_PUT(pos, suite); |
6fc6879b JM |
76 | pos += WPA_SELECTOR_LEN; |
77 | ||
78 | *pos++ = 1; | |
79 | *pos++ = 0; | |
80 | if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { | |
81 | RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); | |
82 | } else if (key_mgmt == WPA_KEY_MGMT_PSK) { | |
83 | RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); | |
84 | } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) { | |
85 | RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE); | |
369c8d7b JM |
86 | } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { |
87 | RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM); | |
6fc6879b JM |
88 | } else { |
89 | wpa_printf(MSG_WARNING, "Invalid key management type (%d).", | |
90 | key_mgmt); | |
91 | return -1; | |
92 | } | |
93 | pos += WPA_SELECTOR_LEN; | |
94 | ||
95 | /* WPA Capabilities; use defaults, so no need to include it */ | |
96 | ||
97 | hdr->len = (pos - wpa_ie) - 2; | |
98 | ||
99 | WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); | |
100 | ||
101 | return pos - wpa_ie; | |
102 | } | |
103 | ||
104 | ||
105 | static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, | |
106 | int pairwise_cipher, int group_cipher, | |
107 | int key_mgmt, int mgmt_group_cipher, | |
108 | struct wpa_sm *sm) | |
109 | { | |
6fc6879b JM |
110 | u8 *pos; |
111 | struct rsn_ie_hdr *hdr; | |
112 | u16 capab; | |
c3550295 | 113 | u32 suite; |
6fc6879b JM |
114 | |
115 | if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN + | |
116 | 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + | |
117 | (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) { | |
118 | wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)", | |
119 | (unsigned long) rsn_ie_len); | |
120 | return -1; | |
121 | } | |
122 | ||
123 | hdr = (struct rsn_ie_hdr *) rsn_ie; | |
124 | hdr->elem_id = WLAN_EID_RSN; | |
125 | WPA_PUT_LE16(hdr->version, RSN_VERSION); | |
126 | pos = (u8 *) (hdr + 1); | |
127 | ||
c3550295 JM |
128 | suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); |
129 | if (suite == 0) { | |
6fc6879b JM |
130 | wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", |
131 | group_cipher); | |
132 | return -1; | |
133 | } | |
c3550295 | 134 | RSN_SELECTOR_PUT(pos, suite); |
6fc6879b JM |
135 | pos += RSN_SELECTOR_LEN; |
136 | ||
137 | *pos++ = 1; | |
138 | *pos++ = 0; | |
c3550295 JM |
139 | suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); |
140 | if (suite == 0 || | |
141 | (!wpa_cipher_valid_pairwise(pairwise_cipher) && | |
142 | pairwise_cipher != WPA_CIPHER_NONE)) { | |
6fc6879b JM |
143 | wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", |
144 | pairwise_cipher); | |
145 | return -1; | |
146 | } | |
c3550295 | 147 | RSN_SELECTOR_PUT(pos, suite); |
6fc6879b JM |
148 | pos += RSN_SELECTOR_LEN; |
149 | ||
150 | *pos++ = 1; | |
151 | *pos++ = 0; | |
152 | if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { | |
153 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); | |
154 | } else if (key_mgmt == WPA_KEY_MGMT_PSK) { | |
155 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); | |
369c8d7b JM |
156 | } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { |
157 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM); | |
6fc6879b JM |
158 | #ifdef CONFIG_IEEE80211R |
159 | } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { | |
160 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); | |
161 | } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) { | |
162 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); | |
163 | #endif /* CONFIG_IEEE80211R */ | |
56586197 JM |
164 | #ifdef CONFIG_IEEE80211W |
165 | } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) { | |
166 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); | |
167 | } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { | |
168 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); | |
169 | #endif /* CONFIG_IEEE80211W */ | |
c10347f2 JM |
170 | #ifdef CONFIG_SAE |
171 | } else if (key_mgmt == WPA_KEY_MGMT_SAE) { | |
172 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); | |
173 | } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) { | |
174 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); | |
175 | #endif /* CONFIG_SAE */ | |
6fc6879b JM |
176 | } else { |
177 | wpa_printf(MSG_WARNING, "Invalid key management type (%d).", | |
178 | key_mgmt); | |
179 | return -1; | |
180 | } | |
181 | pos += RSN_SELECTOR_LEN; | |
182 | ||
183 | /* RSN Capabilities */ | |
184 | capab = 0; | |
185 | #ifdef CONFIG_IEEE80211W | |
e820cf95 | 186 | if (sm->mfp) |
0b60b0aa | 187 | capab |= WPA_CAPABILITY_MFPC; |
e820cf95 JM |
188 | if (sm->mfp == 2) |
189 | capab |= WPA_CAPABILITY_MFPR; | |
6fc6879b JM |
190 | #endif /* CONFIG_IEEE80211W */ |
191 | WPA_PUT_LE16(pos, capab); | |
192 | pos += 2; | |
193 | ||
194 | if (sm->cur_pmksa) { | |
195 | /* PMKID Count (2 octets, little endian) */ | |
196 | *pos++ = 1; | |
197 | *pos++ = 0; | |
198 | /* PMKID */ | |
199 | os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN); | |
200 | pos += PMKID_LEN; | |
201 | } | |
202 | ||
203 | #ifdef CONFIG_IEEE80211W | |
8dd9f9cd | 204 | if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) { |
6fc6879b JM |
205 | if (!sm->cur_pmksa) { |
206 | /* PMKID Count */ | |
207 | WPA_PUT_LE16(pos, 0); | |
208 | pos += 2; | |
209 | } | |
210 | ||
211 | /* Management Group Cipher Suite */ | |
8dd9f9cd JM |
212 | RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, |
213 | mgmt_group_cipher)); | |
6fc6879b JM |
214 | pos += RSN_SELECTOR_LEN; |
215 | } | |
216 | #endif /* CONFIG_IEEE80211W */ | |
217 | ||
218 | hdr->len = (pos - rsn_ie) - 2; | |
219 | ||
220 | WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); | |
221 | ||
222 | return pos - rsn_ie; | |
6fc6879b JM |
223 | } |
224 | ||
225 | ||
df0f01d9 JM |
226 | #ifdef CONFIG_HS20 |
227 | static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len, | |
228 | int pairwise_cipher, int group_cipher, | |
229 | int key_mgmt) | |
230 | { | |
231 | u8 *pos, *len; | |
232 | u32 suite; | |
233 | ||
234 | if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN + | |
235 | 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN) | |
236 | return -1; | |
237 | ||
238 | pos = wpa_ie; | |
239 | *pos++ = WLAN_EID_VENDOR_SPECIFIC; | |
240 | len = pos++; /* to be filled */ | |
241 | WPA_PUT_BE24(pos, OUI_WFA); | |
242 | pos += 3; | |
243 | *pos++ = HS20_OSEN_OUI_TYPE; | |
244 | ||
245 | /* Group Data Cipher Suite */ | |
246 | suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); | |
247 | if (suite == 0) { | |
248 | wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", | |
249 | group_cipher); | |
250 | return -1; | |
251 | } | |
252 | RSN_SELECTOR_PUT(pos, suite); | |
253 | pos += RSN_SELECTOR_LEN; | |
254 | ||
255 | /* Pairwise Cipher Suite Count and List */ | |
256 | WPA_PUT_LE16(pos, 1); | |
257 | pos += 2; | |
258 | suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); | |
259 | if (suite == 0 || | |
260 | (!wpa_cipher_valid_pairwise(pairwise_cipher) && | |
261 | pairwise_cipher != WPA_CIPHER_NONE)) { | |
262 | wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", | |
263 | pairwise_cipher); | |
264 | return -1; | |
265 | } | |
266 | RSN_SELECTOR_PUT(pos, suite); | |
267 | pos += RSN_SELECTOR_LEN; | |
268 | ||
269 | /* AKM Suite Count and List */ | |
270 | WPA_PUT_LE16(pos, 1); | |
271 | pos += 2; | |
272 | RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); | |
273 | pos += RSN_SELECTOR_LEN; | |
274 | ||
275 | *len = pos - len - 1; | |
276 | ||
277 | WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); | |
278 | ||
279 | return pos - wpa_ie; | |
280 | } | |
281 | #endif /* CONFIG_HS20 */ | |
282 | ||
283 | ||
6fc6879b JM |
284 | /** |
285 | * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy | |
286 | * @sm: Pointer to WPA state machine data from wpa_sm_init() | |
287 | * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE | |
288 | * @wpa_ie_len: Maximum length of the generated WPA/RSN IE | |
289 | * Returns: Length of the generated WPA/RSN IE or -1 on failure | |
290 | */ | |
291 | int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) | |
292 | { | |
293 | if (sm->proto == WPA_PROTO_RSN) | |
294 | return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len, | |
295 | sm->pairwise_cipher, | |
296 | sm->group_cipher, | |
297 | sm->key_mgmt, sm->mgmt_group_cipher, | |
298 | sm); | |
df0f01d9 JM |
299 | #ifdef CONFIG_HS20 |
300 | else if (sm->proto == WPA_PROTO_OSEN) | |
301 | return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len, | |
302 | sm->pairwise_cipher, | |
303 | sm->group_cipher, | |
304 | sm->key_mgmt); | |
305 | #endif /* CONFIG_HS20 */ | |
6fc6879b JM |
306 | else |
307 | return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, | |
308 | sm->pairwise_cipher, | |
309 | sm->group_cipher, | |
310 | sm->key_mgmt); | |
311 | } | |
312 | ||
313 | ||
dda8be71 SD |
314 | /** |
315 | * wpa_parse_vendor_specific - Parse Vendor Specific IEs | |
316 | * @pos: Pointer to the IE header | |
317 | * @end: Pointer to the end of the Key Data buffer | |
318 | * @ie: Pointer to parsed IE data | |
319 | * Returns: 0 on success, 1 if end mark is found, -1 on failure | |
320 | */ | |
321 | static int wpa_parse_vendor_specific(const u8 *pos, const u8 *end, | |
322 | struct wpa_eapol_ie_parse *ie) | |
323 | { | |
324 | unsigned int oui; | |
325 | ||
326 | if (pos[1] < 4) { | |
327 | wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)", | |
328 | pos[1]); | |
329 | return 1; | |
330 | } | |
331 | ||
332 | oui = WPA_GET_BE24(&pos[2]); | |
333 | if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) { | |
334 | if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) { | |
335 | ie->wmm = &pos[2]; | |
336 | ie->wmm_len = pos[1]; | |
337 | wpa_hexdump(MSG_DEBUG, "WPA: WMM IE", | |
338 | ie->wmm, ie->wmm_len); | |
339 | } else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) { | |
340 | ie->wmm = &pos[2]; | |
341 | ie->wmm_len = pos[1]; | |
342 | wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element", | |
343 | ie->wmm, ie->wmm_len); | |
344 | } | |
345 | } | |
346 | return 0; | |
347 | } | |
348 | ||
349 | ||
6fc6879b JM |
350 | /** |
351 | * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs | |
352 | * @pos: Pointer to the IE header | |
353 | * @end: Pointer to the end of the Key Data buffer | |
354 | * @ie: Pointer to parsed IE data | |
355 | * Returns: 0 on success, 1 if end mark is found, -1 on failure | |
356 | */ | |
357 | static int wpa_parse_generic(const u8 *pos, const u8 *end, | |
358 | struct wpa_eapol_ie_parse *ie) | |
359 | { | |
360 | if (pos[1] == 0) | |
361 | return 1; | |
362 | ||
363 | if (pos[1] >= 6 && | |
364 | RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && | |
365 | pos[2 + WPA_SELECTOR_LEN] == 1 && | |
366 | pos[2 + WPA_SELECTOR_LEN + 1] == 0) { | |
367 | ie->wpa_ie = pos; | |
368 | ie->wpa_ie_len = pos[1] + 2; | |
48de343c JM |
369 | wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key", |
370 | ie->wpa_ie, ie->wpa_ie_len); | |
6fc6879b JM |
371 | return 0; |
372 | } | |
373 | ||
374 | if (pos + 1 + RSN_SELECTOR_LEN < end && | |
375 | pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && | |
376 | RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { | |
377 | ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; | |
48de343c JM |
378 | wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", |
379 | pos, pos[1] + 2); | |
6fc6879b JM |
380 | return 0; |
381 | } | |
382 | ||
383 | if (pos[1] > RSN_SELECTOR_LEN + 2 && | |
384 | RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { | |
385 | ie->gtk = pos + 2 + RSN_SELECTOR_LEN; | |
386 | ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; | |
48de343c JM |
387 | wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key", |
388 | pos, pos[1] + 2); | |
6fc6879b JM |
389 | return 0; |
390 | } | |
391 | ||
392 | if (pos[1] > RSN_SELECTOR_LEN + 2 && | |
393 | RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { | |
394 | ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; | |
395 | ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; | |
48de343c JM |
396 | wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key", |
397 | pos, pos[1] + 2); | |
6fc6879b JM |
398 | return 0; |
399 | } | |
400 | ||
401 | #ifdef CONFIG_PEERKEY | |
402 | if (pos[1] > RSN_SELECTOR_LEN + 2 && | |
403 | RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { | |
404 | ie->smk = pos + 2 + RSN_SELECTOR_LEN; | |
405 | ie->smk_len = pos[1] - RSN_SELECTOR_LEN; | |
48de343c JM |
406 | wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key", |
407 | pos, pos[1] + 2); | |
6fc6879b JM |
408 | return 0; |
409 | } | |
410 | ||
411 | if (pos[1] > RSN_SELECTOR_LEN + 2 && | |
412 | RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { | |
413 | ie->nonce = pos + 2 + RSN_SELECTOR_LEN; | |
414 | ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; | |
48de343c JM |
415 | wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key", |
416 | pos, pos[1] + 2); | |
6fc6879b JM |
417 | return 0; |
418 | } | |
419 | ||
420 | if (pos[1] > RSN_SELECTOR_LEN + 2 && | |
421 | RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { | |
422 | ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; | |
423 | ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; | |
48de343c JM |
424 | wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key", |
425 | pos, pos[1] + 2); | |
6fc6879b JM |
426 | return 0; |
427 | } | |
428 | ||
429 | if (pos[1] > RSN_SELECTOR_LEN + 2 && | |
430 | RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { | |
431 | ie->error = pos + 2 + RSN_SELECTOR_LEN; | |
432 | ie->error_len = pos[1] - RSN_SELECTOR_LEN; | |
48de343c JM |
433 | wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key", |
434 | pos, pos[1] + 2); | |
6fc6879b JM |
435 | return 0; |
436 | } | |
437 | #endif /* CONFIG_PEERKEY */ | |
438 | ||
439 | #ifdef CONFIG_IEEE80211W | |
440 | if (pos[1] > RSN_SELECTOR_LEN + 2 && | |
441 | RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { | |
442 | ie->igtk = pos + 2 + RSN_SELECTOR_LEN; | |
443 | ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; | |
48de343c JM |
444 | wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key", |
445 | pos, pos[1] + 2); | |
6fc6879b JM |
446 | return 0; |
447 | } | |
448 | #endif /* CONFIG_IEEE80211W */ | |
449 | ||
25ef8529 JM |
450 | #ifdef CONFIG_P2P |
451 | if (pos[1] >= RSN_SELECTOR_LEN + 1 && | |
452 | RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { | |
453 | ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; | |
454 | wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", | |
455 | ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); | |
456 | return 0; | |
457 | } | |
458 | ||
459 | if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && | |
460 | RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { | |
461 | ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; | |
462 | wpa_hexdump(MSG_DEBUG, | |
463 | "WPA: IP Address Allocation in EAPOL-Key", | |
464 | ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); | |
465 | return 0; | |
466 | } | |
467 | #endif /* CONFIG_P2P */ | |
468 | ||
6fc6879b JM |
469 | return 0; |
470 | } | |
471 | ||
472 | ||
473 | /** | |
474 | * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs | |
475 | * @buf: Pointer to the Key Data buffer | |
476 | * @len: Key Data Length | |
477 | * @ie: Pointer to parsed IE data | |
478 | * Returns: 0 on success, -1 on failure | |
479 | */ | |
480 | int wpa_supplicant_parse_ies(const u8 *buf, size_t len, | |
481 | struct wpa_eapol_ie_parse *ie) | |
482 | { | |
483 | const u8 *pos, *end; | |
484 | int ret = 0; | |
485 | ||
486 | os_memset(ie, 0, sizeof(*ie)); | |
487 | for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { | |
488 | if (pos[0] == 0xdd && | |
489 | ((pos == buf + len - 1) || pos[1] == 0)) { | |
490 | /* Ignore padding */ | |
491 | break; | |
492 | } | |
493 | if (pos + 2 + pos[1] > end) { | |
494 | wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " | |
495 | "underflow (ie=%d len=%d pos=%d)", | |
496 | pos[0], pos[1], (int) (pos - buf)); | |
497 | wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", | |
498 | buf, len); | |
499 | ret = -1; | |
500 | break; | |
501 | } | |
502 | if (*pos == WLAN_EID_RSN) { | |
503 | ie->rsn_ie = pos; | |
504 | ie->rsn_ie_len = pos[1] + 2; | |
48de343c JM |
505 | wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", |
506 | ie->rsn_ie, ie->rsn_ie_len); | |
6fc6879b JM |
507 | } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { |
508 | ie->mdie = pos; | |
509 | ie->mdie_len = pos[1] + 2; | |
48de343c JM |
510 | wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key", |
511 | ie->mdie, ie->mdie_len); | |
512 | } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { | |
513 | ie->ftie = pos; | |
514 | ie->ftie_len = pos[1] + 2; | |
515 | wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", | |
516 | ie->ftie, ie->ftie_len); | |
517 | } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) { | |
518 | if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) { | |
519 | ie->reassoc_deadline = pos; | |
520 | wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline " | |
521 | "in EAPOL-Key", | |
522 | ie->reassoc_deadline, pos[1] + 2); | |
523 | } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) { | |
524 | ie->key_lifetime = pos; | |
525 | wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime " | |
526 | "in EAPOL-Key", | |
527 | ie->key_lifetime, pos[1] + 2); | |
528 | } else { | |
529 | wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized " | |
530 | "EAPOL-Key Key Data IE", | |
531 | pos, 2 + pos[1]); | |
532 | } | |
281ff0aa | 533 | } else if (*pos == WLAN_EID_LINK_ID) { |
979bcccf AN |
534 | if (pos[1] >= 18) { |
535 | ie->lnkid = pos; | |
536 | ie->lnkid_len = pos[1] + 2; | |
537 | } | |
52c9e6f3 JM |
538 | } else if (*pos == WLAN_EID_EXT_CAPAB) { |
539 | ie->ext_capab = pos; | |
540 | ie->ext_capab_len = pos[1] + 2; | |
979bcccf AN |
541 | } else if (*pos == WLAN_EID_SUPP_RATES) { |
542 | ie->supp_rates = pos; | |
543 | ie->supp_rates_len = pos[1] + 2; | |
544 | } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { | |
545 | ie->ext_supp_rates = pos; | |
546 | ie->ext_supp_rates_len = pos[1] + 2; | |
ff4178d5 SD |
547 | } else if (*pos == WLAN_EID_HT_CAP) { |
548 | ie->ht_capabilities = pos + 2; | |
549 | ie->ht_capabilities_len = pos[1]; | |
78533699 SD |
550 | } else if (*pos == WLAN_EID_VHT_AID) { |
551 | if (pos[1] >= 2) | |
552 | ie->aid = WPA_GET_LE16(pos + 2); | |
f8361e3d SD |
553 | } else if (*pos == WLAN_EID_VHT_CAP) { |
554 | ie->vht_capabilities = pos + 2; | |
555 | ie->vht_capabilities_len = pos[1]; | |
ff4178d5 SD |
556 | } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { |
557 | ie->qosinfo = pos[2]; | |
3ed97271 SD |
558 | } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { |
559 | ie->supp_channels = pos + 2; | |
560 | ie->supp_channels_len = pos[1]; | |
561 | } else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) { | |
1578796a SDU |
562 | /* |
563 | * The value of the Length field of the Supported | |
564 | * Operating Classes element is between 2 and 253. | |
565 | * Silently skip invalid elements to avoid interop | |
566 | * issues when trying to use the value. | |
567 | */ | |
568 | if (pos[1] >= 2 && pos[1] <= 253) { | |
569 | ie->supp_oper_classes = pos + 2; | |
570 | ie->supp_oper_classes_len = pos[1]; | |
571 | } | |
6fc6879b JM |
572 | } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { |
573 | ret = wpa_parse_generic(pos, end, ie); | |
574 | if (ret < 0) | |
575 | break; | |
576 | if (ret > 0) { | |
577 | ret = 0; | |
578 | break; | |
579 | } | |
dda8be71 SD |
580 | |
581 | ret = wpa_parse_vendor_specific(pos, end, ie); | |
582 | if (ret < 0) | |
583 | break; | |
584 | if (ret > 0) { | |
585 | ret = 0; | |
586 | break; | |
587 | } | |
6fc6879b JM |
588 | } else { |
589 | wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " | |
590 | "Key Data IE", pos, 2 + pos[1]); | |
591 | } | |
592 | } | |
593 | ||
594 | return ret; | |
595 | } |