Oops, forgot to hook CVE-2007-1218 fix into building.
[dragonfly.git] / contrib / hostapd-0.4.9 / eap_mschapv2.c
blob097da254170231fc5b3b77d535a709d9731977bd
1 /*
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
10 * license.
12 * See README and COPYING for more details.
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <netinet/in.h>
20 #include "hostapd.h"
21 #include "common.h"
22 #include "eap_i.h"
23 #include "ms_funcs.h"
26 struct eap_mschapv2_hdr {
27 u8 code;
28 u8 identifier;
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
33 * success/failure */
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];
60 u8 auth_response[20];
61 enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
62 u8 resp_mschapv2_id;
66 static void * eap_mschapv2_init(struct eap_sm *sm)
68 struct eap_mschapv2_data *data;
70 data = malloc(sizeof(*data));
71 if (data == NULL)
72 return data;
73 memset(data, 0, sizeof(*data));
74 data->state = CHALLENGE;
76 return data;
80 static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
82 struct eap_mschapv2_data *data = priv;
83 free(data);
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;
92 u8 *pos;
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 "
97 "data");
98 data->state = FAILURE;
99 return NULL;
102 *reqDataLen = sizeof(*req) + 1 + CHALLENGE_LEN + strlen(name);
103 req = malloc(*reqDataLen);
104 if (req == NULL) {
105 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
106 " for request");
107 data->state = FAILURE;
108 return NULL;
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,
123 CHALLENGE_LEN);
124 pos += CHALLENGE_LEN;
125 memcpy(pos, name, strlen(name));
127 return (u8 *) req;
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;
136 u8 *pos, *msg, *end;
137 char *message = "OK";
138 size_t msg_len;
139 int i;
141 msg_len = 2 + 2 * sizeof(data->auth_response) + 3 + strlen(message);
142 *reqDataLen = sizeof(*req) + msg_len;
143 req = malloc(*reqDataLen + 1);
144 if (req == NULL) {
145 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
146 " for request");
147 data->state = FAILURE;
148 return NULL;
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",
171 msg, msg_len);
173 return (u8 *) req;
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;
182 u8 *pos;
183 char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
184 "M=FAILED";
185 size_t msg_len;
187 msg_len = strlen(message);
188 *reqDataLen = sizeof(*req) + msg_len;
189 req = malloc(*reqDataLen + 1);
190 if (req == NULL) {
191 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
192 " for request");
193 data->state = FAILURE;
194 return NULL;
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);
212 return (u8 *) req;
216 static u8 * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, int id,
217 size_t *reqDataLen)
219 struct eap_mschapv2_data *data = priv;
221 switch (data->state) {
222 case CHALLENGE:
223 return eap_mschapv2_build_challenge(sm, data, id, reqDataLen);
224 case SUCCESS_REQ:
225 return eap_mschapv2_build_success_req(sm, data, id,
226 reqDataLen);
227 case FAILURE_REQ:
228 return eap_mschapv2_build_failure_req(sm, data, id,
229 reqDataLen);
230 default:
231 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
232 "buildReq", data->state);
233 break;
235 return NULL;
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;
244 u8 *pos;
245 size_t len;
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");
252 return TRUE;
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);
259 return TRUE;
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);
267 return TRUE;
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);
274 return TRUE;
277 return FALSE;
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;
286 u8 *pos;
287 u8 *peer_challenge, *nt_response, flags, *name;
288 size_t name_len;
289 u8 expected[24];
290 int i;
291 u8 *username, *user;
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 ||
299 pos[0] != 49) {
300 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
301 respData, respDataLen);
302 data->state = FAILURE;
303 return;
305 data->resp_mschapv2_id = resp->mschapv2_id;
306 pos++;
307 peer_challenge = pos;
308 pos += 16 + 8;
309 nt_response = pos;
310 pos += 24;
311 flags = *pos++;
312 name = pos;
313 name_len = respData + respDataLen - name;
315 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
316 peer_challenge, 16);
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
323 * (if present). */
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;
329 username += i + 1;
330 break;
334 user = name;
335 user_len = name_len;
336 for (i = 0; i < user_len; i++) {
337 if (user[i] == '\\') {
338 user_len -= i + 1;
339 user += i + 1;
340 break;
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;
352 return;
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,
361 expected);
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
370 * not be saved. */
371 generate_authenticator_response(sm->user->password,
372 sm->user->password_len,
373 peer_challenge,
374 data->auth_challenge,
375 username, username_len,
376 nt_response,
377 data->auth_response);
378 } else {
379 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
380 expected, 24);
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;
399 } else {
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");
418 } else {
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;
435 return;
438 switch (data->state) {
439 case CHALLENGE:
440 eap_mschapv2_process_response(sm, data, respData, respDataLen);
441 break;
442 case SUCCESS_REQ:
443 eap_mschapv2_process_success_resp(sm, data, respData,
444 respDataLen);
445 break;
446 case FAILURE_REQ:
447 eap_mschapv2_process_failure_resp(sm, data, respData,
448 respDataLen);
449 break;
450 default:
451 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
452 "process", data->state);
453 break;
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,
475 .name = "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,