1 | /* |
---|
2 | * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. |
---|
3 | * |
---|
4 | * This program is free software; you can redistribute it and/or |
---|
5 | * modify it under the terms of the GNU General Public License as |
---|
6 | * published by the Free Software Foundation; either version 2 of the |
---|
7 | * License, or any later version. |
---|
8 | * |
---|
9 | * This program is distributed in the hope that it will be useful, but |
---|
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
12 | * General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU General Public License |
---|
15 | * along with this program; if not, write to the Free Software |
---|
16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
17 | */ |
---|
18 | |
---|
19 | FILE_LICENCE ( GPL2_OR_LATER ); |
---|
20 | |
---|
21 | #include <gpxe/net80211.h> |
---|
22 | #include <gpxe/sec80211.h> |
---|
23 | #include <gpxe/wpa.h> |
---|
24 | #include <gpxe/eapol.h> |
---|
25 | #include <gpxe/crypto.h> |
---|
26 | #include <gpxe/arc4.h> |
---|
27 | #include <gpxe/crc32.h> |
---|
28 | #include <gpxe/sha1.h> |
---|
29 | #include <gpxe/hmac.h> |
---|
30 | #include <gpxe/list.h> |
---|
31 | #include <gpxe/ethernet.h> |
---|
32 | #include <stdlib.h> |
---|
33 | #include <string.h> |
---|
34 | #include <errno.h> |
---|
35 | |
---|
36 | /** @file |
---|
37 | * |
---|
38 | * Handler for the aspects of WPA handshaking that are independent of |
---|
39 | * 802.1X/PSK or TKIP/CCMP; this mostly involves the 4-Way Handshake. |
---|
40 | */ |
---|
41 | |
---|
42 | /** List of WPA contexts in active use. */ |
---|
43 | struct list_head wpa_contexts = LIST_HEAD_INIT ( wpa_contexts ); |
---|
44 | |
---|
45 | |
---|
46 | /** |
---|
47 | * Return an error code and deauthenticate |
---|
48 | * |
---|
49 | * @v ctx WPA common context |
---|
50 | * @v rc Return status code |
---|
51 | * @ret rc The passed return status code |
---|
52 | */ |
---|
53 | static int wpa_fail ( struct wpa_common_ctx *ctx, int rc ) |
---|
54 | { |
---|
55 | net80211_deauthenticate ( ctx->dev, rc ); |
---|
56 | return rc; |
---|
57 | } |
---|
58 | |
---|
59 | |
---|
60 | /** |
---|
61 | * Find a cryptosystem handler structure from a crypto ID |
---|
62 | * |
---|
63 | * @v crypt Cryptosystem ID |
---|
64 | * @ret crypto Cryptosystem handler structure |
---|
65 | * |
---|
66 | * If support for @a crypt is not compiled in to gPXE, or if @a crypt |
---|
67 | * is NET80211_CRYPT_UNKNOWN, returns @c NULL. |
---|
68 | */ |
---|
69 | static struct net80211_crypto * |
---|
70 | wpa_find_cryptosystem ( enum net80211_crypto_alg crypt ) |
---|
71 | { |
---|
72 | struct net80211_crypto *crypto; |
---|
73 | |
---|
74 | for_each_table_entry ( crypto, NET80211_CRYPTOS ) { |
---|
75 | if ( crypto->algorithm == crypt ) |
---|
76 | return crypto; |
---|
77 | } |
---|
78 | |
---|
79 | return NULL; |
---|
80 | } |
---|
81 | |
---|
82 | |
---|
83 | /** |
---|
84 | * Find WPA key integrity and encryption handler from key version field |
---|
85 | * |
---|
86 | * @v ver Version bits of EAPOL-Key info field |
---|
87 | * @ret kie Key integrity and encryption handler |
---|
88 | */ |
---|
89 | struct wpa_kie * wpa_find_kie ( int version ) |
---|
90 | { |
---|
91 | struct wpa_kie *kie; |
---|
92 | |
---|
93 | for_each_table_entry ( kie, WPA_KIES ) { |
---|
94 | if ( kie->version == version ) |
---|
95 | return kie; |
---|
96 | } |
---|
97 | |
---|
98 | return NULL; |
---|
99 | } |
---|
100 | |
---|
101 | |
---|
102 | /** |
---|
103 | * Construct RSN or WPA information element |
---|
104 | * |
---|
105 | * @v dev 802.11 device |
---|
106 | * @ret ie_ret RSN or WPA information element |
---|
107 | * @ret rc Return status code |
---|
108 | * |
---|
109 | * This function allocates, fills, and returns a RSN or WPA |
---|
110 | * information element suitable for including in an association |
---|
111 | * request frame to the network identified by @c dev->associating. |
---|
112 | * If it is impossible to construct an information element consistent |
---|
113 | * with gPXE's capabilities that is compatible with that network, or |
---|
114 | * if none should be sent because that network's beacon included no |
---|
115 | * security information, returns an error indication and leaves |
---|
116 | * @a ie_ret unchanged. |
---|
117 | * |
---|
118 | * The returned IE will be of the same type (RSN or WPA) as was |
---|
119 | * included in the beacon for the network it is destined for. |
---|
120 | */ |
---|
121 | int wpa_make_rsn_ie ( struct net80211_device *dev, union ieee80211_ie **ie_ret ) |
---|
122 | { |
---|
123 | u8 *rsn, *rsn_end; |
---|
124 | int is_rsn; |
---|
125 | u32 group_cipher; |
---|
126 | enum net80211_crypto_alg gcrypt; |
---|
127 | int ie_len; |
---|
128 | u8 *iep; |
---|
129 | struct ieee80211_ie_rsn *ie; |
---|
130 | struct ieee80211_frame *hdr; |
---|
131 | struct ieee80211_beacon *beacon; |
---|
132 | |
---|
133 | if ( ! dev->associating ) { |
---|
134 | DBG ( "WPA: Can't make RSN IE for a non-associating device\n" ); |
---|
135 | return -EINVAL; |
---|
136 | } |
---|
137 | |
---|
138 | hdr = dev->associating->beacon->data; |
---|
139 | beacon = ( struct ieee80211_beacon * ) hdr->data; |
---|
140 | rsn = sec80211_find_rsn ( beacon->info_element, |
---|
141 | dev->associating->beacon->tail, &is_rsn, |
---|
142 | &rsn_end ); |
---|
143 | if ( ! rsn ) { |
---|
144 | DBG ( "WPA: Can't make RSN IE when we didn't get one\n" ); |
---|
145 | return -EINVAL; |
---|
146 | } |
---|
147 | |
---|
148 | rsn += 2; /* skip version */ |
---|
149 | group_cipher = *( u32 * ) rsn; |
---|
150 | gcrypt = sec80211_rsn_get_net80211_crypt ( group_cipher ); |
---|
151 | |
---|
152 | if ( ! wpa_find_cryptosystem ( gcrypt ) || |
---|
153 | ! wpa_find_cryptosystem ( dev->associating->crypto ) ) { |
---|
154 | DBG ( "WPA: No support for (GC:%d, PC:%d)\n", |
---|
155 | gcrypt, dev->associating->crypto ); |
---|
156 | return -ENOTSUP; |
---|
157 | } |
---|
158 | |
---|
159 | /* Everything looks good - make our IE. */ |
---|
160 | |
---|
161 | /* WPA IEs need 4 more bytes for the OUI+type */ |
---|
162 | ie_len = ieee80211_rsn_size ( 1, 1, 0, is_rsn ) + ( 4 * ! is_rsn ); |
---|
163 | iep = malloc ( ie_len ); |
---|
164 | if ( ! iep ) |
---|
165 | return -ENOMEM; |
---|
166 | |
---|
167 | *ie_ret = ( union ieee80211_ie * ) iep; |
---|
168 | |
---|
169 | /* Store ID and length bytes. */ |
---|
170 | *iep++ = ( is_rsn ? IEEE80211_IE_RSN : IEEE80211_IE_VENDOR ); |
---|
171 | *iep++ = ie_len - 2; |
---|
172 | |
---|
173 | /* Store OUI+type for WPA IEs. */ |
---|
174 | if ( ! is_rsn ) { |
---|
175 | *( u32 * ) iep = IEEE80211_WPA_OUI_VEN; |
---|
176 | iep += 4; |
---|
177 | } |
---|
178 | |
---|
179 | /* If this is a WPA IE, the id and len bytes in the |
---|
180 | ieee80211_ie_rsn structure will not be valid, but by doing |
---|
181 | the cast we can fill all the other fields much more |
---|
182 | readily. */ |
---|
183 | |
---|
184 | ie = ( struct ieee80211_ie_rsn * ) ( iep - 2 ); |
---|
185 | ie->version = IEEE80211_RSN_VERSION; |
---|
186 | ie->group_cipher = group_cipher; |
---|
187 | ie->pairwise_count = 1; |
---|
188 | ie->pairwise_cipher[0] = |
---|
189 | sec80211_rsn_get_crypto_desc ( dev->associating->crypto, |
---|
190 | is_rsn ); |
---|
191 | ie->akm_count = 1; |
---|
192 | ie->akm_list[0] = |
---|
193 | sec80211_rsn_get_akm_desc ( dev->associating->handshaking, |
---|
194 | is_rsn ); |
---|
195 | if ( is_rsn ) { |
---|
196 | ie->rsn_capab = 0; |
---|
197 | ie->pmkid_count = 0; |
---|
198 | } |
---|
199 | |
---|
200 | return 0; |
---|
201 | } |
---|
202 | |
---|
203 | |
---|
204 | /** |
---|
205 | * Set up generic WPA support to handle 4-Way Handshake |
---|
206 | * |
---|
207 | * @v dev 802.11 device |
---|
208 | * @v ctx WPA common context |
---|
209 | * @v pmk Pairwise Master Key to use for session |
---|
210 | * @v pmk_len Length of PMK, almost always 32 |
---|
211 | * @ret rc Return status code |
---|
212 | */ |
---|
213 | int wpa_start ( struct net80211_device *dev, struct wpa_common_ctx *ctx, |
---|
214 | const void *pmk, size_t pmk_len ) |
---|
215 | { |
---|
216 | struct io_buffer *iob; |
---|
217 | struct ieee80211_frame *hdr; |
---|
218 | struct ieee80211_beacon *beacon; |
---|
219 | u8 *ap_rsn_ie = NULL, *ap_rsn_ie_end; |
---|
220 | |
---|
221 | if ( ! dev->rsn_ie || ! dev->associating ) |
---|
222 | return -EINVAL; |
---|
223 | |
---|
224 | ctx->dev = dev; |
---|
225 | memcpy ( ctx->pmk, pmk, ctx->pmk_len = pmk_len ); |
---|
226 | ctx->state = WPA_READY; |
---|
227 | ctx->replay = ~0ULL; |
---|
228 | |
---|
229 | iob = dev->associating->beacon; |
---|
230 | hdr = iob->data; |
---|
231 | beacon = ( struct ieee80211_beacon * ) hdr->data; |
---|
232 | ap_rsn_ie = sec80211_find_rsn ( beacon->info_element, iob->tail, |
---|
233 | &ctx->ap_rsn_is_rsn, &ap_rsn_ie_end ); |
---|
234 | if ( ap_rsn_ie ) { |
---|
235 | ctx->ap_rsn_ie = malloc ( ap_rsn_ie_end - ap_rsn_ie ); |
---|
236 | if ( ! ctx->ap_rsn_ie ) |
---|
237 | return -ENOMEM; |
---|
238 | memcpy ( ctx->ap_rsn_ie, ap_rsn_ie, ap_rsn_ie_end - ap_rsn_ie ); |
---|
239 | ctx->ap_rsn_ie_len = ap_rsn_ie_end - ap_rsn_ie; |
---|
240 | } else { |
---|
241 | return -ENOENT; |
---|
242 | } |
---|
243 | |
---|
244 | ctx->crypt = dev->associating->crypto; |
---|
245 | ctx->gcrypt = NET80211_CRYPT_UNKNOWN; |
---|
246 | |
---|
247 | list_add_tail ( &ctx->list, &wpa_contexts ); |
---|
248 | return 0; |
---|
249 | } |
---|
250 | |
---|
251 | |
---|
252 | /** |
---|
253 | * Disable handling of received WPA handshake frames |
---|
254 | * |
---|
255 | * @v dev 802.11 device |
---|
256 | */ |
---|
257 | void wpa_stop ( struct net80211_device *dev ) |
---|
258 | { |
---|
259 | struct wpa_common_ctx *ctx, *tmp; |
---|
260 | |
---|
261 | list_for_each_entry_safe ( ctx, tmp, &wpa_contexts, list ) { |
---|
262 | if ( ctx->dev == dev ) { |
---|
263 | free ( ctx->ap_rsn_ie ); |
---|
264 | ctx->ap_rsn_ie = NULL; |
---|
265 | list_del ( &ctx->list ); |
---|
266 | } |
---|
267 | } |
---|
268 | } |
---|
269 | |
---|
270 | |
---|
271 | /** |
---|
272 | * Check PMKID consistency |
---|
273 | * |
---|
274 | * @v ctx WPA common context |
---|
275 | * @v pmkid PMKID to check against (16 bytes long) |
---|
276 | * @ret rc Zero if they match, or a negative error code if not |
---|
277 | */ |
---|
278 | int wpa_check_pmkid ( struct wpa_common_ctx *ctx, const u8 *pmkid ) |
---|
279 | { |
---|
280 | u8 sha1_ctx[SHA1_CTX_SIZE]; |
---|
281 | u8 my_pmkid[SHA1_SIZE]; |
---|
282 | u8 pmk[ctx->pmk_len]; |
---|
283 | size_t pmk_len; |
---|
284 | struct { |
---|
285 | char name[8]; |
---|
286 | u8 aa[ETH_ALEN]; |
---|
287 | u8 spa[ETH_ALEN]; |
---|
288 | } __attribute__ (( packed )) pmkid_data; |
---|
289 | |
---|
290 | memcpy ( pmk, ctx->pmk, ctx->pmk_len ); |
---|
291 | pmk_len = ctx->pmk_len; |
---|
292 | |
---|
293 | memcpy ( pmkid_data.name, "PMK Name", 8 ); |
---|
294 | memcpy ( pmkid_data.aa, ctx->dev->bssid, ETH_ALEN ); |
---|
295 | memcpy ( pmkid_data.spa, ctx->dev->netdev->ll_addr, ETH_ALEN ); |
---|
296 | |
---|
297 | hmac_init ( &sha1_algorithm, sha1_ctx, pmk, &pmk_len ); |
---|
298 | hmac_update ( &sha1_algorithm, sha1_ctx, &pmkid_data, |
---|
299 | sizeof ( pmkid_data ) ); |
---|
300 | hmac_final ( &sha1_algorithm, sha1_ctx, pmk, &pmk_len, my_pmkid ); |
---|
301 | |
---|
302 | if ( memcmp ( my_pmkid, pmkid, WPA_PMKID_LEN ) != 0 ) |
---|
303 | return -EACCES; |
---|
304 | |
---|
305 | return 0; |
---|
306 | } |
---|
307 | |
---|
308 | |
---|
309 | /** |
---|
310 | * Derive pairwise transient key |
---|
311 | * |
---|
312 | * @v ctx WPA common context |
---|
313 | */ |
---|
314 | static void wpa_derive_ptk ( struct wpa_common_ctx *ctx ) |
---|
315 | { |
---|
316 | struct { |
---|
317 | u8 mac1[ETH_ALEN]; |
---|
318 | u8 mac2[ETH_ALEN]; |
---|
319 | u8 nonce1[WPA_NONCE_LEN]; |
---|
320 | u8 nonce2[WPA_NONCE_LEN]; |
---|
321 | } __attribute__ (( packed )) ptk_data; |
---|
322 | |
---|
323 | /* The addresses and nonces are stored in numerical order (!) */ |
---|
324 | |
---|
325 | if ( memcmp ( ctx->dev->netdev->ll_addr, ctx->dev->bssid, |
---|
326 | ETH_ALEN ) < 0 ) { |
---|
327 | memcpy ( ptk_data.mac1, ctx->dev->netdev->ll_addr, ETH_ALEN ); |
---|
328 | memcpy ( ptk_data.mac2, ctx->dev->bssid, ETH_ALEN ); |
---|
329 | } else { |
---|
330 | memcpy ( ptk_data.mac1, ctx->dev->bssid, ETH_ALEN ); |
---|
331 | memcpy ( ptk_data.mac2, ctx->dev->netdev->ll_addr, ETH_ALEN ); |
---|
332 | } |
---|
333 | |
---|
334 | if ( memcmp ( ctx->Anonce, ctx->Snonce, WPA_NONCE_LEN ) < 0 ) { |
---|
335 | memcpy ( ptk_data.nonce1, ctx->Anonce, WPA_NONCE_LEN ); |
---|
336 | memcpy ( ptk_data.nonce2, ctx->Snonce, WPA_NONCE_LEN ); |
---|
337 | } else { |
---|
338 | memcpy ( ptk_data.nonce1, ctx->Snonce, WPA_NONCE_LEN ); |
---|
339 | memcpy ( ptk_data.nonce2, ctx->Anonce, WPA_NONCE_LEN ); |
---|
340 | } |
---|
341 | |
---|
342 | DBGC2 ( ctx, "WPA %p A1 %s, A2 %s\n", ctx, eth_ntoa ( ptk_data.mac1 ), |
---|
343 | eth_ntoa ( ptk_data.mac2 ) ); |
---|
344 | DBGC2 ( ctx, "WPA %p Nonce1, Nonce2:\n", ctx ); |
---|
345 | DBGC2_HD ( ctx, ptk_data.nonce1, WPA_NONCE_LEN ); |
---|
346 | DBGC2_HD ( ctx, ptk_data.nonce2, WPA_NONCE_LEN ); |
---|
347 | |
---|
348 | prf_sha1 ( ctx->pmk, ctx->pmk_len, |
---|
349 | "Pairwise key expansion", |
---|
350 | &ptk_data, sizeof ( ptk_data ), |
---|
351 | &ctx->ptk, sizeof ( ctx->ptk ) ); |
---|
352 | |
---|
353 | DBGC2 ( ctx, "WPA %p PTK:\n", ctx ); |
---|
354 | DBGC2_HD ( ctx, &ctx->ptk, sizeof ( ctx->ptk ) ); |
---|
355 | } |
---|
356 | |
---|
357 | |
---|
358 | /** |
---|
359 | * Install pairwise transient key |
---|
360 | * |
---|
361 | * @v ctx WPA common context |
---|
362 | * @v len Key length (16 for CCMP, 32 for TKIP) |
---|
363 | * @ret rc Return status code |
---|
364 | */ |
---|
365 | static inline int wpa_install_ptk ( struct wpa_common_ctx *ctx, int len ) |
---|
366 | { |
---|
367 | DBGC ( ctx, "WPA %p: installing %d-byte pairwise transient key\n", |
---|
368 | ctx, len ); |
---|
369 | DBGC2_HD ( ctx, &ctx->ptk.tk, len ); |
---|
370 | |
---|
371 | return sec80211_install ( &ctx->dev->crypto, ctx->crypt, |
---|
372 | &ctx->ptk.tk, len, NULL ); |
---|
373 | } |
---|
374 | |
---|
375 | /** |
---|
376 | * Install group transient key |
---|
377 | * |
---|
378 | * @v ctx WPA common context |
---|
379 | * @v len Key length (16 for CCMP, 32 for TKIP) |
---|
380 | * @v rsc Receive sequence counter field in EAPOL-Key packet |
---|
381 | * @ret rc Return status code |
---|
382 | */ |
---|
383 | static inline int wpa_install_gtk ( struct wpa_common_ctx *ctx, int len, |
---|
384 | const void *rsc ) |
---|
385 | { |
---|
386 | DBGC ( ctx, "WPA %p: installing %d-byte group transient key\n", |
---|
387 | ctx, len ); |
---|
388 | DBGC2_HD ( ctx, &ctx->gtk.tk, len ); |
---|
389 | |
---|
390 | return sec80211_install ( &ctx->dev->gcrypto, ctx->gcrypt, |
---|
391 | &ctx->gtk.tk, len, rsc ); |
---|
392 | } |
---|
393 | |
---|
394 | /** |
---|
395 | * Search for group transient key, and install it if found |
---|
396 | * |
---|
397 | * @v ctx WPA common context |
---|
398 | * @v ie Pointer to first IE in key data field |
---|
399 | * @v ie_end Pointer to first byte not in key data field |
---|
400 | * @v rsc Receive sequence counter field in EAPOL-Key packet |
---|
401 | * @ret rc Return status code |
---|
402 | */ |
---|
403 | static int wpa_maybe_install_gtk ( struct wpa_common_ctx *ctx, |
---|
404 | union ieee80211_ie *ie, void *ie_end, |
---|
405 | const void *rsc ) |
---|
406 | { |
---|
407 | struct wpa_kde *kde; |
---|
408 | |
---|
409 | if ( ! ieee80211_ie_bound ( ie, ie_end ) ) |
---|
410 | return -ENOENT; |
---|
411 | |
---|
412 | while ( ie ) { |
---|
413 | if ( ie->id == IEEE80211_IE_VENDOR && |
---|
414 | ie->vendor.oui == WPA_KDE_GTK ) |
---|
415 | break; |
---|
416 | |
---|
417 | ie = ieee80211_next_ie ( ie, ie_end ); |
---|
418 | } |
---|
419 | |
---|
420 | if ( ! ie ) |
---|
421 | return -ENOENT; |
---|
422 | |
---|
423 | if ( ie->len - 6u > sizeof ( ctx->gtk.tk ) ) { |
---|
424 | DBGC ( ctx, "WPA %p: GTK KDE is too long (%d bytes, max %d)\n", |
---|
425 | ctx, ie->len - 4, sizeof ( ctx->gtk.tk ) ); |
---|
426 | return -EINVAL; |
---|
427 | } |
---|
428 | |
---|
429 | /* XXX We ignore key ID for now. */ |
---|
430 | kde = ( struct wpa_kde * ) ie; |
---|
431 | memcpy ( &ctx->gtk.tk, &kde->gtk_encap.gtk, kde->len - 6 ); |
---|
432 | |
---|
433 | return wpa_install_gtk ( ctx, kde->len - 6, rsc ); |
---|
434 | } |
---|
435 | |
---|
436 | |
---|
437 | /** |
---|
438 | * Allocate I/O buffer for construction of outgoing EAPOL-Key frame |
---|
439 | * |
---|
440 | * @v kdlen Maximum number of bytes in the Key Data field |
---|
441 | * @ret iob Newly allocated I/O buffer |
---|
442 | * |
---|
443 | * The returned buffer will have space reserved for the link-layer and |
---|
444 | * EAPOL headers, and will have @c iob->tail pointing to the start of |
---|
445 | * the Key Data field. Thus, it is necessary to use iob_put() in |
---|
446 | * filling the Key Data. |
---|
447 | */ |
---|
448 | static struct io_buffer * wpa_alloc_frame ( int kdlen ) |
---|
449 | { |
---|
450 | struct io_buffer *ret = alloc_iob ( sizeof ( struct eapol_key_pkt ) + |
---|
451 | kdlen + EAPOL_HDR_LEN + |
---|
452 | MAX_LL_HEADER_LEN ); |
---|
453 | if ( ! ret ) |
---|
454 | return NULL; |
---|
455 | |
---|
456 | iob_reserve ( ret, MAX_LL_HEADER_LEN + EAPOL_HDR_LEN ); |
---|
457 | memset ( iob_put ( ret, sizeof ( struct eapol_key_pkt ) ), 0, |
---|
458 | sizeof ( struct eapol_key_pkt ) ); |
---|
459 | |
---|
460 | return ret; |
---|
461 | } |
---|
462 | |
---|
463 | |
---|
464 | /** |
---|
465 | * Send EAPOL-Key packet |
---|
466 | * |
---|
467 | * @v iob I/O buffer, with sufficient headroom for headers |
---|
468 | * @v dev 802.11 device |
---|
469 | * @v kie Key integrity and encryption handler |
---|
470 | * @v is_rsn If TRUE, handshake uses new RSN format |
---|
471 | * @ret rc Return status code |
---|
472 | * |
---|
473 | * If a KIE is specified, the MIC will be filled in before transmission. |
---|
474 | */ |
---|
475 | static int wpa_send_eapol ( struct io_buffer *iob, struct wpa_common_ctx *ctx, |
---|
476 | struct wpa_kie *kie ) |
---|
477 | { |
---|
478 | struct eapol_key_pkt *pkt = iob->data; |
---|
479 | struct eapol_frame *eapol = iob_push ( iob, EAPOL_HDR_LEN ); |
---|
480 | |
---|
481 | pkt->info = htons ( pkt->info ); |
---|
482 | pkt->keysize = htons ( pkt->keysize ); |
---|
483 | pkt->datalen = htons ( pkt->datalen ); |
---|
484 | pkt->replay = cpu_to_be64 ( pkt->replay ); |
---|
485 | eapol->version = EAPOL_THIS_VERSION; |
---|
486 | eapol->type = EAPOL_TYPE_KEY; |
---|
487 | eapol->length = htons ( iob->tail - iob->data - sizeof ( *eapol ) ); |
---|
488 | |
---|
489 | memset ( pkt->mic, 0, sizeof ( pkt->mic ) ); |
---|
490 | if ( kie ) |
---|
491 | kie->mic ( &ctx->ptk.kck, eapol, EAPOL_HDR_LEN + |
---|
492 | sizeof ( *pkt ) + ntohs ( pkt->datalen ), |
---|
493 | pkt->mic ); |
---|
494 | |
---|
495 | return net_tx ( iob, ctx->dev->netdev, &eapol_protocol, |
---|
496 | ctx->dev->bssid ); |
---|
497 | } |
---|
498 | |
---|
499 | |
---|
500 | /** |
---|
501 | * Send second frame in 4-Way Handshake |
---|
502 | * |
---|
503 | * @v ctx WPA common context |
---|
504 | * @v pkt First frame, to which this is a reply |
---|
505 | * @v is_rsn If TRUE, handshake uses new RSN format |
---|
506 | * @v kie Key integrity and encryption handler |
---|
507 | * @ret rc Return status code |
---|
508 | */ |
---|
509 | static int wpa_send_2_of_4 ( struct wpa_common_ctx *ctx, |
---|
510 | struct eapol_key_pkt *pkt, int is_rsn, |
---|
511 | struct wpa_kie *kie ) |
---|
512 | { |
---|
513 | struct io_buffer *iob = wpa_alloc_frame ( ctx->dev->rsn_ie->len + 2 ); |
---|
514 | struct eapol_key_pkt *npkt; |
---|
515 | |
---|
516 | if ( ! iob ) |
---|
517 | return -ENOMEM; |
---|
518 | |
---|
519 | npkt = iob->data; |
---|
520 | memcpy ( npkt, pkt, sizeof ( *pkt ) ); |
---|
521 | npkt->info &= ~EAPOL_KEY_INFO_KEY_ACK; |
---|
522 | npkt->info |= EAPOL_KEY_INFO_KEY_MIC; |
---|
523 | if ( is_rsn ) |
---|
524 | npkt->keysize = 0; |
---|
525 | memcpy ( npkt->nonce, ctx->Snonce, sizeof ( npkt->nonce ) ); |
---|
526 | npkt->datalen = ctx->dev->rsn_ie->len + 2; |
---|
527 | memcpy ( iob_put ( iob, npkt->datalen ), ctx->dev->rsn_ie, |
---|
528 | npkt->datalen ); |
---|
529 | |
---|
530 | DBGC ( ctx, "WPA %p: sending 2/4\n", ctx ); |
---|
531 | |
---|
532 | return wpa_send_eapol ( iob, ctx, kie ); |
---|
533 | } |
---|
534 | |
---|
535 | |
---|
536 | /** |
---|
537 | * Handle receipt of first frame in 4-Way Handshake |
---|
538 | * |
---|
539 | * @v ctx WPA common context |
---|
540 | * @v pkt EAPOL-Key packet |
---|
541 | * @v is_rsn If TRUE, frame uses new RSN format |
---|
542 | * @v kie Key integrity and encryption handler |
---|
543 | * @ret rc Return status code |
---|
544 | */ |
---|
545 | static int wpa_handle_1_of_4 ( struct wpa_common_ctx *ctx, |
---|
546 | struct eapol_key_pkt *pkt, int is_rsn, |
---|
547 | struct wpa_kie *kie ) |
---|
548 | { |
---|
549 | int rc; |
---|
550 | |
---|
551 | if ( ctx->state == WPA_WAITING ) |
---|
552 | return -EINVAL; |
---|
553 | |
---|
554 | ctx->state = WPA_WORKING; |
---|
555 | memcpy ( ctx->Anonce, pkt->nonce, sizeof ( ctx->Anonce ) ); |
---|
556 | if ( ! ctx->have_Snonce ) { |
---|
557 | get_random_bytes ( ctx->Snonce, sizeof ( ctx->Snonce ) ); |
---|
558 | ctx->have_Snonce = 1; |
---|
559 | } |
---|
560 | |
---|
561 | if ( is_rsn && pkt->datalen ) { |
---|
562 | union ieee80211_ie *ie = ( union ieee80211_ie * ) pkt->data; |
---|
563 | void *ie_end = pkt->data + pkt->datalen; |
---|
564 | |
---|
565 | if ( ! ieee80211_ie_bound ( ie, ie_end ) ) { |
---|
566 | DBGC ( ctx, "WPA %p: malformed PMKID KDE\n", ctx ); |
---|
567 | return wpa_fail ( ctx, -EINVAL ); |
---|
568 | } |
---|
569 | |
---|
570 | while ( ie ) { |
---|
571 | if ( ie->id == IEEE80211_IE_VENDOR && |
---|
572 | ie->vendor.oui == WPA_KDE_PMKID ) { |
---|
573 | rc = wpa_check_pmkid ( ctx, ie->vendor.data ); |
---|
574 | if ( rc < 0 ) { |
---|
575 | DBGC ( ctx, "WPA %p ALERT: PMKID " |
---|
576 | "mismatch in 1/4\n", ctx ); |
---|
577 | return wpa_fail ( ctx, rc ); |
---|
578 | } |
---|
579 | } |
---|
580 | |
---|
581 | ie = ieee80211_next_ie ( ie, ie_end ); |
---|
582 | } |
---|
583 | } |
---|
584 | |
---|
585 | DBGC ( ctx, "WPA %p: received 1/4, looks OK\n", ctx ); |
---|
586 | |
---|
587 | wpa_derive_ptk ( ctx ); |
---|
588 | |
---|
589 | return wpa_send_2_of_4 ( ctx, pkt, is_rsn, kie ); |
---|
590 | } |
---|
591 | |
---|
592 | |
---|
593 | /** |
---|
594 | * Send fourth frame in 4-Way Handshake, or second in Group Key Handshake |
---|
595 | * |
---|
596 | * @v ctx WPA common context |
---|
597 | * @v pkt EAPOL-Key packet for frame to which we're replying |
---|
598 | * @v is_rsn If TRUE, frame uses new RSN format |
---|
599 | * @v kie Key integrity and encryption handler |
---|
600 | * @ret rc Return status code |
---|
601 | */ |
---|
602 | static int wpa_send_final ( struct wpa_common_ctx *ctx, |
---|
603 | struct eapol_key_pkt *pkt, int is_rsn, |
---|
604 | struct wpa_kie *kie ) |
---|
605 | { |
---|
606 | struct io_buffer *iob = wpa_alloc_frame ( 0 ); |
---|
607 | struct eapol_key_pkt *npkt; |
---|
608 | |
---|
609 | if ( ! iob ) |
---|
610 | return -ENOMEM; |
---|
611 | |
---|
612 | npkt = iob->data; |
---|
613 | memcpy ( npkt, pkt, sizeof ( *pkt ) ); |
---|
614 | npkt->info &= ~( EAPOL_KEY_INFO_KEY_ACK | EAPOL_KEY_INFO_INSTALL | |
---|
615 | EAPOL_KEY_INFO_KEY_ENC ); |
---|
616 | if ( is_rsn ) |
---|
617 | npkt->keysize = 0; |
---|
618 | memset ( npkt->nonce, 0, sizeof ( npkt->nonce ) ); |
---|
619 | memset ( npkt->iv, 0, sizeof ( npkt->iv ) ); |
---|
620 | npkt->datalen = 0; |
---|
621 | |
---|
622 | if ( npkt->info & EAPOL_KEY_INFO_TYPE ) |
---|
623 | DBGC ( ctx, "WPA %p: sending 4/4\n", ctx ); |
---|
624 | else |
---|
625 | DBGC ( ctx, "WPA %p: sending 2/2\n", ctx ); |
---|
626 | |
---|
627 | return wpa_send_eapol ( iob, ctx, kie ); |
---|
628 | |
---|
629 | } |
---|
630 | |
---|
631 | |
---|
632 | /** |
---|
633 | * Handle receipt of third frame in 4-Way Handshake |
---|
634 | * |
---|
635 | * @v ctx WPA common context |
---|
636 | * @v pkt EAPOL-Key packet |
---|
637 | * @v is_rsn If TRUE, frame uses new RSN format |
---|
638 | * @v kie Key integrity and encryption handler |
---|
639 | * @ret rc Return status code |
---|
640 | */ |
---|
641 | static int wpa_handle_3_of_4 ( struct wpa_common_ctx *ctx, |
---|
642 | struct eapol_key_pkt *pkt, int is_rsn, |
---|
643 | struct wpa_kie *kie ) |
---|
644 | { |
---|
645 | int rc; |
---|
646 | u8 *this_rsn, *this_rsn_end; |
---|
647 | u8 *new_rsn, *new_rsn_end; |
---|
648 | int this_is_rsn, new_is_rsn; |
---|
649 | |
---|
650 | if ( ctx->state == WPA_WAITING ) |
---|
651 | return -EINVAL; |
---|
652 | |
---|
653 | ctx->state = WPA_WORKING; |
---|
654 | |
---|
655 | /* Check nonce */ |
---|
656 | if ( memcmp ( ctx->Anonce, pkt->nonce, WPA_NONCE_LEN ) != 0 ) { |
---|
657 | DBGC ( ctx, "WPA %p ALERT: nonce mismatch in 3/4\n", ctx ); |
---|
658 | return wpa_fail ( ctx, -EACCES ); |
---|
659 | } |
---|
660 | |
---|
661 | /* Check RSN IE */ |
---|
662 | this_rsn = sec80211_find_rsn ( ( union ieee80211_ie * ) pkt->data, |
---|
663 | pkt->data + pkt->datalen, |
---|
664 | &this_is_rsn, &this_rsn_end ); |
---|
665 | if ( this_rsn ) |
---|
666 | new_rsn = sec80211_find_rsn ( ( union ieee80211_ie * ) |
---|
667 | this_rsn_end, |
---|
668 | pkt->data + pkt->datalen, |
---|
669 | &new_is_rsn, &new_rsn_end ); |
---|
670 | else |
---|
671 | new_rsn = NULL; |
---|
672 | |
---|
673 | if ( ! ctx->ap_rsn_ie || ! this_rsn || |
---|
674 | ctx->ap_rsn_ie_len != ( this_rsn_end - this_rsn ) || |
---|
675 | ctx->ap_rsn_is_rsn != this_is_rsn || |
---|
676 | memcmp ( ctx->ap_rsn_ie, this_rsn, ctx->ap_rsn_ie_len ) != 0 ) { |
---|
677 | DBGC ( ctx, "WPA %p ALERT: RSN mismatch in 3/4\n", ctx ); |
---|
678 | DBGC2 ( ctx, "WPA %p RSNs (in 3/4, in beacon):\n", ctx ); |
---|
679 | DBGC2_HD ( ctx, this_rsn, this_rsn_end - this_rsn ); |
---|
680 | DBGC2_HD ( ctx, ctx->ap_rsn_ie, ctx->ap_rsn_ie_len ); |
---|
681 | return wpa_fail ( ctx, -EACCES ); |
---|
682 | } |
---|
683 | |
---|
684 | /* Don't switch if they just supplied both styles of IE |
---|
685 | simultaneously; we need two RSN IEs or two WPA IEs to |
---|
686 | switch ciphers. They'll be immediately consecutive because |
---|
687 | of ordering guarantees. */ |
---|
688 | if ( new_rsn && this_is_rsn == new_is_rsn ) { |
---|
689 | struct net80211_wlan *assoc = ctx->dev->associating; |
---|
690 | DBGC ( ctx, "WPA %p: accommodating bait-and-switch tactics\n", |
---|
691 | ctx ); |
---|
692 | DBGC2 ( ctx, "WPA %p RSNs (in 3/4+beacon, new in 3/4):\n", |
---|
693 | ctx ); |
---|
694 | DBGC2_HD ( ctx, this_rsn, this_rsn_end - this_rsn ); |
---|
695 | DBGC2_HD ( ctx, new_rsn, new_rsn_end - new_rsn ); |
---|
696 | |
---|
697 | if ( ( rc = sec80211_detect_ie ( new_is_rsn, new_rsn, |
---|
698 | new_rsn_end, |
---|
699 | &assoc->handshaking, |
---|
700 | &assoc->crypto ) ) != 0 ) |
---|
701 | DBGC ( ctx, "WPA %p: bait-and-switch invalid, staying " |
---|
702 | "with original request\n", ctx ); |
---|
703 | } else { |
---|
704 | new_rsn = this_rsn; |
---|
705 | new_is_rsn = this_is_rsn; |
---|
706 | new_rsn_end = this_rsn_end; |
---|
707 | } |
---|
708 | |
---|
709 | /* Grab group cryptosystem ID */ |
---|
710 | ctx->gcrypt = sec80211_rsn_get_net80211_crypt ( *( u32 * ) |
---|
711 | ( new_rsn + 2 ) ); |
---|
712 | |
---|
713 | /* Check for a GTK, if info field is encrypted */ |
---|
714 | if ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) { |
---|
715 | rc = wpa_maybe_install_gtk ( ctx, |
---|
716 | ( union ieee80211_ie * ) pkt->data, |
---|
717 | pkt->data + pkt->datalen, |
---|
718 | pkt->rsc ); |
---|
719 | if ( rc < 0 ) { |
---|
720 | DBGC ( ctx, "WPA %p did not install GTK in 3/4: %s\n", |
---|
721 | ctx, strerror ( rc ) ); |
---|
722 | if ( rc != -ENOENT ) |
---|
723 | return wpa_fail ( ctx, rc ); |
---|
724 | } |
---|
725 | } |
---|
726 | |
---|
727 | DBGC ( ctx, "WPA %p: received 3/4, looks OK\n", ctx ); |
---|
728 | |
---|
729 | /* Send final message */ |
---|
730 | rc = wpa_send_final ( ctx, pkt, is_rsn, kie ); |
---|
731 | if ( rc < 0 ) |
---|
732 | return wpa_fail ( ctx, rc ); |
---|
733 | |
---|
734 | /* Install PTK */ |
---|
735 | rc = wpa_install_ptk ( ctx, pkt->keysize ); |
---|
736 | if ( rc < 0 ) { |
---|
737 | DBGC ( ctx, "WPA %p failed to install PTK: %s\n", ctx, |
---|
738 | strerror ( rc ) ); |
---|
739 | return wpa_fail ( ctx, rc ); |
---|
740 | } |
---|
741 | |
---|
742 | /* Mark us as needing a new Snonce if we rekey */ |
---|
743 | ctx->have_Snonce = 0; |
---|
744 | |
---|
745 | /* Done! */ |
---|
746 | ctx->state = WPA_SUCCESS; |
---|
747 | return 0; |
---|
748 | } |
---|
749 | |
---|
750 | |
---|
751 | /** |
---|
752 | * Handle receipt of first frame in Group Key Handshake |
---|
753 | * |
---|
754 | * @v ctx WPA common context |
---|
755 | * @v pkt EAPOL-Key packet |
---|
756 | * @v is_rsn If TRUE, frame uses new RSN format |
---|
757 | * @v kie Key integrity and encryption handler |
---|
758 | * @ret rc Return status code |
---|
759 | */ |
---|
760 | static int wpa_handle_1_of_2 ( struct wpa_common_ctx *ctx, |
---|
761 | struct eapol_key_pkt *pkt, int is_rsn, |
---|
762 | struct wpa_kie *kie ) |
---|
763 | { |
---|
764 | int rc; |
---|
765 | |
---|
766 | /* |
---|
767 | * WPA and RSN do this completely differently. |
---|
768 | * |
---|
769 | * The idea of encoding the GTK (or PMKID, or various other |
---|
770 | * things) into a KDE that looks like an information element |
---|
771 | * is an RSN innovation; old WPA code never encapsulates |
---|
772 | * things like that. If it looks like an info element, it |
---|
773 | * really is (for the WPA IE check in frames 2/4 and 3/4). The |
---|
774 | * "key data encrypted" bit in the info field is also specific |
---|
775 | * to RSN. |
---|
776 | * |
---|
777 | * So from an old WPA host, 3/4 does not contain an |
---|
778 | * encapsulated GTK. The first frame of the GK handshake |
---|
779 | * contains it, encrypted, but without a KDE wrapper, and with |
---|
780 | * the key ID field (which gPXE doesn't use) shoved away in |
---|
781 | * the reserved bits in the info field, and the TxRx bit |
---|
782 | * stealing the Install bit's spot. |
---|
783 | */ |
---|
784 | |
---|
785 | if ( is_rsn && ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) ) { |
---|
786 | rc = wpa_maybe_install_gtk ( ctx, |
---|
787 | ( union ieee80211_ie * ) pkt->data, |
---|
788 | pkt->data + pkt->datalen, |
---|
789 | pkt->rsc ); |
---|
790 | if ( rc < 0 ) { |
---|
791 | DBGC ( ctx, "WPA %p: failed to install GTK in 1/2: " |
---|
792 | "%s\n", ctx, strerror ( rc ) ); |
---|
793 | return wpa_fail ( ctx, rc ); |
---|
794 | } |
---|
795 | } else { |
---|
796 | rc = kie->decrypt ( &ctx->ptk.kek, pkt->iv, pkt->data, |
---|
797 | &pkt->datalen ); |
---|
798 | if ( rc < 0 ) { |
---|
799 | DBGC ( ctx, "WPA %p: failed to decrypt GTK: %s\n", |
---|
800 | ctx, strerror ( rc ) ); |
---|
801 | return rc; /* non-fatal */ |
---|
802 | } |
---|
803 | if ( pkt->datalen > sizeof ( ctx->gtk.tk ) ) { |
---|
804 | DBGC ( ctx, "WPA %p: too much GTK data (%d > %d)\n", |
---|
805 | ctx, pkt->datalen, sizeof ( ctx->gtk.tk ) ); |
---|
806 | return wpa_fail ( ctx, -EINVAL ); |
---|
807 | } |
---|
808 | |
---|
809 | memcpy ( &ctx->gtk.tk, pkt->data, pkt->datalen ); |
---|
810 | wpa_install_gtk ( ctx, pkt->datalen, pkt->rsc ); |
---|
811 | } |
---|
812 | |
---|
813 | DBGC ( ctx, "WPA %p: received 1/2, looks OK\n", ctx ); |
---|
814 | |
---|
815 | return wpa_send_final ( ctx, pkt, is_rsn, kie ); |
---|
816 | } |
---|
817 | |
---|
818 | |
---|
819 | /** |
---|
820 | * Handle receipt of EAPOL-Key frame for WPA |
---|
821 | * |
---|
822 | * @v iob I/O buffer |
---|
823 | * @v netdev Network device |
---|
824 | * @v ll_source Source link-layer address |
---|
825 | */ |
---|
826 | static int eapol_key_rx ( struct io_buffer *iob, struct net_device *netdev, |
---|
827 | const void *ll_source ) |
---|
828 | { |
---|
829 | struct net80211_device *dev = net80211_get ( netdev ); |
---|
830 | struct eapol_key_pkt *pkt = iob->data; |
---|
831 | int is_rsn, found_ctx; |
---|
832 | struct wpa_common_ctx *ctx; |
---|
833 | int rc = 0; |
---|
834 | struct wpa_kie *kie; |
---|
835 | u8 their_mic[16], our_mic[16]; |
---|
836 | |
---|
837 | if ( pkt->type != EAPOL_KEY_TYPE_WPA && |
---|
838 | pkt->type != EAPOL_KEY_TYPE_RSN ) { |
---|
839 | DBG ( "EAPOL-Key: packet not of 802.11 type\n" ); |
---|
840 | rc = -EINVAL; |
---|
841 | goto drop; |
---|
842 | } |
---|
843 | |
---|
844 | is_rsn = ( pkt->type == EAPOL_KEY_TYPE_RSN ); |
---|
845 | |
---|
846 | if ( ! dev ) { |
---|
847 | DBG ( "EAPOL-Key: packet not from 802.11\n" ); |
---|
848 | rc = -EINVAL; |
---|
849 | goto drop; |
---|
850 | } |
---|
851 | |
---|
852 | if ( memcmp ( dev->bssid, ll_source, ETH_ALEN ) != 0 ) { |
---|
853 | DBG ( "EAPOL-Key: packet not from associated AP\n" ); |
---|
854 | rc = -EINVAL; |
---|
855 | goto drop; |
---|
856 | } |
---|
857 | |
---|
858 | if ( ! ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_KEY_ACK ) ) { |
---|
859 | DBG ( "EAPOL-Key: packet sent in wrong direction\n" ); |
---|
860 | rc = -EINVAL; |
---|
861 | goto drop; |
---|
862 | } |
---|
863 | |
---|
864 | found_ctx = 0; |
---|
865 | list_for_each_entry ( ctx, &wpa_contexts, list ) { |
---|
866 | if ( ctx->dev == dev ) { |
---|
867 | found_ctx = 1; |
---|
868 | break; |
---|
869 | } |
---|
870 | } |
---|
871 | |
---|
872 | if ( ! found_ctx ) { |
---|
873 | DBG ( "EAPOL-Key: no WPA context to handle packet for %p\n", |
---|
874 | dev ); |
---|
875 | rc = -ENOENT; |
---|
876 | goto drop; |
---|
877 | } |
---|
878 | |
---|
879 | if ( ( void * ) ( pkt + 1 ) + ntohs ( pkt->datalen ) > iob->tail ) { |
---|
880 | DBGC ( ctx, "WPA %p: packet truncated (has %d extra bytes, " |
---|
881 | "states %d)\n", ctx, iob->tail - ( void * ) ( pkt + 1 ), |
---|
882 | ntohs ( pkt->datalen ) ); |
---|
883 | rc = -EINVAL; |
---|
884 | goto drop; |
---|
885 | } |
---|
886 | |
---|
887 | /* Get a handle on key integrity/encryption handler */ |
---|
888 | kie = wpa_find_kie ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_VERSION ); |
---|
889 | if ( ! kie ) { |
---|
890 | DBGC ( ctx, "WPA %p: no support for packet version %d\n", ctx, |
---|
891 | ntohs ( pkt->info ) & EAPOL_KEY_INFO_VERSION ); |
---|
892 | rc = wpa_fail ( ctx, -ENOTSUP ); |
---|
893 | goto drop; |
---|
894 | } |
---|
895 | |
---|
896 | /* Check MIC */ |
---|
897 | if ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_KEY_MIC ) { |
---|
898 | memcpy ( their_mic, pkt->mic, sizeof ( pkt->mic ) ); |
---|
899 | memset ( pkt->mic, 0, sizeof ( pkt->mic ) ); |
---|
900 | kie->mic ( &ctx->ptk.kck, ( void * ) pkt - EAPOL_HDR_LEN, |
---|
901 | EAPOL_HDR_LEN + sizeof ( *pkt ) + |
---|
902 | ntohs ( pkt->datalen ), our_mic ); |
---|
903 | DBGC2 ( ctx, "WPA %p MIC comparison (theirs, ours):\n", ctx ); |
---|
904 | DBGC2_HD ( ctx, their_mic, 16 ); |
---|
905 | DBGC2_HD ( ctx, our_mic, 16 ); |
---|
906 | if ( memcmp ( their_mic, our_mic, sizeof ( pkt->mic ) ) != 0 ) { |
---|
907 | DBGC ( ctx, "WPA %p: EAPOL MIC failure\n", ctx ); |
---|
908 | goto drop; |
---|
909 | } |
---|
910 | } |
---|
911 | |
---|
912 | /* Fix byte order to local */ |
---|
913 | pkt->info = ntohs ( pkt->info ); |
---|
914 | pkt->keysize = ntohs ( pkt->keysize ); |
---|
915 | pkt->datalen = ntohs ( pkt->datalen ); |
---|
916 | pkt->replay = be64_to_cpu ( pkt->replay ); |
---|
917 | |
---|
918 | /* Check replay counter */ |
---|
919 | if ( ctx->replay != ~0ULL && ctx->replay >= pkt->replay ) { |
---|
920 | DBGC ( ctx, "WPA %p ALERT: Replay detected! " |
---|
921 | "(%08x:%08x >= %08x:%08x)\n", ctx, |
---|
922 | ( u32 ) ( ctx->replay >> 32 ), ( u32 ) ctx->replay, |
---|
923 | ( u32 ) ( pkt->replay >> 32 ), ( u32 ) pkt->replay ); |
---|
924 | rc = 0; /* ignore without error */ |
---|
925 | goto drop; |
---|
926 | } |
---|
927 | ctx->replay = pkt->replay; |
---|
928 | |
---|
929 | /* Decrypt key data */ |
---|
930 | if ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) { |
---|
931 | rc = kie->decrypt ( &ctx->ptk.kek, pkt->iv, pkt->data, |
---|
932 | &pkt->datalen ); |
---|
933 | if ( rc < 0 ) { |
---|
934 | DBGC ( ctx, "WPA %p: failed to decrypt packet: %s\n", |
---|
935 | ctx, strerror ( rc ) ); |
---|
936 | goto drop; |
---|
937 | } |
---|
938 | } |
---|
939 | |
---|
940 | /* Hand it off to appropriate handler */ |
---|
941 | switch ( pkt->info & ( EAPOL_KEY_INFO_TYPE | |
---|
942 | EAPOL_KEY_INFO_KEY_MIC ) ) { |
---|
943 | case EAPOL_KEY_TYPE_PTK: |
---|
944 | rc = wpa_handle_1_of_4 ( ctx, pkt, is_rsn, kie ); |
---|
945 | break; |
---|
946 | |
---|
947 | case EAPOL_KEY_TYPE_PTK | EAPOL_KEY_INFO_KEY_MIC: |
---|
948 | rc = wpa_handle_3_of_4 ( ctx, pkt, is_rsn, kie ); |
---|
949 | break; |
---|
950 | |
---|
951 | case EAPOL_KEY_TYPE_GTK | EAPOL_KEY_INFO_KEY_MIC: |
---|
952 | rc = wpa_handle_1_of_2 ( ctx, pkt, is_rsn, kie ); |
---|
953 | break; |
---|
954 | |
---|
955 | default: |
---|
956 | DBGC ( ctx, "WPA %p: Invalid combination of key flags %04x\n", |
---|
957 | ctx, pkt->info ); |
---|
958 | rc = -EINVAL; |
---|
959 | break; |
---|
960 | } |
---|
961 | |
---|
962 | drop: |
---|
963 | free_iob ( iob ); |
---|
964 | return rc; |
---|
965 | } |
---|
966 | |
---|
967 | struct eapol_handler eapol_key_handler __eapol_handler = { |
---|
968 | .type = EAPOL_TYPE_KEY, |
---|
969 | .rx = eapol_key_rx, |
---|
970 | }; |
---|
971 | |
---|
972 | /* WPA always needs EAPOL in order to be useful */ |
---|
973 | REQUIRE_OBJECT ( eapol ); |
---|