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 && challenge_response(challenge, password, rpos)) ||
    119 	    (!pwhash &&
    120 	     nt_challenge_response(challenge, password, password_len, rpos))) {
    121 		wpa_printf(MSG_DEBUG, "EAP-LEAP: Failed to derive response");
    122 		ret->ignore = TRUE;
    123 		wpabuf_free(resp);
    124 		return NULL;
    125 	}
    126 	os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
    127 	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
    128 		    rpos, LEAP_RESPONSE_LEN);
    129 	wpabuf_put_data(resp, identity, identity_len);
    130 
    131 	data->state = LEAP_WAIT_SUCCESS;
    132 
    133 	return resp;
    134 }
    135 
    136 
    137 static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
    138 						struct eap_method_ret *ret,
    139 						const struct wpabuf *reqData)
    140 {
    141 	struct eap_leap_data *data = priv;
    142 	struct wpabuf *resp;
    143 	u8 *pos;
    144 	const u8 *identity;
    145 	size_t identity_len;
    146 
    147 	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
    148 
    149 	identity = eap_get_config_identity(sm, &identity_len);
    150 	if (identity == NULL)
    151 		return NULL;
    152 
    153 	if (data->state != LEAP_WAIT_SUCCESS) {
    154 		wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
    155 			   "unexpected state (%d) - ignored", data->state);
    156 		ret->ignore = TRUE;
    157 		return NULL;
    158 	}
    159 
    160 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
    161 			     3 + LEAP_CHALLENGE_LEN + identity_len,
    162 			     EAP_CODE_REQUEST, eap_get_id(reqData));
    163 	if (resp == NULL)
    164 		return NULL;
    165 	wpabuf_put_u8(resp, LEAP_VERSION);
    166 	wpabuf_put_u8(resp, 0); /* unused */
    167 	wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
    168 	pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
    169 	if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
    170 		wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
    171 			   "for challenge");
    172 		wpabuf_free(resp);
    173 		ret->ignore = TRUE;
    174 		return NULL;
    175 	}
    176 	os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
    177 	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
    178 		    LEAP_CHALLENGE_LEN);
    179 	wpabuf_put_data(resp, identity, identity_len);
    180 
    181 	data->state = LEAP_WAIT_RESPONSE;
    182 
    183 	return resp;
    184 }
    185 
    186 
    187 static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
    188 						 struct eap_method_ret *ret,
    189 						 const struct wpabuf *reqData)
    190 {
    191 	struct eap_leap_data *data = priv;
    192 	const u8 *pos, *password;
    193 	u8 response_len, pw_hash[16], pw_hash_hash[16],
    194 		expected[LEAP_RESPONSE_LEN];
    195 	size_t password_len, len;
    196 	int pwhash;
    197 
    198 	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
    199 
    200 	password = eap_get_config_password2(sm, &password_len, &pwhash);
    201 	if (password == NULL)
    202 		return NULL;
    203 
    204 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
    205 	if (pos == NULL || len < 3) {
    206 		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
    207 		ret->ignore = TRUE;
    208 		return NULL;
    209 	}
    210 
    211 	if (*pos != LEAP_VERSION) {
    212 		wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
    213 			   "%d", *pos);
    214 		ret->ignore = TRUE;
    215 		return NULL;
    216 	}
    217 	pos++;
    218 
    219 	pos++; /* skip unused byte */
    220 
    221 	response_len = *pos++;
    222 	if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) {
    223 		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
    224 			   "(response_len=%d reqDataLen=%lu)",
    225 			   response_len, (unsigned long) wpabuf_len(reqData));
    226 		ret->ignore = TRUE;
    227 		return NULL;
    228 	}
    229 
    230 	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
    231 		    pos, LEAP_RESPONSE_LEN);
    232 	os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
    233 
    234 	if (pwhash) {
    235 		if (hash_nt_password_hash(password, pw_hash_hash)) {
    236 			ret->ignore = TRUE;
    237 			return NULL;
    238 		}
    239 	} else {
    240 		if (nt_password_hash(password, password_len, pw_hash) ||
    241 		    hash_nt_password_hash(pw_hash, pw_hash_hash)) {
    242 			ret->ignore = TRUE;
    243 			return NULL;
    244 		}
    245 	}
    246 	if (challenge_response(data->ap_challenge, pw_hash_hash, expected)) {
    247 		ret->ignore = TRUE;
    248 		return NULL;
    249 	}
    250 
    251 	ret->methodState = METHOD_DONE;
    252 	ret->allowNotifications = FALSE;
    253 
    254 	if (os_memcmp_const(pos, expected, LEAP_RESPONSE_LEN) != 0) {
    255 		wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
    256 			   "response - authentication failed");
    257 		wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
    258 			    expected, LEAP_RESPONSE_LEN);
    259 		ret->decision = DECISION_FAIL;
    260 		return NULL;
    261 	}
    262 
    263 	ret->decision = DECISION_UNCOND_SUCC;
    264 
    265 	/* LEAP is somewhat odd method since it sends EAP-Success in the middle
    266 	 * of the authentication. Use special variable to transit EAP state
    267 	 * machine to SUCCESS state. */
    268 	sm->leap_done = TRUE;
    269 	data->state = LEAP_DONE;
    270 
    271 	/* No more authentication messages expected; AP will send EAPOL-Key
    272 	 * frames if encryption is enabled. */
    273 	return NULL;
    274 }
    275 
    276 
    277 static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv,
    278 					struct eap_method_ret *ret,
    279 					const struct wpabuf *reqData)
    280 {
    281 	const struct eap_hdr *eap;
    282 	size_t password_len;
    283 	const u8 *password;
    284 
    285 	password = eap_get_config_password(sm, &password_len);
    286 	if (password == NULL) {
    287 		wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
    288 		eap_sm_request_password(sm);
    289 		ret->ignore = TRUE;
    290 		return NULL;
    291 	}
    292 
    293 	/*
    294 	 * LEAP needs to be able to handle EAP-Success frame which does not
    295 	 * include Type field. Consequently, eap_hdr_validate() cannot be used
    296 	 * here. This validation will be done separately for EAP-Request and
    297 	 * EAP-Response frames.
    298 	 */
    299 	eap = wpabuf_head(reqData);
    300 	if (wpabuf_len(reqData) < sizeof(*eap) ||
    301 	    be_to_host16(eap->length) > wpabuf_len(reqData)) {
    302 		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
    303 		ret->ignore = TRUE;
    304 		return NULL;
    305 	}
    306 
    307 	ret->ignore = FALSE;
    308 	ret->allowNotifications = TRUE;
    309 	ret->methodState = METHOD_MAY_CONT;
    310 	ret->decision = DECISION_FAIL;
    311 
    312 	sm->leap_done = FALSE;
    313 
    314 	switch (eap->code) {
    315 	case EAP_CODE_REQUEST:
    316 		return eap_leap_process_request(sm, priv, ret, reqData);
    317 	case EAP_CODE_SUCCESS:
    318 		return eap_leap_process_success(sm, priv, ret, reqData);
    319 	case EAP_CODE_RESPONSE:
    320 		return eap_leap_process_response(sm, priv, ret, reqData);
    321 	default:
    322 		wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
    323 			   "ignored", eap->code);
    324 		ret->ignore = TRUE;
    325 		return NULL;
    326 	}
    327 }
    328 
    329 
    330 static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
    331 {
    332 	struct eap_leap_data *data = priv;
    333 	return data->state == LEAP_DONE;
    334 }
    335 
    336 
    337 static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
    338 {
    339 	struct eap_leap_data *data = priv;
    340 	u8 *key, pw_hash_hash[16], pw_hash[16];
    341 	const u8 *addr[5], *password;
    342 	size_t elen[5], password_len;
    343 	int pwhash;
    344 
    345 	if (data->state != LEAP_DONE)
    346 		return NULL;
    347 
    348 	password = eap_get_config_password2(sm, &password_len, &pwhash);
    349 	if (password == NULL)
    350 		return NULL;
    351 
    352 	key = os_malloc(LEAP_KEY_LEN);
    353 	if (key == NULL)
    354 		return NULL;
    355 
    356 	if (pwhash) {
    357 		if (hash_nt_password_hash(password, pw_hash_hash)) {
    358 			os_free(key);
    359 			return NULL;
    360 		}
    361 	} else {
    362 		if (nt_password_hash(password, password_len, pw_hash) ||
    363 		    hash_nt_password_hash(pw_hash, pw_hash_hash)) {
    364 			os_free(key);
    365 			return NULL;
    366 		}
    367 	}
    368 	wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
    369 			pw_hash_hash, 16);
    370 	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
    371 		    data->peer_challenge, LEAP_CHALLENGE_LEN);
    372 	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
    373 		    data->peer_response, LEAP_RESPONSE_LEN);
    374 	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
    375 		    data->ap_challenge, LEAP_CHALLENGE_LEN);
    376 	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
    377 		    data->ap_response, LEAP_RESPONSE_LEN);
    378 
    379 	addr[0] = pw_hash_hash;
    380 	elen[0] = 16;
    381 	addr[1] = data->ap_challenge;
    382 	elen[1] = LEAP_CHALLENGE_LEN;
    383 	addr[2] = data->ap_response;
    384 	elen[2] = LEAP_RESPONSE_LEN;
    385 	addr[3] = data->peer_challenge;
    386 	elen[3] = LEAP_CHALLENGE_LEN;
    387 	addr[4] = data->peer_response;
    388 	elen[4] = LEAP_RESPONSE_LEN;
    389 	md5_vector(5, addr, elen, key);
    390 	wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
    391 	*len = LEAP_KEY_LEN;
    392 
    393 	os_memset(pw_hash, 0, sizeof(pw_hash));
    394 	os_memset(pw_hash_hash, 0, sizeof(pw_hash_hash));
    395 
    396 	return key;
    397 }
    398 
    399 
    400 int eap_peer_leap_register(void)
    401 {
    402 	struct eap_method *eap;
    403 
    404 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
    405 				    EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP");
    406 	if (eap == NULL)
    407 		return -1;
    408 
    409 	eap->init = eap_leap_init;
    410 	eap->deinit = eap_leap_deinit;
    411 	eap->process = eap_leap_process;
    412 	eap->isKeyAvailable = eap_leap_isKeyAvailable;
    413 	eap->getKey = eap_leap_getKey;
    414 
    415 	return eap_peer_method_register(eap);
    416 }
    417