diff options
Diffstat (limited to 'net/mac80211/wpa.c')
| -rw-r--r-- | net/mac80211/wpa.c | 487 | 
1 files changed, 357 insertions, 130 deletions
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index bee230d8fd1..9b3dcc20114 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -15,6 +15,7 @@  #include <linux/gfp.h>  #include <asm/unaligned.h>  #include <net/mac80211.h> +#include <crypto/aes.h>  #include "ieee80211_i.h"  #include "michael.h" @@ -26,13 +27,12 @@  ieee80211_tx_result  ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)  { -	u8 *data, *key, *mic, key_offset; +	u8 *data, *key, *mic;  	size_t data_len;  	unsigned int hdrlen;  	struct ieee80211_hdr *hdr;  	struct sk_buff *skb = tx->skb;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -	int authenticator;  	int tail;  	hdr = (struct ieee80211_hdr *)skb->data; @@ -47,8 +47,14 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)  	data = skb->data + hdrlen;  	data_len = skb->len - hdrlen; +	if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) { +		/* Need to use software crypto for the test */ +		info->control.hw_key = NULL; +	} +  	if (info->control.hw_key && -	    !(tx->flags & IEEE80211_TX_FRAGMENTED) && +	    (info->flags & IEEE80211_TX_CTL_DONTFRAG || +	     tx->local->ops->set_frag_threshold) &&  	    !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) {  		/* hwaccel - with no need for SW-generated MMIC */  		return TX_CONTINUE; @@ -56,23 +62,17 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)  	tail = MICHAEL_MIC_LEN;  	if (!info->control.hw_key) -		tail += TKIP_ICV_LEN; +		tail += IEEE80211_TKIP_ICV_LEN;  	if (WARN_ON(skb_tailroom(skb) < tail || -		    skb_headroom(skb) < TKIP_IV_LEN)) +		    skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))  		return TX_DROP; -#if 0 -	authenticator = fc & IEEE80211_FCTL_FROMDS; /* FIX */ -#else -	authenticator = 1; -#endif -	key_offset = authenticator ? -		NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY : -		NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; -	key = &tx->key->conf.key[key_offset]; +	key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY];  	mic = skb_put(skb, MICHAEL_MIC_LEN);  	michael_mic(key, hdr, data, data_len, mic); +	if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) +		mic[0]++;  	return TX_CONTINUE;  } @@ -81,59 +81,98 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)  ieee80211_rx_result  ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)  { -	u8 *data, *key = NULL, key_offset; +	u8 *data, *key = NULL;  	size_t data_len;  	unsigned int hdrlen;  	u8 mic[MICHAEL_MIC_LEN];  	struct sk_buff *skb = rx->skb;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; -	int authenticator = 1, wpa_test = 0; -	/* No way to verify the MIC if the hardware stripped it */ -	if (status->flag & RX_FLAG_MMIC_STRIPPED) +	/* +	 * it makes no sense to check for MIC errors on anything other +	 * than data frames. +	 */ +	if (!ieee80211_is_data_present(hdr->frame_control))  		return RX_CONTINUE; +	/* +	 * No way to verify the MIC if the hardware stripped it or +	 * the IV with the key index. In this case we have solely rely +	 * on the driver to set RX_FLAG_MMIC_ERROR in the event of a +	 * MIC failure report. +	 */ +	if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) { +		if (status->flag & RX_FLAG_MMIC_ERROR) +			goto mic_fail_no_key; + +		if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key && +		    rx->key->conf.cipher == WLAN_CIPHER_SUITE_TKIP) +			goto update_iv; + +		return RX_CONTINUE; +	} + +	/* +	 * Some hardware seems to generate Michael MIC failure reports; even +	 * though, the frame was not encrypted with TKIP and therefore has no +	 * MIC. Ignore the flag them to avoid triggering countermeasures. +	 */  	if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP || -	    !ieee80211_has_protected(hdr->frame_control) || -	    !ieee80211_is_data_present(hdr->frame_control)) +	    !(status->flag & RX_FLAG_DECRYPTED))  		return RX_CONTINUE; +	if (rx->sdata->vif.type == NL80211_IFTYPE_AP && rx->key->conf.keyidx) { +		/* +		 * APs with pairwise keys should never receive Michael MIC +		 * errors for non-zero keyidx because these are reserved for +		 * group keys and only the AP is sending real multicast +		 * frames in the BSS. +		 */ +		return RX_DROP_UNUSABLE; +	} + +	if (status->flag & RX_FLAG_MMIC_ERROR) +		goto mic_fail; +  	hdrlen = ieee80211_hdrlen(hdr->frame_control);  	if (skb->len < hdrlen + MICHAEL_MIC_LEN)  		return RX_DROP_UNUSABLE; +	if (skb_linearize(rx->skb)) +		return RX_DROP_UNUSABLE; +	hdr = (void *)skb->data; +  	data = skb->data + hdrlen;  	data_len = skb->len - hdrlen - MICHAEL_MIC_LEN; - -#if 0 -	authenticator = fc & IEEE80211_FCTL_TODS; /* FIX */ -#else -	authenticator = 1; -#endif -	key_offset = authenticator ? -		NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY : -		NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; -	key = &rx->key->conf.key[key_offset]; +	key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY];  	michael_mic(key, hdr, data, data_len, mic); -	if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0 || wpa_test) { -		if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) -			return RX_DROP_UNUSABLE; - -		mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx, -						(void *) skb->data, NULL, -						GFP_ATOMIC); -		return RX_DROP_UNUSABLE; -	} +	if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0) +		goto mic_fail;  	/* remove Michael MIC from payload */  	skb_trim(skb, skb->len - MICHAEL_MIC_LEN); +update_iv:  	/* update IV in key information to be able to detect replays */ -	rx->key->u.tkip.rx[rx->queue].iv32 = rx->tkip_iv32; -	rx->key->u.tkip.rx[rx->queue].iv16 = rx->tkip_iv16; +	rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip_iv32; +	rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip_iv16;  	return RX_CONTINUE; + +mic_fail: +	rx->key->u.tkip.mic_failures++; + +mic_fail_no_key: +	/* +	 * In some cases the key can be unset - e.g. a multicast packet, in +	 * a driver that supports HW encryption. Send up the key idx only if +	 * the key is set. +	 */ +	mac80211_ev_michael_mic_failure(rx->sdata, +					rx->key ? rx->key->conf.keyidx : -1, +					(void *) skb->data, NULL, GFP_ATOMIC); +	return RX_DROP_UNUSABLE;  } @@ -147,7 +186,8 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	u8 *pos;  	if (info->control.hw_key && -	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {  		/* hwaccel - with no need for software-generated IV */  		return 0;  	} @@ -158,47 +198,54 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	if (info->control.hw_key)  		tail = 0;  	else -		tail = TKIP_ICV_LEN; +		tail = IEEE80211_TKIP_ICV_LEN;  	if (WARN_ON(skb_tailroom(skb) < tail || -		    skb_headroom(skb) < TKIP_IV_LEN)) +		    skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))  		return -1; -	pos = skb_push(skb, TKIP_IV_LEN); -	memmove(pos, pos + TKIP_IV_LEN, hdrlen); +	pos = skb_push(skb, IEEE80211_TKIP_IV_LEN); +	memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen); +	skb_set_network_header(skb, skb_network_offset(skb) + +				    IEEE80211_TKIP_IV_LEN);  	pos += hdrlen; +	/* the HW only needs room for the IV, but not the actual IV */ +	if (info->control.hw_key && +	    (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) +		return 0; +  	/* Increase IV for the frame */ +	spin_lock(&key->u.tkip.txlock);  	key->u.tkip.tx.iv16++;  	if (key->u.tkip.tx.iv16 == 0)  		key->u.tkip.tx.iv32++; - -	pos = ieee80211_tkip_add_iv(pos, key, key->u.tkip.tx.iv16); +	pos = ieee80211_tkip_add_iv(pos, key); +	spin_unlock(&key->u.tkip.txlock);  	/* hwaccel - with software IV */  	if (info->control.hw_key)  		return 0;  	/* Add room for ICV */ -	skb_put(skb, TKIP_ICV_LEN); +	skb_put(skb, IEEE80211_TKIP_ICV_LEN); -	hdr = (struct ieee80211_hdr *) skb->data;  	return ieee80211_tkip_encrypt_data(tx->local->wep_tx_tfm, -					   key, pos, len, hdr->addr2); +					   key, skb, pos, len);  }  ieee80211_tx_result  ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)  { -	struct sk_buff *skb = tx->skb; +	struct sk_buff *skb;  	ieee80211_tx_set_protected(tx); -	do { +	skb_queue_walk(&tx->skbs, skb) {  		if (tkip_encrypt_skb(tx, skb) < 0)  			return TX_DROP; -	} while ((skb = skb->next)); +	}  	return TX_CONTINUE;  } @@ -208,7 +255,7 @@ ieee80211_rx_result  ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; -	int hdrlen, res, hwaccel = 0, wpa_test = 0; +	int hdrlen, res, hwaccel = 0;  	struct ieee80211_key *key = rx->key;  	struct sk_buff *skb = rx->skb;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); @@ -221,6 +268,11 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)  	if (!rx->sta || skb->len - hdrlen < 12)  		return RX_DROP_UNUSABLE; +	/* it may be possible to optimize this a bit more */ +	if (skb_linearize(rx->skb)) +		return RX_DROP_UNUSABLE; +	hdr = (void *)skb->data; +  	/*  	 * Let TKIP code verify IV, but skip decryption.  	 * In the case where hardware checks the IV as well, @@ -232,37 +284,32 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)  	res = ieee80211_tkip_decrypt_data(rx->local->wep_rx_tfm,  					  key, skb->data + hdrlen,  					  skb->len - hdrlen, rx->sta->sta.addr, -					  hdr->addr1, hwaccel, rx->queue, +					  hdr->addr1, hwaccel, rx->security_idx,  					  &rx->tkip_iv32,  					  &rx->tkip_iv16); -	if (res != TKIP_DECRYPT_OK || wpa_test) +	if (res != TKIP_DECRYPT_OK)  		return RX_DROP_UNUSABLE;  	/* Trim ICV */ -	skb_trim(skb, skb->len - TKIP_ICV_LEN); +	skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);  	/* Remove IV */ -	memmove(skb->data + TKIP_IV_LEN, skb->data, hdrlen); -	skb_pull(skb, TKIP_IV_LEN); +	memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen); +	skb_pull(skb, IEEE80211_TKIP_IV_LEN);  	return RX_CONTINUE;  } -static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, -				int encrypted) +static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad)  {  	__le16 mask_fc;  	int a4_included, mgmt;  	u8 qos_tid; -	u8 *b_0, *aad; -	u16 data_len, len_a; +	u16 len_a;  	unsigned int hdrlen;  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; -	b_0 = scratch + 3 * AES_BLOCK_LEN; -	aad = scratch + 4 * AES_BLOCK_LEN; -  	/*  	 * Mask FC: zero subtype b4 b5 b6 (if not mgmt)  	 * Retry, PwrMgt, MoreData; set Protected @@ -284,20 +331,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,  	else  		qos_tid = 0; -	data_len = skb->len - hdrlen - CCMP_HDR_LEN; -	if (encrypted) -		data_len -= CCMP_MIC_LEN; +	/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC +	 * mode authentication are not allowed to collide, yet both are derived +	 * from this vector b_0. We only set L := 1 here to indicate that the +	 * data size can be represented in (L+1) bytes. The CCM layer will take +	 * care of storing the data length in the top (L+1) bytes and setting +	 * and clearing the other bits as is required to derive the two IVs. +	 */ +	b_0[0] = 0x1; -	/* First block, b_0 */ -	b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */  	/* Nonce: Nonce Flags | A2 | PN  	 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)  	 */  	b_0[1] = qos_tid | (mgmt << 4);  	memcpy(&b_0[2], hdr->addr2, ETH_ALEN); -	memcpy(&b_0[8], pn, CCMP_PN_LEN); -	/* l(m) */ -	put_unaligned_be16(data_len, &b_0[14]); +	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);  	/* AAD (extra authenticate-only data) / masked 802.11 header  	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ @@ -350,11 +398,18 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	struct ieee80211_key *key = tx->key;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  	int hdrlen, len, tail; -	u8 *pos, *pn; -	int i; +	u8 *pos; +	u8 pn[6]; +	u64 pn64; +	u8 aad[2 * AES_BLOCK_SIZE]; +	u8 b_0[AES_BLOCK_SIZE];  	if (info->control.hw_key && -	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) && +	    !((info->control.hw_key->flags & +	       IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) && +	      ieee80211_is_mgmt(hdr->frame_control))) {  		/*  		 * hwaccel has no need for preallocated room for CCMP  		 * header or MIC fields @@ -368,25 +423,33 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	if (info->control.hw_key)  		tail = 0;  	else -		tail = CCMP_MIC_LEN; +		tail = IEEE80211_CCMP_MIC_LEN;  	if (WARN_ON(skb_tailroom(skb) < tail || -		    skb_headroom(skb) < CCMP_HDR_LEN)) +		    skb_headroom(skb) < IEEE80211_CCMP_HDR_LEN))  		return -1; -	pos = skb_push(skb, CCMP_HDR_LEN); -	memmove(pos, pos + CCMP_HDR_LEN, hdrlen); +	pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN); +	memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen); +	skb_set_network_header(skb, skb_network_offset(skb) + +				    IEEE80211_CCMP_HDR_LEN); + +	/* the HW only needs room for the IV, but not the actual IV */ +	if (info->control.hw_key && +	    (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) +		return 0; +  	hdr = (struct ieee80211_hdr *) pos;  	pos += hdrlen; -	/* PN = PN + 1 */ -	pn = key->u.ccmp.tx_pn; +	pn64 = atomic64_inc_return(&key->u.ccmp.tx_pn); -	for (i = CCMP_PN_LEN - 1; i >= 0; i--) { -		pn[i]++; -		if (pn[i]) -			break; -	} +	pn[5] = pn64; +	pn[4] = pn64 >> 8; +	pn[3] = pn64 >> 16; +	pn[2] = pn64 >> 24; +	pn[1] = pn64 >> 32; +	pn[0] = pn64 >> 40;  	ccmp_pn2hdr(pos, pn, key->conf.keyidx); @@ -394,10 +457,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	if (info->control.hw_key)  		return 0; -	pos += CCMP_HDR_LEN; -	ccmp_special_blocks(skb, pn, key->u.ccmp.tx_crypto_buf, 0); -	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, key->u.ccmp.tx_crypto_buf, pos, len, -				  pos, skb_put(skb, CCMP_MIC_LEN)); +	pos += IEEE80211_CCMP_HDR_LEN; +	ccmp_special_blocks(skb, pn, b_0, aad); +	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, +				  skb_put(skb, IEEE80211_CCMP_MIC_LEN));  	return 0;  } @@ -406,14 +469,14 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  ieee80211_tx_result  ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)  { -	struct sk_buff *skb = tx->skb; +	struct sk_buff *skb;  	ieee80211_tx_set_protected(tx); -	do { +	skb_queue_walk(&tx->skbs, skb) {  		if (ccmp_encrypt_skb(tx, skb) < 0)  			return TX_DROP; -	} while ((skb = skb->next)); +	}  	return TX_CONTINUE;  } @@ -427,66 +490,192 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)  	struct ieee80211_key *key = rx->key;  	struct sk_buff *skb = rx->skb;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); -	u8 pn[CCMP_PN_LEN]; +	u8 pn[IEEE80211_CCMP_PN_LEN];  	int data_len;  	int queue;  	hdrlen = ieee80211_hdrlen(hdr->frame_control);  	if (!ieee80211_is_data(hdr->frame_control) && -	    !ieee80211_is_robust_mgmt_frame(hdr)) +	    !ieee80211_is_robust_mgmt_frame(skb))  		return RX_CONTINUE; -	data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN; +	data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - +		   IEEE80211_CCMP_MIC_LEN;  	if (!rx->sta || data_len < 0)  		return RX_DROP_UNUSABLE; +	if (status->flag & RX_FLAG_DECRYPTED) { +		if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN)) +			return RX_DROP_UNUSABLE; +	} else { +		if (skb_linearize(rx->skb)) +			return RX_DROP_UNUSABLE; +	} +  	ccmp_hdr2pn(pn, skb->data + hdrlen); -	queue = ieee80211_is_mgmt(hdr->frame_control) ? -		NUM_RX_DATA_QUEUES : rx->queue; +	queue = rx->security_idx; -	if (memcmp(pn, key->u.ccmp.rx_pn[queue], CCMP_PN_LEN) <= 0) { +	if (memcmp(pn, key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN) <= 0) {  		key->u.ccmp.replays++;  		return RX_DROP_UNUSABLE;  	}  	if (!(status->flag & RX_FLAG_DECRYPTED)) { +		u8 aad[2 * AES_BLOCK_SIZE]; +		u8 b_0[AES_BLOCK_SIZE];  		/* hardware didn't decrypt/verify MIC */ -		ccmp_special_blocks(skb, pn, key->u.ccmp.rx_crypto_buf, 1); +		ccmp_special_blocks(skb, pn, b_0, aad);  		if (ieee80211_aes_ccm_decrypt( -			    key->u.ccmp.tfm, key->u.ccmp.rx_crypto_buf, -			    skb->data + hdrlen + CCMP_HDR_LEN, data_len, -			    skb->data + skb->len - CCMP_MIC_LEN, -			    skb->data + hdrlen + CCMP_HDR_LEN)) +			    key->u.ccmp.tfm, b_0, aad, +			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, +			    data_len, +			    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))  			return RX_DROP_UNUSABLE;  	} -	memcpy(key->u.ccmp.rx_pn[queue], pn, CCMP_PN_LEN); +	memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);  	/* Remove CCMP header and MIC */ -	skb_trim(skb, skb->len - CCMP_MIC_LEN); -	memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen); -	skb_pull(skb, CCMP_HDR_LEN); +	if (pskb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN)) +		return RX_DROP_UNUSABLE; +	memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen); +	skb_pull(skb, IEEE80211_CCMP_HDR_LEN);  	return RX_CONTINUE;  } +static ieee80211_tx_result +ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx, +			    struct sk_buff *skb) +{ +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	struct ieee80211_key *key = tx->key; +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme; +	int hdrlen; +	u8 *pos; + +	if (info->control.hw_key && +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { +		/* hwaccel has no need for preallocated head room */ +		return TX_CONTINUE; +	} + +	if (unlikely(skb_headroom(skb) < cs->hdr_len && +		     pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC))) +		return TX_DROP; + +	hdrlen = ieee80211_hdrlen(hdr->frame_control); + +	pos = skb_push(skb, cs->hdr_len); +	memmove(pos, pos + cs->hdr_len, hdrlen); +	skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len); + +	return TX_CONTINUE; +} + +static inline int ieee80211_crypto_cs_pn_compare(u8 *pn1, u8 *pn2, int len) +{ +	int i; + +	/* pn is little endian */ +	for (i = len - 1; i >= 0; i--) { +		if (pn1[i] < pn2[i]) +			return -1; +		else if (pn1[i] > pn2[i]) +			return 1; +	} + +	return 0; +} + +static ieee80211_rx_result +ieee80211_crypto_cs_decrypt(struct ieee80211_rx_data *rx) +{ +	struct ieee80211_key *key = rx->key; +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; +	const struct ieee80211_cipher_scheme *cs = NULL; +	int hdrlen = ieee80211_hdrlen(hdr->frame_control); +	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); +	int data_len; +	u8 *rx_pn; +	u8 *skb_pn; +	u8 qos_tid; + +	if (!rx->sta || !rx->sta->cipher_scheme || +	    !(status->flag & RX_FLAG_DECRYPTED)) +		return RX_DROP_UNUSABLE; + +	if (!ieee80211_is_data(hdr->frame_control)) +		return RX_CONTINUE; + +	cs = rx->sta->cipher_scheme; + +	data_len = rx->skb->len - hdrlen - cs->hdr_len; + +	if (data_len < 0) +		return RX_DROP_UNUSABLE; + +	if (ieee80211_is_data_qos(hdr->frame_control)) +		qos_tid = *ieee80211_get_qos_ctl(hdr) & +				IEEE80211_QOS_CTL_TID_MASK; +	else +		qos_tid = 0; + +	if (skb_linearize(rx->skb)) +		return RX_DROP_UNUSABLE; + +	hdr = (struct ieee80211_hdr *)rx->skb->data; + +	rx_pn = key->u.gen.rx_pn[qos_tid]; +	skb_pn = rx->skb->data + hdrlen + cs->pn_off; + +	if (ieee80211_crypto_cs_pn_compare(skb_pn, rx_pn, cs->pn_len) <= 0) +		return RX_DROP_UNUSABLE; + +	memcpy(rx_pn, skb_pn, cs->pn_len); + +	/* remove security header and MIC */ +	if (pskb_trim(rx->skb, rx->skb->len - cs->mic_len)) +		return RX_DROP_UNUSABLE; + +	memmove(rx->skb->data + cs->hdr_len, rx->skb->data, hdrlen); +	skb_pull(rx->skb, cs->hdr_len); + +	return RX_CONTINUE; +}  static void bip_aad(struct sk_buff *skb, u8 *aad)  { +	__le16 mask_fc; +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +  	/* BIP AAD: FC(masked) || A1 || A2 || A3 */  	/* FC type/subtype */ -	aad[0] = skb->data[0];  	/* Mask FC Retry, PwrMgt, MoreData flags to zero */ -	aad[1] = skb->data[1] & ~(BIT(4) | BIT(5) | BIT(6)); +	mask_fc = hdr->frame_control; +	mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY | IEEE80211_FCTL_PM | +				IEEE80211_FCTL_MOREDATA); +	put_unaligned(mask_fc, (__le16 *) &aad[0]);  	/* A1 || A2 || A3 */ -	memcpy(aad + 2, skb->data + 4, 3 * ETH_ALEN); +	memcpy(aad + 2, &hdr->addr1, 3 * ETH_ALEN);  } +static inline void bip_ipn_set64(u8 *d, u64 pn) +{ +	*d++ = pn; +	*d++ = pn >> 8; +	*d++ = pn >> 16; +	*d++ = pn >> 24; +	*d++ = pn >> 32; +	*d = pn >> 40; +} +  static inline void bip_ipn_swap(u8 *d, const u8 *s)  {  	*d++ = s[5]; @@ -501,15 +690,22 @@ static inline void bip_ipn_swap(u8 *d, const u8 *s)  ieee80211_tx_result  ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)  { -	struct sk_buff *skb = tx->skb; -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct sk_buff *skb; +	struct ieee80211_tx_info *info;  	struct ieee80211_key *key = tx->key;  	struct ieee80211_mmie *mmie; -	u8 *pn, aad[20]; -	int i; +	u8 aad[20]; +	u64 pn64; + +	if (WARN_ON(skb_queue_len(&tx->skbs) != 1)) +		return TX_DROP; + +	skb = skb_peek(&tx->skbs); + +	info = IEEE80211_SKB_CB(skb);  	if (info->control.hw_key) -		return 0; +		return TX_CONTINUE;  	if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))  		return TX_DROP; @@ -520,22 +716,17 @@ ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)  	mmie->key_id = cpu_to_le16(key->conf.keyidx);  	/* PN = PN + 1 */ -	pn = key->u.aes_cmac.tx_pn; +	pn64 = atomic64_inc_return(&key->u.aes_cmac.tx_pn); -	for (i = sizeof(key->u.aes_cmac.tx_pn) - 1; i >= 0; i--) { -		pn[i]++; -		if (pn[i]) -			break; -	} -	bip_ipn_swap(mmie->sequence_number, pn); +	bip_ipn_set64(mmie->sequence_number, pn64);  	bip_aad(skb, aad);  	/*  	 * MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64)  	 */ -	ieee80211_aes_cmac(key->u.aes_cmac.tfm, key->u.aes_cmac.tx_crypto_buf, -			   aad, skb->data + 24, skb->len - 24, mmie->mic); +	ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad, +			   skb->data + 24, skb->len - 24, mmie->mic);  	return TX_CONTINUE;  } @@ -554,6 +745,8 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)  	if (!ieee80211_is_mgmt(hdr->frame_control))  		return RX_CONTINUE; +	/* management frames are already linear */ +  	if (skb->len < 24 + sizeof(*mmie))  		return RX_DROP_UNUSABLE; @@ -573,8 +766,7 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)  	if (!(status->flag & RX_FLAG_DECRYPTED)) {  		/* hardware didn't decrypt/verify MIC */  		bip_aad(skb, aad); -		ieee80211_aes_cmac(key->u.aes_cmac.tfm, -				   key->u.aes_cmac.rx_crypto_buf, aad, +		ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad,  				   skb->data + 24, skb->len - 24, mic);  		if (memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) {  			key->u.aes_cmac.icverrors++; @@ -589,3 +781,38 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)  	return RX_CONTINUE;  } + +ieee80211_tx_result +ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx) +{ +	struct sk_buff *skb; +	struct ieee80211_tx_info *info = NULL; +	ieee80211_tx_result res; + +	skb_queue_walk(&tx->skbs, skb) { +		info  = IEEE80211_SKB_CB(skb); + +		/* handle hw-only algorithm */ +		if (!info->control.hw_key) +			return TX_DROP; + +		if (tx->key->sta->cipher_scheme) { +			res = ieee80211_crypto_cs_encrypt(tx, skb); +			if (res != TX_CONTINUE) +				return res; +		} +	} + +	ieee80211_tx_set_protected(tx); + +	return TX_CONTINUE; +} + +ieee80211_rx_result +ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx) +{ +	if (rx->sta->cipher_scheme) +		return ieee80211_crypto_cs_decrypt(rx); + +	return RX_DROP_UNUSABLE; +}  | 
