2
* hostapd / EAP-PAX (RFC 4746) server
3
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.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
12
* See README and COPYING for more details.
18
#include "eap_server/eap_i.h"
19
#include "eap_common/eap_pax_common.h"
22
* Note: only PAX_STD subprotocol is currently supported
24
* TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
25
* (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
26
* recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
31
enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
34
u8 e[2 * EAP_PAX_RAND_LEN];
36
u8 x[EAP_PAX_RAND_LEN]; /* server rand */
37
u8 y[EAP_PAX_RAND_LEN]; /* client rand */
40
u8 ak[EAP_PAX_AK_LEN];
41
u8 mk[EAP_PAX_MK_LEN];
42
u8 ck[EAP_PAX_CK_LEN];
43
u8 ick[EAP_PAX_ICK_LEN];
50
static void * eap_pax_init(struct eap_sm *sm)
52
struct eap_pax_data *data;
54
data = os_zalloc(sizeof(*data));
57
data->state = PAX_STD_1;
59
* TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
62
data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
68
static void eap_pax_reset(struct eap_sm *sm, void *priv)
70
struct eap_pax_data *data = priv;
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;
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;
91
*reqDataLen = sizeof(*req) + 2 + EAP_PAX_RAND_LEN + EAP_PAX_ICV_LEN;
92
req = malloc(*reqDataLen);
94
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
96
data->state = FAILURE;
100
req->code = EAP_CODE_REQUEST;
101
req->identifier = id;
102
req->length = host_to_be16(*reqDataLen);
103
req->type = EAP_TYPE_PAX;
104
req->op_code = EAP_PAX_OP_STD_1;
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);
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;
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;
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);
139
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
141
data->state = FAILURE;
145
req->code = EAP_CODE_REQUEST;
146
req->identifier = id;
147
req->length = host_to_be16(*reqDataLen);
148
req->type = EAP_TYPE_PAX;
149
req->op_code = EAP_PAX_OP_STD_3;
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);
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
/* Optional ADE could be added here, if needed */
166
eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
167
(u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN,
168
NULL, 0, NULL, 0, pos);
169
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
170
pos += EAP_PAX_ICV_LEN;
176
static u8 * eap_pax_buildReq(struct eap_sm *sm, void *priv, int id,
179
struct eap_pax_data *data = priv;
181
switch (data->state) {
183
return eap_pax_build_std_1(sm, data, id, reqDataLen);
185
return eap_pax_build_std_3(sm, data, id, reqDataLen);
187
wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
195
static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
196
u8 *respData, size_t respDataLen)
198
struct eap_pax_data *data = priv;
199
struct eap_pax_hdr *resp;
201
u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
203
resp = (struct eap_pax_hdr *) respData;
204
if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PAX ||
205
(len = be_to_host16(resp->length)) > respDataLen ||
206
len < sizeof(*resp) + EAP_PAX_ICV_LEN) {
207
wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
211
wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
212
"flags 0x%x mac_id 0x%x dh_group_id 0x%x "
213
"public_key_id 0x%x",
214
resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
215
resp->public_key_id);
216
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
217
(u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
219
if (data->state == PAX_STD_1 &&
220
resp->op_code != EAP_PAX_OP_STD_2) {
221
wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
222
"ignore op %d", resp->op_code);
226
if (data->state == PAX_STD_3 &&
227
resp->op_code != EAP_PAX_OP_ACK) {
228
wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
229
"ignore op %d", resp->op_code);
233
if (resp->op_code != EAP_PAX_OP_STD_2 &&
234
resp->op_code != EAP_PAX_OP_ACK) {
235
wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
239
if (data->mac_id != resp->mac_id) {
240
wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
241
"received 0x%x", data->mac_id, resp->mac_id);
245
if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
246
wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
247
"received 0x%x", EAP_PAX_DH_GROUP_NONE,
252
if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
253
wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
254
"received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
255
resp->public_key_id);
259
if (resp->flags & EAP_PAX_FLAGS_MF) {
260
/* TODO: add support for reassembling fragments */
261
wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
265
if (resp->flags & EAP_PAX_FLAGS_CE) {
266
wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
270
if (data->keys_set) {
271
if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
272
wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
275
icv = respData + len - EAP_PAX_ICV_LEN;
276
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
277
eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
278
respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
280
if (memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
281
wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
282
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
283
icvbuf, EAP_PAX_ICV_LEN);
292
static void eap_pax_process_std_2(struct eap_sm *sm,
293
struct eap_pax_data *data,
294
u8 *respData, size_t respDataLen)
296
struct eap_pax_hdr *resp;
297
u8 *pos, mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
301
if (data->state != PAX_STD_1)
304
wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
306
resp = (struct eap_pax_hdr *) respData;
307
len = be_to_host16(resp->length);
308
pos = (u8 *) (resp + 1);
309
left = len - sizeof(*resp);
311
if (left < 2 + EAP_PAX_RAND_LEN ||
312
WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
313
wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
318
memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
319
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
320
data->rand.r.y, EAP_PAX_RAND_LEN);
321
pos += EAP_PAX_RAND_LEN;
322
left -= EAP_PAX_RAND_LEN;
324
if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) {
325
wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
328
data->cid_len = WPA_GET_BE16(pos);
330
data->cid = malloc(data->cid_len);
331
if (data->cid == NULL) {
332
wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
336
memcpy(data->cid, pos + 2, data->cid_len);
337
pos += 2 + data->cid_len;
338
left -= 2 + data->cid_len;
339
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
340
(u8 *) data->cid, data->cid_len);
342
if (left < 2 + EAP_PAX_MAC_LEN ||
343
WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
344
wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
349
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
350
pos, EAP_PAX_MAC_LEN);
352
if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
353
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
354
(u8 *) data->cid, data->cid_len);
355
data->state = FAILURE;
360
i < EAP_MAX_METHODS &&
361
(sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
362
sm->user->methods[i].method != EAP_TYPE_NONE);
364
if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
365
sm->user->methods[i].method == EAP_TYPE_PAX)
369
if (i >= EAP_MAX_METHODS ||
370
sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
371
sm->user->methods[i].method != EAP_TYPE_PAX) {
372
wpa_hexdump_ascii(MSG_DEBUG,
373
"EAP-PAX: EAP-PAX not enabled for CID",
374
(u8 *) data->cid, data->cid_len);
375
data->state = FAILURE;
379
if (sm->user->password == NULL ||
380
sm->user->password_len != EAP_PAX_AK_LEN) {
381
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
382
"user database for CID",
383
(u8 *) data->cid, data->cid_len);
384
data->state = FAILURE;
387
memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
389
if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
390
data->rand.e, data->mk, data->ck,
392
wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
394
data->state = FAILURE;
399
eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
400
data->rand.r.x, EAP_PAX_RAND_LEN,
401
data->rand.r.y, EAP_PAX_RAND_LEN,
402
(u8 *) data->cid, data->cid_len, mac);
403
if (memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) {
404
wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
406
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
407
mac, EAP_PAX_MAC_LEN);
408
data->state = FAILURE;
412
pos += EAP_PAX_MAC_LEN;
413
left -= EAP_PAX_MAC_LEN;
415
if (left < EAP_PAX_ICV_LEN) {
416
wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
417
"PAX_STD-2", (unsigned long) left);
420
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
421
eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
422
respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf);
423
if (memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
424
wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
425
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
426
icvbuf, EAP_PAX_ICV_LEN);
429
pos += EAP_PAX_ICV_LEN;
430
left -= EAP_PAX_ICV_LEN;
433
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
437
data->state = PAX_STD_3;
441
static void eap_pax_process_ack(struct eap_sm *sm,
442
struct eap_pax_data *data,
443
u8 *respData, size_t respDataLen)
445
if (data->state != PAX_STD_3)
448
wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
449
"completed successfully");
450
data->state = SUCCESS;
454
static void eap_pax_process(struct eap_sm *sm, void *priv,
455
u8 *respData, size_t respDataLen)
457
struct eap_pax_data *data = priv;
458
struct eap_pax_hdr *resp;
460
if (sm->user == NULL || sm->user->password == NULL) {
461
wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
463
data->state = FAILURE;
467
resp = (struct eap_pax_hdr *) respData;
469
switch (resp->op_code) {
470
case EAP_PAX_OP_STD_2:
471
eap_pax_process_std_2(sm, data, respData, respDataLen);
474
eap_pax_process_ack(sm, data, respData, respDataLen);
480
static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
482
struct eap_pax_data *data = priv;
483
return data->state == SUCCESS || data->state == FAILURE;
487
static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
489
struct eap_pax_data *data = priv;
492
if (data->state != SUCCESS)
495
key = malloc(EAP_MSK_LEN);
500
eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
501
"Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
508
static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
510
struct eap_pax_data *data = priv;
513
if (data->state != SUCCESS)
516
key = malloc(EAP_EMSK_LEN);
521
eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
522
"Extended Master Session Key",
523
data->rand.e, 2 * EAP_PAX_RAND_LEN,
530
static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
532
struct eap_pax_data *data = priv;
533
return data->state == SUCCESS;
537
int eap_server_pax_register(void)
539
struct eap_method *eap;
542
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
543
EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
547
eap->init = eap_pax_init;
548
eap->reset = eap_pax_reset;
549
eap->buildReq = eap_pax_buildReq;
550
eap->check = eap_pax_check;
551
eap->process = eap_pax_process;
552
eap->isDone = eap_pax_isDone;
553
eap->getKey = eap_pax_getKey;
554
eap->isSuccess = eap_pax_isSuccess;
555
eap->get_emsk = eap_pax_get_emsk;
557
ret = eap_server_method_register(eap);
559
eap_server_method_free(eap);