xref: /dragonfly/contrib/wpa_supplicant/src/eap_peer/eap_leap.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
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 
eap_leap_init(struct eap_sm * sm)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 
eap_leap_deinit(struct eap_sm * sm,void * priv)53 static void eap_leap_deinit(struct eap_sm *sm, void *priv)
54 {
55           os_free(priv);
56 }
57 
58 
eap_leap_process_request(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)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 
eap_leap_process_success(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)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 
eap_leap_process_response(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)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 
eap_leap_process(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)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 
eap_leap_isKeyAvailable(struct eap_sm * sm,void * priv)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 
eap_leap_getKey(struct eap_sm * sm,void * priv,size_t * len)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           forced_memzero(pw_hash, sizeof(pw_hash));
394           forced_memzero(pw_hash_hash, sizeof(pw_hash_hash));
395 
396           return key;
397 }
398 
399 
eap_peer_leap_register(void)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 }
417