13ff40c12SJohn Marino /*
23ff40c12SJohn Marino * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
33ff40c12SJohn Marino * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
43ff40c12SJohn Marino *
53ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino * See README for more details.
73ff40c12SJohn Marino */
83ff40c12SJohn Marino
93ff40c12SJohn Marino #include "includes.h"
103ff40c12SJohn Marino
113ff40c12SJohn Marino #include "common.h"
123ff40c12SJohn Marino #include "crypto/ms_funcs.h"
133ff40c12SJohn Marino #include "crypto/random.h"
143ff40c12SJohn Marino #include "eap_i.h"
153ff40c12SJohn Marino
163ff40c12SJohn Marino
173ff40c12SJohn Marino struct eap_mschapv2_hdr {
183ff40c12SJohn Marino u8 op_code; /* MSCHAPV2_OP_* */
193ff40c12SJohn Marino u8 mschapv2_id; /* must be changed for challenges, but not for
203ff40c12SJohn Marino * success/failure */
213ff40c12SJohn Marino u8 ms_length[2]; /* Note: misaligned; length - 5 */
223ff40c12SJohn Marino /* followed by data */
233ff40c12SJohn Marino } STRUCT_PACKED;
243ff40c12SJohn Marino
253ff40c12SJohn Marino #define MSCHAPV2_OP_CHALLENGE 1
263ff40c12SJohn Marino #define MSCHAPV2_OP_RESPONSE 2
273ff40c12SJohn Marino #define MSCHAPV2_OP_SUCCESS 3
283ff40c12SJohn Marino #define MSCHAPV2_OP_FAILURE 4
293ff40c12SJohn Marino #define MSCHAPV2_OP_CHANGE_PASSWORD 7
303ff40c12SJohn Marino
313ff40c12SJohn Marino #define MSCHAPV2_RESP_LEN 49
323ff40c12SJohn Marino
333ff40c12SJohn Marino #define ERROR_RESTRICTED_LOGON_HOURS 646
343ff40c12SJohn Marino #define ERROR_ACCT_DISABLED 647
353ff40c12SJohn Marino #define ERROR_PASSWD_EXPIRED 648
363ff40c12SJohn Marino #define ERROR_NO_DIALIN_PERMISSION 649
373ff40c12SJohn Marino #define ERROR_AUTHENTICATION_FAILURE 691
383ff40c12SJohn Marino #define ERROR_CHANGING_PASSWORD 709
393ff40c12SJohn Marino
403ff40c12SJohn Marino #define PASSWD_CHANGE_CHAL_LEN 16
413ff40c12SJohn Marino #define MSCHAPV2_KEY_LEN 16
423ff40c12SJohn Marino
433ff40c12SJohn Marino
443ff40c12SJohn Marino #define CHALLENGE_LEN 16
453ff40c12SJohn Marino
463ff40c12SJohn Marino struct eap_mschapv2_data {
473ff40c12SJohn Marino u8 auth_challenge[CHALLENGE_LEN];
483ff40c12SJohn Marino int auth_challenge_from_tls;
493ff40c12SJohn Marino u8 *peer_challenge;
503ff40c12SJohn Marino u8 auth_response[20];
513ff40c12SJohn Marino enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
523ff40c12SJohn Marino u8 resp_mschapv2_id;
533ff40c12SJohn Marino u8 master_key[16];
543ff40c12SJohn Marino int master_key_valid;
553ff40c12SJohn Marino };
563ff40c12SJohn Marino
573ff40c12SJohn Marino
eap_mschapv2_init(struct eap_sm * sm)583ff40c12SJohn Marino static void * eap_mschapv2_init(struct eap_sm *sm)
593ff40c12SJohn Marino {
603ff40c12SJohn Marino struct eap_mschapv2_data *data;
613ff40c12SJohn Marino
623ff40c12SJohn Marino data = os_zalloc(sizeof(*data));
633ff40c12SJohn Marino if (data == NULL)
643ff40c12SJohn Marino return NULL;
653ff40c12SJohn Marino data->state = CHALLENGE;
663ff40c12SJohn Marino
673ff40c12SJohn Marino if (sm->auth_challenge) {
683ff40c12SJohn Marino os_memcpy(data->auth_challenge, sm->auth_challenge,
693ff40c12SJohn Marino CHALLENGE_LEN);
703ff40c12SJohn Marino data->auth_challenge_from_tls = 1;
713ff40c12SJohn Marino }
723ff40c12SJohn Marino
733ff40c12SJohn Marino if (sm->peer_challenge) {
74*a1157835SDaniel Fojt data->peer_challenge = os_memdup(sm->peer_challenge,
75*a1157835SDaniel Fojt CHALLENGE_LEN);
763ff40c12SJohn Marino if (data->peer_challenge == NULL) {
773ff40c12SJohn Marino os_free(data);
783ff40c12SJohn Marino return NULL;
793ff40c12SJohn Marino }
803ff40c12SJohn Marino }
813ff40c12SJohn Marino
823ff40c12SJohn Marino return data;
833ff40c12SJohn Marino }
843ff40c12SJohn Marino
853ff40c12SJohn Marino
eap_mschapv2_reset(struct eap_sm * sm,void * priv)863ff40c12SJohn Marino static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
873ff40c12SJohn Marino {
883ff40c12SJohn Marino struct eap_mschapv2_data *data = priv;
893ff40c12SJohn Marino if (data == NULL)
903ff40c12SJohn Marino return;
913ff40c12SJohn Marino
923ff40c12SJohn Marino os_free(data->peer_challenge);
93*a1157835SDaniel Fojt bin_clear_free(data, sizeof(*data));
943ff40c12SJohn Marino }
953ff40c12SJohn Marino
963ff40c12SJohn Marino
eap_mschapv2_build_challenge(struct eap_sm * sm,struct eap_mschapv2_data * data,u8 id)973ff40c12SJohn Marino static struct wpabuf * eap_mschapv2_build_challenge(
983ff40c12SJohn Marino struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
993ff40c12SJohn Marino {
1003ff40c12SJohn Marino struct wpabuf *req;
1013ff40c12SJohn Marino struct eap_mschapv2_hdr *ms;
1023ff40c12SJohn Marino size_t ms_len;
1033ff40c12SJohn Marino
1043ff40c12SJohn Marino if (!data->auth_challenge_from_tls &&
1053ff40c12SJohn Marino random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
1063ff40c12SJohn Marino wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
1073ff40c12SJohn Marino "data");
1083ff40c12SJohn Marino data->state = FAILURE;
1093ff40c12SJohn Marino return NULL;
1103ff40c12SJohn Marino }
1113ff40c12SJohn Marino
1123ff40c12SJohn Marino ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len;
1133ff40c12SJohn Marino req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
1143ff40c12SJohn Marino EAP_CODE_REQUEST, id);
1153ff40c12SJohn Marino if (req == NULL) {
1163ff40c12SJohn Marino wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
1173ff40c12SJohn Marino " for request");
1183ff40c12SJohn Marino data->state = FAILURE;
1193ff40c12SJohn Marino return NULL;
1203ff40c12SJohn Marino }
1213ff40c12SJohn Marino
1223ff40c12SJohn Marino ms = wpabuf_put(req, sizeof(*ms));
1233ff40c12SJohn Marino ms->op_code = MSCHAPV2_OP_CHALLENGE;
1243ff40c12SJohn Marino ms->mschapv2_id = id;
1253ff40c12SJohn Marino WPA_PUT_BE16(ms->ms_length, ms_len);
1263ff40c12SJohn Marino
1273ff40c12SJohn Marino wpabuf_put_u8(req, CHALLENGE_LEN);
1283ff40c12SJohn Marino if (!data->auth_challenge_from_tls)
1293ff40c12SJohn Marino wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
1303ff40c12SJohn Marino else
1313ff40c12SJohn Marino wpabuf_put(req, CHALLENGE_LEN);
1323ff40c12SJohn Marino wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
1333ff40c12SJohn Marino data->auth_challenge, CHALLENGE_LEN);
1343ff40c12SJohn Marino wpabuf_put_data(req, sm->server_id, sm->server_id_len);
1353ff40c12SJohn Marino
1363ff40c12SJohn Marino return req;
1373ff40c12SJohn Marino }
1383ff40c12SJohn Marino
1393ff40c12SJohn Marino
eap_mschapv2_build_success_req(struct eap_sm * sm,struct eap_mschapv2_data * data,u8 id)1403ff40c12SJohn Marino static struct wpabuf * eap_mschapv2_build_success_req(
1413ff40c12SJohn Marino struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
1423ff40c12SJohn Marino {
1433ff40c12SJohn Marino struct wpabuf *req;
1443ff40c12SJohn Marino struct eap_mschapv2_hdr *ms;
1453ff40c12SJohn Marino u8 *msg;
1463ff40c12SJohn Marino char *message = "OK";
1473ff40c12SJohn Marino size_t ms_len;
1483ff40c12SJohn Marino
1493ff40c12SJohn Marino ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
1503ff40c12SJohn Marino os_strlen(message);
1513ff40c12SJohn Marino req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
1523ff40c12SJohn Marino EAP_CODE_REQUEST, id);
1533ff40c12SJohn Marino if (req == NULL) {
1543ff40c12SJohn Marino wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
1553ff40c12SJohn Marino " for request");
1563ff40c12SJohn Marino data->state = FAILURE;
1573ff40c12SJohn Marino return NULL;
1583ff40c12SJohn Marino }
1593ff40c12SJohn Marino
1603ff40c12SJohn Marino ms = wpabuf_put(req, sizeof(*ms));
1613ff40c12SJohn Marino ms->op_code = MSCHAPV2_OP_SUCCESS;
1623ff40c12SJohn Marino ms->mschapv2_id = data->resp_mschapv2_id;
1633ff40c12SJohn Marino WPA_PUT_BE16(ms->ms_length, ms_len);
1643ff40c12SJohn Marino msg = (u8 *) (ms + 1);
1653ff40c12SJohn Marino
1663ff40c12SJohn Marino wpabuf_put_u8(req, 'S');
1673ff40c12SJohn Marino wpabuf_put_u8(req, '=');
1683ff40c12SJohn Marino wpa_snprintf_hex_uppercase(
1693ff40c12SJohn Marino wpabuf_put(req, sizeof(data->auth_response) * 2),
1703ff40c12SJohn Marino sizeof(data->auth_response) * 2 + 1,
1713ff40c12SJohn Marino data->auth_response, sizeof(data->auth_response));
1723ff40c12SJohn Marino wpabuf_put_u8(req, ' ');
1733ff40c12SJohn Marino wpabuf_put_u8(req, 'M');
1743ff40c12SJohn Marino wpabuf_put_u8(req, '=');
1753ff40c12SJohn Marino wpabuf_put_data(req, message, os_strlen(message));
1763ff40c12SJohn Marino
1773ff40c12SJohn Marino wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
1783ff40c12SJohn Marino msg, ms_len - sizeof(*ms));
1793ff40c12SJohn Marino
1803ff40c12SJohn Marino return req;
1813ff40c12SJohn Marino }
1823ff40c12SJohn Marino
1833ff40c12SJohn Marino
eap_mschapv2_build_failure_req(struct eap_sm * sm,struct eap_mschapv2_data * data,u8 id)1843ff40c12SJohn Marino static struct wpabuf * eap_mschapv2_build_failure_req(
1853ff40c12SJohn Marino struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
1863ff40c12SJohn Marino {
1873ff40c12SJohn Marino struct wpabuf *req;
1883ff40c12SJohn Marino struct eap_mschapv2_hdr *ms;
1893ff40c12SJohn Marino char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
1903ff40c12SJohn Marino "M=FAILED";
1913ff40c12SJohn Marino size_t ms_len;
1923ff40c12SJohn Marino
1933ff40c12SJohn Marino ms_len = sizeof(*ms) + os_strlen(message);
1943ff40c12SJohn Marino req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
1953ff40c12SJohn Marino EAP_CODE_REQUEST, id);
1963ff40c12SJohn Marino if (req == NULL) {
1973ff40c12SJohn Marino wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
1983ff40c12SJohn Marino " for request");
1993ff40c12SJohn Marino data->state = FAILURE;
2003ff40c12SJohn Marino return NULL;
2013ff40c12SJohn Marino }
2023ff40c12SJohn Marino
2033ff40c12SJohn Marino ms = wpabuf_put(req, sizeof(*ms));
2043ff40c12SJohn Marino ms->op_code = MSCHAPV2_OP_FAILURE;
2053ff40c12SJohn Marino ms->mschapv2_id = data->resp_mschapv2_id;
2063ff40c12SJohn Marino WPA_PUT_BE16(ms->ms_length, ms_len);
2073ff40c12SJohn Marino
2083ff40c12SJohn Marino wpabuf_put_data(req, message, os_strlen(message));
2093ff40c12SJohn Marino
2103ff40c12SJohn Marino wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
2113ff40c12SJohn Marino (u8 *) message, os_strlen(message));
2123ff40c12SJohn Marino
2133ff40c12SJohn Marino return req;
2143ff40c12SJohn Marino }
2153ff40c12SJohn Marino
2163ff40c12SJohn Marino
eap_mschapv2_buildReq(struct eap_sm * sm,void * priv,u8 id)2173ff40c12SJohn Marino static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
2183ff40c12SJohn Marino u8 id)
2193ff40c12SJohn Marino {
2203ff40c12SJohn Marino struct eap_mschapv2_data *data = priv;
2213ff40c12SJohn Marino
2223ff40c12SJohn Marino switch (data->state) {
2233ff40c12SJohn Marino case CHALLENGE:
2243ff40c12SJohn Marino return eap_mschapv2_build_challenge(sm, data, id);
2253ff40c12SJohn Marino case SUCCESS_REQ:
2263ff40c12SJohn Marino return eap_mschapv2_build_success_req(sm, data, id);
2273ff40c12SJohn Marino case FAILURE_REQ:
2283ff40c12SJohn Marino return eap_mschapv2_build_failure_req(sm, data, id);
2293ff40c12SJohn Marino default:
2303ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
2313ff40c12SJohn Marino "buildReq", data->state);
2323ff40c12SJohn Marino break;
2333ff40c12SJohn Marino }
2343ff40c12SJohn Marino return NULL;
2353ff40c12SJohn Marino }
2363ff40c12SJohn Marino
2373ff40c12SJohn Marino
eap_mschapv2_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)2383ff40c12SJohn Marino static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
2393ff40c12SJohn Marino struct wpabuf *respData)
2403ff40c12SJohn Marino {
2413ff40c12SJohn Marino struct eap_mschapv2_data *data = priv;
2423ff40c12SJohn Marino struct eap_mschapv2_hdr *resp;
2433ff40c12SJohn Marino const u8 *pos;
2443ff40c12SJohn Marino size_t len;
2453ff40c12SJohn Marino
2463ff40c12SJohn Marino pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
2473ff40c12SJohn Marino &len);
2483ff40c12SJohn Marino if (pos == NULL || len < 1) {
2493ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
2503ff40c12SJohn Marino return TRUE;
2513ff40c12SJohn Marino }
2523ff40c12SJohn Marino
2533ff40c12SJohn Marino resp = (struct eap_mschapv2_hdr *) pos;
2543ff40c12SJohn Marino if (data->state == CHALLENGE &&
2553ff40c12SJohn Marino resp->op_code != MSCHAPV2_OP_RESPONSE) {
2563ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
2573ff40c12SJohn Marino "ignore op %d", resp->op_code);
2583ff40c12SJohn Marino return TRUE;
2593ff40c12SJohn Marino }
2603ff40c12SJohn Marino
2613ff40c12SJohn Marino if (data->state == SUCCESS_REQ &&
2623ff40c12SJohn Marino resp->op_code != MSCHAPV2_OP_SUCCESS &&
2633ff40c12SJohn Marino resp->op_code != MSCHAPV2_OP_FAILURE) {
2643ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
2653ff40c12SJohn Marino "Failure - ignore op %d", resp->op_code);
2663ff40c12SJohn Marino return TRUE;
2673ff40c12SJohn Marino }
2683ff40c12SJohn Marino
2693ff40c12SJohn Marino if (data->state == FAILURE_REQ &&
2703ff40c12SJohn Marino resp->op_code != MSCHAPV2_OP_FAILURE) {
2713ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
2723ff40c12SJohn Marino "- ignore op %d", resp->op_code);
2733ff40c12SJohn Marino return TRUE;
2743ff40c12SJohn Marino }
2753ff40c12SJohn Marino
2763ff40c12SJohn Marino return FALSE;
2773ff40c12SJohn Marino }
2783ff40c12SJohn Marino
2793ff40c12SJohn Marino
eap_mschapv2_process_response(struct eap_sm * sm,struct eap_mschapv2_data * data,struct wpabuf * respData)2803ff40c12SJohn Marino static void eap_mschapv2_process_response(struct eap_sm *sm,
2813ff40c12SJohn Marino struct eap_mschapv2_data *data,
2823ff40c12SJohn Marino struct wpabuf *respData)
2833ff40c12SJohn Marino {
2843ff40c12SJohn Marino struct eap_mschapv2_hdr *resp;
2853ff40c12SJohn Marino const u8 *pos, *end, *peer_challenge, *nt_response, *name;
2863ff40c12SJohn Marino u8 flags;
2873ff40c12SJohn Marino size_t len, name_len, i;
2883ff40c12SJohn Marino u8 expected[24];
2893ff40c12SJohn Marino const u8 *username, *user;
2903ff40c12SJohn Marino size_t username_len, user_len;
2913ff40c12SJohn Marino int res;
292*a1157835SDaniel Fojt char *buf;
2933ff40c12SJohn Marino
2943ff40c12SJohn Marino pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
2953ff40c12SJohn Marino &len);
2963ff40c12SJohn Marino if (pos == NULL || len < 1)
2973ff40c12SJohn Marino return; /* Should not happen - frame already validated */
2983ff40c12SJohn Marino
2993ff40c12SJohn Marino end = pos + len;
3003ff40c12SJohn Marino resp = (struct eap_mschapv2_hdr *) pos;
3013ff40c12SJohn Marino pos = (u8 *) (resp + 1);
3023ff40c12SJohn Marino
3033ff40c12SJohn Marino if (len < sizeof(*resp) + 1 + 49 ||
3043ff40c12SJohn Marino resp->op_code != MSCHAPV2_OP_RESPONSE ||
3053ff40c12SJohn Marino pos[0] != 49) {
3063ff40c12SJohn Marino wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
3073ff40c12SJohn Marino respData);
3083ff40c12SJohn Marino data->state = FAILURE;
3093ff40c12SJohn Marino return;
3103ff40c12SJohn Marino }
3113ff40c12SJohn Marino data->resp_mschapv2_id = resp->mschapv2_id;
3123ff40c12SJohn Marino pos++;
3133ff40c12SJohn Marino peer_challenge = pos;
3143ff40c12SJohn Marino pos += 16 + 8;
3153ff40c12SJohn Marino nt_response = pos;
3163ff40c12SJohn Marino pos += 24;
3173ff40c12SJohn Marino flags = *pos++;
3183ff40c12SJohn Marino name = pos;
3193ff40c12SJohn Marino name_len = end - name;
3203ff40c12SJohn Marino
3213ff40c12SJohn Marino if (data->peer_challenge) {
3223ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
3233ff40c12SJohn Marino "Peer-Challenge");
3243ff40c12SJohn Marino peer_challenge = data->peer_challenge;
3253ff40c12SJohn Marino }
3263ff40c12SJohn Marino wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
3273ff40c12SJohn Marino peer_challenge, 16);
3283ff40c12SJohn Marino wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
3293ff40c12SJohn Marino wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
3303ff40c12SJohn Marino wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
3313ff40c12SJohn Marino
332*a1157835SDaniel Fojt buf = os_malloc(name_len * 4 + 1);
333*a1157835SDaniel Fojt if (buf) {
334*a1157835SDaniel Fojt printf_encode(buf, name_len * 4 + 1, name, name_len);
335*a1157835SDaniel Fojt eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf);
336*a1157835SDaniel Fojt os_free(buf);
337*a1157835SDaniel Fojt }
338*a1157835SDaniel Fojt
3393ff40c12SJohn Marino /* MSCHAPv2 does not include optional domain name in the
3403ff40c12SJohn Marino * challenge-response calculation, so remove domain prefix
3413ff40c12SJohn Marino * (if present). */
3423ff40c12SJohn Marino username = sm->identity;
3433ff40c12SJohn Marino username_len = sm->identity_len;
3443ff40c12SJohn Marino for (i = 0; i < username_len; i++) {
3453ff40c12SJohn Marino if (username[i] == '\\') {
3463ff40c12SJohn Marino username_len -= i + 1;
3473ff40c12SJohn Marino username += i + 1;
3483ff40c12SJohn Marino break;
3493ff40c12SJohn Marino }
3503ff40c12SJohn Marino }
3513ff40c12SJohn Marino
3523ff40c12SJohn Marino user = name;
3533ff40c12SJohn Marino user_len = name_len;
3543ff40c12SJohn Marino for (i = 0; i < user_len; i++) {
3553ff40c12SJohn Marino if (user[i] == '\\') {
3563ff40c12SJohn Marino user_len -= i + 1;
3573ff40c12SJohn Marino user += i + 1;
3583ff40c12SJohn Marino break;
3593ff40c12SJohn Marino }
3603ff40c12SJohn Marino }
3613ff40c12SJohn Marino
362*a1157835SDaniel Fojt #ifdef CONFIG_TESTING_OPTIONS
363*a1157835SDaniel Fojt {
364*a1157835SDaniel Fojt u8 challenge[8];
365*a1157835SDaniel Fojt
366*a1157835SDaniel Fojt if (challenge_hash(peer_challenge, data->auth_challenge,
367*a1157835SDaniel Fojt username, username_len, challenge) == 0) {
368*a1157835SDaniel Fojt eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2",
369*a1157835SDaniel Fojt username, username_len,
370*a1157835SDaniel Fojt challenge, nt_response);
371*a1157835SDaniel Fojt }
372*a1157835SDaniel Fojt }
373*a1157835SDaniel Fojt #endif /* CONFIG_TESTING_OPTIONS */
374*a1157835SDaniel Fojt
3753ff40c12SJohn Marino if (username_len != user_len ||
3763ff40c12SJohn Marino os_memcmp(username, user, username_len) != 0) {
3773ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
3783ff40c12SJohn Marino wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
3793ff40c12SJohn Marino "name", username, username_len);
3803ff40c12SJohn Marino wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
3813ff40c12SJohn Marino "name", user, user_len);
3823ff40c12SJohn Marino data->state = FAILURE;
3833ff40c12SJohn Marino return;
3843ff40c12SJohn Marino }
3853ff40c12SJohn Marino
3863ff40c12SJohn Marino wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
3873ff40c12SJohn Marino username, username_len);
3883ff40c12SJohn Marino
3893ff40c12SJohn Marino if (sm->user->password_hash) {
3903ff40c12SJohn Marino res = generate_nt_response_pwhash(data->auth_challenge,
3913ff40c12SJohn Marino peer_challenge,
3923ff40c12SJohn Marino username, username_len,
3933ff40c12SJohn Marino sm->user->password,
3943ff40c12SJohn Marino expected);
3953ff40c12SJohn Marino } else {
3963ff40c12SJohn Marino res = generate_nt_response(data->auth_challenge,
3973ff40c12SJohn Marino peer_challenge,
3983ff40c12SJohn Marino username, username_len,
3993ff40c12SJohn Marino sm->user->password,
4003ff40c12SJohn Marino sm->user->password_len,
4013ff40c12SJohn Marino expected);
4023ff40c12SJohn Marino }
4033ff40c12SJohn Marino if (res) {
4043ff40c12SJohn Marino data->state = FAILURE;
4053ff40c12SJohn Marino return;
4063ff40c12SJohn Marino }
4073ff40c12SJohn Marino
408*a1157835SDaniel Fojt if (os_memcmp_const(nt_response, expected, 24) == 0) {
4093ff40c12SJohn Marino const u8 *pw_hash;
4103ff40c12SJohn Marino u8 pw_hash_buf[16], pw_hash_hash[16];
4113ff40c12SJohn Marino
4123ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
4133ff40c12SJohn Marino data->state = SUCCESS_REQ;
4143ff40c12SJohn Marino
4153ff40c12SJohn Marino /* Authenticator response is not really needed yet, but
4163ff40c12SJohn Marino * calculate it here so that peer_challenge and username need
4173ff40c12SJohn Marino * not be saved. */
4183ff40c12SJohn Marino if (sm->user->password_hash) {
4193ff40c12SJohn Marino pw_hash = sm->user->password;
4203ff40c12SJohn Marino } else {
4213ff40c12SJohn Marino if (nt_password_hash(sm->user->password,
4223ff40c12SJohn Marino sm->user->password_len,
4233ff40c12SJohn Marino pw_hash_buf) < 0) {
4243ff40c12SJohn Marino data->state = FAILURE;
4253ff40c12SJohn Marino return;
4263ff40c12SJohn Marino }
4273ff40c12SJohn Marino pw_hash = pw_hash_buf;
4283ff40c12SJohn Marino }
429*a1157835SDaniel Fojt if (generate_authenticator_response_pwhash(
4303ff40c12SJohn Marino pw_hash, peer_challenge, data->auth_challenge,
4313ff40c12SJohn Marino username, username_len, nt_response,
432*a1157835SDaniel Fojt data->auth_response) < 0 ||
433*a1157835SDaniel Fojt hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 ||
434*a1157835SDaniel Fojt get_master_key(pw_hash_hash, nt_response,
435*a1157835SDaniel Fojt data->master_key)) {
436*a1157835SDaniel Fojt data->state = FAILURE;
437*a1157835SDaniel Fojt return;
438*a1157835SDaniel Fojt }
4393ff40c12SJohn Marino data->master_key_valid = 1;
4403ff40c12SJohn Marino wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
4413ff40c12SJohn Marino data->master_key, MSCHAPV2_KEY_LEN);
4423ff40c12SJohn Marino } else {
4433ff40c12SJohn Marino wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
4443ff40c12SJohn Marino expected, 24);
4453ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
4463ff40c12SJohn Marino data->state = FAILURE_REQ;
4473ff40c12SJohn Marino }
4483ff40c12SJohn Marino }
4493ff40c12SJohn Marino
4503ff40c12SJohn Marino
eap_mschapv2_process_success_resp(struct eap_sm * sm,struct eap_mschapv2_data * data,struct wpabuf * respData)4513ff40c12SJohn Marino static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
4523ff40c12SJohn Marino struct eap_mschapv2_data *data,
4533ff40c12SJohn Marino struct wpabuf *respData)
4543ff40c12SJohn Marino {
4553ff40c12SJohn Marino struct eap_mschapv2_hdr *resp;
4563ff40c12SJohn Marino const u8 *pos;
4573ff40c12SJohn Marino size_t len;
4583ff40c12SJohn Marino
4593ff40c12SJohn Marino pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
4603ff40c12SJohn Marino &len);
4613ff40c12SJohn Marino if (pos == NULL || len < 1)
4623ff40c12SJohn Marino return; /* Should not happen - frame already validated */
4633ff40c12SJohn Marino
4643ff40c12SJohn Marino resp = (struct eap_mschapv2_hdr *) pos;
4653ff40c12SJohn Marino
4663ff40c12SJohn Marino if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
4673ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
4683ff40c12SJohn Marino " - authentication completed successfully");
4693ff40c12SJohn Marino data->state = SUCCESS;
4703ff40c12SJohn Marino } else {
4713ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
4723ff40c12SJohn Marino "Response - peer rejected authentication");
4733ff40c12SJohn Marino data->state = FAILURE;
4743ff40c12SJohn Marino }
4753ff40c12SJohn Marino }
4763ff40c12SJohn Marino
4773ff40c12SJohn Marino
eap_mschapv2_process_failure_resp(struct eap_sm * sm,struct eap_mschapv2_data * data,struct wpabuf * respData)4783ff40c12SJohn Marino static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
4793ff40c12SJohn Marino struct eap_mschapv2_data *data,
4803ff40c12SJohn Marino struct wpabuf *respData)
4813ff40c12SJohn Marino {
4823ff40c12SJohn Marino struct eap_mschapv2_hdr *resp;
4833ff40c12SJohn Marino const u8 *pos;
4843ff40c12SJohn Marino size_t len;
4853ff40c12SJohn Marino
4863ff40c12SJohn Marino pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
4873ff40c12SJohn Marino &len);
4883ff40c12SJohn Marino if (pos == NULL || len < 1)
4893ff40c12SJohn Marino return; /* Should not happen - frame already validated */
4903ff40c12SJohn Marino
4913ff40c12SJohn Marino resp = (struct eap_mschapv2_hdr *) pos;
4923ff40c12SJohn Marino
4933ff40c12SJohn Marino if (resp->op_code == MSCHAPV2_OP_FAILURE) {
4943ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
4953ff40c12SJohn Marino " - authentication failed");
4963ff40c12SJohn Marino } else {
4973ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
4983ff40c12SJohn Marino "Response - authentication failed");
4993ff40c12SJohn Marino }
5003ff40c12SJohn Marino
5013ff40c12SJohn Marino data->state = FAILURE;
5023ff40c12SJohn Marino }
5033ff40c12SJohn Marino
5043ff40c12SJohn Marino
eap_mschapv2_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)5053ff40c12SJohn Marino static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
5063ff40c12SJohn Marino struct wpabuf *respData)
5073ff40c12SJohn Marino {
5083ff40c12SJohn Marino struct eap_mschapv2_data *data = priv;
5093ff40c12SJohn Marino
5103ff40c12SJohn Marino if (sm->user == NULL || sm->user->password == NULL) {
5113ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
5123ff40c12SJohn Marino data->state = FAILURE;
5133ff40c12SJohn Marino return;
5143ff40c12SJohn Marino }
5153ff40c12SJohn Marino
5163ff40c12SJohn Marino switch (data->state) {
5173ff40c12SJohn Marino case CHALLENGE:
5183ff40c12SJohn Marino eap_mschapv2_process_response(sm, data, respData);
5193ff40c12SJohn Marino break;
5203ff40c12SJohn Marino case SUCCESS_REQ:
5213ff40c12SJohn Marino eap_mschapv2_process_success_resp(sm, data, respData);
5223ff40c12SJohn Marino break;
5233ff40c12SJohn Marino case FAILURE_REQ:
5243ff40c12SJohn Marino eap_mschapv2_process_failure_resp(sm, data, respData);
5253ff40c12SJohn Marino break;
5263ff40c12SJohn Marino default:
5273ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
5283ff40c12SJohn Marino "process", data->state);
5293ff40c12SJohn Marino break;
5303ff40c12SJohn Marino }
5313ff40c12SJohn Marino }
5323ff40c12SJohn Marino
5333ff40c12SJohn Marino
eap_mschapv2_isDone(struct eap_sm * sm,void * priv)5343ff40c12SJohn Marino static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
5353ff40c12SJohn Marino {
5363ff40c12SJohn Marino struct eap_mschapv2_data *data = priv;
5373ff40c12SJohn Marino return data->state == SUCCESS || data->state == FAILURE;
5383ff40c12SJohn Marino }
5393ff40c12SJohn Marino
5403ff40c12SJohn Marino
eap_mschapv2_getKey(struct eap_sm * sm,void * priv,size_t * len)5413ff40c12SJohn Marino static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
5423ff40c12SJohn Marino {
5433ff40c12SJohn Marino struct eap_mschapv2_data *data = priv;
5443ff40c12SJohn Marino u8 *key;
5453ff40c12SJohn Marino
5463ff40c12SJohn Marino if (data->state != SUCCESS || !data->master_key_valid)
5473ff40c12SJohn Marino return NULL;
5483ff40c12SJohn Marino
5493ff40c12SJohn Marino *len = 2 * MSCHAPV2_KEY_LEN;
5503ff40c12SJohn Marino key = os_malloc(*len);
5513ff40c12SJohn Marino if (key == NULL)
5523ff40c12SJohn Marino return NULL;
5533ff40c12SJohn Marino /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
554*a1157835SDaniel Fojt if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0,
555*a1157835SDaniel Fojt 1) < 0 ||
5563ff40c12SJohn Marino get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
557*a1157835SDaniel Fojt MSCHAPV2_KEY_LEN, 1, 1) < 0) {
558*a1157835SDaniel Fojt os_free(key);
559*a1157835SDaniel Fojt return NULL;
560*a1157835SDaniel Fojt }
5613ff40c12SJohn Marino wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
5623ff40c12SJohn Marino
5633ff40c12SJohn Marino return key;
5643ff40c12SJohn Marino }
5653ff40c12SJohn Marino
5663ff40c12SJohn Marino
eap_mschapv2_isSuccess(struct eap_sm * sm,void * priv)5673ff40c12SJohn Marino static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
5683ff40c12SJohn Marino {
5693ff40c12SJohn Marino struct eap_mschapv2_data *data = priv;
5703ff40c12SJohn Marino return data->state == SUCCESS;
5713ff40c12SJohn Marino }
5723ff40c12SJohn Marino
5733ff40c12SJohn Marino
eap_server_mschapv2_register(void)5743ff40c12SJohn Marino int eap_server_mschapv2_register(void)
5753ff40c12SJohn Marino {
5763ff40c12SJohn Marino struct eap_method *eap;
5773ff40c12SJohn Marino
5783ff40c12SJohn Marino eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
5793ff40c12SJohn Marino EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
5803ff40c12SJohn Marino "MSCHAPV2");
5813ff40c12SJohn Marino if (eap == NULL)
5823ff40c12SJohn Marino return -1;
5833ff40c12SJohn Marino
5843ff40c12SJohn Marino eap->init = eap_mschapv2_init;
5853ff40c12SJohn Marino eap->reset = eap_mschapv2_reset;
5863ff40c12SJohn Marino eap->buildReq = eap_mschapv2_buildReq;
5873ff40c12SJohn Marino eap->check = eap_mschapv2_check;
5883ff40c12SJohn Marino eap->process = eap_mschapv2_process;
5893ff40c12SJohn Marino eap->isDone = eap_mschapv2_isDone;
5903ff40c12SJohn Marino eap->getKey = eap_mschapv2_getKey;
5913ff40c12SJohn Marino eap->isSuccess = eap_mschapv2_isSuccess;
5923ff40c12SJohn Marino
593*a1157835SDaniel Fojt return eap_server_method_register(eap);
5943ff40c12SJohn Marino }
595