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