1e28a4053SRui Paulo /*
2e28a4053SRui Paulo * hostapd / EAP-PAX (RFC 4746) server
3e28a4053SRui Paulo * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
7e28a4053SRui Paulo */
8e28a4053SRui Paulo
9e28a4053SRui Paulo #include "includes.h"
10e28a4053SRui Paulo
11e28a4053SRui Paulo #include "common.h"
12f05cddf9SRui Paulo #include "crypto/random.h"
13e28a4053SRui Paulo #include "eap_server/eap_i.h"
14e28a4053SRui Paulo #include "eap_common/eap_pax_common.h"
15e28a4053SRui Paulo
16e28a4053SRui Paulo /*
17e28a4053SRui Paulo * Note: only PAX_STD subprotocol is currently supported
18e28a4053SRui Paulo *
19e28a4053SRui Paulo * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
20e28a4053SRui Paulo * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
21e28a4053SRui Paulo * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
22e28a4053SRui Paulo * RSAES-OAEP).
23e28a4053SRui Paulo */
24e28a4053SRui Paulo
25e28a4053SRui Paulo struct eap_pax_data {
26e28a4053SRui Paulo enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
27e28a4053SRui Paulo u8 mac_id;
28e28a4053SRui Paulo union {
29e28a4053SRui Paulo u8 e[2 * EAP_PAX_RAND_LEN];
30e28a4053SRui Paulo struct {
31e28a4053SRui Paulo u8 x[EAP_PAX_RAND_LEN]; /* server rand */
32e28a4053SRui Paulo u8 y[EAP_PAX_RAND_LEN]; /* client rand */
33e28a4053SRui Paulo } r;
34e28a4053SRui Paulo } rand;
35e28a4053SRui Paulo u8 ak[EAP_PAX_AK_LEN];
36e28a4053SRui Paulo u8 mk[EAP_PAX_MK_LEN];
37e28a4053SRui Paulo u8 ck[EAP_PAX_CK_LEN];
38e28a4053SRui Paulo u8 ick[EAP_PAX_ICK_LEN];
395b9c547cSRui Paulo u8 mid[EAP_PAX_MID_LEN];
40e28a4053SRui Paulo int keys_set;
41e28a4053SRui Paulo char *cid;
42e28a4053SRui Paulo size_t cid_len;
43e28a4053SRui Paulo };
44e28a4053SRui Paulo
45e28a4053SRui Paulo
eap_pax_init(struct eap_sm * sm)46e28a4053SRui Paulo static void * eap_pax_init(struct eap_sm *sm)
47e28a4053SRui Paulo {
48e28a4053SRui Paulo struct eap_pax_data *data;
49e28a4053SRui Paulo
50e28a4053SRui Paulo data = os_zalloc(sizeof(*data));
51e28a4053SRui Paulo if (data == NULL)
52e28a4053SRui Paulo return NULL;
53e28a4053SRui Paulo data->state = PAX_STD_1;
54e28a4053SRui Paulo /*
55e28a4053SRui Paulo * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
56e28a4053SRui Paulo * supported
57e28a4053SRui Paulo */
58e28a4053SRui Paulo data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
59e28a4053SRui Paulo
60e28a4053SRui Paulo return data;
61e28a4053SRui Paulo }
62e28a4053SRui Paulo
63e28a4053SRui Paulo
eap_pax_reset(struct eap_sm * sm,void * priv)64e28a4053SRui Paulo static void eap_pax_reset(struct eap_sm *sm, void *priv)
65e28a4053SRui Paulo {
66e28a4053SRui Paulo struct eap_pax_data *data = priv;
67e28a4053SRui Paulo os_free(data->cid);
685b9c547cSRui Paulo bin_clear_free(data, sizeof(*data));
69e28a4053SRui Paulo }
70e28a4053SRui Paulo
71e28a4053SRui Paulo
eap_pax_build_std_1(struct eap_sm * sm,struct eap_pax_data * data,u8 id)72e28a4053SRui Paulo static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
73e28a4053SRui Paulo struct eap_pax_data *data, u8 id)
74e28a4053SRui Paulo {
75e28a4053SRui Paulo struct wpabuf *req;
76e28a4053SRui Paulo struct eap_pax_hdr *pax;
77e28a4053SRui Paulo u8 *pos;
78e28a4053SRui Paulo
79e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
80e28a4053SRui Paulo
81f05cddf9SRui Paulo if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) {
82e28a4053SRui Paulo wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
83e28a4053SRui Paulo data->state = FAILURE;
84e28a4053SRui Paulo return NULL;
85e28a4053SRui Paulo }
86e28a4053SRui Paulo
87e28a4053SRui Paulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
88e28a4053SRui Paulo sizeof(*pax) + 2 + EAP_PAX_RAND_LEN +
89e28a4053SRui Paulo EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
90e28a4053SRui Paulo if (req == NULL) {
91e28a4053SRui Paulo wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
92e28a4053SRui Paulo "request");
93e28a4053SRui Paulo data->state = FAILURE;
94e28a4053SRui Paulo return NULL;
95e28a4053SRui Paulo }
96e28a4053SRui Paulo
97e28a4053SRui Paulo pax = wpabuf_put(req, sizeof(*pax));
98e28a4053SRui Paulo pax->op_code = EAP_PAX_OP_STD_1;
99e28a4053SRui Paulo pax->flags = 0;
100e28a4053SRui Paulo pax->mac_id = data->mac_id;
101e28a4053SRui Paulo pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
102e28a4053SRui Paulo pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
103e28a4053SRui Paulo
104e28a4053SRui Paulo wpabuf_put_be16(req, EAP_PAX_RAND_LEN);
105e28a4053SRui Paulo wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN);
106e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
107e28a4053SRui Paulo data->rand.r.x, EAP_PAX_RAND_LEN);
108e28a4053SRui Paulo
109e28a4053SRui Paulo pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
1104bc52338SCy Schubert if (eap_pax_mac(data->mac_id, (u8 *) "", 0,
111e28a4053SRui Paulo wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
1124bc52338SCy Schubert NULL, 0, NULL, 0, pos) < 0) {
1134bc52338SCy Schubert wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
1144bc52338SCy Schubert data->state = FAILURE;
1154bc52338SCy Schubert wpabuf_free(req);
1164bc52338SCy Schubert return NULL;
1174bc52338SCy Schubert }
118e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
119e28a4053SRui Paulo
120e28a4053SRui Paulo return req;
121e28a4053SRui Paulo }
122e28a4053SRui Paulo
123e28a4053SRui Paulo
eap_pax_build_std_3(struct eap_sm * sm,struct eap_pax_data * data,u8 id)124e28a4053SRui Paulo static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
125e28a4053SRui Paulo struct eap_pax_data *data, u8 id)
126e28a4053SRui Paulo {
127e28a4053SRui Paulo struct wpabuf *req;
128e28a4053SRui Paulo struct eap_pax_hdr *pax;
129e28a4053SRui Paulo u8 *pos;
130e28a4053SRui Paulo
131e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
132e28a4053SRui Paulo
133e28a4053SRui Paulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
134e28a4053SRui Paulo sizeof(*pax) + 2 + EAP_PAX_MAC_LEN +
135e28a4053SRui Paulo EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
136e28a4053SRui Paulo if (req == NULL) {
137e28a4053SRui Paulo wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
138e28a4053SRui Paulo "request");
139e28a4053SRui Paulo data->state = FAILURE;
140e28a4053SRui Paulo return NULL;
141e28a4053SRui Paulo }
142e28a4053SRui Paulo
143e28a4053SRui Paulo pax = wpabuf_put(req, sizeof(*pax));
144e28a4053SRui Paulo pax->op_code = EAP_PAX_OP_STD_3;
145e28a4053SRui Paulo pax->flags = 0;
146e28a4053SRui Paulo pax->mac_id = data->mac_id;
147e28a4053SRui Paulo pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
148e28a4053SRui Paulo pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
149e28a4053SRui Paulo
150e28a4053SRui Paulo wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
151e28a4053SRui Paulo pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
1524bc52338SCy Schubert if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
153e28a4053SRui Paulo data->rand.r.y, EAP_PAX_RAND_LEN,
1544bc52338SCy Schubert (u8 *) data->cid, data->cid_len, NULL, 0, pos) < 0) {
1554bc52338SCy Schubert wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC");
1564bc52338SCy Schubert data->state = FAILURE;
1574bc52338SCy Schubert wpabuf_free(req);
1584bc52338SCy Schubert return NULL;
1594bc52338SCy Schubert }
160e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
161e28a4053SRui Paulo pos, EAP_PAX_MAC_LEN);
162e28a4053SRui Paulo
163e28a4053SRui Paulo /* Optional ADE could be added here, if needed */
164e28a4053SRui Paulo
165e28a4053SRui Paulo pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
1664bc52338SCy Schubert if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
167e28a4053SRui Paulo wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
1684bc52338SCy Schubert NULL, 0, NULL, 0, pos) < 0) {
1694bc52338SCy Schubert wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
1704bc52338SCy Schubert data->state = FAILURE;
1714bc52338SCy Schubert wpabuf_free(req);
1724bc52338SCy Schubert return NULL;
1734bc52338SCy Schubert }
174e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
175e28a4053SRui Paulo
176e28a4053SRui Paulo return req;
177e28a4053SRui Paulo }
178e28a4053SRui Paulo
179e28a4053SRui Paulo
eap_pax_buildReq(struct eap_sm * sm,void * priv,u8 id)180e28a4053SRui Paulo static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id)
181e28a4053SRui Paulo {
182e28a4053SRui Paulo struct eap_pax_data *data = priv;
183e28a4053SRui Paulo
184e28a4053SRui Paulo switch (data->state) {
185e28a4053SRui Paulo case PAX_STD_1:
186e28a4053SRui Paulo return eap_pax_build_std_1(sm, data, id);
187e28a4053SRui Paulo case PAX_STD_3:
188e28a4053SRui Paulo return eap_pax_build_std_3(sm, data, id);
189e28a4053SRui Paulo default:
190e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
191e28a4053SRui Paulo data->state);
192e28a4053SRui Paulo break;
193e28a4053SRui Paulo }
194e28a4053SRui Paulo return NULL;
195e28a4053SRui Paulo }
196e28a4053SRui Paulo
197e28a4053SRui Paulo
eap_pax_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)198*c1d255d3SCy Schubert static bool eap_pax_check(struct eap_sm *sm, void *priv,
199e28a4053SRui Paulo struct wpabuf *respData)
200e28a4053SRui Paulo {
201e28a4053SRui Paulo struct eap_pax_data *data = priv;
202e28a4053SRui Paulo struct eap_pax_hdr *resp;
203e28a4053SRui Paulo const u8 *pos;
204e28a4053SRui Paulo size_t len, mlen;
205e28a4053SRui Paulo u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
206e28a4053SRui Paulo
207e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
2084bc52338SCy Schubert if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) {
209e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
210*c1d255d3SCy Schubert return true;
211e28a4053SRui Paulo }
212e28a4053SRui Paulo
213e28a4053SRui Paulo mlen = sizeof(struct eap_hdr) + 1 + len;
214e28a4053SRui Paulo resp = (struct eap_pax_hdr *) pos;
215e28a4053SRui Paulo
216e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
217e28a4053SRui Paulo "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
218e28a4053SRui Paulo "public_key_id 0x%x",
219e28a4053SRui Paulo resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
220e28a4053SRui Paulo resp->public_key_id);
221e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
222e28a4053SRui Paulo (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
223e28a4053SRui Paulo
224e28a4053SRui Paulo if (data->state == PAX_STD_1 &&
225e28a4053SRui Paulo resp->op_code != EAP_PAX_OP_STD_2) {
226e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
227e28a4053SRui Paulo "ignore op %d", resp->op_code);
228*c1d255d3SCy Schubert return true;
229e28a4053SRui Paulo }
230e28a4053SRui Paulo
231e28a4053SRui Paulo if (data->state == PAX_STD_3 &&
232e28a4053SRui Paulo resp->op_code != EAP_PAX_OP_ACK) {
233e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
234e28a4053SRui Paulo "ignore op %d", resp->op_code);
235*c1d255d3SCy Schubert return true;
236e28a4053SRui Paulo }
237e28a4053SRui Paulo
238e28a4053SRui Paulo if (resp->op_code != EAP_PAX_OP_STD_2 &&
239e28a4053SRui Paulo resp->op_code != EAP_PAX_OP_ACK) {
240e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
241e28a4053SRui Paulo resp->op_code);
242e28a4053SRui Paulo }
243e28a4053SRui Paulo
244e28a4053SRui Paulo if (data->mac_id != resp->mac_id) {
245e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
246e28a4053SRui Paulo "received 0x%x", data->mac_id, resp->mac_id);
247*c1d255d3SCy Schubert return true;
248e28a4053SRui Paulo }
249e28a4053SRui Paulo
250e28a4053SRui Paulo if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
251e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
252e28a4053SRui Paulo "received 0x%x", EAP_PAX_DH_GROUP_NONE,
253e28a4053SRui Paulo resp->dh_group_id);
254*c1d255d3SCy Schubert return true;
255e28a4053SRui Paulo }
256e28a4053SRui Paulo
257e28a4053SRui Paulo if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
258e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
259e28a4053SRui Paulo "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
260e28a4053SRui Paulo resp->public_key_id);
261*c1d255d3SCy Schubert return true;
262e28a4053SRui Paulo }
263e28a4053SRui Paulo
264e28a4053SRui Paulo if (resp->flags & EAP_PAX_FLAGS_MF) {
265e28a4053SRui Paulo /* TODO: add support for reassembling fragments */
266e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
267*c1d255d3SCy Schubert return true;
268e28a4053SRui Paulo }
269e28a4053SRui Paulo
270e28a4053SRui Paulo if (resp->flags & EAP_PAX_FLAGS_CE) {
271e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
272*c1d255d3SCy Schubert return true;
273e28a4053SRui Paulo }
274e28a4053SRui Paulo
275e28a4053SRui Paulo if (data->keys_set) {
276e28a4053SRui Paulo if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
277e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
278*c1d255d3SCy Schubert return true;
279e28a4053SRui Paulo }
280e28a4053SRui Paulo icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
281e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
2824bc52338SCy Schubert if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
283e28a4053SRui Paulo wpabuf_mhead(respData),
284e28a4053SRui Paulo wpabuf_len(respData) - EAP_PAX_ICV_LEN,
285206b73d0SCy Schubert NULL, 0, NULL, 0, icvbuf) < 0) {
286206b73d0SCy Schubert wpa_printf(MSG_INFO,
287206b73d0SCy Schubert "EAP-PAX: Failed to calculate ICV");
288*c1d255d3SCy Schubert return true;
289206b73d0SCy Schubert }
290206b73d0SCy Schubert
291206b73d0SCy Schubert if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
292e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
293e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
294e28a4053SRui Paulo icvbuf, EAP_PAX_ICV_LEN);
295*c1d255d3SCy Schubert return true;
296e28a4053SRui Paulo }
297e28a4053SRui Paulo }
298e28a4053SRui Paulo
299*c1d255d3SCy Schubert return false;
300e28a4053SRui Paulo }
301e28a4053SRui Paulo
302e28a4053SRui Paulo
eap_pax_process_std_2(struct eap_sm * sm,struct eap_pax_data * data,struct wpabuf * respData)303e28a4053SRui Paulo static void eap_pax_process_std_2(struct eap_sm *sm,
304e28a4053SRui Paulo struct eap_pax_data *data,
305e28a4053SRui Paulo struct wpabuf *respData)
306e28a4053SRui Paulo {
307e28a4053SRui Paulo struct eap_pax_hdr *resp;
308e28a4053SRui Paulo u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
309e28a4053SRui Paulo const u8 *pos;
3105b9c547cSRui Paulo size_t len, left, cid_len;
311e28a4053SRui Paulo int i;
312e28a4053SRui Paulo
313e28a4053SRui Paulo if (data->state != PAX_STD_1)
314e28a4053SRui Paulo return;
315e28a4053SRui Paulo
316e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
317e28a4053SRui Paulo
318e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
319e28a4053SRui Paulo if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN)
320e28a4053SRui Paulo return;
321e28a4053SRui Paulo
322e28a4053SRui Paulo resp = (struct eap_pax_hdr *) pos;
323e28a4053SRui Paulo pos = (u8 *) (resp + 1);
324e28a4053SRui Paulo left = len - sizeof(*resp);
325e28a4053SRui Paulo
326e28a4053SRui Paulo if (left < 2 + EAP_PAX_RAND_LEN ||
327e28a4053SRui Paulo WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
328e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
329e28a4053SRui Paulo return;
330e28a4053SRui Paulo }
331e28a4053SRui Paulo pos += 2;
332e28a4053SRui Paulo left -= 2;
333e28a4053SRui Paulo os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
334e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
335e28a4053SRui Paulo data->rand.r.y, EAP_PAX_RAND_LEN);
336e28a4053SRui Paulo pos += EAP_PAX_RAND_LEN;
337e28a4053SRui Paulo left -= EAP_PAX_RAND_LEN;
338e28a4053SRui Paulo
339e28a4053SRui Paulo if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) {
340e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
341e28a4053SRui Paulo return;
342e28a4053SRui Paulo }
3435b9c547cSRui Paulo cid_len = WPA_GET_BE16(pos);
3445b9c547cSRui Paulo if (cid_len > 1500) {
3455b9c547cSRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Too long CID");
3465b9c547cSRui Paulo return;
3475b9c547cSRui Paulo }
3485b9c547cSRui Paulo data->cid_len = cid_len;
349e28a4053SRui Paulo os_free(data->cid);
35085732ac8SCy Schubert data->cid = os_memdup(pos + 2, data->cid_len);
351e28a4053SRui Paulo if (data->cid == NULL) {
352e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
353e28a4053SRui Paulo "CID");
354e28a4053SRui Paulo return;
355e28a4053SRui Paulo }
356e28a4053SRui Paulo pos += 2 + data->cid_len;
357e28a4053SRui Paulo left -= 2 + data->cid_len;
358e28a4053SRui Paulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
359e28a4053SRui Paulo (u8 *) data->cid, data->cid_len);
360e28a4053SRui Paulo
361e28a4053SRui Paulo if (left < 2 + EAP_PAX_MAC_LEN ||
362e28a4053SRui Paulo WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
363e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
364e28a4053SRui Paulo return;
365e28a4053SRui Paulo }
366e28a4053SRui Paulo pos += 2;
367e28a4053SRui Paulo left -= 2;
368e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
369e28a4053SRui Paulo pos, EAP_PAX_MAC_LEN);
370e28a4053SRui Paulo
371e28a4053SRui Paulo if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
372e28a4053SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
373e28a4053SRui Paulo (u8 *) data->cid, data->cid_len);
374e28a4053SRui Paulo data->state = FAILURE;
375e28a4053SRui Paulo return;
376e28a4053SRui Paulo }
377e28a4053SRui Paulo
378e28a4053SRui Paulo for (i = 0;
379e28a4053SRui Paulo i < EAP_MAX_METHODS &&
380e28a4053SRui Paulo (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
381e28a4053SRui Paulo sm->user->methods[i].method != EAP_TYPE_NONE);
382e28a4053SRui Paulo i++) {
383e28a4053SRui Paulo if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
384e28a4053SRui Paulo sm->user->methods[i].method == EAP_TYPE_PAX)
385e28a4053SRui Paulo break;
386e28a4053SRui Paulo }
387e28a4053SRui Paulo
388e28a4053SRui Paulo if (i >= EAP_MAX_METHODS ||
389e28a4053SRui Paulo sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
390e28a4053SRui Paulo sm->user->methods[i].method != EAP_TYPE_PAX) {
391e28a4053SRui Paulo wpa_hexdump_ascii(MSG_DEBUG,
392e28a4053SRui Paulo "EAP-PAX: EAP-PAX not enabled for CID",
393e28a4053SRui Paulo (u8 *) data->cid, data->cid_len);
394e28a4053SRui Paulo data->state = FAILURE;
395e28a4053SRui Paulo return;
396e28a4053SRui Paulo }
397e28a4053SRui Paulo
398e28a4053SRui Paulo if (sm->user->password == NULL ||
399e28a4053SRui Paulo sm->user->password_len != EAP_PAX_AK_LEN) {
400e28a4053SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
401e28a4053SRui Paulo "user database for CID",
402e28a4053SRui Paulo (u8 *) data->cid, data->cid_len);
403e28a4053SRui Paulo data->state = FAILURE;
404e28a4053SRui Paulo return;
405e28a4053SRui Paulo }
406e28a4053SRui Paulo os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
407e28a4053SRui Paulo
408e28a4053SRui Paulo if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
409e28a4053SRui Paulo data->rand.e, data->mk, data->ck,
4105b9c547cSRui Paulo data->ick, data->mid) < 0) {
411e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
412e28a4053SRui Paulo "key derivation");
413e28a4053SRui Paulo data->state = FAILURE;
414e28a4053SRui Paulo return;
415e28a4053SRui Paulo }
416e28a4053SRui Paulo data->keys_set = 1;
417e28a4053SRui Paulo
4184bc52338SCy Schubert if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
419e28a4053SRui Paulo data->rand.r.x, EAP_PAX_RAND_LEN,
420e28a4053SRui Paulo data->rand.r.y, EAP_PAX_RAND_LEN,
421206b73d0SCy Schubert (u8 *) data->cid, data->cid_len, mac) < 0) {
422206b73d0SCy Schubert wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate MAC_CK");
423206b73d0SCy Schubert data->state = FAILURE;
424206b73d0SCy Schubert return;
425206b73d0SCy Schubert }
426206b73d0SCy Schubert
427206b73d0SCy Schubert if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) {
428e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
429e28a4053SRui Paulo "PAX_STD-2");
430e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
431e28a4053SRui Paulo mac, EAP_PAX_MAC_LEN);
432e28a4053SRui Paulo data->state = FAILURE;
433e28a4053SRui Paulo return;
434e28a4053SRui Paulo }
435e28a4053SRui Paulo
436e28a4053SRui Paulo pos += EAP_PAX_MAC_LEN;
437e28a4053SRui Paulo left -= EAP_PAX_MAC_LEN;
438e28a4053SRui Paulo
439e28a4053SRui Paulo if (left < EAP_PAX_ICV_LEN) {
440e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
441e28a4053SRui Paulo "PAX_STD-2", (unsigned long) left);
442e28a4053SRui Paulo return;
443e28a4053SRui Paulo }
444e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
4454bc52338SCy Schubert if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
446e28a4053SRui Paulo wpabuf_head(respData),
4474bc52338SCy Schubert wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0,
448206b73d0SCy Schubert NULL, 0, icvbuf) < 0) {
449206b73d0SCy Schubert wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate ICV");
450206b73d0SCy Schubert return;
451206b73d0SCy Schubert }
452206b73d0SCy Schubert
453206b73d0SCy Schubert if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
454e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
455e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
456e28a4053SRui Paulo icvbuf, EAP_PAX_ICV_LEN);
457e28a4053SRui Paulo return;
458e28a4053SRui Paulo }
459e28a4053SRui Paulo pos += EAP_PAX_ICV_LEN;
460e28a4053SRui Paulo left -= EAP_PAX_ICV_LEN;
461e28a4053SRui Paulo
462e28a4053SRui Paulo if (left > 0) {
463e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
464e28a4053SRui Paulo pos, left);
465e28a4053SRui Paulo }
466e28a4053SRui Paulo
467e28a4053SRui Paulo data->state = PAX_STD_3;
468e28a4053SRui Paulo }
469e28a4053SRui Paulo
470e28a4053SRui Paulo
eap_pax_process_ack(struct eap_sm * sm,struct eap_pax_data * data,struct wpabuf * respData)471e28a4053SRui Paulo static void eap_pax_process_ack(struct eap_sm *sm,
472e28a4053SRui Paulo struct eap_pax_data *data,
473e28a4053SRui Paulo struct wpabuf *respData)
474e28a4053SRui Paulo {
475e28a4053SRui Paulo if (data->state != PAX_STD_3)
476e28a4053SRui Paulo return;
477e28a4053SRui Paulo
478e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
479e28a4053SRui Paulo "completed successfully");
480e28a4053SRui Paulo data->state = SUCCESS;
481e28a4053SRui Paulo }
482e28a4053SRui Paulo
483e28a4053SRui Paulo
eap_pax_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)484e28a4053SRui Paulo static void eap_pax_process(struct eap_sm *sm, void *priv,
485e28a4053SRui Paulo struct wpabuf *respData)
486e28a4053SRui Paulo {
487e28a4053SRui Paulo struct eap_pax_data *data = priv;
488e28a4053SRui Paulo struct eap_pax_hdr *resp;
489e28a4053SRui Paulo const u8 *pos;
490e28a4053SRui Paulo size_t len;
491e28a4053SRui Paulo
492e28a4053SRui Paulo if (sm->user == NULL || sm->user->password == NULL) {
493e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
494e28a4053SRui Paulo "configured");
495e28a4053SRui Paulo data->state = FAILURE;
496e28a4053SRui Paulo return;
497e28a4053SRui Paulo }
498e28a4053SRui Paulo
499e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
500e28a4053SRui Paulo if (pos == NULL || len < sizeof(*resp))
501e28a4053SRui Paulo return;
502e28a4053SRui Paulo
503e28a4053SRui Paulo resp = (struct eap_pax_hdr *) pos;
504e28a4053SRui Paulo
505e28a4053SRui Paulo switch (resp->op_code) {
506e28a4053SRui Paulo case EAP_PAX_OP_STD_2:
507e28a4053SRui Paulo eap_pax_process_std_2(sm, data, respData);
508e28a4053SRui Paulo break;
509e28a4053SRui Paulo case EAP_PAX_OP_ACK:
510e28a4053SRui Paulo eap_pax_process_ack(sm, data, respData);
511e28a4053SRui Paulo break;
512e28a4053SRui Paulo }
513e28a4053SRui Paulo }
514e28a4053SRui Paulo
515e28a4053SRui Paulo
eap_pax_isDone(struct eap_sm * sm,void * priv)516*c1d255d3SCy Schubert static bool eap_pax_isDone(struct eap_sm *sm, void *priv)
517e28a4053SRui Paulo {
518e28a4053SRui Paulo struct eap_pax_data *data = priv;
519e28a4053SRui Paulo return data->state == SUCCESS || data->state == FAILURE;
520e28a4053SRui Paulo }
521e28a4053SRui Paulo
522e28a4053SRui Paulo
eap_pax_getKey(struct eap_sm * sm,void * priv,size_t * len)523e28a4053SRui Paulo static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
524e28a4053SRui Paulo {
525e28a4053SRui Paulo struct eap_pax_data *data = priv;
526e28a4053SRui Paulo u8 *key;
527e28a4053SRui Paulo
528e28a4053SRui Paulo if (data->state != SUCCESS)
529e28a4053SRui Paulo return NULL;
530e28a4053SRui Paulo
531e28a4053SRui Paulo key = os_malloc(EAP_MSK_LEN);
532e28a4053SRui Paulo if (key == NULL)
533e28a4053SRui Paulo return NULL;
534e28a4053SRui Paulo
535e28a4053SRui Paulo *len = EAP_MSK_LEN;
536e28a4053SRui Paulo eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
537e28a4053SRui Paulo "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
538e28a4053SRui Paulo EAP_MSK_LEN, key);
539e28a4053SRui Paulo
540e28a4053SRui Paulo return key;
541e28a4053SRui Paulo }
542e28a4053SRui Paulo
543e28a4053SRui Paulo
eap_pax_get_emsk(struct eap_sm * sm,void * priv,size_t * len)544e28a4053SRui Paulo static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
545e28a4053SRui Paulo {
546e28a4053SRui Paulo struct eap_pax_data *data = priv;
547e28a4053SRui Paulo u8 *key;
548e28a4053SRui Paulo
549e28a4053SRui Paulo if (data->state != SUCCESS)
550e28a4053SRui Paulo return NULL;
551e28a4053SRui Paulo
552e28a4053SRui Paulo key = os_malloc(EAP_EMSK_LEN);
553e28a4053SRui Paulo if (key == NULL)
554e28a4053SRui Paulo return NULL;
555e28a4053SRui Paulo
556e28a4053SRui Paulo *len = EAP_EMSK_LEN;
557e28a4053SRui Paulo eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
558e28a4053SRui Paulo "Extended Master Session Key",
559e28a4053SRui Paulo data->rand.e, 2 * EAP_PAX_RAND_LEN,
560e28a4053SRui Paulo EAP_EMSK_LEN, key);
561e28a4053SRui Paulo
562e28a4053SRui Paulo return key;
563e28a4053SRui Paulo }
564e28a4053SRui Paulo
565e28a4053SRui Paulo
eap_pax_isSuccess(struct eap_sm * sm,void * priv)566*c1d255d3SCy Schubert static bool eap_pax_isSuccess(struct eap_sm *sm, void *priv)
567e28a4053SRui Paulo {
568e28a4053SRui Paulo struct eap_pax_data *data = priv;
569e28a4053SRui Paulo return data->state == SUCCESS;
570e28a4053SRui Paulo }
571e28a4053SRui Paulo
572e28a4053SRui Paulo
eap_pax_get_session_id(struct eap_sm * sm,void * priv,size_t * len)5735b9c547cSRui Paulo static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
5745b9c547cSRui Paulo {
5755b9c547cSRui Paulo struct eap_pax_data *data = priv;
5765b9c547cSRui Paulo u8 *sid;
5775b9c547cSRui Paulo
5785b9c547cSRui Paulo if (data->state != SUCCESS)
5795b9c547cSRui Paulo return NULL;
5805b9c547cSRui Paulo
5815b9c547cSRui Paulo sid = os_malloc(1 + EAP_PAX_MID_LEN);
5825b9c547cSRui Paulo if (sid == NULL)
5835b9c547cSRui Paulo return NULL;
5845b9c547cSRui Paulo
5855b9c547cSRui Paulo *len = 1 + EAP_PAX_MID_LEN;
5865b9c547cSRui Paulo sid[0] = EAP_TYPE_PAX;
5875b9c547cSRui Paulo os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
5885b9c547cSRui Paulo
5895b9c547cSRui Paulo return sid;
5905b9c547cSRui Paulo }
5915b9c547cSRui Paulo
5925b9c547cSRui Paulo
eap_server_pax_register(void)593e28a4053SRui Paulo int eap_server_pax_register(void)
594e28a4053SRui Paulo {
595e28a4053SRui Paulo struct eap_method *eap;
596e28a4053SRui Paulo
597e28a4053SRui Paulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
598e28a4053SRui Paulo EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
599e28a4053SRui Paulo if (eap == NULL)
600e28a4053SRui Paulo return -1;
601e28a4053SRui Paulo
602e28a4053SRui Paulo eap->init = eap_pax_init;
603e28a4053SRui Paulo eap->reset = eap_pax_reset;
604e28a4053SRui Paulo eap->buildReq = eap_pax_buildReq;
605e28a4053SRui Paulo eap->check = eap_pax_check;
606e28a4053SRui Paulo eap->process = eap_pax_process;
607e28a4053SRui Paulo eap->isDone = eap_pax_isDone;
608e28a4053SRui Paulo eap->getKey = eap_pax_getKey;
609e28a4053SRui Paulo eap->isSuccess = eap_pax_isSuccess;
610e28a4053SRui Paulo eap->get_emsk = eap_pax_get_emsk;
6115b9c547cSRui Paulo eap->getSessionId = eap_pax_get_session_id;
612e28a4053SRui Paulo
613780fb4a2SCy Schubert return eap_server_method_register(eap);
614e28a4053SRui Paulo }
615