xref: /dflybsd-src/contrib/wpa_supplicant/src/eap_peer/eap_mschapv2.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer  * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
36d49e1aeSJan Lentfer  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
46d49e1aeSJan Lentfer  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
76d49e1aeSJan Lentfer  *
86d49e1aeSJan Lentfer  * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
96d49e1aeSJan Lentfer  * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
106d49e1aeSJan Lentfer  * Extensions Protocol, Version 2, for mutual authentication and key
116d49e1aeSJan Lentfer  * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
126d49e1aeSJan Lentfer  * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
136d49e1aeSJan Lentfer  * RFC 3079.
146d49e1aeSJan Lentfer  */
156d49e1aeSJan Lentfer 
166d49e1aeSJan Lentfer #include "includes.h"
176d49e1aeSJan Lentfer 
186d49e1aeSJan Lentfer #include "common.h"
193ff40c12SJohn Marino #include "crypto/ms_funcs.h"
203ff40c12SJohn Marino #include "crypto/random.h"
213ff40c12SJohn Marino #include "common/wpa_ctrl.h"
223ff40c12SJohn Marino #include "mschapv2.h"
236d49e1aeSJan Lentfer #include "eap_i.h"
246d49e1aeSJan Lentfer #include "eap_config.h"
256d49e1aeSJan Lentfer 
266d49e1aeSJan Lentfer 
276d49e1aeSJan Lentfer #ifdef _MSC_VER
286d49e1aeSJan Lentfer #pragma pack(push, 1)
296d49e1aeSJan Lentfer #endif /* _MSC_VER */
306d49e1aeSJan Lentfer 
316d49e1aeSJan Lentfer struct eap_mschapv2_hdr {
326d49e1aeSJan Lentfer 	u8 op_code; /* MSCHAPV2_OP_* */
336d49e1aeSJan Lentfer 	u8 mschapv2_id; /* usually same as EAP identifier; must be changed
346d49e1aeSJan Lentfer 			 * for challenges, but not for success/failure */
356d49e1aeSJan Lentfer 	u8 ms_length[2]; /* Note: misaligned; length - 5 */
366d49e1aeSJan Lentfer 	/* followed by data */
376d49e1aeSJan Lentfer } STRUCT_PACKED;
386d49e1aeSJan Lentfer 
396d49e1aeSJan Lentfer /* Response Data field */
406d49e1aeSJan Lentfer struct ms_response {
416d49e1aeSJan Lentfer 	u8 peer_challenge[MSCHAPV2_CHAL_LEN];
426d49e1aeSJan Lentfer 	u8 reserved[8];
436d49e1aeSJan Lentfer 	u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
446d49e1aeSJan Lentfer 	u8 flags;
456d49e1aeSJan Lentfer } STRUCT_PACKED;
466d49e1aeSJan Lentfer 
476d49e1aeSJan Lentfer /* Change-Password Data field */
486d49e1aeSJan Lentfer struct ms_change_password {
496d49e1aeSJan Lentfer 	u8 encr_password[516];
506d49e1aeSJan Lentfer 	u8 encr_hash[16];
516d49e1aeSJan Lentfer 	u8 peer_challenge[MSCHAPV2_CHAL_LEN];
526d49e1aeSJan Lentfer 	u8 reserved[8];
536d49e1aeSJan Lentfer 	u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
546d49e1aeSJan Lentfer 	u8 flags[2];
556d49e1aeSJan Lentfer } STRUCT_PACKED;
566d49e1aeSJan Lentfer 
576d49e1aeSJan Lentfer #ifdef _MSC_VER
586d49e1aeSJan Lentfer #pragma pack(pop)
596d49e1aeSJan Lentfer #endif /* _MSC_VER */
606d49e1aeSJan Lentfer 
616d49e1aeSJan Lentfer #define MSCHAPV2_OP_CHALLENGE 1
626d49e1aeSJan Lentfer #define MSCHAPV2_OP_RESPONSE 2
636d49e1aeSJan Lentfer #define MSCHAPV2_OP_SUCCESS 3
646d49e1aeSJan Lentfer #define MSCHAPV2_OP_FAILURE 4
656d49e1aeSJan Lentfer #define MSCHAPV2_OP_CHANGE_PASSWORD 7
666d49e1aeSJan Lentfer 
676d49e1aeSJan Lentfer #define ERROR_RESTRICTED_LOGON_HOURS 646
686d49e1aeSJan Lentfer #define ERROR_ACCT_DISABLED 647
696d49e1aeSJan Lentfer #define ERROR_PASSWD_EXPIRED 648
706d49e1aeSJan Lentfer #define ERROR_NO_DIALIN_PERMISSION 649
716d49e1aeSJan Lentfer #define ERROR_AUTHENTICATION_FAILURE 691
726d49e1aeSJan Lentfer #define ERROR_CHANGING_PASSWORD 709
736d49e1aeSJan Lentfer 
746d49e1aeSJan Lentfer #define PASSWD_CHANGE_CHAL_LEN 16
756d49e1aeSJan Lentfer #define MSCHAPV2_KEY_LEN 16
766d49e1aeSJan Lentfer 
776d49e1aeSJan Lentfer 
786d49e1aeSJan Lentfer struct eap_mschapv2_data {
796d49e1aeSJan Lentfer 	u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
806d49e1aeSJan Lentfer 	int auth_response_valid;
816d49e1aeSJan Lentfer 
826d49e1aeSJan Lentfer 	int prev_error;
836d49e1aeSJan Lentfer 	u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
846d49e1aeSJan Lentfer 	int passwd_change_challenge_valid;
856d49e1aeSJan Lentfer 	int passwd_change_version;
866d49e1aeSJan Lentfer 
876d49e1aeSJan Lentfer 	/* Optional challenge values generated in EAP-FAST Phase 1 negotiation
886d49e1aeSJan Lentfer 	 */
896d49e1aeSJan Lentfer 	u8 *peer_challenge;
906d49e1aeSJan Lentfer 	u8 *auth_challenge;
916d49e1aeSJan Lentfer 
926d49e1aeSJan Lentfer 	int phase2;
936d49e1aeSJan Lentfer 	u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
946d49e1aeSJan Lentfer 	int master_key_valid;
956d49e1aeSJan Lentfer 	int success;
966d49e1aeSJan Lentfer 
976d49e1aeSJan Lentfer 	struct wpabuf *prev_challenge;
986d49e1aeSJan Lentfer };
996d49e1aeSJan Lentfer 
1006d49e1aeSJan Lentfer 
1016d49e1aeSJan Lentfer static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
1026d49e1aeSJan Lentfer 
1036d49e1aeSJan Lentfer 
eap_mschapv2_init(struct eap_sm * sm)1046d49e1aeSJan Lentfer static void * eap_mschapv2_init(struct eap_sm *sm)
1056d49e1aeSJan Lentfer {
1066d49e1aeSJan Lentfer 	struct eap_mschapv2_data *data;
1076d49e1aeSJan Lentfer 	data = os_zalloc(sizeof(*data));
1086d49e1aeSJan Lentfer 	if (data == NULL)
1096d49e1aeSJan Lentfer 		return NULL;
1106d49e1aeSJan Lentfer 
1116d49e1aeSJan Lentfer 	if (sm->peer_challenge) {
112*a1157835SDaniel Fojt 		data->peer_challenge = os_memdup(sm->peer_challenge,
113*a1157835SDaniel Fojt 						 MSCHAPV2_CHAL_LEN);
1146d49e1aeSJan Lentfer 		if (data->peer_challenge == NULL) {
1156d49e1aeSJan Lentfer 			eap_mschapv2_deinit(sm, data);
1166d49e1aeSJan Lentfer 			return NULL;
1176d49e1aeSJan Lentfer 		}
1186d49e1aeSJan Lentfer 	}
1196d49e1aeSJan Lentfer 
1206d49e1aeSJan Lentfer 	if (sm->auth_challenge) {
121*a1157835SDaniel Fojt 		data->auth_challenge = os_memdup(sm->auth_challenge,
122*a1157835SDaniel Fojt 						 MSCHAPV2_CHAL_LEN);
1236d49e1aeSJan Lentfer 		if (data->auth_challenge == NULL) {
1246d49e1aeSJan Lentfer 			eap_mschapv2_deinit(sm, data);
1256d49e1aeSJan Lentfer 			return NULL;
1266d49e1aeSJan Lentfer 		}
1276d49e1aeSJan Lentfer 	}
1286d49e1aeSJan Lentfer 
1296d49e1aeSJan Lentfer 	data->phase2 = sm->init_phase2;
1306d49e1aeSJan Lentfer 
1316d49e1aeSJan Lentfer 	return data;
1326d49e1aeSJan Lentfer }
1336d49e1aeSJan Lentfer 
1346d49e1aeSJan Lentfer 
eap_mschapv2_deinit(struct eap_sm * sm,void * priv)1356d49e1aeSJan Lentfer static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
1366d49e1aeSJan Lentfer {
1376d49e1aeSJan Lentfer 	struct eap_mschapv2_data *data = priv;
1386d49e1aeSJan Lentfer 	os_free(data->peer_challenge);
1396d49e1aeSJan Lentfer 	os_free(data->auth_challenge);
1406d49e1aeSJan Lentfer 	wpabuf_free(data->prev_challenge);
141*a1157835SDaniel Fojt 	bin_clear_free(data, sizeof(*data));
1426d49e1aeSJan Lentfer }
1436d49e1aeSJan Lentfer 
1446d49e1aeSJan Lentfer 
eap_mschapv2_challenge_reply(struct eap_sm * sm,struct eap_mschapv2_data * data,u8 id,u8 mschapv2_id,const u8 * auth_challenge)1456d49e1aeSJan Lentfer static struct wpabuf * eap_mschapv2_challenge_reply(
1466d49e1aeSJan Lentfer 	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id,
1476d49e1aeSJan Lentfer 	u8 mschapv2_id, const u8 *auth_challenge)
1486d49e1aeSJan Lentfer {
1496d49e1aeSJan Lentfer 	struct wpabuf *resp;
1506d49e1aeSJan Lentfer 	struct eap_mschapv2_hdr *ms;
1516d49e1aeSJan Lentfer 	u8 *peer_challenge;
1526d49e1aeSJan Lentfer 	int ms_len;
1536d49e1aeSJan Lentfer 	struct ms_response *r;
1546d49e1aeSJan Lentfer 	size_t identity_len, password_len;
1556d49e1aeSJan Lentfer 	const u8 *identity, *password;
1566d49e1aeSJan Lentfer 	int pwhash;
1576d49e1aeSJan Lentfer 
1586d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
1596d49e1aeSJan Lentfer 
1606d49e1aeSJan Lentfer 	identity = eap_get_config_identity(sm, &identity_len);
1616d49e1aeSJan Lentfer 	password = eap_get_config_password2(sm, &password_len, &pwhash);
1626d49e1aeSJan Lentfer 	if (identity == NULL || password == NULL)
1636d49e1aeSJan Lentfer 		return NULL;
1646d49e1aeSJan Lentfer 
1656d49e1aeSJan Lentfer 	ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
1666d49e1aeSJan Lentfer 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
1676d49e1aeSJan Lentfer 			     EAP_CODE_RESPONSE, id);
1686d49e1aeSJan Lentfer 	if (resp == NULL)
1696d49e1aeSJan Lentfer 		return NULL;
1706d49e1aeSJan Lentfer 
1716d49e1aeSJan Lentfer 	ms = wpabuf_put(resp, sizeof(*ms));
1726d49e1aeSJan Lentfer 	ms->op_code = MSCHAPV2_OP_RESPONSE;
1736d49e1aeSJan Lentfer 	ms->mschapv2_id = mschapv2_id;
1746d49e1aeSJan Lentfer 	if (data->prev_error) {
1756d49e1aeSJan Lentfer 		/*
1766d49e1aeSJan Lentfer 		 * TODO: this does not seem to be enough when processing two
1776d49e1aeSJan Lentfer 		 * or more failure messages. IAS did not increment mschapv2_id
1786d49e1aeSJan Lentfer 		 * in its own packets, but it seemed to expect the peer to
1796d49e1aeSJan Lentfer 		 * increment this for all packets(?).
1806d49e1aeSJan Lentfer 		 */
1816d49e1aeSJan Lentfer 		ms->mschapv2_id++;
1826d49e1aeSJan Lentfer 	}
1836d49e1aeSJan Lentfer 	WPA_PUT_BE16(ms->ms_length, ms_len);
1846d49e1aeSJan Lentfer 
1856d49e1aeSJan Lentfer 	wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
1866d49e1aeSJan Lentfer 
1876d49e1aeSJan Lentfer 	/* Response */
1886d49e1aeSJan Lentfer 	r = wpabuf_put(resp, sizeof(*r));
1896d49e1aeSJan Lentfer 	peer_challenge = r->peer_challenge;
1906d49e1aeSJan Lentfer 	if (data->peer_challenge) {
1916d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
1926d49e1aeSJan Lentfer 			   "in Phase 1");
1936d49e1aeSJan Lentfer 		peer_challenge = data->peer_challenge;
1946d49e1aeSJan Lentfer 		os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
1953ff40c12SJohn Marino 	} else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
1966d49e1aeSJan Lentfer 		wpabuf_free(resp);
1976d49e1aeSJan Lentfer 		return NULL;
1986d49e1aeSJan Lentfer 	}
1996d49e1aeSJan Lentfer 	os_memset(r->reserved, 0, 8);
2006d49e1aeSJan Lentfer 	if (data->auth_challenge) {
2016d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
2026d49e1aeSJan Lentfer 			   "in Phase 1");
2036d49e1aeSJan Lentfer 		auth_challenge = data->auth_challenge;
2046d49e1aeSJan Lentfer 	}
2053ff40c12SJohn Marino 	if (mschapv2_derive_response(identity, identity_len, password,
2066d49e1aeSJan Lentfer 				     password_len, pwhash, auth_challenge,
2076d49e1aeSJan Lentfer 				     peer_challenge, r->nt_response,
2083ff40c12SJohn Marino 				     data->auth_response, data->master_key)) {
2093ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive "
2103ff40c12SJohn Marino 			   "response");
2113ff40c12SJohn Marino 		wpabuf_free(resp);
2123ff40c12SJohn Marino 		return NULL;
2133ff40c12SJohn Marino 	}
2146d49e1aeSJan Lentfer 	data->auth_response_valid = 1;
2156d49e1aeSJan Lentfer 	data->master_key_valid = 1;
2166d49e1aeSJan Lentfer 
2176d49e1aeSJan Lentfer 	r->flags = 0; /* reserved, must be zero */
2186d49e1aeSJan Lentfer 
2196d49e1aeSJan Lentfer 	wpabuf_put_data(resp, identity, identity_len);
2206d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
2216d49e1aeSJan Lentfer 		   "(response)", id, ms->mschapv2_id);
2226d49e1aeSJan Lentfer 	return resp;
2236d49e1aeSJan Lentfer }
2246d49e1aeSJan Lentfer 
2256d49e1aeSJan Lentfer 
2266d49e1aeSJan Lentfer /**
2276d49e1aeSJan Lentfer  * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
2286d49e1aeSJan Lentfer  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
2296d49e1aeSJan Lentfer  * @data: Pointer to private EAP method data from eap_mschapv2_init()
2306d49e1aeSJan Lentfer  * @ret: Return values from EAP request validation and processing
2316d49e1aeSJan Lentfer  * @req: Pointer to EAP-MSCHAPv2 header from the request
2326d49e1aeSJan Lentfer  * @req_len: Length of the EAP-MSCHAPv2 data
2336d49e1aeSJan Lentfer  * @id: EAP identifier used in the request
2346d49e1aeSJan Lentfer  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
2356d49e1aeSJan Lentfer  * no reply available
2366d49e1aeSJan Lentfer  */
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 req_len,u8 id)2376d49e1aeSJan Lentfer static struct wpabuf * eap_mschapv2_challenge(
2386d49e1aeSJan Lentfer 	struct eap_sm *sm, struct eap_mschapv2_data *data,
2396d49e1aeSJan Lentfer 	struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
2406d49e1aeSJan Lentfer 	size_t req_len, u8 id)
2416d49e1aeSJan Lentfer {
2426d49e1aeSJan Lentfer 	size_t len, challenge_len;
2436d49e1aeSJan Lentfer 	const u8 *pos, *challenge;
2446d49e1aeSJan Lentfer 
2456d49e1aeSJan Lentfer 	if (eap_get_config_identity(sm, &len) == NULL ||
2466d49e1aeSJan Lentfer 	    eap_get_config_password(sm, &len) == NULL)
2476d49e1aeSJan Lentfer 		return NULL;
2486d49e1aeSJan Lentfer 
2496d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
2506d49e1aeSJan Lentfer 	if (req_len < sizeof(*req) + 1) {
2516d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
2526d49e1aeSJan Lentfer 			   "(len %lu)", (unsigned long) req_len);
2536d49e1aeSJan Lentfer 		ret->ignore = TRUE;
2546d49e1aeSJan Lentfer 		return NULL;
2556d49e1aeSJan Lentfer 	}
2566d49e1aeSJan Lentfer 	pos = (const u8 *) (req + 1);
2576d49e1aeSJan Lentfer 	challenge_len = *pos++;
2586d49e1aeSJan Lentfer 	len = req_len - sizeof(*req) - 1;
2596d49e1aeSJan Lentfer 	if (challenge_len != MSCHAPV2_CHAL_LEN) {
2606d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
2616d49e1aeSJan Lentfer 			   "%lu", (unsigned long) challenge_len);
2626d49e1aeSJan Lentfer 		ret->ignore = TRUE;
2636d49e1aeSJan Lentfer 		return NULL;
2646d49e1aeSJan Lentfer 	}
2656d49e1aeSJan Lentfer 
2666d49e1aeSJan Lentfer 	if (len < challenge_len) {
2676d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
2686d49e1aeSJan Lentfer 			   " packet: len=%lu challenge_len=%lu",
2696d49e1aeSJan Lentfer 			   (unsigned long) len, (unsigned long) challenge_len);
2706d49e1aeSJan Lentfer 		ret->ignore = TRUE;
2716d49e1aeSJan Lentfer 		return NULL;
2726d49e1aeSJan Lentfer 	}
2736d49e1aeSJan Lentfer 
2746d49e1aeSJan Lentfer 	if (data->passwd_change_challenge_valid) {
2756d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
2766d49e1aeSJan Lentfer 			   "failure message");
2776d49e1aeSJan Lentfer 		challenge = data->passwd_change_challenge;
2786d49e1aeSJan Lentfer 	} else
2796d49e1aeSJan Lentfer 		challenge = pos;
2806d49e1aeSJan Lentfer 	pos += challenge_len;
2816d49e1aeSJan Lentfer 	len -= challenge_len;
2826d49e1aeSJan Lentfer 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
2836d49e1aeSJan Lentfer 		    pos, len);
2846d49e1aeSJan Lentfer 
2856d49e1aeSJan Lentfer 	ret->ignore = FALSE;
2866d49e1aeSJan Lentfer 	ret->methodState = METHOD_MAY_CONT;
2876d49e1aeSJan Lentfer 	ret->decision = DECISION_FAIL;
2886d49e1aeSJan Lentfer 	ret->allowNotifications = TRUE;
2896d49e1aeSJan Lentfer 
2906d49e1aeSJan Lentfer 	return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
2916d49e1aeSJan Lentfer 					    challenge);
2926d49e1aeSJan Lentfer }
2936d49e1aeSJan Lentfer 
2946d49e1aeSJan Lentfer 
eap_mschapv2_password_changed(struct eap_sm * sm,struct eap_mschapv2_data * data)2956d49e1aeSJan Lentfer static void eap_mschapv2_password_changed(struct eap_sm *sm,
2966d49e1aeSJan Lentfer 					  struct eap_mschapv2_data *data)
2976d49e1aeSJan Lentfer {
2986d49e1aeSJan Lentfer 	struct eap_peer_config *config = eap_get_config(sm);
2996d49e1aeSJan Lentfer 	if (config && config->new_password) {
3006d49e1aeSJan Lentfer 		wpa_msg(sm->msg_ctx, MSG_INFO,
3016d49e1aeSJan Lentfer 			WPA_EVENT_PASSWORD_CHANGED
3026d49e1aeSJan Lentfer 			"EAP-MSCHAPV2: Password changed successfully");
3036d49e1aeSJan Lentfer 		data->prev_error = 0;
304*a1157835SDaniel Fojt 		bin_clear_free(config->password, config->password_len);
3053ff40c12SJohn Marino 		if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
3063ff40c12SJohn Marino 			/* TODO: update external storage */
3073ff40c12SJohn Marino 		} else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
3086d49e1aeSJan Lentfer 			config->password = os_malloc(16);
3096d49e1aeSJan Lentfer 			config->password_len = 16;
310*a1157835SDaniel Fojt 			if (config->password &&
3116d49e1aeSJan Lentfer 			    nt_password_hash(config->new_password,
3126d49e1aeSJan Lentfer 					     config->new_password_len,
313*a1157835SDaniel Fojt 					     config->password)) {
314*a1157835SDaniel Fojt 				bin_clear_free(config->password,
315*a1157835SDaniel Fojt 					       config->password_len);
316*a1157835SDaniel Fojt 				config->password = NULL;
317*a1157835SDaniel Fojt 				config->password_len = 0;
3186d49e1aeSJan Lentfer 			}
319*a1157835SDaniel Fojt 			bin_clear_free(config->new_password,
320*a1157835SDaniel Fojt 				       config->new_password_len);
3216d49e1aeSJan Lentfer 		} else {
3226d49e1aeSJan Lentfer 			config->password = config->new_password;
3236d49e1aeSJan Lentfer 			config->password_len = config->new_password_len;
3246d49e1aeSJan Lentfer 		}
3256d49e1aeSJan Lentfer 		config->new_password = NULL;
3266d49e1aeSJan Lentfer 		config->new_password_len = 0;
3276d49e1aeSJan Lentfer 	}
3286d49e1aeSJan Lentfer }
3296d49e1aeSJan Lentfer 
3306d49e1aeSJan Lentfer 
3316d49e1aeSJan Lentfer /**
3326d49e1aeSJan Lentfer  * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
3336d49e1aeSJan Lentfer  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
3346d49e1aeSJan Lentfer  * @data: Pointer to private EAP method data from eap_mschapv2_init()
3356d49e1aeSJan Lentfer  * @ret: Return values from EAP request validation and processing
3366d49e1aeSJan Lentfer  * @req: Pointer to EAP-MSCHAPv2 header from the request
3376d49e1aeSJan Lentfer  * @req_len: Length of the EAP-MSCHAPv2 data
3386d49e1aeSJan Lentfer  * @id: EAP identifier used in th erequest
3396d49e1aeSJan Lentfer  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
3406d49e1aeSJan Lentfer  * no reply available
3416d49e1aeSJan Lentfer  */
eap_mschapv2_success(struct eap_sm * sm,struct eap_mschapv2_data * data,struct eap_method_ret * ret,const struct eap_mschapv2_hdr * req,size_t req_len,u8 id)3426d49e1aeSJan Lentfer static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
3436d49e1aeSJan Lentfer 					    struct eap_mschapv2_data *data,
3446d49e1aeSJan Lentfer 					    struct eap_method_ret *ret,
3456d49e1aeSJan Lentfer 					    const struct eap_mschapv2_hdr *req,
3466d49e1aeSJan Lentfer 					    size_t req_len, u8 id)
3476d49e1aeSJan Lentfer {
3486d49e1aeSJan Lentfer 	struct wpabuf *resp;
3496d49e1aeSJan Lentfer 	const u8 *pos;
3506d49e1aeSJan Lentfer 	size_t len;
3516d49e1aeSJan Lentfer 
3526d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
3536d49e1aeSJan Lentfer 	len = req_len - sizeof(*req);
3546d49e1aeSJan Lentfer 	pos = (const u8 *) (req + 1);
3556d49e1aeSJan Lentfer 	if (!data->auth_response_valid ||
3566d49e1aeSJan Lentfer 	    mschapv2_verify_auth_response(data->auth_response, pos, len)) {
3576d49e1aeSJan Lentfer 		wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
3586d49e1aeSJan Lentfer 			   "response in success request");
3596d49e1aeSJan Lentfer 		ret->methodState = METHOD_DONE;
3606d49e1aeSJan Lentfer 		ret->decision = DECISION_FAIL;
3616d49e1aeSJan Lentfer 		return NULL;
3626d49e1aeSJan Lentfer 	}
3636d49e1aeSJan Lentfer 	pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
3646d49e1aeSJan Lentfer 	len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
3656d49e1aeSJan Lentfer 	while (len > 0 && *pos == ' ') {
3666d49e1aeSJan Lentfer 		pos++;
3676d49e1aeSJan Lentfer 		len--;
3686d49e1aeSJan Lentfer 	}
3696d49e1aeSJan Lentfer 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
3706d49e1aeSJan Lentfer 			  pos, len);
3716d49e1aeSJan Lentfer 	wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
3726d49e1aeSJan Lentfer 
3736d49e1aeSJan Lentfer 	/* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
3746d49e1aeSJan Lentfer 	 * message. */
3756d49e1aeSJan Lentfer 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
3766d49e1aeSJan Lentfer 			     EAP_CODE_RESPONSE, id);
3776d49e1aeSJan Lentfer 	if (resp == NULL) {
3786d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
3796d49e1aeSJan Lentfer 			   "buffer for success response");
3806d49e1aeSJan Lentfer 		ret->ignore = TRUE;
3816d49e1aeSJan Lentfer 		return NULL;
3826d49e1aeSJan Lentfer 	}
3836d49e1aeSJan Lentfer 
3846d49e1aeSJan Lentfer 	wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
3856d49e1aeSJan Lentfer 
3866d49e1aeSJan Lentfer 	ret->methodState = METHOD_DONE;
3876d49e1aeSJan Lentfer 	ret->decision = DECISION_UNCOND_SUCC;
3886d49e1aeSJan Lentfer 	ret->allowNotifications = FALSE;
3896d49e1aeSJan Lentfer 	data->success = 1;
3906d49e1aeSJan Lentfer 
3916d49e1aeSJan Lentfer 	if (data->prev_error == ERROR_PASSWD_EXPIRED)
3926d49e1aeSJan Lentfer 		eap_mschapv2_password_changed(sm, data);
3936d49e1aeSJan Lentfer 
3946d49e1aeSJan Lentfer 	return resp;
3956d49e1aeSJan Lentfer }
3966d49e1aeSJan Lentfer 
3976d49e1aeSJan Lentfer 
eap_mschapv2_failure_txt(struct eap_sm * sm,struct eap_mschapv2_data * data,char * txt)3986d49e1aeSJan Lentfer static int eap_mschapv2_failure_txt(struct eap_sm *sm,
3996d49e1aeSJan Lentfer 				    struct eap_mschapv2_data *data, char *txt)
4006d49e1aeSJan Lentfer {
4016d49e1aeSJan Lentfer 	char *pos, *msg = "";
4026d49e1aeSJan Lentfer 	int retry = 1;
4036d49e1aeSJan Lentfer 	struct eap_peer_config *config = eap_get_config(sm);
4046d49e1aeSJan Lentfer 
4056d49e1aeSJan Lentfer 	/* For example:
4066d49e1aeSJan Lentfer 	 * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
4076d49e1aeSJan Lentfer 	 */
4086d49e1aeSJan Lentfer 
4096d49e1aeSJan Lentfer 	pos = txt;
4106d49e1aeSJan Lentfer 
4116d49e1aeSJan Lentfer 	if (pos && os_strncmp(pos, "E=", 2) == 0) {
4126d49e1aeSJan Lentfer 		pos += 2;
4136d49e1aeSJan Lentfer 		data->prev_error = atoi(pos);
4146d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
4156d49e1aeSJan Lentfer 			   data->prev_error);
4166d49e1aeSJan Lentfer 		pos = os_strchr(pos, ' ');
4176d49e1aeSJan Lentfer 		if (pos)
4186d49e1aeSJan Lentfer 			pos++;
4196d49e1aeSJan Lentfer 	}
4206d49e1aeSJan Lentfer 
4216d49e1aeSJan Lentfer 	if (pos && os_strncmp(pos, "R=", 2) == 0) {
4226d49e1aeSJan Lentfer 		pos += 2;
4236d49e1aeSJan Lentfer 		retry = atoi(pos);
4246d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
4256d49e1aeSJan Lentfer 			   retry == 1 ? "" : "not ");
4266d49e1aeSJan Lentfer 		pos = os_strchr(pos, ' ');
4276d49e1aeSJan Lentfer 		if (pos)
4286d49e1aeSJan Lentfer 			pos++;
4296d49e1aeSJan Lentfer 	}
4306d49e1aeSJan Lentfer 
4316d49e1aeSJan Lentfer 	if (pos && os_strncmp(pos, "C=", 2) == 0) {
4326d49e1aeSJan Lentfer 		int hex_len;
4336d49e1aeSJan Lentfer 		pos += 2;
4346d49e1aeSJan Lentfer 		hex_len = os_strchr(pos, ' ') - (char *) pos;
4356d49e1aeSJan Lentfer 		if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
4366d49e1aeSJan Lentfer 			if (hexstr2bin(pos, data->passwd_change_challenge,
4376d49e1aeSJan Lentfer 				       PASSWD_CHANGE_CHAL_LEN)) {
4386d49e1aeSJan Lentfer 				wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
4396d49e1aeSJan Lentfer 					   "failure challenge");
4406d49e1aeSJan Lentfer 			} else {
4416d49e1aeSJan Lentfer 				wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
4426d49e1aeSJan Lentfer 					    "challenge",
4436d49e1aeSJan Lentfer 					    data->passwd_change_challenge,
4446d49e1aeSJan Lentfer 					    PASSWD_CHANGE_CHAL_LEN);
4456d49e1aeSJan Lentfer 				data->passwd_change_challenge_valid = 1;
4466d49e1aeSJan Lentfer 			}
4476d49e1aeSJan Lentfer 		} else {
4486d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
4496d49e1aeSJan Lentfer 				   "challenge len %d", hex_len);
4506d49e1aeSJan Lentfer 		}
4516d49e1aeSJan Lentfer 		pos = os_strchr(pos, ' ');
4526d49e1aeSJan Lentfer 		if (pos)
4536d49e1aeSJan Lentfer 			pos++;
4546d49e1aeSJan Lentfer 	} else {
4556d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
4566d49e1aeSJan Lentfer 			   "was not present in failure message");
4576d49e1aeSJan Lentfer 	}
4586d49e1aeSJan Lentfer 
4596d49e1aeSJan Lentfer 	if (pos && os_strncmp(pos, "V=", 2) == 0) {
4606d49e1aeSJan Lentfer 		pos += 2;
4616d49e1aeSJan Lentfer 		data->passwd_change_version = atoi(pos);
4626d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
4636d49e1aeSJan Lentfer 			   "protocol version %d", data->passwd_change_version);
4646d49e1aeSJan Lentfer 		pos = os_strchr(pos, ' ');
4656d49e1aeSJan Lentfer 		if (pos)
4666d49e1aeSJan Lentfer 			pos++;
4676d49e1aeSJan Lentfer 	}
4686d49e1aeSJan Lentfer 
4696d49e1aeSJan Lentfer 	if (pos && os_strncmp(pos, "M=", 2) == 0) {
4706d49e1aeSJan Lentfer 		pos += 2;
4716d49e1aeSJan Lentfer 		msg = pos;
4726d49e1aeSJan Lentfer 	}
473*a1157835SDaniel Fojt 	if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry &&
474*a1157835SDaniel Fojt 	    config && config->phase2 &&
475*a1157835SDaniel Fojt 	    os_strstr(config->phase2, "mschapv2_retry=0")) {
476*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
477*a1157835SDaniel Fojt 			   "EAP-MSCHAPV2: mark password retry disabled based on local configuration");
478*a1157835SDaniel Fojt 		retry = 0;
479*a1157835SDaniel Fojt 	}
4806d49e1aeSJan Lentfer 	wpa_msg(sm->msg_ctx, MSG_WARNING,
4816d49e1aeSJan Lentfer 		"EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
4826d49e1aeSJan Lentfer 		"%d)",
4836d49e1aeSJan Lentfer 		msg, retry == 1 ? "" : "not ", data->prev_error);
4846d49e1aeSJan Lentfer 	if (data->prev_error == ERROR_PASSWD_EXPIRED &&
4856d49e1aeSJan Lentfer 	    data->passwd_change_version == 3 && config) {
4866d49e1aeSJan Lentfer 		if (config->new_password == NULL) {
4876d49e1aeSJan Lentfer 			wpa_msg(sm->msg_ctx, MSG_INFO,
4886d49e1aeSJan Lentfer 				"EAP-MSCHAPV2: Password expired - password "
4896d49e1aeSJan Lentfer 				"change required");
4906d49e1aeSJan Lentfer 			eap_sm_request_new_password(sm);
4916d49e1aeSJan Lentfer 		}
4926d49e1aeSJan Lentfer 	} else if (retry == 1 && config) {
4936d49e1aeSJan Lentfer 		/* TODO: could prevent the current password from being used
4946d49e1aeSJan Lentfer 		 * again at least for some period of time */
4956d49e1aeSJan Lentfer 		if (!config->mschapv2_retry)
4966d49e1aeSJan Lentfer 			eap_sm_request_identity(sm);
4976d49e1aeSJan Lentfer 		eap_sm_request_password(sm);
4986d49e1aeSJan Lentfer 		config->mschapv2_retry = 1;
4996d49e1aeSJan Lentfer 	} else if (config) {
5006d49e1aeSJan Lentfer 		/* TODO: prevent retries using same username/password */
5016d49e1aeSJan Lentfer 		config->mschapv2_retry = 0;
5026d49e1aeSJan Lentfer 	}
5036d49e1aeSJan Lentfer 
5046d49e1aeSJan Lentfer 	return retry == 1;
5056d49e1aeSJan Lentfer }
5066d49e1aeSJan Lentfer 
5076d49e1aeSJan Lentfer 
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)5086d49e1aeSJan Lentfer static struct wpabuf * eap_mschapv2_change_password(
5096d49e1aeSJan Lentfer 	struct eap_sm *sm, struct eap_mschapv2_data *data,
5106d49e1aeSJan Lentfer 	struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
5116d49e1aeSJan Lentfer {
512*a1157835SDaniel Fojt #ifdef CONFIG_NO_RC4
513*a1157835SDaniel Fojt 	wpa_printf(MSG_ERROR,
514*a1157835SDaniel Fojt 		"EAP-MSCHAPV2: RC4 not support in the build - cannot change password");
515*a1157835SDaniel Fojt 	return NULL;
516*a1157835SDaniel Fojt #else /* CONFIG_NO_RC4 */
5176d49e1aeSJan Lentfer 	struct wpabuf *resp;
5186d49e1aeSJan Lentfer 	int ms_len;
5196d49e1aeSJan Lentfer 	const u8 *username, *password, *new_password;
5206d49e1aeSJan Lentfer 	size_t username_len, password_len, new_password_len;
5216d49e1aeSJan Lentfer 	struct eap_mschapv2_hdr *ms;
5226d49e1aeSJan Lentfer 	struct ms_change_password *cp;
5236d49e1aeSJan Lentfer 	u8 password_hash[16], password_hash_hash[16];
5246d49e1aeSJan Lentfer 	int pwhash;
5256d49e1aeSJan Lentfer 
5266d49e1aeSJan Lentfer 	username = eap_get_config_identity(sm, &username_len);
5276d49e1aeSJan Lentfer 	password = eap_get_config_password2(sm, &password_len, &pwhash);
5286d49e1aeSJan Lentfer 	new_password = eap_get_config_new_password(sm, &new_password_len);
5296d49e1aeSJan Lentfer 	if (username == NULL || password == NULL || new_password == NULL)
5306d49e1aeSJan Lentfer 		return NULL;
5316d49e1aeSJan Lentfer 
5326d49e1aeSJan Lentfer 	username = mschapv2_remove_domain(username, &username_len);
5336d49e1aeSJan Lentfer 
5346d49e1aeSJan Lentfer 	ret->ignore = FALSE;
5356d49e1aeSJan Lentfer 	ret->methodState = METHOD_MAY_CONT;
5366d49e1aeSJan Lentfer 	ret->decision = DECISION_COND_SUCC;
5376d49e1aeSJan Lentfer 	ret->allowNotifications = TRUE;
5386d49e1aeSJan Lentfer 
5396d49e1aeSJan Lentfer 	ms_len = sizeof(*ms) + sizeof(*cp);
5406d49e1aeSJan Lentfer 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
5416d49e1aeSJan Lentfer 			     EAP_CODE_RESPONSE, id);
5426d49e1aeSJan Lentfer 	if (resp == NULL)
5436d49e1aeSJan Lentfer 		return NULL;
5446d49e1aeSJan Lentfer 
5456d49e1aeSJan Lentfer 	ms = wpabuf_put(resp, sizeof(*ms));
5466d49e1aeSJan Lentfer 	ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
5476d49e1aeSJan Lentfer 	ms->mschapv2_id = req->mschapv2_id + 1;
5486d49e1aeSJan Lentfer 	WPA_PUT_BE16(ms->ms_length, ms_len);
5496d49e1aeSJan Lentfer 	cp = wpabuf_put(resp, sizeof(*cp));
5506d49e1aeSJan Lentfer 
5516d49e1aeSJan Lentfer 	/* Encrypted-Password */
5526d49e1aeSJan Lentfer 	if (pwhash) {
5536d49e1aeSJan Lentfer 		if (encrypt_pw_block_with_password_hash(
5546d49e1aeSJan Lentfer 			    new_password, new_password_len,
5556d49e1aeSJan Lentfer 			    password, cp->encr_password))
5566d49e1aeSJan Lentfer 			goto fail;
5576d49e1aeSJan Lentfer 	} else {
5586d49e1aeSJan Lentfer 		if (new_password_encrypted_with_old_nt_password_hash(
5596d49e1aeSJan Lentfer 			    new_password, new_password_len,
5606d49e1aeSJan Lentfer 			    password, password_len, cp->encr_password))
5616d49e1aeSJan Lentfer 			goto fail;
5626d49e1aeSJan Lentfer 	}
5636d49e1aeSJan Lentfer 
5646d49e1aeSJan Lentfer 	/* Encrypted-Hash */
5656d49e1aeSJan Lentfer 	if (pwhash) {
5666d49e1aeSJan Lentfer 		u8 new_password_hash[16];
567*a1157835SDaniel Fojt 		if (nt_password_hash(new_password, new_password_len,
568*a1157835SDaniel Fojt 				     new_password_hash) ||
5696d49e1aeSJan Lentfer 		    nt_password_hash_encrypted_with_block(password,
5706d49e1aeSJan Lentfer 							  new_password_hash,
571*a1157835SDaniel Fojt 							  cp->encr_hash))
572*a1157835SDaniel Fojt 			goto fail;
5736d49e1aeSJan Lentfer 	} else {
574*a1157835SDaniel Fojt 		if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
5756d49e1aeSJan Lentfer 			    new_password, new_password_len,
576*a1157835SDaniel Fojt 			    password, password_len, cp->encr_hash))
577*a1157835SDaniel Fojt 			goto fail;
5786d49e1aeSJan Lentfer 	}
5796d49e1aeSJan Lentfer 
5806d49e1aeSJan Lentfer 	/* Peer-Challenge */
5813ff40c12SJohn Marino 	if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
5826d49e1aeSJan Lentfer 		goto fail;
5836d49e1aeSJan Lentfer 
5846d49e1aeSJan Lentfer 	/* Reserved, must be zero */
5856d49e1aeSJan Lentfer 	os_memset(cp->reserved, 0, 8);
5866d49e1aeSJan Lentfer 
5876d49e1aeSJan Lentfer 	/* NT-Response */
5886d49e1aeSJan Lentfer 	wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
5896d49e1aeSJan Lentfer 		    data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
5906d49e1aeSJan Lentfer 	wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
5916d49e1aeSJan Lentfer 		    cp->peer_challenge, MSCHAPV2_CHAL_LEN);
5926d49e1aeSJan Lentfer 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
5936d49e1aeSJan Lentfer 			  username, username_len);
5946d49e1aeSJan Lentfer 	wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
5956d49e1aeSJan Lentfer 			      new_password, new_password_len);
5966d49e1aeSJan Lentfer 	generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
5976d49e1aeSJan Lentfer 			     username, username_len,
5986d49e1aeSJan Lentfer 			     new_password, new_password_len,
5996d49e1aeSJan Lentfer 			     cp->nt_response);
6006d49e1aeSJan Lentfer 	wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
6016d49e1aeSJan Lentfer 		    cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
6026d49e1aeSJan Lentfer 
6036d49e1aeSJan Lentfer 	/* Authenticator response is not really needed yet, but calculate it
6046d49e1aeSJan Lentfer 	 * here so that challenges need not be saved. */
6056d49e1aeSJan Lentfer 	generate_authenticator_response(new_password, new_password_len,
6066d49e1aeSJan Lentfer 					cp->peer_challenge,
6076d49e1aeSJan Lentfer 					data->passwd_change_challenge,
6086d49e1aeSJan Lentfer 					username, username_len,
6096d49e1aeSJan Lentfer 					cp->nt_response, data->auth_response);
6106d49e1aeSJan Lentfer 	data->auth_response_valid = 1;
6116d49e1aeSJan Lentfer 
6126d49e1aeSJan Lentfer 	/* Likewise, generate master_key here since we have the needed data
6136d49e1aeSJan Lentfer 	 * available. */
614*a1157835SDaniel Fojt 	if (nt_password_hash(new_password, new_password_len, password_hash) ||
615*a1157835SDaniel Fojt 	    hash_nt_password_hash(password_hash, password_hash_hash) ||
616*a1157835SDaniel Fojt 	    get_master_key(password_hash_hash, cp->nt_response,
617*a1157835SDaniel Fojt 			   data->master_key)) {
618*a1157835SDaniel Fojt 		data->auth_response_valid = 0;
619*a1157835SDaniel Fojt 		goto fail;
620*a1157835SDaniel Fojt 	}
6216d49e1aeSJan Lentfer 	data->master_key_valid = 1;
6226d49e1aeSJan Lentfer 
6236d49e1aeSJan Lentfer 	/* Flags */
6246d49e1aeSJan Lentfer 	os_memset(cp->flags, 0, 2);
6256d49e1aeSJan Lentfer 
6266d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
6276d49e1aeSJan Lentfer 		   "(change pw)", id, ms->mschapv2_id);
6286d49e1aeSJan Lentfer 
6296d49e1aeSJan Lentfer 	return resp;
6306d49e1aeSJan Lentfer 
6316d49e1aeSJan Lentfer fail:
6326d49e1aeSJan Lentfer 	wpabuf_free(resp);
6336d49e1aeSJan Lentfer 	return NULL;
634*a1157835SDaniel Fojt #endif /* CONFIG_NO_RC4 */
6356d49e1aeSJan Lentfer }
6366d49e1aeSJan Lentfer 
6376d49e1aeSJan Lentfer 
6386d49e1aeSJan Lentfer /**
6396d49e1aeSJan Lentfer  * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
6406d49e1aeSJan Lentfer  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
6416d49e1aeSJan Lentfer  * @data: Pointer to private EAP method data from eap_mschapv2_init()
6426d49e1aeSJan Lentfer  * @ret: Return values from EAP request validation and processing
6436d49e1aeSJan Lentfer  * @req: Pointer to EAP-MSCHAPv2 header from the request
6446d49e1aeSJan Lentfer  * @req_len: Length of the EAP-MSCHAPv2 data
6456d49e1aeSJan Lentfer  * @id: EAP identifier used in th erequest
6466d49e1aeSJan Lentfer  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
6476d49e1aeSJan Lentfer  * no reply available
6486d49e1aeSJan Lentfer  */
eap_mschapv2_failure(struct eap_sm * sm,struct eap_mschapv2_data * data,struct eap_method_ret * ret,const struct eap_mschapv2_hdr * req,size_t req_len,u8 id)6496d49e1aeSJan Lentfer static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
6506d49e1aeSJan Lentfer 					    struct eap_mschapv2_data *data,
6516d49e1aeSJan Lentfer 					    struct eap_method_ret *ret,
6526d49e1aeSJan Lentfer 					    const struct eap_mschapv2_hdr *req,
6536d49e1aeSJan Lentfer 					    size_t req_len, u8 id)
6546d49e1aeSJan Lentfer {
6556d49e1aeSJan Lentfer 	struct wpabuf *resp;
6566d49e1aeSJan Lentfer 	const u8 *msdata = (const u8 *) (req + 1);
6576d49e1aeSJan Lentfer 	char *buf;
6586d49e1aeSJan Lentfer 	size_t len = req_len - sizeof(*req);
6596d49e1aeSJan Lentfer 	int retry = 0;
6606d49e1aeSJan Lentfer 
6616d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
6626d49e1aeSJan Lentfer 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
6636d49e1aeSJan Lentfer 			  msdata, len);
6646d49e1aeSJan Lentfer 	/*
6656d49e1aeSJan Lentfer 	 * eap_mschapv2_failure_txt() expects a nul terminated string, so we
6666d49e1aeSJan Lentfer 	 * must allocate a large enough temporary buffer to create that since
6676d49e1aeSJan Lentfer 	 * the received message does not include nul termination.
6686d49e1aeSJan Lentfer 	 */
6693ff40c12SJohn Marino 	buf = dup_binstr(msdata, len);
6706d49e1aeSJan Lentfer 	if (buf) {
6716d49e1aeSJan Lentfer 		retry = eap_mschapv2_failure_txt(sm, data, buf);
6726d49e1aeSJan Lentfer 		os_free(buf);
6736d49e1aeSJan Lentfer 	}
6746d49e1aeSJan Lentfer 
6756d49e1aeSJan Lentfer 	ret->ignore = FALSE;
6766d49e1aeSJan Lentfer 	ret->methodState = METHOD_DONE;
6776d49e1aeSJan Lentfer 	ret->decision = DECISION_FAIL;
6786d49e1aeSJan Lentfer 	ret->allowNotifications = FALSE;
6796d49e1aeSJan Lentfer 
6806d49e1aeSJan Lentfer 	if (data->prev_error == ERROR_PASSWD_EXPIRED &&
6816d49e1aeSJan Lentfer 	    data->passwd_change_version == 3) {
6826d49e1aeSJan Lentfer 		struct eap_peer_config *config = eap_get_config(sm);
6836d49e1aeSJan Lentfer 		if (config && config->new_password)
6846d49e1aeSJan Lentfer 			return eap_mschapv2_change_password(sm, data, ret, req,
6856d49e1aeSJan Lentfer 							    id);
6866d49e1aeSJan Lentfer 		if (config && config->pending_req_new_password)
6876d49e1aeSJan Lentfer 			return NULL;
6886d49e1aeSJan Lentfer 	} else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
6896d49e1aeSJan Lentfer 		/* TODO: could try to retry authentication, e.g, after having
6906d49e1aeSJan Lentfer 		 * changed the username/password. In this case, EAP MS-CHAP-v2
6916d49e1aeSJan Lentfer 		 * Failure Response would not be sent here. */
6926d49e1aeSJan Lentfer 		return NULL;
6936d49e1aeSJan Lentfer 	}
6946d49e1aeSJan Lentfer 
6956d49e1aeSJan Lentfer 	/* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
6966d49e1aeSJan Lentfer 	 * message. */
6976d49e1aeSJan Lentfer 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
6986d49e1aeSJan Lentfer 			     EAP_CODE_RESPONSE, id);
6996d49e1aeSJan Lentfer 	if (resp == NULL)
7006d49e1aeSJan Lentfer 		return NULL;
7016d49e1aeSJan Lentfer 
7026d49e1aeSJan Lentfer 	wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
7036d49e1aeSJan Lentfer 
7046d49e1aeSJan Lentfer 	return resp;
7056d49e1aeSJan Lentfer }
7066d49e1aeSJan Lentfer 
7076d49e1aeSJan Lentfer 
eap_mschapv2_check_config(struct eap_sm * sm)7086d49e1aeSJan Lentfer static int eap_mschapv2_check_config(struct eap_sm *sm)
7096d49e1aeSJan Lentfer {
7106d49e1aeSJan Lentfer 	size_t len;
7116d49e1aeSJan Lentfer 
7126d49e1aeSJan Lentfer 	if (eap_get_config_identity(sm, &len) == NULL) {
7136d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
7146d49e1aeSJan Lentfer 		eap_sm_request_identity(sm);
7156d49e1aeSJan Lentfer 		return -1;
7166d49e1aeSJan Lentfer 	}
7176d49e1aeSJan Lentfer 
7186d49e1aeSJan Lentfer 	if (eap_get_config_password(sm, &len) == NULL) {
7196d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
7206d49e1aeSJan Lentfer 		eap_sm_request_password(sm);
7216d49e1aeSJan Lentfer 		return -1;
7226d49e1aeSJan Lentfer 	}
7236d49e1aeSJan Lentfer 
7246d49e1aeSJan Lentfer 	return 0;
7256d49e1aeSJan Lentfer }
7266d49e1aeSJan Lentfer 
7276d49e1aeSJan Lentfer 
eap_mschapv2_check_mslen(struct eap_sm * sm,size_t len,const struct eap_mschapv2_hdr * ms)7286d49e1aeSJan Lentfer static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
7296d49e1aeSJan Lentfer 				    const struct eap_mschapv2_hdr *ms)
7306d49e1aeSJan Lentfer {
7316d49e1aeSJan Lentfer 	size_t ms_len = WPA_GET_BE16(ms->ms_length);
7326d49e1aeSJan Lentfer 
7336d49e1aeSJan Lentfer 	if (ms_len == len)
7346d49e1aeSJan Lentfer 		return 0;
7356d49e1aeSJan Lentfer 
7366d49e1aeSJan Lentfer 	wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
7376d49e1aeSJan Lentfer 		   "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
7386d49e1aeSJan Lentfer 	if (sm->workaround) {
7396d49e1aeSJan Lentfer 		/* Some authentication servers use invalid ms_len,
7406d49e1aeSJan Lentfer 		 * ignore it for interoperability. */
7416d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
7426d49e1aeSJan Lentfer 			   " invalid ms_len %lu (len %lu)",
7436d49e1aeSJan Lentfer 			   (unsigned long) ms_len,
7446d49e1aeSJan Lentfer 			   (unsigned long) len);
7456d49e1aeSJan Lentfer 		return 0;
7466d49e1aeSJan Lentfer 	}
7476d49e1aeSJan Lentfer 
7486d49e1aeSJan Lentfer 	return -1;
7496d49e1aeSJan Lentfer }
7506d49e1aeSJan Lentfer 
7516d49e1aeSJan Lentfer 
eap_mschapv2_copy_challenge(struct eap_mschapv2_data * data,const struct wpabuf * reqData)7526d49e1aeSJan Lentfer static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
7536d49e1aeSJan Lentfer 					const struct wpabuf *reqData)
7546d49e1aeSJan Lentfer {
7556d49e1aeSJan Lentfer 	/*
7566d49e1aeSJan Lentfer 	 * Store a copy of the challenge message, so that it can be processed
7576d49e1aeSJan Lentfer 	 * again in case retry is allowed after a possible failure.
7586d49e1aeSJan Lentfer 	 */
7596d49e1aeSJan Lentfer 	wpabuf_free(data->prev_challenge);
7606d49e1aeSJan Lentfer 	data->prev_challenge = wpabuf_dup(reqData);
7616d49e1aeSJan Lentfer }
7626d49e1aeSJan Lentfer 
7636d49e1aeSJan Lentfer 
7646d49e1aeSJan Lentfer /**
7656d49e1aeSJan Lentfer  * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
7666d49e1aeSJan Lentfer  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
7676d49e1aeSJan Lentfer  * @priv: Pointer to private EAP method data from eap_mschapv2_init()
7686d49e1aeSJan Lentfer  * @ret: Return values from EAP request validation and processing
7696d49e1aeSJan Lentfer  * @reqData: EAP request to be processed (eapReqData)
7706d49e1aeSJan Lentfer  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
7716d49e1aeSJan Lentfer  * no reply available
7726d49e1aeSJan Lentfer  */
eap_mschapv2_process(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)7736d49e1aeSJan Lentfer static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
7746d49e1aeSJan Lentfer 					    struct eap_method_ret *ret,
7756d49e1aeSJan Lentfer 					    const struct wpabuf *reqData)
7766d49e1aeSJan Lentfer {
7776d49e1aeSJan Lentfer 	struct eap_mschapv2_data *data = priv;
7786d49e1aeSJan Lentfer 	struct eap_peer_config *config = eap_get_config(sm);
7796d49e1aeSJan Lentfer 	const struct eap_mschapv2_hdr *ms;
7806d49e1aeSJan Lentfer 	int using_prev_challenge = 0;
7816d49e1aeSJan Lentfer 	const u8 *pos;
7826d49e1aeSJan Lentfer 	size_t len;
7836d49e1aeSJan Lentfer 	u8 id;
7846d49e1aeSJan Lentfer 
7856d49e1aeSJan Lentfer 	if (eap_mschapv2_check_config(sm)) {
7866d49e1aeSJan Lentfer 		ret->ignore = TRUE;
7876d49e1aeSJan Lentfer 		return NULL;
7886d49e1aeSJan Lentfer 	}
7896d49e1aeSJan Lentfer 
7906d49e1aeSJan Lentfer 	if (config->mschapv2_retry && data->prev_challenge &&
7916d49e1aeSJan Lentfer 	    data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
7926d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
7936d49e1aeSJan Lentfer 			   "with the previous challenge");
7946d49e1aeSJan Lentfer 
7956d49e1aeSJan Lentfer 		reqData = data->prev_challenge;
7966d49e1aeSJan Lentfer 		using_prev_challenge = 1;
7976d49e1aeSJan Lentfer 		config->mschapv2_retry = 0;
7986d49e1aeSJan Lentfer 	}
7996d49e1aeSJan Lentfer 
8006d49e1aeSJan Lentfer 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
8016d49e1aeSJan Lentfer 			       &len);
8026d49e1aeSJan Lentfer 	if (pos == NULL || len < sizeof(*ms) + 1) {
8036d49e1aeSJan Lentfer 		ret->ignore = TRUE;
8046d49e1aeSJan Lentfer 		return NULL;
8056d49e1aeSJan Lentfer 	}
8066d49e1aeSJan Lentfer 
8076d49e1aeSJan Lentfer 	ms = (const struct eap_mschapv2_hdr *) pos;
8086d49e1aeSJan Lentfer 	if (eap_mschapv2_check_mslen(sm, len, ms)) {
8096d49e1aeSJan Lentfer 		ret->ignore = TRUE;
8106d49e1aeSJan Lentfer 		return NULL;
8116d49e1aeSJan Lentfer 	}
8126d49e1aeSJan Lentfer 
8136d49e1aeSJan Lentfer 	id = eap_get_id(reqData);
8146d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
8156d49e1aeSJan Lentfer 		   id, ms->mschapv2_id);
8166d49e1aeSJan Lentfer 
8176d49e1aeSJan Lentfer 	switch (ms->op_code) {
8186d49e1aeSJan Lentfer 	case MSCHAPV2_OP_CHALLENGE:
8196d49e1aeSJan Lentfer 		if (!using_prev_challenge)
8206d49e1aeSJan Lentfer 			eap_mschapv2_copy_challenge(data, reqData);
8216d49e1aeSJan Lentfer 		return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
8226d49e1aeSJan Lentfer 	case MSCHAPV2_OP_SUCCESS:
8236d49e1aeSJan Lentfer 		return eap_mschapv2_success(sm, data, ret, ms, len, id);
8246d49e1aeSJan Lentfer 	case MSCHAPV2_OP_FAILURE:
8256d49e1aeSJan Lentfer 		return eap_mschapv2_failure(sm, data, ret, ms, len, id);
8266d49e1aeSJan Lentfer 	default:
8276d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
8286d49e1aeSJan Lentfer 			   ms->op_code);
8296d49e1aeSJan Lentfer 		ret->ignore = TRUE;
8306d49e1aeSJan Lentfer 		return NULL;
8316d49e1aeSJan Lentfer 	}
8326d49e1aeSJan Lentfer }
8336d49e1aeSJan Lentfer 
8346d49e1aeSJan Lentfer 
eap_mschapv2_isKeyAvailable(struct eap_sm * sm,void * priv)8356d49e1aeSJan Lentfer static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
8366d49e1aeSJan Lentfer {
8376d49e1aeSJan Lentfer 	struct eap_mschapv2_data *data = priv;
8386d49e1aeSJan Lentfer 	return data->success && data->master_key_valid;
8396d49e1aeSJan Lentfer }
8406d49e1aeSJan Lentfer 
8416d49e1aeSJan Lentfer 
eap_mschapv2_getKey(struct eap_sm * sm,void * priv,size_t * len)8426d49e1aeSJan Lentfer static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
8436d49e1aeSJan Lentfer {
8446d49e1aeSJan Lentfer 	struct eap_mschapv2_data *data = priv;
8456d49e1aeSJan Lentfer 	u8 *key;
8466d49e1aeSJan Lentfer 	int key_len;
8476d49e1aeSJan Lentfer 
8486d49e1aeSJan Lentfer 	if (!data->master_key_valid || !data->success)
8496d49e1aeSJan Lentfer 		return NULL;
8506d49e1aeSJan Lentfer 
8516d49e1aeSJan Lentfer 	key_len = 2 * MSCHAPV2_KEY_LEN;
8526d49e1aeSJan Lentfer 
8536d49e1aeSJan Lentfer 	key = os_malloc(key_len);
8546d49e1aeSJan Lentfer 	if (key == NULL)
8556d49e1aeSJan Lentfer 		return NULL;
8566d49e1aeSJan Lentfer 
8576d49e1aeSJan Lentfer 	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
8586d49e1aeSJan Lentfer 	 *	peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
859*a1157835SDaniel Fojt 	if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1,
860*a1157835SDaniel Fojt 				    0) < 0 ||
8616d49e1aeSJan Lentfer 	    get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
862*a1157835SDaniel Fojt 				    MSCHAPV2_KEY_LEN, 0, 0) < 0) {
863*a1157835SDaniel Fojt 		os_free(key);
864*a1157835SDaniel Fojt 		return NULL;
865*a1157835SDaniel Fojt 	}
8666d49e1aeSJan Lentfer 
8676d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
8686d49e1aeSJan Lentfer 			key, key_len);
8696d49e1aeSJan Lentfer 
8706d49e1aeSJan Lentfer 	*len = key_len;
8716d49e1aeSJan Lentfer 	return key;
8726d49e1aeSJan Lentfer }
8736d49e1aeSJan Lentfer 
8746d49e1aeSJan Lentfer 
8756d49e1aeSJan Lentfer /**
8766d49e1aeSJan Lentfer  * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
8776d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
8786d49e1aeSJan Lentfer  *
8796d49e1aeSJan Lentfer  * This function is used to register EAP-MSCHAPv2 peer method into the EAP
8806d49e1aeSJan Lentfer  * method list.
8816d49e1aeSJan Lentfer  */
eap_peer_mschapv2_register(void)8826d49e1aeSJan Lentfer int eap_peer_mschapv2_register(void)
8836d49e1aeSJan Lentfer {
8846d49e1aeSJan Lentfer 	struct eap_method *eap;
8856d49e1aeSJan Lentfer 
8866d49e1aeSJan Lentfer 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
8876d49e1aeSJan Lentfer 				    EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
8886d49e1aeSJan Lentfer 				    "MSCHAPV2");
8896d49e1aeSJan Lentfer 	if (eap == NULL)
8906d49e1aeSJan Lentfer 		return -1;
8916d49e1aeSJan Lentfer 
8926d49e1aeSJan Lentfer 	eap->init = eap_mschapv2_init;
8936d49e1aeSJan Lentfer 	eap->deinit = eap_mschapv2_deinit;
8946d49e1aeSJan Lentfer 	eap->process = eap_mschapv2_process;
8956d49e1aeSJan Lentfer 	eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
8966d49e1aeSJan Lentfer 	eap->getKey = eap_mschapv2_getKey;
8976d49e1aeSJan Lentfer 
898*a1157835SDaniel Fojt 	return eap_peer_method_register(eap);
8996d49e1aeSJan Lentfer }
900