1 /*
2 * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
3 * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9 #include "includes.h"
10
11 #include "common.h"
12 #include "crypto/ms_funcs.h"
13 #include "crypto/random.h"
14 #include "eap_i.h"
15
16
17 struct eap_mschapv2_hdr {
18 u8 op_code; /* MSCHAPV2_OP_* */
19 u8 mschapv2_id; /* must be changed for challenges, but not for
20 * success/failure */
21 u8 ms_length[2]; /* Note: misaligned; length - 5 */
22 /* followed by data */
23 } STRUCT_PACKED;
24
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
30
31 #define MSCHAPV2_RESP_LEN 49
32
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
39
40 #define PASSWD_CHANGE_CHAL_LEN 16
41 #define MSCHAPV2_KEY_LEN 16
42
43
44 #define CHALLENGE_LEN 16
45
46 struct eap_mschapv2_data {
47 u8 auth_challenge[CHALLENGE_LEN];
48 int auth_challenge_from_tls;
49 u8 *peer_challenge;
50 u8 auth_response[20];
51 enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
52 u8 resp_mschapv2_id;
53 u8 master_key[16];
54 int master_key_valid;
55 };
56
57
eap_mschapv2_init(struct eap_sm * sm)58 static void * eap_mschapv2_init(struct eap_sm *sm)
59 {
60 struct eap_mschapv2_data *data;
61
62 data = os_zalloc(sizeof(*data));
63 if (data == NULL)
64 return NULL;
65 data->state = CHALLENGE;
66
67 if (sm->auth_challenge) {
68 os_memcpy(data->auth_challenge, sm->auth_challenge,
69 CHALLENGE_LEN);
70 data->auth_challenge_from_tls = 1;
71 }
72
73 if (sm->peer_challenge) {
74 data->peer_challenge = os_memdup(sm->peer_challenge,
75 CHALLENGE_LEN);
76 if (data->peer_challenge == NULL) {
77 os_free(data);
78 return NULL;
79 }
80 }
81
82 return data;
83 }
84
85
eap_mschapv2_reset(struct eap_sm * sm,void * priv)86 static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
87 {
88 struct eap_mschapv2_data *data = priv;
89 if (data == NULL)
90 return;
91
92 os_free(data->peer_challenge);
93 bin_clear_free(data, sizeof(*data));
94 }
95
96
eap_mschapv2_build_challenge(struct eap_sm * sm,struct eap_mschapv2_data * data,u8 id)97 static struct wpabuf * eap_mschapv2_build_challenge(
98 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
99 {
100 struct wpabuf *req;
101 struct eap_mschapv2_hdr *ms;
102 size_t ms_len;
103
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 "
107 "data");
108 data->state = FAILURE;
109 return NULL;
110 }
111
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);
115 if (req == NULL) {
116 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
117 " for request");
118 data->state = FAILURE;
119 return NULL;
120 }
121
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);
126
127 wpabuf_put_u8(req, CHALLENGE_LEN);
128 if (!data->auth_challenge_from_tls)
129 wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
130 else
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);
135
136 return req;
137 }
138
139
eap_mschapv2_build_success_req(struct eap_sm * sm,struct eap_mschapv2_data * data,u8 id)140 static struct wpabuf * eap_mschapv2_build_success_req(
141 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
142 {
143 struct wpabuf *req;
144 struct eap_mschapv2_hdr *ms;
145 u8 *msg;
146 char *message = "OK";
147 size_t ms_len;
148
149 ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
150 os_strlen(message);
151 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
152 EAP_CODE_REQUEST, id);
153 if (req == NULL) {
154 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
155 " for request");
156 data->state = FAILURE;
157 return NULL;
158 }
159
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);
165
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));
176
177 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
178 msg, ms_len - sizeof(*ms));
179
180 return req;
181 }
182
183
eap_mschapv2_build_failure_req(struct eap_sm * sm,struct eap_mschapv2_data * data,u8 id)184 static struct wpabuf * eap_mschapv2_build_failure_req(
185 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
186 {
187 struct wpabuf *req;
188 struct eap_mschapv2_hdr *ms;
189 char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
190 "M=FAILED";
191 size_t ms_len;
192
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);
196 if (req == NULL) {
197 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
198 " for request");
199 data->state = FAILURE;
200 return NULL;
201 }
202
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);
207
208 wpabuf_put_data(req, message, os_strlen(message));
209
210 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
211 (u8 *) message, os_strlen(message));
212
213 return req;
214 }
215
216
eap_mschapv2_buildReq(struct eap_sm * sm,void * priv,u8 id)217 static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
218 u8 id)
219 {
220 struct eap_mschapv2_data *data = priv;
221
222 switch (data->state) {
223 case CHALLENGE:
224 return eap_mschapv2_build_challenge(sm, data, id);
225 case SUCCESS_REQ:
226 return eap_mschapv2_build_success_req(sm, data, id);
227 case FAILURE_REQ:
228 return eap_mschapv2_build_failure_req(sm, data, id);
229 default:
230 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
231 "buildReq", data->state);
232 break;
233 }
234 return NULL;
235 }
236
237
eap_mschapv2_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)238 static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
239 struct wpabuf *respData)
240 {
241 struct eap_mschapv2_data *data = priv;
242 struct eap_mschapv2_hdr *resp;
243 const u8 *pos;
244 size_t len;
245
246 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
247 &len);
248 if (pos == NULL || len < 1) {
249 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
250 return TRUE;
251 }
252
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);
258 return TRUE;
259 }
260
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);
266 return TRUE;
267 }
268
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);
273 return TRUE;
274 }
275
276 return FALSE;
277 }
278
279
eap_mschapv2_process_response(struct eap_sm * sm,struct eap_mschapv2_data * data,struct wpabuf * respData)280 static void eap_mschapv2_process_response(struct eap_sm *sm,
281 struct eap_mschapv2_data *data,
282 struct wpabuf *respData)
283 {
284 struct eap_mschapv2_hdr *resp;
285 const u8 *pos, *end, *peer_challenge, *nt_response, *name;
286 u8 flags;
287 size_t len, name_len, i;
288 u8 expected[24];
289 const u8 *username, *user;
290 size_t username_len, user_len;
291 int res;
292 char *buf;
293
294 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
295 &len);
296 if (pos == NULL || len < 1)
297 return; /* Should not happen - frame already validated */
298
299 end = pos + len;
300 resp = (struct eap_mschapv2_hdr *) pos;
301 pos = (u8 *) (resp + 1);
302
303 if (len < sizeof(*resp) + 1 + 49 ||
304 resp->op_code != MSCHAPV2_OP_RESPONSE ||
305 pos[0] != 49) {
306 wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
307 respData);
308 data->state = FAILURE;
309 return;
310 }
311 data->resp_mschapv2_id = resp->mschapv2_id;
312 pos++;
313 peer_challenge = pos;
314 pos += 16 + 8;
315 nt_response = pos;
316 pos += 24;
317 flags = *pos++;
318 name = pos;
319 name_len = end - name;
320
321 if (data->peer_challenge) {
322 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
323 "Peer-Challenge");
324 peer_challenge = data->peer_challenge;
325 }
326 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
327 peer_challenge, 16);
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);
331
332 buf = os_malloc(name_len * 4 + 1);
333 if (buf) {
334 printf_encode(buf, name_len * 4 + 1, name, name_len);
335 eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf);
336 os_free(buf);
337 }
338
339 /* MSCHAPv2 does not include optional domain name in the
340 * challenge-response calculation, so remove domain prefix
341 * (if present). */
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;
347 username += i + 1;
348 break;
349 }
350 }
351
352 user = name;
353 user_len = name_len;
354 for (i = 0; i < user_len; i++) {
355 if (user[i] == '\\') {
356 user_len -= i + 1;
357 user += i + 1;
358 break;
359 }
360 }
361
362 #ifdef CONFIG_TESTING_OPTIONS
363 {
364 u8 challenge[8];
365
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);
371 }
372 }
373 #endif /* CONFIG_TESTING_OPTIONS */
374
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;
383 return;
384 }
385
386 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
387 username, username_len);
388
389 if (sm->user->password_hash) {
390 res = generate_nt_response_pwhash(data->auth_challenge,
391 peer_challenge,
392 username, username_len,
393 sm->user->password,
394 expected);
395 } else {
396 res = generate_nt_response(data->auth_challenge,
397 peer_challenge,
398 username, username_len,
399 sm->user->password,
400 sm->user->password_len,
401 expected);
402 }
403 if (res) {
404 data->state = FAILURE;
405 return;
406 }
407
408 if (os_memcmp_const(nt_response, expected, 24) == 0) {
409 const u8 *pw_hash;
410 u8 pw_hash_buf[16], pw_hash_hash[16];
411
412 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
413 data->state = SUCCESS_REQ;
414
415 /* Authenticator response is not really needed yet, but
416 * calculate it here so that peer_challenge and username need
417 * not be saved. */
418 if (sm->user->password_hash) {
419 pw_hash = sm->user->password;
420 } else {
421 if (nt_password_hash(sm->user->password,
422 sm->user->password_len,
423 pw_hash_buf) < 0) {
424 data->state = FAILURE;
425 return;
426 }
427 pw_hash = pw_hash_buf;
428 }
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,
435 data->master_key)) {
436 data->state = FAILURE;
437 return;
438 }
439 data->master_key_valid = 1;
440 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
441 data->master_key, MSCHAPV2_KEY_LEN);
442 } else {
443 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
444 expected, 24);
445 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
446 data->state = FAILURE_REQ;
447 }
448 }
449
450
eap_mschapv2_process_success_resp(struct eap_sm * sm,struct eap_mschapv2_data * data,struct wpabuf * respData)451 static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
452 struct eap_mschapv2_data *data,
453 struct wpabuf *respData)
454 {
455 struct eap_mschapv2_hdr *resp;
456 const u8 *pos;
457 size_t len;
458
459 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
460 &len);
461 if (pos == NULL || len < 1)
462 return; /* Should not happen - frame already validated */
463
464 resp = (struct eap_mschapv2_hdr *) pos;
465
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;
470 } else {
471 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
472 "Response - peer rejected authentication");
473 data->state = FAILURE;
474 }
475 }
476
477
eap_mschapv2_process_failure_resp(struct eap_sm * sm,struct eap_mschapv2_data * data,struct wpabuf * respData)478 static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
479 struct eap_mschapv2_data *data,
480 struct wpabuf *respData)
481 {
482 struct eap_mschapv2_hdr *resp;
483 const u8 *pos;
484 size_t len;
485
486 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
487 &len);
488 if (pos == NULL || len < 1)
489 return; /* Should not happen - frame already validated */
490
491 resp = (struct eap_mschapv2_hdr *) pos;
492
493 if (resp->op_code == MSCHAPV2_OP_FAILURE) {
494 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
495 " - authentication failed");
496 } else {
497 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
498 "Response - authentication failed");
499 }
500
501 data->state = FAILURE;
502 }
503
504
eap_mschapv2_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)505 static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
506 struct wpabuf *respData)
507 {
508 struct eap_mschapv2_data *data = priv;
509
510 if (sm->user == NULL || sm->user->password == NULL) {
511 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
512 data->state = FAILURE;
513 return;
514 }
515
516 switch (data->state) {
517 case CHALLENGE:
518 eap_mschapv2_process_response(sm, data, respData);
519 break;
520 case SUCCESS_REQ:
521 eap_mschapv2_process_success_resp(sm, data, respData);
522 break;
523 case FAILURE_REQ:
524 eap_mschapv2_process_failure_resp(sm, data, respData);
525 break;
526 default:
527 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
528 "process", data->state);
529 break;
530 }
531 }
532
533
eap_mschapv2_isDone(struct eap_sm * sm,void * priv)534 static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
535 {
536 struct eap_mschapv2_data *data = priv;
537 return data->state == SUCCESS || data->state == FAILURE;
538 }
539
540
eap_mschapv2_getKey(struct eap_sm * sm,void * priv,size_t * len)541 static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
542 {
543 struct eap_mschapv2_data *data = priv;
544 u8 *key;
545
546 if (data->state != SUCCESS || !data->master_key_valid)
547 return NULL;
548
549 *len = 2 * MSCHAPV2_KEY_LEN;
550 key = os_malloc(*len);
551 if (key == NULL)
552 return NULL;
553 /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
554 if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0,
555 1) < 0 ||
556 get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
557 MSCHAPV2_KEY_LEN, 1, 1) < 0) {
558 os_free(key);
559 return NULL;
560 }
561 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
562
563 return key;
564 }
565
566
eap_mschapv2_isSuccess(struct eap_sm * sm,void * priv)567 static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
568 {
569 struct eap_mschapv2_data *data = priv;
570 return data->state == SUCCESS;
571 }
572
573
eap_server_mschapv2_register(void)574 int eap_server_mschapv2_register(void)
575 {
576 struct eap_method *eap;
577
578 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
579 EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
580 "MSCHAPV2");
581 if (eap == NULL)
582 return -1;
583
584 eap->init = eap_mschapv2_init;
585 eap->reset = eap_mschapv2_reset;
586 eap->buildReq = eap_mschapv2_buildReq;
587 eap->check = eap_mschapv2_check;
588 eap->process = eap_mschapv2_process;
589 eap->isDone = eap_mschapv2_isDone;
590 eap->getKey = eap_mschapv2_getKey;
591 eap->isSuccess = eap_mschapv2_isSuccess;
592
593 return eap_server_method_register(eap);
594 }
595