Merge from vendor branch WPA_SUPPLICANT:
[dragonfly.git] / contrib / wpa_supplicant-0.4.9 / eap_mschapv2.c
1 /*
2  * WPA Supplicant / EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
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 "wpa_ctrl.h"
25
26
27 struct eap_mschapv2_hdr {
28         u8 code;
29         u8 identifier;
30         u16 length; /* including code, identifier, and length */
31         u8 type; /* EAP_TYPE_MSCHAPV2 */
32         u8 op_code; /* MSCHAPV2_OP_* */
33         u8 mschapv2_id; /* usually same as identifier */
34         u8 ms_length[2]; /* Note: misaligned; length - 5 */
35         /* followed by data */
36 } __attribute__ ((packed));
37
38 #define MSCHAPV2_OP_CHALLENGE 1
39 #define MSCHAPV2_OP_RESPONSE 2
40 #define MSCHAPV2_OP_SUCCESS 3
41 #define MSCHAPV2_OP_FAILURE 4
42 #define MSCHAPV2_OP_CHANGE_PASSWORD 7
43
44 #define MSCHAPV2_RESP_LEN 49
45
46 #define ERROR_RESTRICTED_LOGON_HOURS 646
47 #define ERROR_ACCT_DISABLED 647
48 #define ERROR_PASSWD_EXPIRED 648
49 #define ERROR_NO_DIALIN_PERMISSION 649
50 #define ERROR_AUTHENTICATION_FAILURE 691
51 #define ERROR_CHANGING_PASSWORD 709
52
53 #define PASSWD_CHANGE_CHAL_LEN 16
54 #define MSCHAPV2_KEY_LEN 16
55
56
57 struct eap_mschapv2_data {
58         u8 auth_response[20];
59         int auth_response_valid;
60
61         int prev_error;
62         u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
63         int passwd_change_challenge_valid;
64         int passwd_change_version;
65
66         /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
67          */
68         u8 *peer_challenge;
69         u8 *auth_challenge;
70
71         int phase2;
72         u8 master_key[16];
73         int master_key_valid;
74         int success;
75
76         u8 *prev_challenge;
77         size_t prev_challenge_len;
78 };
79
80
81 static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
82
83
84 static void * eap_mschapv2_init(struct eap_sm *sm)
85 {
86         struct eap_mschapv2_data *data;
87         data = malloc(sizeof(*data));
88         if (data == NULL)
89                 return NULL;
90         memset(data, 0, sizeof(*data));
91
92         if (sm->peer_challenge) {
93                 data->peer_challenge = malloc(16);
94                 if (data->peer_challenge == NULL) {
95                         eap_mschapv2_deinit(sm, data);
96                         return NULL;
97                 }
98                 memcpy(data->peer_challenge, sm->peer_challenge, 16);
99         }
100
101         if (sm->auth_challenge) {
102                 data->auth_challenge = malloc(16);
103                 if (data->auth_challenge == NULL) {
104                         eap_mschapv2_deinit(sm, data);
105                         return NULL;
106                 }
107                 memcpy(data->auth_challenge, sm->auth_challenge, 16);
108         }
109
110         data->phase2 = sm->init_phase2;
111
112         return data;
113 }
114
115
116 static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
117 {
118         struct eap_mschapv2_data *data = priv;
119         free(data->peer_challenge);
120         free(data->auth_challenge);
121         free(data->prev_challenge);
122         free(data);
123 }
124
125
126 static u8 * eap_mschapv2_challenge(struct eap_sm *sm,
127                                    struct eap_mschapv2_data *data,
128                                    struct eap_method_ret *ret,
129                                    const struct eap_mschapv2_hdr *req,
130                                    size_t *respDataLen)
131 {
132         struct wpa_ssid *config = eap_get_config(sm);
133         u8 *challenge, *peer_challenge, *username, *pos;
134         int i, ms_len;
135         size_t len, challenge_len, username_len;
136         struct eap_mschapv2_hdr *resp;
137         u8 password_hash[16], password_hash_hash[16];
138
139         if (config == NULL)
140                 return NULL;
141
142         /* MSCHAPv2 does not include optional domain name in the
143          * challenge-response calculation, so remove domain prefix
144          * (if present). */
145         username = config->identity;
146         username_len = config->identity_len;
147         for (i = 0; i < username_len; i++) {
148                 if (username[i] == '\\') {
149                         username_len -= i + 1;
150                         username += i + 1;
151                         break;
152                 }
153         }
154
155         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
156         len = be_to_host16(req->length);
157         pos = (u8 *) (req + 1);
158         challenge_len = *pos++;
159         if (challenge_len != 16) {
160                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
161                            "%lu", (unsigned long) challenge_len);
162                 ret->ignore = TRUE;
163                 return NULL;
164         }
165
166         if (len < 10 || len - 10 < challenge_len) {
167                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
168                            " packet: len=%lu challenge_len=%lu",
169                            (unsigned long) len, (unsigned long) challenge_len);
170                 ret->ignore = TRUE;
171                 return NULL;
172         }
173
174         if (data->passwd_change_challenge_valid) {
175                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
176                            "failure message");
177                 challenge = data->passwd_change_challenge;
178         } else
179                 challenge = pos;
180         pos += challenge_len;
181         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
182                     pos, len - challenge_len - 10);
183
184         ret->ignore = FALSE;
185         ret->methodState = METHOD_MAY_CONT;
186         ret->decision = DECISION_FAIL;
187         ret->allowNotifications = TRUE;
188
189         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
190
191         *respDataLen = sizeof(*resp) + 1 + MSCHAPV2_RESP_LEN +
192                 config->identity_len;
193         resp = malloc(*respDataLen);
194         if (resp == NULL)
195                 return NULL;
196         memset(resp, 0, *respDataLen);
197         resp->code = EAP_CODE_RESPONSE;
198         resp->identifier = req->identifier;
199         resp->length = host_to_be16(*respDataLen);
200         resp->type = EAP_TYPE_MSCHAPV2;
201         resp->op_code = MSCHAPV2_OP_RESPONSE;
202         resp->mschapv2_id = req->mschapv2_id;
203         if (data->prev_error) {
204                 /*
205                  * TODO: this does not seem to be enough when processing two
206                  * or more failure messages. IAS did not increment mschapv2_id
207                  * in its own packets, but it seemed to expect the peer to
208                  * increment this for all packets(?).
209                  */
210                 resp->mschapv2_id++;
211         }
212         ms_len = *respDataLen - 5;
213         WPA_PUT_BE16(resp->ms_length, ms_len);
214         pos = (u8 *) (resp + 1);
215         *pos++ = MSCHAPV2_RESP_LEN; /* Value-Size */
216
217         /* Response */
218         peer_challenge = pos;
219         if (data->peer_challenge) {
220                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
221                            "in Phase 1");
222                 peer_challenge = data->peer_challenge;
223         } else if (hostapd_get_rand(peer_challenge, 16)) {
224                 free(resp);
225                 return NULL;
226         }
227         pos += 16;
228         pos += 8; /* Reserved, must be zero */
229         if (data->auth_challenge) {
230                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
231                            "in Phase 1");
232                 challenge = data->auth_challenge;
233         }
234         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", challenge, 16);
235         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
236                     peer_challenge, 16);
237         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
238                           username, username_len);
239         wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: password",
240                               config->password, config->password_len);
241         generate_nt_response(challenge, peer_challenge,
242                              username, username_len,
243                              config->password, config->password_len,
244                              pos);
245         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: response", pos, 24);
246         /* Authenticator response is not really needed yet, but calculate it
247          * here so that challenges need not be saved. */
248         generate_authenticator_response(config->password, config->password_len,
249                                         peer_challenge, challenge,
250                                         username, username_len, pos,
251                                         data->auth_response);
252         data->auth_response_valid = 1;
253
254         /* Likewise, generate master_key here since we have the needed data
255          * available. */
256         nt_password_hash(config->password, config->password_len,
257                          password_hash);
258         hash_nt_password_hash(password_hash, password_hash_hash);
259         get_master_key(password_hash_hash, pos /* nt_response */,
260                        data->master_key);
261         data->master_key_valid = 1;
262
263         pos += 24;
264         pos++; /* Flag / reserved, must be zero */
265
266         memcpy(pos, config->identity, config->identity_len);
267         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
268                    "(response)", resp->identifier, resp->mschapv2_id);
269         return (u8 *) resp;
270 }
271
272
273 static u8 * eap_mschapv2_success(struct eap_sm *sm,
274                                  struct eap_mschapv2_data *data,
275                                  struct eap_method_ret *ret,
276                                  const struct eap_mschapv2_hdr *req,
277                                  size_t *respDataLen)
278 {
279         struct eap_mschapv2_hdr *resp;
280         const u8 *pos;
281         u8 recv_response[20];
282         int len, left;
283
284         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
285         len = be_to_host16(req->length);
286         pos = (const u8 *) (req + 1);
287         if (!data->auth_response_valid || len < sizeof(*req) + 42 ||
288             pos[0] != 'S' || pos[1] != '=' ||
289             hexstr2bin((char *) (pos + 2), recv_response, 20) ||
290             memcmp(data->auth_response, recv_response, 20) != 0) {
291                 wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
292                            "response in success request");
293                 ret->methodState = METHOD_DONE;
294                 ret->decision = DECISION_FAIL;
295                 return NULL;
296         }
297         pos += 42;
298         left = len - sizeof(*req) - 42;
299         while (left > 0 && *pos == ' ') {
300                 pos++;
301                 left--;
302         }
303         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
304                           pos, left);
305         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
306         *respDataLen = 6;
307         resp = malloc(6);
308         if (resp == NULL) {
309                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
310                            "buffer for success response");
311                 ret->ignore = TRUE;
312                 return NULL;
313         }
314
315         resp->code = EAP_CODE_RESPONSE;
316         resp->identifier = req->identifier;
317         resp->length = host_to_be16(6);
318         resp->type = EAP_TYPE_MSCHAPV2;
319         resp->op_code = MSCHAPV2_OP_SUCCESS;
320
321         ret->methodState = METHOD_DONE;
322         ret->decision = DECISION_UNCOND_SUCC;
323         ret->allowNotifications = FALSE;
324         data->success = 1;
325
326         if (data->prev_error == ERROR_PASSWD_EXPIRED) {
327                 struct wpa_ssid *config = eap_get_config(sm);
328                 if (config && config->new_password) {
329                         wpa_msg(sm->msg_ctx, MSG_INFO,
330                                 WPA_EVENT_PASSWORD_CHANGED
331                                 "EAP-MSCHAPV2: Password changed successfully");
332                         data->prev_error = 0;
333                         free(config->password);
334                         config->password = config->new_password;
335                         config->new_password = NULL;
336                         config->password_len = config->new_password_len;
337                         config->new_password_len = 0;
338                 }
339         }
340
341         return (u8 *) resp;
342 }
343
344
345 static int eap_mschapv2_failure_txt(struct eap_sm *sm,
346                                     struct eap_mschapv2_data *data, char *txt)
347 {
348         char *pos, *msg = "";
349         int retry = 1;
350         struct wpa_ssid *config = eap_get_config(sm);
351
352         /* For example:
353          * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
354          */
355
356         pos = txt;
357
358         if (pos && strncmp(pos, "E=", 2) == 0) {
359                 pos += 2;
360                 data->prev_error = atoi(pos);
361                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
362                            data->prev_error);
363                 pos = strchr(pos, ' ');
364                 if (pos)
365                         pos++;
366         }
367
368         if (pos && strncmp(pos, "R=", 2) == 0) {
369                 pos += 2;
370                 retry = atoi(pos);
371                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
372                            retry == 1 ? "" : "not ");
373                 pos = strchr(pos, ' ');
374                 if (pos)
375                         pos++;
376         }
377
378         if (pos && strncmp(pos, "C=", 2) == 0) {
379                 int hex_len;
380                 pos += 2;
381                 hex_len = strchr(pos, ' ') - (char *) pos;
382                 if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
383                         if (hexstr2bin(pos, data->passwd_change_challenge,
384                                        PASSWD_CHANGE_CHAL_LEN)) {
385                                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
386                                            "failure challenge");
387                         } else {
388                                 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
389                                             "challenge",
390                                             data->passwd_change_challenge,
391                                             PASSWD_CHANGE_CHAL_LEN);
392                                 data->passwd_change_challenge_valid = 1;
393                         }
394                 } else {
395                         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
396                                    "challenge len %d", hex_len);
397                 }
398                 pos = strchr(pos, ' ');
399                 if (pos)
400                         pos++;
401         } else {
402                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
403                            "was not present in failure message");
404         }
405
406         if (pos && strncmp(pos, "V=", 2) == 0) {
407                 pos += 2;
408                 data->passwd_change_version = atoi(pos);
409                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
410                            "protocol version %d", data->passwd_change_version);
411                 pos = strchr(pos, ' ');
412                 if (pos)
413                         pos++;
414         }
415
416         if (pos && strncmp(pos, "M=", 2) == 0) {
417                 pos += 2;
418                 msg = pos;
419         }
420         wpa_msg(sm->msg_ctx, MSG_WARNING,
421                 "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
422                 "%d)",
423                 msg, retry == 1 ? "" : "not ", data->prev_error);
424         if (data->prev_error == ERROR_PASSWD_EXPIRED &&
425             data->passwd_change_version == 3 && config) {
426                 if (config->new_password == NULL) {
427                         wpa_msg(sm->msg_ctx, MSG_INFO,
428                                 "EAP-MSCHAPV2: Password expired - password "
429                                 "change required");
430                         eap_sm_request_new_password(sm, config);
431                 }
432         } else if (retry == 1 && config) {
433                 /* TODO: could prevent the current password from being used
434                  * again at least for some period of time */
435                 if (!config->mschapv2_retry)
436                         eap_sm_request_identity(sm, config);
437                 eap_sm_request_password(sm, config);
438                 config->mschapv2_retry = 1;
439         } else if (config) {
440                 /* TODO: prevent retries using same username/password */
441                 config->mschapv2_retry = 0;
442         }
443
444         return retry == 1;
445 }
446
447
448 static u8 * eap_mschapv2_change_password(struct eap_sm *sm,
449                                          struct eap_mschapv2_data *data,
450                                          struct eap_method_ret *ret,
451                                          const struct eap_mschapv2_hdr *req,
452                                          size_t *respDataLen)
453 {
454         struct eap_mschapv2_hdr *resp;
455         int ms_len, i;
456         u8 *peer_challenge, *username, *pos;
457         size_t username_len;
458         struct wpa_ssid *config = eap_get_config(sm);
459
460         if (config == NULL || config->identity == NULL ||
461             config->new_password == NULL || config->password == NULL)
462                 return NULL;
463
464         /*
465          * MSCHAPv2 does not include optional domain name in the
466          * challenge-response calculation, so remove domain prefix
467          * (if present).
468          */
469         username = config->identity;
470         username_len = config->identity_len;
471         for (i = 0; i < username_len; i++) {
472                 if (username[i] == '\\') {
473                         username_len -= i + 1;
474                         username += i + 1;
475                         break;
476                 }
477         }
478
479         ret->ignore = FALSE;
480         ret->methodState = METHOD_MAY_CONT;
481         ret->decision = DECISION_COND_SUCC;
482         ret->allowNotifications = TRUE;
483
484         *respDataLen = 591;
485         resp = malloc(*respDataLen);
486         if (resp == NULL) {
487                 return NULL;
488         }
489
490         resp->code = EAP_CODE_RESPONSE;
491         resp->identifier = req->identifier;
492         resp->length = host_to_be16((u16) *respDataLen);
493         resp->type = EAP_TYPE_MSCHAPV2;
494         resp->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
495         resp->mschapv2_id = req->mschapv2_id + 1;
496         ms_len = *respDataLen - 5;
497         WPA_PUT_BE16(resp->ms_length, ms_len);
498         pos = (u8 *) (resp + 1);
499
500         /* Encrypted-Password */
501         new_password_encrypted_with_old_nt_password_hash(
502                 config->new_password, config->new_password_len,
503                 config->password, config->password_len, pos);
504         pos += 516;
505
506         /* Encrypted-Hash */
507         old_nt_password_hash_encrypted_with_new_nt_password_hash(
508                 config->new_password, config->new_password_len,
509                 config->password, config->password_len, pos);
510         pos += 16;
511
512         /* Peer-Challenge */
513         peer_challenge = pos;
514         if (hostapd_get_rand(peer_challenge, 16)) {
515                 free(resp);
516                 return NULL;
517         }
518         pos += 16;
519
520         /* Reserved, must be zero */
521         memset(pos, 0, 8);
522         pos += 8;
523
524         /* NT-Response */
525         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
526                     data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
527         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
528                     peer_challenge, 16);
529         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
530                           username, username_len);
531         wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
532                               config->new_password, config->new_password_len);
533         generate_nt_response(data->passwd_change_challenge, peer_challenge,
534                              username, username_len,
535                              config->new_password, config->new_password_len,
536                              pos);
537         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response", pos, 24);
538
539         /* Authenticator response is not really needed yet, but calculate it
540          * here so that challenges need not be saved. */
541         generate_authenticator_response(config->new_password,
542                                         config->new_password_len,
543                                         peer_challenge,
544                                         data->passwd_change_challenge,
545                                         username, username_len, pos,
546                                         data->auth_response);
547         data->auth_response_valid = 1;
548
549         pos += 24;
550
551         /* Flags */
552         *pos++ = 0;
553         *pos++ = 0;
554
555         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
556                    "(change pw)", resp->identifier, resp->mschapv2_id);
557
558         return (u8 *) resp;
559 }
560
561
562 static u8 * eap_mschapv2_failure(struct eap_sm *sm,
563                                  struct eap_mschapv2_data *data,
564                                  struct eap_method_ret *ret,
565                                  const struct eap_mschapv2_hdr *req,
566                                  size_t *respDataLen)
567 {
568         struct eap_mschapv2_hdr *resp;
569         const u8 *msdata = (const u8 *) (req + 1);
570         char *buf;
571         int len = be_to_host16(req->length) - sizeof(*req);
572         int retry = 0;
573
574         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
575         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
576                           msdata, len);
577         buf = malloc(len + 1);
578         if (buf) {
579                 memcpy(buf, msdata, len);
580                 buf[len] = '\0';
581                 retry = eap_mschapv2_failure_txt(sm, data, buf);
582                 free(buf);
583         }
584
585         ret->ignore = FALSE;
586         ret->methodState = METHOD_DONE;
587         ret->decision = DECISION_FAIL;
588         ret->allowNotifications = FALSE;
589
590         if (data->prev_error == ERROR_PASSWD_EXPIRED &&
591             data->passwd_change_version == 3) {
592                 struct wpa_ssid *config = eap_get_config(sm);
593                 if (config && config->new_password)
594                         return eap_mschapv2_change_password(sm, data, ret, req,
595                                                             respDataLen);
596                 if (config && config->pending_req_new_password)
597                         return NULL;
598         } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
599                 /* TODO: could try to retry authentication, e.g, after having
600                  * changed the username/password. In this case, EAP MS-CHAP-v2
601                  * Failure Response would not be sent here. */
602                 return NULL;
603         }
604
605         *respDataLen = 6;
606         resp = malloc(6);
607         if (resp == NULL) {
608                 return NULL;
609         }
610
611         resp->code = EAP_CODE_RESPONSE;
612         resp->identifier = req->identifier;
613         resp->length = host_to_be16(6);
614         resp->type = EAP_TYPE_MSCHAPV2;
615         resp->op_code = MSCHAPV2_OP_FAILURE;
616
617         return (u8 *) resp;
618 }
619
620
621 static u8 * eap_mschapv2_process(struct eap_sm *sm, void *priv,
622                                  struct eap_method_ret *ret,
623                                  const u8 *reqData, size_t reqDataLen,
624                                  size_t *respDataLen)
625 {
626         struct eap_mschapv2_data *data = priv;
627         struct wpa_ssid *config = eap_get_config(sm);
628         const struct eap_mschapv2_hdr *req;
629         int ms_len, using_prev_challenge = 0;
630         const u8 *pos;
631         size_t len;
632
633         if (config == NULL || config->identity == NULL) {
634                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
635                 eap_sm_request_identity(sm, config);
636                 ret->ignore = TRUE;
637                 return NULL;
638         }
639
640         if (config->password == NULL) {
641                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
642                 eap_sm_request_password(sm, config);
643                 ret->ignore = TRUE;
644                 return NULL;
645         }
646
647         if (config->mschapv2_retry && data->prev_challenge &&
648             data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
649                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
650                            "with the previous challenge");
651
652                 reqData = data->prev_challenge;
653                 reqDataLen = data->prev_challenge_len;
654                 using_prev_challenge = 1;
655                 config->mschapv2_retry = 0;
656         }
657
658         pos = eap_hdr_validate(EAP_TYPE_MSCHAPV2, reqData, reqDataLen, &len);
659         if (pos == NULL || len < 5) {
660                 ret->ignore = TRUE;
661                 return NULL;
662         }
663         req = (const struct eap_mschapv2_hdr *) reqData;
664         len = be_to_host16(req->length);
665         ms_len = WPA_GET_BE16(req->ms_length);
666         if (ms_len != len - 5) {
667                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
668                            "ms_len=%d", (unsigned long) len, ms_len);
669                 if (sm->workaround) {
670                         /* Some authentication servers use invalid ms_len,
671                          * ignore it for interoperability. */
672                         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
673                                    " invalid ms_len");
674                 } else {
675                         ret->ignore = TRUE;
676                         return NULL;
677                 }
678         }
679
680         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
681                    req->identifier, req->mschapv2_id);
682
683         switch (req->op_code) {
684         case MSCHAPV2_OP_CHALLENGE:
685                 if (!using_prev_challenge) {
686                         free(data->prev_challenge);
687                         data->prev_challenge = malloc(len);
688                         if (data->prev_challenge) {
689                                 data->prev_challenge_len = len;
690                                 memcpy(data->prev_challenge, reqData, len);
691                         }
692                 }
693                 return eap_mschapv2_challenge(sm, data, ret, req, respDataLen);
694         case MSCHAPV2_OP_SUCCESS:
695                 return eap_mschapv2_success(sm, data, ret, req, respDataLen);
696         case MSCHAPV2_OP_FAILURE:
697                 return eap_mschapv2_failure(sm, data, ret, req, respDataLen);
698         default:
699                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
700                            req->op_code);
701                 ret->ignore = TRUE;
702                 return NULL;
703         }
704 }
705
706
707 static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
708 {
709         struct eap_mschapv2_data *data = priv;
710         return data->success && data->master_key_valid;
711 }
712
713
714 static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
715 {
716         struct eap_mschapv2_data *data = priv;
717         u8 *key;
718         int key_len;
719
720         if (!data->master_key_valid || !data->success)
721                 return NULL;
722
723         if (data->peer_challenge) {
724                 /* EAP-FAST needs both send and receive keys */
725                 key_len = 2 * MSCHAPV2_KEY_LEN;
726         } else {
727                 key_len = MSCHAPV2_KEY_LEN;
728         }
729
730         key = malloc(key_len);
731         if (key == NULL)
732                 return NULL;
733
734         if (data->peer_challenge) {
735                 get_asymetric_start_key(data->master_key, key,
736                                         MSCHAPV2_KEY_LEN, 0, 0);
737                 get_asymetric_start_key(data->master_key,
738                                         key + MSCHAPV2_KEY_LEN,
739                                         MSCHAPV2_KEY_LEN, 1, 0);
740         } else {
741                 get_asymetric_start_key(data->master_key, key,
742                                         MSCHAPV2_KEY_LEN, 1, 0);
743         }
744
745         wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
746                         key, key_len);
747
748         *len = key_len;
749         return key;
750 }
751
752
753 const struct eap_method eap_method_mschapv2 =
754 {
755         .method = EAP_TYPE_MSCHAPV2,
756         .name = "MSCHAPV2",
757         .init = eap_mschapv2_init,
758         .deinit = eap_mschapv2_deinit,
759         .process = eap_mschapv2_process,
760         .isKeyAvailable = eap_mschapv2_isKeyAvailable,
761         .getKey = eap_mschapv2_getKey,
762 };