]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * EAP peer method: EAP-PSK (RFC 4764) | |
3 | * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * Alternatively, this software may be distributed under the terms of BSD | |
10 | * license. | |
11 | * | |
12 | * See README and COPYING for more details. | |
13 | * | |
14 | * Note: EAP-PSK is an EAP authentication method and as such, completely | |
15 | * different from WPA-PSK. This file is not needed for WPA-PSK functionality. | |
16 | */ | |
17 | ||
18 | #include "includes.h" | |
19 | ||
20 | #include "common.h" | |
03da66bd | 21 | #include "crypto/aes_wrap.h" |
3642c431 | 22 | #include "crypto/random.h" |
6fc6879b | 23 | #include "eap_common/eap_psk_common.h" |
03da66bd | 24 | #include "eap_i.h" |
6fc6879b JM |
25 | |
26 | ||
27 | struct eap_psk_data { | |
28 | enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state; | |
29 | u8 rand_p[EAP_PSK_RAND_LEN]; | |
30 | u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; | |
31 | u8 *id_s, *id_p; | |
32 | size_t id_s_len, id_p_len; | |
33 | u8 msk[EAP_MSK_LEN]; | |
34 | u8 emsk[EAP_EMSK_LEN]; | |
35 | }; | |
36 | ||
37 | ||
38 | static void * eap_psk_init(struct eap_sm *sm) | |
39 | { | |
40 | struct eap_psk_data *data; | |
41 | const u8 *identity, *password; | |
42 | size_t identity_len, password_len; | |
43 | ||
44 | password = eap_get_config_password(sm, &password_len); | |
45 | if (!password || password_len != 16) { | |
46 | wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not " | |
47 | "configured"); | |
48 | return NULL; | |
49 | } | |
50 | ||
51 | data = os_zalloc(sizeof(*data)); | |
52 | if (data == NULL) | |
53 | return NULL; | |
54 | if (eap_psk_key_setup(password, data->ak, data->kdk)) { | |
55 | os_free(data); | |
56 | return NULL; | |
57 | } | |
58 | wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); | |
59 | wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); | |
60 | data->state = PSK_INIT; | |
61 | ||
62 | identity = eap_get_config_identity(sm, &identity_len); | |
63 | if (identity) { | |
64 | data->id_p = os_malloc(identity_len); | |
65 | if (data->id_p) | |
66 | os_memcpy(data->id_p, identity, identity_len); | |
67 | data->id_p_len = identity_len; | |
68 | } | |
69 | if (data->id_p == NULL) { | |
70 | wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity"); | |
71 | os_free(data); | |
72 | return NULL; | |
73 | } | |
74 | ||
75 | return data; | |
76 | } | |
77 | ||
78 | ||
79 | static void eap_psk_deinit(struct eap_sm *sm, void *priv) | |
80 | { | |
81 | struct eap_psk_data *data = priv; | |
82 | os_free(data->id_s); | |
83 | os_free(data->id_p); | |
84 | os_free(data); | |
85 | } | |
86 | ||
87 | ||
88 | static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, | |
89 | struct eap_method_ret *ret, | |
90 | const struct wpabuf *reqData) | |
91 | { | |
92 | const struct eap_psk_hdr_1 *hdr1; | |
93 | struct eap_psk_hdr_2 *hdr2; | |
94 | struct wpabuf *resp; | |
95 | u8 *buf, *pos; | |
96 | size_t buflen, len; | |
97 | const u8 *cpos; | |
98 | ||
99 | wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state"); | |
100 | ||
101 | cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); | |
102 | hdr1 = (const struct eap_psk_hdr_1 *) cpos; | |
103 | if (cpos == NULL || len < sizeof(*hdr1)) { | |
104 | wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message " | |
105 | "length (%lu; expected %lu or more)", | |
106 | (unsigned long) len, | |
107 | (unsigned long) sizeof(*hdr1)); | |
108 | ret->ignore = TRUE; | |
109 | return NULL; | |
110 | } | |
111 | wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags); | |
112 | if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) { | |
113 | wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)", | |
114 | EAP_PSK_FLAGS_GET_T(hdr1->flags)); | |
115 | ret->methodState = METHOD_DONE; | |
116 | ret->decision = DECISION_FAIL; | |
117 | return NULL; | |
118 | } | |
119 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, | |
120 | EAP_PSK_RAND_LEN); | |
121 | os_free(data->id_s); | |
122 | data->id_s_len = len - sizeof(*hdr1); | |
123 | data->id_s = os_malloc(data->id_s_len); | |
124 | if (data->id_s == NULL) { | |
125 | wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " | |
126 | "ID_S (len=%lu)", (unsigned long) data->id_s_len); | |
127 | ret->ignore = TRUE; | |
128 | return NULL; | |
129 | } | |
130 | os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); | |
131 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", | |
132 | data->id_s, data->id_s_len); | |
133 | ||
3642c431 | 134 | if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) { |
6fc6879b JM |
135 | wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); |
136 | ret->ignore = TRUE; | |
137 | return NULL; | |
138 | } | |
139 | ||
140 | resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, | |
141 | sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE, | |
142 | eap_get_id(reqData)); | |
143 | if (resp == NULL) | |
144 | return NULL; | |
145 | hdr2 = wpabuf_put(resp, sizeof(*hdr2)); | |
146 | hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */ | |
147 | os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); | |
148 | os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN); | |
149 | wpabuf_put_data(resp, data->id_p, data->id_p_len); | |
150 | /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ | |
151 | buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; | |
152 | buf = os_malloc(buflen); | |
153 | if (buf == NULL) { | |
154 | wpabuf_free(resp); | |
155 | return NULL; | |
156 | } | |
157 | os_memcpy(buf, data->id_p, data->id_p_len); | |
158 | pos = buf + data->id_p_len; | |
159 | os_memcpy(pos, data->id_s, data->id_s_len); | |
160 | pos += data->id_s_len; | |
161 | os_memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN); | |
162 | pos += EAP_PSK_RAND_LEN; | |
163 | os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); | |
164 | if (omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p)) { | |
165 | os_free(buf); | |
166 | wpabuf_free(resp); | |
167 | return NULL; | |
168 | } | |
169 | os_free(buf); | |
170 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p, | |
171 | EAP_PSK_RAND_LEN); | |
172 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN); | |
173 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P", | |
174 | data->id_p, data->id_p_len); | |
175 | ||
176 | data->state = PSK_MAC_SENT; | |
177 | ||
178 | return resp; | |
179 | } | |
180 | ||
181 | ||
182 | static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data, | |
183 | struct eap_method_ret *ret, | |
184 | const struct wpabuf *reqData) | |
185 | { | |
186 | const struct eap_psk_hdr_3 *hdr3; | |
187 | struct eap_psk_hdr_4 *hdr4; | |
188 | struct wpabuf *resp; | |
189 | u8 *buf, *rpchannel, nonce[16], *decrypted; | |
190 | const u8 *pchannel, *tag, *msg; | |
191 | u8 mac[EAP_PSK_MAC_LEN]; | |
192 | size_t buflen, left, data_len, len, plen; | |
193 | int failed = 0; | |
194 | const u8 *pos; | |
195 | ||
196 | wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state"); | |
197 | ||
198 | pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, | |
199 | reqData, &len); | |
200 | hdr3 = (const struct eap_psk_hdr_3 *) pos; | |
201 | if (pos == NULL || len < sizeof(*hdr3)) { | |
202 | wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message " | |
203 | "length (%lu; expected %lu or more)", | |
204 | (unsigned long) len, | |
205 | (unsigned long) sizeof(*hdr3)); | |
206 | ret->ignore = TRUE; | |
207 | return NULL; | |
208 | } | |
209 | left = len - sizeof(*hdr3); | |
210 | pchannel = (const u8 *) (hdr3 + 1); | |
211 | wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags); | |
212 | if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) { | |
213 | wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)", | |
214 | EAP_PSK_FLAGS_GET_T(hdr3->flags)); | |
215 | ret->methodState = METHOD_DONE; | |
216 | ret->decision = DECISION_FAIL; | |
217 | return NULL; | |
218 | } | |
219 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s, | |
220 | EAP_PSK_RAND_LEN); | |
221 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN); | |
222 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left); | |
223 | ||
224 | if (left < 4 + 16 + 1) { | |
225 | wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " | |
226 | "third message (len=%lu, expected 21)", | |
227 | (unsigned long) left); | |
228 | ret->ignore = TRUE; | |
229 | return NULL; | |
230 | } | |
231 | ||
232 | /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ | |
233 | buflen = data->id_s_len + EAP_PSK_RAND_LEN; | |
234 | buf = os_malloc(buflen); | |
235 | if (buf == NULL) | |
236 | return NULL; | |
237 | os_memcpy(buf, data->id_s, data->id_s_len); | |
238 | os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); | |
239 | if (omac1_aes_128(data->ak, buf, buflen, mac)) { | |
240 | os_free(buf); | |
241 | return NULL; | |
242 | } | |
243 | os_free(buf); | |
244 | if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) { | |
245 | wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third " | |
246 | "message"); | |
247 | ret->methodState = METHOD_DONE; | |
248 | ret->decision = DECISION_FAIL; | |
249 | return NULL; | |
250 | } | |
251 | wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully"); | |
252 | ||
253 | if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, | |
254 | data->msk, data->emsk)) { | |
255 | ret->methodState = METHOD_DONE; | |
256 | ret->decision = DECISION_FAIL; | |
257 | return NULL; | |
258 | } | |
259 | wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); | |
260 | wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN); | |
261 | wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN); | |
262 | ||
263 | os_memset(nonce, 0, 12); | |
264 | os_memcpy(nonce + 12, pchannel, 4); | |
265 | pchannel += 4; | |
266 | left -= 4; | |
267 | ||
268 | tag = pchannel; | |
269 | pchannel += 16; | |
270 | left -= 16; | |
271 | ||
272 | msg = pchannel; | |
273 | ||
274 | wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce", | |
275 | nonce, sizeof(nonce)); | |
276 | wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", | |
277 | wpabuf_head(reqData), 5); | |
278 | wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); | |
279 | ||
280 | decrypted = os_malloc(left); | |
281 | if (decrypted == NULL) { | |
282 | ret->methodState = METHOD_DONE; | |
283 | ret->decision = DECISION_FAIL; | |
284 | return NULL; | |
285 | } | |
286 | os_memcpy(decrypted, msg, left); | |
287 | ||
288 | if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), | |
289 | wpabuf_head(reqData), | |
290 | sizeof(struct eap_hdr) + 1 + | |
291 | sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted, | |
292 | left, tag)) { | |
293 | wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); | |
294 | os_free(decrypted); | |
295 | return NULL; | |
296 | } | |
297 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", | |
298 | decrypted, left); | |
299 | ||
300 | /* Verify R flag */ | |
301 | switch (decrypted[0] >> 6) { | |
302 | case EAP_PSK_R_FLAG_CONT: | |
303 | wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); | |
304 | failed = 1; | |
305 | break; | |
306 | case EAP_PSK_R_FLAG_DONE_SUCCESS: | |
307 | wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); | |
308 | break; | |
309 | case EAP_PSK_R_FLAG_DONE_FAILURE: | |
310 | wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); | |
311 | wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected " | |
312 | "authentication"); | |
313 | failed = 1; | |
314 | break; | |
315 | } | |
316 | ||
317 | data_len = 1; | |
318 | if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1) | |
319 | data_len++; | |
320 | plen = sizeof(*hdr4) + 4 + 16 + data_len; | |
321 | resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen, | |
322 | EAP_CODE_RESPONSE, eap_get_id(reqData)); | |
323 | if (resp == NULL) { | |
324 | os_free(decrypted); | |
325 | return NULL; | |
326 | } | |
327 | hdr4 = wpabuf_put(resp, sizeof(*hdr4)); | |
328 | hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */ | |
329 | os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN); | |
330 | rpchannel = wpabuf_put(resp, 4 + 16 + data_len); | |
331 | ||
332 | /* nonce++ */ | |
333 | inc_byte_array(nonce, sizeof(nonce)); | |
334 | os_memcpy(rpchannel, nonce + 12, 4); | |
335 | ||
336 | if (decrypted[0] & EAP_PSK_E_FLAG) { | |
337 | wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag"); | |
338 | failed = 1; | |
339 | rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) | | |
340 | EAP_PSK_E_FLAG; | |
341 | if (left > 1) { | |
342 | /* Add empty EXT_Payload with same EXT_Type */ | |
343 | rpchannel[4 + 16 + 1] = decrypted[1]; | |
344 | } | |
345 | } else if (failed) | |
346 | rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6; | |
347 | else | |
348 | rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; | |
349 | ||
350 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)", | |
351 | rpchannel + 4 + 16, data_len); | |
352 | if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), | |
353 | wpabuf_head(resp), | |
354 | sizeof(struct eap_hdr) + 1 + sizeof(*hdr4), | |
355 | rpchannel + 4 + 16, data_len, rpchannel + 4)) { | |
356 | os_free(decrypted); | |
357 | wpabuf_free(resp); | |
358 | return NULL; | |
359 | } | |
360 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)", | |
361 | rpchannel, 4 + 16 + data_len); | |
362 | ||
363 | wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully", | |
364 | failed ? "un" : ""); | |
365 | data->state = PSK_DONE; | |
366 | ret->methodState = METHOD_DONE; | |
367 | ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC; | |
368 | ||
369 | os_free(decrypted); | |
370 | ||
371 | return resp; | |
372 | } | |
373 | ||
374 | ||
375 | static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv, | |
376 | struct eap_method_ret *ret, | |
377 | const struct wpabuf *reqData) | |
378 | { | |
379 | struct eap_psk_data *data = priv; | |
380 | const u8 *pos; | |
381 | struct wpabuf *resp = NULL; | |
382 | size_t len; | |
383 | ||
384 | pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); | |
385 | if (pos == NULL) { | |
386 | ret->ignore = TRUE; | |
387 | return NULL; | |
388 | } | |
389 | ||
390 | ret->ignore = FALSE; | |
391 | ret->methodState = METHOD_MAY_CONT; | |
392 | ret->decision = DECISION_FAIL; | |
393 | ret->allowNotifications = TRUE; | |
394 | ||
395 | switch (data->state) { | |
396 | case PSK_INIT: | |
397 | resp = eap_psk_process_1(data, ret, reqData); | |
398 | break; | |
399 | case PSK_MAC_SENT: | |
400 | resp = eap_psk_process_3(data, ret, reqData); | |
401 | break; | |
402 | case PSK_DONE: | |
403 | wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore " | |
404 | "unexpected message"); | |
405 | ret->ignore = TRUE; | |
406 | return NULL; | |
407 | } | |
408 | ||
409 | if (ret->methodState == METHOD_DONE) { | |
410 | ret->allowNotifications = FALSE; | |
411 | } | |
412 | ||
413 | return resp; | |
414 | } | |
415 | ||
416 | ||
417 | static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv) | |
418 | { | |
419 | struct eap_psk_data *data = priv; | |
420 | return data->state == PSK_DONE; | |
421 | } | |
422 | ||
423 | ||
424 | static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) | |
425 | { | |
426 | struct eap_psk_data *data = priv; | |
427 | u8 *key; | |
428 | ||
429 | if (data->state != PSK_DONE) | |
430 | return NULL; | |
431 | ||
432 | key = os_malloc(EAP_MSK_LEN); | |
433 | if (key == NULL) | |
434 | return NULL; | |
435 | ||
436 | *len = EAP_MSK_LEN; | |
437 | os_memcpy(key, data->msk, EAP_MSK_LEN); | |
438 | ||
439 | return key; | |
440 | } | |
441 | ||
442 | ||
443 | static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) | |
444 | { | |
445 | struct eap_psk_data *data = priv; | |
446 | u8 *key; | |
447 | ||
448 | if (data->state != PSK_DONE) | |
449 | return NULL; | |
450 | ||
451 | key = os_malloc(EAP_EMSK_LEN); | |
452 | if (key == NULL) | |
453 | return NULL; | |
454 | ||
455 | *len = EAP_EMSK_LEN; | |
456 | os_memcpy(key, data->emsk, EAP_EMSK_LEN); | |
457 | ||
458 | return key; | |
459 | } | |
460 | ||
461 | ||
462 | int eap_peer_psk_register(void) | |
463 | { | |
464 | struct eap_method *eap; | |
465 | int ret; | |
466 | ||
467 | eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, | |
468 | EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); | |
469 | if (eap == NULL) | |
470 | return -1; | |
471 | ||
472 | eap->init = eap_psk_init; | |
473 | eap->deinit = eap_psk_deinit; | |
474 | eap->process = eap_psk_process; | |
475 | eap->isKeyAvailable = eap_psk_isKeyAvailable; | |
476 | eap->getKey = eap_psk_getKey; | |
477 | eap->get_emsk = eap_psk_get_emsk; | |
478 | ||
479 | ret = eap_peer_method_register(eap); | |
480 | if (ret) | |
481 | eap_peer_method_free(eap); | |
482 | return ret; | |
483 | } |