2 * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
3 * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
12 #include "crypto/ms_funcs.h"
13 #include "crypto/random.h"
17 struct eap_mschapv2_hdr
{
18 u8 op_code
; /* MSCHAPV2_OP_* */
19 u8 mschapv2_id
; /* must be changed for challenges, but not for
21 u8 ms_length
[2]; /* Note: misaligned; length - 5 */
22 /* followed by data */
25 #define MSCHAPV2_OP_CHALLENGE 1
26 #define MSCHAPV2_OP_RESPONSE 2
27 #define MSCHAPV2_OP_SUCCESS 3
28 #define MSCHAPV2_OP_FAILURE 4
29 #define MSCHAPV2_OP_CHANGE_PASSWORD 7
31 #define MSCHAPV2_RESP_LEN 49
33 #define ERROR_RESTRICTED_LOGON_HOURS 646
34 #define ERROR_ACCT_DISABLED 647
35 #define ERROR_PASSWD_EXPIRED 648
36 #define ERROR_NO_DIALIN_PERMISSION 649
37 #define ERROR_AUTHENTICATION_FAILURE 691
38 #define ERROR_CHANGING_PASSWORD 709
40 #define PASSWD_CHANGE_CHAL_LEN 16
41 #define MSCHAPV2_KEY_LEN 16
44 #define CHALLENGE_LEN 16
46 struct eap_mschapv2_data
{
47 u8 auth_challenge
[CHALLENGE_LEN
];
48 int auth_challenge_from_tls
;
51 enum { CHALLENGE
, SUCCESS_REQ
, FAILURE_REQ
, SUCCESS
, FAILURE
} state
;
58 static void * eap_mschapv2_init(struct eap_sm
*sm
)
60 struct eap_mschapv2_data
*data
;
62 data
= os_zalloc(sizeof(*data
));
65 data
->state
= CHALLENGE
;
67 if (sm
->auth_challenge
) {
68 os_memcpy(data
->auth_challenge
, sm
->auth_challenge
,
70 data
->auth_challenge_from_tls
= 1;
73 if (sm
->peer_challenge
) {
74 data
->peer_challenge
= os_memdup(sm
->peer_challenge
,
76 if (data
->peer_challenge
== NULL
) {
86 static void eap_mschapv2_reset(struct eap_sm
*sm
, void *priv
)
88 struct eap_mschapv2_data
*data
= priv
;
92 os_free(data
->peer_challenge
);
93 bin_clear_free(data
, sizeof(*data
));
97 static struct wpabuf
* eap_mschapv2_build_challenge(
98 struct eap_sm
*sm
, struct eap_mschapv2_data
*data
, u8 id
)
101 struct eap_mschapv2_hdr
*ms
;
104 if (!data
->auth_challenge_from_tls
&&
105 random_get_bytes(data
->auth_challenge
, CHALLENGE_LEN
)) {
106 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to get random "
108 data
->state
= FAILURE
;
112 ms_len
= sizeof(*ms
) + 1 + CHALLENGE_LEN
+ sm
->server_id_len
;
113 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, ms_len
,
114 EAP_CODE_REQUEST
, id
);
116 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to allocate memory"
118 data
->state
= FAILURE
;
122 ms
= wpabuf_put(req
, sizeof(*ms
));
123 ms
->op_code
= MSCHAPV2_OP_CHALLENGE
;
124 ms
->mschapv2_id
= id
;
125 WPA_PUT_BE16(ms
->ms_length
, ms_len
);
127 wpabuf_put_u8(req
, CHALLENGE_LEN
);
128 if (!data
->auth_challenge_from_tls
)
129 wpabuf_put_data(req
, data
->auth_challenge
, CHALLENGE_LEN
);
131 wpabuf_put(req
, CHALLENGE_LEN
);
132 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: Challenge",
133 data
->auth_challenge
, CHALLENGE_LEN
);
134 wpabuf_put_data(req
, sm
->server_id
, sm
->server_id_len
);
140 static struct wpabuf
* eap_mschapv2_build_success_req(
141 struct eap_sm
*sm
, struct eap_mschapv2_data
*data
, u8 id
)
144 struct eap_mschapv2_hdr
*ms
;
146 char *message
= "OK";
149 ms_len
= sizeof(*ms
) + 2 + 2 * sizeof(data
->auth_response
) + 1 + 2 +
151 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, ms_len
,
152 EAP_CODE_REQUEST
, id
);
154 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to allocate memory"
156 data
->state
= FAILURE
;
160 ms
= wpabuf_put(req
, sizeof(*ms
));
161 ms
->op_code
= MSCHAPV2_OP_SUCCESS
;
162 ms
->mschapv2_id
= data
->resp_mschapv2_id
;
163 WPA_PUT_BE16(ms
->ms_length
, ms_len
);
164 msg
= (u8
*) (ms
+ 1);
166 wpabuf_put_u8(req
, 'S');
167 wpabuf_put_u8(req
, '=');
168 wpa_snprintf_hex_uppercase(
169 wpabuf_put(req
, sizeof(data
->auth_response
) * 2),
170 sizeof(data
->auth_response
) * 2 + 1,
171 data
->auth_response
, sizeof(data
->auth_response
));
172 wpabuf_put_u8(req
, ' ');
173 wpabuf_put_u8(req
, 'M');
174 wpabuf_put_u8(req
, '=');
175 wpabuf_put_data(req
, message
, os_strlen(message
));
177 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: Success Request Message",
178 msg
, ms_len
- sizeof(*ms
));
184 static struct wpabuf
* eap_mschapv2_build_failure_req(
185 struct eap_sm
*sm
, struct eap_mschapv2_data
*data
, u8 id
)
188 struct eap_mschapv2_hdr
*ms
;
189 char *message
= "E=691 R=0 C=00000000000000000000000000000000 V=3 "
193 ms_len
= sizeof(*ms
) + os_strlen(message
);
194 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, ms_len
,
195 EAP_CODE_REQUEST
, id
);
197 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to allocate memory"
199 data
->state
= FAILURE
;
203 ms
= wpabuf_put(req
, sizeof(*ms
));
204 ms
->op_code
= MSCHAPV2_OP_FAILURE
;
205 ms
->mschapv2_id
= data
->resp_mschapv2_id
;
206 WPA_PUT_BE16(ms
->ms_length
, ms_len
);
208 wpabuf_put_data(req
, message
, os_strlen(message
));
210 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: Failure Request Message",
211 (u8
*) message
, os_strlen(message
));
217 static struct wpabuf
* eap_mschapv2_buildReq(struct eap_sm
*sm
, void *priv
,
220 struct eap_mschapv2_data
*data
= priv
;
222 switch (data
->state
) {
224 return eap_mschapv2_build_challenge(sm
, data
, id
);
226 return eap_mschapv2_build_success_req(sm
, data
, id
);
228 return eap_mschapv2_build_failure_req(sm
, data
, id
);
230 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Unknown state %d in "
231 "buildReq", data
->state
);
238 static Boolean
eap_mschapv2_check(struct eap_sm
*sm
, void *priv
,
239 struct wpabuf
*respData
)
241 struct eap_mschapv2_data
*data
= priv
;
242 struct eap_mschapv2_hdr
*resp
;
246 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, respData
,
248 if (pos
== NULL
|| len
< 1) {
249 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Invalid frame");
253 resp
= (struct eap_mschapv2_hdr
*) pos
;
254 if (data
->state
== CHALLENGE
&&
255 resp
->op_code
!= MSCHAPV2_OP_RESPONSE
) {
256 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Expected Response - "
257 "ignore op %d", resp
->op_code
);
261 if (data
->state
== SUCCESS_REQ
&&
262 resp
->op_code
!= MSCHAPV2_OP_SUCCESS
&&
263 resp
->op_code
!= MSCHAPV2_OP_FAILURE
) {
264 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Expected Success or "
265 "Failure - ignore op %d", resp
->op_code
);
269 if (data
->state
== FAILURE_REQ
&&
270 resp
->op_code
!= MSCHAPV2_OP_FAILURE
) {
271 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Expected Failure "
272 "- ignore op %d", resp
->op_code
);
280 static void eap_mschapv2_process_response(struct eap_sm
*sm
,
281 struct eap_mschapv2_data
*data
,
282 struct wpabuf
*respData
)
284 struct eap_mschapv2_hdr
*resp
;
285 const u8
*pos
, *end
, *peer_challenge
, *nt_response
, *name
;
287 size_t len
, name_len
, i
;
289 const u8
*username
, *user
;
290 size_t username_len
, user_len
;
294 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, respData
,
296 if (pos
== NULL
|| len
< 1)
297 return; /* Should not happen - frame already validated */
300 resp
= (struct eap_mschapv2_hdr
*) pos
;
301 pos
= (u8
*) (resp
+ 1);
303 if (len
< sizeof(*resp
) + 1 + 49 ||
304 resp
->op_code
!= MSCHAPV2_OP_RESPONSE
||
306 wpa_hexdump_buf(MSG_DEBUG
, "EAP-MSCHAPV2: Invalid response",
308 data
->state
= FAILURE
;
311 data
->resp_mschapv2_id
= resp
->mschapv2_id
;
313 peer_challenge
= pos
;
319 name_len
= end
- name
;
321 if (data
->peer_challenge
) {
322 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Using pre-configured "
324 peer_challenge
= data
->peer_challenge
;
326 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: Peer-Challenge",
328 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: NT-Response", nt_response
, 24);
329 wpa_printf(MSG_MSGDUMP
, "EAP-MSCHAPV2: Flags 0x%x", flags
);
330 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: Name", name
, name_len
);
332 buf
= os_malloc(name_len
* 4 + 1);
334 printf_encode(buf
, name_len
* 4 + 1, name
, name_len
);
335 eap_log_msg(sm
, "EAP-MSCHAPV2 Name '%s'", buf
);
339 /* MSCHAPv2 does not include optional domain name in the
340 * challenge-response calculation, so remove domain prefix
342 username
= sm
->identity
;
343 username_len
= sm
->identity_len
;
344 for (i
= 0; i
< username_len
; i
++) {
345 if (username
[i
] == '\\') {
346 username_len
-= i
+ 1;
354 for (i
= 0; i
< user_len
; i
++) {
355 if (user
[i
] == '\\') {
362 #ifdef CONFIG_TESTING_OPTIONS
366 if (challenge_hash(peer_challenge
, data
->auth_challenge
,
367 username
, username_len
, challenge
) == 0) {
368 eap_server_mschap_rx_callback(sm
, "EAP-MSCHAPV2",
369 username
, username_len
,
370 challenge
, nt_response
);
373 #endif /* CONFIG_TESTING_OPTIONS */
375 if (username_len
!= user_len
||
376 os_memcmp(username
, user
, username_len
) != 0) {
377 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Mismatch in user names");
378 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: Expected user "
379 "name", username
, username_len
);
380 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: Received user "
381 "name", user
, user_len
);
382 data
->state
= FAILURE
;
386 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: User name",
387 username
, username_len
);
389 if (sm
->user
->password_hash
) {
390 res
= generate_nt_response_pwhash(data
->auth_challenge
,
392 username
, username_len
,
396 res
= generate_nt_response(data
->auth_challenge
,
398 username
, username_len
,
400 sm
->user
->password_len
,
404 data
->state
= FAILURE
;
408 if (os_memcmp_const(nt_response
, expected
, 24) == 0) {
410 u8 pw_hash_buf
[16], pw_hash_hash
[16];
412 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Correct NT-Response");
413 data
->state
= SUCCESS_REQ
;
415 /* Authenticator response is not really needed yet, but
416 * calculate it here so that peer_challenge and username need
418 if (sm
->user
->password_hash
) {
419 pw_hash
= sm
->user
->password
;
421 if (nt_password_hash(sm
->user
->password
,
422 sm
->user
->password_len
,
424 data
->state
= FAILURE
;
427 pw_hash
= pw_hash_buf
;
429 if (generate_authenticator_response_pwhash(
430 pw_hash
, peer_challenge
, data
->auth_challenge
,
431 username
, username_len
, nt_response
,
432 data
->auth_response
) < 0 ||
433 hash_nt_password_hash(pw_hash
, pw_hash_hash
) < 0 ||
434 get_master_key(pw_hash_hash
, nt_response
,
436 data
->state
= FAILURE
;
439 data
->master_key_valid
= 1;
440 wpa_hexdump_key(MSG_DEBUG
, "EAP-MSCHAPV2: Derived Master Key",
441 data
->master_key
, MSCHAPV2_KEY_LEN
);
443 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: Expected NT-Response",
445 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Invalid NT-Response");
446 data
->state
= FAILURE_REQ
;
451 static void eap_mschapv2_process_success_resp(struct eap_sm
*sm
,
452 struct eap_mschapv2_data
*data
,
453 struct wpabuf
*respData
)
455 struct eap_mschapv2_hdr
*resp
;
459 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, respData
,
461 if (pos
== NULL
|| len
< 1)
462 return; /* Should not happen - frame already validated */
464 resp
= (struct eap_mschapv2_hdr
*) pos
;
466 if (resp
->op_code
== MSCHAPV2_OP_SUCCESS
) {
467 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Received Success Response"
468 " - authentication completed successfully");
469 data
->state
= SUCCESS
;
471 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Did not receive Success "
472 "Response - peer rejected authentication");
473 data
->state
= FAILURE
;
478 static void eap_mschapv2_process_failure_resp(struct eap_sm
*sm
,
479 struct eap_mschapv2_data
*data
,
480 struct wpabuf
*respData
)
482 struct eap_mschapv2_hdr
*resp
;
486 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
, respData
,
488 if (pos
== NULL
|| len
< 1)
489 return; /* Should not happen - frame already validated */
491 resp
= (struct eap_mschapv2_hdr
*) pos
;
493 if (resp
->op_code
== MSCHAPV2_OP_FAILURE
) {
494 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Received Failure Response"
495 " - authentication failed");
497 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Did not receive Failure "
498 "Response - authentication failed");
501 data
->state
= FAILURE
;
505 static void eap_mschapv2_process(struct eap_sm
*sm
, void *priv
,
506 struct wpabuf
*respData
)
508 struct eap_mschapv2_data
*data
= priv
;
510 if (sm
->user
== NULL
|| sm
->user
->password
== NULL
) {
511 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Password not configured");
512 data
->state
= FAILURE
;
516 switch (data
->state
) {
518 eap_mschapv2_process_response(sm
, data
, respData
);
521 eap_mschapv2_process_success_resp(sm
, data
, respData
);
524 eap_mschapv2_process_failure_resp(sm
, data
, respData
);
527 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Unknown state %d in "
528 "process", data
->state
);
534 static Boolean
eap_mschapv2_isDone(struct eap_sm
*sm
, void *priv
)
536 struct eap_mschapv2_data
*data
= priv
;
537 return data
->state
== SUCCESS
|| data
->state
== FAILURE
;
541 static u8
* eap_mschapv2_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
543 struct eap_mschapv2_data
*data
= priv
;
546 if (data
->state
!= SUCCESS
|| !data
->master_key_valid
)
549 *len
= 2 * MSCHAPV2_KEY_LEN
;
550 key
= os_malloc(*len
);
553 /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
554 get_asymetric_start_key(data
->master_key
, key
, MSCHAPV2_KEY_LEN
, 0, 1);
555 get_asymetric_start_key(data
->master_key
, key
+ MSCHAPV2_KEY_LEN
,
556 MSCHAPV2_KEY_LEN
, 1, 1);
557 wpa_hexdump_key(MSG_DEBUG
, "EAP-MSCHAPV2: Derived key", key
, *len
);
563 static Boolean
eap_mschapv2_isSuccess(struct eap_sm
*sm
, void *priv
)
565 struct eap_mschapv2_data
*data
= priv
;
566 return data
->state
== SUCCESS
;
570 int eap_server_mschapv2_register(void)
572 struct eap_method
*eap
;
574 eap
= eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION
,
575 EAP_VENDOR_IETF
, EAP_TYPE_MSCHAPV2
,
580 eap
->init
= eap_mschapv2_init
;
581 eap
->reset
= eap_mschapv2_reset
;
582 eap
->buildReq
= eap_mschapv2_buildReq
;
583 eap
->check
= eap_mschapv2_check
;
584 eap
->process
= eap_mschapv2_process
;
585 eap
->isDone
= eap_mschapv2_isDone
;
586 eap
->getKey
= eap_mschapv2_getKey
;
587 eap
->isSuccess
= eap_mschapv2_isSuccess
;
589 return eap_server_method_register(eap
);