hostapd vendor branch: Update version from 0.6.10 => 2.1
[dragonfly.git] / contrib / hostapd / 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)
119                 challenge_response(challenge, password, rpos);
120         else
121                 nt_challenge_response(challenge, password, password_len, rpos);
122         os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
123         wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
124                     rpos, LEAP_RESPONSE_LEN);
125         wpabuf_put_data(resp, identity, identity_len);
126
127         data->state = LEAP_WAIT_SUCCESS;
128
129         return resp;
130 }
131
132
133 static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
134                                                 struct eap_method_ret *ret,
135                                                 const struct wpabuf *reqData)
136 {
137         struct eap_leap_data *data = priv;
138         struct wpabuf *resp;
139         u8 *pos;
140         const u8 *identity;
141         size_t identity_len;
142
143         wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
144
145         identity = eap_get_config_identity(sm, &identity_len);
146         if (identity == NULL)
147                 return NULL;
148
149         if (data->state != LEAP_WAIT_SUCCESS) {
150                 wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
151                            "unexpected state (%d) - ignored", data->state);
152                 ret->ignore = TRUE;
153                 return NULL;
154         }
155
156         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
157                              3 + LEAP_CHALLENGE_LEN + identity_len,
158                              EAP_CODE_REQUEST, eap_get_id(reqData));
159         if (resp == NULL)
160                 return NULL;
161         wpabuf_put_u8(resp, LEAP_VERSION);
162         wpabuf_put_u8(resp, 0); /* unused */
163         wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
164         pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
165         if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
166                 wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
167                            "for challenge");
168                 wpabuf_free(resp);
169                 ret->ignore = TRUE;
170                 return NULL;
171         }
172         os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
173         wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
174                     LEAP_CHALLENGE_LEN);
175         wpabuf_put_data(resp, identity, identity_len);
176
177         data->state = LEAP_WAIT_RESPONSE;
178
179         return resp;
180 }
181
182
183 static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
184                                                  struct eap_method_ret *ret,
185                                                  const struct wpabuf *reqData)
186 {
187         struct eap_leap_data *data = priv;
188         const u8 *pos, *password;
189         u8 response_len, pw_hash[16], pw_hash_hash[16],
190                 expected[LEAP_RESPONSE_LEN];
191         size_t password_len, len;
192         int pwhash;
193
194         wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
195
196         password = eap_get_config_password2(sm, &password_len, &pwhash);
197         if (password == NULL)
198                 return NULL;
199
200         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
201         if (pos == NULL || len < 3) {
202                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
203                 ret->ignore = TRUE;
204                 return NULL;
205         }
206
207         if (*pos != LEAP_VERSION) {
208                 wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
209                            "%d", *pos);
210                 ret->ignore = TRUE;
211                 return NULL;
212         }
213         pos++;
214
215         pos++; /* skip unused byte */
216
217         response_len = *pos++;
218         if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) {
219                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
220                            "(response_len=%d reqDataLen=%lu)",
221                            response_len, (unsigned long) wpabuf_len(reqData));
222                 ret->ignore = TRUE;
223                 return NULL;
224         }
225
226         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
227                     pos, LEAP_RESPONSE_LEN);
228         os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
229
230         if (pwhash) {
231                 if (hash_nt_password_hash(password, pw_hash_hash)) {
232                         ret->ignore = TRUE;
233                         return NULL;
234                 }
235         } else {
236                 if (nt_password_hash(password, password_len, pw_hash) ||
237                     hash_nt_password_hash(pw_hash, pw_hash_hash)) {
238                         ret->ignore = TRUE;
239                         return NULL;
240                 }
241         }
242         challenge_response(data->ap_challenge, pw_hash_hash, expected);
243
244         ret->methodState = METHOD_DONE;
245         ret->allowNotifications = FALSE;
246
247         if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
248                 wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
249                            "response - authentication failed");
250                 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
251                             expected, LEAP_RESPONSE_LEN);
252                 ret->decision = DECISION_FAIL;
253                 return NULL;
254         }
255
256         ret->decision = DECISION_UNCOND_SUCC;
257
258         /* LEAP is somewhat odd method since it sends EAP-Success in the middle
259          * of the authentication. Use special variable to transit EAP state
260          * machine to SUCCESS state. */
261         sm->leap_done = TRUE;
262         data->state = LEAP_DONE;
263
264         /* No more authentication messages expected; AP will send EAPOL-Key
265          * frames if encryption is enabled. */
266         return NULL;
267 }
268
269
270 static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv,
271                                         struct eap_method_ret *ret,
272                                         const struct wpabuf *reqData)
273 {
274         const struct eap_hdr *eap;
275         size_t password_len;
276         const u8 *password;
277
278         password = eap_get_config_password(sm, &password_len);
279         if (password == NULL) {
280                 wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
281                 eap_sm_request_password(sm);
282                 ret->ignore = TRUE;
283                 return NULL;
284         }
285
286         /*
287          * LEAP needs to be able to handle EAP-Success frame which does not
288          * include Type field. Consequently, eap_hdr_validate() cannot be used
289          * here. This validation will be done separately for EAP-Request and
290          * EAP-Response frames.
291          */
292         eap = wpabuf_head(reqData);
293         if (wpabuf_len(reqData) < sizeof(*eap) ||
294             be_to_host16(eap->length) > wpabuf_len(reqData)) {
295                 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
296                 ret->ignore = TRUE;
297                 return NULL;
298         }
299
300         ret->ignore = FALSE;
301         ret->allowNotifications = TRUE;
302         ret->methodState = METHOD_MAY_CONT;
303         ret->decision = DECISION_FAIL;
304
305         sm->leap_done = FALSE;
306
307         switch (eap->code) {
308         case EAP_CODE_REQUEST:
309                 return eap_leap_process_request(sm, priv, ret, reqData);
310         case EAP_CODE_SUCCESS:
311                 return eap_leap_process_success(sm, priv, ret, reqData);
312         case EAP_CODE_RESPONSE:
313                 return eap_leap_process_response(sm, priv, ret, reqData);
314         default:
315                 wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
316                            "ignored", eap->code);
317                 ret->ignore = TRUE;
318                 return NULL;
319         }
320 }
321
322
323 static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
324 {
325         struct eap_leap_data *data = priv;
326         return data->state == LEAP_DONE;
327 }
328
329
330 static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
331 {
332         struct eap_leap_data *data = priv;
333         u8 *key, pw_hash_hash[16], pw_hash[16];
334         const u8 *addr[5], *password;
335         size_t elen[5], password_len;
336         int pwhash;
337
338         if (data->state != LEAP_DONE)
339                 return NULL;
340
341         password = eap_get_config_password2(sm, &password_len, &pwhash);
342         if (password == NULL)
343                 return NULL;
344
345         key = os_malloc(LEAP_KEY_LEN);
346         if (key == NULL)
347                 return NULL;
348
349         if (pwhash) {
350                 if (hash_nt_password_hash(password, pw_hash_hash)) {
351                         os_free(key);
352                         return NULL;
353                 }
354         } else {
355                 if (nt_password_hash(password, password_len, pw_hash) ||
356                     hash_nt_password_hash(pw_hash, pw_hash_hash)) {
357                         os_free(key);
358                         return NULL;
359                 }
360         }
361         wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
362                         pw_hash_hash, 16);
363         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
364                     data->peer_challenge, LEAP_CHALLENGE_LEN);
365         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
366                     data->peer_response, LEAP_RESPONSE_LEN);
367         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
368                     data->ap_challenge, LEAP_CHALLENGE_LEN);
369         wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
370                     data->ap_response, LEAP_RESPONSE_LEN);
371
372         addr[0] = pw_hash_hash;
373         elen[0] = 16;
374         addr[1] = data->ap_challenge;
375         elen[1] = LEAP_CHALLENGE_LEN;
376         addr[2] = data->ap_response;
377         elen[2] = LEAP_RESPONSE_LEN;
378         addr[3] = data->peer_challenge;
379         elen[3] = LEAP_CHALLENGE_LEN;
380         addr[4] = data->peer_response;
381         elen[4] = LEAP_RESPONSE_LEN;
382         md5_vector(5, addr, elen, key);
383         wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
384         *len = LEAP_KEY_LEN;
385
386         return key;
387 }
388
389
390 int eap_peer_leap_register(void)
391 {
392         struct eap_method *eap;
393         int ret;
394
395         eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
396                                     EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP");
397         if (eap == NULL)
398                 return -1;
399
400         eap->init = eap_leap_init;
401         eap->deinit = eap_leap_deinit;
402         eap->process = eap_leap_process;
403         eap->isKeyAvailable = eap_leap_isKeyAvailable;
404         eap->getKey = eap_leap_getKey;
405
406         ret = eap_peer_method_register(eap);
407         if (ret)
408                 eap_peer_method_free(eap);
409         return ret;
410 }