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 struct wpabuf
* eap_pax_build_std_1(struct eap_sm
*sm
,
77 struct eap_pax_data
*data
, u8 id
)
80 struct eap_pax_hdr
*pax
;
83 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-1 (sending)");
85 if (os_get_random(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 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_PAX
,
92 sizeof(*pax
) + 2 + EAP_PAX_RAND_LEN
+
93 EAP_PAX_ICV_LEN
, EAP_CODE_REQUEST
, id
);
95 wpa_printf(MSG_ERROR
, "EAP-PAX: Failed to allocate memory "
97 data
->state
= FAILURE
;
101 pax
= wpabuf_put(req
, sizeof(*pax
));
102 pax
->op_code
= EAP_PAX_OP_STD_1
;
104 pax
->mac_id
= data
->mac_id
;
105 pax
->dh_group_id
= EAP_PAX_DH_GROUP_NONE
;
106 pax
->public_key_id
= EAP_PAX_PUBLIC_KEY_NONE
;
108 wpabuf_put_be16(req
, EAP_PAX_RAND_LEN
);
109 wpabuf_put_data(req
, data
->rand
.r
.x
, EAP_PAX_RAND_LEN
);
110 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: A = X (server rand)",
111 data
->rand
.r
.x
, EAP_PAX_RAND_LEN
);
113 pos
= wpabuf_put(req
, EAP_PAX_MAC_LEN
);
114 eap_pax_mac(data
->mac_id
, (u8
*) "", 0,
115 wpabuf_mhead(req
), wpabuf_len(req
) - EAP_PAX_ICV_LEN
,
116 NULL
, 0, NULL
, 0, pos
);
117 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", pos
, EAP_PAX_ICV_LEN
);
123 static struct wpabuf
* eap_pax_build_std_3(struct eap_sm
*sm
,
124 struct eap_pax_data
*data
, u8 id
)
127 struct eap_pax_hdr
*pax
;
130 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-3 (sending)");
132 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_PAX
,
133 sizeof(*pax
) + 2 + EAP_PAX_MAC_LEN
+
134 EAP_PAX_ICV_LEN
, EAP_CODE_REQUEST
, id
);
136 wpa_printf(MSG_ERROR
, "EAP-PAX: Failed to allocate memory "
138 data
->state
= FAILURE
;
142 pax
= wpabuf_put(req
, sizeof(*pax
));
143 pax
->op_code
= EAP_PAX_OP_STD_3
;
145 pax
->mac_id
= data
->mac_id
;
146 pax
->dh_group_id
= EAP_PAX_DH_GROUP_NONE
;
147 pax
->public_key_id
= EAP_PAX_PUBLIC_KEY_NONE
;
149 wpabuf_put_be16(req
, EAP_PAX_MAC_LEN
);
150 pos
= wpabuf_put(req
, EAP_PAX_MAC_LEN
);
151 eap_pax_mac(data
->mac_id
, data
->ck
, EAP_PAX_CK_LEN
,
152 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
,
153 (u8
*) data
->cid
, data
->cid_len
, NULL
, 0, pos
);
154 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: MAC_CK(B, CID)",
155 pos
, EAP_PAX_MAC_LEN
);
156 pos
+= EAP_PAX_MAC_LEN
;
158 /* Optional ADE could be added here, if needed */
160 pos
= wpabuf_put(req
, EAP_PAX_MAC_LEN
);
161 eap_pax_mac(data
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
162 wpabuf_mhead(req
), wpabuf_len(req
) - EAP_PAX_ICV_LEN
,
163 NULL
, 0, NULL
, 0, pos
);
164 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", pos
, EAP_PAX_ICV_LEN
);
170 static struct wpabuf
* eap_pax_buildReq(struct eap_sm
*sm
, void *priv
, u8 id
)
172 struct eap_pax_data
*data
= priv
;
174 switch (data
->state
) {
176 return eap_pax_build_std_1(sm
, data
, id
);
178 return eap_pax_build_std_3(sm
, data
, id
);
180 wpa_printf(MSG_DEBUG
, "EAP-PAX: Unknown state %d in buildReq",
188 static Boolean
eap_pax_check(struct eap_sm
*sm
, void *priv
,
189 struct wpabuf
*respData
)
191 struct eap_pax_data
*data
= priv
;
192 struct eap_pax_hdr
*resp
;
195 u8 icvbuf
[EAP_PAX_ICV_LEN
], *icv
;
197 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_PAX
, respData
, &len
);
198 if (pos
== NULL
|| len
< sizeof(*resp
)) {
199 wpa_printf(MSG_INFO
, "EAP-PAX: Invalid frame");
203 mlen
= sizeof(struct eap_hdr
) + 1 + len
;
204 resp
= (struct eap_pax_hdr
*) pos
;
206 wpa_printf(MSG_DEBUG
, "EAP-PAX: received frame: op_code 0x%x "
207 "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
208 "public_key_id 0x%x",
209 resp
->op_code
, resp
->flags
, resp
->mac_id
, resp
->dh_group_id
,
210 resp
->public_key_id
);
211 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: received payload",
212 (u8
*) (resp
+ 1), len
- sizeof(*resp
) - EAP_PAX_ICV_LEN
);
214 if (data
->state
== PAX_STD_1
&&
215 resp
->op_code
!= EAP_PAX_OP_STD_2
) {
216 wpa_printf(MSG_DEBUG
, "EAP-PAX: Expected PAX_STD-2 - "
217 "ignore op %d", resp
->op_code
);
221 if (data
->state
== PAX_STD_3
&&
222 resp
->op_code
!= EAP_PAX_OP_ACK
) {
223 wpa_printf(MSG_DEBUG
, "EAP-PAX: Expected PAX-ACK - "
224 "ignore op %d", resp
->op_code
);
228 if (resp
->op_code
!= EAP_PAX_OP_STD_2
&&
229 resp
->op_code
!= EAP_PAX_OP_ACK
) {
230 wpa_printf(MSG_DEBUG
, "EAP-PAX: Unknown op_code 0x%x",
234 if (data
->mac_id
!= resp
->mac_id
) {
235 wpa_printf(MSG_DEBUG
, "EAP-PAX: Expected MAC ID 0x%x, "
236 "received 0x%x", data
->mac_id
, resp
->mac_id
);
240 if (resp
->dh_group_id
!= EAP_PAX_DH_GROUP_NONE
) {
241 wpa_printf(MSG_INFO
, "EAP-PAX: Expected DH Group ID 0x%x, "
242 "received 0x%x", EAP_PAX_DH_GROUP_NONE
,
247 if (resp
->public_key_id
!= EAP_PAX_PUBLIC_KEY_NONE
) {
248 wpa_printf(MSG_INFO
, "EAP-PAX: Expected Public Key ID 0x%x, "
249 "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE
,
250 resp
->public_key_id
);
254 if (resp
->flags
& EAP_PAX_FLAGS_MF
) {
255 /* TODO: add support for reassembling fragments */
256 wpa_printf(MSG_INFO
, "EAP-PAX: fragmentation not supported");
260 if (resp
->flags
& EAP_PAX_FLAGS_CE
) {
261 wpa_printf(MSG_INFO
, "EAP-PAX: Unexpected CE flag");
265 if (data
->keys_set
) {
266 if (len
- sizeof(*resp
) < EAP_PAX_ICV_LEN
) {
267 wpa_printf(MSG_INFO
, "EAP-PAX: No ICV in the packet");
270 icv
= wpabuf_mhead_u8(respData
) + mlen
- EAP_PAX_ICV_LEN
;
271 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", icv
, EAP_PAX_ICV_LEN
);
272 eap_pax_mac(data
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
273 wpabuf_mhead(respData
),
274 wpabuf_len(respData
) - EAP_PAX_ICV_LEN
,
275 NULL
, 0, NULL
, 0, icvbuf
);
276 if (os_memcmp(icvbuf
, icv
, EAP_PAX_ICV_LEN
) != 0) {
277 wpa_printf(MSG_INFO
, "EAP-PAX: Invalid ICV");
278 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: Expected ICV",
279 icvbuf
, EAP_PAX_ICV_LEN
);
288 static void eap_pax_process_std_2(struct eap_sm
*sm
,
289 struct eap_pax_data
*data
,
290 struct wpabuf
*respData
)
292 struct eap_pax_hdr
*resp
;
293 u8 mac
[EAP_PAX_MAC_LEN
], icvbuf
[EAP_PAX_ICV_LEN
];
298 if (data
->state
!= PAX_STD_1
)
301 wpa_printf(MSG_DEBUG
, "EAP-PAX: Received PAX_STD-2");
303 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_PAX
, respData
, &len
);
304 if (pos
== NULL
|| len
< sizeof(*resp
) + EAP_PAX_ICV_LEN
)
307 resp
= (struct eap_pax_hdr
*) pos
;
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 os_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
= os_malloc(data
->cid_len
);
331 if (data
->cid
== NULL
) {
332 wpa_printf(MSG_INFO
, "EAP-PAX: Failed to allocate memory for "
336 os_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 os_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 (os_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 wpabuf_head(respData
),
423 wpabuf_len(respData
) - EAP_PAX_ICV_LEN
, NULL
, 0, NULL
, 0,
425 if (os_memcmp(icvbuf
, pos
, EAP_PAX_ICV_LEN
) != 0) {
426 wpa_printf(MSG_INFO
, "EAP-PAX: Invalid ICV in PAX_STD-2");
427 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: Expected ICV",
428 icvbuf
, EAP_PAX_ICV_LEN
);
431 pos
+= EAP_PAX_ICV_LEN
;
432 left
-= EAP_PAX_ICV_LEN
;
435 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ignored extra payload",
439 data
->state
= PAX_STD_3
;
443 static void eap_pax_process_ack(struct eap_sm
*sm
,
444 struct eap_pax_data
*data
,
445 struct wpabuf
*respData
)
447 if (data
->state
!= PAX_STD_3
)
450 wpa_printf(MSG_DEBUG
, "EAP-PAX: Received PAX-ACK - authentication "
451 "completed successfully");
452 data
->state
= SUCCESS
;
456 static void eap_pax_process(struct eap_sm
*sm
, void *priv
,
457 struct wpabuf
*respData
)
459 struct eap_pax_data
*data
= priv
;
460 struct eap_pax_hdr
*resp
;
464 if (sm
->user
== NULL
|| sm
->user
->password
== NULL
) {
465 wpa_printf(MSG_INFO
, "EAP-PAX: Plaintext password not "
467 data
->state
= FAILURE
;
471 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_PAX
, respData
, &len
);
472 if (pos
== NULL
|| len
< sizeof(*resp
))
475 resp
= (struct eap_pax_hdr
*) pos
;
477 switch (resp
->op_code
) {
478 case EAP_PAX_OP_STD_2
:
479 eap_pax_process_std_2(sm
, data
, respData
);
482 eap_pax_process_ack(sm
, data
, respData
);
488 static Boolean
eap_pax_isDone(struct eap_sm
*sm
, void *priv
)
490 struct eap_pax_data
*data
= priv
;
491 return data
->state
== SUCCESS
|| data
->state
== FAILURE
;
495 static u8
* eap_pax_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
497 struct eap_pax_data
*data
= priv
;
500 if (data
->state
!= SUCCESS
)
503 key
= os_malloc(EAP_MSK_LEN
);
508 eap_pax_kdf(data
->mac_id
, data
->mk
, EAP_PAX_MK_LEN
,
509 "Master Session Key", data
->rand
.e
, 2 * EAP_PAX_RAND_LEN
,
516 static u8
* eap_pax_get_emsk(struct eap_sm
*sm
, void *priv
, size_t *len
)
518 struct eap_pax_data
*data
= priv
;
521 if (data
->state
!= SUCCESS
)
524 key
= os_malloc(EAP_EMSK_LEN
);
529 eap_pax_kdf(data
->mac_id
, data
->mk
, EAP_PAX_MK_LEN
,
530 "Extended Master Session Key",
531 data
->rand
.e
, 2 * EAP_PAX_RAND_LEN
,
538 static Boolean
eap_pax_isSuccess(struct eap_sm
*sm
, void *priv
)
540 struct eap_pax_data
*data
= priv
;
541 return data
->state
== SUCCESS
;
545 int eap_server_pax_register(void)
547 struct eap_method
*eap
;
550 eap
= eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION
,
551 EAP_VENDOR_IETF
, EAP_TYPE_PAX
, "PAX");
555 eap
->init
= eap_pax_init
;
556 eap
->reset
= eap_pax_reset
;
557 eap
->buildReq
= eap_pax_buildReq
;
558 eap
->check
= eap_pax_check
;
559 eap
->process
= eap_pax_process
;
560 eap
->isDone
= eap_pax_isDone
;
561 eap
->getKey
= eap_pax_getKey
;
562 eap
->isSuccess
= eap_pax_isSuccess
;
563 eap
->get_emsk
= eap_pax_get_emsk
;
565 ret
= eap_server_method_register(eap
);
567 eap_server_method_free(eap
);