Merge from vendor branch LESS:
[dragonfly.git] / contrib / hostapd-0.5.8 / eap_pax.c
1 /*
2  * hostapd / EAP-PAX (RFC 4746) server
3  * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
4  *
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.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "hostapd.h"
18 #include "common.h"
19 #include "eap_i.h"
20 #include "eap_pax_common.h"
21
22 /*
23  * Note: only PAX_STD subprotocol is currently supported
24  *
25  * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
26  * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
27  * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
28  * RSAES-OAEP).
29  */
30
31 struct eap_pax_data {
32         enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
33         u8 mac_id;
34         union {
35                 u8 e[2 * EAP_PAX_RAND_LEN];
36                 struct {
37                         u8 x[EAP_PAX_RAND_LEN]; /* server rand */
38                         u8 y[EAP_PAX_RAND_LEN]; /* client rand */
39                 } r;
40         } rand;
41         u8 ak[EAP_PAX_AK_LEN];
42         u8 mk[EAP_PAX_MK_LEN];
43         u8 ck[EAP_PAX_CK_LEN];
44         u8 ick[EAP_PAX_ICK_LEN];
45         int keys_set;
46         char *cid;
47         size_t cid_len;
48 };
49
50
51 static void * eap_pax_init(struct eap_sm *sm)
52 {
53         struct eap_pax_data *data;
54
55         data = wpa_zalloc(sizeof(*data));
56         if (data == NULL)
57                 return NULL;
58         data->state = PAX_STD_1;
59         /*
60          * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
61          * supported
62          */
63         data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
64
65         return data;
66 }
67
68
69 static void eap_pax_reset(struct eap_sm *sm, void *priv)
70 {
71         struct eap_pax_data *data = priv;
72         free(data->cid);
73         free(data);
74 }
75
76
77 static u8 * eap_pax_build_std_1(struct eap_sm *sm,
78                                 struct eap_pax_data *data,
79                                 int id, size_t *reqDataLen)
80 {
81         struct eap_pax_hdr *req;
82         u8 *pos;
83
84         wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
85
86         if (hostapd_get_rand(data->rand.r.x, EAP_PAX_RAND_LEN)) {
87                 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
88                 data->state = FAILURE;
89                 return NULL;
90         }
91
92         *reqDataLen = sizeof(*req) + 2 + EAP_PAX_RAND_LEN + EAP_PAX_ICV_LEN;
93         req = malloc(*reqDataLen);
94         if (req == NULL) {
95                 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
96                            "request");
97                 data->state = FAILURE;
98                 return NULL;
99         }
100
101         req->code = EAP_CODE_REQUEST;
102         req->identifier = id;
103         req->length = htons(*reqDataLen);
104         req->type = EAP_TYPE_PAX;
105         req->op_code = EAP_PAX_OP_STD_1;
106         req->flags = 0;
107         req->mac_id = data->mac_id;
108         req->dh_group_id = EAP_PAX_DH_GROUP_NONE;
109         req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
110         pos = (u8 *) (req + 1);
111         *pos++ = 0;
112         *pos++ = EAP_PAX_RAND_LEN;
113         memcpy(pos, data->rand.r.x, EAP_PAX_RAND_LEN);
114         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
115                     pos, EAP_PAX_RAND_LEN);
116         pos += EAP_PAX_RAND_LEN;
117
118         eap_pax_mac(data->mac_id, (u8 *) "", 0,
119                     (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN,
120                     NULL, 0, NULL, 0, pos);
121         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
122         pos += EAP_PAX_ICV_LEN;
123
124         return (u8 *) req;
125 }
126
127
128 static u8 * eap_pax_build_std_3(struct eap_sm *sm,
129                                 struct eap_pax_data *data,
130                                 int id, size_t *reqDataLen)
131 {
132         struct eap_pax_hdr *req;
133         u8 *pos;
134
135         wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
136
137         *reqDataLen = sizeof(*req) + 2 + EAP_PAX_MAC_LEN + EAP_PAX_ICV_LEN;
138         req = malloc(*reqDataLen);
139         if (req == NULL) {
140                 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
141                            "request");
142                 data->state = FAILURE;
143                 return NULL;
144         }
145
146         req->code = EAP_CODE_REQUEST;
147         req->identifier = id;
148         req->length = htons(*reqDataLen);
149         req->type = EAP_TYPE_PAX;
150         req->op_code = EAP_PAX_OP_STD_3;
151         req->flags = 0;
152         req->mac_id = data->mac_id;
153         req->dh_group_id = EAP_PAX_DH_GROUP_NONE;
154         req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
155         pos = (u8 *) (req + 1);
156         *pos++ = 0;
157         *pos++ = EAP_PAX_MAC_LEN;
158         eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
159                     data->rand.r.y, EAP_PAX_RAND_LEN,
160                     (u8 *) data->cid, data->cid_len, NULL, 0, pos);
161         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
162                     pos, EAP_PAX_MAC_LEN);
163         pos += EAP_PAX_MAC_LEN;
164
165         /* Optional ADE could be added here, if needed */
166
167         eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
168                     (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN,
169                     NULL, 0, NULL, 0, pos);
170         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
171         pos += EAP_PAX_ICV_LEN;
172
173         return (u8 *) req;
174 }
175
176
177 static u8 * eap_pax_buildReq(struct eap_sm *sm, void *priv, int id,
178                                   size_t *reqDataLen)
179 {
180         struct eap_pax_data *data = priv;
181
182         switch (data->state) {
183         case PAX_STD_1:
184                 return eap_pax_build_std_1(sm, data, id, reqDataLen);
185         case PAX_STD_3:
186                 return eap_pax_build_std_3(sm, data, id, reqDataLen);
187         default:
188                 wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
189                            data->state);
190                 break;
191         }
192         return NULL;
193 }
194
195
196 static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
197                              u8 *respData, size_t respDataLen)
198 {
199         struct eap_pax_data *data = priv;
200         struct eap_pax_hdr *resp;
201         size_t len;
202         u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
203
204         resp = (struct eap_pax_hdr *) respData;
205         if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PAX ||
206             (len = ntohs(resp->length)) > respDataLen ||
207             len < sizeof(*resp) + EAP_PAX_ICV_LEN) {
208                 wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
209                 return TRUE;
210         }
211
212         wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
213                    "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
214                    "public_key_id 0x%x",
215                    resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
216                    resp->public_key_id);
217         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
218                     (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
219
220         if (data->state == PAX_STD_1 &&
221             resp->op_code != EAP_PAX_OP_STD_2) {
222                 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
223                            "ignore op %d", resp->op_code);
224                 return TRUE;
225         }
226
227         if (data->state == PAX_STD_3 &&
228             resp->op_code != EAP_PAX_OP_ACK) {
229                 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
230                            "ignore op %d", resp->op_code);
231                 return TRUE;
232         }
233
234         if (resp->op_code != EAP_PAX_OP_STD_2 &&
235             resp->op_code != EAP_PAX_OP_ACK) {
236                 wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
237                            resp->op_code);
238         }
239
240         if (data->mac_id != resp->mac_id) {
241                 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
242                            "received 0x%x", data->mac_id, resp->mac_id);
243                 return TRUE;
244         }
245
246         if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
247                 wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
248                            "received 0x%x", EAP_PAX_DH_GROUP_NONE,
249                            resp->dh_group_id);
250                 return TRUE;
251         }
252
253         if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
254                 wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
255                            "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
256                            resp->public_key_id);
257                 return TRUE;
258         }
259
260         if (resp->flags & EAP_PAX_FLAGS_MF) {
261                 /* TODO: add support for reassembling fragments */
262                 wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
263                 return TRUE;
264         }
265
266         if (resp->flags & EAP_PAX_FLAGS_CE) {
267                 wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
268                 return TRUE;
269         }
270
271         if (data->keys_set) {
272                 if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
273                         wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
274                         return TRUE;
275                 }
276                 icv = respData + len - EAP_PAX_ICV_LEN;
277                 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
278                 eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
279                             respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
280                             icvbuf);
281                 if (memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
282                         wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
283                         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
284                                     icvbuf, EAP_PAX_ICV_LEN);
285                         return TRUE;
286                 }
287         }
288
289         return FALSE;
290 }
291
292
293 static void eap_pax_process_std_2(struct eap_sm *sm,
294                                   struct eap_pax_data *data,
295                                   u8 *respData, size_t respDataLen)
296 {
297         struct eap_pax_hdr *resp;
298         u8 *pos, mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
299         size_t len, left;
300         int i;
301
302         if (data->state != PAX_STD_1)
303                 return;
304
305         wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
306
307         resp = (struct eap_pax_hdr *) respData;
308         len = ntohs(resp->length);
309         pos = (u8 *) (resp + 1);
310         left = len - sizeof(*resp);
311
312         if (left < 2 + EAP_PAX_RAND_LEN ||
313             ((pos[0] << 8) | pos[1]) != EAP_PAX_RAND_LEN) {
314                 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
315                 return;
316         }
317         pos += 2;
318         left -= 2;
319         memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
320         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
321                     data->rand.r.y, EAP_PAX_RAND_LEN);
322         pos += EAP_PAX_RAND_LEN;
323         left -= EAP_PAX_RAND_LEN;
324
325         if (left < 2 || (size_t) 2 + ((pos[0] << 8) | pos[1]) > left) {
326                 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
327                 return;
328         }
329         data->cid_len = (pos[0] << 8) | pos[1];
330         free(data->cid);
331         data->cid = malloc(data->cid_len);
332         if (data->cid == NULL) {
333                 wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
334                            "CID");
335                 return;
336         }
337         memcpy(data->cid, pos + 2, data->cid_len);
338         pos += 2 + data->cid_len;
339         left -= 2 + data->cid_len;
340         wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
341                           (u8 *) data->cid, data->cid_len);
342
343         if (left < 2 + EAP_PAX_MAC_LEN ||
344             ((pos[0] << 8) | pos[1]) != EAP_PAX_MAC_LEN) {
345                 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
346                 return;
347         }
348         pos += 2;
349         left -= 2;
350         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
351                     pos, EAP_PAX_MAC_LEN);
352
353         if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
354                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
355                                   (u8 *) data->cid, data->cid_len);
356                 data->state = FAILURE;
357                 return;
358         }
359
360         for (i = 0;
361              i < EAP_MAX_METHODS &&
362                      (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
363                       sm->user->methods[i].method != EAP_TYPE_NONE);
364              i++) {
365                 if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
366                     sm->user->methods[i].method == EAP_TYPE_PAX)
367                         break;
368         }
369
370         if (i >= EAP_MAX_METHODS ||
371             sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
372             sm->user->methods[i].method != EAP_TYPE_PAX) {
373                 wpa_hexdump_ascii(MSG_DEBUG,
374                                   "EAP-PAX: EAP-PAX not enabled for CID",
375                                   (u8 *) data->cid, data->cid_len);
376                 data->state = FAILURE;
377                 return;
378         }
379
380         if (sm->user->password == NULL ||
381             sm->user->password_len != EAP_PAX_AK_LEN) {
382                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
383                                   "user database for CID",
384                                   (u8 *) data->cid, data->cid_len);
385                 data->state = FAILURE;
386                 return;
387         }
388         memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
389
390         if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
391                                            data->rand.e, data->mk, data->ck,
392                                            data->ick) < 0) {
393                 wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
394                            "key derivation");
395                 data->state = FAILURE;
396                 return;
397         }
398         data->keys_set = 1;
399
400         eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
401                     data->rand.r.x, EAP_PAX_RAND_LEN,
402                     data->rand.r.y, EAP_PAX_RAND_LEN,
403                     (u8 *) data->cid, data->cid_len, mac);
404         if (memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) {
405                 wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
406                            "PAX_STD-2");
407                 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
408                             mac, EAP_PAX_MAC_LEN);
409                 data->state = FAILURE;
410                 return;
411         }
412
413         pos += EAP_PAX_MAC_LEN;
414         left -= EAP_PAX_MAC_LEN;
415
416         if (left < EAP_PAX_ICV_LEN) {
417                 wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
418                            "PAX_STD-2", (unsigned long) left);
419                 return;
420         }
421         wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
422         eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
423                     respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf);
424         if (memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
425                 wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
426                 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
427                             icvbuf, EAP_PAX_ICV_LEN);
428                 return;
429         }
430         pos += EAP_PAX_ICV_LEN;
431         left -= EAP_PAX_ICV_LEN;
432
433         if (left > 0) {
434                 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
435                             pos, left);
436         }
437
438         data->state = PAX_STD_3;
439 }
440
441
442 static void eap_pax_process_ack(struct eap_sm *sm,
443                                 struct eap_pax_data *data,
444                                 u8 *respData, size_t respDataLen)
445 {
446         if (data->state != PAX_STD_3)
447                 return;
448
449         wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
450                    "completed successfully");
451         data->state = SUCCESS;
452 }
453
454
455 static void eap_pax_process(struct eap_sm *sm, void *priv,
456                                  u8 *respData, size_t respDataLen)
457 {
458         struct eap_pax_data *data = priv;
459         struct eap_pax_hdr *resp;
460
461         if (sm->user == NULL || sm->user->password == NULL) {
462                 wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
463                            "configured");
464                 data->state = FAILURE;
465                 return;
466         }
467
468         resp = (struct eap_pax_hdr *) respData;
469
470         switch (resp->op_code) {
471         case EAP_PAX_OP_STD_2:
472                 eap_pax_process_std_2(sm, data, respData, respDataLen);
473                 break;
474         case EAP_PAX_OP_ACK:
475                 eap_pax_process_ack(sm, data, respData, respDataLen);
476                 break;
477         }
478 }
479
480
481 static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
482 {
483         struct eap_pax_data *data = priv;
484         return data->state == SUCCESS || data->state == FAILURE;
485 }
486
487
488 static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
489 {
490         struct eap_pax_data *data = priv;
491         u8 *key;
492
493         if (data->state != SUCCESS)
494                 return NULL;
495
496         key = malloc(EAP_MSK_LEN);
497         if (key == NULL)
498                 return NULL;
499
500         *len = EAP_MSK_LEN;
501         eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
502                     "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
503                     EAP_MSK_LEN, key);
504
505         return key;
506 }
507
508
509 static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
510 {
511         struct eap_pax_data *data = priv;
512         u8 *key;
513
514         if (data->state != SUCCESS)
515                 return NULL;
516
517         key = malloc(EAP_EMSK_LEN);
518         if (key == NULL)
519                 return NULL;
520
521         *len = EAP_EMSK_LEN;
522         eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
523                     "Extended Master Session Key",
524                     data->rand.e, 2 * EAP_PAX_RAND_LEN,
525                     EAP_EMSK_LEN, key);
526
527         return key;
528 }
529
530
531 static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
532 {
533         struct eap_pax_data *data = priv;
534         return data->state == SUCCESS;
535 }
536
537
538 int eap_server_pax_register(void)
539 {
540         struct eap_method *eap;
541         int ret;
542
543         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
544                                       EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
545         if (eap == NULL)
546                 return -1;
547
548         eap->init = eap_pax_init;
549         eap->reset = eap_pax_reset;
550         eap->buildReq = eap_pax_buildReq;
551         eap->check = eap_pax_check;
552         eap->process = eap_pax_process;
553         eap->isDone = eap_pax_isDone;
554         eap->getKey = eap_pax_getKey;
555         eap->isSuccess = eap_pax_isSuccess;
556         eap->get_emsk = eap_pax_get_emsk;
557
558         ret = eap_server_method_register(eap);
559         if (ret)
560                 eap_server_method_free(eap);
561         return ret;
562 }