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,