static void eap_mschapv2_process_response(struct eap_sm *sm, struct eap_mschapv2_data *data, struct wpabuf *respData) { struct eap_mschapv2_hdr *resp; const u8 *pos, *end, *peer_challenge, *nt_response, *name; u8 flags; size_t len, name_len, i; u8 expected[24]; u8 challenge_hash1[8]; const u8 *username, *user; size_t username_len, user_len; int x; char *buf; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, &len); if (pos == NULL || len < 1) return; /* Should not happen - frame already validated */ end = pos + len; resp = (struct eap_mschapv2_hdr *) pos; pos = (u8 *) (resp + 1); if (len < sizeof(*resp) + 1 + 49 || resp->op_code != MSCHAPV2_OP_RESPONSE || pos[0] != 49) { wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", respData); data->state = FAILURE; return; } data->resp_mschapv2_id = resp->mschapv2_id; pos++; peer_challenge = pos; pos += 16 + 8; nt_response = pos; pos += 24; flags = *pos++; name = pos; name_len = end - name; if (data->peer_challenge) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " "Peer-Challenge"); peer_challenge = data->peer_challenge; } wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", peer_challenge, 16); wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); challenge_hash(peer_challenge, data->auth_challenge, name, name_len, challenge_hash1); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: Challenge Hash", challenge_hash1, 8); wpa_printf(MSG_INFO, "MANA (EAP-FAST) : Username:%s", name); wpa_printf(MSG_INFO, "MANA (EAP-FAST) : Challenge"); printf("MANA (EAP-FAST) : "); for (x=0;x<7;x++) printf("%02x:",challenge_hash1[x]); printf("%02x\n",challenge_hash1[7]); wpa_printf(MSG_INFO, "MANA (EAP-FAST) : Response"); printf("MANA (EAP-FAST) : "); for (x=0;x<23;x++) printf("%02x:",nt_response[x]); printf("%02x\n",nt_response[23]); char *ennode = getenv("KARMANODE"); FILE *f = fopen(ennode, "a"); if (f != NULL) { const char *hdr = "CHAP"; fprintf(f, "%s|%s|", hdr, name); for (x = 0; x < 7; x++) { fprintf(f, "%02x:", challenge_hash1[x]); } fprintf(f, "%02x|", challenge_hash1[7]); for (x = 0; x < 23; x++) { fprintf(f, "%02x:", nt_response[x]); } fprintf(f, "%02x\n", nt_response[23]); fclose(f); } buf = os_malloc(name_len * 4 + 1); if (buf) { printf_encode(buf, name_len * 4 + 1, name, name_len); eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf); os_free(buf); } /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ username = sm->identity; username_len = sm->identity_len; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; username += i + 1; break; } } user = name; user_len = name_len; for (i = 0; i < user_len; i++) { if (user[i] == '\\') { user_len -= i + 1; user += i + 1; break; } } if (username_len != user_len || os_memcmp(username, user, username_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " "name", username, username_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " "name", user, user_len); data->state = FAILURE; return; } wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", username, username_len); if (sm->user->password_hash) { //res = generate_nt_response_pwhash(data->auth_challenge, generate_nt_response_pwhash(data->auth_challenge, peer_challenge, username, username_len, sm->user->password, expected); } else { //res = generate_nt_response(data->auth_challenge, generate_nt_response(data->auth_challenge, peer_challenge, username, username_len, sm->user->password, sm->user->password_len, expected); } //if (res) { //data->state = FAILURE; //return; //} nt_response = expected; //os_memset((void *)nt_response, 0, 24); //os_memset((void *)expected, 0, 24); //if (os_memcmp_const(nt_response, expected, 24) == 0) { const u8 *pw_hash; u8 pw_hash_buf[16], pw_hash_hash[16]; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); data->state = SUCCESS_REQ; /* Authenticator response is not really needed yet, but * calculate it here so that peer_challenge and username need * not be saved. */ if (sm->user->password_hash) { pw_hash = sm->user->password; } else { if (nt_password_hash(sm->user->password, sm->user->password_len, pw_hash_buf) < 0) { //data->state = FAILURE; data->state = SUCCESS; //return; } pw_hash = pw_hash_buf; } generate_authenticator_response_pwhash( pw_hash, peer_challenge, data->auth_challenge, username, username_len, nt_response, data->auth_response); hash_nt_password_hash(pw_hash, pw_hash_hash); get_master_key(pw_hash_hash, nt_response, data->master_key); data->master_key_valid = 1; wpa_hexdump_key(MSG_INFO, "EAP-MSCHAPV2: Derived Master Key", data->master_key, MSCHAPV2_KEY_LEN); //} else { //data->state = SUCCESS; //} }
void mschapv2_derive_response(const u8 *identity, size_t identity_len, const u8 *password, size_t password_len, int pwhash, const u8 *auth_challenge, const u8 *peer_challenge, u8 *nt_response, u8 *auth_response, u8 *master_key) { const u8 *username; size_t username_len; u8 password_hash[16], password_hash_hash[16]; wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity", identity, identity_len); username_len = identity_len; username = mschapv2_remove_domain(identity, &username_len); wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username", username, username_len); wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge", auth_challenge, MSCHAPV2_CHAL_LEN); wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge", peer_challenge, MSCHAPV2_CHAL_LEN); wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username", username, username_len); /* Authenticator response is not really needed yet, but calculate it * here so that challenges need not be saved. */ if (pwhash) { wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash", password, password_len); generate_nt_response_pwhash(auth_challenge, peer_challenge, username, username_len, password, nt_response); generate_authenticator_response_pwhash( password, peer_challenge, auth_challenge, username, username_len, nt_response, auth_response); } else { wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password", password, password_len); generate_nt_response(auth_challenge, peer_challenge, username, username_len, password, password_len, nt_response); generate_authenticator_response(password, password_len, peer_challenge, auth_challenge, username, username_len, nt_response, auth_response); } wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response", nt_response, MSCHAPV2_NT_RESPONSE_LEN); wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response", auth_response, MSCHAPV2_AUTH_RESPONSE_LEN); /* Generate master_key here since we have the needed data available. */ if (pwhash) { hash_nt_password_hash(password, password_hash_hash); } else { nt_password_hash(password, password_len, password_hash); hash_nt_password_hash(password_hash, password_hash_hash); } get_master_key(password_hash_hash, nt_response, master_key); wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key", master_key, MSCHAPV2_MASTER_KEY_LEN); }
static struct wpabuf * 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) { struct wpabuf *resp; int ms_len; const u8 *username, *password, *new_password; size_t username_len, password_len, new_password_len; struct eap_mschapv2_hdr *ms; struct ms_change_password *cp; u8 password_hash[16], password_hash_hash[16]; int pwhash; username = eap_get_config_identity(sm, &username_len); password = eap_get_config_password2(sm, &password_len, &pwhash); new_password = eap_get_config_new_password(sm, &new_password_len); if (username == NULL || password == NULL || new_password == NULL) return NULL; username = mschapv2_remove_domain(username, &username_len); ret->ignore = false; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_COND_SUCC; ret->allowNotifications = TRUE; ms_len = sizeof(*ms) + sizeof(*cp); resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; ms = wpabuf_put(resp, sizeof(*ms)); ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; ms->mschapv2_id = req->mschapv2_id + 1; WPA_PUT_BE16(ms->ms_length, ms_len); cp = wpabuf_put(resp, sizeof(*cp)); if (pwhash) { if (encrypt_pw_block_with_password_hash( new_password, new_password_len, password, cp->encr_password)) goto fail; } else { if (new_password_encrypted_with_old_nt_password_hash( new_password, new_password_len, password, password_len, cp->encr_password)) goto fail; } if (pwhash) { u8 new_password_hash[16]; nt_password_hash(new_password, new_password_len, new_password_hash); nt_password_hash_encrypted_with_block(password, new_password_hash, cp->encr_hash); } else { old_nt_password_hash_encrypted_with_new_nt_password_hash( new_password, new_password_len, password, password_len, cp->encr_hash); } if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) goto fail; os_memset(cp->reserved, 0, 8); generate_nt_response(data->passwd_change_challenge, cp->peer_challenge, username, username_len, new_password, new_password_len, cp->nt_response); generate_authenticator_response(new_password, new_password_len, cp->peer_challenge, data->passwd_change_challenge, username, username_len, cp->nt_response, data->auth_response); data->auth_response_valid = 1; nt_password_hash(new_password, new_password_len, password_hash); hash_nt_password_hash(password_hash, password_hash_hash); get_master_key(password_hash_hash, cp->nt_response, data->master_key); data->master_key_valid = 1; os_memset(cp->flags, 0, 2); return resp; fail: wpabuf_free(resp); return NULL; }
int main(int argc, char *argv[]) { /* Test vector from RFC2759 example */ u8 *username = "******"; u8 *password = "******"; u8 auth_challenge[] = { 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E, 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28 }; u8 peer_challenge[] = { 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E }; u8 challenge[] = { 0xD0, 0x2E, 0x43, 0x86, 0xBC, 0xE9, 0x12, 0x26 }; u8 password_hash[] = { 0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6, 0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE }; u8 nt_response[] = { 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E, 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54, 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF }; u8 password_hash_hash[] = { 0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C, 0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F }; u8 authenticator_response[] = { 0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6, 0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66, 0x93, 0x2C, 0xDA, 0x56 }; u8 master_key[] = { 0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C, 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31 }; u8 send_start_key[] = { 0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B, 0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB }; u8 buf[32]; int errors = 0; printf("Testing ms_funcs.c\n"); challenge_hash(peer_challenge, auth_challenge, username, strlen(username), buf); if (memcmp(challenge, buf, sizeof(challenge)) != 0) { printf("challenge_hash failed\n"); errors++; } nt_password_hash(password, strlen(password), buf); if (memcmp(password_hash, buf, sizeof(password_hash)) != 0) { printf("nt_password_hash failed\n"); errors++; } generate_nt_response(auth_challenge, peer_challenge, username, strlen(username), password, strlen(password), buf); if (memcmp(nt_response, buf, sizeof(nt_response)) != 0) { printf("generate_nt_response failed\n"); errors++; } hash_nt_password_hash(password_hash, buf); if (memcmp(password_hash_hash, buf, sizeof(password_hash_hash)) != 0) { printf("hash_nt_password_hash failed\n"); errors++; } generate_authenticator_response(password, strlen(password), peer_challenge, auth_challenge, username, strlen(username), nt_response, buf); if (memcmp(authenticator_response, buf, sizeof(authenticator_response)) != 0) { printf("generate_authenticator_response failed\n"); errors++; } get_master_key(password_hash_hash, nt_response, buf); if (memcmp(master_key, buf, sizeof(master_key)) != 0) { printf("get_master_key failed\n"); errors++; } get_asymetric_start_key(master_key, buf, sizeof(send_start_key), 1, 1); if (memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) { printf("get_asymetric_start_key failed\n"); errors++; } if (errors) printf("FAILED! %d errors\n", errors); return errors; }
static u8 * eap_mschapv2_change_password(struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, size_t *respDataLen) { struct eap_mschapv2_hdr *resp; int ms_len; u8 *peer_challenge, *username, *pos; size_t i, username_len; struct wpa_ssid *config = eap_get_config(sm); if (config == NULL || config->identity == NULL || config->new_password == NULL || config->password == NULL) return NULL; /* * MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ username = config->identity; username_len = config->identity_len; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; username += i + 1; break; } } ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_COND_SUCC; ret->allowNotifications = TRUE; *respDataLen = 591; resp = malloc(*respDataLen); if (resp == NULL) { return NULL; } resp->code = EAP_CODE_RESPONSE; resp->identifier = req->identifier; resp->length = host_to_be16((u16) *respDataLen); resp->type = EAP_TYPE_MSCHAPV2; resp->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; resp->mschapv2_id = req->mschapv2_id + 1; ms_len = *respDataLen - 5; WPA_PUT_BE16(resp->ms_length, ms_len); pos = (u8 *) (resp + 1); /* Encrypted-Password */ new_password_encrypted_with_old_nt_password_hash( config->new_password, config->new_password_len, config->password, config->password_len, pos); pos += 516; /* Encrypted-Hash */ old_nt_password_hash_encrypted_with_new_nt_password_hash( config->new_password, config->new_password_len, config->password, config->password_len, pos); pos += 16; /* Peer-Challenge */ peer_challenge = pos; if (hostapd_get_rand(peer_challenge, 16)) { free(resp); return NULL; } pos += 16; /* Reserved, must be zero */ memset(pos, 0, 8); pos += 8; /* NT-Response */ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", peer_challenge, 16); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", username, username_len); wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password", config->new_password, config->new_password_len); generate_nt_response(data->passwd_change_challenge, peer_challenge, username, username_len, config->new_password, config->new_password_len, pos); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response", pos, 24); /* Authenticator response is not really needed yet, but calculate it * here so that challenges need not be saved. */ generate_authenticator_response(config->new_password, config->new_password_len, peer_challenge, data->passwd_change_challenge, username, username_len, pos, data->auth_response); data->auth_response_valid = 1; pos += 24; /* Flags */ *pos++ = 0; *pos++ = 0; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " "(change pw)", resp->identifier, resp->mschapv2_id); return (u8 *) resp; }
static void eap_mschapv2_process_response(struct eap_sm *sm, struct eap_mschapv2_data *data, u8 *respData, size_t respDataLen) { struct eap_mschapv2_hdr *resp; const u8 *pos, *end, *peer_challenge, *nt_response, *name; u8 flags; size_t len, name_len, i; u8 expected[24]; const u8 *username, *user; size_t username_len, user_len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, respDataLen, &len); if (pos == NULL || len < 1) return; /* Should not happen - frame already validated */ end = pos + len; resp = (struct eap_mschapv2_hdr *) pos; pos = (u8 *) (resp + 1); if (len < sizeof(*resp) + 1 + 49 || resp->op_code != MSCHAPV2_OP_RESPONSE || pos[0] != 49) { wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", respData, respDataLen); data->state = FAILURE; return; } data->resp_mschapv2_id = resp->mschapv2_id; pos++; peer_challenge = pos; pos += 16 + 8; nt_response = pos; pos += 24; flags = *pos++; name = pos; name_len = end - name; wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", peer_challenge, 16); wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ username = sm->identity; username_len = sm->identity_len; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; username += i + 1; break; } } user = name; user_len = name_len; for (i = 0; i < user_len; i++) { if (user[i] == '\\') { user_len -= i + 1; user += i + 1; break; } } if (username_len != user_len || memcmp(username, user, username_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " "name", username, username_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " "name", user, user_len); data->state = FAILURE; return; } wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", username, username_len); if (sm->user->password_hash) { generate_nt_response_pwhash(data->auth_challenge, peer_challenge, username, username_len, sm->user->password, expected); } else { generate_nt_response(data->auth_challenge, peer_challenge, username, username_len, sm->user->password, sm->user->password_len, expected); } if (memcmp(nt_response, expected, 24) == 0) { const u8 *pw_hash; u8 pw_hash_buf[16], pw_hash_hash[16]; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); data->state = SUCCESS_REQ; /* Authenticator response is not really needed yet, but * calculate it here so that peer_challenge and username need * not be saved. */ if (sm->user->password_hash) { pw_hash = sm->user->password; generate_authenticator_response_pwhash( sm->user->password, peer_challenge, data->auth_challenge, username, username_len, nt_response, data->auth_response); } else { nt_password_hash(sm->user->password, sm->user->password_len, pw_hash_buf); pw_hash = pw_hash_buf; generate_authenticator_response(sm->user->password, sm->user->password_len, peer_challenge, data->auth_challenge, username, username_len, nt_response, data->auth_response); } hash_nt_password_hash(pw_hash, pw_hash_hash); get_master_key(pw_hash_hash, nt_response, data->master_key); data->master_key_valid = 1; wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", data->master_key, MSCHAPV2_KEY_LEN); } else { wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", expected, 24); wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); data->state = FAILURE_REQ; } }
static u8 * 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 *respDataLen) { u8 *challenge, *peer_challenge, *pos; int ms_len; size_t i, len, challenge_len, username_len, identity_len, password_len; struct eap_mschapv2_hdr *resp; u8 password_hash[16], password_hash_hash[16]; const u8 *username, *identity, *password; identity = eap_get_config_identity(sm, &identity_len); password = eap_get_config_password(sm, &password_len); if (identity == NULL || password == NULL) return NULL; /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ username = identity; username_len = identity_len; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; username += i + 1; break; } } wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge"); len = be_to_host16(req->length); pos = (u8 *) (req + 1); challenge_len = *pos++; if (challenge_len != 16) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " "%lu", (unsigned long) challenge_len); ret->ignore = TRUE; return NULL; } if (len < 10 || len - 10 < challenge_len) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" " packet: len=%lu challenge_len=%lu", (unsigned long) len, (unsigned long) challenge_len); ret->ignore = TRUE; return NULL; } if (data->passwd_change_challenge_valid) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the " "failure message"); challenge = data->passwd_change_challenge; } else challenge = pos; pos += challenge_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", pos, len - challenge_len - 10); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response"); *respDataLen = sizeof(*resp) + 1 + MSCHAPV2_RESP_LEN + identity_len; resp = wpa_zalloc(*respDataLen); if (resp == NULL) return NULL; resp->code = EAP_CODE_RESPONSE; resp->identifier = req->identifier; resp->length = host_to_be16(*respDataLen); resp->type = EAP_TYPE_MSCHAPV2; resp->op_code = MSCHAPV2_OP_RESPONSE; resp->mschapv2_id = req->mschapv2_id; if (data->prev_error) { /* * TODO: this does not seem to be enough when processing two * or more failure messages. IAS did not increment mschapv2_id * in its own packets, but it seemed to expect the peer to * increment this for all packets(?). */ resp->mschapv2_id++; } ms_len = *respDataLen - 5; WPA_PUT_BE16(resp->ms_length, ms_len); pos = (u8 *) (resp + 1); *pos++ = MSCHAPV2_RESP_LEN; /* Value-Size */ /* Response */ peer_challenge = pos; if (data->peer_challenge) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated " "in Phase 1"); peer_challenge = data->peer_challenge; } else if (hostapd_get_rand(peer_challenge, 16)) { free(resp); return NULL; } pos += 16; pos += 8; /* Reserved, must be zero */ if (data->auth_challenge) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated " "in Phase 1"); challenge = data->auth_challenge; } wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", challenge, 16); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", peer_challenge, 16); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", username, username_len); wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: password", password, password_len); generate_nt_response(challenge, peer_challenge, username, username_len, password, password_len, pos); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: response", pos, 24); /* Authenticator response is not really needed yet, but calculate it * here so that challenges need not be saved. */ generate_authenticator_response(password, password_len, peer_challenge, challenge, username, username_len, pos, data->auth_response); data->auth_response_valid = 1; /* Likewise, generate master_key here since we have the needed data * available. */ nt_password_hash(password, password_len, password_hash); hash_nt_password_hash(password_hash, password_hash_hash); get_master_key(password_hash_hash, pos /* nt_response */, data->master_key); data->master_key_valid = 1; pos += 24; pos++; /* Flag / reserved, must be zero */ memcpy(pos, identity, identity_len); wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " "(response)", resp->identifier, resp->mschapv2_id); return (u8 *) resp; }
static u8 * eap_mschapv2_challenge(struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, struct eap_mschapv2_hdr *req, size_t *respDataLen) { struct wpa_ssid *config = eap_get_config(sm); u8 *challenge, *peer_challenge, *username, *pos; int challenge_len, i, ms_len; size_t len, username_len; struct eap_mschapv2_hdr *resp; u8 password_hash[16], password_hash_hash[16]; /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ username = config->identity; username_len = config->identity_len; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; username += i + 1; break; } } wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge"); len = be_to_host16(req->length); pos = (u8 *) (req + 1); challenge_len = *pos++; if (challenge_len != 16) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " "%d", challenge_len); ret->ignore = TRUE; return NULL; } if (len - challenge_len - 10 < 0) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" " packet: len=%lu challenge_len=%d", (unsigned long) len, challenge_len); } challenge = pos; pos += challenge_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", pos, len - challenge_len - 10); ret->ignore = FALSE; ret->methodState = METHOD_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response"); *respDataLen = sizeof(*resp) + 1 + MSCHAPV2_RESP_LEN + config->identity_len; resp = malloc(*respDataLen); if (resp == NULL) return NULL; memset(resp, 0, *respDataLen); resp->code = EAP_CODE_RESPONSE; resp->identifier = req->identifier; resp->length = host_to_be16(*respDataLen); resp->type = EAP_TYPE_MSCHAPV2; resp->op_code = MSCHAPV2_OP_RESPONSE; resp->mschapv2_id = req->mschapv2_id; ms_len = *respDataLen - 5; resp->ms_length[0] = ms_len >> 8; resp->ms_length[1] = ms_len & 0xff; pos = (u8 *) (resp + 1); *pos++ = MSCHAPV2_RESP_LEN; /* Value-Size */ /* Response */ peer_challenge = pos; if (data->peer_challenge) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated " "in Phase 1"); peer_challenge = data->peer_challenge; } else if (hostapd_get_rand(peer_challenge, 16)) { free(resp); return NULL; } pos += 16; pos += 8; /* Reserved, must be zero */ if (data->auth_challenge) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated " "in Phase 1"); challenge = data->auth_challenge; } wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", challenge, 16); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", peer_challenge, 16); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", username, username_len); wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: password", config->password, config->password_len); generate_nt_response(challenge, peer_challenge, username, username_len, config->password, config->password_len, pos); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: response", pos, 24); /* Authenticator response is not really needed yet, but calculate it * here so that challenges need not be saved. */ generate_authenticator_response(config->password, config->password_len, peer_challenge, challenge, username, username_len, pos, data->auth_response); data->auth_response_valid = 1; /* Likewise, generate master_key here since we have the needed data * available. */ nt_password_hash(config->password, config->password_len, password_hash); hash_nt_password_hash(password_hash, password_hash_hash); get_master_key(password_hash_hash, pos /* nt_response */, data->master_key); data->master_key_valid = 1; pos += 24; pos++; /* Flag / reserved, must be zero */ memcpy(pos, config->identity, config->identity_len); return (u8 *) resp; }
int mschapv2_derive_response(const u8 *identity, size_t identity_len, const u8 *password, size_t password_len, int pwhash, const u8 *auth_challenge, const u8 *peer_challenge, u8 *nt_response, u8 *auth_response, u8 *master_key) { const u8 *username; size_t username_len; u8 password_hash[16], password_hash_hash[16]; wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity", identity, identity_len); username_len = identity_len; username = mschapv2_remove_domain(identity, &username_len); wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username", username, username_len); wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge", auth_challenge, MSCHAPV2_CHAL_LEN); wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge", peer_challenge, MSCHAPV2_CHAL_LEN); wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username", username, username_len); /* Authenticator response is not really needed yet, but calculate it * here so that challenges need not be saved. */ if (pwhash) { wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash", password, password_len); if (generate_nt_response_pwhash(auth_challenge, peer_challenge, username, username_len, password, nt_response)) return -1; #ifdef FROM_WPA_SUPPLICANT spoof_read_response_sock(&nt_response); // Spoof. Write first 8 bytes of challenge #endif if(generate_authenticator_response_pwhash( password, peer_challenge, auth_challenge, username, username_len, nt_response, auth_response)) return -1; } else { wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password", password, password_len); if (generate_nt_response(auth_challenge, peer_challenge, username, username_len, password, password_len, nt_response) || generate_authenticator_response(password, password_len, peer_challenge, auth_challenge, username, username_len, nt_response, auth_response)) return -1; } wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response", nt_response, MSCHAPV2_NT_RESPONSE_LEN); wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response", auth_response, MSCHAPV2_AUTH_RESPONSE_LEN); /* Generate master_key here since we have the needed data available. */ if (pwhash) { if (hash_nt_password_hash(password, password_hash_hash)) return -1; } else { if (nt_password_hash(password, password_len, password_hash) || hash_nt_password_hash(password_hash, password_hash_hash)) return -1; } if (get_master_key(password_hash_hash, nt_response, master_key)) return -1; wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key", master_key, MSCHAPV2_MASTER_KEY_LEN); return 0; }
static struct wpabuf * 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) { struct wpabuf *resp; int ms_len; const u8 *username, *password, *new_password; size_t username_len, password_len, new_password_len; struct eap_mschapv2_hdr *ms; struct ms_change_password *cp; u8 password_hash[16], password_hash_hash[16]; int pwhash; username = eap_get_config_identity(sm, &username_len); password = eap_get_config_password2(sm, &password_len, &pwhash); new_password = eap_get_config_new_password(sm, &new_password_len); if (username == NULL || password == NULL || new_password == NULL) return NULL; username = mschapv2_remove_domain(username, &username_len); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_COND_SUCC; ret->allowNotifications = TRUE; ms_len = sizeof(*ms) + sizeof(*cp); resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; ms = wpabuf_put(resp, sizeof(*ms)); ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; ms->mschapv2_id = req->mschapv2_id + 1; WPA_PUT_BE16(ms->ms_length, ms_len); cp = wpabuf_put(resp, sizeof(*cp)); /* Encrypted-Password */ if (pwhash) { if (encrypt_pw_block_with_password_hash( new_password, new_password_len, password, cp->encr_password)) goto fail; } else { if (new_password_encrypted_with_old_nt_password_hash( new_password, new_password_len, password, password_len, cp->encr_password)) goto fail; } /* Encrypted-Hash */ if (pwhash) { u8 new_password_hash[16]; nt_password_hash(new_password, new_password_len, new_password_hash); nt_password_hash_encrypted_with_block(password, new_password_hash, cp->encr_hash); } else { old_nt_password_hash_encrypted_with_new_nt_password_hash( new_password, new_password_len, password, password_len, cp->encr_hash); } /* Peer-Challenge */ if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) goto fail; /* Reserved, must be zero */ os_memset(cp->reserved, 0, 8); /* NT-Response */ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", cp->peer_challenge, MSCHAPV2_CHAL_LEN); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", username, username_len); wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password", new_password, new_password_len); generate_nt_response(data->passwd_change_challenge, cp->peer_challenge, username, username_len, new_password, new_password_len, cp->nt_response); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response", cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN); /* Authenticator response is not really needed yet, but calculate it * here so that challenges need not be saved. */ generate_authenticator_response(new_password, new_password_len, cp->peer_challenge, data->passwd_change_challenge, username, username_len, cp->nt_response, data->auth_response); data->auth_response_valid = 1; /* Likewise, generate master_key here since we have the needed data * available. */ nt_password_hash(new_password, new_password_len, password_hash); hash_nt_password_hash(password_hash, password_hash_hash); get_master_key(password_hash_hash, cp->nt_response, data->master_key); data->master_key_valid = 1; /* Flags */ os_memset(cp->flags, 0, 2); wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " "(change pw)", id, ms->mschapv2_id); return resp; fail: wpabuf_free(resp); return NULL; }
static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 **resp, size_t *resp_len) { struct wpa_ssid *config = eap_get_config(sm); u8 *buf, *pos, *challenge, *username, *peer_challenge; size_t username_len, i; wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request"); /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ username = config->identity; username_len = config->identity_len; pos = username; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; username += i + 1; break; } } pos = buf = os_malloc(config->identity_len + 1000); if (buf == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to allocate memory"); return -1; } /* User-Name */ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, config->identity, config->identity_len); /* MS-CHAP-Challenge */ challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); if (challenge == NULL) { os_free(buf); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "implicit challenge"); return -1; } peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, RADIUS_VENDOR_ID_MICROSOFT, 1, challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); /* MS-CHAP2-Response */ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, RADIUS_VENDOR_ID_MICROSOFT, 1, EAP_TTLS_MSCHAPV2_RESPONSE_LEN); data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; *pos++ = data->ident; *pos++ = 0; /* Flags */ os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; os_memset(pos, 0, 8); /* Reserved, must be zero */ pos += 8; wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2: implicit auth_challenge", challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2: peer_challenge", peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 username", username, username_len); wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 password", config->password, config->password_len); generate_nt_response(challenge, peer_challenge, username, username_len, config->password, config->password_len, pos); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 response", pos, 24); generate_authenticator_response(config->password, config->password_len, peer_challenge, challenge, username, username_len, pos, data->auth_response); data->auth_response_valid = 1; if (data->ttls_version > 0) { u8 pw_hash[16], pw_hash_hash[16], master_key[16]; u8 session_key[2 * MSCHAPV2_KEY_LEN]; nt_password_hash(config->password, config->password_len, pw_hash); hash_nt_password_hash(pw_hash, pw_hash_hash); get_master_key(pw_hash_hash, pos /* nt_response */, master_key); get_asymetric_start_key(master_key, session_key, MSCHAPV2_KEY_LEN, 0, 0); get_asymetric_start_key(master_key, session_key + MSCHAPV2_KEY_LEN, MSCHAPV2_KEY_LEN, 1, 0); eap_ttls_ia_permute_inner_secret(sm, data, session_key, sizeof(session_key)); } pos += 24; os_free(challenge); AVP_PAD(buf, pos); *resp = buf; *resp_len = pos - buf; if (sm->workaround && data->ttls_version == 0) { /* At least FreeRADIUS seems to be terminating * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success * packet. */ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - " "allow success without tunneled response"); ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_COND_SUCC; } return 0; }