]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/wpa/src/eap_peer/eap_leap.c
Update hostapd/wpa_supplicant to 2.8 to fix multiple vulnerabilities.
[FreeBSD/FreeBSD.git] / contrib / wpa / src / eap_peer / eap_leap.c
1 /*
2  * EAP peer method: LEAP
3  * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8
9 #include "includes.h"
10
11 #include "common.h"
12 #include "crypto/ms_funcs.h"
13 #include "crypto/crypto.h"
14 #include "crypto/random.h"
15 #include "eap_i.h"
16
17 #define LEAP_VERSION 1
18 #define LEAP_CHALLENGE_LEN 8
19 #define LEAP_RESPONSE_LEN 24
20 #define LEAP_KEY_LEN 16
21
22
23 struct eap_leap_data {
24         enum {
25                 LEAP_WAIT_CHALLENGE,
26                 LEAP_WAIT_SUCCESS,
27                 LEAP_WAIT_RESPONSE,
28                 LEAP_DONE
29         } state;
30
31         u8 peer_challenge[LEAP_CHALLENGE_LEN];
32         u8 peer_response[LEAP_RESPONSE_LEN];
33
34         u8 ap_challenge[LEAP_CHALLENGE_LEN];
35         u8 ap_response[LEAP_RESPONSE_LEN];
36 };
37
38
39 static void * eap_leap_init(struct eap_sm *sm)
40 {
41         struct eap_leap_data *data;
42
43         data = os_zalloc(sizeof(*data));
44         if (data == NULL)
45                 return NULL;
46         data->state = LEAP_WAIT_CHALLENGE;
47
48         sm->leap_done = FALSE;
49         return data;
50 }
51
52
53 static void eap_leap_deinit(struct eap_sm *sm, void *priv)
54 {
55         os_free(priv);
56 }
57
58
59 static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv,
60                                                 struct eap_method_ret *ret,
61                                                 const struct wpabuf *reqData)
62 {
63         struct eap_leap_data *data = priv;
64         struct wpabuf *resp;
65         const u8 *pos, *challenge, *identity, *password;
66         u8 challenge_len, *rpos;
67         size_t identity_len, password_len, len;
68         int pwhash;
69
70         wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request");
71
72         identity = eap_get_config_identity(sm, &identity_len);
73         password = eap_get_config_password2(sm, &password_len, &pwhash);
74         if (identity == NULL || password == NULL)
75                 return NULL;
76
77         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
78         if (pos == NULL || len < 3) {
79                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame");
80                 ret->ignore = TRUE;
81                 return NULL;
82         }
83
84         if (*pos != LEAP_VERSION) {
85                 wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
86                            "%d", *pos);
87                 ret->ignore = TRUE;
88                 return NULL;
89         }
90         pos++;
91
92         pos++; /* skip unused byte */
93
94         challenge_len = *pos++;
95         if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) {
96                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge "
97                            "(challenge_len=%d reqDataLen=%lu)",
98                            challenge_len, (unsigned long) wpabuf_len(reqData));
99                 ret->ignore = TRUE;
100                 return NULL;
101         }
102         challenge = pos;
103         os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN);
104         wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP",
105                     challenge, LEAP_CHALLENGE_LEN);
106
107         wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response");
108
109         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
110                              3 + LEAP_RESPONSE_LEN + identity_len,
111                              EAP_CODE_RESPONSE, eap_get_id(reqData));
112         if (resp == NULL)
113                 return NULL;
114         wpabuf_put_u8(resp, LEAP_VERSION);
115         wpabuf_put_u8(resp, 0); /* unused */
116         wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
117         rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
118         if ((pwhash && challenge_response(challenge, password, rpos)) ||
119             (!pwhash &&
120              nt_challenge_response(challenge, password, password_len, rpos))) {
121                 wpa_printf(MSG_DEBUG, "EAP-LEAP: Failed to derive response");
122                 ret->ignore = TRUE;
123                 wpabuf_free(resp);
124                 return NULL;
125         }
126         os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
127         wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
128                     rpos, LEAP_RESPONSE_LEN);
129         wpabuf_put_data(resp, identity, identity_len);
130
131         data->state = LEAP_WAIT_SUCCESS;
132
133         return resp;
134 }
135
136
137 static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
138                                                 struct eap_method_ret *ret,
139                                                 const struct wpabuf *reqData)
140 {
141         struct eap_leap_data *data = priv;
142         struct wpabuf *resp;
143         u8 *pos;
144         const u8 *identity;
145         size_t identity_len;
146
147         wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
148
149         identity = eap_get_config_identity(sm, &identity_len);
150         if (identity == NULL)
151                 return NULL;
152
153         if (data->state != LEAP_WAIT_SUCCESS) {
154                 wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
155                            "unexpected state (%d) - ignored", data->state);
156                 ret->ignore = TRUE;
157                 return NULL;
158         }
159
160         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
161                              3 + LEAP_CHALLENGE_LEN + identity_len,
162                              EAP_CODE_REQUEST, eap_get_id(reqData));
163         if (resp == NULL)
164                 return NULL;
165         wpabuf_put_u8(resp, LEAP_VERSION);
166         wpabuf_put_u8(resp, 0); /* unused */
167         wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
168         pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
169         if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
170                 wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
171                            "for challenge");
172                 wpabuf_free(resp);
173                 ret->ignore = TRUE;
174                 return NULL;
175         }
176         os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
177         wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
178                     LEAP_CHALLENGE_LEN);
179         wpabuf_put_data(resp, identity, identity_len);
180
181         data->state = LEAP_WAIT_RESPONSE;
182
183         return resp;
184 }
185
186
187 static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
188                                                  struct eap_method_ret *ret,
189                                                  const struct wpabuf *reqData)
190 {
191         struct eap_leap_data *data = priv;
192         const u8 *pos, *password;
193         u8 response_len, pw_hash[16], pw_hash_hash[16],
194                 expected[LEAP_RESPONSE_LEN];
195         size_t password_len, len;
196         int pwhash;
197
198         wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
199
200         password = eap_get_config_password2(sm, &password_len, &pwhash);
201         if (password == NULL)
202                 return NULL;
203
204         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
205         if (pos == NULL || len < 3) {
206                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
207                 ret->ignore = TRUE;
208                 return NULL;
209         }
210
211         if (*pos != LEAP_VERSION) {
212                 wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
213                            "%d", *pos);
214                 ret->ignore = TRUE;
215                 return NULL;
216         }
217         pos++;
218
219         pos++; /* skip unused byte */
220
221         response_len = *pos++;
222         if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) {
223                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
224                            "(response_len=%d reqDataLen=%lu)",
225                            response_len, (unsigned long) wpabuf_len(reqData));
226                 ret->ignore = TRUE;
227                 return NULL;
228         }
229
230         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
231                     pos, LEAP_RESPONSE_LEN);
232         os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
233
234         if (pwhash) {
235                 if (hash_nt_password_hash(password, pw_hash_hash)) {
236                         ret->ignore = TRUE;
237                         return NULL;
238                 }
239         } else {
240                 if (nt_password_hash(password, password_len, pw_hash) ||
241                     hash_nt_password_hash(pw_hash, pw_hash_hash)) {
242                         ret->ignore = TRUE;
243                         return NULL;
244                 }
245         }
246         if (challenge_response(data->ap_challenge, pw_hash_hash, expected)) {
247                 ret->ignore = TRUE;
248                 return NULL;
249         }
250
251         ret->methodState = METHOD_DONE;
252         ret->allowNotifications = FALSE;
253
254         if (os_memcmp_const(pos, expected, LEAP_RESPONSE_LEN) != 0) {
255                 wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
256                            "response - authentication failed");
257                 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
258                             expected, LEAP_RESPONSE_LEN);
259                 ret->decision = DECISION_FAIL;
260                 return NULL;
261         }
262
263         ret->decision = DECISION_UNCOND_SUCC;
264
265         /* LEAP is somewhat odd method since it sends EAP-Success in the middle
266          * of the authentication. Use special variable to transit EAP state
267          * machine to SUCCESS state. */
268         sm->leap_done = TRUE;
269         data->state = LEAP_DONE;
270
271         /* No more authentication messages expected; AP will send EAPOL-Key
272          * frames if encryption is enabled. */
273         return NULL;
274 }
275
276
277 static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv,
278                                         struct eap_method_ret *ret,
279                                         const struct wpabuf *reqData)
280 {
281         const struct eap_hdr *eap;
282         size_t password_len;
283         const u8 *password;
284
285         password = eap_get_config_password(sm, &password_len);
286         if (password == NULL) {
287                 wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
288                 eap_sm_request_password(sm);
289                 ret->ignore = TRUE;
290                 return NULL;
291         }
292
293         /*
294          * LEAP needs to be able to handle EAP-Success frame which does not
295          * include Type field. Consequently, eap_hdr_validate() cannot be used
296          * here. This validation will be done separately for EAP-Request and
297          * EAP-Response frames.
298          */
299         eap = wpabuf_head(reqData);
300         if (wpabuf_len(reqData) < sizeof(*eap) ||
301             be_to_host16(eap->length) > wpabuf_len(reqData)) {
302                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
303                 ret->ignore = TRUE;
304                 return NULL;
305         }
306
307         ret->ignore = FALSE;
308         ret->allowNotifications = TRUE;
309         ret->methodState = METHOD_MAY_CONT;
310         ret->decision = DECISION_FAIL;
311
312         sm->leap_done = FALSE;
313
314         switch (eap->code) {
315         case EAP_CODE_REQUEST:
316                 return eap_leap_process_request(sm, priv, ret, reqData);
317         case EAP_CODE_SUCCESS:
318                 return eap_leap_process_success(sm, priv, ret, reqData);
319         case EAP_CODE_RESPONSE:
320                 return eap_leap_process_response(sm, priv, ret, reqData);
321         default:
322                 wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
323                            "ignored", eap->code);
324                 ret->ignore = TRUE;
325                 return NULL;
326         }
327 }
328
329
330 static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
331 {
332         struct eap_leap_data *data = priv;
333         return data->state == LEAP_DONE;
334 }
335
336
337 static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
338 {
339         struct eap_leap_data *data = priv;
340         u8 *key, pw_hash_hash[16], pw_hash[16];
341         const u8 *addr[5], *password;
342         size_t elen[5], password_len;
343         int pwhash;
344
345         if (data->state != LEAP_DONE)
346                 return NULL;
347
348         password = eap_get_config_password2(sm, &password_len, &pwhash);
349         if (password == NULL)
350                 return NULL;
351
352         key = os_malloc(LEAP_KEY_LEN);
353         if (key == NULL)
354                 return NULL;
355
356         if (pwhash) {
357                 if (hash_nt_password_hash(password, pw_hash_hash)) {
358                         os_free(key);
359                         return NULL;
360                 }
361         } else {
362                 if (nt_password_hash(password, password_len, pw_hash) ||
363                     hash_nt_password_hash(pw_hash, pw_hash_hash)) {
364                         os_free(key);
365                         return NULL;
366                 }
367         }
368         wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
369                         pw_hash_hash, 16);
370         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
371                     data->peer_challenge, LEAP_CHALLENGE_LEN);
372         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
373                     data->peer_response, LEAP_RESPONSE_LEN);
374         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
375                     data->ap_challenge, LEAP_CHALLENGE_LEN);
376         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
377                     data->ap_response, LEAP_RESPONSE_LEN);
378
379         addr[0] = pw_hash_hash;
380         elen[0] = 16;
381         addr[1] = data->ap_challenge;
382         elen[1] = LEAP_CHALLENGE_LEN;
383         addr[2] = data->ap_response;
384         elen[2] = LEAP_RESPONSE_LEN;
385         addr[3] = data->peer_challenge;
386         elen[3] = LEAP_CHALLENGE_LEN;
387         addr[4] = data->peer_response;
388         elen[4] = LEAP_RESPONSE_LEN;
389         md5_vector(5, addr, elen, key);
390         wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
391         *len = LEAP_KEY_LEN;
392
393         os_memset(pw_hash, 0, sizeof(pw_hash));
394         os_memset(pw_hash_hash, 0, sizeof(pw_hash_hash));
395
396         return key;
397 }
398
399
400 int eap_peer_leap_register(void)
401 {
402         struct eap_method *eap;
403
404         eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
405                                     EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP");
406         if (eap == NULL)
407                 return -1;
408
409         eap->init = eap_leap_init;
410         eap->deinit = eap_leap_deinit;
411         eap->process = eap_leap_process;
412         eap->isKeyAvailable = eap_leap_isKeyAvailable;
413         eap->getKey = eap_leap_getKey;
414
415         return eap_peer_method_register(eap);
416 }