Change the kernel dev_t, representing a pointer to a specinfo structure,
[dragonfly.git] / contrib / wpa_supplicant-0.4.9 / eap_leap.c
1 /*
2  * WPA Supplicant / EAP-LEAP
3  * Copyright (c) 2004-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
19 #include "common.h"
20 #include "eap_i.h"
21 #include "wpa_supplicant.h"
22 #include "config_ssid.h"
23 #include "ms_funcs.h"
24 #include "crypto.h"
25
26 #define LEAP_VERSION 1
27 #define LEAP_CHALLENGE_LEN 8
28 #define LEAP_RESPONSE_LEN 24
29 #define LEAP_KEY_LEN 16
30
31
32 struct eap_leap_data {
33         enum {
34                 LEAP_WAIT_CHALLENGE,
35                 LEAP_WAIT_SUCCESS,
36                 LEAP_WAIT_RESPONSE,
37                 LEAP_DONE
38         } state;
39
40         u8 peer_challenge[LEAP_CHALLENGE_LEN];
41         u8 peer_response[LEAP_RESPONSE_LEN];
42
43         u8 ap_challenge[LEAP_CHALLENGE_LEN];
44         u8 ap_response[LEAP_RESPONSE_LEN];
45 };
46
47
48 static void * eap_leap_init(struct eap_sm *sm)
49 {
50         struct eap_leap_data *data;
51
52         data = malloc(sizeof(*data));
53         if (data == NULL)
54                 return NULL;
55         memset(data, 0, sizeof(*data));
56         data->state = LEAP_WAIT_CHALLENGE;
57
58         sm->leap_done = FALSE;
59         return data;
60 }
61
62
63 static void eap_leap_deinit(struct eap_sm *sm, void *priv)
64 {
65         free(priv);
66 }
67
68
69 static u8 * eap_leap_process_request(struct eap_sm *sm, void *priv,
70                                      struct eap_method_ret *ret,
71                                      const u8 *reqData, size_t reqDataLen,
72                                      size_t *respDataLen)
73 {
74         struct eap_leap_data *data = priv;
75         struct wpa_ssid *config = eap_get_config(sm);
76         const struct eap_hdr *req;
77         struct eap_hdr *resp;
78         const u8 *pos, *challenge;
79         u8 challenge_len, *rpos;
80
81         wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request");
82
83         req = (const struct eap_hdr *) reqData;
84         pos = (const u8 *) (req + 1);
85         if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_LEAP) {
86                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame");
87                 ret->ignore = TRUE;
88                 return NULL;
89         }
90         pos++;
91
92         if (*pos != LEAP_VERSION) {
93                 wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
94                            "%d", *pos);
95                 ret->ignore = TRUE;
96                 return NULL;
97         }
98         pos++;
99
100         pos++; /* skip unused byte */
101
102         challenge_len = *pos++;
103         if (challenge_len != LEAP_CHALLENGE_LEN ||
104             challenge_len > reqDataLen - sizeof(*req) - 4) {
105                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge "
106                            "(challenge_len=%d reqDataLen=%lu)",
107                            challenge_len, (unsigned long) reqDataLen);
108                 ret->ignore = TRUE;
109                 return NULL;
110         }
111         challenge = pos;
112         memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN);
113         wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP",
114                     challenge, LEAP_CHALLENGE_LEN);
115
116         wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response");
117
118         *respDataLen = sizeof(struct eap_hdr) + 1 + 3 + LEAP_RESPONSE_LEN +
119                 config->identity_len;
120         resp = malloc(*respDataLen);
121         if (resp == NULL)
122                 return NULL;
123         resp->code = EAP_CODE_RESPONSE;
124         resp->identifier = req->identifier;
125         resp->length = host_to_be16(*respDataLen);
126         rpos = (u8 *) (resp + 1);
127         *rpos++ = EAP_TYPE_LEAP;
128         *rpos++ = LEAP_VERSION;
129         *rpos++ = 0; /* unused */
130         *rpos++ = LEAP_RESPONSE_LEN;
131         nt_challenge_response(challenge,
132                               config->password, config->password_len, rpos);
133         memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
134         wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
135                     rpos, LEAP_RESPONSE_LEN);
136         rpos += LEAP_RESPONSE_LEN;
137         memcpy(rpos, config->identity, config->identity_len);
138
139         data->state = LEAP_WAIT_SUCCESS;
140
141         return (u8 *) resp;
142 }
143
144
145 static u8 * eap_leap_process_success(struct eap_sm *sm, void *priv,
146                                      struct eap_method_ret *ret,
147                                      const u8 *reqData, size_t reqDataLen,
148                                      size_t *respDataLen)
149 {
150         struct eap_leap_data *data = priv;
151         struct wpa_ssid *config = eap_get_config(sm);
152         const struct eap_hdr *req;
153         struct eap_hdr *resp;
154         u8 *pos;
155
156         wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
157
158         if (data->state != LEAP_WAIT_SUCCESS) {
159                 wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
160                            "unexpected state (%d) - ignored", data->state);
161                 ret->ignore = TRUE;
162                 return NULL;
163         }
164
165         req = (const struct eap_hdr *) reqData;
166
167         *respDataLen = sizeof(struct eap_hdr) + 1 + 3 + LEAP_CHALLENGE_LEN +
168                 config->identity_len;
169         resp = malloc(*respDataLen);
170         if (resp == NULL)
171                 return NULL;
172         resp->code = EAP_CODE_REQUEST;
173         resp->identifier = req->identifier;
174         resp->length = host_to_be16(*respDataLen);
175         pos = (u8 *) (resp + 1);
176         *pos++ = EAP_TYPE_LEAP;
177         *pos++ = LEAP_VERSION;
178         *pos++ = 0; /* unused */
179         *pos++ = LEAP_CHALLENGE_LEN;
180         if (hostapd_get_rand(pos, LEAP_CHALLENGE_LEN)) {
181                 wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
182                            "for challenge");
183                 free(resp);
184                 ret->ignore = TRUE;
185                 return NULL;
186         }
187         memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
188         wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
189                     LEAP_CHALLENGE_LEN);
190         pos += LEAP_CHALLENGE_LEN;
191         memcpy(pos, config->identity, config->identity_len);
192
193         data->state = LEAP_WAIT_RESPONSE;
194
195         return (u8 *) resp;
196 }
197
198
199 static u8 * eap_leap_process_response(struct eap_sm *sm, void *priv,
200                                       struct eap_method_ret *ret,
201                                       const u8 *reqData, size_t reqDataLen,
202                                       size_t *respDataLen)
203 {
204         struct eap_leap_data *data = priv;
205         struct wpa_ssid *config = eap_get_config(sm);
206         const struct eap_hdr *resp;
207         const u8 *pos;
208         u8 response_len, pw_hash[16], pw_hash_hash[16],
209                 expected[LEAP_RESPONSE_LEN];
210
211         wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
212
213         resp = (const struct eap_hdr *) reqData;
214         pos = (const u8 *) (resp + 1);
215         if (reqDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_LEAP) {
216                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
217                 ret->ignore = TRUE;
218                 return NULL;
219         }
220         pos++;
221
222         if (*pos != LEAP_VERSION) {
223                 wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
224                            "%d", *pos);
225                 ret->ignore = TRUE;
226                 return NULL;
227         }
228         pos++;
229
230         pos++; /* skip unused byte */
231
232         response_len = *pos++;
233         if (response_len != LEAP_RESPONSE_LEN ||
234             response_len > reqDataLen - sizeof(*resp) - 4) {
235                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
236                            "(response_len=%d reqDataLen=%lu)",
237                            response_len, (unsigned long) reqDataLen);
238                 ret->ignore = TRUE;
239                 return NULL;
240         }
241
242         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
243                     pos, LEAP_RESPONSE_LEN);
244         memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
245
246         nt_password_hash(config->password, config->password_len, pw_hash);
247         hash_nt_password_hash(pw_hash, pw_hash_hash);
248         challenge_response(data->ap_challenge, pw_hash_hash, expected);
249
250         ret->methodState = METHOD_DONE;
251         ret->allowNotifications = FALSE;
252
253         if (memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
254                 wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
255                            "response - authentication failed");
256                 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
257                             expected, LEAP_RESPONSE_LEN);
258                 ret->decision = DECISION_FAIL;
259                 return NULL;
260         }
261
262         ret->decision = DECISION_UNCOND_SUCC;
263
264         /* LEAP is somewhat odd method since it sends EAP-Success in the middle
265          * of the authentication. Use special variable to transit EAP state
266          * machine to SUCCESS state. */
267         sm->leap_done = TRUE;
268         data->state = LEAP_DONE;
269
270         /* No more authentication messages expected; AP will send EAPOL-Key
271          * frames if encryption is enabled. */
272         return NULL;
273 }
274
275
276 static u8 * eap_leap_process(struct eap_sm *sm, void *priv,
277                              struct eap_method_ret *ret,
278                              const u8 *reqData, size_t reqDataLen,
279                              size_t *respDataLen)
280 {
281         struct wpa_ssid *config = eap_get_config(sm);
282         const struct eap_hdr *eap;
283         size_t len;
284
285         if (config == NULL || config->password == NULL) {
286                 wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
287                 eap_sm_request_password(sm, config);
288                 ret->ignore = TRUE;
289                 return NULL;
290         }
291
292         eap = (const struct eap_hdr *) reqData;
293
294         if (reqDataLen < sizeof(*eap) ||
295             (len = be_to_host16(eap->length)) > reqDataLen) {
296                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
297                 ret->ignore = TRUE;
298                 return NULL;
299         }
300
301         ret->ignore = FALSE;
302         ret->allowNotifications = TRUE;
303         ret->methodState = METHOD_MAY_CONT;
304         ret->decision = DECISION_FAIL;
305
306         sm->leap_done = FALSE;
307
308         switch (eap->code) {
309         case EAP_CODE_REQUEST:
310                 return eap_leap_process_request(sm, priv, ret, reqData, len,
311                                                 respDataLen);
312         case EAP_CODE_SUCCESS:
313                 return eap_leap_process_success(sm, priv, ret, reqData, len,
314                                                 respDataLen);
315         case EAP_CODE_RESPONSE:
316                 return eap_leap_process_response(sm, priv, ret, reqData, len,
317                                                  respDataLen);
318         default:
319                 wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
320                            "ignored", eap->code);
321                 ret->ignore = TRUE;
322                 return NULL;
323         }
324 }
325
326
327 static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
328 {
329         struct eap_leap_data *data = priv;
330         return data->state == LEAP_DONE;
331 }
332
333
334 static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
335 {
336         struct eap_leap_data *data = priv;
337         struct wpa_ssid *config = eap_get_config(sm);
338         u8 *key, pw_hash_hash[16], pw_hash[16];
339         const u8 *addr[5];
340         size_t elen[5];
341
342         if (data->state != LEAP_DONE)
343                 return NULL;
344
345         key = malloc(LEAP_KEY_LEN);
346         if (key == NULL)
347                 return NULL;
348
349         nt_password_hash(config->password, config->password_len, pw_hash);
350         hash_nt_password_hash(pw_hash, pw_hash_hash);
351         wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
352                         pw_hash_hash, 16);
353         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
354                     data->peer_challenge, LEAP_CHALLENGE_LEN);
355         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
356                     data->peer_response, LEAP_RESPONSE_LEN);
357         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
358                     data->ap_challenge, LEAP_CHALLENGE_LEN);
359         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
360                     data->ap_response, LEAP_RESPONSE_LEN);
361
362         addr[0] = pw_hash_hash;
363         elen[0] = 16;
364         addr[1] = data->ap_challenge;
365         elen[1] = LEAP_CHALLENGE_LEN;
366         addr[2] = data->ap_response;
367         elen[2] = LEAP_RESPONSE_LEN;
368         addr[3] = data->peer_challenge;
369         elen[3] = LEAP_CHALLENGE_LEN;
370         addr[4] = data->peer_response;
371         elen[4] = LEAP_RESPONSE_LEN;
372         md5_vector(5, addr, elen, key);
373         wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
374         *len = LEAP_KEY_LEN;
375
376         return key;
377 }
378
379
380 const struct eap_method eap_method_leap =
381 {
382         .method = EAP_TYPE_LEAP,
383         .name = "LEAP",
384         .init = eap_leap_init,
385         .deinit = eap_leap_deinit,
386         .process = eap_leap_process,
387         .isKeyAvailable = eap_leap_isKeyAvailable,
388         .getKey = eap_leap_getKey,
389 };