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