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