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