Home | History | Annotate | Download | only in eap_peer
      1 /*
      2  * EAP peer method: LEAP
      3  * Copyright (c) 2004-2007, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "includes.h"
     10 
     11 #include "common.h"
     12 #include "crypto/ms_funcs.h"
     13 #include "crypto/crypto.h"
     14 #include "crypto/random.h"
     15 #include "eap_i.h"
     16 
     17 #define LEAP_VERSION 1
     18 #define LEAP_CHALLENGE_LEN 8
     19 #define LEAP_RESPONSE_LEN 24
     20 #define LEAP_KEY_LEN 16
     21 
     22 
     23 struct eap_leap_data {
     24 	enum {
     25 		LEAP_WAIT_CHALLENGE,
     26 		LEAP_WAIT_SUCCESS,
     27 		LEAP_WAIT_RESPONSE,
     28 		LEAP_DONE
     29 	} state;
     30 
     31 	u8 peer_challenge[LEAP_CHALLENGE_LEN];
     32 	u8 peer_response[LEAP_RESPONSE_LEN];
     33 
     34 	u8 ap_challenge[LEAP_CHALLENGE_LEN];
     35 	u8 ap_response[LEAP_RESPONSE_LEN];
     36 };
     37 
     38 
     39 static void * eap_leap_init(struct eap_sm *sm)
     40 {
     41 	struct eap_leap_data *data;
     42 
     43 	data = os_zalloc(sizeof(*data));
     44 	if (data == NULL)
     45 		return NULL;
     46 	data->state = LEAP_WAIT_CHALLENGE;
     47 
     48 	sm->leap_done = FALSE;
     49 	return data;
     50 }
     51 
     52 
     53 static void eap_leap_deinit(struct eap_sm *sm, void *priv)
     54 {
     55 	os_free(priv);
     56 }
     57 
     58 
     59 static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv,
     60 						struct eap_method_ret *ret,
     61 						const struct wpabuf *reqData)
     62 {
     63 	struct eap_leap_data *data = priv;
     64 	struct wpabuf *resp;
     65 	const u8 *pos, *challenge, *identity, *password;
     66 	u8 challenge_len, *rpos;
     67 	size_t identity_len, password_len, len;
     68 	int pwhash;
     69 
     70 	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request");
     71 
     72 	identity = eap_get_config_identity(sm, &identity_len);
     73 	password = eap_get_config_password2(sm, &password_len, &pwhash);
     74 	if (identity == NULL || password == NULL)
     75 		return NULL;
     76 
     77 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
     78 	if (pos == NULL || len < 3) {
     79 		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame");
     80 		ret->ignore = TRUE;
     81 		return NULL;
     82 	}
     83 
     84 	if (*pos != LEAP_VERSION) {
     85 		wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
     86 			   "%d", *pos);
     87 		ret->ignore = TRUE;
     88 		return NULL;
     89 	}
     90 	pos++;
     91 
     92 	pos++; /* skip unused byte */
     93 
     94 	challenge_len = *pos++;
     95 	if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) {
     96 		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge "
     97 			   "(challenge_len=%d reqDataLen=%lu)",
     98 			   challenge_len, (unsigned long) wpabuf_len(reqData));
     99 		ret->ignore = TRUE;
    100 		return NULL;
    101 	}
    102 	challenge = pos;
    103 	os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN);
    104 	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP",
    105 		    challenge, LEAP_CHALLENGE_LEN);
    106 
    107 	wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response");
    108 
    109 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
    110 			     3 + LEAP_RESPONSE_LEN + identity_len,
    111 			     EAP_CODE_RESPONSE, eap_get_id(reqData));
    112 	if (resp == NULL)
    113 		return NULL;
    114 	wpabuf_put_u8(resp, LEAP_VERSION);
    115 	wpabuf_put_u8(resp, 0); /* unused */
    116 	wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
    117 	rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
    118 	if (pwhash)
    119 		challenge_response(challenge, password, rpos);
    120 	else
    121 		nt_challenge_response(challenge, password, password_len, rpos);
    122 	os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
    123 	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
    124 		    rpos, LEAP_RESPONSE_LEN);
    125 	wpabuf_put_data(resp, identity, identity_len);
    126 
    127 	data->state = LEAP_WAIT_SUCCESS;
    128 
    129 	return resp;
    130 }
    131 
    132 
    133 static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
    134 						struct eap_method_ret *ret,
    135 						const struct wpabuf *reqData)
    136 {
    137 	struct eap_leap_data *data = priv;
    138 	struct wpabuf *resp;
    139 	u8 *pos;
    140 	const u8 *identity;
    141 	size_t identity_len;
    142 
    143 	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
    144 
    145 	identity = eap_get_config_identity(sm, &identity_len);
    146 	if (identity == NULL)
    147 		return NULL;
    148 
    149 	if (data->state != LEAP_WAIT_SUCCESS) {
    150 		wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
    151 			   "unexpected state (%d) - ignored", data->state);
    152 		ret->ignore = TRUE;
    153 		return NULL;
    154 	}
    155 
    156 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
    157 			     3 + LEAP_CHALLENGE_LEN + identity_len,
    158 			     EAP_CODE_REQUEST, eap_get_id(reqData));
    159 	if (resp == NULL)
    160 		return NULL;
    161 	wpabuf_put_u8(resp, LEAP_VERSION);
    162 	wpabuf_put_u8(resp, 0); /* unused */
    163 	wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
    164 	pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
    165 	if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
    166 		wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
    167 			   "for challenge");
    168 		wpabuf_free(resp);
    169 		ret->ignore = TRUE;
    170 		return NULL;
    171 	}
    172 	os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
    173 	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
    174 		    LEAP_CHALLENGE_LEN);
    175 	wpabuf_put_data(resp, identity, identity_len);
    176 
    177 	data->state = LEAP_WAIT_RESPONSE;
    178 
    179 	return resp;
    180 }
    181 
    182 
    183 static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
    184 						 struct eap_method_ret *ret,
    185 						 const struct wpabuf *reqData)
    186 {
    187 	struct eap_leap_data *data = priv;
    188 	const u8 *pos, *password;
    189 	u8 response_len, pw_hash[16], pw_hash_hash[16],
    190 		expected[LEAP_RESPONSE_LEN];
    191 	size_t password_len, len;
    192 	int pwhash;
    193 
    194 	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
    195 
    196 	password = eap_get_config_password2(sm, &password_len, &pwhash);
    197 	if (password == NULL)
    198 		return NULL;
    199 
    200 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
    201 	if (pos == NULL || len < 3) {
    202 		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
    203 		ret->ignore = TRUE;
    204 		return NULL;
    205 	}
    206 
    207 	if (*pos != LEAP_VERSION) {
    208 		wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
    209 			   "%d", *pos);
    210 		ret->ignore = TRUE;
    211 		return NULL;
    212 	}
    213 	pos++;
    214 
    215 	pos++; /* skip unused byte */
    216 
    217 	response_len = *pos++;
    218 	if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) {
    219 		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
    220 			   "(response_len=%d reqDataLen=%lu)",
    221 			   response_len, (unsigned long) wpabuf_len(reqData));
    222 		ret->ignore = TRUE;
    223 		return NULL;
    224 	}
    225 
    226 	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
    227 		    pos, LEAP_RESPONSE_LEN);
    228 	os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
    229 
    230 	if (pwhash) {
    231 		if (hash_nt_password_hash(password, pw_hash_hash)) {
    232 			ret->ignore = TRUE;
    233 			return NULL;
    234 		}
    235 	} else {
    236 		if (nt_password_hash(password, password_len, pw_hash) ||
    237 		    hash_nt_password_hash(pw_hash, pw_hash_hash)) {
    238 			ret->ignore = TRUE;
    239 			return NULL;
    240 		}
    241 	}
    242 	challenge_response(data->ap_challenge, pw_hash_hash, expected);
    243 
    244 	ret->methodState = METHOD_DONE;
    245 	ret->allowNotifications = FALSE;
    246 
    247 	if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
    248 		wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
    249 			   "response - authentication failed");
    250 		wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
    251 			    expected, LEAP_RESPONSE_LEN);
    252 		ret->decision = DECISION_FAIL;
    253 		return NULL;
    254 	}
    255 
    256 	ret->decision = DECISION_UNCOND_SUCC;
    257 
    258 	/* LEAP is somewhat odd method since it sends EAP-Success in the middle
    259 	 * of the authentication. Use special variable to transit EAP state
    260 	 * machine to SUCCESS state. */
    261 	sm->leap_done = TRUE;
    262 	data->state = LEAP_DONE;
    263 
    264 	/* No more authentication messages expected; AP will send EAPOL-Key
    265 	 * frames if encryption is enabled. */
    266 	return NULL;
    267 }
    268 
    269 
    270 static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv,
    271 					struct eap_method_ret *ret,
    272 					const struct wpabuf *reqData)
    273 {
    274 	const struct eap_hdr *eap;
    275 	size_t password_len;
    276 	const u8 *password;
    277 
    278 	password = eap_get_config_password(sm, &password_len);
    279 	if (password == NULL) {
    280 		wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
    281 		eap_sm_request_password(sm);
    282 		ret->ignore = TRUE;
    283 		return NULL;
    284 	}
    285 
    286 	/*
    287 	 * LEAP needs to be able to handle EAP-Success frame which does not
    288 	 * include Type field. Consequently, eap_hdr_validate() cannot be used
    289 	 * here. This validation will be done separately for EAP-Request and
    290 	 * EAP-Response frames.
    291 	 */
    292 	eap = wpabuf_head(reqData);
    293 	if (wpabuf_len(reqData) < sizeof(*eap) ||
    294 	    be_to_host16(eap->length) > wpabuf_len(reqData)) {
    295 		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
    296 		ret->ignore = TRUE;
    297 		return NULL;
    298 	}
    299 
    300 	ret->ignore = FALSE;
    301 	ret->allowNotifications = TRUE;
    302 	ret->methodState = METHOD_MAY_CONT;
    303 	ret->decision = DECISION_FAIL;
    304 
    305 	sm->leap_done = FALSE;
    306 
    307 	switch (eap->code) {
    308 	case EAP_CODE_REQUEST:
    309 		return eap_leap_process_request(sm, priv, ret, reqData);
    310 	case EAP_CODE_SUCCESS:
    311 		return eap_leap_process_success(sm, priv, ret, reqData);
    312 	case EAP_CODE_RESPONSE:
    313 		return eap_leap_process_response(sm, priv, ret, reqData);
    314 	default:
    315 		wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
    316 			   "ignored", eap->code);
    317 		ret->ignore = TRUE;
    318 		return NULL;
    319 	}
    320 }
    321 
    322 
    323 static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
    324 {
    325 	struct eap_leap_data *data = priv;
    326 	return data->state == LEAP_DONE;
    327 }
    328 
    329 
    330 static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
    331 {
    332 	struct eap_leap_data *data = priv;
    333 	u8 *key, pw_hash_hash[16], pw_hash[16];
    334 	const u8 *addr[5], *password;
    335 	size_t elen[5], password_len;
    336 	int pwhash;
    337 
    338 	if (data->state != LEAP_DONE)
    339 		return NULL;
    340 
    341 	password = eap_get_config_password2(sm, &password_len, &pwhash);
    342 	if (password == NULL)
    343 		return NULL;
    344 
    345 	key = os_malloc(LEAP_KEY_LEN);
    346 	if (key == NULL)
    347 		return NULL;
    348 
    349 	if (pwhash) {
    350 		if (hash_nt_password_hash(password, pw_hash_hash)) {
    351 			os_free(key);
    352 			return NULL;
    353 		}
    354 	} else {
    355 		if (nt_password_hash(password, password_len, pw_hash) ||
    356 		    hash_nt_password_hash(pw_hash, pw_hash_hash)) {
    357 			os_free(key);
    358 			return NULL;
    359 		}
    360 	}
    361 	wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
    362 			pw_hash_hash, 16);
    363 	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
    364 		    data->peer_challenge, LEAP_CHALLENGE_LEN);
    365 	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
    366 		    data->peer_response, LEAP_RESPONSE_LEN);
    367 	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
    368 		    data->ap_challenge, LEAP_CHALLENGE_LEN);
    369 	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
    370 		    data->ap_response, LEAP_RESPONSE_LEN);
    371 
    372 	addr[0] = pw_hash_hash;
    373 	elen[0] = 16;
    374 	addr[1] = data->ap_challenge;
    375 	elen[1] = LEAP_CHALLENGE_LEN;
    376 	addr[2] = data->ap_response;
    377 	elen[2] = LEAP_RESPONSE_LEN;
    378 	addr[3] = data->peer_challenge;
    379 	elen[3] = LEAP_CHALLENGE_LEN;
    380 	addr[4] = data->peer_response;
    381 	elen[4] = LEAP_RESPONSE_LEN;
    382 	md5_vector(5, addr, elen, key);
    383 	wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
    384 	*len = LEAP_KEY_LEN;
    385 
    386 	return key;
    387 }
    388 
    389 
    390 int eap_peer_leap_register(void)
    391 {
    392 	struct eap_method *eap;
    393 	int ret;
    394 
    395 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
    396 				    EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP");
    397 	if (eap == NULL)
    398 		return -1;
    399 
    400 	eap->init = eap_leap_init;
    401 	eap->deinit = eap_leap_deinit;
    402 	eap->process = eap_leap_process;
    403 	eap->isKeyAvailable = eap_leap_isKeyAvailable;
    404 	eap->getKey = eap_leap_getKey;
    405 
    406 	ret = eap_peer_method_register(eap);
    407 	if (ret)
    408 		eap_peer_method_free(eap);
    409 	return ret;
    410 }
    411