diff options
Diffstat (limited to 'src/rsn_supp/wpa.c')
| -rw-r--r-- | src/rsn_supp/wpa.c | 167 | 
1 files changed, 123 insertions, 44 deletions
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index a9f255e37b436..3c4787925e20a 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1,6 +1,7 @@  /*   * WPA Supplicant - WPA state machine and EAPOL-Key processing   * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright(c) 2015 Intel Deutschland GmbH   *   * This software may be distributed under the terms of the BSD license.   * See README for more details. @@ -23,6 +24,9 @@  #include "peerkey.h" +static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +  /**   * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message   * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -34,11 +38,13 @@   * @msg: EAPOL-Key message   * @msg_len: Length of message   * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written + * Returns: >= 0 on success, < 0 on failure   */ -void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, -			int ver, const u8 *dest, u16 proto, -			u8 *msg, size_t msg_len, u8 *key_mic) +int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, +		       int ver, const u8 *dest, u16 proto, +		       u8 *msg, size_t msg_len, u8 *key_mic)  { +	int ret = -1;  	size_t mic_len = wpa_mic_len(sm->key_mgmt);  	if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { @@ -69,10 +75,11 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,  	wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);  	wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);  	wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); -	wpa_sm_ether_send(sm, dest, proto, msg, msg_len); +	ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);  	eapol_sm_notify_tx_eapol_key(sm->eapol);  out:  	os_free(msg); +	return ret;  } @@ -124,7 +131,7 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)  		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;  	key_info = WPA_KEY_INFO_REQUEST | ver;  	if (sm->ptk_set) -		key_info |= WPA_KEY_INFO_MIC; +		key_info |= WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;  	if (error)  		key_info |= WPA_KEY_INFO_ERROR;  	if (pairwise) @@ -206,15 +213,21 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,  #endif /* CONFIG_IEEE80211R */  	} else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {  		int res, pmk_len; -		pmk_len = PMK_LEN; -		res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); + +		if (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) +			pmk_len = PMK_LEN_SUITE_B_192; +		else +			pmk_len = PMK_LEN; +		res = eapol_sm_get_key(sm->eapol, sm->pmk, pmk_len);  		if (res) { -			/* -			 * EAP-LEAP is an exception from other EAP methods: it -			 * uses only 16-byte PMK. -			 */ -			res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); -			pmk_len = 16; +			if (pmk_len == PMK_LEN) { +				/* +				 * EAP-LEAP is an exception from other EAP +				 * methods: it uses only 16-byte PMK. +				 */ +				res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); +				pmk_len = 16; +			}  		} else {  #ifdef CONFIG_IEEE80211R  			u8 buf[2 * PMK_LEN]; @@ -236,7 +249,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,  			    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&  			    !wpa_key_mgmt_ft(sm->key_mgmt)) {  				sa = pmksa_cache_add(sm->pmksa, -						     sm->pmk, pmk_len, +						     sm->pmk, pmk_len, NULL,  						     NULL, 0,  						     src_addr, sm->own_addr,  						     sm->network_ctx, @@ -257,7 +270,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,  				 * much we can do here without knowing what  				 * exactly caused the server to misbehave.  				 */ -				wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, +				wpa_msg(sm->ctx->msg_ctx, MSG_INFO,  					"RSN: PMKID mismatch - authentication server may have derived different MSK?!");  				return -1;  			} @@ -318,7 +331,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,   * @wpa_ie: WPA/RSN IE   * @wpa_ie_len: Length of the WPA/RSN IE   * @ptk: PTK to use for keyed hash and encryption - * Returns: 0 on success, -1 on failure + * Returns: >= 0 on success, < 0 on failure   */  int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,  			       const struct wpa_eapol_key *key, @@ -351,13 +364,12 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,  		if (rsn_ie_buf == NULL)  			return -1;  		os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len); -		res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len, +		res = wpa_insert_pmkid(rsn_ie_buf, &wpa_ie_len,  				       sm->pmk_r1_name);  		if (res < 0) {  			os_free(rsn_ie_buf);  			return -1;  		} -		wpa_ie_len += res;  		if (sm->assoc_resp_ies) {  			os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies, @@ -409,10 +421,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,  	os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);  	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); -	wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, -			   rbuf, rlen, key_mic); - -	return 0; +	return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, +				  ETH_P_EAPOL, rbuf, rlen, key_mic);  } @@ -500,6 +510,7 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,  		os_memset(buf, 0, sizeof(buf));  	}  	sm->tptk_set = 1; +	sm->tk_to_set = 1;  	kde = sm->assoc_wpa_ie;  	kde_len = sm->assoc_wpa_ie_len; @@ -525,7 +536,7 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,  #endif /* CONFIG_P2P */  	if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, -				       kde, kde_len, ptk)) +				       kde, kde_len, ptk) < 0)  		goto failed;  	os_free(kde_buf); @@ -603,7 +614,12 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,  	int keylen, rsclen;  	enum wpa_alg alg;  	const u8 *key_rsc; -	u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +	if (!sm->tk_to_set) { +		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, +			"WPA: Do not re-install same PTK to the driver"); +		return 0; +	}  	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,  		"WPA: Installing PTK to the driver"); @@ -643,6 +659,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,  	/* TK is not needed anymore in supplicant */  	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); +	sm->tk_to_set = 0;  	if (sm->wpa_ptk_rekey) {  		eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); @@ -753,12 +770,43 @@ static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,  } +static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm, +					 const u8 *rsc) +{ +	int rsclen; + +	if (!sm->wpa_rsc_relaxation) +		return 0; + +	rsclen = wpa_cipher_rsc_len(sm->group_cipher); + +	/* +	 * Try to detect RSC (endian) corruption issue where the AP sends +	 * the RSC bytes in EAPOL-Key message in the wrong order, both if +	 * it's actually a 6-byte field (as it should be) and if it treats +	 * it as an 8-byte field. +	 * An AP model known to have this bug is the Sapido RB-1632. +	 */ +	if (rsclen == 6 && ((rsc[5] && !rsc[0]) || rsc[6] || rsc[7])) { +		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, +			"RSC %02x%02x%02x%02x%02x%02x%02x%02x is likely bogus, using 0", +			rsc[0], rsc[1], rsc[2], rsc[3], +			rsc[4], rsc[5], rsc[6], rsc[7]); + +		return 1; +	} + +	return 0; +} + +  static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,  				       const struct wpa_eapol_key *key,  				       const u8 *gtk, size_t gtk_len,  				       int key_info)  {  	struct wpa_gtk_data gd; +	const u8 *key_rsc;  	/*  	 * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x @@ -784,11 +832,15 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,  	os_memcpy(gd.gtk, gtk, gtk_len);  	gd.gtk_len = gtk_len; +	key_rsc = key->key_rsc; +	if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) +		key_rsc = null_rsc; +  	if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED &&  	    (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,  					       gtk_len, gtk_len,  					       &gd.key_rsc_len, &gd.alg) || -	     wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) { +	     wpa_supplicant_install_gtk(sm, &gd, key_rsc))) {  		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,  			"RSN: Failed to install GTK");  		os_memset(&gd, 0, sizeof(gd)); @@ -989,8 +1041,8 @@ static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm,  	if (sm->assoc_resp_ies) {  		pos = sm->assoc_resp_ies;  		end = pos + sm->assoc_resp_ies_len; -		while (pos + 2 < end) { -			if (pos + 2 + pos[1] > end) +		while (end - pos > 2) { +			if (2 + pos[1] > end - pos)  				break;  			switch (*pos) {  			case WLAN_EID_MOBILITY_DOMAIN: @@ -1086,7 +1138,7 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm,   * @ver: Version bits from EAPOL-Key Key Info   * @key_info: Key Info   * @ptk: PTK to use for keyed hash and encryption - * Returns: 0 on success, -1 on failure + * Returns: >= 0 on success, < 0 on failure   */  int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,  			       const struct wpa_eapol_key *key, @@ -1126,10 +1178,8 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,  		WPA_PUT_BE16(reply->key_data_length, 0);  	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); -	wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, -			   rbuf, rlen, key_mic); - -	return 0; +	return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, +				  ETH_P_EAPOL, rbuf, rlen, key_mic);  } @@ -1202,7 +1252,7 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,  #endif /* CONFIG_P2P */  	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, -				       &sm->ptk)) { +				       &sm->ptk) < 0) {  		goto failed;  	} @@ -1247,7 +1297,7 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,  	if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) {  		struct rsn_pmksa_cache_entry *sa; -		sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, +		sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,  				     sm->ptk.kck, sm->ptk.kck_len,  				     sm->bssid, sm->own_addr,  				     sm->network_ctx, sm->key_mgmt); @@ -1437,10 +1487,8 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,  		WPA_PUT_BE16(reply->key_data_length, 0);  	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); -	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, -			   ETH_P_EAPOL, rbuf, rlen, key_mic); - -	return 0; +	return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, +				  sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic);  } @@ -1453,6 +1501,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,  	u16 key_info;  	int rekey, ret;  	struct wpa_gtk_data gd; +	const u8 *key_rsc;  	if (!sm->msg_3_of_4_ok) {  		wpa_msg(sm->ctx->msg_ctx, MSG_INFO, @@ -1483,8 +1532,12 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,  	if (ret)  		goto failed; -	if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || -	    wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) +	key_rsc = key->key_rsc; +	if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) +		key_rsc = null_rsc; + +	if (wpa_supplicant_install_gtk(sm, &gd, key_rsc) || +	    wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)  		goto failed;  	os_memset(&gd, 0, sizeof(gd)); @@ -1617,14 +1670,14 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,  		}  		if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8,  			       key_data, buf)) { -			os_free(buf); +			bin_clear_free(buf, *key_data_len);  			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,  				"WPA: AES unwrap failed - "  				"could not decrypt EAPOL-Key key data");  			return -1;  		}  		os_memcpy(key_data, buf, *key_data_len); -		os_free(buf); +		bin_clear_free(buf, *key_data_len);  		WPA_PUT_BE16(key->key_data_length, *key_data_len);  	} else {  		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -2237,6 +2290,9 @@ void wpa_sm_deinit(struct wpa_sm *sm)  #ifdef CONFIG_IEEE80211R  	os_free(sm->assoc_resp_ies);  #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_TESTING_OPTIONS +	wpabuf_free(sm->test_assoc_ie); +#endif /* CONFIG_TESTING_OPTIONS */  	os_free(sm);  } @@ -2335,12 +2391,13 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)   * @sm: Pointer to WPA state machine data from wpa_sm_init()   * @pmk: The new PMK   * @pmk_len: The length of the new PMK in bytes + * @pmkid: Calculated PMKID   * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK   *   * Configure the PMK for WPA state machine.   */  void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, -		    const u8 *bssid) +		    const u8 *pmkid, const u8 *bssid)  {  	if (sm == NULL)  		return; @@ -2355,7 +2412,7 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,  #endif /* CONFIG_IEEE80211R */  	if (bssid) { -		pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0, +		pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,  				bssid, sm->own_addr,  				sm->network_ctx, sm->key_mgmt);  	} @@ -2439,6 +2496,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)  			sm->ssid_len = 0;  		sm->wpa_ptk_rekey = config->wpa_ptk_rekey;  		sm->p2p = config->p2p; +		sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation;  	} else {  		sm->network_ctx = NULL;  		sm->peerkey_enabled = 0; @@ -2449,6 +2507,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)  		sm->ssid_len = 0;  		sm->wpa_ptk_rekey = 0;  		sm->p2p = 0; +		sm->wpa_rsc_relaxation = 0;  	}  } @@ -2636,6 +2695,17 @@ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,  	if (sm == NULL)  		return -1; +#ifdef CONFIG_TESTING_OPTIONS +	if (sm->test_assoc_ie) { +		wpa_printf(MSG_DEBUG, +			   "TESTING: Replace association WPA/RSN IE"); +		if (*wpa_ie_len < wpabuf_len(sm->test_assoc_ie)) +			return -1; +		os_memcpy(wpa_ie, wpabuf_head(sm->test_assoc_ie), +			  wpabuf_len(sm->test_assoc_ie)); +		res = wpabuf_len(sm->test_assoc_ie); +	} else +#endif /* CONFIG_TESTING_OPTIONS */  	res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);  	if (res < 0)  		return -1; @@ -2975,3 +3045,12 @@ void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,  	}  	sm->ptk_set = 1;  } + + +#ifdef CONFIG_TESTING_OPTIONS +void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf) +{ +	wpabuf_free(sm->test_assoc_ie); +	sm->test_assoc_ie = buf; +} +#endif /* CONFIG_TESTING_OPTIONS */  | 
