2 * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
3 * Copyright (c) 2004-2007, 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.
23 struct eap_mschapv2_hdr
{
24 u8 op_code
; /* MSCHAPV2_OP_* */
25 u8 mschapv2_id
; /* must be changed for challenges, but not for
27 u8 ms_length
[2]; /* Note: misaligned; length - 5 */
28 /* followed by data */
31 #define MSCHAPV2_OP_CHALLENGE 1
32 #define MSCHAPV2_OP_RESPONSE 2
33 #define MSCHAPV2_OP_SUCCESS 3
34 #define MSCHAPV2_OP_FAILURE 4
35 #define MSCHAPV2_OP_CHANGE_PASSWORD 7
37 #define MSCHAPV2_RESP_LEN 49
39 #define ERROR_RESTRICTED_LOGON_HOURS 646
40 #define ERROR_ACCT_DISABLED 647
41 #define ERROR_PASSWD_EXPIRED 648
42 #define ERROR_NO_DIALIN_PERMISSION 649
43 #define ERROR_AUTHENTICATION_FAILURE 691
44 #define ERROR_CHANGING_PASSWORD 709
46 #define PASSWD_CHANGE_CHAL_LEN 16
47 #define MSCHAPV2_KEY_LEN 16
50 #define CHALLENGE_LEN 16
52 struct eap_mschapv2_data
{
53 u8 auth_challenge
[CHALLENGE_LEN
];
55 enum { CHALLENGE
, SUCCESS_REQ
, FAILURE_REQ
, SUCCESS
, FAILURE
} state
;
62 static void * eap_mschapv2_init(struct eap_sm
*sm
)
64 struct eap_mschapv2_data
*data
;
66 data
= wpa_zalloc(sizeof(*data
));
69 data
->state
= CHALLENGE
;
75 static void eap_mschapv2_reset(struct eap_sm
*sm
, void *priv
)
77 struct eap_mschapv2_data
*data
= priv
;
82 static u8
* eap_mschapv2_build_challenge(struct eap_sm
*sm
,
83 struct eap_mschapv2_data
*data
,
84 int id
, size_t *reqDataLen
)
87 struct eap_mschapv2_hdr
*ms
;
89 char *name
= "hostapd"; /* TODO: make this configurable */
92 if (hostapd_get_rand(data
->auth_challenge
, CHALLENGE_LEN
)) {
93 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to get random "
95 data
->state
= FAILURE
;
99 ms_len
= sizeof(*ms
) + 1 + CHALLENGE_LEN
+ strlen(name
);
100 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, reqDataLen
,
101 ms_len
, EAP_CODE_REQUEST
, id
, &pos
);
103 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to allocate memory"
105 data
->state
= FAILURE
;
109 ms
= (struct eap_mschapv2_hdr
*) pos
;
110 ms
->op_code
= MSCHAPV2_OP_CHALLENGE
;
111 ms
->mschapv2_id
= id
;
112 WPA_PUT_BE16(ms
->ms_length
, ms_len
);
114 pos
= (u8
*) (ms
+ 1);
115 *pos
++ = CHALLENGE_LEN
;
116 memcpy(pos
, data
->auth_challenge
, CHALLENGE_LEN
);
117 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: Challenge", pos
,
119 pos
+= CHALLENGE_LEN
;
120 memcpy(pos
, name
, strlen(name
));
126 static u8
* eap_mschapv2_build_success_req(struct eap_sm
*sm
,
127 struct eap_mschapv2_data
*data
,
128 int id
, size_t *reqDataLen
)
131 struct eap_mschapv2_hdr
*ms
;
133 char *message
= "OK";
136 ms_len
= sizeof(*ms
) + 2 + 2 * sizeof(data
->auth_response
) + 1 + 2 +
138 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, reqDataLen
,
139 ms_len
, EAP_CODE_REQUEST
, id
, &pos
);
141 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to allocate memory"
143 data
->state
= FAILURE
;
147 ms
= (struct eap_mschapv2_hdr
*) pos
;
148 ms
->op_code
= MSCHAPV2_OP_SUCCESS
;
149 ms
->mschapv2_id
= data
->resp_mschapv2_id
;
150 WPA_PUT_BE16(ms
->ms_length
, ms_len
);
152 msg
= pos
= (u8
*) (ms
+ 1);
155 pos
+= wpa_snprintf_hex_uppercase((char *) pos
,
156 sizeof(data
->auth_response
) * 2 + 1,
158 sizeof(data
->auth_response
));
162 memcpy(pos
, message
, strlen(message
));
164 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: Success Request Message",
165 msg
, ms_len
- sizeof(*ms
));
171 static u8
* eap_mschapv2_build_failure_req(struct eap_sm
*sm
,
172 struct eap_mschapv2_data
*data
,
173 int id
, size_t *reqDataLen
)
176 struct eap_mschapv2_hdr
*ms
;
178 char *message
= "E=691 R=0 C=00000000000000000000000000000000 V=3 "
182 ms_len
= sizeof(*ms
) + strlen(message
);
183 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, reqDataLen
,
184 ms_len
, EAP_CODE_REQUEST
, id
, &pos
);
186 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to allocate memory"
188 data
->state
= FAILURE
;
192 ms
= (struct eap_mschapv2_hdr
*) pos
;
193 ms
->op_code
= MSCHAPV2_OP_FAILURE
;
194 ms
->mschapv2_id
= data
->resp_mschapv2_id
;
195 WPA_PUT_BE16(ms
->ms_length
, ms_len
);
197 memcpy((u8
*) (ms
+ 1), message
, strlen(message
));
199 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: Failure Request Message",
200 (u8
*) message
, strlen(message
));
206 static u8
* eap_mschapv2_buildReq(struct eap_sm
*sm
, void *priv
, int id
,
209 struct eap_mschapv2_data
*data
= priv
;
211 switch (data
->state
) {
213 return eap_mschapv2_build_challenge(sm
, data
, id
, reqDataLen
);
215 return eap_mschapv2_build_success_req(sm
, data
, id
,
218 return eap_mschapv2_build_failure_req(sm
, data
, id
,
221 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Unknown state %d in "
222 "buildReq", data
->state
);
229 static Boolean
eap_mschapv2_check(struct eap_sm
*sm
, void *priv
,
230 u8
*respData
, size_t respDataLen
)
232 struct eap_mschapv2_data
*data
= priv
;
233 struct eap_mschapv2_hdr
*resp
;
237 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
,
238 respData
, respDataLen
, &len
);
239 if (pos
== NULL
|| len
< 1) {
240 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Invalid frame");
244 resp
= (struct eap_mschapv2_hdr
*) pos
;
245 if (data
->state
== CHALLENGE
&&
246 resp
->op_code
!= MSCHAPV2_OP_RESPONSE
) {
247 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Expected Response - "
248 "ignore op %d", resp
->op_code
);
252 if (data
->state
== SUCCESS_REQ
&&
253 resp
->op_code
!= MSCHAPV2_OP_SUCCESS
&&
254 resp
->op_code
!= MSCHAPV2_OP_FAILURE
) {
255 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Expected Success or "
256 "Failure - ignore op %d", resp
->op_code
);
260 if (data
->state
== FAILURE_REQ
&&
261 resp
->op_code
!= MSCHAPV2_OP_FAILURE
) {
262 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Expected Failure "
263 "- ignore op %d", resp
->op_code
);
271 static void eap_mschapv2_process_response(struct eap_sm
*sm
,
272 struct eap_mschapv2_data
*data
,
273 u8
*respData
, size_t respDataLen
)
275 struct eap_mschapv2_hdr
*resp
;
276 const u8
*pos
, *end
, *peer_challenge
, *nt_response
, *name
;
278 size_t len
, name_len
, i
;
280 const u8
*username
, *user
;
281 size_t username_len
, user_len
;
283 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
,
284 respData
, respDataLen
, &len
);
285 if (pos
== NULL
|| len
< 1)
286 return; /* Should not happen - frame already validated */
289 resp
= (struct eap_mschapv2_hdr
*) pos
;
290 pos
= (u8
*) (resp
+ 1);
292 if (len
< sizeof(*resp
) + 1 + 49 ||
293 resp
->op_code
!= MSCHAPV2_OP_RESPONSE
||
295 wpa_hexdump(MSG_DEBUG
, "EAP-MSCHAPV2: Invalid response",
296 respData
, respDataLen
);
297 data
->state
= FAILURE
;
300 data
->resp_mschapv2_id
= resp
->mschapv2_id
;
302 peer_challenge
= pos
;
308 name_len
= end
- name
;
310 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: Peer-Challenge",
312 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: NT-Response", nt_response
, 24);
313 wpa_printf(MSG_MSGDUMP
, "EAP-MSCHAPV2: Flags 0x%x", flags
);
314 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: Name", name
, name_len
);
316 /* MSCHAPv2 does not include optional domain name in the
317 * challenge-response calculation, so remove domain prefix
319 username
= sm
->identity
;
320 username_len
= sm
->identity_len
;
321 for (i
= 0; i
< username_len
; i
++) {
322 if (username
[i
] == '\\') {
323 username_len
-= i
+ 1;
331 for (i
= 0; i
< user_len
; i
++) {
332 if (user
[i
] == '\\') {
339 if (username_len
!= user_len
||
340 memcmp(username
, user
, username_len
) != 0) {
341 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Mismatch in user names");
342 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: Expected user "
343 "name", username
, username_len
);
344 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: Received user "
345 "name", user
, user_len
);
346 data
->state
= FAILURE
;
350 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: User name",
351 username
, username_len
);
353 if (sm
->user
->password_hash
) {
354 generate_nt_response_pwhash(data
->auth_challenge
,
356 username
, username_len
,
360 generate_nt_response(data
->auth_challenge
, peer_challenge
,
361 username
, username_len
,
363 sm
->user
->password_len
,
367 if (memcmp(nt_response
, expected
, 24) == 0) {
369 u8 pw_hash_buf
[16], pw_hash_hash
[16];
371 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Correct NT-Response");
372 data
->state
= SUCCESS_REQ
;
374 /* Authenticator response is not really needed yet, but
375 * calculate it here so that peer_challenge and username need
377 if (sm
->user
->password_hash
) {
378 pw_hash
= sm
->user
->password
;
379 generate_authenticator_response_pwhash(
380 sm
->user
->password
, peer_challenge
,
381 data
->auth_challenge
, username
, username_len
,
382 nt_response
, data
->auth_response
);
384 nt_password_hash(sm
->user
->password
,
385 sm
->user
->password_len
,
387 pw_hash
= pw_hash_buf
;
388 generate_authenticator_response(sm
->user
->password
,
389 sm
->user
->password_len
,
391 data
->auth_challenge
,
392 username
, username_len
,
394 data
->auth_response
);
397 hash_nt_password_hash(pw_hash
, pw_hash_hash
);
398 get_master_key(pw_hash_hash
, nt_response
, data
->master_key
);
399 data
->master_key_valid
= 1;
400 wpa_hexdump_key(MSG_DEBUG
, "EAP-MSCHAPV2: Derived Master Key",
401 data
->master_key
, MSCHAPV2_KEY_LEN
);
403 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: Expected NT-Response",
405 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Invalid NT-Response");
406 data
->state
= FAILURE_REQ
;
411 static void eap_mschapv2_process_success_resp(struct eap_sm
*sm
,
412 struct eap_mschapv2_data
*data
,
413 u8
*respData
, size_t respDataLen
)
415 struct eap_mschapv2_hdr
*resp
;
419 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
,
420 respData
, respDataLen
, &len
);
421 if (pos
== NULL
|| len
< 1)
422 return; /* Should not happen - frame already validated */
424 resp
= (struct eap_mschapv2_hdr
*) pos
;
426 if (resp
->op_code
== MSCHAPV2_OP_SUCCESS
) {
427 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Received Success Response"
428 " - authentication completed successfully");
429 data
->state
= SUCCESS
;
431 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Did not receive Success "
432 "Response - peer rejected authentication");
433 data
->state
= FAILURE
;
438 static void eap_mschapv2_process_failure_resp(struct eap_sm
*sm
,
439 struct eap_mschapv2_data
*data
,
440 u8
*respData
, size_t respDataLen
)
442 struct eap_mschapv2_hdr
*resp
;
446 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
,
447 respData
, respDataLen
, &len
);
448 if (pos
== NULL
|| len
< 1)
449 return; /* Should not happen - frame already validated */
451 resp
= (struct eap_mschapv2_hdr
*) pos
;
453 if (resp
->op_code
== MSCHAPV2_OP_FAILURE
) {
454 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Received Failure Response"
455 " - authentication failed");
457 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Did not receive Failure "
458 "Response - authentication failed");
461 data
->state
= FAILURE
;
465 static void eap_mschapv2_process(struct eap_sm
*sm
, void *priv
,
466 u8
*respData
, size_t respDataLen
)
468 struct eap_mschapv2_data
*data
= priv
;
470 if (sm
->user
== NULL
|| sm
->user
->password
== NULL
) {
471 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Password not configured");
472 data
->state
= FAILURE
;
476 switch (data
->state
) {
478 eap_mschapv2_process_response(sm
, data
, respData
, respDataLen
);
481 eap_mschapv2_process_success_resp(sm
, data
, respData
,
485 eap_mschapv2_process_failure_resp(sm
, data
, respData
,
489 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Unknown state %d in "
490 "process", data
->state
);
496 static Boolean
eap_mschapv2_isDone(struct eap_sm
*sm
, void *priv
)
498 struct eap_mschapv2_data
*data
= priv
;
499 return data
->state
== SUCCESS
|| data
->state
== FAILURE
;
503 static u8
* eap_mschapv2_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
505 struct eap_mschapv2_data
*data
= priv
;
508 if (data
->state
!= SUCCESS
|| !data
->master_key_valid
)
511 *len
= 2 * MSCHAPV2_KEY_LEN
;
515 get_asymetric_start_key(data
->master_key
, key
, MSCHAPV2_KEY_LEN
, 0, 0);
516 get_asymetric_start_key(data
->master_key
, key
+ MSCHAPV2_KEY_LEN
,
517 MSCHAPV2_KEY_LEN
, 1, 0);
518 wpa_hexdump_key(MSG_DEBUG
, "EAP-MSCHAPV2: Derived key", key
, *len
);
524 static Boolean
eap_mschapv2_isSuccess(struct eap_sm
*sm
, void *priv
)
526 struct eap_mschapv2_data
*data
= priv
;
527 return data
->state
== SUCCESS
;
531 int eap_server_mschapv2_register(void)
533 struct eap_method
*eap
;
536 eap
= eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION
,
537 EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
,
542 eap
->init
= eap_mschapv2_init
;
543 eap
->reset
= eap_mschapv2_reset
;
544 eap
->buildReq
= eap_mschapv2_buildReq
;
545 eap
->check
= eap_mschapv2_check
;
546 eap
->process
= eap_mschapv2_process
;
547 eap
->isDone
= eap_mschapv2_isDone
;
548 eap
->getKey
= eap_mschapv2_getKey
;
549 eap
->isSuccess
= eap_mschapv2_isSuccess
;
551 ret
= eap_server_method_register(eap
);
553 eap_server_method_free(eap
);