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