2 * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
3 * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
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
26 #include "config_ssid.h"
31 #define MSCHAPV2_CHAL_LEN 16
32 #define MSCHAPV2_NT_RESPONSE_LEN 24
38 struct eap_mschapv2_hdr
{
39 u8 op_code
; /* MSCHAPV2_OP_* */
40 u8 mschapv2_id
; /* usually same as EAP identifier; must be changed
41 * for challenges, but not for success/failure */
42 u8 ms_length
[2]; /* Note: misaligned; length - 5 */
43 /* followed by data */
46 /* Response Data field */
48 u8 peer_challenge
[MSCHAPV2_CHAL_LEN
];
50 u8 nt_response
[MSCHAPV2_NT_RESPONSE_LEN
];
54 /* Change-Password Data field */
55 struct ms_change_password
{
56 u8 encr_password
[516];
58 u8 peer_challenge
[MSCHAPV2_CHAL_LEN
];
60 u8 nt_response
[MSCHAPV2_NT_RESPONSE_LEN
];
68 #define MSCHAPV2_OP_CHALLENGE 1
69 #define MSCHAPV2_OP_RESPONSE 2
70 #define MSCHAPV2_OP_SUCCESS 3
71 #define MSCHAPV2_OP_FAILURE 4
72 #define MSCHAPV2_OP_CHANGE_PASSWORD 7
74 #define ERROR_RESTRICTED_LOGON_HOURS 646
75 #define ERROR_ACCT_DISABLED 647
76 #define ERROR_PASSWD_EXPIRED 648
77 #define ERROR_NO_DIALIN_PERMISSION 649
78 #define ERROR_AUTHENTICATION_FAILURE 691
79 #define ERROR_CHANGING_PASSWORD 709
81 #define PASSWD_CHANGE_CHAL_LEN 16
82 #define MSCHAPV2_KEY_LEN 16
85 struct eap_mschapv2_data
{
87 int auth_response_valid
;
90 u8 passwd_change_challenge
[PASSWD_CHANGE_CHAL_LEN
];
91 int passwd_change_challenge_valid
;
92 int passwd_change_version
;
94 /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
102 int master_key_valid
;
106 size_t prev_challenge_len
;
110 static void eap_mschapv2_deinit(struct eap_sm
*sm
, void *priv
);
113 static void * eap_mschapv2_init(struct eap_sm
*sm
)
115 struct eap_mschapv2_data
*data
;
116 data
= os_zalloc(sizeof(*data
));
120 data
->full_key
= sm
->mschapv2_full_key
;
122 if (sm
->peer_challenge
) {
124 data
->peer_challenge
= os_malloc(MSCHAPV2_CHAL_LEN
);
125 if (data
->peer_challenge
== NULL
) {
126 eap_mschapv2_deinit(sm
, data
);
129 os_memcpy(data
->peer_challenge
, sm
->peer_challenge
,
133 if (sm
->auth_challenge
) {
134 data
->auth_challenge
= os_malloc(MSCHAPV2_CHAL_LEN
);
135 if (data
->auth_challenge
== NULL
) {
136 eap_mschapv2_deinit(sm
, data
);
139 os_memcpy(data
->auth_challenge
, sm
->auth_challenge
,
143 data
->phase2
= sm
->init_phase2
;
149 static void eap_mschapv2_deinit(struct eap_sm
*sm
, void *priv
)
151 struct eap_mschapv2_data
*data
= priv
;
152 os_free(data
->peer_challenge
);
153 os_free(data
->auth_challenge
);
154 os_free(data
->prev_challenge
);
159 static const u8
* eap_mschapv2_remove_domain(const u8
*username
, size_t *len
)
164 * MSCHAPv2 does not include optional domain name in the
165 * challenge-response calculation, so remove domain prefix
169 for (i
= 0; i
< *len
; i
++) {
170 if (username
[i
] == '\\') {
172 return username
+ i
+ 1;
180 static void eap_mschapv2_derive_response(
181 struct eap_mschapv2_data
*data
,
182 const u8
*username
, size_t username_len
,
183 const u8
*password
, size_t password_len
,
184 const u8
*auth_challenge
, const u8
*peer_challenge
,
187 u8 password_hash
[16], password_hash_hash
[16];
189 wpa_hexdump(MSG_DEBUG
, "EAP-MSCHAPV2: auth_challenge",
190 auth_challenge
, MSCHAPV2_CHAL_LEN
);
191 wpa_hexdump(MSG_DEBUG
, "EAP-MSCHAPV2: peer_challenge",
192 peer_challenge
, MSCHAPV2_CHAL_LEN
);
193 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: username",
194 username
, username_len
);
195 wpa_hexdump_ascii_key(MSG_DEBUG
, "EAP-MSCHAPV2: password",
196 password
, password_len
);
197 generate_nt_response(auth_challenge
, peer_challenge
,
198 username
, username_len
,
199 password
, password_len
, nt_response
);
200 wpa_hexdump(MSG_DEBUG
, "EAP-MSCHAPV2: response", nt_response
,
201 MSCHAPV2_NT_RESPONSE_LEN
);
202 /* Authenticator response is not really needed yet, but calculate it
203 * here so that challenges need not be saved. */
204 generate_authenticator_response(password
, password_len
,
205 peer_challenge
, auth_challenge
,
206 username
, username_len
, nt_response
,
207 data
->auth_response
);
208 data
->auth_response_valid
= 1;
210 /* Likewise, generate master_key here since we have the needed data
212 nt_password_hash(password
, password_len
, password_hash
);
213 hash_nt_password_hash(password_hash
, password_hash_hash
);
214 get_master_key(password_hash_hash
, nt_response
, data
->master_key
);
215 data
->master_key_valid
= 1;
219 static u8
* eap_mschapv2_challenge_reply(struct eap_sm
*sm
,
220 struct eap_mschapv2_data
*data
, u8 id
,
222 const u8
*auth_challenge
,
225 struct eap_hdr
*resp
;
226 struct eap_mschapv2_hdr
*ms
;
227 u8
*rpos
, *peer_challenge
;
229 struct ms_response
*r
;
230 size_t username_len
, identity_len
, password_len
;
231 const u8
*username
, *identity
, *password
;
233 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Generating Challenge Response");
235 identity
= eap_get_config_identity(sm
, &identity_len
);
236 password
= eap_get_config_password(sm
, &password_len
);
237 if (identity
== NULL
|| password
== NULL
)
240 username_len
= identity_len
;
241 username
= eap_mschapv2_remove_domain(identity
, &username_len
);
243 ms_len
= sizeof(*ms
) + 1 + sizeof(*r
) + identity_len
;
244 resp
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, respDataLen
,
245 ms_len
, EAP_CODE_RESPONSE
, id
, &rpos
);
249 ms
= (struct eap_mschapv2_hdr
*) rpos
;
250 ms
->op_code
= MSCHAPV2_OP_RESPONSE
;
251 ms
->mschapv2_id
= mschapv2_id
;
252 if (data
->prev_error
) {
254 * TODO: this does not seem to be enough when processing two
255 * or more failure messages. IAS did not increment mschapv2_id
256 * in its own packets, but it seemed to expect the peer to
257 * increment this for all packets(?).
261 WPA_PUT_BE16(ms
->ms_length
, ms_len
);
262 rpos
= (u8
*) (ms
+ 1);
263 *rpos
++ = sizeof(*r
); /* Value-Size */
266 r
= (struct ms_response
*) rpos
;
267 peer_challenge
= r
->peer_challenge
;
268 if (data
->peer_challenge
) {
269 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: peer_challenge generated "
271 peer_challenge
= data
->peer_challenge
;
272 os_memset(r
->peer_challenge
, 0, MSCHAPV2_CHAL_LEN
);
273 } else if (os_get_random(peer_challenge
, MSCHAPV2_CHAL_LEN
)) {
277 os_memset(r
->reserved
, 0, 8);
278 if (data
->auth_challenge
) {
279 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: auth_challenge generated "
281 auth_challenge
= data
->auth_challenge
;
283 eap_mschapv2_derive_response(data
, username
, username_len
,
284 password
, password_len
,
285 auth_challenge
, peer_challenge
,
288 r
->flags
= 0; /* reserved, must be zero */
290 os_memcpy((u8
*) (r
+ 1), identity
, identity_len
);
291 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
292 "(response)", resp
->identifier
, ms
->mschapv2_id
);
298 * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
299 * @sm: Pointer to EAP state machine allocated with eap_sm_init()
300 * @data: Pointer to private EAP method data from eap_mschapv2_init()
301 * @ret: Return values from EAP request validation and processing
302 * @req: Pointer to EAP-MSCHAPv2 header from the request
303 * @req_len: Length of the EAP-MSCHAPv2 data
304 * @id: EAP identifier used in th erequest
305 * @respDataLen: Length of the returned EAP response
306 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
309 static u8
* eap_mschapv2_challenge(struct eap_sm
*sm
,
310 struct eap_mschapv2_data
*data
,
311 struct eap_method_ret
*ret
,
312 const struct eap_mschapv2_hdr
*req
,
313 size_t req_len
, u8 id
, size_t *respDataLen
)
315 size_t len
, challenge_len
;
316 const u8
*pos
, *challenge
;
318 if (eap_get_config_identity(sm
, &len
) == NULL
||
319 eap_get_config_password(sm
, &len
) == NULL
)
322 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Received challenge");
323 if (req_len
< sizeof(*req
) + 1) {
324 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Too short challenge data "
325 "(len %lu)", (unsigned long) req_len
);
329 pos
= (const u8
*) (req
+ 1);
330 challenge_len
= *pos
++;
331 len
= req_len
- sizeof(*req
) - 1;
332 if (challenge_len
!= MSCHAPV2_CHAL_LEN
) {
333 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Invalid challenge length "
334 "%lu", (unsigned long) challenge_len
);
339 if (len
< challenge_len
) {
340 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Too short challenge"
341 " packet: len=%lu challenge_len=%lu",
342 (unsigned long) len
, (unsigned long) challenge_len
);
347 if (data
->passwd_change_challenge_valid
) {
348 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Using challenge from the "
350 challenge
= data
->passwd_change_challenge
;
353 pos
+= challenge_len
;
354 len
-= challenge_len
;
355 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: Authentication Servername",
359 ret
->methodState
= METHOD_MAY_CONT
;
360 ret
->decision
= DECISION_FAIL
;
361 ret
->allowNotifications
= TRUE
;
363 return eap_mschapv2_challenge_reply(sm
, data
, id
, req
->mschapv2_id
,
364 challenge
, respDataLen
);
368 static void eap_mschapv2_password_changed(struct eap_sm
*sm
,
369 struct eap_mschapv2_data
*data
)
371 struct wpa_ssid
*config
= eap_get_config(sm
);
372 if (config
&& config
->new_password
) {
373 wpa_msg(sm
->msg_ctx
, MSG_INFO
,
374 WPA_EVENT_PASSWORD_CHANGED
375 "EAP-MSCHAPV2: Password changed successfully");
376 data
->prev_error
= 0;
377 os_free(config
->password
);
378 config
->password
= config
->new_password
;
379 config
->new_password
= NULL
;
380 config
->password_len
= config
->new_password_len
;
381 config
->new_password_len
= 0;
387 * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
388 * @sm: Pointer to EAP state machine allocated with eap_sm_init()
389 * @data: Pointer to private EAP method data from eap_mschapv2_init()
390 * @ret: Return values from EAP request validation and processing
391 * @req: Pointer to EAP-MSCHAPv2 header from the request
392 * @req_len: Length of the EAP-MSCHAPv2 data
393 * @id: EAP identifier used in th erequest
394 * @respDataLen: Length of the returned EAP response
395 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
398 static u8
* eap_mschapv2_success(struct eap_sm
*sm
,
399 struct eap_mschapv2_data
*data
,
400 struct eap_method_ret
*ret
,
401 const struct eap_mschapv2_hdr
*req
,
402 size_t req_len
, u8 id
, size_t *respDataLen
)
404 struct eap_hdr
*resp
;
405 struct eap_mschapv2_hdr
*ms
;
407 u8 recv_response
[20];
411 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Received success");
412 len
= req_len
- sizeof(*req
);
413 pos
= (const u8
*) (req
+ 1);
414 if (!data
->auth_response_valid
|| len
< 42 ||
415 pos
[0] != 'S' || pos
[1] != '=' ||
416 hexstr2bin((char *) (pos
+ 2), recv_response
, 20) ||
417 os_memcmp(data
->auth_response
, recv_response
, 20) != 0) {
418 wpa_printf(MSG_WARNING
, "EAP-MSCHAPV2: Invalid authenticator "
419 "response in success request");
420 ret
->methodState
= METHOD_DONE
;
421 ret
->decision
= DECISION_FAIL
;
426 while (len
> 0 && *pos
== ' ') {
430 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: Success message",
432 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Authentication succeeded");
434 /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
436 resp
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, respDataLen
,
437 1, EAP_CODE_RESPONSE
, id
, &rpos
);
439 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Failed to allocate "
440 "buffer for success response");
445 ms
= (struct eap_mschapv2_hdr
*) rpos
;
446 ms
->op_code
= MSCHAPV2_OP_SUCCESS
;
448 ret
->methodState
= METHOD_DONE
;
449 ret
->decision
= DECISION_UNCOND_SUCC
;
450 ret
->allowNotifications
= FALSE
;
453 if (data
->prev_error
== ERROR_PASSWD_EXPIRED
)
454 eap_mschapv2_password_changed(sm
, data
);
460 static int eap_mschapv2_failure_txt(struct eap_sm
*sm
,
461 struct eap_mschapv2_data
*data
, char *txt
)
463 char *pos
, *msg
= "";
465 struct wpa_ssid
*config
= eap_get_config(sm
);
468 * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
473 if (pos
&& os_strncmp(pos
, "E=", 2) == 0) {
475 data
->prev_error
= atoi(pos
);
476 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: error %d",
478 pos
= os_strchr(pos
, ' ');
483 if (pos
&& os_strncmp(pos
, "R=", 2) == 0) {
486 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: retry is %sallowed",
487 retry
== 1 ? "" : "not ");
488 pos
= os_strchr(pos
, ' ');
493 if (pos
&& os_strncmp(pos
, "C=", 2) == 0) {
496 hex_len
= os_strchr(pos
, ' ') - (char *) pos
;
497 if (hex_len
== PASSWD_CHANGE_CHAL_LEN
* 2) {
498 if (hexstr2bin(pos
, data
->passwd_change_challenge
,
499 PASSWD_CHANGE_CHAL_LEN
)) {
500 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: invalid "
501 "failure challenge");
503 wpa_hexdump(MSG_DEBUG
, "EAP-MSCHAPV2: failure "
505 data
->passwd_change_challenge
,
506 PASSWD_CHANGE_CHAL_LEN
);
507 data
->passwd_change_challenge_valid
= 1;
510 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: invalid failure "
511 "challenge len %d", hex_len
);
513 pos
= os_strchr(pos
, ' ');
517 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: required challenge field "
518 "was not present in failure message");
521 if (pos
&& os_strncmp(pos
, "V=", 2) == 0) {
523 data
->passwd_change_version
= atoi(pos
);
524 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: password changing "
525 "protocol version %d", data
->passwd_change_version
);
526 pos
= os_strchr(pos
, ' ');
531 if (pos
&& os_strncmp(pos
, "M=", 2) == 0) {
535 wpa_msg(sm
->msg_ctx
, MSG_WARNING
,
536 "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
538 msg
, retry
== 1 ? "" : "not ", data
->prev_error
);
539 if (data
->prev_error
== ERROR_PASSWD_EXPIRED
&&
540 data
->passwd_change_version
== 3 && config
) {
541 if (config
->new_password
== NULL
) {
542 wpa_msg(sm
->msg_ctx
, MSG_INFO
,
543 "EAP-MSCHAPV2: Password expired - password "
545 eap_sm_request_new_password(sm
);
547 } else if (retry
== 1 && config
) {
548 /* TODO: could prevent the current password from being used
549 * again at least for some period of time */
550 if (!config
->mschapv2_retry
)
551 eap_sm_request_identity(sm
);
552 eap_sm_request_password(sm
);
553 config
->mschapv2_retry
= 1;
555 /* TODO: prevent retries using same username/password */
556 config
->mschapv2_retry
= 0;
563 static u8
* eap_mschapv2_change_password(struct eap_sm
*sm
,
564 struct eap_mschapv2_data
*data
,
565 struct eap_method_ret
*ret
,
566 const struct eap_mschapv2_hdr
*req
,
567 u8 id
, size_t *respDataLen
)
569 struct eap_hdr
*resp
;
571 const u8
*username
, *password
, *new_password
;
573 size_t username_len
, password_len
, new_password_len
;
574 struct eap_mschapv2_hdr
*ms
;
575 struct ms_change_password
*cp
;
577 username
= eap_get_config_identity(sm
, &username_len
);
578 password
= eap_get_config_password(sm
, &password_len
);
579 new_password
= eap_get_config_new_password(sm
, &new_password_len
);
580 if (username
== NULL
|| password
== NULL
|| new_password
== NULL
)
583 username
= eap_mschapv2_remove_domain(username
, &username_len
);
586 ret
->methodState
= METHOD_MAY_CONT
;
587 ret
->decision
= DECISION_COND_SUCC
;
588 ret
->allowNotifications
= TRUE
;
590 ms_len
= sizeof(*ms
) + sizeof(*cp
);
591 resp
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, respDataLen
,
592 ms_len
, EAP_CODE_RESPONSE
, id
, &pos
);
596 ms
= (struct eap_mschapv2_hdr
*) pos
;
597 ms
->op_code
= MSCHAPV2_OP_CHANGE_PASSWORD
;
598 ms
->mschapv2_id
= req
->mschapv2_id
+ 1;
599 WPA_PUT_BE16(ms
->ms_length
, ms_len
);
600 cp
= (struct ms_change_password
*) (ms
+ 1);
602 /* Encrypted-Password */
603 new_password_encrypted_with_old_nt_password_hash(
604 new_password
, new_password_len
,
605 password
, password_len
, cp
->encr_password
);
608 old_nt_password_hash_encrypted_with_new_nt_password_hash(
609 new_password
, new_password_len
,
610 password
, password_len
, cp
->encr_hash
);
613 if (os_get_random(cp
->peer_challenge
, MSCHAPV2_CHAL_LEN
)) {
618 /* Reserved, must be zero */
619 os_memset(cp
->reserved
, 0, 8);
622 wpa_hexdump(MSG_DEBUG
, "EAP-MSCHAPV2: auth_challenge",
623 data
->passwd_change_challenge
, PASSWD_CHANGE_CHAL_LEN
);
624 wpa_hexdump(MSG_DEBUG
, "EAP-MSCHAPV2: peer_challenge",
625 cp
->peer_challenge
, MSCHAPV2_CHAL_LEN
);
626 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: username",
627 username
, username_len
);
628 wpa_hexdump_ascii_key(MSG_DEBUG
, "EAP-MSCHAPV2: new password",
629 new_password
, new_password_len
);
630 generate_nt_response(data
->passwd_change_challenge
, cp
->peer_challenge
,
631 username
, username_len
,
632 new_password
, new_password_len
,
634 wpa_hexdump(MSG_DEBUG
, "EAP-MSCHAPV2: NT-Response",
635 cp
->nt_response
, MSCHAPV2_NT_RESPONSE_LEN
);
637 /* Authenticator response is not really needed yet, but calculate it
638 * here so that challenges need not be saved. */
639 generate_authenticator_response(new_password
, new_password_len
,
641 data
->passwd_change_challenge
,
642 username
, username_len
,
643 cp
->nt_response
, data
->auth_response
);
644 data
->auth_response_valid
= 1;
647 os_memset(cp
->flags
, 0, 2);
649 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
650 "(change pw)", resp
->identifier
, ms
->mschapv2_id
);
657 * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
658 * @sm: Pointer to EAP state machine allocated with eap_sm_init()
659 * @data: Pointer to private EAP method data from eap_mschapv2_init()
660 * @ret: Return values from EAP request validation and processing
661 * @req: Pointer to EAP-MSCHAPv2 header from the request
662 * @req_len: Length of the EAP-MSCHAPv2 data
663 * @id: EAP identifier used in th erequest
664 * @respDataLen: Length of the returned EAP response
665 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
668 static u8
* eap_mschapv2_failure(struct eap_sm
*sm
,
669 struct eap_mschapv2_data
*data
,
670 struct eap_method_ret
*ret
,
671 const struct eap_mschapv2_hdr
*req
,
672 size_t req_len
, u8 id
, size_t *respDataLen
)
674 struct eap_hdr
*resp
;
675 const u8
*msdata
= (const u8
*) (req
+ 1);
677 size_t len
= req_len
- sizeof(*req
);
679 struct eap_mschapv2_hdr
*ms
;
682 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Received failure");
683 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: Failure data",
686 * eap_mschapv2_failure_txt() expects a nul terminated string, so we
687 * must allocate a large enough temporary buffer to create that since
688 * the received message does not include nul termination.
690 buf
= os_malloc(len
+ 1);
692 os_memcpy(buf
, msdata
, len
);
694 retry
= eap_mschapv2_failure_txt(sm
, data
, buf
);
699 ret
->methodState
= METHOD_DONE
;
700 ret
->decision
= DECISION_FAIL
;
701 ret
->allowNotifications
= FALSE
;
703 if (data
->prev_error
== ERROR_PASSWD_EXPIRED
&&
704 data
->passwd_change_version
== 3) {
705 struct wpa_ssid
*config
= eap_get_config(sm
);
706 if (config
&& config
->new_password
)
707 return eap_mschapv2_change_password(sm
, data
, ret
, req
,
709 if (config
&& config
->pending_req_new_password
)
711 } else if (retry
&& data
->prev_error
== ERROR_AUTHENTICATION_FAILURE
) {
712 /* TODO: could try to retry authentication, e.g, after having
713 * changed the username/password. In this case, EAP MS-CHAP-v2
714 * Failure Response would not be sent here. */
718 /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
720 resp
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, respDataLen
,
721 1, EAP_CODE_RESPONSE
, id
, &pos
);
725 ms
= (struct eap_mschapv2_hdr
*) pos
;
726 ms
->op_code
= MSCHAPV2_OP_FAILURE
;
732 static int eap_mschapv2_check_config(struct eap_sm
*sm
)
736 if (eap_get_config_identity(sm
, &len
) == NULL
) {
737 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Identity not configured");
738 eap_sm_request_identity(sm
);
742 if (eap_get_config_password(sm
, &len
) == NULL
) {
743 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Password not configured");
744 eap_sm_request_password(sm
);
752 static int eap_mschapv2_check_mslen(struct eap_sm
*sm
, size_t len
,
753 const struct eap_mschapv2_hdr
*ms
)
755 size_t ms_len
= WPA_GET_BE16(ms
->ms_length
);
760 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Invalid header: len=%lu "
761 "ms_len=%lu", (unsigned long) len
, (unsigned long) ms_len
);
762 if (sm
->workaround
) {
763 /* Some authentication servers use invalid ms_len,
764 * ignore it for interoperability. */
765 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: workaround, ignore"
766 " invalid ms_len %lu (len %lu)",
767 (unsigned long) ms_len
,
768 (unsigned long) len
);
776 static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data
*data
,
777 const u8
*reqData
, size_t reqDataLen
)
780 * Store a copy of the challenge message, so that it can be processed
781 * again in case retry is allowed after a possible failure.
783 os_free(data
->prev_challenge
);
784 data
->prev_challenge
= os_malloc(reqDataLen
);
785 if (data
->prev_challenge
) {
786 data
->prev_challenge_len
= reqDataLen
;
787 os_memcpy(data
->prev_challenge
, reqData
, reqDataLen
);
793 * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
794 * @sm: Pointer to EAP state machine allocated with eap_sm_init()
795 * @priv: Pointer to private EAP method data from eap_mschapv2_init()
796 * @ret: Return values from EAP request validation and processing
797 * @reqData: EAP request to be processed (eapReqData)
798 * @reqDataLen: Length of the EAP request
799 * @respDataLen: Length of the returned EAP response
800 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
803 static u8
* eap_mschapv2_process(struct eap_sm
*sm
, void *priv
,
804 struct eap_method_ret
*ret
,
805 const u8
*reqData
, size_t reqDataLen
,
808 struct eap_mschapv2_data
*data
= priv
;
809 struct wpa_ssid
*config
= eap_get_config(sm
);
810 const struct eap_hdr
*req
;
811 const struct eap_mschapv2_hdr
*ms
;
812 int using_prev_challenge
= 0;
816 if (eap_mschapv2_check_config(sm
)) {
821 if (config
->mschapv2_retry
&& data
->prev_challenge
&&
822 data
->prev_error
== ERROR_AUTHENTICATION_FAILURE
) {
823 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Replacing pending packet "
824 "with the previous challenge");
826 reqData
= data
->prev_challenge
;
827 reqDataLen
= data
->prev_challenge_len
;
828 using_prev_challenge
= 1;
829 config
->mschapv2_retry
= 0;
832 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
,
833 reqData
, reqDataLen
, &len
);
834 if (pos
== NULL
|| len
< sizeof(*ms
) + 1) {
839 ms
= (const struct eap_mschapv2_hdr
*) pos
;
840 if (eap_mschapv2_check_mslen(sm
, len
, ms
)) {
845 req
= (const struct eap_hdr
*) reqData
;
846 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
847 req
->identifier
, ms
->mschapv2_id
);
849 switch (ms
->op_code
) {
850 case MSCHAPV2_OP_CHALLENGE
:
851 if (!using_prev_challenge
)
852 eap_mschapv2_copy_challenge(data
, reqData
, reqDataLen
);
853 return eap_mschapv2_challenge(sm
, data
, ret
, ms
, len
,
854 req
->identifier
, respDataLen
);
855 case MSCHAPV2_OP_SUCCESS
:
856 return eap_mschapv2_success(sm
, data
, ret
, ms
, len
,
857 req
->identifier
, respDataLen
);
858 case MSCHAPV2_OP_FAILURE
:
859 return eap_mschapv2_failure(sm
, data
, ret
, ms
, len
,
860 req
->identifier
, respDataLen
);
862 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Unknown op %d - ignored",
870 static Boolean
eap_mschapv2_isKeyAvailable(struct eap_sm
*sm
, void *priv
)
872 struct eap_mschapv2_data
*data
= priv
;
873 return data
->success
&& data
->master_key_valid
;
877 static u8
* eap_mschapv2_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
879 struct eap_mschapv2_data
*data
= priv
;
883 if (!data
->master_key_valid
|| !data
->success
)
886 if (data
->full_key
) {
887 /* EAP-FAST needs both send and receive keys */
888 key_len
= 2 * MSCHAPV2_KEY_LEN
;
890 key_len
= MSCHAPV2_KEY_LEN
;
893 key
= os_malloc(key_len
);
897 if (data
->full_key
) {
898 get_asymetric_start_key(data
->master_key
, key
,
899 MSCHAPV2_KEY_LEN
, 0, 0);
900 get_asymetric_start_key(data
->master_key
,
901 key
+ MSCHAPV2_KEY_LEN
,
902 MSCHAPV2_KEY_LEN
, 1, 0);
904 get_asymetric_start_key(data
->master_key
, key
,
905 MSCHAPV2_KEY_LEN
, 1, 0);
908 wpa_hexdump_key(MSG_DEBUG
, "EAP-MSCHAPV2: Derived key",
917 * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
918 * Returns: 0 on success, -1 on failure
920 * This function is used to register EAP-MSCHAPv2 peer method into the EAP
923 int eap_peer_mschapv2_register(void)
925 struct eap_method
*eap
;
928 eap
= eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION
,
929 EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
,
934 eap
->init
= eap_mschapv2_init
;
935 eap
->deinit
= eap_mschapv2_deinit
;
936 eap
->process
= eap_mschapv2_process
;
937 eap
->isKeyAvailable
= eap_mschapv2_isKeyAvailable
;
938 eap
->getKey
= eap_mschapv2_getKey
;
940 ret
= eap_peer_method_register(eap
);
942 eap_peer_method_free(eap
);