Re-enable hardware UDP/TCP checksum calculation with pseudo header on
[dragonfly/port-amd64.git] / contrib / wpa_supplicant-0.4.9 / eap_pax.c
blobb590b90da06c017c5cf0616735cef3fd8a189ede
1 /*
2 * WPA Supplicant / EAP-PAX (draft-clancy-eap-pax-04.txt)
3 * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
12 * See README and COPYING for more details.
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
19 #include "common.h"
20 #include "eap_i.h"
21 #include "wpa_supplicant.h"
22 #include "config_ssid.h"
23 #include "eap_pax_common.h"
24 #include "sha1.h"
25 #include "crypto.h"
28 * Note: only PAX_STD subprotocol is currently supported
31 struct eap_pax_data {
32 enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state;
33 u8 mac_id, dh_group_id, public_key_id;
34 union {
35 u8 e[2 * EAP_PAX_RAND_LEN];
36 struct {
37 u8 x[EAP_PAX_RAND_LEN]; /* server rand */
38 u8 y[EAP_PAX_RAND_LEN]; /* client rand */
39 } r;
40 } rand;
41 char *cid;
42 size_t cid_len;
43 u8 ak[EAP_PAX_AK_LEN];
44 u8 mk[EAP_PAX_MK_LEN];
45 u8 ck[EAP_PAX_CK_LEN];
46 u8 ick[EAP_PAX_ICK_LEN];
50 static void eap_pax_deinit(struct eap_sm *sm, void *priv);
53 static void * eap_pax_init(struct eap_sm *sm)
55 struct wpa_ssid *config = eap_get_config(sm);
56 struct eap_pax_data *data;
58 if (config == NULL || !config->nai ||
59 (!config->eappsk && !config->password)) {
60 wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key "
61 "(eappsk/password) not configured");
62 return NULL;
65 if (config->eappsk && config->eappsk_len != EAP_PAX_AK_LEN) {
66 wpa_printf(MSG_INFO, "EAP-PAX: incorrect key length (eappsk); "
67 "expected %d", EAP_PAX_AK_LEN);
68 return NULL;
71 data = malloc(sizeof(*data));
72 if (data == NULL)
73 return NULL;
74 memset(data, 0, sizeof(*data));
75 data->state = PAX_INIT;
77 data->cid = malloc(config->nai_len);
78 if (data->cid == NULL) {
79 eap_pax_deinit(sm, data);
80 return NULL;
82 memcpy(data->cid, config->nai, config->nai_len);
83 data->cid_len = config->nai_len;
85 if (config->eappsk) {
86 memcpy(data->ak, config->eappsk, EAP_PAX_AK_LEN);
87 } else {
88 u8 hash[SHA1_MAC_LEN];
89 const unsigned char *addr[1];
90 size_t len[1];
91 addr[0] = config->password;
92 len[0] = config->password_len;
93 sha1_vector(1, addr, len, hash);
94 memcpy(data->ak, hash, EAP_PAX_AK_LEN);
97 return data;
101 static void eap_pax_deinit(struct eap_sm *sm, void *priv)
103 struct eap_pax_data *data = priv;
104 free(data->cid);
105 free(data);
109 static struct eap_pax_hdr * eap_pax_alloc_resp(const struct eap_pax_hdr *req,
110 u16 resp_len, u8 op_code)
112 struct eap_pax_hdr *resp;
114 resp = malloc(resp_len);
115 if (resp == NULL)
116 return NULL;
117 resp->code = EAP_CODE_RESPONSE;
118 resp->identifier = req->identifier;
119 resp->length = host_to_be16(resp_len);
120 resp->type = EAP_TYPE_PAX;
121 resp->op_code = op_code;
122 resp->flags = 0;
123 resp->mac_id = req->mac_id;
124 resp->dh_group_id = req->dh_group_id;
125 resp->public_key_id = req->public_key_id;
126 return resp;
130 static u8 * eap_pax_process_std_1(struct eap_sm *sm, struct eap_pax_data *data,
131 struct eap_method_ret *ret,
132 const u8 *reqData, size_t reqDataLen,
133 size_t *respDataLen)
135 const struct eap_pax_hdr *req;
136 struct eap_pax_hdr *resp;
137 const u8 *pos;
138 u8 *rpos;
139 size_t left;
141 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)");
142 req = (const struct eap_pax_hdr *) reqData;
144 if (data->state != PAX_INIT) {
145 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in "
146 "unexpected state (%d) - ignored", data->state);
147 ret->ignore = TRUE;
148 return NULL;
151 if (req->flags & EAP_PAX_FLAGS_CE) {
152 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - "
153 "ignored");
154 ret->ignore = TRUE;
155 return NULL;
158 left = reqDataLen - sizeof(*req);
160 if (left < 2 + EAP_PAX_RAND_LEN) {
161 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short "
162 "payload");
163 ret->ignore = TRUE;
164 return NULL;
167 pos = (const u8 *) (req + 1);
168 if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
169 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A "
170 "length %d (expected %d)",
171 WPA_GET_BE16(pos), EAP_PAX_RAND_LEN);
172 ret->ignore = TRUE;
173 return NULL;
176 pos += 2;
177 left -= 2;
178 memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN);
179 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)",
180 data->rand.r.x, EAP_PAX_RAND_LEN);
181 pos += EAP_PAX_RAND_LEN;
182 left -= EAP_PAX_RAND_LEN;
184 if (left > 0) {
185 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
186 pos, left);
189 if (hostapd_get_rand(data->rand.r.y, EAP_PAX_RAND_LEN)) {
190 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
191 ret->ignore = TRUE;
192 return NULL;
194 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
195 data->rand.r.y, EAP_PAX_RAND_LEN);
197 if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e,
198 data->mk, data->ck, data->ick) < 0)
200 ret->ignore = TRUE;
201 return NULL;
204 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)");
206 *respDataLen = sizeof(*resp) + 2 + EAP_PAX_RAND_LEN +
207 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN + EAP_PAX_ICV_LEN;
208 resp = eap_pax_alloc_resp(req, *respDataLen, EAP_PAX_OP_STD_2);
209 if (resp == NULL)
210 return NULL;
212 rpos = (u8 *) (resp + 1);
213 *rpos++ = 0;
214 *rpos++ = EAP_PAX_RAND_LEN;
215 memcpy(rpos, data->rand.r.y, EAP_PAX_RAND_LEN);
216 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)",
217 rpos, EAP_PAX_RAND_LEN);
218 rpos += EAP_PAX_RAND_LEN;
220 WPA_PUT_BE16(rpos, data->cid_len);
221 rpos += 2;
222 memcpy(rpos, data->cid, data->cid_len);
223 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", rpos, data->cid_len);
224 rpos += data->cid_len;
226 *rpos++ = 0;
227 *rpos++ = EAP_PAX_MAC_LEN;
228 eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN,
229 data->rand.r.x, EAP_PAX_RAND_LEN,
230 data->rand.r.y, EAP_PAX_RAND_LEN,
231 (u8 *) data->cid, data->cid_len, rpos);
232 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
233 rpos, EAP_PAX_MAC_LEN);
234 rpos += EAP_PAX_MAC_LEN;
236 eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
237 (u8 *) resp, *respDataLen - EAP_PAX_ICV_LEN,
238 NULL, 0, NULL, 0, rpos);
239 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
240 rpos += EAP_PAX_ICV_LEN;
242 data->state = PAX_STD_2_SENT;
243 data->mac_id = req->mac_id;
244 data->dh_group_id = req->dh_group_id;
245 data->public_key_id = req->public_key_id;
247 return (u8 *) resp;
251 static u8 * eap_pax_process_std_3(struct eap_sm *sm, struct eap_pax_data *data,
252 struct eap_method_ret *ret,
253 const u8 *reqData, size_t reqDataLen,
254 size_t *respDataLen)
256 const struct eap_pax_hdr *req;
257 struct eap_pax_hdr *resp;
258 u8 *rpos, mac[EAP_PAX_MAC_LEN];
259 const u8 *pos;
260 size_t left;
262 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)");
263 req = (const struct eap_pax_hdr *) reqData;
265 if (data->state != PAX_STD_2_SENT) {
266 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in "
267 "unexpected state (%d) - ignored", data->state);
268 ret->ignore = TRUE;
269 return NULL;
272 if (req->flags & EAP_PAX_FLAGS_CE) {
273 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - "
274 "ignored");
275 ret->ignore = TRUE;
276 return NULL;
279 left = reqDataLen - sizeof(*req);
281 if (left < 2 + EAP_PAX_MAC_LEN) {
282 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short "
283 "payload");
284 ret->ignore = TRUE;
285 return NULL;
288 pos = (const u8 *) (req + 1);
289 if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
290 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect "
291 "MAC_CK length %d (expected %d)",
292 WPA_GET_BE16(pos), EAP_PAX_MAC_LEN);
293 ret->ignore = TRUE;
294 return NULL;
296 pos += 2;
297 left -= 2;
298 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
299 pos, EAP_PAX_MAC_LEN);
300 eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
301 data->rand.r.y, EAP_PAX_RAND_LEN,
302 (u8 *) data->cid, data->cid_len, NULL, 0, mac);
303 if (memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) {
304 wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) "
305 "received");
306 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)",
307 mac, EAP_PAX_MAC_LEN);
308 ret->methodState = METHOD_DONE;
309 ret->decision = DECISION_FAIL;
310 return NULL;
313 pos += EAP_PAX_MAC_LEN;
314 left -= EAP_PAX_MAC_LEN;
316 if (left > 0) {
317 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
318 pos, left);
321 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)");
323 *respDataLen = sizeof(*resp) + EAP_PAX_ICV_LEN;
324 resp = eap_pax_alloc_resp(req, *respDataLen, EAP_PAX_OP_ACK);
325 if (resp == NULL)
326 return NULL;
328 rpos = (u8 *) (resp + 1);
329 eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
330 (u8 *) resp, *respDataLen - EAP_PAX_ICV_LEN,
331 NULL, 0, NULL, 0, rpos);
332 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
334 data->state = PAX_DONE;
335 ret->methodState = METHOD_DONE;
336 ret->decision = DECISION_UNCOND_SUCC;
337 ret->allowNotifications = FALSE;
339 return (u8 *) resp;
343 static u8 * eap_pax_process(struct eap_sm *sm, void *priv,
344 struct eap_method_ret *ret,
345 const u8 *reqData, size_t reqDataLen,
346 size_t *respDataLen)
348 struct eap_pax_data *data = priv;
349 const struct eap_pax_hdr *req;
350 u8 *resp, icvbuf[EAP_PAX_ICV_LEN];
351 const u8 *icv, *pos;
352 size_t len;
353 u16 flen;
355 pos = eap_hdr_validate(EAP_TYPE_PAX, reqData, reqDataLen, &len);
356 if (pos == NULL || len < EAP_PAX_ICV_LEN) {
357 ret->ignore = TRUE;
358 return NULL;
360 req = (const struct eap_pax_hdr *) reqData;
361 flen = be_to_host16(req->length) - EAP_PAX_ICV_LEN;
363 wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
364 "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
365 "public_key_id 0x%x",
366 req->op_code, req->flags, req->mac_id, req->dh_group_id,
367 req->public_key_id);
368 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
369 pos, len - EAP_PAX_ICV_LEN);
371 if (data->state != PAX_INIT && data->mac_id != req->mac_id) {
372 wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during "
373 "authentication (was 0x%d, is 0x%d)",
374 data->mac_id, req->mac_id);
375 ret->ignore = TRUE;
376 return NULL;
379 if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) {
380 wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during "
381 "authentication (was 0x%d, is 0x%d)",
382 data->dh_group_id, req->dh_group_id);
383 ret->ignore = TRUE;
384 return NULL;
387 if (data->state != PAX_INIT &&
388 data->public_key_id != req->public_key_id) {
389 wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during "
390 "authentication (was 0x%d, is 0x%d)",
391 data->public_key_id, req->public_key_id);
392 ret->ignore = TRUE;
393 return NULL;
396 /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */
397 if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) {
398 wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x",
399 req->mac_id);
400 ret->ignore = TRUE;
401 return NULL;
404 if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
405 wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x",
406 req->dh_group_id);
407 ret->ignore = TRUE;
408 return NULL;
411 if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
412 wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x",
413 req->public_key_id);
414 ret->ignore = TRUE;
415 return NULL;
418 if (req->flags & EAP_PAX_FLAGS_MF) {
419 /* TODO: add support for reassembling fragments */
420 wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - "
421 "ignored packet");
422 ret->ignore = TRUE;
423 return NULL;
426 icv = pos + len - EAP_PAX_ICV_LEN;
427 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
428 if (req->op_code == EAP_PAX_OP_STD_1) {
429 eap_pax_mac(req->mac_id, (u8 *) "", 0,
430 reqData, flen, NULL, 0, NULL, 0, icvbuf);
431 } else {
432 eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
433 reqData, flen, NULL, 0, NULL, 0, icvbuf);
435 if (memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) {
436 wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the "
437 "message");
438 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV",
439 icvbuf, EAP_PAX_ICV_LEN);
440 ret->ignore = TRUE;
441 return NULL;
444 ret->ignore = FALSE;
445 ret->methodState = METHOD_MAY_CONT;
446 ret->decision = DECISION_FAIL;
447 ret->allowNotifications = TRUE;
449 switch (req->op_code) {
450 case EAP_PAX_OP_STD_1:
451 resp = eap_pax_process_std_1(sm, data, ret, reqData, flen,
452 respDataLen);
453 break;
454 case EAP_PAX_OP_STD_3:
455 resp = eap_pax_process_std_3(sm, data, ret, reqData, flen,
456 respDataLen);
457 break;
458 default:
459 wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown "
460 "op_code %d", req->op_code);
461 ret->ignore = TRUE;
462 return NULL;
465 if (ret->methodState == METHOD_DONE) {
466 ret->allowNotifications = FALSE;
469 return resp;
473 static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv)
475 struct eap_pax_data *data = priv;
476 return data->state == PAX_DONE;
480 static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
482 struct eap_pax_data *data = priv;
483 u8 *key;
485 if (data->state != PAX_DONE)
486 return NULL;
488 key = malloc(EAP_PAX_MSK_LEN);
489 if (key == NULL)
490 return NULL;
492 *len = EAP_PAX_MSK_LEN;
493 eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
494 "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
495 EAP_PAX_MSK_LEN, key);
497 return key;
501 const struct eap_method eap_method_pax =
503 .method = EAP_TYPE_PAX,
504 .name = "PAX",
505 .init = eap_pax_init,
506 .deinit = eap_pax_deinit,
507 .process = eap_pax_process,
508 .isKeyAvailable = eap_pax_isKeyAvailable,
509 .getKey = eap_pax_getKey,