1e28a4053SRui Paulo /* 2e28a4053SRui Paulo * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server 3e28a4053SRui Paulo * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> 4e28a4053SRui Paulo * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 7e28a4053SRui Paulo */ 8e28a4053SRui Paulo 9e28a4053SRui Paulo #include "includes.h" 10e28a4053SRui Paulo 11e28a4053SRui Paulo #include "common.h" 12e28a4053SRui Paulo #include "crypto/ms_funcs.h" 13f05cddf9SRui Paulo #include "crypto/random.h" 14e28a4053SRui Paulo #include "eap_i.h" 15e28a4053SRui Paulo 16e28a4053SRui Paulo 17e28a4053SRui Paulo struct eap_mschapv2_hdr { 18e28a4053SRui Paulo u8 op_code; /* MSCHAPV2_OP_* */ 19e28a4053SRui Paulo u8 mschapv2_id; /* must be changed for challenges, but not for 20e28a4053SRui Paulo * success/failure */ 21e28a4053SRui Paulo u8 ms_length[2]; /* Note: misaligned; length - 5 */ 22e28a4053SRui Paulo /* followed by data */ 23e28a4053SRui Paulo } STRUCT_PACKED; 24e28a4053SRui Paulo 25e28a4053SRui Paulo #define MSCHAPV2_OP_CHALLENGE 1 26e28a4053SRui Paulo #define MSCHAPV2_OP_RESPONSE 2 27e28a4053SRui Paulo #define MSCHAPV2_OP_SUCCESS 3 28e28a4053SRui Paulo #define MSCHAPV2_OP_FAILURE 4 29e28a4053SRui Paulo #define MSCHAPV2_OP_CHANGE_PASSWORD 7 30e28a4053SRui Paulo 31e28a4053SRui Paulo #define MSCHAPV2_RESP_LEN 49 32e28a4053SRui Paulo 33e28a4053SRui Paulo #define ERROR_RESTRICTED_LOGON_HOURS 646 34e28a4053SRui Paulo #define ERROR_ACCT_DISABLED 647 35e28a4053SRui Paulo #define ERROR_PASSWD_EXPIRED 648 36e28a4053SRui Paulo #define ERROR_NO_DIALIN_PERMISSION 649 37e28a4053SRui Paulo #define ERROR_AUTHENTICATION_FAILURE 691 38e28a4053SRui Paulo #define ERROR_CHANGING_PASSWORD 709 39e28a4053SRui Paulo 40e28a4053SRui Paulo #define PASSWD_CHANGE_CHAL_LEN 16 41e28a4053SRui Paulo #define MSCHAPV2_KEY_LEN 16 42e28a4053SRui Paulo 43e28a4053SRui Paulo 44e28a4053SRui Paulo #define CHALLENGE_LEN 16 45e28a4053SRui Paulo 46e28a4053SRui Paulo struct eap_mschapv2_data { 47e28a4053SRui Paulo u8 auth_challenge[CHALLENGE_LEN]; 48e28a4053SRui Paulo int auth_challenge_from_tls; 49e28a4053SRui Paulo u8 *peer_challenge; 50e28a4053SRui Paulo u8 auth_response[20]; 51e28a4053SRui Paulo enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; 52e28a4053SRui Paulo u8 resp_mschapv2_id; 53e28a4053SRui Paulo u8 master_key[16]; 54e28a4053SRui Paulo int master_key_valid; 55e28a4053SRui Paulo }; 56e28a4053SRui Paulo 57e28a4053SRui Paulo 58e28a4053SRui Paulo static void * eap_mschapv2_init(struct eap_sm *sm) 59e28a4053SRui Paulo { 60e28a4053SRui Paulo struct eap_mschapv2_data *data; 61e28a4053SRui Paulo 62e28a4053SRui Paulo data = os_zalloc(sizeof(*data)); 63e28a4053SRui Paulo if (data == NULL) 64e28a4053SRui Paulo return NULL; 65e28a4053SRui Paulo data->state = CHALLENGE; 66e28a4053SRui Paulo 67*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "EAP-%sMSCHAPv2 init%s%s", 68*a90b9d01SCy Schubert sm->eap_fast_mschapv2 ? "FAST-" : "", 69*a90b9d01SCy Schubert sm->peer_challenge && sm->auth_challenge ? 70*a90b9d01SCy Schubert " with preset challenges" : "", 71*a90b9d01SCy Schubert sm->init_phase2 ? " for Phase 2" : ""); 72*a90b9d01SCy Schubert 73e28a4053SRui Paulo if (sm->auth_challenge) { 74e28a4053SRui Paulo os_memcpy(data->auth_challenge, sm->auth_challenge, 75e28a4053SRui Paulo CHALLENGE_LEN); 76e28a4053SRui Paulo data->auth_challenge_from_tls = 1; 77e28a4053SRui Paulo } 78e28a4053SRui Paulo 79e28a4053SRui Paulo if (sm->peer_challenge) { 8085732ac8SCy Schubert data->peer_challenge = os_memdup(sm->peer_challenge, 8185732ac8SCy Schubert CHALLENGE_LEN); 82e28a4053SRui Paulo if (data->peer_challenge == NULL) { 83e28a4053SRui Paulo os_free(data); 84e28a4053SRui Paulo return NULL; 85e28a4053SRui Paulo } 86e28a4053SRui Paulo } 87e28a4053SRui Paulo 88e28a4053SRui Paulo return data; 89e28a4053SRui Paulo } 90e28a4053SRui Paulo 91e28a4053SRui Paulo 92e28a4053SRui Paulo static void eap_mschapv2_reset(struct eap_sm *sm, void *priv) 93e28a4053SRui Paulo { 94e28a4053SRui Paulo struct eap_mschapv2_data *data = priv; 95e28a4053SRui Paulo if (data == NULL) 96e28a4053SRui Paulo return; 97e28a4053SRui Paulo 98e28a4053SRui Paulo os_free(data->peer_challenge); 995b9c547cSRui Paulo bin_clear_free(data, sizeof(*data)); 100e28a4053SRui Paulo } 101e28a4053SRui Paulo 102e28a4053SRui Paulo 103e28a4053SRui Paulo static struct wpabuf * eap_mschapv2_build_challenge( 104e28a4053SRui Paulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 105e28a4053SRui Paulo { 106e28a4053SRui Paulo struct wpabuf *req; 107e28a4053SRui Paulo struct eap_mschapv2_hdr *ms; 108e28a4053SRui Paulo size_t ms_len; 109e28a4053SRui Paulo 110e28a4053SRui Paulo if (!data->auth_challenge_from_tls && 111f05cddf9SRui Paulo random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) { 112e28a4053SRui Paulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " 113e28a4053SRui Paulo "data"); 114e28a4053SRui Paulo data->state = FAILURE; 115e28a4053SRui Paulo return NULL; 116e28a4053SRui Paulo } 117e28a4053SRui Paulo 118c1d255d3SCy Schubert ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->cfg->server_id_len; 119e28a4053SRui Paulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 120e28a4053SRui Paulo EAP_CODE_REQUEST, id); 121e28a4053SRui Paulo if (req == NULL) { 122e28a4053SRui Paulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 123e28a4053SRui Paulo " for request"); 124e28a4053SRui Paulo data->state = FAILURE; 125e28a4053SRui Paulo return NULL; 126e28a4053SRui Paulo } 127e28a4053SRui Paulo 128e28a4053SRui Paulo ms = wpabuf_put(req, sizeof(*ms)); 129e28a4053SRui Paulo ms->op_code = MSCHAPV2_OP_CHALLENGE; 130e28a4053SRui Paulo ms->mschapv2_id = id; 131e28a4053SRui Paulo WPA_PUT_BE16(ms->ms_length, ms_len); 132e28a4053SRui Paulo 133e28a4053SRui Paulo wpabuf_put_u8(req, CHALLENGE_LEN); 134e28a4053SRui Paulo if (!data->auth_challenge_from_tls) 135e28a4053SRui Paulo wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN); 136e28a4053SRui Paulo else 137e28a4053SRui Paulo wpabuf_put(req, CHALLENGE_LEN); 138e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", 139e28a4053SRui Paulo data->auth_challenge, CHALLENGE_LEN); 140c1d255d3SCy Schubert wpabuf_put_data(req, sm->cfg->server_id, sm->cfg->server_id_len); 141e28a4053SRui Paulo 142e28a4053SRui Paulo return req; 143e28a4053SRui Paulo } 144e28a4053SRui Paulo 145e28a4053SRui Paulo 146e28a4053SRui Paulo static struct wpabuf * eap_mschapv2_build_success_req( 147e28a4053SRui Paulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 148e28a4053SRui Paulo { 149e28a4053SRui Paulo struct wpabuf *req; 150e28a4053SRui Paulo struct eap_mschapv2_hdr *ms; 151e28a4053SRui Paulo u8 *msg; 152e28a4053SRui Paulo char *message = "OK"; 153e28a4053SRui Paulo size_t ms_len; 154e28a4053SRui Paulo 155e28a4053SRui Paulo ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 + 156e28a4053SRui Paulo os_strlen(message); 157e28a4053SRui Paulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 158e28a4053SRui Paulo EAP_CODE_REQUEST, id); 159e28a4053SRui Paulo if (req == NULL) { 160e28a4053SRui Paulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 161e28a4053SRui Paulo " for request"); 162e28a4053SRui Paulo data->state = FAILURE; 163e28a4053SRui Paulo return NULL; 164e28a4053SRui Paulo } 165e28a4053SRui Paulo 166e28a4053SRui Paulo ms = wpabuf_put(req, sizeof(*ms)); 167e28a4053SRui Paulo ms->op_code = MSCHAPV2_OP_SUCCESS; 168e28a4053SRui Paulo ms->mschapv2_id = data->resp_mschapv2_id; 169e28a4053SRui Paulo WPA_PUT_BE16(ms->ms_length, ms_len); 170e28a4053SRui Paulo msg = (u8 *) (ms + 1); 171e28a4053SRui Paulo 172e28a4053SRui Paulo wpabuf_put_u8(req, 'S'); 173e28a4053SRui Paulo wpabuf_put_u8(req, '='); 174e28a4053SRui Paulo wpa_snprintf_hex_uppercase( 175e28a4053SRui Paulo wpabuf_put(req, sizeof(data->auth_response) * 2), 176e28a4053SRui Paulo sizeof(data->auth_response) * 2 + 1, 177e28a4053SRui Paulo data->auth_response, sizeof(data->auth_response)); 178e28a4053SRui Paulo wpabuf_put_u8(req, ' '); 179e28a4053SRui Paulo wpabuf_put_u8(req, 'M'); 180e28a4053SRui Paulo wpabuf_put_u8(req, '='); 181e28a4053SRui Paulo wpabuf_put_data(req, message, os_strlen(message)); 182e28a4053SRui Paulo 183e28a4053SRui Paulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", 184e28a4053SRui Paulo msg, ms_len - sizeof(*ms)); 185e28a4053SRui Paulo 186e28a4053SRui Paulo return req; 187e28a4053SRui Paulo } 188e28a4053SRui Paulo 189e28a4053SRui Paulo 190e28a4053SRui Paulo static struct wpabuf * eap_mschapv2_build_failure_req( 191e28a4053SRui Paulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 192e28a4053SRui Paulo { 193e28a4053SRui Paulo struct wpabuf *req; 194e28a4053SRui Paulo struct eap_mschapv2_hdr *ms; 195e28a4053SRui Paulo char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " 196e28a4053SRui Paulo "M=FAILED"; 197e28a4053SRui Paulo size_t ms_len; 198e28a4053SRui Paulo 199e28a4053SRui Paulo ms_len = sizeof(*ms) + os_strlen(message); 200e28a4053SRui Paulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 201e28a4053SRui Paulo EAP_CODE_REQUEST, id); 202e28a4053SRui Paulo if (req == NULL) { 203e28a4053SRui Paulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 204e28a4053SRui Paulo " for request"); 205e28a4053SRui Paulo data->state = FAILURE; 206e28a4053SRui Paulo return NULL; 207e28a4053SRui Paulo } 208e28a4053SRui Paulo 209e28a4053SRui Paulo ms = wpabuf_put(req, sizeof(*ms)); 210e28a4053SRui Paulo ms->op_code = MSCHAPV2_OP_FAILURE; 211e28a4053SRui Paulo ms->mschapv2_id = data->resp_mschapv2_id; 212e28a4053SRui Paulo WPA_PUT_BE16(ms->ms_length, ms_len); 213e28a4053SRui Paulo 214e28a4053SRui Paulo wpabuf_put_data(req, message, os_strlen(message)); 215e28a4053SRui Paulo 216e28a4053SRui Paulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", 217e28a4053SRui Paulo (u8 *) message, os_strlen(message)); 218e28a4053SRui Paulo 219e28a4053SRui Paulo return req; 220e28a4053SRui Paulo } 221e28a4053SRui Paulo 222e28a4053SRui Paulo 223e28a4053SRui Paulo static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, 224e28a4053SRui Paulo u8 id) 225e28a4053SRui Paulo { 226e28a4053SRui Paulo struct eap_mschapv2_data *data = priv; 227e28a4053SRui Paulo 228e28a4053SRui Paulo switch (data->state) { 229e28a4053SRui Paulo case CHALLENGE: 230e28a4053SRui Paulo return eap_mschapv2_build_challenge(sm, data, id); 231e28a4053SRui Paulo case SUCCESS_REQ: 232e28a4053SRui Paulo return eap_mschapv2_build_success_req(sm, data, id); 233e28a4053SRui Paulo case FAILURE_REQ: 234e28a4053SRui Paulo return eap_mschapv2_build_failure_req(sm, data, id); 235e28a4053SRui Paulo default: 236e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 237e28a4053SRui Paulo "buildReq", data->state); 238e28a4053SRui Paulo break; 239e28a4053SRui Paulo } 240e28a4053SRui Paulo return NULL; 241e28a4053SRui Paulo } 242e28a4053SRui Paulo 243e28a4053SRui Paulo 244c1d255d3SCy Schubert static bool eap_mschapv2_check(struct eap_sm *sm, void *priv, 245e28a4053SRui Paulo struct wpabuf *respData) 246e28a4053SRui Paulo { 247e28a4053SRui Paulo struct eap_mschapv2_data *data = priv; 248e28a4053SRui Paulo struct eap_mschapv2_hdr *resp; 249e28a4053SRui Paulo const u8 *pos; 250e28a4053SRui Paulo size_t len; 251e28a4053SRui Paulo 252e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 253e28a4053SRui Paulo &len); 254e28a4053SRui Paulo if (pos == NULL || len < 1) { 255e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); 256c1d255d3SCy Schubert return true; 257e28a4053SRui Paulo } 258e28a4053SRui Paulo 259e28a4053SRui Paulo resp = (struct eap_mschapv2_hdr *) pos; 260e28a4053SRui Paulo if (data->state == CHALLENGE && 261e28a4053SRui Paulo resp->op_code != MSCHAPV2_OP_RESPONSE) { 262e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " 263e28a4053SRui Paulo "ignore op %d", resp->op_code); 264c1d255d3SCy Schubert return true; 265e28a4053SRui Paulo } 266e28a4053SRui Paulo 267e28a4053SRui Paulo if (data->state == SUCCESS_REQ && 268e28a4053SRui Paulo resp->op_code != MSCHAPV2_OP_SUCCESS && 269e28a4053SRui Paulo resp->op_code != MSCHAPV2_OP_FAILURE) { 270e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or " 271e28a4053SRui Paulo "Failure - ignore op %d", resp->op_code); 272c1d255d3SCy Schubert return true; 273e28a4053SRui Paulo } 274e28a4053SRui Paulo 275e28a4053SRui Paulo if (data->state == FAILURE_REQ && 276e28a4053SRui Paulo resp->op_code != MSCHAPV2_OP_FAILURE) { 277e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure " 278e28a4053SRui Paulo "- ignore op %d", resp->op_code); 279c1d255d3SCy Schubert return true; 280e28a4053SRui Paulo } 281e28a4053SRui Paulo 282c1d255d3SCy Schubert return false; 283e28a4053SRui Paulo } 284e28a4053SRui Paulo 285e28a4053SRui Paulo 286e28a4053SRui Paulo static void eap_mschapv2_process_response(struct eap_sm *sm, 287e28a4053SRui Paulo struct eap_mschapv2_data *data, 288e28a4053SRui Paulo struct wpabuf *respData) 289e28a4053SRui Paulo { 290e28a4053SRui Paulo struct eap_mschapv2_hdr *resp; 291e28a4053SRui Paulo const u8 *pos, *end, *peer_challenge, *nt_response, *name; 292e28a4053SRui Paulo u8 flags; 293e28a4053SRui Paulo size_t len, name_len, i; 294e28a4053SRui Paulo u8 expected[24]; 295e28a4053SRui Paulo const u8 *username, *user; 296e28a4053SRui Paulo size_t username_len, user_len; 297e28a4053SRui Paulo int res; 2985b9c547cSRui Paulo char *buf; 299e28a4053SRui Paulo 300e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 301e28a4053SRui Paulo &len); 302e28a4053SRui Paulo if (pos == NULL || len < 1) 303e28a4053SRui Paulo return; /* Should not happen - frame already validated */ 304e28a4053SRui Paulo 305e28a4053SRui Paulo end = pos + len; 306e28a4053SRui Paulo resp = (struct eap_mschapv2_hdr *) pos; 307e28a4053SRui Paulo pos = (u8 *) (resp + 1); 308e28a4053SRui Paulo 309e28a4053SRui Paulo if (len < sizeof(*resp) + 1 + 49 || 310e28a4053SRui Paulo resp->op_code != MSCHAPV2_OP_RESPONSE || 311e28a4053SRui Paulo pos[0] != 49) { 312e28a4053SRui Paulo wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", 313e28a4053SRui Paulo respData); 314e28a4053SRui Paulo data->state = FAILURE; 315e28a4053SRui Paulo return; 316e28a4053SRui Paulo } 317e28a4053SRui Paulo data->resp_mschapv2_id = resp->mschapv2_id; 318e28a4053SRui Paulo pos++; 319e28a4053SRui Paulo peer_challenge = pos; 320e28a4053SRui Paulo pos += 16 + 8; 321e28a4053SRui Paulo nt_response = pos; 322e28a4053SRui Paulo pos += 24; 323e28a4053SRui Paulo flags = *pos++; 324e28a4053SRui Paulo name = pos; 325e28a4053SRui Paulo name_len = end - name; 326e28a4053SRui Paulo 327e28a4053SRui Paulo if (data->peer_challenge) { 328e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " 329e28a4053SRui Paulo "Peer-Challenge"); 330e28a4053SRui Paulo peer_challenge = data->peer_challenge; 331e28a4053SRui Paulo } 332e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", 333e28a4053SRui Paulo peer_challenge, 16); 334e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); 335e28a4053SRui Paulo wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); 336e28a4053SRui Paulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); 337e28a4053SRui Paulo 3385b9c547cSRui Paulo buf = os_malloc(name_len * 4 + 1); 3395b9c547cSRui Paulo if (buf) { 3405b9c547cSRui Paulo printf_encode(buf, name_len * 4 + 1, name, name_len); 3415b9c547cSRui Paulo eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf); 3425b9c547cSRui Paulo os_free(buf); 3435b9c547cSRui Paulo } 3445b9c547cSRui Paulo 345e28a4053SRui Paulo /* MSCHAPv2 does not include optional domain name in the 346e28a4053SRui Paulo * challenge-response calculation, so remove domain prefix 347e28a4053SRui Paulo * (if present). */ 348e28a4053SRui Paulo username = sm->identity; 349e28a4053SRui Paulo username_len = sm->identity_len; 350e28a4053SRui Paulo for (i = 0; i < username_len; i++) { 351e28a4053SRui Paulo if (username[i] == '\\') { 352e28a4053SRui Paulo username_len -= i + 1; 353e28a4053SRui Paulo username += i + 1; 354e28a4053SRui Paulo break; 355e28a4053SRui Paulo } 356e28a4053SRui Paulo } 357e28a4053SRui Paulo 358e28a4053SRui Paulo user = name; 359e28a4053SRui Paulo user_len = name_len; 360e28a4053SRui Paulo for (i = 0; i < user_len; i++) { 361e28a4053SRui Paulo if (user[i] == '\\') { 362e28a4053SRui Paulo user_len -= i + 1; 363e28a4053SRui Paulo user += i + 1; 364e28a4053SRui Paulo break; 365e28a4053SRui Paulo } 366e28a4053SRui Paulo } 367e28a4053SRui Paulo 368325151a3SRui Paulo #ifdef CONFIG_TESTING_OPTIONS 369325151a3SRui Paulo { 370325151a3SRui Paulo u8 challenge[8]; 371325151a3SRui Paulo 372325151a3SRui Paulo if (challenge_hash(peer_challenge, data->auth_challenge, 373325151a3SRui Paulo username, username_len, challenge) == 0) { 374325151a3SRui Paulo eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2", 375325151a3SRui Paulo username, username_len, 376325151a3SRui Paulo challenge, nt_response); 377325151a3SRui Paulo } 378325151a3SRui Paulo } 379325151a3SRui Paulo #endif /* CONFIG_TESTING_OPTIONS */ 380325151a3SRui Paulo 381e28a4053SRui Paulo if (username_len != user_len || 382e28a4053SRui Paulo os_memcmp(username, user, username_len) != 0) { 383e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); 384e28a4053SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " 385e28a4053SRui Paulo "name", username, username_len); 386e28a4053SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " 387e28a4053SRui Paulo "name", user, user_len); 388e28a4053SRui Paulo data->state = FAILURE; 389e28a4053SRui Paulo return; 390e28a4053SRui Paulo } 391e28a4053SRui Paulo 392e28a4053SRui Paulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", 393e28a4053SRui Paulo username, username_len); 394e28a4053SRui Paulo 395e28a4053SRui Paulo if (sm->user->password_hash) { 396e28a4053SRui Paulo res = generate_nt_response_pwhash(data->auth_challenge, 397e28a4053SRui Paulo peer_challenge, 398e28a4053SRui Paulo username, username_len, 399e28a4053SRui Paulo sm->user->password, 400e28a4053SRui Paulo expected); 401e28a4053SRui Paulo } else { 402e28a4053SRui Paulo res = generate_nt_response(data->auth_challenge, 403e28a4053SRui Paulo peer_challenge, 404e28a4053SRui Paulo username, username_len, 405e28a4053SRui Paulo sm->user->password, 406e28a4053SRui Paulo sm->user->password_len, 407e28a4053SRui Paulo expected); 408e28a4053SRui Paulo } 409e28a4053SRui Paulo if (res) { 410e28a4053SRui Paulo data->state = FAILURE; 411e28a4053SRui Paulo return; 412e28a4053SRui Paulo } 413e28a4053SRui Paulo 4145b9c547cSRui Paulo if (os_memcmp_const(nt_response, expected, 24) == 0) { 415e28a4053SRui Paulo const u8 *pw_hash; 416e28a4053SRui Paulo u8 pw_hash_buf[16], pw_hash_hash[16]; 417e28a4053SRui Paulo 418e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); 419e28a4053SRui Paulo data->state = SUCCESS_REQ; 420e28a4053SRui Paulo 421e28a4053SRui Paulo /* Authenticator response is not really needed yet, but 422e28a4053SRui Paulo * calculate it here so that peer_challenge and username need 423e28a4053SRui Paulo * not be saved. */ 424e28a4053SRui Paulo if (sm->user->password_hash) { 425e28a4053SRui Paulo pw_hash = sm->user->password; 426e28a4053SRui Paulo } else { 427f05cddf9SRui Paulo if (nt_password_hash(sm->user->password, 428e28a4053SRui Paulo sm->user->password_len, 429f05cddf9SRui Paulo pw_hash_buf) < 0) { 430f05cddf9SRui Paulo data->state = FAILURE; 431f05cddf9SRui Paulo return; 432f05cddf9SRui Paulo } 433e28a4053SRui Paulo pw_hash = pw_hash_buf; 434e28a4053SRui Paulo } 4355b9c547cSRui Paulo if (generate_authenticator_response_pwhash( 436e28a4053SRui Paulo pw_hash, peer_challenge, data->auth_challenge, 437e28a4053SRui Paulo username, username_len, nt_response, 4385b9c547cSRui Paulo data->auth_response) < 0 || 4395b9c547cSRui Paulo hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 || 4405b9c547cSRui Paulo get_master_key(pw_hash_hash, nt_response, 4415b9c547cSRui Paulo data->master_key)) { 4425b9c547cSRui Paulo data->state = FAILURE; 4435b9c547cSRui Paulo return; 4445b9c547cSRui Paulo } 445e28a4053SRui Paulo data->master_key_valid = 1; 446e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", 447e28a4053SRui Paulo data->master_key, MSCHAPV2_KEY_LEN); 448e28a4053SRui Paulo } else { 449e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", 450e28a4053SRui Paulo expected, 24); 451e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); 452e28a4053SRui Paulo data->state = FAILURE_REQ; 453e28a4053SRui Paulo } 454e28a4053SRui Paulo } 455e28a4053SRui Paulo 456e28a4053SRui Paulo 457e28a4053SRui Paulo static void eap_mschapv2_process_success_resp(struct eap_sm *sm, 458e28a4053SRui Paulo struct eap_mschapv2_data *data, 459e28a4053SRui Paulo struct wpabuf *respData) 460e28a4053SRui Paulo { 461e28a4053SRui Paulo struct eap_mschapv2_hdr *resp; 462e28a4053SRui Paulo const u8 *pos; 463e28a4053SRui Paulo size_t len; 464e28a4053SRui Paulo 465e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 466e28a4053SRui Paulo &len); 467e28a4053SRui Paulo if (pos == NULL || len < 1) 468e28a4053SRui Paulo return; /* Should not happen - frame already validated */ 469e28a4053SRui Paulo 470e28a4053SRui Paulo resp = (struct eap_mschapv2_hdr *) pos; 471e28a4053SRui Paulo 472e28a4053SRui Paulo if (resp->op_code == MSCHAPV2_OP_SUCCESS) { 473e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" 474e28a4053SRui Paulo " - authentication completed successfully"); 475e28a4053SRui Paulo data->state = SUCCESS; 476e28a4053SRui Paulo } else { 477e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " 478e28a4053SRui Paulo "Response - peer rejected authentication"); 479e28a4053SRui Paulo data->state = FAILURE; 480e28a4053SRui Paulo } 481e28a4053SRui Paulo } 482e28a4053SRui Paulo 483e28a4053SRui Paulo 484e28a4053SRui Paulo static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, 485e28a4053SRui Paulo struct eap_mschapv2_data *data, 486e28a4053SRui Paulo struct wpabuf *respData) 487e28a4053SRui Paulo { 488e28a4053SRui Paulo struct eap_mschapv2_hdr *resp; 489e28a4053SRui Paulo const u8 *pos; 490e28a4053SRui Paulo size_t len; 491e28a4053SRui Paulo 492e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 493e28a4053SRui Paulo &len); 494e28a4053SRui Paulo if (pos == NULL || len < 1) 495e28a4053SRui Paulo return; /* Should not happen - frame already validated */ 496e28a4053SRui Paulo 497e28a4053SRui Paulo resp = (struct eap_mschapv2_hdr *) pos; 498e28a4053SRui Paulo 499e28a4053SRui Paulo if (resp->op_code == MSCHAPV2_OP_FAILURE) { 500e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" 501e28a4053SRui Paulo " - authentication failed"); 502e28a4053SRui Paulo } else { 503e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " 504e28a4053SRui Paulo "Response - authentication failed"); 505e28a4053SRui Paulo } 506e28a4053SRui Paulo 507e28a4053SRui Paulo data->state = FAILURE; 508e28a4053SRui Paulo } 509e28a4053SRui Paulo 510e28a4053SRui Paulo 511e28a4053SRui Paulo static void eap_mschapv2_process(struct eap_sm *sm, void *priv, 512e28a4053SRui Paulo struct wpabuf *respData) 513e28a4053SRui Paulo { 514e28a4053SRui Paulo struct eap_mschapv2_data *data = priv; 515e28a4053SRui Paulo 516e28a4053SRui Paulo if (sm->user == NULL || sm->user->password == NULL) { 517e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 518e28a4053SRui Paulo data->state = FAILURE; 519e28a4053SRui Paulo return; 520e28a4053SRui Paulo } 521e28a4053SRui Paulo 522e28a4053SRui Paulo switch (data->state) { 523e28a4053SRui Paulo case CHALLENGE: 524e28a4053SRui Paulo eap_mschapv2_process_response(sm, data, respData); 525e28a4053SRui Paulo break; 526e28a4053SRui Paulo case SUCCESS_REQ: 527e28a4053SRui Paulo eap_mschapv2_process_success_resp(sm, data, respData); 528e28a4053SRui Paulo break; 529e28a4053SRui Paulo case FAILURE_REQ: 530e28a4053SRui Paulo eap_mschapv2_process_failure_resp(sm, data, respData); 531e28a4053SRui Paulo break; 532e28a4053SRui Paulo default: 533e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 534e28a4053SRui Paulo "process", data->state); 535e28a4053SRui Paulo break; 536e28a4053SRui Paulo } 537e28a4053SRui Paulo } 538e28a4053SRui Paulo 539e28a4053SRui Paulo 540c1d255d3SCy Schubert static bool eap_mschapv2_isDone(struct eap_sm *sm, void *priv) 541e28a4053SRui Paulo { 542e28a4053SRui Paulo struct eap_mschapv2_data *data = priv; 543e28a4053SRui Paulo return data->state == SUCCESS || data->state == FAILURE; 544e28a4053SRui Paulo } 545e28a4053SRui Paulo 546e28a4053SRui Paulo 547e28a4053SRui Paulo static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 548e28a4053SRui Paulo { 549e28a4053SRui Paulo struct eap_mschapv2_data *data = priv; 550e28a4053SRui Paulo u8 *key; 551*a90b9d01SCy Schubert bool first_is_send; 552e28a4053SRui Paulo 553e28a4053SRui Paulo if (data->state != SUCCESS || !data->master_key_valid) 554e28a4053SRui Paulo return NULL; 555e28a4053SRui Paulo 556e28a4053SRui Paulo *len = 2 * MSCHAPV2_KEY_LEN; 557e28a4053SRui Paulo key = os_malloc(*len); 558e28a4053SRui Paulo if (key == NULL) 559e28a4053SRui Paulo return NULL; 560*a90b9d01SCy Schubert /* 561*a90b9d01SCy Schubert * [MS-CHAP], 3.1.5.1 (Master Session Key (MSK) Derivation 562*a90b9d01SCy Schubert * MSK = MasterReceiveKey + MasterSendKey + 32 bytes zeros (padding) 563*a90b9d01SCy Schubert * On an Authenticator: 564*a90b9d01SCy Schubert * MS-MPPE-Recv-Key = MasterReceiveKey 565*a90b9d01SCy Schubert * MS-MPPE-Send-Key = MasterSendKey 566*a90b9d01SCy Schubert * 567*a90b9d01SCy Schubert * RFC 5422, 3.2.3 (Authenticating Using EAP-FAST-MSCHAPv2) 568*a90b9d01SCy Schubert * MSK = MasterSendKey + MasterReceiveKey 569*a90b9d01SCy Schubert * (i.e., reverse order and no padding) 570*a90b9d01SCy Schubert * 571*a90b9d01SCy Schubert * On Peer, EAP-MSCHAPv2 starts with Send key and EAP-FAST-MSCHAPv2 572*a90b9d01SCy Schubert * starts with Receive key. 573*a90b9d01SCy Schubert */ 574*a90b9d01SCy Schubert first_is_send = sm->eap_fast_mschapv2; 575e28a4053SRui Paulo /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ 576*a90b9d01SCy Schubert if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 577*a90b9d01SCy Schubert first_is_send, 1) < 0 || 578e28a4053SRui Paulo get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 579*a90b9d01SCy Schubert MSCHAPV2_KEY_LEN, !first_is_send, 1) < 0) { 5804bc52338SCy Schubert os_free(key); 5814bc52338SCy Schubert return NULL; 5824bc52338SCy Schubert } 583e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); 584e28a4053SRui Paulo 585e28a4053SRui Paulo return key; 586e28a4053SRui Paulo } 587e28a4053SRui Paulo 588e28a4053SRui Paulo 589c1d255d3SCy Schubert static bool eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) 590e28a4053SRui Paulo { 591e28a4053SRui Paulo struct eap_mschapv2_data *data = priv; 592e28a4053SRui Paulo return data->state == SUCCESS; 593e28a4053SRui Paulo } 594e28a4053SRui Paulo 595e28a4053SRui Paulo 596e28a4053SRui Paulo int eap_server_mschapv2_register(void) 597e28a4053SRui Paulo { 598e28a4053SRui Paulo struct eap_method *eap; 599e28a4053SRui Paulo 600e28a4053SRui Paulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 601e28a4053SRui Paulo EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 602e28a4053SRui Paulo "MSCHAPV2"); 603e28a4053SRui Paulo if (eap == NULL) 604e28a4053SRui Paulo return -1; 605e28a4053SRui Paulo 606e28a4053SRui Paulo eap->init = eap_mschapv2_init; 607e28a4053SRui Paulo eap->reset = eap_mschapv2_reset; 608e28a4053SRui Paulo eap->buildReq = eap_mschapv2_buildReq; 609e28a4053SRui Paulo eap->check = eap_mschapv2_check; 610e28a4053SRui Paulo eap->process = eap_mschapv2_process; 611e28a4053SRui Paulo eap->isDone = eap_mschapv2_isDone; 612e28a4053SRui Paulo eap->getKey = eap_mschapv2_getKey; 613e28a4053SRui Paulo eap->isSuccess = eap_mschapv2_isSuccess; 614e28a4053SRui Paulo 615780fb4a2SCy Schubert return eap_server_method_register(eap); 616e28a4053SRui Paulo } 617