wpa-supplicant: remove version tag from directory
[dragonfly.git] / contrib / wpa_supplicant / eap_gpsk.c
1 /*
2  * EAP peer method: EAP-GPSK (draft-ietf-emu-eap-gpsk-03.txt)
3  * Copyright (c) 2006-2007, 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_gpsk_common.h"
21
22 struct eap_gpsk_data {
23         enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
24         u8 rand_server[EAP_GPSK_RAND_LEN];
25         u8 rand_client[EAP_GPSK_RAND_LEN];
26         u8 msk[EAP_MSK_LEN];
27         u8 emsk[EAP_EMSK_LEN];
28         u8 sk[EAP_GPSK_MAX_SK_LEN];
29         size_t sk_len;
30         u8 pk[EAP_GPSK_MAX_PK_LEN];
31         size_t pk_len;
32         u8 session_id;
33         int session_id_set;
34         u8 *id_client;
35         size_t id_client_len;
36         u8 *id_server;
37         size_t id_server_len;
38         int vendor; /* CSuite/Specifier */
39         int specifier; /* CSuite/Specifier */
40         u8 *psk;
41         size_t psk_len;
42 };
43
44
45 #ifndef CONFIG_NO_STDOUT_DEBUG
46 static const char * eap_gpsk_state_txt(int state)
47 {
48         switch (state) {
49         case GPSK_1:
50                 return "GPSK-1";
51         case GPSK_3:
52                 return "GPSK-3";
53         case SUCCESS:
54                 return "SUCCESS";
55         case FAILURE:
56                 return "FAILURE";
57         default:
58                 return "?";
59         }
60 }
61 #endif /* CONFIG_NO_STDOUT_DEBUG */
62
63
64 static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
65 {
66         wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
67                    eap_gpsk_state_txt(data->state),
68                    eap_gpsk_state_txt(state));
69         data->state = state;
70 }
71
72
73 static void eap_gpsk_deinit(struct eap_sm *sm, void *priv);
74
75
76 static void * eap_gpsk_init(struct eap_sm *sm)
77 {
78         struct wpa_ssid *config = eap_get_config(sm);
79         struct eap_gpsk_data *data;
80
81         if (config == NULL) {
82                 wpa_printf(MSG_INFO, "EAP-GPSK: No configuration found");
83                 return NULL;
84         }
85
86         if (config->eappsk == NULL) {
87                 wpa_printf(MSG_INFO, "EAP-GPSK: No key (eappsk) configured");
88                 return NULL;
89         }
90
91         data = os_zalloc(sizeof(*data));
92         if (data == NULL)
93                 return NULL;
94         data->state = GPSK_1;
95
96         if (config->nai) {
97                 data->id_client = os_malloc(config->nai_len);
98                 if (data->id_client == NULL) {
99                         eap_gpsk_deinit(sm, data);
100                         return NULL;
101                 }
102                 os_memcpy(data->id_client, config->nai, config->nai_len);
103                 data->id_client_len = config->nai_len;
104         }
105
106         data->psk = os_malloc(config->eappsk_len);
107         if (data->psk == NULL) {
108                 eap_gpsk_deinit(sm, data);
109                 return NULL;
110         }
111         os_memcpy(data->psk, config->eappsk, config->eappsk_len);
112         data->psk_len = config->eappsk_len;
113
114         return data;
115 }
116
117
118 static void eap_gpsk_deinit(struct eap_sm *sm, void *priv)
119 {
120         struct eap_gpsk_data *data = priv;
121         os_free(data->id_server);
122         os_free(data->id_client);
123         os_free(data->psk);
124         os_free(data);
125 }
126
127
128 static u8 * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
129                                     struct eap_gpsk_data *data,
130                                     struct eap_method_ret *ret,
131                                     const u8 *reqData, size_t reqDataLen,
132                                     const u8 *payload, size_t payload_len,
133                                     size_t *respDataLen)
134 {
135         size_t len, csuite_list_len, miclen;
136         struct eap_hdr *resp;
137         u8 *rpos, *start;
138         const u8 *csuite_list, *pos, *end;
139         const struct eap_hdr *req;
140         struct eap_gpsk_csuite *csuite;
141         u16 alen;
142         int i, count;
143
144         if (data->state != GPSK_1) {
145                 ret->ignore = TRUE;
146                 return NULL;
147         }
148
149         wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1");
150
151         req = (const struct eap_hdr *) reqData;
152         pos = payload;
153         end = payload + payload_len;
154
155         if (end - pos < 2) {
156                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
157                 return NULL;
158         }
159         alen = WPA_GET_BE16(pos);
160         pos += 2;
161         if (end - pos < alen) {
162                 wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow");
163                 return NULL;
164         }
165         os_free(data->id_server);
166         data->id_server = os_malloc(alen);
167         if (data->id_server == NULL) {
168                 wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
169                 return NULL;
170         }
171         os_memcpy(data->id_server, pos, alen);
172         data->id_server_len = alen;
173         wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
174                           data->id_server, data->id_server_len);
175         pos += alen;
176
177         if (end - pos < EAP_GPSK_RAND_LEN) {
178                 wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow");
179                 return NULL;
180         }
181         os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN);
182         wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server",
183                     data->rand_server, EAP_GPSK_RAND_LEN);
184         pos += EAP_GPSK_RAND_LEN;
185
186         if (end - pos < 2) {
187                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
188                 return NULL;
189         }
190         csuite_list_len = WPA_GET_BE16(pos);
191         pos += 2;
192         if (end - pos < (int) csuite_list_len) {
193                 wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
194                 return NULL;
195         }
196         csuite_list = pos;
197
198         if (csuite_list_len == 0 ||
199             csuite_list_len % sizeof(struct eap_gpsk_csuite)) {
200                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %d",
201                            csuite_list_len);
202                 return NULL;
203         }
204         count = csuite_list_len / sizeof(struct eap_gpsk_csuite);
205         data->vendor = EAP_GPSK_VENDOR_IETF;
206         data->specifier = EAP_GPSK_CIPHER_RESERVED;
207         csuite = (struct eap_gpsk_csuite *) csuite_list;
208         for (i = 0; i < count; i++) {
209                 int vendor, specifier;
210                 vendor = WPA_GET_BE24(csuite->vendor);
211                 specifier = WPA_GET_BE24(csuite->specifier);
212                 wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d",
213                            i, vendor, specifier);
214                 if (data->vendor == EAP_GPSK_VENDOR_IETF &&
215                     data->specifier == EAP_GPSK_CIPHER_RESERVED &&
216                     eap_gpsk_supported_ciphersuite(vendor, specifier)) {
217                         data->vendor = vendor;
218                         data->specifier = specifier;
219                 }
220                 csuite++;
221         }
222         if (data->vendor == EAP_GPSK_VENDOR_IETF &&
223             data->specifier == EAP_GPSK_CIPHER_RESERVED) {
224                 wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported "
225                         "ciphersuite found");
226                 eap_gpsk_state(data, FAILURE);
227                 return NULL;
228         }
229         wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d",
230                    data->vendor, data->specifier);
231
232         wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2");
233
234         miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
235         len = 1 + 2 + data->id_client_len + 2 + data->id_server_len +
236                 2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len +
237                 sizeof(struct eap_gpsk_csuite) + 2 + miclen;
238
239         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respDataLen, len,
240                              EAP_CODE_RESPONSE, req->identifier, &rpos);
241         if (resp == NULL)
242                 return NULL;
243
244         *rpos++ = EAP_GPSK_OPCODE_GPSK_2;
245         start = rpos;
246
247         wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Client",
248                           data->id_client, data->id_client_len);
249         WPA_PUT_BE16(rpos, data->id_client_len);
250         rpos += 2;
251         if (data->id_client)
252                 os_memcpy(rpos, data->id_client, data->id_client_len);
253         rpos += data->id_client_len;
254
255         WPA_PUT_BE16(rpos, data->id_server_len);
256         rpos += 2;
257         if (data->id_server)
258                 os_memcpy(rpos, data->id_server, data->id_server_len);
259         rpos += data->id_server_len;
260
261         if (os_get_random(data->rand_client, EAP_GPSK_RAND_LEN)) {
262                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
263                            "for RAND_Client");
264                 eap_gpsk_state(data, FAILURE);
265                 os_free(resp);
266                 return NULL;
267         }
268         wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Client",
269                     data->rand_client, EAP_GPSK_RAND_LEN);
270         os_memcpy(rpos, data->rand_client, EAP_GPSK_RAND_LEN);
271         rpos += EAP_GPSK_RAND_LEN;
272
273         os_memcpy(rpos, data->rand_server, EAP_GPSK_RAND_LEN);
274         rpos += EAP_GPSK_RAND_LEN;
275
276         WPA_PUT_BE16(rpos, csuite_list_len);
277         rpos += 2;
278         os_memcpy(rpos, csuite_list, csuite_list_len);
279         rpos += csuite_list_len;
280
281         csuite = (struct eap_gpsk_csuite *) rpos;
282         WPA_PUT_BE24(csuite->vendor, data->vendor);
283         WPA_PUT_BE24(csuite->specifier, data->specifier);
284         rpos = (u8 *) (csuite + 1);
285
286         if (eap_gpsk_derive_keys(data->psk, data->psk_len,
287                                  data->vendor, data->specifier,
288                                  data->rand_client, data->rand_server,
289                                  data->id_client, data->id_client_len,
290                                  data->id_server, data->id_server_len,
291                                  data->msk, data->emsk,
292                                  data->sk, &data->sk_len,
293                                  data->pk, &data->pk_len) < 0) {
294                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
295                 eap_gpsk_state(data, FAILURE);
296                 os_free(resp);
297                 return NULL;
298         }
299
300         /* No PD_Payload_1 */
301         WPA_PUT_BE16(rpos, 0);
302         rpos += 2;
303
304         if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
305                                  data->specifier, start, rpos - start, rpos) <
306             0) {
307                 eap_gpsk_state(data, FAILURE);
308                 os_free(resp);
309                 return NULL;
310         }
311
312         eap_gpsk_state(data, GPSK_3);
313
314         return (u8 *) resp;
315 }
316
317
318 static u8 * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
319                                     struct eap_gpsk_data *data,
320                                     struct eap_method_ret *ret,
321                                     const u8 *reqData, size_t reqDataLen,
322                                     const u8 *payload, size_t payload_len,
323                                     size_t *respDataLen)
324 {
325         size_t len, miclen;
326         struct eap_hdr *resp;
327         u8 *rpos, *start;
328         const struct eap_hdr *req;
329         const u8 *pos, *end;
330         u16 alen;
331         int vendor, specifier;
332         const struct eap_gpsk_csuite *csuite;
333         u8 mic[EAP_GPSK_MAX_MIC_LEN];
334
335         if (data->state != GPSK_3) {
336                 ret->ignore = TRUE;
337                 return NULL;
338         }
339
340         wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3");
341
342         req = (const struct eap_hdr *) reqData;
343         pos = payload;
344         end = payload + payload_len;
345
346         if (end - pos < EAP_GPSK_RAND_LEN) {
347                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
348                            "RAND_Client");
349                 return NULL;
350         }
351         if (os_memcmp(pos, data->rand_client, EAP_GPSK_RAND_LEN) != 0) {
352                 wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Client in GPSK-2 and "
353                            "GPSK-3 did not match");
354                 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Client in GPSK-2",
355                             data->rand_client, EAP_GPSK_RAND_LEN);
356                 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Client in GPSK-3",
357                             pos, EAP_GPSK_RAND_LEN);
358                 eap_gpsk_state(data, FAILURE);
359                 return NULL;
360         }
361         pos += EAP_GPSK_RAND_LEN;
362
363         if (end - pos < EAP_GPSK_RAND_LEN) {
364                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
365                            "RAND_Server");
366                 return NULL;
367         }
368         if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) {
369                 wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
370                            "GPSK-3 did not match");
371                 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
372                             data->rand_server, EAP_GPSK_RAND_LEN);
373                 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3",
374                             pos, EAP_GPSK_RAND_LEN);
375                 eap_gpsk_state(data, FAILURE);
376                 return NULL;
377         }
378         pos += EAP_GPSK_RAND_LEN;
379
380         if (end - pos < (int) sizeof(*csuite)) {
381                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
382                            "CSuite_Sel");
383                 return NULL;
384         }
385         csuite = (const struct eap_gpsk_csuite *) pos;
386         vendor = WPA_GET_BE24(csuite->vendor);
387         specifier = WPA_GET_BE24(csuite->specifier);
388         pos += sizeof(*csuite);
389         if (vendor != data->vendor || specifier != data->specifier) {
390                 wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not "
391                            "match with the one sent in GPSK-2 (%d:%d)",
392                            vendor, specifier, data->vendor, data->specifier);
393                 eap_gpsk_state(data, FAILURE);
394                 return NULL;
395         }
396
397         if (end - pos < 2) {
398                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
399                            "PD_Payload_2 length");
400                 eap_gpsk_state(data, FAILURE);
401                 return NULL;
402         }
403         alen = WPA_GET_BE16(pos);
404         pos += 2;
405         if (end - pos < alen) {
406                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
407                            "%d-octet PD_Payload_2", alen);
408                 eap_gpsk_state(data, FAILURE);
409                 return NULL;
410         }
411         wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen);
412         pos += alen;
413         miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
414         if (end - pos < (int) miclen) {
415                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
416                            "(left=%d miclen=%d)", end - pos, miclen);
417                 eap_gpsk_state(data, FAILURE);
418                 return NULL;
419         }
420         if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
421                                  data->specifier, payload, pos - payload, mic)
422             < 0) {
423                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
424                 eap_gpsk_state(data, FAILURE);
425                 return NULL;
426         }
427         if (os_memcmp(mic, pos, miclen) != 0) {
428                 wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3");
429                 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
430                 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
431                 eap_gpsk_state(data, FAILURE);
432                 return NULL;
433         }
434         pos += miclen;
435
436         if (pos != end) {
437                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %d bytes of extra "
438                            "data in the end of GPSK-2", end - pos);
439         }
440
441         wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4");
442
443         len = 1 + 2 + miclen;
444
445         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respDataLen, len,
446                              EAP_CODE_RESPONSE, req->identifier, &rpos);
447         if (resp == NULL)
448                 return NULL;
449
450         *rpos++ = EAP_GPSK_OPCODE_GPSK_4;
451         start = rpos;
452
453         /* No PD_Payload_3 */
454         WPA_PUT_BE16(rpos, 0);
455         rpos += 2;
456
457         if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
458                                  data->specifier, start, rpos - start, rpos) <
459             0) {
460                 eap_gpsk_state(data, FAILURE);
461                 os_free(resp);
462                 return NULL;
463         }
464
465         eap_gpsk_state(data, SUCCESS);
466         ret->methodState = METHOD_DONE;
467         ret->decision = DECISION_UNCOND_SUCC;
468
469         return (u8 *) resp;
470 }
471
472
473 static u8 * eap_gpsk_process(struct eap_sm *sm, void *priv,
474                             struct eap_method_ret *ret,
475                             const u8 *reqData, size_t reqDataLen,
476                             size_t *respDataLen)
477 {
478         struct eap_gpsk_data *data = priv;
479         u8 *resp;
480         const u8 *pos;
481         size_t len;
482
483         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK,
484                                reqData, reqDataLen, &len);
485         if (pos == NULL || len < 1) {
486                 ret->ignore = TRUE;
487                 return NULL;
488         }
489
490         wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos);
491
492         ret->ignore = FALSE;
493         ret->methodState = METHOD_MAY_CONT;
494         ret->decision = DECISION_FAIL;
495         ret->allowNotifications = FALSE;
496
497         switch (*pos) {
498         case EAP_GPSK_OPCODE_GPSK_1:
499                 resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData,
500                                                reqDataLen, pos + 1, len - 1,
501                                                respDataLen);
502                 break;
503         case EAP_GPSK_OPCODE_GPSK_3:
504                 resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData,
505                                                reqDataLen, pos + 1, len - 1,
506                                                respDataLen);
507                 break;
508         default:
509                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with "
510                            "unknown opcode %d", *pos);
511                 ret->ignore = TRUE;
512                 return NULL;
513         }
514
515         return resp;
516 }
517
518
519 static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv)
520 {
521         struct eap_gpsk_data *data = priv;
522         return data->state == SUCCESS;
523 }
524
525
526 static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
527 {
528         struct eap_gpsk_data *data = priv;
529         u8 *key;
530
531         if (data->state != SUCCESS)
532                 return NULL;
533
534         key = os_malloc(EAP_MSK_LEN);
535         if (key == NULL)
536                 return NULL;
537         os_memcpy(key, data->msk, EAP_MSK_LEN);
538         *len = EAP_MSK_LEN;
539
540         return key;
541 }
542
543
544 static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
545 {
546         struct eap_gpsk_data *data = priv;
547         u8 *key;
548
549         if (data->state != SUCCESS)
550                 return NULL;
551
552         key = os_malloc(EAP_EMSK_LEN);
553         if (key == NULL)
554                 return NULL;
555         os_memcpy(key, data->emsk, EAP_EMSK_LEN);
556         *len = EAP_EMSK_LEN;
557
558         return key;
559 }
560
561
562 int eap_peer_gpsk_register(void)
563 {
564         struct eap_method *eap;
565         int ret;
566
567         eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
568                                     EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
569         if (eap == NULL)
570                 return -1;
571
572         eap->init = eap_gpsk_init;
573         eap->deinit = eap_gpsk_deinit;
574         eap->process = eap_gpsk_process;
575         eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
576         eap->getKey = eap_gpsk_getKey;
577         eap->get_emsk = eap_gpsk_get_emsk;
578
579         ret = eap_peer_method_register(eap);
580         if (ret)
581                 eap_peer_method_free(eap);
582         return ret;
583 }