e031f29fb436db7c0fee298d11b3e766d2a42ccd
[dragonfly.git] / contrib / hostapd-0.5.8 / eap_sake.c
1 /*
2  * hostapd / EAP-SAKE (RFC 4763) server
3  * Copyright (c) 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_sake_common.h"
21
22
23 struct eap_sake_data {
24         enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
25         u8 rand_s[EAP_SAKE_RAND_LEN];
26         u8 rand_p[EAP_SAKE_RAND_LEN];
27         struct {
28                 u8 auth[EAP_SAKE_TEK_AUTH_LEN];
29                 u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
30         } tek;
31         u8 msk[EAP_MSK_LEN];
32         u8 emsk[EAP_EMSK_LEN];
33         u8 session_id;
34         u8 *peerid;
35         size_t peerid_len;
36         u8 *serverid;
37         size_t serverid_len;
38 };
39
40
41 static const char * eap_sake_state_txt(int state)
42 {
43         switch (state) {
44         case IDENTITY:
45                 return "IDENTITY";
46         case CHALLENGE:
47                 return "CHALLENGE";
48         case CONFIRM:
49                 return "CONFIRM";
50         case SUCCESS:
51                 return "SUCCESS";
52         case FAILURE:
53                 return "FAILURE";
54         default:
55                 return "?";
56         }
57 }
58
59
60 static void eap_sake_state(struct eap_sake_data *data, int state)
61 {
62         wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
63                    eap_sake_state_txt(data->state),
64                    eap_sake_state_txt(state));
65         data->state = state;
66 }
67
68
69 static void * eap_sake_init(struct eap_sm *sm)
70 {
71         struct eap_sake_data *data;
72
73         data = wpa_zalloc(sizeof(*data));
74         if (data == NULL)
75                 return NULL;
76         data->state = CHALLENGE;
77
78         if (hostapd_get_rand(&data->session_id, 1)) {
79                 wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
80                 os_free(data);
81                 return NULL;
82         }
83         wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d",
84                    data->session_id);
85
86         /* TODO: add support for configuring SERVERID */
87         data->serverid = (u8 *) strdup("hostapd");
88         if (data->serverid)
89                 data->serverid_len = strlen((char *) data->serverid);
90
91         return data;
92 }
93
94
95 static void eap_sake_reset(struct eap_sm *sm, void *priv)
96 {
97         struct eap_sake_data *data = priv;
98         os_free(data->serverid);
99         os_free(data->peerid);
100         os_free(data);
101 }
102
103
104 static u8 * eap_sake_build_msg(struct eap_sake_data *data, u8 **payload,
105                                int id, size_t *length, u8 subtype)
106 {
107         struct eap_sake_hdr *req;
108         u8 *msg;
109
110         *length += sizeof(struct eap_sake_hdr);
111
112         msg = wpa_zalloc(*length);
113         if (msg == NULL) {
114                 wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
115                            "request");
116                 return NULL;
117         }
118
119         req = (struct eap_sake_hdr *) msg;
120         req->code = EAP_CODE_REQUEST;
121         req->identifier = id;
122         req->length = htons((u16) *length);
123         req->type = EAP_TYPE_SAKE;
124         req->version = EAP_SAKE_VERSION;
125         req->session_id = data->session_id;
126         req->subtype = subtype;
127         *payload = (u8 *) (req + 1);
128
129         return msg;
130 }
131
132
133 static u8 * eap_sake_build_identity(struct eap_sm *sm,
134                                     struct eap_sake_data *data,
135                                     int id, size_t *reqDataLen)
136 {
137         u8 *msg, *pos;
138
139         wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity");
140
141         *reqDataLen = 4;
142         if (data->serverid)
143                 *reqDataLen += 2 + data->serverid_len;
144         msg = eap_sake_build_msg(data, &pos, id, reqDataLen,
145                                  EAP_SAKE_SUBTYPE_IDENTITY);
146         if (msg == NULL) {
147                 data->state = FAILURE;
148                 return NULL;
149         }
150
151         wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ");
152         *pos++ = EAP_SAKE_AT_PERM_ID_REQ;
153         *pos++ = 4;
154         *pos++ = 0;
155         *pos++ = 0;
156
157         if (data->serverid) {
158                 wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
159                 *pos++ = EAP_SAKE_AT_SERVERID;
160                 *pos++ = 2 + data->serverid_len;
161                 os_memcpy(pos, data->serverid, data->serverid_len);
162         }
163
164         return msg;
165 }
166
167
168 static u8 * eap_sake_build_challenge(struct eap_sm *sm,
169                                      struct eap_sake_data *data,
170                                      int id, size_t *reqDataLen)
171 {
172         u8 *msg, *pos;
173
174         wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
175
176         if (hostapd_get_rand(data->rand_s, EAP_SAKE_RAND_LEN)) {
177                 wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
178                 data->state = FAILURE;
179                 return NULL;
180         }
181         wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
182                     data->rand_s, EAP_SAKE_RAND_LEN);
183
184         *reqDataLen = 2 + EAP_SAKE_RAND_LEN;
185         if (data->serverid)
186                 *reqDataLen += 2 + data->serverid_len;
187         msg = eap_sake_build_msg(data, &pos, id, reqDataLen,
188                                  EAP_SAKE_SUBTYPE_CHALLENGE);
189         if (msg == NULL) {
190                 data->state = FAILURE;
191                 return NULL;
192         }
193
194         wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S");
195         *pos++ = EAP_SAKE_AT_RAND_S;
196         *pos++ = 2 + EAP_SAKE_RAND_LEN;
197         os_memcpy(pos, data->rand_s, EAP_SAKE_RAND_LEN);
198         pos += EAP_SAKE_RAND_LEN;
199
200         if (data->serverid) {
201                 wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
202                 *pos++ = EAP_SAKE_AT_SERVERID;
203                 *pos++ = 2 + data->serverid_len;
204                 os_memcpy(pos, data->serverid, data->serverid_len);
205         }
206
207         return msg;
208 }
209
210
211 static u8 * eap_sake_build_confirm(struct eap_sm *sm,
212                                    struct eap_sake_data *data,
213                                    int id, size_t *reqDataLen)
214 {
215         u8 *msg, *pos;
216
217         wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm");
218
219         *reqDataLen = 2 + EAP_SAKE_MIC_LEN;
220         msg = eap_sake_build_msg(data, &pos, id, reqDataLen,
221                                  EAP_SAKE_SUBTYPE_CONFIRM);
222         if (msg == NULL) {
223                 data->state = FAILURE;
224                 return NULL;
225         }
226
227         wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S");
228         *pos++ = EAP_SAKE_AT_MIC_S;
229         *pos++ = 2 + EAP_SAKE_MIC_LEN;
230         if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
231                                  data->serverid, data->serverid_len,
232                                  data->peerid, data->peerid_len, 0,
233                                  msg, *reqDataLen, pos, pos)) {
234                 wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
235                 data->state = FAILURE;
236                 os_free(msg);
237                 return NULL;
238         }
239
240         return msg;
241 }
242
243
244 static u8 * eap_sake_buildReq(struct eap_sm *sm, void *priv, int id,
245                               size_t *reqDataLen)
246 {
247         struct eap_sake_data *data = priv;
248
249         switch (data->state) {
250         case IDENTITY:
251                 return eap_sake_build_identity(sm, data, id, reqDataLen);
252         case CHALLENGE:
253                 return eap_sake_build_challenge(sm, data, id, reqDataLen);
254         case CONFIRM:
255                 return eap_sake_build_confirm(sm, data, id, reqDataLen);
256         default:
257                 wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq",
258                            data->state);
259                 break;
260         }
261         return NULL;
262 }
263
264
265 static Boolean eap_sake_check(struct eap_sm *sm, void *priv,
266                               u8 *respData, size_t respDataLen)
267 {
268         struct eap_sake_data *data = priv;
269         struct eap_sake_hdr *resp;
270         size_t len;
271         u8 version, session_id, subtype;
272
273         resp = (struct eap_sake_hdr *) respData;
274         if (respDataLen < sizeof(*resp) ||
275             resp->type != EAP_TYPE_SAKE ||
276             (len = ntohs(resp->length)) > respDataLen ||
277             len < sizeof(*resp)) {
278                 wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame");
279                 return TRUE;
280         }
281         version = resp->version;
282         session_id = resp->session_id;
283         subtype = resp->subtype;
284
285         if (version != EAP_SAKE_VERSION) {
286                 wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version);
287                 return TRUE;
288         }
289
290         if (session_id != data->session_id) {
291                 wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
292                            session_id, data->session_id);
293                 return TRUE;
294         }
295
296         wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype);
297
298         if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY)
299                 return FALSE;
300
301         if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE)
302                 return FALSE;
303
304         if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM)
305                 return FALSE;
306
307         if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT)
308                 return FALSE;
309
310         wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d",
311                    subtype, data->state);
312
313         return TRUE;
314 }
315
316
317 static void eap_sake_process_identity(struct eap_sm *sm,
318                                       struct eap_sake_data *data,
319                                       u8 *respData, size_t respDataLen,
320                                       u8 *payload, size_t payloadlen)
321 {
322         if (data->state != IDENTITY)
323                 return;
324
325         wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity");
326         /* TODO: update identity and select new user data */
327         eap_sake_state(data, CHALLENGE);
328 }
329
330
331 static void eap_sake_process_challenge(struct eap_sm *sm,
332                                        struct eap_sake_data *data,
333                                        u8 *respData, size_t respDataLen,
334                                        u8 *payload, size_t payloadlen)
335 {
336         struct eap_sake_parse_attr attr;
337         u8 mic_p[EAP_SAKE_MIC_LEN];
338
339         if (data->state != CHALLENGE)
340                 return;
341
342         wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge");
343
344         if (eap_sake_parse_attributes(payload, payloadlen, &attr))
345                 return;
346
347         if (!attr.rand_p || !attr.mic_p) {
348                 wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not "
349                            "include AT_RAND_P or AT_MIC_P");
350                 return;
351         }
352
353         os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN);
354
355         os_free(data->peerid);
356         data->peerid = NULL;
357         data->peerid_len = 0;
358         if (attr.peerid) {
359                 data->peerid = os_malloc(attr.peerid_len);
360                 if (data->peerid == NULL)
361                         return;
362                 os_memcpy(data->peerid, attr.peerid, attr.peerid_len);
363                 data->peerid_len = attr.peerid_len;
364         }
365
366         if (sm->user == NULL || sm->user->password == NULL ||
367             sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
368                 wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with "
369                            "%d-byte key not configured",
370                            2 * EAP_SAKE_ROOT_SECRET_LEN);
371                 data->state = FAILURE;
372                 return;
373         }
374         eap_sake_derive_keys(sm->user->password,
375                              sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
376                              data->rand_s, data->rand_p,
377                              (u8 *) &data->tek, data->msk, data->emsk);
378
379         eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
380                              data->serverid, data->serverid_len,
381                              data->peerid, data->peerid_len, 1,
382                              respData, respDataLen, attr.mic_p, mic_p);
383         if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
384                 wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
385                 eap_sake_state(data, FAILURE);
386                 return;
387         }
388
389         eap_sake_state(data, CONFIRM);
390 }
391
392
393 static void eap_sake_process_confirm(struct eap_sm *sm,
394                                      struct eap_sake_data *data,
395                                      u8 *respData, size_t respDataLen,
396                                      u8 *payload, size_t payloadlen)
397 {
398         struct eap_sake_parse_attr attr;
399         u8 mic_p[EAP_SAKE_MIC_LEN];
400
401         if (data->state != CONFIRM)
402                 return;
403
404         wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm");
405
406         if (eap_sake_parse_attributes(payload, payloadlen, &attr))
407                 return;
408
409         if (!attr.mic_p) {
410                 wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not "
411                            "include AT_MIC_P");
412                 return;
413         }
414
415         eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
416                              data->serverid, data->serverid_len,
417                              data->peerid, data->peerid_len, 1,
418                              respData, respDataLen, attr.mic_p, mic_p);
419         if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
420                 wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
421                 eap_sake_state(data, FAILURE);
422         } else
423                 eap_sake_state(data, SUCCESS);
424 }
425
426
427 static void eap_sake_process_auth_reject(struct eap_sm *sm,
428                                          struct eap_sake_data *data,
429                                          u8 *respData, size_t respDataLen,
430                                          u8 *payload, size_t payloadlen)
431 {
432         wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject");
433         eap_sake_state(data, FAILURE);
434 }
435
436
437 static void eap_sake_process(struct eap_sm *sm, void *priv,
438                              u8 *respData, size_t respDataLen)
439 {
440         struct eap_sake_data *data = priv;
441         struct eap_sake_hdr *resp;
442         u8 subtype, *pos, *end;
443
444         resp = (struct eap_sake_hdr *) respData;
445         subtype = resp->subtype;
446         pos = (u8 *) (resp + 1);
447         end = respData + ntohs(resp->length);
448
449         wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
450                     pos, end - pos);
451
452         switch (subtype) {
453         case EAP_SAKE_SUBTYPE_IDENTITY:
454                 eap_sake_process_identity(sm, data, respData, respDataLen, pos,
455                                           end - pos);
456                 break;
457         case EAP_SAKE_SUBTYPE_CHALLENGE:
458                 eap_sake_process_challenge(sm, data, respData, respDataLen,
459                                            pos, end - pos);
460                 break;
461         case EAP_SAKE_SUBTYPE_CONFIRM:
462                 eap_sake_process_confirm(sm, data, respData, respDataLen, pos,
463                                          end - pos);
464                 break;
465         case EAP_SAKE_SUBTYPE_AUTH_REJECT:
466                 eap_sake_process_auth_reject(sm, data, respData, respDataLen,
467                                              pos, end - pos);
468                 break;
469         }
470 }
471
472
473 static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv)
474 {
475         struct eap_sake_data *data = priv;
476         return data->state == SUCCESS || data->state == FAILURE;
477 }
478
479
480 static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
481 {
482         struct eap_sake_data *data = priv;
483         u8 *key;
484
485         if (data->state != SUCCESS)
486                 return NULL;
487
488         key = os_malloc(EAP_MSK_LEN);
489         if (key == NULL)
490                 return NULL;
491         os_memcpy(key, data->msk, EAP_MSK_LEN);
492         *len = EAP_MSK_LEN;
493
494         return key;
495 }
496
497
498 static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
499 {
500         struct eap_sake_data *data = priv;
501         u8 *key;
502
503         if (data->state != SUCCESS)
504                 return NULL;
505
506         key = os_malloc(EAP_EMSK_LEN);
507         if (key == NULL)
508                 return NULL;
509         os_memcpy(key, data->emsk, EAP_EMSK_LEN);
510         *len = EAP_EMSK_LEN;
511
512         return key;
513 }
514
515
516 static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
517 {
518         struct eap_sake_data *data = priv;
519         return data->state == SUCCESS;
520 }
521
522
523 int eap_server_sake_register(void)
524 {
525         struct eap_method *eap;
526         int ret;
527
528         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
529                                       EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
530         if (eap == NULL)
531                 return -1;
532
533         eap->init = eap_sake_init;
534         eap->reset = eap_sake_reset;
535         eap->buildReq = eap_sake_buildReq;
536         eap->check = eap_sake_check;
537         eap->process = eap_sake_process;
538         eap->isDone = eap_sake_isDone;
539         eap->getKey = eap_sake_getKey;
540         eap->isSuccess = eap_sake_isSuccess;
541         eap->get_emsk = eap_sake_get_emsk;
542
543         ret = eap_server_method_register(eap);
544         if (ret)
545                 eap_server_method_free(eap);
546         return ret;
547 }