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