Import hostapd 0.5.8
[dragonfly/port-amd64.git] / contrib / hostapd-0.4.9 / eap_pax.c
blob2645ba58f21d92730c30e6b70140b91bfbcb5ffb
1 /*
2 * hostapd / EAP-PAX (draft-clancy-eap-pax-04.txt) server
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>
18 #include <netinet/in.h>
20 #include "hostapd.h"
21 #include "common.h"
22 #include "eap_i.h"
23 #include "eap_pax_common.h"
26 * Note: only PAX_STD subprotocol is currently supported
29 struct eap_pax_data {
30 enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
31 u8 mac_id;
32 union {
33 u8 e[2 * EAP_PAX_RAND_LEN];
34 struct {
35 u8 x[EAP_PAX_RAND_LEN]; /* server rand */
36 u8 y[EAP_PAX_RAND_LEN]; /* client rand */
37 } r;
38 } rand;
39 u8 ak[EAP_PAX_AK_LEN];
40 u8 mk[EAP_PAX_MK_LEN];
41 u8 ck[EAP_PAX_CK_LEN];
42 u8 ick[EAP_PAX_ICK_LEN];
43 int keys_set;
44 char *cid;
45 size_t cid_len;
49 static void * eap_pax_init(struct eap_sm *sm)
51 struct eap_pax_data *data;
53 data = malloc(sizeof(*data));
54 if (data == NULL)
55 return data;
56 memset(data, 0, sizeof(*data));
57 data->state = PAX_STD_1;
59 * TODO: make this configurable once EAP_PAX_MAC_AES_CBC_MAC_128 is
60 * supported
62 data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
64 return data;
68 static void eap_pax_reset(struct eap_sm *sm, void *priv)
70 struct eap_pax_data *data = priv;
71 free(data->cid);
72 free(data);
76 static u8 * eap_pax_build_std_1(struct eap_sm *sm,
77 struct eap_pax_data *data,
78 int id, size_t *reqDataLen)
80 struct eap_pax_hdr *req;
81 u8 *pos;
83 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
85 if (hostapd_get_rand(data->rand.r.x, EAP_PAX_RAND_LEN)) {
86 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
87 data->state = FAILURE;
88 return NULL;
91 *reqDataLen = sizeof(*req) + 2 + EAP_PAX_RAND_LEN + EAP_PAX_ICV_LEN;
92 req = malloc(*reqDataLen);
93 if (req == NULL) {
94 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
95 "request");
96 data->state = FAILURE;
97 return NULL;
100 req->code = EAP_CODE_REQUEST;
101 req->identifier = id;
102 req->length = htons(*reqDataLen);
103 req->type = EAP_TYPE_PAX;
104 req->op_code = EAP_PAX_OP_STD_1;
105 req->flags = 0;
106 req->mac_id = data->mac_id;
107 req->dh_group_id = EAP_PAX_DH_GROUP_NONE;
108 req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
109 pos = (u8 *) (req + 1);
110 *pos++ = 0;
111 *pos++ = EAP_PAX_RAND_LEN;
112 memcpy(pos, data->rand.r.x, EAP_PAX_RAND_LEN);
113 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
114 pos, EAP_PAX_RAND_LEN);
115 pos += EAP_PAX_RAND_LEN;
117 eap_pax_mac(data->mac_id, (u8 *) "", 0,
118 (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN,
119 NULL, 0, NULL, 0, pos);
120 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
121 pos += EAP_PAX_ICV_LEN;
123 return (u8 *) req;
127 static u8 * eap_pax_build_std_3(struct eap_sm *sm,
128 struct eap_pax_data *data,
129 int id, size_t *reqDataLen)
131 struct eap_pax_hdr *req;
132 u8 *pos;
134 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
136 *reqDataLen = sizeof(*req) + 2 + EAP_PAX_MAC_LEN + EAP_PAX_ICV_LEN;
137 req = malloc(*reqDataLen);
138 if (req == NULL) {
139 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
140 "request");
141 data->state = FAILURE;
142 return NULL;
145 req->code = EAP_CODE_REQUEST;
146 req->identifier = id;
147 req->length = htons(*reqDataLen);
148 req->type = EAP_TYPE_PAX;
149 req->op_code = EAP_PAX_OP_STD_3;
150 req->flags = 0;
151 req->mac_id = data->mac_id;
152 req->dh_group_id = EAP_PAX_DH_GROUP_NONE;
153 req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
154 pos = (u8 *) (req + 1);
155 *pos++ = 0;
156 *pos++ = EAP_PAX_MAC_LEN;
157 eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
158 data->rand.r.y, EAP_PAX_RAND_LEN,
159 (u8 *) data->cid, data->cid_len, NULL, 0, pos);
160 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
161 pos, EAP_PAX_MAC_LEN);
162 pos += EAP_PAX_MAC_LEN;
164 eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
165 (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN,
166 NULL, 0, NULL, 0, pos);
167 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
168 pos += EAP_PAX_ICV_LEN;
170 return (u8 *) req;
174 static u8 * eap_pax_buildReq(struct eap_sm *sm, void *priv, int id,
175 size_t *reqDataLen)
177 struct eap_pax_data *data = priv;
179 switch (data->state) {
180 case PAX_STD_1:
181 return eap_pax_build_std_1(sm, data, id, reqDataLen);
182 case PAX_STD_3:
183 return eap_pax_build_std_3(sm, data, id, reqDataLen);
184 default:
185 wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
186 data->state);
187 break;
189 return NULL;
193 static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
194 u8 *respData, size_t respDataLen)
196 struct eap_pax_data *data = priv;
197 struct eap_pax_hdr *resp;
198 size_t len;
199 u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
201 resp = (struct eap_pax_hdr *) respData;
202 if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PAX ||
203 (len = ntohs(resp->length)) > respDataLen ||
204 len < sizeof(*resp) + EAP_PAX_ICV_LEN) {
205 wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
206 return TRUE;
209 wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
210 "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
211 "public_key_id 0x%x",
212 resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
213 resp->public_key_id);
214 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
215 (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
217 if (data->state == PAX_STD_1 &&
218 resp->op_code != EAP_PAX_OP_STD_2) {
219 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
220 "ignore op %d", resp->op_code);
221 return TRUE;
224 if (data->state == PAX_STD_3 &&
225 resp->op_code != EAP_PAX_OP_ACK) {
226 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
227 "ignore op %d", resp->op_code);
228 return TRUE;
231 if (resp->op_code != EAP_PAX_OP_STD_2 &&
232 resp->op_code != EAP_PAX_OP_ACK) {
233 wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
234 resp->op_code);
237 if (data->mac_id != resp->mac_id) {
238 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
239 "received 0x%x", data->mac_id, resp->mac_id);
240 return TRUE;
243 if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
244 wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
245 "received 0x%x", EAP_PAX_DH_GROUP_NONE,
246 resp->dh_group_id);
247 return TRUE;
250 if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
251 wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
252 "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
253 resp->public_key_id);
254 return TRUE;
257 if (resp->flags & EAP_PAX_FLAGS_MF) {
258 /* TODO: add support for reassembling fragments */
259 wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
260 return TRUE;
263 if (resp->flags & EAP_PAX_FLAGS_CE) {
264 wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
265 return TRUE;
268 if (data->keys_set) {
269 if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
270 wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
271 return TRUE;
273 icv = respData + len - EAP_PAX_ICV_LEN;
274 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
275 eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
276 respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
277 icvbuf);
278 if (memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
279 wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
280 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
281 icvbuf, EAP_PAX_ICV_LEN);
282 return TRUE;
286 return FALSE;
290 static void eap_pax_process_std_2(struct eap_sm *sm,
291 struct eap_pax_data *data,
292 u8 *respData, size_t respDataLen)
294 struct eap_pax_hdr *resp;
295 u8 *pos, mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
296 size_t len, left;
297 int i;
299 if (data->state != PAX_STD_1)
300 return;
302 wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
304 resp = (struct eap_pax_hdr *) respData;
305 len = ntohs(resp->length);
306 pos = (u8 *) (resp + 1);
307 left = len - sizeof(*resp);
309 if (left < 2 + EAP_PAX_RAND_LEN ||
310 ((pos[0] << 8) | pos[1]) != EAP_PAX_RAND_LEN) {
311 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
312 return;
314 pos += 2;
315 left -= 2;
316 memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
317 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
318 data->rand.r.y, EAP_PAX_RAND_LEN);
319 pos += EAP_PAX_RAND_LEN;
320 left -= EAP_PAX_RAND_LEN;
322 if (left < 2 || 2 + ((pos[0] << 8) | pos[1]) > left) {
323 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
324 return;
326 data->cid_len = (pos[0] << 8) | pos[1];
327 free(data->cid);
328 data->cid = malloc(data->cid_len);
329 if (data->cid == NULL) {
330 wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
331 "CID");
332 return;
334 memcpy (data->cid, pos + 2, data->cid_len);
335 pos += 2 + data->cid_len;
336 left -= 2 + data->cid_len;
337 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
338 (u8 *) data->cid, data->cid_len);
340 if (left < 2 + EAP_PAX_MAC_LEN ||
341 ((pos[0] << 8) | pos[1]) != EAP_PAX_MAC_LEN) {
342 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
343 return;
345 pos += 2;
346 left -= 2;
347 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
348 pos, EAP_PAX_MAC_LEN);
350 if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
351 wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
352 (u8 *) data->cid, data->cid_len);
353 data->state = FAILURE;
354 return;
357 for (i = 0;
358 i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE;
359 i++) {
360 if (sm->user->methods[i] == EAP_TYPE_PAX)
361 break;
364 if (sm->user->methods[i] != EAP_TYPE_PAX) {
365 wpa_hexdump_ascii(MSG_DEBUG,
366 "EAP-PAX: EAP-PAX not enabled for CID",
367 (u8 *) data->cid, data->cid_len);
368 data->state = FAILURE;
369 return;
372 if (sm->user->password == NULL ||
373 sm->user->password_len != EAP_PAX_AK_LEN) {
374 wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
375 "user database for CID",
376 (u8 *) data->cid, data->cid_len);
377 data->state = FAILURE;
378 return;
380 memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
382 if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
383 data->rand.e, data->mk, data->ck,
384 data->ick) < 0) {
385 wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
386 "key derivation");
387 data->state = FAILURE;
388 return;
390 data->keys_set = 1;
392 eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
393 data->rand.r.x, EAP_PAX_RAND_LEN,
394 data->rand.r.y, EAP_PAX_RAND_LEN,
395 (u8 *) data->cid, data->cid_len, mac);
396 if (memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) {
397 wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
398 "PAX_STD-2");
399 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
400 mac, EAP_PAX_MAC_LEN);
401 data->state = FAILURE;
402 return;
405 pos += EAP_PAX_MAC_LEN;
406 left -= EAP_PAX_MAC_LEN;
408 if (left < EAP_PAX_ICV_LEN) {
409 wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
410 "PAX_STD-2", (unsigned long) left);
411 return;
413 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
414 eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
415 respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf);
416 if (memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
417 wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
418 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
419 icvbuf, EAP_PAX_ICV_LEN);
420 return;
422 pos += EAP_PAX_ICV_LEN;
423 left -= EAP_PAX_ICV_LEN;
425 if (left > 0) {
426 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
427 pos, left);
430 data->state = PAX_STD_3;
434 static void eap_pax_process_ack(struct eap_sm *sm,
435 struct eap_pax_data *data,
436 u8 *respData, size_t respDataLen)
438 if (data->state != PAX_STD_3)
439 return;
441 wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
442 "completed successfully");
443 data->state = SUCCESS;
447 static void eap_pax_process(struct eap_sm *sm, void *priv,
448 u8 *respData, size_t respDataLen)
450 struct eap_pax_data *data = priv;
451 struct eap_pax_hdr *resp;
453 if (sm->user == NULL || sm->user->password == NULL) {
454 wpa_printf(MSG_INFO, "EAP-PAX: Password not configured");
455 data->state = FAILURE;
456 return;
459 resp = (struct eap_pax_hdr *) respData;
461 switch (resp->op_code) {
462 case EAP_PAX_OP_STD_2:
463 eap_pax_process_std_2(sm, data, respData, respDataLen);
464 break;
465 case EAP_PAX_OP_ACK:
466 eap_pax_process_ack(sm, data, respData, respDataLen);
467 break;
472 static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
474 struct eap_pax_data *data = priv;
475 return data->state == SUCCESS || data->state == FAILURE;
479 static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
481 struct eap_pax_data *data = priv;
482 u8 *key;
484 if (data->state != SUCCESS)
485 return NULL;
487 key = malloc(EAP_PAX_MSK_LEN);
488 if (key == NULL)
489 return NULL;
491 *len = EAP_PAX_MSK_LEN;
492 eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
493 "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
494 EAP_PAX_MSK_LEN, key);
496 return key;
500 static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
502 struct eap_pax_data *data = priv;
503 return data->state == SUCCESS;
507 const struct eap_method eap_method_pax =
509 .method = EAP_TYPE_PAX,
510 .name = "PAX",
511 .init = eap_pax_init,
512 .reset = eap_pax_reset,
513 .buildReq = eap_pax_buildReq,
514 .check = eap_pax_check,
515 .process = eap_pax_process,
516 .isDone = eap_pax_isDone,
517 .getKey = eap_pax_getKey,
518 .isSuccess = eap_pax_isSuccess,