Re-initialize hostapd/wpa_supplicant git repository based on 0.6.3 release
[wpasupplicant] / src / eap_peer / eap_mschapv2.c
1 /*
2  * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
3  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.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  * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
15  * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
16  * Extensions Protocol, Version 2, for mutual authentication and key
17  * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
18  * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
19  * RFC 3079.
20  */
21
22 #include "includes.h"
23
24 #include "common.h"
25 #include "eap_i.h"
26 #include "eap_config.h"
27 #include "ms_funcs.h"
28 #include "wpa_ctrl.h"
29 #include "mschapv2.h"
30
31
32 #ifdef _MSC_VER
33 #pragma pack(push, 1)
34 #endif /* _MSC_VER */
35
36 struct eap_mschapv2_hdr {
37         u8 op_code; /* MSCHAPV2_OP_* */
38         u8 mschapv2_id; /* usually same as EAP identifier; must be changed
39                          * for challenges, but not for success/failure */
40         u8 ms_length[2]; /* Note: misaligned; length - 5 */
41         /* followed by data */
42 } STRUCT_PACKED;
43
44 /* Response Data field */
45 struct ms_response {
46         u8 peer_challenge[MSCHAPV2_CHAL_LEN];
47         u8 reserved[8];
48         u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
49         u8 flags;
50 } STRUCT_PACKED;
51
52 /* Change-Password Data field */
53 struct ms_change_password {
54         u8 encr_password[516];
55         u8 encr_hash[16];
56         u8 peer_challenge[MSCHAPV2_CHAL_LEN];
57         u8 reserved[8];
58         u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
59         u8 flags[2];
60 } STRUCT_PACKED;
61
62 #ifdef _MSC_VER
63 #pragma pack(pop)
64 #endif /* _MSC_VER */
65
66 #define MSCHAPV2_OP_CHALLENGE 1
67 #define MSCHAPV2_OP_RESPONSE 2
68 #define MSCHAPV2_OP_SUCCESS 3
69 #define MSCHAPV2_OP_FAILURE 4
70 #define MSCHAPV2_OP_CHANGE_PASSWORD 7
71
72 #define ERROR_RESTRICTED_LOGON_HOURS 646
73 #define ERROR_ACCT_DISABLED 647
74 #define ERROR_PASSWD_EXPIRED 648
75 #define ERROR_NO_DIALIN_PERMISSION 649
76 #define ERROR_AUTHENTICATION_FAILURE 691
77 #define ERROR_CHANGING_PASSWORD 709
78
79 #define PASSWD_CHANGE_CHAL_LEN 16
80 #define MSCHAPV2_KEY_LEN 16
81
82
83 struct eap_mschapv2_data {
84         u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
85         int auth_response_valid;
86
87         int prev_error;
88         u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
89         int passwd_change_challenge_valid;
90         int passwd_change_version;
91
92         /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
93          */
94         u8 *peer_challenge;
95         u8 *auth_challenge;
96         int full_key;
97
98         int phase2;
99         u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
100         int master_key_valid;
101         int success;
102
103         struct wpabuf *prev_challenge;
104 };
105
106
107 static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
108
109
110 static void * eap_mschapv2_init(struct eap_sm *sm)
111 {
112         struct eap_mschapv2_data *data;
113         data = os_zalloc(sizeof(*data));
114         if (data == NULL)
115                 return NULL;
116
117         data->full_key = sm->mschapv2_full_key;
118
119         if (sm->peer_challenge) {
120                 data->full_key = 1;
121                 data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
122                 if (data->peer_challenge == NULL) {
123                         eap_mschapv2_deinit(sm, data);
124                         return NULL;
125                 }
126                 os_memcpy(data->peer_challenge, sm->peer_challenge,
127                           MSCHAPV2_CHAL_LEN);
128         }
129
130         if (sm->auth_challenge) {
131                 data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
132                 if (data->auth_challenge == NULL) {
133                         eap_mschapv2_deinit(sm, data);
134                         return NULL;
135                 }
136                 os_memcpy(data->auth_challenge, sm->auth_challenge,
137                           MSCHAPV2_CHAL_LEN);
138         }
139
140         data->phase2 = sm->init_phase2;
141
142         return data;
143 }
144
145
146 static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
147 {
148         struct eap_mschapv2_data *data = priv;
149         os_free(data->peer_challenge);
150         os_free(data->auth_challenge);
151         wpabuf_free(data->prev_challenge);
152         os_free(data);
153 }
154
155
156 static struct wpabuf * eap_mschapv2_challenge_reply(
157         struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id,
158         u8 mschapv2_id, const u8 *auth_challenge)
159 {
160         struct wpabuf *resp;
161         struct eap_mschapv2_hdr *ms;
162         u8 *peer_challenge;
163         int ms_len;
164         struct ms_response *r;
165         size_t identity_len, password_len;
166         const u8 *identity, *password;
167         int pwhash;
168
169         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
170
171         identity = eap_get_config_identity(sm, &identity_len);
172         password = eap_get_config_password2(sm, &password_len, &pwhash);
173         if (identity == NULL || password == NULL)
174                 return NULL;
175
176         ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
177         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
178                              EAP_CODE_RESPONSE, id);
179         if (resp == NULL)
180                 return NULL;
181
182         ms = wpabuf_put(resp, sizeof(*ms));
183         ms->op_code = MSCHAPV2_OP_RESPONSE;
184         ms->mschapv2_id = mschapv2_id;
185         if (data->prev_error) {
186                 /*
187                  * TODO: this does not seem to be enough when processing two
188                  * or more failure messages. IAS did not increment mschapv2_id
189                  * in its own packets, but it seemed to expect the peer to
190                  * increment this for all packets(?).
191                  */
192                 ms->mschapv2_id++;
193         }
194         WPA_PUT_BE16(ms->ms_length, ms_len);
195
196         wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
197
198         /* Response */
199         r = wpabuf_put(resp, sizeof(*r));
200         peer_challenge = r->peer_challenge;
201         if (data->peer_challenge) {
202                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
203                            "in Phase 1");
204                 peer_challenge = data->peer_challenge;
205                 os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
206         } else if (os_get_random(peer_challenge, MSCHAPV2_CHAL_LEN)) {
207                 wpabuf_free(resp);
208                 return NULL;
209         }
210         os_memset(r->reserved, 0, 8);
211         if (data->auth_challenge) {
212                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
213                            "in Phase 1");
214                 auth_challenge = data->auth_challenge;
215         }
216         mschapv2_derive_response(identity, identity_len, password,
217                                  password_len, pwhash, auth_challenge,
218                                  peer_challenge, r->nt_response,
219                                  data->auth_response, data->master_key);
220         data->auth_response_valid = 1;
221         data->master_key_valid = 1;
222
223         r->flags = 0; /* reserved, must be zero */
224
225         wpabuf_put_data(resp, identity, identity_len);
226         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
227                    "(response)", id, ms->mschapv2_id);
228         return resp;
229 }
230
231
232 /**
233  * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
234  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
235  * @data: Pointer to private EAP method data from eap_mschapv2_init()
236  * @ret: Return values from EAP request validation and processing
237  * @req: Pointer to EAP-MSCHAPv2 header from the request
238  * @req_len: Length of the EAP-MSCHAPv2 data
239  * @id: EAP identifier used in the request
240  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
241  * no reply available
242  */
243 static struct wpabuf * eap_mschapv2_challenge(
244         struct eap_sm *sm, struct eap_mschapv2_data *data,
245         struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
246         size_t req_len, u8 id)
247 {
248         size_t len, challenge_len;
249         const u8 *pos, *challenge;
250
251         if (eap_get_config_identity(sm, &len) == NULL ||
252             eap_get_config_password(sm, &len) == NULL)
253                 return NULL;
254
255         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
256         if (req_len < sizeof(*req) + 1) {
257                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
258                            "(len %lu)", (unsigned long) req_len);
259                 ret->ignore = TRUE;
260                 return NULL;
261         }
262         pos = (const u8 *) (req + 1);
263         challenge_len = *pos++;
264         len = req_len - sizeof(*req) - 1;
265         if (challenge_len != MSCHAPV2_CHAL_LEN) {
266                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
267                            "%lu", (unsigned long) challenge_len);
268                 ret->ignore = TRUE;
269                 return NULL;
270         }
271
272         if (len < challenge_len) {
273                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
274                            " packet: len=%lu challenge_len=%lu",
275                            (unsigned long) len, (unsigned long) challenge_len);
276                 ret->ignore = TRUE;
277                 return NULL;
278         }
279
280         if (data->passwd_change_challenge_valid) {
281                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
282                            "failure message");
283                 challenge = data->passwd_change_challenge;
284         } else
285                 challenge = pos;
286         pos += challenge_len;
287         len -= challenge_len;
288         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
289                     pos, len);
290
291         ret->ignore = FALSE;
292         ret->methodState = METHOD_MAY_CONT;
293         ret->decision = DECISION_FAIL;
294         ret->allowNotifications = TRUE;
295
296         return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
297                                             challenge);
298 }
299
300
301 static void eap_mschapv2_password_changed(struct eap_sm *sm,
302                                           struct eap_mschapv2_data *data)
303 {
304         struct eap_peer_config *config = eap_get_config(sm);
305         if (config && config->new_password) {
306                 wpa_msg(sm->msg_ctx, MSG_INFO,
307                         WPA_EVENT_PASSWORD_CHANGED
308                         "EAP-MSCHAPV2: Password changed successfully");
309                 data->prev_error = 0;
310                 os_free(config->password);
311                 if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
312                         config->password = os_malloc(16);
313                         config->password_len = 16;
314                         if (config->password) {
315                                 nt_password_hash(config->new_password,
316                                                  config->new_password_len,
317                                                  config->password);
318                         }
319                         os_free(config->new_password);
320                 } else {
321                         config->password = config->new_password;
322                         config->password_len = config->new_password_len;
323                 }
324                 config->new_password = NULL;
325                 config->new_password_len = 0;
326         }
327 }
328
329
330 /**
331  * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
332  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
333  * @data: Pointer to private EAP method data from eap_mschapv2_init()
334  * @ret: Return values from EAP request validation and processing
335  * @req: Pointer to EAP-MSCHAPv2 header from the request
336  * @req_len: Length of the EAP-MSCHAPv2 data
337  * @id: EAP identifier used in th erequest
338  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
339  * no reply available
340  */
341 static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
342                                             struct eap_mschapv2_data *data,
343                                             struct eap_method_ret *ret,
344                                             const struct eap_mschapv2_hdr *req,
345                                             size_t req_len, u8 id)
346 {
347         struct wpabuf *resp;
348         const u8 *pos;
349         size_t len;
350
351         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
352         len = req_len - sizeof(*req);
353         pos = (const u8 *) (req + 1);
354         if (!data->auth_response_valid ||
355             mschapv2_verify_auth_response(data->auth_response, pos, len)) {
356                 wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
357                            "response in success request");
358                 ret->methodState = METHOD_DONE;
359                 ret->decision = DECISION_FAIL;
360                 return NULL;
361         }
362         pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
363         len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
364         while (len > 0 && *pos == ' ') {
365                 pos++;
366                 len--;
367         }
368         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
369                           pos, len);
370         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
371
372         /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
373          * message. */
374         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
375                              EAP_CODE_RESPONSE, id);
376         if (resp == NULL) {
377                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
378                            "buffer for success response");
379                 ret->ignore = TRUE;
380                 return NULL;
381         }
382
383         wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
384
385         ret->methodState = METHOD_DONE;
386         ret->decision = DECISION_UNCOND_SUCC;
387         ret->allowNotifications = FALSE;
388         data->success = 1;
389
390         if (data->prev_error == ERROR_PASSWD_EXPIRED)
391                 eap_mschapv2_password_changed(sm, data);
392
393         return resp;
394 }
395
396
397 static int eap_mschapv2_failure_txt(struct eap_sm *sm,
398                                     struct eap_mschapv2_data *data, char *txt)
399 {
400         char *pos, *msg = "";
401         int retry = 1;
402         struct eap_peer_config *config = eap_get_config(sm);
403
404         /* For example:
405          * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
406          */
407
408         pos = txt;
409
410         if (pos && os_strncmp(pos, "E=", 2) == 0) {
411                 pos += 2;
412                 data->prev_error = atoi(pos);
413                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
414                            data->prev_error);
415                 pos = os_strchr(pos, ' ');
416                 if (pos)
417                         pos++;
418         }
419
420         if (pos && os_strncmp(pos, "R=", 2) == 0) {
421                 pos += 2;
422                 retry = atoi(pos);
423                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
424                            retry == 1 ? "" : "not ");
425                 pos = os_strchr(pos, ' ');
426                 if (pos)
427                         pos++;
428         }
429
430         if (pos && os_strncmp(pos, "C=", 2) == 0) {
431                 int hex_len;
432                 pos += 2;
433                 hex_len = os_strchr(pos, ' ') - (char *) pos;
434                 if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
435                         if (hexstr2bin(pos, data->passwd_change_challenge,
436                                        PASSWD_CHANGE_CHAL_LEN)) {
437                                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
438                                            "failure challenge");
439                         } else {
440                                 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
441                                             "challenge",
442                                             data->passwd_change_challenge,
443                                             PASSWD_CHANGE_CHAL_LEN);
444                                 data->passwd_change_challenge_valid = 1;
445                         }
446                 } else {
447                         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
448                                    "challenge len %d", hex_len);
449                 }
450                 pos = os_strchr(pos, ' ');
451                 if (pos)
452                         pos++;
453         } else {
454                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
455                            "was not present in failure message");
456         }
457
458         if (pos && os_strncmp(pos, "V=", 2) == 0) {
459                 pos += 2;
460                 data->passwd_change_version = atoi(pos);
461                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
462                            "protocol version %d", data->passwd_change_version);
463                 pos = os_strchr(pos, ' ');
464                 if (pos)
465                         pos++;
466         }
467
468         if (pos && os_strncmp(pos, "M=", 2) == 0) {
469                 pos += 2;
470                 msg = pos;
471         }
472         wpa_msg(sm->msg_ctx, MSG_WARNING,
473                 "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
474                 "%d)",
475                 msg, retry == 1 ? "" : "not ", data->prev_error);
476         if (data->prev_error == ERROR_PASSWD_EXPIRED &&
477             data->passwd_change_version == 3 && config) {
478                 if (config->new_password == NULL) {
479                         wpa_msg(sm->msg_ctx, MSG_INFO,
480                                 "EAP-MSCHAPV2: Password expired - password "
481                                 "change required");
482                         eap_sm_request_new_password(sm);
483                 }
484         } else if (retry == 1 && config) {
485                 /* TODO: could prevent the current password from being used
486                  * again at least for some period of time */
487                 if (!config->mschapv2_retry)
488                         eap_sm_request_identity(sm);
489                 eap_sm_request_password(sm);
490                 config->mschapv2_retry = 1;
491         } else if (config) {
492                 /* TODO: prevent retries using same username/password */
493                 config->mschapv2_retry = 0;
494         }
495
496         return retry == 1;
497 }
498
499
500 static struct wpabuf * eap_mschapv2_change_password(
501         struct eap_sm *sm, struct eap_mschapv2_data *data,
502         struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
503 {
504         struct wpabuf *resp;
505         int ms_len;
506         const u8 *username, *password, *new_password;
507         size_t username_len, password_len, new_password_len;
508         struct eap_mschapv2_hdr *ms;
509         struct ms_change_password *cp;
510         u8 password_hash[16], password_hash_hash[16];
511         int pwhash;
512
513         username = eap_get_config_identity(sm, &username_len);
514         password = eap_get_config_password2(sm, &password_len, &pwhash);
515         new_password = eap_get_config_new_password(sm, &new_password_len);
516         if (username == NULL || password == NULL || new_password == NULL)
517                 return NULL;
518
519         username = mschapv2_remove_domain(username, &username_len);
520
521         ret->ignore = FALSE;
522         ret->methodState = METHOD_MAY_CONT;
523         ret->decision = DECISION_COND_SUCC;
524         ret->allowNotifications = TRUE;
525
526         ms_len = sizeof(*ms) + sizeof(*cp);
527         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
528                              EAP_CODE_RESPONSE, id);
529         if (resp == NULL)
530                 return NULL;
531
532         ms = wpabuf_put(resp, sizeof(*ms));
533         ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
534         ms->mschapv2_id = req->mschapv2_id + 1;
535         WPA_PUT_BE16(ms->ms_length, ms_len);
536         cp = wpabuf_put(resp, sizeof(*cp));
537
538         /* Encrypted-Password */
539         if (pwhash) {
540                 if (encrypt_pw_block_with_password_hash(
541                             new_password, new_password_len,
542                             password, cp->encr_password))
543                         goto fail;
544         } else {
545                 if (new_password_encrypted_with_old_nt_password_hash(
546                             new_password, new_password_len,
547                             password, password_len, cp->encr_password))
548                         goto fail;
549         }
550
551         /* Encrypted-Hash */
552         if (pwhash) {
553                 u8 new_password_hash[16];
554                 nt_password_hash(new_password, new_password_len,
555                                  new_password_hash);
556                 nt_password_hash_encrypted_with_block(password,
557                                                       new_password_hash,
558                                                       cp->encr_hash);
559         } else {
560                 old_nt_password_hash_encrypted_with_new_nt_password_hash(
561                         new_password, new_password_len,
562                         password, password_len, cp->encr_hash);
563         }
564
565         /* Peer-Challenge */
566         if (os_get_random(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
567                 goto fail;
568
569         /* Reserved, must be zero */
570         os_memset(cp->reserved, 0, 8);
571
572         /* NT-Response */
573         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
574                     data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
575         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
576                     cp->peer_challenge, MSCHAPV2_CHAL_LEN);
577         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
578                           username, username_len);
579         wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
580                               new_password, new_password_len);
581         generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
582                              username, username_len,
583                              new_password, new_password_len,
584                              cp->nt_response);
585         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
586                     cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
587
588         /* Authenticator response is not really needed yet, but calculate it
589          * here so that challenges need not be saved. */
590         generate_authenticator_response(new_password, new_password_len,
591                                         cp->peer_challenge,
592                                         data->passwd_change_challenge,
593                                         username, username_len,
594                                         cp->nt_response, data->auth_response);
595         data->auth_response_valid = 1;
596
597         /* Likewise, generate master_key here since we have the needed data
598          * available. */
599         nt_password_hash(new_password, new_password_len, password_hash);
600         hash_nt_password_hash(password_hash, password_hash_hash);
601         get_master_key(password_hash_hash, cp->nt_response, data->master_key);
602         data->master_key_valid = 1;
603
604         /* Flags */
605         os_memset(cp->flags, 0, 2);
606
607         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
608                    "(change pw)", id, ms->mschapv2_id);
609
610         return resp;
611
612 fail:
613         wpabuf_free(resp);
614         return NULL;
615 }
616
617
618 /**
619  * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
620  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
621  * @data: Pointer to private EAP method data from eap_mschapv2_init()
622  * @ret: Return values from EAP request validation and processing
623  * @req: Pointer to EAP-MSCHAPv2 header from the request
624  * @req_len: Length of the EAP-MSCHAPv2 data
625  * @id: EAP identifier used in th erequest
626  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
627  * no reply available
628  */
629 static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
630                                             struct eap_mschapv2_data *data,
631                                             struct eap_method_ret *ret,
632                                             const struct eap_mschapv2_hdr *req,
633                                             size_t req_len, u8 id)
634 {
635         struct wpabuf *resp;
636         const u8 *msdata = (const u8 *) (req + 1);
637         char *buf;
638         size_t len = req_len - sizeof(*req);
639         int retry = 0;
640
641         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
642         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
643                           msdata, len);
644         /*
645          * eap_mschapv2_failure_txt() expects a nul terminated string, so we
646          * must allocate a large enough temporary buffer to create that since
647          * the received message does not include nul termination.
648          */
649         buf = os_malloc(len + 1);
650         if (buf) {
651                 os_memcpy(buf, msdata, len);
652                 buf[len] = '\0';
653                 retry = eap_mschapv2_failure_txt(sm, data, buf);
654                 os_free(buf);
655         }
656
657         ret->ignore = FALSE;
658         ret->methodState = METHOD_DONE;
659         ret->decision = DECISION_FAIL;
660         ret->allowNotifications = FALSE;
661
662         if (data->prev_error == ERROR_PASSWD_EXPIRED &&
663             data->passwd_change_version == 3) {
664                 struct eap_peer_config *config = eap_get_config(sm);
665                 if (config && config->new_password)
666                         return eap_mschapv2_change_password(sm, data, ret, req,
667                                                             id);
668                 if (config && config->pending_req_new_password)
669                         return NULL;
670         } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
671                 /* TODO: could try to retry authentication, e.g, after having
672                  * changed the username/password. In this case, EAP MS-CHAP-v2
673                  * Failure Response would not be sent here. */
674                 return NULL;
675         }
676
677         /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
678          * message. */
679         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
680                              EAP_CODE_RESPONSE, id);
681         if (resp == NULL)
682                 return NULL;
683
684         wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
685
686         return resp;
687 }
688
689
690 static int eap_mschapv2_check_config(struct eap_sm *sm)
691 {
692         size_t len;
693
694         if (eap_get_config_identity(sm, &len) == NULL) {
695                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
696                 eap_sm_request_identity(sm);
697                 return -1;
698         }
699
700         if (eap_get_config_password(sm, &len) == NULL) {
701                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
702                 eap_sm_request_password(sm);
703                 return -1;
704         }
705
706         return 0;
707 }
708
709
710 static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
711                                     const struct eap_mschapv2_hdr *ms)
712 {
713         size_t ms_len = WPA_GET_BE16(ms->ms_length);
714
715         if (ms_len == len)
716                 return 0;
717
718         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
719                    "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
720         if (sm->workaround) {
721                 /* Some authentication servers use invalid ms_len,
722                  * ignore it for interoperability. */
723                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
724                            " invalid ms_len %lu (len %lu)",
725                            (unsigned long) ms_len,
726                            (unsigned long) len);
727                 return 0;
728         }
729
730         return -1;
731 }
732
733
734 static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
735                                         const struct wpabuf *reqData)
736 {
737         /*
738          * Store a copy of the challenge message, so that it can be processed
739          * again in case retry is allowed after a possible failure.
740          */
741         wpabuf_free(data->prev_challenge);
742         data->prev_challenge = wpabuf_dup(reqData);
743 }
744
745
746 /**
747  * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
748  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
749  * @priv: Pointer to private EAP method data from eap_mschapv2_init()
750  * @ret: Return values from EAP request validation and processing
751  * @reqData: EAP request to be processed (eapReqData)
752  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
753  * no reply available
754  */
755 static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
756                                             struct eap_method_ret *ret,
757                                             const struct wpabuf *reqData)
758 {
759         struct eap_mschapv2_data *data = priv;
760         struct eap_peer_config *config = eap_get_config(sm);
761         const struct eap_mschapv2_hdr *ms;
762         int using_prev_challenge = 0;
763         const u8 *pos;
764         size_t len;
765         u8 id;
766
767         if (eap_mschapv2_check_config(sm)) {
768                 ret->ignore = TRUE;
769                 return NULL;
770         }
771
772         if (config->mschapv2_retry && data->prev_challenge &&
773             data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
774                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
775                            "with the previous challenge");
776
777                 reqData = data->prev_challenge;
778                 using_prev_challenge = 1;
779                 config->mschapv2_retry = 0;
780         }
781
782         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
783                                &len);
784         if (pos == NULL || len < sizeof(*ms) + 1) {
785                 ret->ignore = TRUE;
786                 return NULL;
787         }
788
789         ms = (const struct eap_mschapv2_hdr *) pos;
790         if (eap_mschapv2_check_mslen(sm, len, ms)) {
791                 ret->ignore = TRUE;
792                 return NULL;
793         }
794
795         id = eap_get_id(reqData);
796         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
797                    id, ms->mschapv2_id);
798
799         switch (ms->op_code) {
800         case MSCHAPV2_OP_CHALLENGE:
801                 if (!using_prev_challenge)
802                         eap_mschapv2_copy_challenge(data, reqData);
803                 return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
804         case MSCHAPV2_OP_SUCCESS:
805                 return eap_mschapv2_success(sm, data, ret, ms, len, id);
806         case MSCHAPV2_OP_FAILURE:
807                 return eap_mschapv2_failure(sm, data, ret, ms, len, id);
808         default:
809                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
810                            ms->op_code);
811                 ret->ignore = TRUE;
812                 return NULL;
813         }
814 }
815
816
817 static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
818 {
819         struct eap_mschapv2_data *data = priv;
820         return data->success && data->master_key_valid;
821 }
822
823
824 static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
825 {
826         struct eap_mschapv2_data *data = priv;
827         u8 *key;
828         int key_len;
829
830         if (!data->master_key_valid || !data->success)
831                 return NULL;
832
833         if (data->full_key) {
834                 /* EAP-FAST needs both send and receive keys */
835                 key_len = 2 * MSCHAPV2_KEY_LEN;
836         } else {
837                 key_len = MSCHAPV2_KEY_LEN;
838         }
839
840         key = os_malloc(key_len);
841         if (key == NULL)
842                 return NULL;
843
844         if (data->full_key) {
845                 get_asymetric_start_key(data->master_key, key,
846                                         MSCHAPV2_KEY_LEN, 0, 0);
847                 get_asymetric_start_key(data->master_key,
848                                         key + MSCHAPV2_KEY_LEN,
849                                         MSCHAPV2_KEY_LEN, 1, 0);
850         } else {
851                 get_asymetric_start_key(data->master_key, key,
852                                         MSCHAPV2_KEY_LEN, 1, 0);
853         }
854
855         wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
856                         key, key_len);
857
858         *len = key_len;
859         return key;
860 }
861
862
863 /**
864  * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
865  * Returns: 0 on success, -1 on failure
866  *
867  * This function is used to register EAP-MSCHAPv2 peer method into the EAP
868  * method list.
869  */
870 int eap_peer_mschapv2_register(void)
871 {
872         struct eap_method *eap;
873         int ret;
874
875         eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
876                                     EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
877                                     "MSCHAPV2");
878         if (eap == NULL)
879                 return -1;
880
881         eap->init = eap_mschapv2_init;
882         eap->deinit = eap_mschapv2_deinit;
883         eap->process = eap_mschapv2_process;
884         eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
885         eap->getKey = eap_mschapv2_getKey;
886
887         ret = eap_peer_method_register(eap);
888         if (ret)
889                 eap_peer_method_free(eap);
890         return ret;
891 }