xref: /dflybsd-src/contrib/wpa_supplicant/src/eap_server/eap_server_pax.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
13ff40c12SJohn Marino /*
23ff40c12SJohn Marino  * hostapd / EAP-PAX (RFC 4746) server
33ff40c12SJohn Marino  * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
43ff40c12SJohn Marino  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
73ff40c12SJohn Marino  */
83ff40c12SJohn Marino 
93ff40c12SJohn Marino #include "includes.h"
103ff40c12SJohn Marino 
113ff40c12SJohn Marino #include "common.h"
123ff40c12SJohn Marino #include "crypto/random.h"
133ff40c12SJohn Marino #include "eap_server/eap_i.h"
143ff40c12SJohn Marino #include "eap_common/eap_pax_common.h"
153ff40c12SJohn Marino 
163ff40c12SJohn Marino /*
173ff40c12SJohn Marino  * Note: only PAX_STD subprotocol is currently supported
183ff40c12SJohn Marino  *
193ff40c12SJohn Marino  * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
203ff40c12SJohn Marino  * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
213ff40c12SJohn Marino  * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
223ff40c12SJohn Marino  * RSAES-OAEP).
233ff40c12SJohn Marino  */
243ff40c12SJohn Marino 
253ff40c12SJohn Marino struct eap_pax_data {
263ff40c12SJohn Marino 	enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
273ff40c12SJohn Marino 	u8 mac_id;
283ff40c12SJohn Marino 	union {
293ff40c12SJohn Marino 		u8 e[2 * EAP_PAX_RAND_LEN];
303ff40c12SJohn Marino 		struct {
313ff40c12SJohn Marino 			u8 x[EAP_PAX_RAND_LEN]; /* server rand */
323ff40c12SJohn Marino 			u8 y[EAP_PAX_RAND_LEN]; /* client rand */
333ff40c12SJohn Marino 		} r;
343ff40c12SJohn Marino 	} rand;
353ff40c12SJohn Marino 	u8 ak[EAP_PAX_AK_LEN];
363ff40c12SJohn Marino 	u8 mk[EAP_PAX_MK_LEN];
373ff40c12SJohn Marino 	u8 ck[EAP_PAX_CK_LEN];
383ff40c12SJohn Marino 	u8 ick[EAP_PAX_ICK_LEN];
39*a1157835SDaniel Fojt 	u8 mid[EAP_PAX_MID_LEN];
403ff40c12SJohn Marino 	int keys_set;
413ff40c12SJohn Marino 	char *cid;
423ff40c12SJohn Marino 	size_t cid_len;
433ff40c12SJohn Marino };
443ff40c12SJohn Marino 
453ff40c12SJohn Marino 
eap_pax_init(struct eap_sm * sm)463ff40c12SJohn Marino static void * eap_pax_init(struct eap_sm *sm)
473ff40c12SJohn Marino {
483ff40c12SJohn Marino 	struct eap_pax_data *data;
493ff40c12SJohn Marino 
503ff40c12SJohn Marino 	data = os_zalloc(sizeof(*data));
513ff40c12SJohn Marino 	if (data == NULL)
523ff40c12SJohn Marino 		return NULL;
533ff40c12SJohn Marino 	data->state = PAX_STD_1;
543ff40c12SJohn Marino 	/*
553ff40c12SJohn Marino 	 * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
563ff40c12SJohn Marino 	 * supported
573ff40c12SJohn Marino 	 */
583ff40c12SJohn Marino 	data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
593ff40c12SJohn Marino 
603ff40c12SJohn Marino 	return data;
613ff40c12SJohn Marino }
623ff40c12SJohn Marino 
633ff40c12SJohn Marino 
eap_pax_reset(struct eap_sm * sm,void * priv)643ff40c12SJohn Marino static void eap_pax_reset(struct eap_sm *sm, void *priv)
653ff40c12SJohn Marino {
663ff40c12SJohn Marino 	struct eap_pax_data *data = priv;
673ff40c12SJohn Marino 	os_free(data->cid);
68*a1157835SDaniel Fojt 	bin_clear_free(data, sizeof(*data));
693ff40c12SJohn Marino }
703ff40c12SJohn Marino 
713ff40c12SJohn Marino 
eap_pax_build_std_1(struct eap_sm * sm,struct eap_pax_data * data,u8 id)723ff40c12SJohn Marino static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
733ff40c12SJohn Marino 					   struct eap_pax_data *data, u8 id)
743ff40c12SJohn Marino {
753ff40c12SJohn Marino 	struct wpabuf *req;
763ff40c12SJohn Marino 	struct eap_pax_hdr *pax;
773ff40c12SJohn Marino 	u8 *pos;
783ff40c12SJohn Marino 
793ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
803ff40c12SJohn Marino 
813ff40c12SJohn Marino 	if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) {
823ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
833ff40c12SJohn Marino 		data->state = FAILURE;
843ff40c12SJohn Marino 		return NULL;
853ff40c12SJohn Marino 	}
863ff40c12SJohn Marino 
873ff40c12SJohn Marino 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
883ff40c12SJohn Marino 			    sizeof(*pax) + 2 + EAP_PAX_RAND_LEN +
893ff40c12SJohn Marino 			    EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
903ff40c12SJohn Marino 	if (req == NULL) {
913ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
923ff40c12SJohn Marino 			   "request");
933ff40c12SJohn Marino 		data->state = FAILURE;
943ff40c12SJohn Marino 		return NULL;
953ff40c12SJohn Marino 	}
963ff40c12SJohn Marino 
973ff40c12SJohn Marino 	pax = wpabuf_put(req, sizeof(*pax));
983ff40c12SJohn Marino 	pax->op_code = EAP_PAX_OP_STD_1;
993ff40c12SJohn Marino 	pax->flags = 0;
1003ff40c12SJohn Marino 	pax->mac_id = data->mac_id;
1013ff40c12SJohn Marino 	pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
1023ff40c12SJohn Marino 	pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
1033ff40c12SJohn Marino 
1043ff40c12SJohn Marino 	wpabuf_put_be16(req, EAP_PAX_RAND_LEN);
1053ff40c12SJohn Marino 	wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN);
1063ff40c12SJohn Marino 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
1073ff40c12SJohn Marino 		    data->rand.r.x, EAP_PAX_RAND_LEN);
1083ff40c12SJohn Marino 
1093ff40c12SJohn Marino 	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
110*a1157835SDaniel Fojt 	if (eap_pax_mac(data->mac_id, (u8 *) "", 0,
1113ff40c12SJohn Marino 			wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
112*a1157835SDaniel Fojt 			NULL, 0, NULL, 0, pos) < 0) {
113*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
114*a1157835SDaniel Fojt 		data->state = FAILURE;
115*a1157835SDaniel Fojt 		wpabuf_free(req);
116*a1157835SDaniel Fojt 		return NULL;
117*a1157835SDaniel Fojt 	}
1183ff40c12SJohn Marino 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
1193ff40c12SJohn Marino 
1203ff40c12SJohn Marino 	return req;
1213ff40c12SJohn Marino }
1223ff40c12SJohn Marino 
1233ff40c12SJohn Marino 
eap_pax_build_std_3(struct eap_sm * sm,struct eap_pax_data * data,u8 id)1243ff40c12SJohn Marino static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
1253ff40c12SJohn Marino 					   struct eap_pax_data *data, u8 id)
1263ff40c12SJohn Marino {
1273ff40c12SJohn Marino 	struct wpabuf *req;
1283ff40c12SJohn Marino 	struct eap_pax_hdr *pax;
1293ff40c12SJohn Marino 	u8 *pos;
1303ff40c12SJohn Marino 
1313ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
1323ff40c12SJohn Marino 
1333ff40c12SJohn Marino 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
1343ff40c12SJohn Marino 			    sizeof(*pax) + 2 + EAP_PAX_MAC_LEN +
1353ff40c12SJohn Marino 			    EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
1363ff40c12SJohn Marino 	if (req == NULL) {
1373ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
1383ff40c12SJohn Marino 			   "request");
1393ff40c12SJohn Marino 		data->state = FAILURE;
1403ff40c12SJohn Marino 		return NULL;
1413ff40c12SJohn Marino 	}
1423ff40c12SJohn Marino 
1433ff40c12SJohn Marino 	pax = wpabuf_put(req, sizeof(*pax));
1443ff40c12SJohn Marino 	pax->op_code = EAP_PAX_OP_STD_3;
1453ff40c12SJohn Marino 	pax->flags = 0;
1463ff40c12SJohn Marino 	pax->mac_id = data->mac_id;
1473ff40c12SJohn Marino 	pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
1483ff40c12SJohn Marino 	pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
1493ff40c12SJohn Marino 
1503ff40c12SJohn Marino 	wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
1513ff40c12SJohn Marino 	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
152*a1157835SDaniel Fojt 	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
1533ff40c12SJohn Marino 			data->rand.r.y, EAP_PAX_RAND_LEN,
154*a1157835SDaniel Fojt 			(u8 *) data->cid, data->cid_len, NULL, 0, pos) < 0) {
155*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC");
156*a1157835SDaniel Fojt 		data->state = FAILURE;
157*a1157835SDaniel Fojt 		wpabuf_free(req);
158*a1157835SDaniel Fojt 		return NULL;
159*a1157835SDaniel Fojt 	}
1603ff40c12SJohn Marino 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
1613ff40c12SJohn Marino 		    pos, EAP_PAX_MAC_LEN);
1623ff40c12SJohn Marino 
1633ff40c12SJohn Marino 	/* Optional ADE could be added here, if needed */
1643ff40c12SJohn Marino 
1653ff40c12SJohn Marino 	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
166*a1157835SDaniel Fojt 	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
1673ff40c12SJohn Marino 			wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
168*a1157835SDaniel Fojt 			NULL, 0, NULL, 0, pos) < 0) {
169*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
170*a1157835SDaniel Fojt 		data->state = FAILURE;
171*a1157835SDaniel Fojt 		wpabuf_free(req);
172*a1157835SDaniel Fojt 		return NULL;
173*a1157835SDaniel Fojt 	}
1743ff40c12SJohn Marino 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
1753ff40c12SJohn Marino 
1763ff40c12SJohn Marino 	return req;
1773ff40c12SJohn Marino }
1783ff40c12SJohn Marino 
1793ff40c12SJohn Marino 
eap_pax_buildReq(struct eap_sm * sm,void * priv,u8 id)1803ff40c12SJohn Marino static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id)
1813ff40c12SJohn Marino {
1823ff40c12SJohn Marino 	struct eap_pax_data *data = priv;
1833ff40c12SJohn Marino 
1843ff40c12SJohn Marino 	switch (data->state) {
1853ff40c12SJohn Marino 	case PAX_STD_1:
1863ff40c12SJohn Marino 		return eap_pax_build_std_1(sm, data, id);
1873ff40c12SJohn Marino 	case PAX_STD_3:
1883ff40c12SJohn Marino 		return eap_pax_build_std_3(sm, data, id);
1893ff40c12SJohn Marino 	default:
1903ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
1913ff40c12SJohn Marino 			   data->state);
1923ff40c12SJohn Marino 		break;
1933ff40c12SJohn Marino 	}
1943ff40c12SJohn Marino 	return NULL;
1953ff40c12SJohn Marino }
1963ff40c12SJohn Marino 
1973ff40c12SJohn Marino 
eap_pax_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)1983ff40c12SJohn Marino static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
1993ff40c12SJohn Marino 			     struct wpabuf *respData)
2003ff40c12SJohn Marino {
2013ff40c12SJohn Marino 	struct eap_pax_data *data = priv;
2023ff40c12SJohn Marino 	struct eap_pax_hdr *resp;
2033ff40c12SJohn Marino 	const u8 *pos;
2043ff40c12SJohn Marino 	size_t len, mlen;
2053ff40c12SJohn Marino 	u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
2063ff40c12SJohn Marino 
2073ff40c12SJohn Marino 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
208*a1157835SDaniel Fojt 	if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) {
2093ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
2103ff40c12SJohn Marino 		return TRUE;
2113ff40c12SJohn Marino 	}
2123ff40c12SJohn Marino 
2133ff40c12SJohn Marino 	mlen = sizeof(struct eap_hdr) + 1 + len;
2143ff40c12SJohn Marino 	resp = (struct eap_pax_hdr *) pos;
2153ff40c12SJohn Marino 
2163ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
2173ff40c12SJohn Marino 		   "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
2183ff40c12SJohn Marino 		   "public_key_id 0x%x",
2193ff40c12SJohn Marino 		   resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
2203ff40c12SJohn Marino 		   resp->public_key_id);
2213ff40c12SJohn Marino 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
2223ff40c12SJohn Marino 		    (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
2233ff40c12SJohn Marino 
2243ff40c12SJohn Marino 	if (data->state == PAX_STD_1 &&
2253ff40c12SJohn Marino 	    resp->op_code != EAP_PAX_OP_STD_2) {
2263ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
2273ff40c12SJohn Marino 			   "ignore op %d", resp->op_code);
2283ff40c12SJohn Marino 		return TRUE;
2293ff40c12SJohn Marino 	}
2303ff40c12SJohn Marino 
2313ff40c12SJohn Marino 	if (data->state == PAX_STD_3 &&
2323ff40c12SJohn Marino 	    resp->op_code != EAP_PAX_OP_ACK) {
2333ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
2343ff40c12SJohn Marino 			   "ignore op %d", resp->op_code);
2353ff40c12SJohn Marino 		return TRUE;
2363ff40c12SJohn Marino 	}
2373ff40c12SJohn Marino 
2383ff40c12SJohn Marino 	if (resp->op_code != EAP_PAX_OP_STD_2 &&
2393ff40c12SJohn Marino 	    resp->op_code != EAP_PAX_OP_ACK) {
2403ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
2413ff40c12SJohn Marino 			   resp->op_code);
2423ff40c12SJohn Marino 	}
2433ff40c12SJohn Marino 
2443ff40c12SJohn Marino 	if (data->mac_id != resp->mac_id) {
2453ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
2463ff40c12SJohn Marino 			   "received 0x%x", data->mac_id, resp->mac_id);
2473ff40c12SJohn Marino 		return TRUE;
2483ff40c12SJohn Marino 	}
2493ff40c12SJohn Marino 
2503ff40c12SJohn Marino 	if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
2513ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
2523ff40c12SJohn Marino 			   "received 0x%x", EAP_PAX_DH_GROUP_NONE,
2533ff40c12SJohn Marino 			   resp->dh_group_id);
2543ff40c12SJohn Marino 		return TRUE;
2553ff40c12SJohn Marino 	}
2563ff40c12SJohn Marino 
2573ff40c12SJohn Marino 	if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
2583ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
2593ff40c12SJohn Marino 			   "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
2603ff40c12SJohn Marino 			   resp->public_key_id);
2613ff40c12SJohn Marino 		return TRUE;
2623ff40c12SJohn Marino 	}
2633ff40c12SJohn Marino 
2643ff40c12SJohn Marino 	if (resp->flags & EAP_PAX_FLAGS_MF) {
2653ff40c12SJohn Marino 		/* TODO: add support for reassembling fragments */
2663ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
2673ff40c12SJohn Marino 		return TRUE;
2683ff40c12SJohn Marino 	}
2693ff40c12SJohn Marino 
2703ff40c12SJohn Marino 	if (resp->flags & EAP_PAX_FLAGS_CE) {
2713ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
2723ff40c12SJohn Marino 		return TRUE;
2733ff40c12SJohn Marino 	}
2743ff40c12SJohn Marino 
2753ff40c12SJohn Marino 	if (data->keys_set) {
2763ff40c12SJohn Marino 		if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
2773ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
2783ff40c12SJohn Marino 			return TRUE;
2793ff40c12SJohn Marino 		}
2803ff40c12SJohn Marino 		icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
2813ff40c12SJohn Marino 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
282*a1157835SDaniel Fojt 		if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
2833ff40c12SJohn Marino 				wpabuf_mhead(respData),
2843ff40c12SJohn Marino 				wpabuf_len(respData) - EAP_PAX_ICV_LEN,
285*a1157835SDaniel Fojt 				NULL, 0, NULL, 0, icvbuf) < 0) {
286*a1157835SDaniel Fojt 			wpa_printf(MSG_INFO,
287*a1157835SDaniel Fojt 				   "EAP-PAX: Failed to calculate ICV");
288*a1157835SDaniel Fojt 			return TRUE;
289*a1157835SDaniel Fojt 		}
290*a1157835SDaniel Fojt 
291*a1157835SDaniel Fojt 		if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
2923ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
2933ff40c12SJohn Marino 			wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
2943ff40c12SJohn Marino 				    icvbuf, EAP_PAX_ICV_LEN);
2953ff40c12SJohn Marino 			return TRUE;
2963ff40c12SJohn Marino 		}
2973ff40c12SJohn Marino 	}
2983ff40c12SJohn Marino 
2993ff40c12SJohn Marino 	return FALSE;
3003ff40c12SJohn Marino }
3013ff40c12SJohn Marino 
3023ff40c12SJohn Marino 
eap_pax_process_std_2(struct eap_sm * sm,struct eap_pax_data * data,struct wpabuf * respData)3033ff40c12SJohn Marino static void eap_pax_process_std_2(struct eap_sm *sm,
3043ff40c12SJohn Marino 				  struct eap_pax_data *data,
3053ff40c12SJohn Marino 				  struct wpabuf *respData)
3063ff40c12SJohn Marino {
3073ff40c12SJohn Marino 	struct eap_pax_hdr *resp;
3083ff40c12SJohn Marino 	u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
3093ff40c12SJohn Marino 	const u8 *pos;
310*a1157835SDaniel Fojt 	size_t len, left, cid_len;
3113ff40c12SJohn Marino 	int i;
3123ff40c12SJohn Marino 
3133ff40c12SJohn Marino 	if (data->state != PAX_STD_1)
3143ff40c12SJohn Marino 		return;
3153ff40c12SJohn Marino 
3163ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
3173ff40c12SJohn Marino 
3183ff40c12SJohn Marino 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
3193ff40c12SJohn Marino 	if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN)
3203ff40c12SJohn Marino 		return;
3213ff40c12SJohn Marino 
3223ff40c12SJohn Marino 	resp = (struct eap_pax_hdr *) pos;
3233ff40c12SJohn Marino 	pos = (u8 *) (resp + 1);
3243ff40c12SJohn Marino 	left = len - sizeof(*resp);
3253ff40c12SJohn Marino 
3263ff40c12SJohn Marino 	if (left < 2 + EAP_PAX_RAND_LEN ||
3273ff40c12SJohn Marino 	    WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
3283ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
3293ff40c12SJohn Marino 		return;
3303ff40c12SJohn Marino 	}
3313ff40c12SJohn Marino 	pos += 2;
3323ff40c12SJohn Marino 	left -= 2;
3333ff40c12SJohn Marino 	os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
3343ff40c12SJohn Marino 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
3353ff40c12SJohn Marino 		    data->rand.r.y, EAP_PAX_RAND_LEN);
3363ff40c12SJohn Marino 	pos += EAP_PAX_RAND_LEN;
3373ff40c12SJohn Marino 	left -= EAP_PAX_RAND_LEN;
3383ff40c12SJohn Marino 
3393ff40c12SJohn Marino 	if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) {
3403ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
3413ff40c12SJohn Marino 		return;
3423ff40c12SJohn Marino 	}
343*a1157835SDaniel Fojt 	cid_len = WPA_GET_BE16(pos);
344*a1157835SDaniel Fojt 	if (cid_len > 1500) {
345*a1157835SDaniel Fojt 		wpa_printf(MSG_INFO, "EAP-PAX: Too long CID");
346*a1157835SDaniel Fojt 		return;
347*a1157835SDaniel Fojt 	}
348*a1157835SDaniel Fojt 	data->cid_len = cid_len;
3493ff40c12SJohn Marino 	os_free(data->cid);
350*a1157835SDaniel Fojt 	data->cid = os_memdup(pos + 2, data->cid_len);
3513ff40c12SJohn Marino 	if (data->cid == NULL) {
3523ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
3533ff40c12SJohn Marino 			   "CID");
3543ff40c12SJohn Marino 		return;
3553ff40c12SJohn Marino 	}
3563ff40c12SJohn Marino 	pos += 2 + data->cid_len;
3573ff40c12SJohn Marino 	left -= 2 + data->cid_len;
3583ff40c12SJohn Marino 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
3593ff40c12SJohn Marino 			  (u8 *) data->cid, data->cid_len);
3603ff40c12SJohn Marino 
3613ff40c12SJohn Marino 	if (left < 2 + EAP_PAX_MAC_LEN ||
3623ff40c12SJohn Marino 	    WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
3633ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
3643ff40c12SJohn Marino 		return;
3653ff40c12SJohn Marino 	}
3663ff40c12SJohn Marino 	pos += 2;
3673ff40c12SJohn Marino 	left -= 2;
3683ff40c12SJohn Marino 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
3693ff40c12SJohn Marino 		    pos, EAP_PAX_MAC_LEN);
3703ff40c12SJohn Marino 
3713ff40c12SJohn Marino 	if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
3723ff40c12SJohn Marino 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
3733ff40c12SJohn Marino 				  (u8 *) data->cid, data->cid_len);
3743ff40c12SJohn Marino 		data->state = FAILURE;
3753ff40c12SJohn Marino 		return;
3763ff40c12SJohn Marino 	}
3773ff40c12SJohn Marino 
3783ff40c12SJohn Marino 	for (i = 0;
3793ff40c12SJohn Marino 	     i < EAP_MAX_METHODS &&
3803ff40c12SJohn Marino 		     (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
3813ff40c12SJohn Marino 		      sm->user->methods[i].method != EAP_TYPE_NONE);
3823ff40c12SJohn Marino 	     i++) {
3833ff40c12SJohn Marino 		if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
3843ff40c12SJohn Marino 		    sm->user->methods[i].method == EAP_TYPE_PAX)
3853ff40c12SJohn Marino 			break;
3863ff40c12SJohn Marino 	}
3873ff40c12SJohn Marino 
3883ff40c12SJohn Marino 	if (i >= EAP_MAX_METHODS ||
3893ff40c12SJohn Marino 	    sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
3903ff40c12SJohn Marino 	    sm->user->methods[i].method != EAP_TYPE_PAX) {
3913ff40c12SJohn Marino 		wpa_hexdump_ascii(MSG_DEBUG,
3923ff40c12SJohn Marino 				  "EAP-PAX: EAP-PAX not enabled for CID",
3933ff40c12SJohn Marino 				  (u8 *) data->cid, data->cid_len);
3943ff40c12SJohn Marino 		data->state = FAILURE;
3953ff40c12SJohn Marino 		return;
3963ff40c12SJohn Marino 	}
3973ff40c12SJohn Marino 
3983ff40c12SJohn Marino 	if (sm->user->password == NULL ||
3993ff40c12SJohn Marino 	    sm->user->password_len != EAP_PAX_AK_LEN) {
4003ff40c12SJohn Marino 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
4013ff40c12SJohn Marino 				  "user database for CID",
4023ff40c12SJohn Marino 				  (u8 *) data->cid, data->cid_len);
4033ff40c12SJohn Marino 		data->state = FAILURE;
4043ff40c12SJohn Marino 		return;
4053ff40c12SJohn Marino 	}
4063ff40c12SJohn Marino 	os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
4073ff40c12SJohn Marino 
4083ff40c12SJohn Marino 	if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
4093ff40c12SJohn Marino 					   data->rand.e, data->mk, data->ck,
410*a1157835SDaniel Fojt 					   data->ick, data->mid) < 0) {
4113ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
4123ff40c12SJohn Marino 			   "key derivation");
4133ff40c12SJohn Marino 		data->state = FAILURE;
4143ff40c12SJohn Marino 		return;
4153ff40c12SJohn Marino 	}
4163ff40c12SJohn Marino 	data->keys_set = 1;
4173ff40c12SJohn Marino 
418*a1157835SDaniel Fojt 	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
4193ff40c12SJohn Marino 			data->rand.r.x, EAP_PAX_RAND_LEN,
4203ff40c12SJohn Marino 			data->rand.r.y, EAP_PAX_RAND_LEN,
421*a1157835SDaniel Fojt 			(u8 *) data->cid, data->cid_len, mac) < 0) {
422*a1157835SDaniel Fojt 		wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate MAC_CK");
423*a1157835SDaniel Fojt 		data->state = FAILURE;
424*a1157835SDaniel Fojt 		return;
425*a1157835SDaniel Fojt 	}
426*a1157835SDaniel Fojt 
427*a1157835SDaniel Fojt 	if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) {
4283ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
4293ff40c12SJohn Marino 			   "PAX_STD-2");
4303ff40c12SJohn Marino 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
4313ff40c12SJohn Marino 			    mac, EAP_PAX_MAC_LEN);
4323ff40c12SJohn Marino 		data->state = FAILURE;
4333ff40c12SJohn Marino 		return;
4343ff40c12SJohn Marino 	}
4353ff40c12SJohn Marino 
4363ff40c12SJohn Marino 	pos += EAP_PAX_MAC_LEN;
4373ff40c12SJohn Marino 	left -= EAP_PAX_MAC_LEN;
4383ff40c12SJohn Marino 
4393ff40c12SJohn Marino 	if (left < EAP_PAX_ICV_LEN) {
4403ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
4413ff40c12SJohn Marino 			   "PAX_STD-2", (unsigned long) left);
4423ff40c12SJohn Marino 		return;
4433ff40c12SJohn Marino 	}
4443ff40c12SJohn Marino 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
445*a1157835SDaniel Fojt 	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
4463ff40c12SJohn Marino 			wpabuf_head(respData),
447*a1157835SDaniel Fojt 			wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0,
448*a1157835SDaniel Fojt 			NULL, 0, icvbuf) < 0) {
449*a1157835SDaniel Fojt 		wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate ICV");
450*a1157835SDaniel Fojt 		return;
451*a1157835SDaniel Fojt 	}
452*a1157835SDaniel Fojt 
453*a1157835SDaniel Fojt 	if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
4543ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
4553ff40c12SJohn Marino 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
4563ff40c12SJohn Marino 			    icvbuf, EAP_PAX_ICV_LEN);
4573ff40c12SJohn Marino 		return;
4583ff40c12SJohn Marino 	}
4593ff40c12SJohn Marino 	pos += EAP_PAX_ICV_LEN;
4603ff40c12SJohn Marino 	left -= EAP_PAX_ICV_LEN;
4613ff40c12SJohn Marino 
4623ff40c12SJohn Marino 	if (left > 0) {
4633ff40c12SJohn Marino 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
4643ff40c12SJohn Marino 			    pos, left);
4653ff40c12SJohn Marino 	}
4663ff40c12SJohn Marino 
4673ff40c12SJohn Marino 	data->state = PAX_STD_3;
4683ff40c12SJohn Marino }
4693ff40c12SJohn Marino 
4703ff40c12SJohn Marino 
eap_pax_process_ack(struct eap_sm * sm,struct eap_pax_data * data,struct wpabuf * respData)4713ff40c12SJohn Marino static void eap_pax_process_ack(struct eap_sm *sm,
4723ff40c12SJohn Marino 				struct eap_pax_data *data,
4733ff40c12SJohn Marino 				struct wpabuf *respData)
4743ff40c12SJohn Marino {
4753ff40c12SJohn Marino 	if (data->state != PAX_STD_3)
4763ff40c12SJohn Marino 		return;
4773ff40c12SJohn Marino 
4783ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
4793ff40c12SJohn Marino 		   "completed successfully");
4803ff40c12SJohn Marino 	data->state = SUCCESS;
4813ff40c12SJohn Marino }
4823ff40c12SJohn Marino 
4833ff40c12SJohn Marino 
eap_pax_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)4843ff40c12SJohn Marino static void eap_pax_process(struct eap_sm *sm, void *priv,
4853ff40c12SJohn Marino 			    struct wpabuf *respData)
4863ff40c12SJohn Marino {
4873ff40c12SJohn Marino 	struct eap_pax_data *data = priv;
4883ff40c12SJohn Marino 	struct eap_pax_hdr *resp;
4893ff40c12SJohn Marino 	const u8 *pos;
4903ff40c12SJohn Marino 	size_t len;
4913ff40c12SJohn Marino 
4923ff40c12SJohn Marino 	if (sm->user == NULL || sm->user->password == NULL) {
4933ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
4943ff40c12SJohn Marino 			   "configured");
4953ff40c12SJohn Marino 		data->state = FAILURE;
4963ff40c12SJohn Marino 		return;
4973ff40c12SJohn Marino 	}
4983ff40c12SJohn Marino 
4993ff40c12SJohn Marino 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
5003ff40c12SJohn Marino 	if (pos == NULL || len < sizeof(*resp))
5013ff40c12SJohn Marino 		return;
5023ff40c12SJohn Marino 
5033ff40c12SJohn Marino 	resp = (struct eap_pax_hdr *) pos;
5043ff40c12SJohn Marino 
5053ff40c12SJohn Marino 	switch (resp->op_code) {
5063ff40c12SJohn Marino 	case EAP_PAX_OP_STD_2:
5073ff40c12SJohn Marino 		eap_pax_process_std_2(sm, data, respData);
5083ff40c12SJohn Marino 		break;
5093ff40c12SJohn Marino 	case EAP_PAX_OP_ACK:
5103ff40c12SJohn Marino 		eap_pax_process_ack(sm, data, respData);
5113ff40c12SJohn Marino 		break;
5123ff40c12SJohn Marino 	}
5133ff40c12SJohn Marino }
5143ff40c12SJohn Marino 
5153ff40c12SJohn Marino 
eap_pax_isDone(struct eap_sm * sm,void * priv)5163ff40c12SJohn Marino static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
5173ff40c12SJohn Marino {
5183ff40c12SJohn Marino 	struct eap_pax_data *data = priv;
5193ff40c12SJohn Marino 	return data->state == SUCCESS || data->state == FAILURE;
5203ff40c12SJohn Marino }
5213ff40c12SJohn Marino 
5223ff40c12SJohn Marino 
eap_pax_getKey(struct eap_sm * sm,void * priv,size_t * len)5233ff40c12SJohn Marino static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
5243ff40c12SJohn Marino {
5253ff40c12SJohn Marino 	struct eap_pax_data *data = priv;
5263ff40c12SJohn Marino 	u8 *key;
5273ff40c12SJohn Marino 
5283ff40c12SJohn Marino 	if (data->state != SUCCESS)
5293ff40c12SJohn Marino 		return NULL;
5303ff40c12SJohn Marino 
5313ff40c12SJohn Marino 	key = os_malloc(EAP_MSK_LEN);
5323ff40c12SJohn Marino 	if (key == NULL)
5333ff40c12SJohn Marino 		return NULL;
5343ff40c12SJohn Marino 
5353ff40c12SJohn Marino 	*len = EAP_MSK_LEN;
5363ff40c12SJohn Marino 	eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
5373ff40c12SJohn Marino 		    "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
5383ff40c12SJohn Marino 		    EAP_MSK_LEN, key);
5393ff40c12SJohn Marino 
5403ff40c12SJohn Marino 	return key;
5413ff40c12SJohn Marino }
5423ff40c12SJohn Marino 
5433ff40c12SJohn Marino 
eap_pax_get_emsk(struct eap_sm * sm,void * priv,size_t * len)5443ff40c12SJohn Marino static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
5453ff40c12SJohn Marino {
5463ff40c12SJohn Marino 	struct eap_pax_data *data = priv;
5473ff40c12SJohn Marino 	u8 *key;
5483ff40c12SJohn Marino 
5493ff40c12SJohn Marino 	if (data->state != SUCCESS)
5503ff40c12SJohn Marino 		return NULL;
5513ff40c12SJohn Marino 
5523ff40c12SJohn Marino 	key = os_malloc(EAP_EMSK_LEN);
5533ff40c12SJohn Marino 	if (key == NULL)
5543ff40c12SJohn Marino 		return NULL;
5553ff40c12SJohn Marino 
5563ff40c12SJohn Marino 	*len = EAP_EMSK_LEN;
5573ff40c12SJohn Marino 	eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
5583ff40c12SJohn Marino 		    "Extended Master Session Key",
5593ff40c12SJohn Marino 		    data->rand.e, 2 * EAP_PAX_RAND_LEN,
5603ff40c12SJohn Marino 		    EAP_EMSK_LEN, key);
5613ff40c12SJohn Marino 
5623ff40c12SJohn Marino 	return key;
5633ff40c12SJohn Marino }
5643ff40c12SJohn Marino 
5653ff40c12SJohn Marino 
eap_pax_isSuccess(struct eap_sm * sm,void * priv)5663ff40c12SJohn Marino static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
5673ff40c12SJohn Marino {
5683ff40c12SJohn Marino 	struct eap_pax_data *data = priv;
5693ff40c12SJohn Marino 	return data->state == SUCCESS;
5703ff40c12SJohn Marino }
5713ff40c12SJohn Marino 
5723ff40c12SJohn Marino 
eap_pax_get_session_id(struct eap_sm * sm,void * priv,size_t * len)573*a1157835SDaniel Fojt static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
574*a1157835SDaniel Fojt {
575*a1157835SDaniel Fojt 	struct eap_pax_data *data = priv;
576*a1157835SDaniel Fojt 	u8 *sid;
577*a1157835SDaniel Fojt 
578*a1157835SDaniel Fojt 	if (data->state != SUCCESS)
579*a1157835SDaniel Fojt 		return NULL;
580*a1157835SDaniel Fojt 
581*a1157835SDaniel Fojt 	sid = os_malloc(1 + EAP_PAX_MID_LEN);
582*a1157835SDaniel Fojt 	if (sid == NULL)
583*a1157835SDaniel Fojt 		return NULL;
584*a1157835SDaniel Fojt 
585*a1157835SDaniel Fojt 	*len = 1 + EAP_PAX_MID_LEN;
586*a1157835SDaniel Fojt 	sid[0] = EAP_TYPE_PAX;
587*a1157835SDaniel Fojt 	os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
588*a1157835SDaniel Fojt 
589*a1157835SDaniel Fojt 	return sid;
590*a1157835SDaniel Fojt }
591*a1157835SDaniel Fojt 
592*a1157835SDaniel Fojt 
eap_server_pax_register(void)5933ff40c12SJohn Marino int eap_server_pax_register(void)
5943ff40c12SJohn Marino {
5953ff40c12SJohn Marino 	struct eap_method *eap;
5963ff40c12SJohn Marino 
5973ff40c12SJohn Marino 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
5983ff40c12SJohn Marino 				      EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
5993ff40c12SJohn Marino 	if (eap == NULL)
6003ff40c12SJohn Marino 		return -1;
6013ff40c12SJohn Marino 
6023ff40c12SJohn Marino 	eap->init = eap_pax_init;
6033ff40c12SJohn Marino 	eap->reset = eap_pax_reset;
6043ff40c12SJohn Marino 	eap->buildReq = eap_pax_buildReq;
6053ff40c12SJohn Marino 	eap->check = eap_pax_check;
6063ff40c12SJohn Marino 	eap->process = eap_pax_process;
6073ff40c12SJohn Marino 	eap->isDone = eap_pax_isDone;
6083ff40c12SJohn Marino 	eap->getKey = eap_pax_getKey;
6093ff40c12SJohn Marino 	eap->isSuccess = eap_pax_isSuccess;
6103ff40c12SJohn Marino 	eap->get_emsk = eap_pax_get_emsk;
611*a1157835SDaniel Fojt 	eap->getSessionId = eap_pax_get_session_id;
6123ff40c12SJohn Marino 
613*a1157835SDaniel Fojt 	return eap_server_method_register(eap);
6143ff40c12SJohn Marino }
615