1 /*
2 * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
3 * Copyright (c) 2004-2008, 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 * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
9 * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
10 * Extensions Protocol, Version 2, for mutual authentication and key
11 * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
12 * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
13 * RFC 3079.
14 */
15
16 #include "includes.h"
17
18 #include "common.h"
19 #include "crypto/ms_funcs.h"
20 #include "crypto/random.h"
21 #include "common/wpa_ctrl.h"
22 #include "mschapv2.h"
23 #include "eap_i.h"
24 #include "eap_config.h"
25
26
27 #ifdef _MSC_VER
28 #pragma pack(push, 1)
29 #endif /* _MSC_VER */
30
31 struct eap_mschapv2_hdr {
32 u8 op_code; /* MSCHAPV2_OP_* */
33 u8 mschapv2_id; /* usually same as EAP identifier; must be changed
34 * for challenges, but not for success/failure */
35 u8 ms_length[2]; /* Note: misaligned; length - 5 */
36 /* followed by data */
37 } STRUCT_PACKED;
38
39 /* Response Data field */
40 struct ms_response {
41 u8 peer_challenge[MSCHAPV2_CHAL_LEN];
42 u8 reserved[8];
43 u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
44 u8 flags;
45 } STRUCT_PACKED;
46
47 /* Change-Password Data field */
48 struct ms_change_password {
49 u8 encr_password[516];
50 u8 encr_hash[16];
51 u8 peer_challenge[MSCHAPV2_CHAL_LEN];
52 u8 reserved[8];
53 u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
54 u8 flags[2];
55 } STRUCT_PACKED;
56
57 #ifdef _MSC_VER
58 #pragma pack(pop)
59 #endif /* _MSC_VER */
60
61 #define MSCHAPV2_OP_CHALLENGE 1
62 #define MSCHAPV2_OP_RESPONSE 2
63 #define MSCHAPV2_OP_SUCCESS 3
64 #define MSCHAPV2_OP_FAILURE 4
65 #define MSCHAPV2_OP_CHANGE_PASSWORD 7
66
67 #define ERROR_RESTRICTED_LOGON_HOURS 646
68 #define ERROR_ACCT_DISABLED 647
69 #define ERROR_PASSWD_EXPIRED 648
70 #define ERROR_NO_DIALIN_PERMISSION 649
71 #define ERROR_AUTHENTICATION_FAILURE 691
72 #define ERROR_CHANGING_PASSWORD 709
73
74 #define PASSWD_CHANGE_CHAL_LEN 16
75 #define MSCHAPV2_KEY_LEN 16
76
77
78 struct eap_mschapv2_data {
79 u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
80 int auth_response_valid;
81
82 int prev_error;
83 u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
84 int passwd_change_challenge_valid;
85 int passwd_change_version;
86
87 /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
88 */
89 u8 *peer_challenge;
90 u8 *auth_challenge;
91
92 int phase2;
93 u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
94 int master_key_valid;
95 int success;
96
97 struct wpabuf *prev_challenge;
98 };
99
100
101 static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
102
103
eap_mschapv2_init(struct eap_sm * sm)104 static void * eap_mschapv2_init(struct eap_sm *sm)
105 {
106 struct eap_mschapv2_data *data;
107 data = os_zalloc(sizeof(*data));
108 if (data == NULL)
109 return NULL;
110
111 if (sm->peer_challenge) {
112 data->peer_challenge = os_memdup(sm->peer_challenge,
113 MSCHAPV2_CHAL_LEN);
114 if (data->peer_challenge == NULL) {
115 eap_mschapv2_deinit(sm, data);
116 return NULL;
117 }
118 }
119
120 if (sm->auth_challenge) {
121 data->auth_challenge = os_memdup(sm->auth_challenge,
122 MSCHAPV2_CHAL_LEN);
123 if (data->auth_challenge == NULL) {
124 eap_mschapv2_deinit(sm, data);
125 return NULL;
126 }
127 }
128
129 data->phase2 = sm->init_phase2;
130
131 return data;
132 }
133
134
eap_mschapv2_deinit(struct eap_sm * sm,void * priv)135 static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
136 {
137 struct eap_mschapv2_data *data = priv;
138 os_free(data->peer_challenge);
139 os_free(data->auth_challenge);
140 wpabuf_free(data->prev_challenge);
141 bin_clear_free(data, sizeof(*data));
142 }
143
144
eap_mschapv2_challenge_reply(struct eap_sm * sm,struct eap_mschapv2_data * data,u8 id,u8 mschapv2_id,const u8 * auth_challenge)145 static struct wpabuf * eap_mschapv2_challenge_reply(
146 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id,
147 u8 mschapv2_id, const u8 *auth_challenge)
148 {
149 struct wpabuf *resp;
150 struct eap_mschapv2_hdr *ms;
151 u8 *peer_challenge;
152 int ms_len;
153 struct ms_response *r;
154 size_t identity_len, password_len;
155 const u8 *identity, *password;
156 int pwhash;
157
158 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
159
160 identity = eap_get_config_identity(sm, &identity_len);
161 password = eap_get_config_password2(sm, &password_len, &pwhash);
162 if (identity == NULL || password == NULL)
163 return NULL;
164
165 ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
166 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
167 EAP_CODE_RESPONSE, id);
168 if (resp == NULL)
169 return NULL;
170
171 ms = wpabuf_put(resp, sizeof(*ms));
172 ms->op_code = MSCHAPV2_OP_RESPONSE;
173 ms->mschapv2_id = mschapv2_id;
174 if (data->prev_error) {
175 /*
176 * TODO: this does not seem to be enough when processing two
177 * or more failure messages. IAS did not increment mschapv2_id
178 * in its own packets, but it seemed to expect the peer to
179 * increment this for all packets(?).
180 */
181 ms->mschapv2_id++;
182 }
183 WPA_PUT_BE16(ms->ms_length, ms_len);
184
185 wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
186
187 /* Response */
188 r = wpabuf_put(resp, sizeof(*r));
189 peer_challenge = r->peer_challenge;
190 if (data->peer_challenge) {
191 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
192 "in Phase 1");
193 peer_challenge = data->peer_challenge;
194 os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
195 } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
196 wpabuf_free(resp);
197 return NULL;
198 }
199 os_memset(r->reserved, 0, 8);
200 if (data->auth_challenge) {
201 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
202 "in Phase 1");
203 auth_challenge = data->auth_challenge;
204 }
205 if (mschapv2_derive_response(identity, identity_len, password,
206 password_len, pwhash, auth_challenge,
207 peer_challenge, r->nt_response,
208 data->auth_response, data->master_key)) {
209 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive "
210 "response");
211 wpabuf_free(resp);
212 return NULL;
213 }
214 data->auth_response_valid = 1;
215 data->master_key_valid = 1;
216
217 r->flags = 0; /* reserved, must be zero */
218
219 wpabuf_put_data(resp, identity, identity_len);
220 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
221 "(response)", id, ms->mschapv2_id);
222 return resp;
223 }
224
225
226 /**
227 * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
228 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
229 * @data: Pointer to private EAP method data from eap_mschapv2_init()
230 * @ret: Return values from EAP request validation and processing
231 * @req: Pointer to EAP-MSCHAPv2 header from the request
232 * @req_len: Length of the EAP-MSCHAPv2 data
233 * @id: EAP identifier used in the request
234 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
235 * no reply available
236 */
eap_mschapv2_challenge(struct eap_sm * sm,struct eap_mschapv2_data * data,struct eap_method_ret * ret,const struct eap_mschapv2_hdr * req,size_t req_len,u8 id)237 static struct wpabuf * eap_mschapv2_challenge(
238 struct eap_sm *sm, struct eap_mschapv2_data *data,
239 struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
240 size_t req_len, u8 id)
241 {
242 size_t len, challenge_len;
243 const u8 *pos, *challenge;
244
245 if (eap_get_config_identity(sm, &len) == NULL ||
246 eap_get_config_password(sm, &len) == NULL)
247 return NULL;
248
249 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
250 if (req_len < sizeof(*req) + 1) {
251 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
252 "(len %lu)", (unsigned long) req_len);
253 ret->ignore = true;
254 return NULL;
255 }
256 pos = (const u8 *) (req + 1);
257 challenge_len = *pos++;
258 len = req_len - sizeof(*req) - 1;
259 if (challenge_len != MSCHAPV2_CHAL_LEN) {
260 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
261 "%lu", (unsigned long) challenge_len);
262 ret->ignore = true;
263 return NULL;
264 }
265
266 if (len < challenge_len) {
267 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
268 " packet: len=%lu challenge_len=%lu",
269 (unsigned long) len, (unsigned long) challenge_len);
270 ret->ignore = true;
271 return NULL;
272 }
273
274 if (data->passwd_change_challenge_valid) {
275 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
276 "failure message");
277 challenge = data->passwd_change_challenge;
278 } else
279 challenge = pos;
280 pos += challenge_len;
281 len -= challenge_len;
282 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
283 pos, len);
284
285 ret->ignore = false;
286 ret->methodState = METHOD_MAY_CONT;
287 ret->decision = DECISION_FAIL;
288 ret->allowNotifications = true;
289
290 return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
291 challenge);
292 }
293
294
eap_mschapv2_password_changed(struct eap_sm * sm,struct eap_mschapv2_data * data)295 static void eap_mschapv2_password_changed(struct eap_sm *sm,
296 struct eap_mschapv2_data *data)
297 {
298 struct eap_peer_config *config = eap_get_config(sm);
299 if (config && config->new_password) {
300 wpa_msg(sm->msg_ctx, MSG_INFO,
301 WPA_EVENT_PASSWORD_CHANGED
302 "EAP-MSCHAPV2: Password changed successfully");
303 data->prev_error = 0;
304 bin_clear_free(config->password, config->password_len);
305 if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
306 /* TODO: update external storage */
307 } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
308 config->password = os_malloc(16);
309 config->password_len = 16;
310 if (config->password &&
311 nt_password_hash(config->new_password,
312 config->new_password_len,
313 config->password)) {
314 bin_clear_free(config->password,
315 config->password_len);
316 config->password = NULL;
317 config->password_len = 0;
318 }
319 bin_clear_free(config->new_password,
320 config->new_password_len);
321 } else {
322 config->password = config->new_password;
323 config->password_len = config->new_password_len;
324 }
325 config->new_password = NULL;
326 config->new_password_len = 0;
327 }
328 }
329
330
331 /**
332 * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
333 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
334 * @data: Pointer to private EAP method data from eap_mschapv2_init()
335 * @ret: Return values from EAP request validation and processing
336 * @req: Pointer to EAP-MSCHAPv2 header from the request
337 * @req_len: Length of the EAP-MSCHAPv2 data
338 * @id: EAP identifier used in th erequest
339 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
340 * no reply available
341 */
eap_mschapv2_success(struct eap_sm * sm,struct eap_mschapv2_data * data,struct eap_method_ret * ret,const struct eap_mschapv2_hdr * req,size_t req_len,u8 id)342 static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
343 struct eap_mschapv2_data *data,
344 struct eap_method_ret *ret,
345 const struct eap_mschapv2_hdr *req,
346 size_t req_len, u8 id)
347 {
348 struct wpabuf *resp;
349 const u8 *pos;
350 size_t len;
351
352 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
353 len = req_len - sizeof(*req);
354 pos = (const u8 *) (req + 1);
355 if (!data->auth_response_valid ||
356 mschapv2_verify_auth_response(data->auth_response, pos, len)) {
357 wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
358 "response in success request");
359 ret->methodState = METHOD_DONE;
360 ret->decision = DECISION_FAIL;
361 return NULL;
362 }
363 pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
364 len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
365 while (len > 0 && *pos == ' ') {
366 pos++;
367 len--;
368 }
369 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
370 pos, len);
371 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
372
373 /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
374 * message. */
375 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
376 EAP_CODE_RESPONSE, id);
377 if (resp == NULL) {
378 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
379 "buffer for success response");
380 ret->ignore = true;
381 return NULL;
382 }
383
384 wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
385
386 ret->methodState = METHOD_DONE;
387 ret->decision = DECISION_UNCOND_SUCC;
388 ret->allowNotifications = false;
389 data->success = 1;
390
391 if (data->prev_error == ERROR_PASSWD_EXPIRED)
392 eap_mschapv2_password_changed(sm, data);
393
394 return resp;
395 }
396
397
eap_mschapv2_failure_txt(struct eap_sm * sm,struct eap_mschapv2_data * data,char * txt)398 static int eap_mschapv2_failure_txt(struct eap_sm *sm,
399 struct eap_mschapv2_data *data, char *txt)
400 {
401 char *pos, *msg = "";
402 int retry = 1;
403 struct eap_peer_config *config = eap_get_config(sm);
404
405 /* For example:
406 * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
407 */
408
409 pos = txt;
410
411 if (pos && os_strncmp(pos, "E=", 2) == 0) {
412 pos += 2;
413 data->prev_error = atoi(pos);
414 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
415 data->prev_error);
416 pos = os_strchr(pos, ' ');
417 if (pos)
418 pos++;
419 }
420
421 if (pos && os_strncmp(pos, "R=", 2) == 0) {
422 pos += 2;
423 retry = atoi(pos);
424 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
425 retry == 1 ? "" : "not ");
426 pos = os_strchr(pos, ' ');
427 if (pos)
428 pos++;
429 }
430
431 if (pos && os_strncmp(pos, "C=", 2) == 0) {
432 int hex_len;
433 pos += 2;
434 hex_len = os_strchr(pos, ' ') - (char *) pos;
435 if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
436 if (hexstr2bin(pos, data->passwd_change_challenge,
437 PASSWD_CHANGE_CHAL_LEN)) {
438 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
439 "failure challenge");
440 } else {
441 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
442 "challenge",
443 data->passwd_change_challenge,
444 PASSWD_CHANGE_CHAL_LEN);
445 data->passwd_change_challenge_valid = 1;
446 }
447 } else {
448 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
449 "challenge len %d", hex_len);
450 }
451 pos = os_strchr(pos, ' ');
452 if (pos)
453 pos++;
454 } else {
455 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
456 "was not present in failure message");
457 }
458
459 if (pos && os_strncmp(pos, "V=", 2) == 0) {
460 pos += 2;
461 data->passwd_change_version = atoi(pos);
462 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
463 "protocol version %d", data->passwd_change_version);
464 pos = os_strchr(pos, ' ');
465 if (pos)
466 pos++;
467 }
468
469 if (pos && os_strncmp(pos, "M=", 2) == 0) {
470 pos += 2;
471 msg = pos;
472 }
473 if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry &&
474 config && config->phase2 &&
475 os_strstr(config->phase2, "mschapv2_retry=0")) {
476 wpa_printf(MSG_DEBUG,
477 "EAP-MSCHAPV2: mark password retry disabled based on local configuration");
478 retry = 0;
479 }
480 wpa_msg(sm->msg_ctx, MSG_WARNING,
481 "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
482 "%d)",
483 msg, retry == 1 ? "" : "not ", data->prev_error);
484 if (data->prev_error == ERROR_PASSWD_EXPIRED &&
485 data->passwd_change_version == 3 && config) {
486 if (config->new_password == NULL) {
487 wpa_msg(sm->msg_ctx, MSG_INFO,
488 "EAP-MSCHAPV2: Password expired - password "
489 "change required");
490 eap_sm_request_new_password(sm);
491 }
492 } else if (retry == 1 && config) {
493 /* TODO: could prevent the current password from being used
494 * again at least for some period of time */
495 if (!config->mschapv2_retry)
496 eap_sm_request_identity(sm);
497 eap_sm_request_password(sm);
498 config->mschapv2_retry = 1;
499 } else if (config) {
500 /* TODO: prevent retries using same username/password */
501 config->mschapv2_retry = 0;
502 }
503
504 return retry == 1;
505 }
506
507
eap_mschapv2_change_password(struct eap_sm * sm,struct eap_mschapv2_data * data,struct eap_method_ret * ret,const struct eap_mschapv2_hdr * req,u8 id)508 static struct wpabuf * eap_mschapv2_change_password(
509 struct eap_sm *sm, struct eap_mschapv2_data *data,
510 struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
511 {
512 #ifdef CONFIG_NO_RC4
513 wpa_printf(MSG_ERROR,
514 "EAP-MSCHAPV2: RC4 not support in the build - cannot change password");
515 return NULL;
516 #else /* CONFIG_NO_RC4 */
517 struct wpabuf *resp;
518 int ms_len;
519 const u8 *username, *password, *new_password;
520 size_t username_len, password_len, new_password_len;
521 struct eap_mschapv2_hdr *ms;
522 struct ms_change_password *cp;
523 u8 password_hash[16], password_hash_hash[16];
524 int pwhash;
525
526 username = eap_get_config_identity(sm, &username_len);
527 password = eap_get_config_password2(sm, &password_len, &pwhash);
528 new_password = eap_get_config_new_password(sm, &new_password_len);
529 if (username == NULL || password == NULL || new_password == NULL)
530 return NULL;
531
532 username = mschapv2_remove_domain(username, &username_len);
533
534 ret->ignore = false;
535 ret->methodState = METHOD_MAY_CONT;
536 ret->decision = DECISION_COND_SUCC;
537 ret->allowNotifications = true;
538
539 ms_len = sizeof(*ms) + sizeof(*cp);
540 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
541 EAP_CODE_RESPONSE, id);
542 if (resp == NULL)
543 return NULL;
544
545 ms = wpabuf_put(resp, sizeof(*ms));
546 ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
547 ms->mschapv2_id = req->mschapv2_id + 1;
548 WPA_PUT_BE16(ms->ms_length, ms_len);
549 cp = wpabuf_put(resp, sizeof(*cp));
550
551 /* Encrypted-Password */
552 if (pwhash) {
553 if (encrypt_pw_block_with_password_hash(
554 new_password, new_password_len,
555 password, cp->encr_password))
556 goto fail;
557 } else {
558 if (new_password_encrypted_with_old_nt_password_hash(
559 new_password, new_password_len,
560 password, password_len, cp->encr_password))
561 goto fail;
562 }
563
564 /* Encrypted-Hash */
565 if (pwhash) {
566 u8 new_password_hash[16];
567 if (nt_password_hash(new_password, new_password_len,
568 new_password_hash) ||
569 nt_password_hash_encrypted_with_block(password,
570 new_password_hash,
571 cp->encr_hash))
572 goto fail;
573 } else {
574 if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
575 new_password, new_password_len,
576 password, password_len, cp->encr_hash))
577 goto fail;
578 }
579
580 /* Peer-Challenge */
581 if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
582 goto fail;
583
584 /* Reserved, must be zero */
585 os_memset(cp->reserved, 0, 8);
586
587 /* NT-Response */
588 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
589 data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
590 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
591 cp->peer_challenge, MSCHAPV2_CHAL_LEN);
592 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
593 username, username_len);
594 wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
595 new_password, new_password_len);
596 generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
597 username, username_len,
598 new_password, new_password_len,
599 cp->nt_response);
600 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
601 cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
602
603 /* Authenticator response is not really needed yet, but calculate it
604 * here so that challenges need not be saved. */
605 generate_authenticator_response(new_password, new_password_len,
606 cp->peer_challenge,
607 data->passwd_change_challenge,
608 username, username_len,
609 cp->nt_response, data->auth_response);
610 data->auth_response_valid = 1;
611
612 /* Likewise, generate master_key here since we have the needed data
613 * available. */
614 if (nt_password_hash(new_password, new_password_len, password_hash) ||
615 hash_nt_password_hash(password_hash, password_hash_hash) ||
616 get_master_key(password_hash_hash, cp->nt_response,
617 data->master_key)) {
618 data->auth_response_valid = 0;
619 goto fail;
620 }
621 data->master_key_valid = 1;
622
623 /* Flags */
624 os_memset(cp->flags, 0, 2);
625
626 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
627 "(change pw)", id, ms->mschapv2_id);
628
629 return resp;
630
631 fail:
632 wpabuf_free(resp);
633 return NULL;
634 #endif /* CONFIG_NO_RC4 */
635 }
636
637
638 /**
639 * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
640 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
641 * @data: Pointer to private EAP method data from eap_mschapv2_init()
642 * @ret: Return values from EAP request validation and processing
643 * @req: Pointer to EAP-MSCHAPv2 header from the request
644 * @req_len: Length of the EAP-MSCHAPv2 data
645 * @id: EAP identifier used in th erequest
646 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
647 * no reply available
648 */
eap_mschapv2_failure(struct eap_sm * sm,struct eap_mschapv2_data * data,struct eap_method_ret * ret,const struct eap_mschapv2_hdr * req,size_t req_len,u8 id)649 static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
650 struct eap_mschapv2_data *data,
651 struct eap_method_ret *ret,
652 const struct eap_mschapv2_hdr *req,
653 size_t req_len, u8 id)
654 {
655 struct wpabuf *resp;
656 const u8 *msdata = (const u8 *) (req + 1);
657 char *buf;
658 size_t len = req_len - sizeof(*req);
659 int retry = 0;
660
661 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
662 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
663 msdata, len);
664 /*
665 * eap_mschapv2_failure_txt() expects a nul terminated string, so we
666 * must allocate a large enough temporary buffer to create that since
667 * the received message does not include nul termination.
668 */
669 buf = dup_binstr(msdata, len);
670 if (buf) {
671 retry = eap_mschapv2_failure_txt(sm, data, buf);
672 os_free(buf);
673 }
674
675 ret->ignore = false;
676 ret->methodState = METHOD_DONE;
677 ret->decision = DECISION_FAIL;
678 ret->allowNotifications = false;
679
680 if (data->prev_error == ERROR_PASSWD_EXPIRED &&
681 data->passwd_change_version == 3) {
682 struct eap_peer_config *config = eap_get_config(sm);
683 if (config && config->new_password)
684 return eap_mschapv2_change_password(sm, data, ret, req,
685 id);
686 if (config && config->pending_req_new_password)
687 return NULL;
688 } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
689 /* TODO: could try to retry authentication, e.g, after having
690 * changed the username/password. In this case, EAP MS-CHAP-v2
691 * Failure Response would not be sent here. */
692 return NULL;
693 }
694
695 /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
696 * message. */
697 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
698 EAP_CODE_RESPONSE, id);
699 if (resp == NULL)
700 return NULL;
701
702 wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
703
704 return resp;
705 }
706
707
eap_mschapv2_check_config(struct eap_sm * sm)708 static int eap_mschapv2_check_config(struct eap_sm *sm)
709 {
710 size_t len;
711
712 if (eap_get_config_identity(sm, &len) == NULL) {
713 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
714 eap_sm_request_identity(sm);
715 return -1;
716 }
717
718 if (eap_get_config_password(sm, &len) == NULL) {
719 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
720 eap_sm_request_password(sm);
721 return -1;
722 }
723
724 return 0;
725 }
726
727
eap_mschapv2_check_mslen(struct eap_sm * sm,size_t len,const struct eap_mschapv2_hdr * ms)728 static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
729 const struct eap_mschapv2_hdr *ms)
730 {
731 size_t ms_len = WPA_GET_BE16(ms->ms_length);
732
733 if (ms_len == len)
734 return 0;
735
736 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
737 "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
738 if (sm->workaround) {
739 /* Some authentication servers use invalid ms_len,
740 * ignore it for interoperability. */
741 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
742 " invalid ms_len %lu (len %lu)",
743 (unsigned long) ms_len,
744 (unsigned long) len);
745 return 0;
746 }
747
748 return -1;
749 }
750
751
eap_mschapv2_copy_challenge(struct eap_mschapv2_data * data,const struct wpabuf * reqData)752 static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
753 const struct wpabuf *reqData)
754 {
755 /*
756 * Store a copy of the challenge message, so that it can be processed
757 * again in case retry is allowed after a possible failure.
758 */
759 wpabuf_free(data->prev_challenge);
760 data->prev_challenge = wpabuf_dup(reqData);
761 }
762
763
764 /**
765 * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
766 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
767 * @priv: Pointer to private EAP method data from eap_mschapv2_init()
768 * @ret: Return values from EAP request validation and processing
769 * @reqData: EAP request to be processed (eapReqData)
770 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
771 * no reply available
772 */
eap_mschapv2_process(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)773 static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
774 struct eap_method_ret *ret,
775 const struct wpabuf *reqData)
776 {
777 struct eap_mschapv2_data *data = priv;
778 struct eap_peer_config *config = eap_get_config(sm);
779 const struct eap_mschapv2_hdr *ms;
780 int using_prev_challenge = 0;
781 const u8 *pos;
782 size_t len;
783 u8 id;
784
785 if (eap_mschapv2_check_config(sm)) {
786 ret->ignore = true;
787 return NULL;
788 }
789
790 if (config->mschapv2_retry && data->prev_challenge &&
791 data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
792 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
793 "with the previous challenge");
794
795 reqData = data->prev_challenge;
796 using_prev_challenge = 1;
797 config->mschapv2_retry = 0;
798 }
799
800 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
801 &len);
802 if (pos == NULL || len < sizeof(*ms) + 1) {
803 ret->ignore = true;
804 return NULL;
805 }
806
807 ms = (const struct eap_mschapv2_hdr *) pos;
808 if (eap_mschapv2_check_mslen(sm, len, ms)) {
809 ret->ignore = true;
810 return NULL;
811 }
812
813 id = eap_get_id(reqData);
814 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
815 id, ms->mschapv2_id);
816
817 switch (ms->op_code) {
818 case MSCHAPV2_OP_CHALLENGE:
819 if (!using_prev_challenge)
820 eap_mschapv2_copy_challenge(data, reqData);
821 return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
822 case MSCHAPV2_OP_SUCCESS:
823 return eap_mschapv2_success(sm, data, ret, ms, len, id);
824 case MSCHAPV2_OP_FAILURE:
825 return eap_mschapv2_failure(sm, data, ret, ms, len, id);
826 default:
827 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
828 ms->op_code);
829 ret->ignore = true;
830 return NULL;
831 }
832 }
833
834
eap_mschapv2_isKeyAvailable(struct eap_sm * sm,void * priv)835 static bool eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
836 {
837 struct eap_mschapv2_data *data = priv;
838 return data->success && data->master_key_valid;
839 }
840
841
eap_mschapv2_getKey(struct eap_sm * sm,void * priv,size_t * len)842 static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
843 {
844 struct eap_mschapv2_data *data = priv;
845 u8 *key;
846 int key_len;
847
848 if (!data->master_key_valid || !data->success)
849 return NULL;
850
851 key_len = 2 * MSCHAPV2_KEY_LEN;
852
853 key = os_malloc(key_len);
854 if (key == NULL)
855 return NULL;
856
857 /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
858 * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
859 if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1,
860 0) < 0 ||
861 get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
862 MSCHAPV2_KEY_LEN, 0, 0) < 0) {
863 os_free(key);
864 return NULL;
865 }
866
867 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
868 key, key_len);
869
870 *len = key_len;
871 return key;
872 }
873
874
875 /**
876 * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
877 * Returns: 0 on success, -1 on failure
878 *
879 * This function is used to register EAP-MSCHAPv2 peer method into the EAP
880 * method list.
881 */
eap_peer_mschapv2_register(void)882 int eap_peer_mschapv2_register(void)
883 {
884 struct eap_method *eap;
885
886 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
887 EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
888 "MSCHAPV2");
889 if (eap == NULL)
890 return -1;
891
892 eap->init = eap_mschapv2_init;
893 eap->deinit = eap_mschapv2_deinit;
894 eap->process = eap_mschapv2_process;
895 eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
896 eap->getKey = eap_mschapv2_getKey;
897
898 return eap_peer_method_register(eap);
899 }
900