xref: /dflybsd-src/contrib/wpa_supplicant/src/eap_server/eap_server_mschapv2.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
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