2 * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
3 * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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.
18 #include <netinet/in.h>
26 struct eap_mschapv2_hdr
{
29 u16 length
; /* including code, identifier, and length */
30 u8 type
; /* EAP_TYPE_MSCHAPV2 */
31 u8 op_code
; /* MSCHAPV2_OP_* */
32 u8 mschapv2_id
; /* must be changed for challenges, but not for
34 u8 ms_length
[2]; /* Note: misaligned; length - 5 */
35 /* followed by data */
36 } __attribute__ ((packed
));
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
44 #define MSCHAPV2_RESP_LEN 49
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
53 #define PASSWD_CHANGE_CHAL_LEN 16
56 #define CHALLENGE_LEN 16
58 struct eap_mschapv2_data
{
59 u8 auth_challenge
[CHALLENGE_LEN
];
61 enum { CHALLENGE
, SUCCESS_REQ
, FAILURE_REQ
, SUCCESS
, FAILURE
} state
;
66 static void * eap_mschapv2_init(struct eap_sm
*sm
)
68 struct eap_mschapv2_data
*data
;
70 data
= malloc(sizeof(*data
));
73 memset(data
, 0, sizeof(*data
));
74 data
->state
= CHALLENGE
;
80 static void eap_mschapv2_reset(struct eap_sm
*sm
, void *priv
)
82 struct eap_mschapv2_data
*data
= priv
;
87 static u8
* eap_mschapv2_build_challenge(struct eap_sm
*sm
,
88 struct eap_mschapv2_data
*data
,
89 int id
, size_t *reqDataLen
)
91 struct eap_mschapv2_hdr
*req
;
93 char *name
= "hostapd"; /* TODO: make this configurable */
95 if (hostapd_get_rand(data
->auth_challenge
, CHALLENGE_LEN
)) {
96 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to get random "
98 data
->state
= FAILURE
;
102 *reqDataLen
= sizeof(*req
) + 1 + CHALLENGE_LEN
+ strlen(name
);
103 req
= malloc(*reqDataLen
);
105 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to allocate memory"
107 data
->state
= FAILURE
;
111 req
->code
= EAP_CODE_REQUEST
;
112 req
->identifier
= id
;
113 req
->length
= htons(*reqDataLen
);
114 req
->type
= EAP_TYPE_MSCHAPV2
;
115 req
->op_code
= MSCHAPV2_OP_CHALLENGE
;
116 req
->mschapv2_id
= id
;
117 req
->ms_length
[0] = (*reqDataLen
- 5) >> 8;
118 req
->ms_length
[1] = (*reqDataLen
- 5) & 0xff;
119 pos
= (u8
*) (req
+ 1);
120 *pos
++ = CHALLENGE_LEN
;
121 memcpy(pos
, data
->auth_challenge
, CHALLENGE_LEN
);
122 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: Challenge", pos
,
124 pos
+= CHALLENGE_LEN
;
125 memcpy(pos
, name
, strlen(name
));
131 static u8
* eap_mschapv2_build_success_req(struct eap_sm
*sm
,
132 struct eap_mschapv2_data
*data
,
133 int id
, size_t *reqDataLen
)
135 struct eap_mschapv2_hdr
*req
;
137 char *message
= "OK";
141 msg_len
= 2 + 2 * sizeof(data
->auth_response
) + 3 + strlen(message
);
142 *reqDataLen
= sizeof(*req
) + msg_len
;
143 req
= malloc(*reqDataLen
+ 1);
145 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to allocate memory"
147 data
->state
= FAILURE
;
151 req
->code
= EAP_CODE_REQUEST
;
152 req
->identifier
= id
;
153 req
->length
= htons(*reqDataLen
);
154 req
->type
= EAP_TYPE_MSCHAPV2
;
155 req
->op_code
= MSCHAPV2_OP_SUCCESS
;
156 req
->mschapv2_id
= data
->resp_mschapv2_id
;
157 req
->ms_length
[0] = (*reqDataLen
- 5) >> 8;
158 req
->ms_length
[1] = (*reqDataLen
- 5) & 0xff;
160 msg
= pos
= (u8
*) (req
+ 1);
161 end
= ((u8
*) req
) + *reqDataLen
+ 1;
163 pos
+= snprintf((char *) pos
, end
- pos
, "S=");
164 for (i
= 0; i
< sizeof(data
->auth_response
); i
++) {
165 pos
+= snprintf((char *) pos
, end
- pos
, "%02X",
166 data
->auth_response
[i
]);
168 pos
+= snprintf((char *) pos
, end
- pos
, " M=%s", message
);
170 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: Success Request Message",
177 static u8
* eap_mschapv2_build_failure_req(struct eap_sm
*sm
,
178 struct eap_mschapv2_data
*data
,
179 int id
, size_t *reqDataLen
)
181 struct eap_mschapv2_hdr
*req
;
183 char *message
= "E=691 R=0 C=00000000000000000000000000000000 V=3 "
187 msg_len
= strlen(message
);
188 *reqDataLen
= sizeof(*req
) + msg_len
;
189 req
= malloc(*reqDataLen
+ 1);
191 wpa_printf(MSG_ERROR
, "EAP-MSCHAPV2: Failed to allocate memory"
193 data
->state
= FAILURE
;
197 req
->code
= EAP_CODE_REQUEST
;
198 req
->identifier
= id
;
199 req
->length
= htons(*reqDataLen
);
200 req
->type
= EAP_TYPE_MSCHAPV2
;
201 req
->op_code
= MSCHAPV2_OP_FAILURE
;
202 req
->mschapv2_id
= data
->resp_mschapv2_id
;
203 req
->ms_length
[0] = (*reqDataLen
- 5) >> 8;
204 req
->ms_length
[1] = (*reqDataLen
- 5) & 0xff;
206 pos
= (u8
*) (req
+ 1);
207 memcpy(pos
, message
, msg_len
);
209 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: Failure Request Message",
210 (u8
*) message
, msg_len
);
216 static u8
* eap_mschapv2_buildReq(struct eap_sm
*sm
, void *priv
, int id
,
219 struct eap_mschapv2_data
*data
= priv
;
221 switch (data
->state
) {
223 return eap_mschapv2_build_challenge(sm
, data
, id
, reqDataLen
);
225 return eap_mschapv2_build_success_req(sm
, data
, id
,
228 return eap_mschapv2_build_failure_req(sm
, data
, id
,
231 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Unknown state %d in "
232 "buildReq", data
->state
);
239 static Boolean
eap_mschapv2_check(struct eap_sm
*sm
, void *priv
,
240 u8
*respData
, size_t respDataLen
)
242 struct eap_mschapv2_data
*data
= priv
;
243 struct eap_mschapv2_hdr
*resp
;
247 resp
= (struct eap_mschapv2_hdr
*) respData
;
248 pos
= (u8
*) (resp
+ 1);
249 if (respDataLen
< 6 || resp
->type
!= EAP_TYPE_MSCHAPV2
||
250 (len
= ntohs(resp
->length
)) > respDataLen
) {
251 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Invalid frame");
255 if (data
->state
== CHALLENGE
&&
256 resp
->op_code
!= MSCHAPV2_OP_RESPONSE
) {
257 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Expected Response - "
258 "ignore op %d", resp
->op_code
);
262 if (data
->state
== SUCCESS_REQ
&&
263 resp
->op_code
!= MSCHAPV2_OP_SUCCESS
&&
264 resp
->op_code
!= MSCHAPV2_OP_FAILURE
) {
265 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Expected Success or "
266 "Failure - ignore op %d", resp
->op_code
);
270 if (data
->state
== FAILURE_REQ
&&
271 resp
->op_code
!= MSCHAPV2_OP_FAILURE
) {
272 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Expected Failure "
273 "- ignore op %d", resp
->op_code
);
281 static void eap_mschapv2_process_response(struct eap_sm
*sm
,
282 struct eap_mschapv2_data
*data
,
283 u8
*respData
, size_t respDataLen
)
285 struct eap_mschapv2_hdr
*resp
;
287 u8
*peer_challenge
, *nt_response
, flags
, *name
;
292 size_t username_len
, user_len
;
294 resp
= (struct eap_mschapv2_hdr
*) respData
;
295 pos
= (u8
*) (resp
+ 1);
297 if (respDataLen
< sizeof(*resp
) + 1 + 49 ||
298 resp
->op_code
!= MSCHAPV2_OP_RESPONSE
||
300 wpa_hexdump(MSG_DEBUG
, "EAP-MSCHAPV2: Invalid response",
301 respData
, respDataLen
);
302 data
->state
= FAILURE
;
305 data
->resp_mschapv2_id
= resp
->mschapv2_id
;
307 peer_challenge
= pos
;
313 name_len
= respData
+ respDataLen
- name
;
315 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: Peer-Challenge",
317 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: NT-Response", nt_response
, 24);
318 wpa_printf(MSG_MSGDUMP
, "EAP-MSCHAPV2: Flags 0x%x", flags
);
319 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: Name", name
, name_len
);
321 /* MSCHAPv2 does not include optional domain name in the
322 * challenge-response calculation, so remove domain prefix
324 username
= sm
->identity
;
325 username_len
= sm
->identity_len
;
326 for (i
= 0; i
< username_len
; i
++) {
327 if (username
[i
] == '\\') {
328 username_len
-= i
+ 1;
336 for (i
= 0; i
< user_len
; i
++) {
337 if (user
[i
] == '\\') {
344 if (username_len
!= user_len
||
345 memcmp(username
, user
, username_len
) != 0) {
346 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Mismatch in user names");
347 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: Expected user "
348 "name", username
, username_len
);
349 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-MSCHAPV2: Received user "
350 "name", user
, user_len
);
351 data
->state
= FAILURE
;
355 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-MSCHAPV2: User name",
356 username
, username_len
);
358 generate_nt_response(data
->auth_challenge
, peer_challenge
,
359 username
, username_len
,
360 sm
->user
->password
, sm
->user
->password_len
,
363 if (memcmp(nt_response
, expected
, 24) == 0) {
364 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Correct NT-Response");
365 data
->state
= SUCCESS_REQ
;
368 /* Authenticator response is not really needed yet, but
369 * calculate it here so that peer_challenge and username need
371 generate_authenticator_response(sm
->user
->password
,
372 sm
->user
->password_len
,
374 data
->auth_challenge
,
375 username
, username_len
,
377 data
->auth_response
);
379 wpa_hexdump(MSG_MSGDUMP
, "EAP-MSCHAPV2: Expected NT-Response",
381 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Invalid NT-Response");
382 data
->state
= FAILURE_REQ
;
387 static void eap_mschapv2_process_success_resp(struct eap_sm
*sm
,
388 struct eap_mschapv2_data
*data
,
389 u8
*respData
, size_t respDataLen
)
391 struct eap_mschapv2_hdr
*resp
;
393 resp
= (struct eap_mschapv2_hdr
*) respData
;
395 if (resp
->op_code
== MSCHAPV2_OP_SUCCESS
) {
396 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Received Success Response"
397 " - authentication completed successfully");
398 data
->state
= SUCCESS
;
400 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Did not receive Success "
401 "Response - peer rejected authentication");
402 data
->state
= FAILURE
;
407 static void eap_mschapv2_process_failure_resp(struct eap_sm
*sm
,
408 struct eap_mschapv2_data
*data
,
409 u8
*respData
, size_t respDataLen
)
411 struct eap_mschapv2_hdr
*resp
;
413 resp
= (struct eap_mschapv2_hdr
*) respData
;
415 if (resp
->op_code
== MSCHAPV2_OP_FAILURE
) {
416 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Received Failure Response"
417 " - authentication failed");
419 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Did not receive Failure "
420 "Response - authentication failed");
423 data
->state
= FAILURE
;
427 static void eap_mschapv2_process(struct eap_sm
*sm
, void *priv
,
428 u8
*respData
, size_t respDataLen
)
430 struct eap_mschapv2_data
*data
= priv
;
432 if (sm
->user
== NULL
|| sm
->user
->password
== NULL
) {
433 wpa_printf(MSG_INFO
, "EAP-MSCHAPV2: Password not configured");
434 data
->state
= FAILURE
;
438 switch (data
->state
) {
440 eap_mschapv2_process_response(sm
, data
, respData
, respDataLen
);
443 eap_mschapv2_process_success_resp(sm
, data
, respData
,
447 eap_mschapv2_process_failure_resp(sm
, data
, respData
,
451 wpa_printf(MSG_DEBUG
, "EAP-MSCHAPV2: Unknown state %d in "
452 "process", data
->state
);
458 static Boolean
eap_mschapv2_isDone(struct eap_sm
*sm
, void *priv
)
460 struct eap_mschapv2_data
*data
= priv
;
461 return data
->state
== SUCCESS
|| data
->state
== FAILURE
;
465 static Boolean
eap_mschapv2_isSuccess(struct eap_sm
*sm
, void *priv
)
467 struct eap_mschapv2_data
*data
= priv
;
468 return data
->state
== SUCCESS
;
472 const struct eap_method eap_method_mschapv2
=
474 .method
= EAP_TYPE_MSCHAPV2
,
476 .init
= eap_mschapv2_init
,
477 .reset
= eap_mschapv2_reset
,
478 .buildReq
= eap_mschapv2_buildReq
,
479 .check
= eap_mschapv2_check
,
480 .process
= eap_mschapv2_process
,
481 .isDone
= eap_mschapv2_isDone
,
482 .isSuccess
= eap_mschapv2_isSuccess
,