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