1214501Srpaulo/*
2214501Srpaulo * hostapd / EAP-PAX (RFC 4746) server
3214501Srpaulo * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "includes.h"
10214501Srpaulo
11214501Srpaulo#include "common.h"
12252726Srpaulo#include "crypto/random.h"
13214501Srpaulo#include "eap_server/eap_i.h"
14214501Srpaulo#include "eap_common/eap_pax_common.h"
15214501Srpaulo
16214501Srpaulo/*
17214501Srpaulo * Note: only PAX_STD subprotocol is currently supported
18214501Srpaulo *
19214501Srpaulo * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
20214501Srpaulo * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
21214501Srpaulo * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
22214501Srpaulo * RSAES-OAEP).
23214501Srpaulo */
24214501Srpaulo
25214501Srpaulostruct eap_pax_data {
26214501Srpaulo	enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
27214501Srpaulo	u8 mac_id;
28214501Srpaulo	union {
29214501Srpaulo		u8 e[2 * EAP_PAX_RAND_LEN];
30214501Srpaulo		struct {
31214501Srpaulo			u8 x[EAP_PAX_RAND_LEN]; /* server rand */
32214501Srpaulo			u8 y[EAP_PAX_RAND_LEN]; /* client rand */
33214501Srpaulo		} r;
34214501Srpaulo	} rand;
35214501Srpaulo	u8 ak[EAP_PAX_AK_LEN];
36214501Srpaulo	u8 mk[EAP_PAX_MK_LEN];
37214501Srpaulo	u8 ck[EAP_PAX_CK_LEN];
38214501Srpaulo	u8 ick[EAP_PAX_ICK_LEN];
39281806Srpaulo	u8 mid[EAP_PAX_MID_LEN];
40214501Srpaulo	int keys_set;
41214501Srpaulo	char *cid;
42214501Srpaulo	size_t cid_len;
43214501Srpaulo};
44214501Srpaulo
45214501Srpaulo
46214501Srpaulostatic void * eap_pax_init(struct eap_sm *sm)
47214501Srpaulo{
48214501Srpaulo	struct eap_pax_data *data;
49214501Srpaulo
50214501Srpaulo	data = os_zalloc(sizeof(*data));
51214501Srpaulo	if (data == NULL)
52214501Srpaulo		return NULL;
53214501Srpaulo	data->state = PAX_STD_1;
54214501Srpaulo	/*
55214501Srpaulo	 * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
56214501Srpaulo	 * supported
57214501Srpaulo	 */
58214501Srpaulo	data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
59214501Srpaulo
60214501Srpaulo	return data;
61214501Srpaulo}
62214501Srpaulo
63214501Srpaulo
64214501Srpaulostatic void eap_pax_reset(struct eap_sm *sm, void *priv)
65214501Srpaulo{
66214501Srpaulo	struct eap_pax_data *data = priv;
67214501Srpaulo	os_free(data->cid);
68281806Srpaulo	bin_clear_free(data, sizeof(*data));
69214501Srpaulo}
70214501Srpaulo
71214501Srpaulo
72214501Srpaulostatic struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
73214501Srpaulo					   struct eap_pax_data *data, u8 id)
74214501Srpaulo{
75214501Srpaulo	struct wpabuf *req;
76214501Srpaulo	struct eap_pax_hdr *pax;
77214501Srpaulo	u8 *pos;
78214501Srpaulo
79214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
80214501Srpaulo
81252726Srpaulo	if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) {
82214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
83214501Srpaulo		data->state = FAILURE;
84214501Srpaulo		return NULL;
85214501Srpaulo	}
86214501Srpaulo
87214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
88214501Srpaulo			    sizeof(*pax) + 2 + EAP_PAX_RAND_LEN +
89214501Srpaulo			    EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
90214501Srpaulo	if (req == NULL) {
91214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
92214501Srpaulo			   "request");
93214501Srpaulo		data->state = FAILURE;
94214501Srpaulo		return NULL;
95214501Srpaulo	}
96214501Srpaulo
97214501Srpaulo	pax = wpabuf_put(req, sizeof(*pax));
98214501Srpaulo	pax->op_code = EAP_PAX_OP_STD_1;
99214501Srpaulo	pax->flags = 0;
100214501Srpaulo	pax->mac_id = data->mac_id;
101214501Srpaulo	pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
102214501Srpaulo	pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
103214501Srpaulo
104214501Srpaulo	wpabuf_put_be16(req, EAP_PAX_RAND_LEN);
105214501Srpaulo	wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN);
106214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
107214501Srpaulo		    data->rand.r.x, EAP_PAX_RAND_LEN);
108214501Srpaulo
109214501Srpaulo	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
110346981Scy	if (eap_pax_mac(data->mac_id, (u8 *) "", 0,
111346981Scy			wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
112346981Scy			NULL, 0, NULL, 0, pos) < 0) {
113346981Scy		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
114346981Scy		data->state = FAILURE;
115346981Scy		wpabuf_free(req);
116346981Scy		return NULL;
117346981Scy	}
118214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
119214501Srpaulo
120214501Srpaulo	return req;
121214501Srpaulo}
122214501Srpaulo
123214501Srpaulo
124214501Srpaulostatic struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
125214501Srpaulo					   struct eap_pax_data *data, u8 id)
126214501Srpaulo{
127214501Srpaulo	struct wpabuf *req;
128214501Srpaulo	struct eap_pax_hdr *pax;
129214501Srpaulo	u8 *pos;
130214501Srpaulo
131214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
132214501Srpaulo
133214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
134214501Srpaulo			    sizeof(*pax) + 2 + EAP_PAX_MAC_LEN +
135214501Srpaulo			    EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
136214501Srpaulo	if (req == NULL) {
137214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
138214501Srpaulo			   "request");
139214501Srpaulo		data->state = FAILURE;
140214501Srpaulo		return NULL;
141214501Srpaulo	}
142214501Srpaulo
143214501Srpaulo	pax = wpabuf_put(req, sizeof(*pax));
144214501Srpaulo	pax->op_code = EAP_PAX_OP_STD_3;
145214501Srpaulo	pax->flags = 0;
146214501Srpaulo	pax->mac_id = data->mac_id;
147214501Srpaulo	pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
148214501Srpaulo	pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
149214501Srpaulo
150214501Srpaulo	wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
151214501Srpaulo	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
152346981Scy	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
153346981Scy			data->rand.r.y, EAP_PAX_RAND_LEN,
154346981Scy			(u8 *) data->cid, data->cid_len, NULL, 0, pos) < 0) {
155346981Scy		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC");
156346981Scy		data->state = FAILURE;
157346981Scy		wpabuf_free(req);
158346981Scy		return NULL;
159346981Scy	}
160214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
161214501Srpaulo		    pos, EAP_PAX_MAC_LEN);
162214501Srpaulo
163214501Srpaulo	/* Optional ADE could be added here, if needed */
164214501Srpaulo
165214501Srpaulo	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
166346981Scy	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
167346981Scy			wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
168346981Scy			NULL, 0, NULL, 0, pos) < 0) {
169346981Scy		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
170346981Scy		data->state = FAILURE;
171346981Scy		wpabuf_free(req);
172346981Scy		return NULL;
173346981Scy	}
174214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
175214501Srpaulo
176214501Srpaulo	return req;
177214501Srpaulo}
178214501Srpaulo
179214501Srpaulo
180214501Srpaulostatic struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id)
181214501Srpaulo{
182214501Srpaulo	struct eap_pax_data *data = priv;
183214501Srpaulo
184214501Srpaulo	switch (data->state) {
185214501Srpaulo	case PAX_STD_1:
186214501Srpaulo		return eap_pax_build_std_1(sm, data, id);
187214501Srpaulo	case PAX_STD_3:
188214501Srpaulo		return eap_pax_build_std_3(sm, data, id);
189214501Srpaulo	default:
190214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
191214501Srpaulo			   data->state);
192214501Srpaulo		break;
193214501Srpaulo	}
194214501Srpaulo	return NULL;
195214501Srpaulo}
196214501Srpaulo
197214501Srpaulo
198214501Srpaulostatic Boolean eap_pax_check(struct eap_sm *sm, void *priv,
199214501Srpaulo			     struct wpabuf *respData)
200214501Srpaulo{
201214501Srpaulo	struct eap_pax_data *data = priv;
202214501Srpaulo	struct eap_pax_hdr *resp;
203214501Srpaulo	const u8 *pos;
204214501Srpaulo	size_t len, mlen;
205214501Srpaulo	u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
206214501Srpaulo
207214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
208346981Scy	if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) {
209214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
210214501Srpaulo		return TRUE;
211214501Srpaulo	}
212214501Srpaulo
213214501Srpaulo	mlen = sizeof(struct eap_hdr) + 1 + len;
214214501Srpaulo	resp = (struct eap_pax_hdr *) pos;
215214501Srpaulo
216214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
217214501Srpaulo		   "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
218214501Srpaulo		   "public_key_id 0x%x",
219214501Srpaulo		   resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
220214501Srpaulo		   resp->public_key_id);
221214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
222214501Srpaulo		    (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
223214501Srpaulo
224214501Srpaulo	if (data->state == PAX_STD_1 &&
225214501Srpaulo	    resp->op_code != EAP_PAX_OP_STD_2) {
226214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
227214501Srpaulo			   "ignore op %d", resp->op_code);
228214501Srpaulo		return TRUE;
229214501Srpaulo	}
230214501Srpaulo
231214501Srpaulo	if (data->state == PAX_STD_3 &&
232214501Srpaulo	    resp->op_code != EAP_PAX_OP_ACK) {
233214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
234214501Srpaulo			   "ignore op %d", resp->op_code);
235214501Srpaulo		return TRUE;
236214501Srpaulo	}
237214501Srpaulo
238214501Srpaulo	if (resp->op_code != EAP_PAX_OP_STD_2 &&
239214501Srpaulo	    resp->op_code != EAP_PAX_OP_ACK) {
240214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
241214501Srpaulo			   resp->op_code);
242214501Srpaulo	}
243214501Srpaulo
244214501Srpaulo	if (data->mac_id != resp->mac_id) {
245214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
246214501Srpaulo			   "received 0x%x", data->mac_id, resp->mac_id);
247214501Srpaulo		return TRUE;
248214501Srpaulo	}
249214501Srpaulo
250214501Srpaulo	if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
251214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
252214501Srpaulo			   "received 0x%x", EAP_PAX_DH_GROUP_NONE,
253214501Srpaulo			   resp->dh_group_id);
254214501Srpaulo		return TRUE;
255214501Srpaulo	}
256214501Srpaulo
257214501Srpaulo	if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
258214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
259214501Srpaulo			   "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
260214501Srpaulo			   resp->public_key_id);
261214501Srpaulo		return TRUE;
262214501Srpaulo	}
263214501Srpaulo
264214501Srpaulo	if (resp->flags & EAP_PAX_FLAGS_MF) {
265214501Srpaulo		/* TODO: add support for reassembling fragments */
266214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
267214501Srpaulo		return TRUE;
268214501Srpaulo	}
269214501Srpaulo
270214501Srpaulo	if (resp->flags & EAP_PAX_FLAGS_CE) {
271214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
272214501Srpaulo		return TRUE;
273214501Srpaulo	}
274214501Srpaulo
275214501Srpaulo	if (data->keys_set) {
276214501Srpaulo		if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
277214501Srpaulo			wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
278214501Srpaulo			return TRUE;
279214501Srpaulo		}
280214501Srpaulo		icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
281214501Srpaulo		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
282346981Scy		if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
283346981Scy				wpabuf_mhead(respData),
284346981Scy				wpabuf_len(respData) - EAP_PAX_ICV_LEN,
285351611Scy				NULL, 0, NULL, 0, icvbuf) < 0) {
286351611Scy			wpa_printf(MSG_INFO,
287351611Scy				   "EAP-PAX: Failed to calculate ICV");
288351611Scy			return TRUE;
289351611Scy		}
290351611Scy
291351611Scy		if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
292214501Srpaulo			wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
293214501Srpaulo			wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
294214501Srpaulo				    icvbuf, EAP_PAX_ICV_LEN);
295214501Srpaulo			return TRUE;
296214501Srpaulo		}
297214501Srpaulo	}
298214501Srpaulo
299214501Srpaulo	return FALSE;
300214501Srpaulo}
301214501Srpaulo
302214501Srpaulo
303214501Srpaulostatic void eap_pax_process_std_2(struct eap_sm *sm,
304214501Srpaulo				  struct eap_pax_data *data,
305214501Srpaulo				  struct wpabuf *respData)
306214501Srpaulo{
307214501Srpaulo	struct eap_pax_hdr *resp;
308214501Srpaulo	u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
309214501Srpaulo	const u8 *pos;
310281806Srpaulo	size_t len, left, cid_len;
311214501Srpaulo	int i;
312214501Srpaulo
313214501Srpaulo	if (data->state != PAX_STD_1)
314214501Srpaulo		return;
315214501Srpaulo
316214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
317214501Srpaulo
318214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
319214501Srpaulo	if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN)
320214501Srpaulo		return;
321214501Srpaulo
322214501Srpaulo	resp = (struct eap_pax_hdr *) pos;
323214501Srpaulo	pos = (u8 *) (resp + 1);
324214501Srpaulo	left = len - sizeof(*resp);
325214501Srpaulo
326214501Srpaulo	if (left < 2 + EAP_PAX_RAND_LEN ||
327214501Srpaulo	    WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
328214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
329214501Srpaulo		return;
330214501Srpaulo	}
331214501Srpaulo	pos += 2;
332214501Srpaulo	left -= 2;
333214501Srpaulo	os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
334214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
335214501Srpaulo		    data->rand.r.y, EAP_PAX_RAND_LEN);
336214501Srpaulo	pos += EAP_PAX_RAND_LEN;
337214501Srpaulo	left -= EAP_PAX_RAND_LEN;
338214501Srpaulo
339214501Srpaulo	if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) {
340214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
341214501Srpaulo		return;
342214501Srpaulo	}
343281806Srpaulo	cid_len = WPA_GET_BE16(pos);
344281806Srpaulo	if (cid_len > 1500) {
345281806Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Too long CID");
346281806Srpaulo		return;
347281806Srpaulo	}
348281806Srpaulo	data->cid_len = cid_len;
349214501Srpaulo	os_free(data->cid);
350346981Scy	data->cid = os_memdup(pos + 2, data->cid_len);
351214501Srpaulo	if (data->cid == NULL) {
352214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
353214501Srpaulo			   "CID");
354214501Srpaulo		return;
355214501Srpaulo	}
356214501Srpaulo	pos += 2 + data->cid_len;
357214501Srpaulo	left -= 2 + data->cid_len;
358214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
359214501Srpaulo			  (u8 *) data->cid, data->cid_len);
360214501Srpaulo
361214501Srpaulo	if (left < 2 + EAP_PAX_MAC_LEN ||
362214501Srpaulo	    WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
363214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
364214501Srpaulo		return;
365214501Srpaulo	}
366214501Srpaulo	pos += 2;
367214501Srpaulo	left -= 2;
368214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
369214501Srpaulo		    pos, EAP_PAX_MAC_LEN);
370214501Srpaulo
371214501Srpaulo	if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
372214501Srpaulo		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
373214501Srpaulo				  (u8 *) data->cid, data->cid_len);
374214501Srpaulo		data->state = FAILURE;
375214501Srpaulo		return;
376214501Srpaulo	}
377214501Srpaulo
378214501Srpaulo	for (i = 0;
379214501Srpaulo	     i < EAP_MAX_METHODS &&
380214501Srpaulo		     (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
381214501Srpaulo		      sm->user->methods[i].method != EAP_TYPE_NONE);
382214501Srpaulo	     i++) {
383214501Srpaulo		if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
384214501Srpaulo		    sm->user->methods[i].method == EAP_TYPE_PAX)
385214501Srpaulo			break;
386214501Srpaulo	}
387214501Srpaulo
388214501Srpaulo	if (i >= EAP_MAX_METHODS ||
389214501Srpaulo	    sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
390214501Srpaulo	    sm->user->methods[i].method != EAP_TYPE_PAX) {
391214501Srpaulo		wpa_hexdump_ascii(MSG_DEBUG,
392214501Srpaulo				  "EAP-PAX: EAP-PAX not enabled for CID",
393214501Srpaulo				  (u8 *) data->cid, data->cid_len);
394214501Srpaulo		data->state = FAILURE;
395214501Srpaulo		return;
396214501Srpaulo	}
397214501Srpaulo
398214501Srpaulo	if (sm->user->password == NULL ||
399214501Srpaulo	    sm->user->password_len != EAP_PAX_AK_LEN) {
400214501Srpaulo		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
401214501Srpaulo				  "user database for CID",
402214501Srpaulo				  (u8 *) data->cid, data->cid_len);
403214501Srpaulo		data->state = FAILURE;
404214501Srpaulo		return;
405214501Srpaulo	}
406214501Srpaulo	os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
407214501Srpaulo
408214501Srpaulo	if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
409214501Srpaulo					   data->rand.e, data->mk, data->ck,
410281806Srpaulo					   data->ick, data->mid) < 0) {
411214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
412214501Srpaulo			   "key derivation");
413214501Srpaulo		data->state = FAILURE;
414214501Srpaulo		return;
415214501Srpaulo	}
416214501Srpaulo	data->keys_set = 1;
417214501Srpaulo
418346981Scy	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
419346981Scy			data->rand.r.x, EAP_PAX_RAND_LEN,
420346981Scy			data->rand.r.y, EAP_PAX_RAND_LEN,
421351611Scy			(u8 *) data->cid, data->cid_len, mac) < 0) {
422351611Scy		wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate MAC_CK");
423351611Scy		data->state = FAILURE;
424351611Scy		return;
425351611Scy	}
426351611Scy
427351611Scy	if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) {
428214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
429214501Srpaulo			   "PAX_STD-2");
430214501Srpaulo		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
431214501Srpaulo			    mac, EAP_PAX_MAC_LEN);
432214501Srpaulo		data->state = FAILURE;
433214501Srpaulo		return;
434214501Srpaulo	}
435214501Srpaulo
436214501Srpaulo	pos += EAP_PAX_MAC_LEN;
437214501Srpaulo	left -= EAP_PAX_MAC_LEN;
438214501Srpaulo
439214501Srpaulo	if (left < EAP_PAX_ICV_LEN) {
440214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
441214501Srpaulo			   "PAX_STD-2", (unsigned long) left);
442214501Srpaulo		return;
443214501Srpaulo	}
444214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
445346981Scy	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
446346981Scy			wpabuf_head(respData),
447346981Scy			wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0,
448351611Scy			NULL, 0, icvbuf) < 0) {
449351611Scy		wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate ICV");
450351611Scy		return;
451351611Scy	}
452351611Scy
453351611Scy	if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
454214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
455214501Srpaulo		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
456214501Srpaulo			    icvbuf, EAP_PAX_ICV_LEN);
457214501Srpaulo		return;
458214501Srpaulo	}
459214501Srpaulo	pos += EAP_PAX_ICV_LEN;
460214501Srpaulo	left -= EAP_PAX_ICV_LEN;
461214501Srpaulo
462214501Srpaulo	if (left > 0) {
463214501Srpaulo		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
464214501Srpaulo			    pos, left);
465214501Srpaulo	}
466214501Srpaulo
467214501Srpaulo	data->state = PAX_STD_3;
468214501Srpaulo}
469214501Srpaulo
470214501Srpaulo
471214501Srpaulostatic void eap_pax_process_ack(struct eap_sm *sm,
472214501Srpaulo				struct eap_pax_data *data,
473214501Srpaulo				struct wpabuf *respData)
474214501Srpaulo{
475214501Srpaulo	if (data->state != PAX_STD_3)
476214501Srpaulo		return;
477214501Srpaulo
478214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
479214501Srpaulo		   "completed successfully");
480214501Srpaulo	data->state = SUCCESS;
481214501Srpaulo}
482214501Srpaulo
483214501Srpaulo
484214501Srpaulostatic void eap_pax_process(struct eap_sm *sm, void *priv,
485214501Srpaulo			    struct wpabuf *respData)
486214501Srpaulo{
487214501Srpaulo	struct eap_pax_data *data = priv;
488214501Srpaulo	struct eap_pax_hdr *resp;
489214501Srpaulo	const u8 *pos;
490214501Srpaulo	size_t len;
491214501Srpaulo
492214501Srpaulo	if (sm->user == NULL || sm->user->password == NULL) {
493214501Srpaulo		wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
494214501Srpaulo			   "configured");
495214501Srpaulo		data->state = FAILURE;
496214501Srpaulo		return;
497214501Srpaulo	}
498214501Srpaulo
499214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
500214501Srpaulo	if (pos == NULL || len < sizeof(*resp))
501214501Srpaulo		return;
502214501Srpaulo
503214501Srpaulo	resp = (struct eap_pax_hdr *) pos;
504214501Srpaulo
505214501Srpaulo	switch (resp->op_code) {
506214501Srpaulo	case EAP_PAX_OP_STD_2:
507214501Srpaulo		eap_pax_process_std_2(sm, data, respData);
508214501Srpaulo		break;
509214501Srpaulo	case EAP_PAX_OP_ACK:
510214501Srpaulo		eap_pax_process_ack(sm, data, respData);
511214501Srpaulo		break;
512214501Srpaulo	}
513214501Srpaulo}
514214501Srpaulo
515214501Srpaulo
516214501Srpaulostatic Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
517214501Srpaulo{
518214501Srpaulo	struct eap_pax_data *data = priv;
519214501Srpaulo	return data->state == SUCCESS || data->state == FAILURE;
520214501Srpaulo}
521214501Srpaulo
522214501Srpaulo
523214501Srpaulostatic u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
524214501Srpaulo{
525214501Srpaulo	struct eap_pax_data *data = priv;
526214501Srpaulo	u8 *key;
527214501Srpaulo
528214501Srpaulo	if (data->state != SUCCESS)
529214501Srpaulo		return NULL;
530214501Srpaulo
531214501Srpaulo	key = os_malloc(EAP_MSK_LEN);
532214501Srpaulo	if (key == NULL)
533214501Srpaulo		return NULL;
534214501Srpaulo
535214501Srpaulo	*len = EAP_MSK_LEN;
536214501Srpaulo	eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
537214501Srpaulo		    "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
538214501Srpaulo		    EAP_MSK_LEN, key);
539214501Srpaulo
540214501Srpaulo	return key;
541214501Srpaulo}
542214501Srpaulo
543214501Srpaulo
544214501Srpaulostatic u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
545214501Srpaulo{
546214501Srpaulo	struct eap_pax_data *data = priv;
547214501Srpaulo	u8 *key;
548214501Srpaulo
549214501Srpaulo	if (data->state != SUCCESS)
550214501Srpaulo		return NULL;
551214501Srpaulo
552214501Srpaulo	key = os_malloc(EAP_EMSK_LEN);
553214501Srpaulo	if (key == NULL)
554214501Srpaulo		return NULL;
555214501Srpaulo
556214501Srpaulo	*len = EAP_EMSK_LEN;
557214501Srpaulo	eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
558214501Srpaulo		    "Extended Master Session Key",
559214501Srpaulo		    data->rand.e, 2 * EAP_PAX_RAND_LEN,
560214501Srpaulo		    EAP_EMSK_LEN, key);
561214501Srpaulo
562214501Srpaulo	return key;
563214501Srpaulo}
564214501Srpaulo
565214501Srpaulo
566214501Srpaulostatic Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
567214501Srpaulo{
568214501Srpaulo	struct eap_pax_data *data = priv;
569214501Srpaulo	return data->state == SUCCESS;
570214501Srpaulo}
571214501Srpaulo
572214501Srpaulo
573281806Srpaulostatic u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
574281806Srpaulo{
575281806Srpaulo	struct eap_pax_data *data = priv;
576281806Srpaulo	u8 *sid;
577281806Srpaulo
578281806Srpaulo	if (data->state != SUCCESS)
579281806Srpaulo		return NULL;
580281806Srpaulo
581281806Srpaulo	sid = os_malloc(1 + EAP_PAX_MID_LEN);
582281806Srpaulo	if (sid == NULL)
583281806Srpaulo		return NULL;
584281806Srpaulo
585281806Srpaulo	*len = 1 + EAP_PAX_MID_LEN;
586281806Srpaulo	sid[0] = EAP_TYPE_PAX;
587281806Srpaulo	os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
588281806Srpaulo
589281806Srpaulo	return sid;
590281806Srpaulo}
591281806Srpaulo
592281806Srpaulo
593214501Srpauloint eap_server_pax_register(void)
594214501Srpaulo{
595214501Srpaulo	struct eap_method *eap;
596214501Srpaulo
597214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
598214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
599214501Srpaulo	if (eap == NULL)
600214501Srpaulo		return -1;
601214501Srpaulo
602214501Srpaulo	eap->init = eap_pax_init;
603214501Srpaulo	eap->reset = eap_pax_reset;
604214501Srpaulo	eap->buildReq = eap_pax_buildReq;
605214501Srpaulo	eap->check = eap_pax_check;
606214501Srpaulo	eap->process = eap_pax_process;
607214501Srpaulo	eap->isDone = eap_pax_isDone;
608214501Srpaulo	eap->getKey = eap_pax_getKey;
609214501Srpaulo	eap->isSuccess = eap_pax_isSuccess;
610214501Srpaulo	eap->get_emsk = eap_pax_get_emsk;
611281806Srpaulo	eap->getSessionId = eap_pax_get_session_id;
612214501Srpaulo
613337817Scy	return eap_server_method_register(eap);
614214501Srpaulo}
615