$search
00001 /* 00002 * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server 00003 * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License version 2 as 00007 * published by the Free Software Foundation. 00008 * 00009 * Alternatively, this software may be distributed under the terms of BSD 00010 * license. 00011 * 00012 * See README and COPYING for more details. 00013 */ 00014 00015 #include "includes.h" 00016 00017 #include "common.h" 00018 #include "crypto/ms_funcs.h" 00019 #include "eap_i.h" 00020 00021 00022 struct eap_mschapv2_hdr { 00023 u8 op_code; /* MSCHAPV2_OP_* */ 00024 u8 mschapv2_id; /* must be changed for challenges, but not for 00025 * success/failure */ 00026 u8 ms_length[2]; /* Note: misaligned; length - 5 */ 00027 /* followed by data */ 00028 } STRUCT_PACKED; 00029 00030 #define MSCHAPV2_OP_CHALLENGE 1 00031 #define MSCHAPV2_OP_RESPONSE 2 00032 #define MSCHAPV2_OP_SUCCESS 3 00033 #define MSCHAPV2_OP_FAILURE 4 00034 #define MSCHAPV2_OP_CHANGE_PASSWORD 7 00035 00036 #define MSCHAPV2_RESP_LEN 49 00037 00038 #define ERROR_RESTRICTED_LOGON_HOURS 646 00039 #define ERROR_ACCT_DISABLED 647 00040 #define ERROR_PASSWD_EXPIRED 648 00041 #define ERROR_NO_DIALIN_PERMISSION 649 00042 #define ERROR_AUTHENTICATION_FAILURE 691 00043 #define ERROR_CHANGING_PASSWORD 709 00044 00045 #define PASSWD_CHANGE_CHAL_LEN 16 00046 #define MSCHAPV2_KEY_LEN 16 00047 00048 00049 #define CHALLENGE_LEN 16 00050 00051 struct eap_mschapv2_data { 00052 u8 auth_challenge[CHALLENGE_LEN]; 00053 int auth_challenge_from_tls; 00054 u8 *peer_challenge; 00055 u8 auth_response[20]; 00056 enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; 00057 u8 resp_mschapv2_id; 00058 u8 master_key[16]; 00059 int master_key_valid; 00060 }; 00061 00062 00063 static void * eap_mschapv2_init(struct eap_sm *sm) 00064 { 00065 struct eap_mschapv2_data *data; 00066 00067 data = os_zalloc(sizeof(*data)); 00068 if (data == NULL) 00069 return NULL; 00070 data->state = CHALLENGE; 00071 00072 if (sm->auth_challenge) { 00073 os_memcpy(data->auth_challenge, sm->auth_challenge, 00074 CHALLENGE_LEN); 00075 data->auth_challenge_from_tls = 1; 00076 } 00077 00078 if (sm->peer_challenge) { 00079 data->peer_challenge = os_malloc(CHALLENGE_LEN); 00080 if (data->peer_challenge == NULL) { 00081 os_free(data); 00082 return NULL; 00083 } 00084 os_memcpy(data->peer_challenge, sm->peer_challenge, 00085 CHALLENGE_LEN); 00086 } 00087 00088 return data; 00089 } 00090 00091 00092 static void eap_mschapv2_reset(struct eap_sm *sm, void *priv) 00093 { 00094 struct eap_mschapv2_data *data = priv; 00095 if (data == NULL) 00096 return; 00097 00098 os_free(data->peer_challenge); 00099 os_free(data); 00100 } 00101 00102 00103 static struct wpabuf * eap_mschapv2_build_challenge( 00104 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 00105 { 00106 struct wpabuf *req; 00107 struct eap_mschapv2_hdr *ms; 00108 char *name = "hostapd"; /* TODO: make this configurable */ 00109 size_t ms_len; 00110 00111 if (!data->auth_challenge_from_tls && 00112 os_get_random(data->auth_challenge, CHALLENGE_LEN)) { 00113 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " 00114 "data"); 00115 data->state = FAILURE; 00116 return NULL; 00117 } 00118 00119 ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name); 00120 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 00121 EAP_CODE_REQUEST, id); 00122 if (req == NULL) { 00123 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 00124 " for request"); 00125 data->state = FAILURE; 00126 return NULL; 00127 } 00128 00129 ms = wpabuf_put(req, sizeof(*ms)); 00130 ms->op_code = MSCHAPV2_OP_CHALLENGE; 00131 ms->mschapv2_id = id; 00132 WPA_PUT_BE16(ms->ms_length, ms_len); 00133 00134 wpabuf_put_u8(req, CHALLENGE_LEN); 00135 if (!data->auth_challenge_from_tls) 00136 wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN); 00137 else 00138 wpabuf_put(req, CHALLENGE_LEN); 00139 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", 00140 data->auth_challenge, CHALLENGE_LEN); 00141 wpabuf_put_data(req, name, os_strlen(name)); 00142 00143 return req; 00144 } 00145 00146 00147 static struct wpabuf * eap_mschapv2_build_success_req( 00148 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 00149 { 00150 struct wpabuf *req; 00151 struct eap_mschapv2_hdr *ms; 00152 u8 *msg; 00153 char *message = "OK"; 00154 size_t ms_len; 00155 00156 ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 + 00157 os_strlen(message); 00158 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 00159 EAP_CODE_REQUEST, id); 00160 if (req == NULL) { 00161 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 00162 " for request"); 00163 data->state = FAILURE; 00164 return NULL; 00165 } 00166 00167 ms = wpabuf_put(req, sizeof(*ms)); 00168 ms->op_code = MSCHAPV2_OP_SUCCESS; 00169 ms->mschapv2_id = data->resp_mschapv2_id; 00170 WPA_PUT_BE16(ms->ms_length, ms_len); 00171 msg = (u8 *) (ms + 1); 00172 00173 wpabuf_put_u8(req, 'S'); 00174 wpabuf_put_u8(req, '='); 00175 wpa_snprintf_hex_uppercase( 00176 wpabuf_put(req, sizeof(data->auth_response) * 2), 00177 sizeof(data->auth_response) * 2 + 1, 00178 data->auth_response, sizeof(data->auth_response)); 00179 wpabuf_put_u8(req, ' '); 00180 wpabuf_put_u8(req, 'M'); 00181 wpabuf_put_u8(req, '='); 00182 wpabuf_put_data(req, message, os_strlen(message)); 00183 00184 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", 00185 msg, ms_len - sizeof(*ms)); 00186 00187 return req; 00188 } 00189 00190 00191 static struct wpabuf * eap_mschapv2_build_failure_req( 00192 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 00193 { 00194 struct wpabuf *req; 00195 struct eap_mschapv2_hdr *ms; 00196 char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " 00197 "M=FAILED"; 00198 size_t ms_len; 00199 00200 ms_len = sizeof(*ms) + os_strlen(message); 00201 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 00202 EAP_CODE_REQUEST, id); 00203 if (req == NULL) { 00204 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 00205 " for request"); 00206 data->state = FAILURE; 00207 return NULL; 00208 } 00209 00210 ms = wpabuf_put(req, sizeof(*ms)); 00211 ms->op_code = MSCHAPV2_OP_FAILURE; 00212 ms->mschapv2_id = data->resp_mschapv2_id; 00213 WPA_PUT_BE16(ms->ms_length, ms_len); 00214 00215 wpabuf_put_data(req, message, os_strlen(message)); 00216 00217 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", 00218 (u8 *) message, os_strlen(message)); 00219 00220 return req; 00221 } 00222 00223 00224 static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, 00225 u8 id) 00226 { 00227 struct eap_mschapv2_data *data = priv; 00228 00229 switch (data->state) { 00230 case CHALLENGE: 00231 return eap_mschapv2_build_challenge(sm, data, id); 00232 case SUCCESS_REQ: 00233 return eap_mschapv2_build_success_req(sm, data, id); 00234 case FAILURE_REQ: 00235 return eap_mschapv2_build_failure_req(sm, data, id); 00236 default: 00237 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 00238 "buildReq", data->state); 00239 break; 00240 } 00241 return NULL; 00242 } 00243 00244 00245 static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, 00246 struct wpabuf *respData) 00247 { 00248 struct eap_mschapv2_data *data = priv; 00249 struct eap_mschapv2_hdr *resp; 00250 const u8 *pos; 00251 size_t len; 00252 00253 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 00254 &len); 00255 if (pos == NULL || len < 1) { 00256 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); 00257 return TRUE; 00258 } 00259 00260 resp = (struct eap_mschapv2_hdr *) pos; 00261 if (data->state == CHALLENGE && 00262 resp->op_code != MSCHAPV2_OP_RESPONSE) { 00263 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " 00264 "ignore op %d", resp->op_code); 00265 return TRUE; 00266 } 00267 00268 if (data->state == SUCCESS_REQ && 00269 resp->op_code != MSCHAPV2_OP_SUCCESS && 00270 resp->op_code != MSCHAPV2_OP_FAILURE) { 00271 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or " 00272 "Failure - ignore op %d", resp->op_code); 00273 return TRUE; 00274 } 00275 00276 if (data->state == FAILURE_REQ && 00277 resp->op_code != MSCHAPV2_OP_FAILURE) { 00278 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure " 00279 "- ignore op %d", resp->op_code); 00280 return TRUE; 00281 } 00282 00283 return FALSE; 00284 } 00285 00286 00287 static void eap_mschapv2_process_response(struct eap_sm *sm, 00288 struct eap_mschapv2_data *data, 00289 struct wpabuf *respData) 00290 { 00291 struct eap_mschapv2_hdr *resp; 00292 const u8 *pos, *end, *peer_challenge, *nt_response, *name; 00293 u8 flags; 00294 size_t len, name_len, i; 00295 u8 expected[24]; 00296 const u8 *username, *user; 00297 size_t username_len, user_len; 00298 int res; 00299 00300 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 00301 &len); 00302 if (pos == NULL || len < 1) 00303 return; /* Should not happen - frame already validated */ 00304 00305 end = pos + len; 00306 resp = (struct eap_mschapv2_hdr *) pos; 00307 pos = (u8 *) (resp + 1); 00308 00309 if (len < sizeof(*resp) + 1 + 49 || 00310 resp->op_code != MSCHAPV2_OP_RESPONSE || 00311 pos[0] != 49) { 00312 wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", 00313 respData); 00314 data->state = FAILURE; 00315 return; 00316 } 00317 data->resp_mschapv2_id = resp->mschapv2_id; 00318 pos++; 00319 peer_challenge = pos; 00320 pos += 16 + 8; 00321 nt_response = pos; 00322 pos += 24; 00323 flags = *pos++; 00324 name = pos; 00325 name_len = end - name; 00326 00327 if (data->peer_challenge) { 00328 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " 00329 "Peer-Challenge"); 00330 peer_challenge = data->peer_challenge; 00331 } 00332 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", 00333 peer_challenge, 16); 00334 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); 00335 wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); 00336 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); 00337 00338 /* MSCHAPv2 does not include optional domain name in the 00339 * challenge-response calculation, so remove domain prefix 00340 * (if present). */ 00341 username = sm->identity; 00342 username_len = sm->identity_len; 00343 for (i = 0; i < username_len; i++) { 00344 if (username[i] == '\\') { 00345 username_len -= i + 1; 00346 username += i + 1; 00347 break; 00348 } 00349 } 00350 00351 user = name; 00352 user_len = name_len; 00353 for (i = 0; i < user_len; i++) { 00354 if (user[i] == '\\') { 00355 user_len -= i + 1; 00356 user += i + 1; 00357 break; 00358 } 00359 } 00360 00361 if (username_len != user_len || 00362 os_memcmp(username, user, username_len) != 0) { 00363 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); 00364 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " 00365 "name", username, username_len); 00366 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " 00367 "name", user, user_len); 00368 data->state = FAILURE; 00369 return; 00370 } 00371 00372 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", 00373 username, username_len); 00374 00375 if (sm->user->password_hash) { 00376 res = generate_nt_response_pwhash(data->auth_challenge, 00377 peer_challenge, 00378 username, username_len, 00379 sm->user->password, 00380 expected); 00381 } else { 00382 res = generate_nt_response(data->auth_challenge, 00383 peer_challenge, 00384 username, username_len, 00385 sm->user->password, 00386 sm->user->password_len, 00387 expected); 00388 } 00389 if (res) { 00390 data->state = FAILURE; 00391 return; 00392 } 00393 00394 if (os_memcmp(nt_response, expected, 24) == 0) { 00395 const u8 *pw_hash; 00396 u8 pw_hash_buf[16], pw_hash_hash[16]; 00397 00398 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); 00399 data->state = SUCCESS_REQ; 00400 00401 /* Authenticator response is not really needed yet, but 00402 * calculate it here so that peer_challenge and username need 00403 * not be saved. */ 00404 if (sm->user->password_hash) { 00405 pw_hash = sm->user->password; 00406 } else { 00407 nt_password_hash(sm->user->password, 00408 sm->user->password_len, 00409 pw_hash_buf); 00410 pw_hash = pw_hash_buf; 00411 } 00412 generate_authenticator_response_pwhash( 00413 pw_hash, peer_challenge, data->auth_challenge, 00414 username, username_len, nt_response, 00415 data->auth_response); 00416 00417 hash_nt_password_hash(pw_hash, pw_hash_hash); 00418 get_master_key(pw_hash_hash, nt_response, data->master_key); 00419 data->master_key_valid = 1; 00420 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", 00421 data->master_key, MSCHAPV2_KEY_LEN); 00422 } else { 00423 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", 00424 expected, 24); 00425 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); 00426 data->state = FAILURE_REQ; 00427 } 00428 } 00429 00430 00431 static void eap_mschapv2_process_success_resp(struct eap_sm *sm, 00432 struct eap_mschapv2_data *data, 00433 struct wpabuf *respData) 00434 { 00435 struct eap_mschapv2_hdr *resp; 00436 const u8 *pos; 00437 size_t len; 00438 00439 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 00440 &len); 00441 if (pos == NULL || len < 1) 00442 return; /* Should not happen - frame already validated */ 00443 00444 resp = (struct eap_mschapv2_hdr *) pos; 00445 00446 if (resp->op_code == MSCHAPV2_OP_SUCCESS) { 00447 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" 00448 " - authentication completed successfully"); 00449 data->state = SUCCESS; 00450 } else { 00451 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " 00452 "Response - peer rejected authentication"); 00453 data->state = FAILURE; 00454 } 00455 } 00456 00457 00458 static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, 00459 struct eap_mschapv2_data *data, 00460 struct wpabuf *respData) 00461 { 00462 struct eap_mschapv2_hdr *resp; 00463 const u8 *pos; 00464 size_t len; 00465 00466 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 00467 &len); 00468 if (pos == NULL || len < 1) 00469 return; /* Should not happen - frame already validated */ 00470 00471 resp = (struct eap_mschapv2_hdr *) pos; 00472 00473 if (resp->op_code == MSCHAPV2_OP_FAILURE) { 00474 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" 00475 " - authentication failed"); 00476 } else { 00477 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " 00478 "Response - authentication failed"); 00479 } 00480 00481 data->state = FAILURE; 00482 } 00483 00484 00485 static void eap_mschapv2_process(struct eap_sm *sm, void *priv, 00486 struct wpabuf *respData) 00487 { 00488 struct eap_mschapv2_data *data = priv; 00489 00490 if (sm->user == NULL || sm->user->password == NULL) { 00491 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 00492 data->state = FAILURE; 00493 return; 00494 } 00495 00496 switch (data->state) { 00497 case CHALLENGE: 00498 eap_mschapv2_process_response(sm, data, respData); 00499 break; 00500 case SUCCESS_REQ: 00501 eap_mschapv2_process_success_resp(sm, data, respData); 00502 break; 00503 case FAILURE_REQ: 00504 eap_mschapv2_process_failure_resp(sm, data, respData); 00505 break; 00506 default: 00507 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 00508 "process", data->state); 00509 break; 00510 } 00511 } 00512 00513 00514 static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) 00515 { 00516 struct eap_mschapv2_data *data = priv; 00517 return data->state == SUCCESS || data->state == FAILURE; 00518 } 00519 00520 00521 static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 00522 { 00523 struct eap_mschapv2_data *data = priv; 00524 u8 *key; 00525 00526 if (data->state != SUCCESS || !data->master_key_valid) 00527 return NULL; 00528 00529 *len = 2 * MSCHAPV2_KEY_LEN; 00530 key = os_malloc(*len); 00531 if (key == NULL) 00532 return NULL; 00533 /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ 00534 get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); 00535 get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 00536 MSCHAPV2_KEY_LEN, 1, 1); 00537 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); 00538 00539 return key; 00540 } 00541 00542 00543 static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) 00544 { 00545 struct eap_mschapv2_data *data = priv; 00546 return data->state == SUCCESS; 00547 } 00548 00549 00550 int eap_server_mschapv2_register(void) 00551 { 00552 struct eap_method *eap; 00553 int ret; 00554 00555 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 00556 EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 00557 "MSCHAPV2"); 00558 if (eap == NULL) 00559 return -1; 00560 00561 eap->init = eap_mschapv2_init; 00562 eap->reset = eap_mschapv2_reset; 00563 eap->buildReq = eap_mschapv2_buildReq; 00564 eap->check = eap_mschapv2_check; 00565 eap->process = eap_mschapv2_process; 00566 eap->isDone = eap_mschapv2_isDone; 00567 eap->getKey = eap_mschapv2_getKey; 00568 eap->isSuccess = eap_mschapv2_isSuccess; 00569 00570 ret = eap_server_method_register(eap); 00571 if (ret) 00572 eap_server_method_free(eap); 00573 return ret; 00574 }