[PATCH] ieee80211: Fixed a kernel oops on module unload
tree 367069f24fc38b4aa910e86ff40094d2078d8aa7
parent a33a198201
author James Ketrenos <jketreno@linux.intel.com> 1124430800 -0500
committer James Ketrenos <jketreno@linux.intel.com> 1127310571 -0500
Fixed a kernel oops on module unload by adding spin lock protection to
ieee80211's crypt handlers (thanks to Zhu Yi)
Modified scan result logic to report WPA and RSN IEs if set (vs.being
based on wpa_enabled)
Added ieee80211_device as the first parameter to the crypt init()
method. TKIP modified to use that structure for determining whether to
countermeasures are active.
Signed-off-by: James Ketrenos <jketreno@linux.intel.com>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
This commit is contained in:
parent
4ca5253d57
commit
20d64713ae
9 changed files with 40 additions and 38 deletions
|
@ -434,6 +434,7 @@ struct ieee80211_device;
|
||||||
#define SEC_KEY_2 (1<<1)
|
#define SEC_KEY_2 (1<<1)
|
||||||
#define SEC_KEY_3 (1<<2)
|
#define SEC_KEY_3 (1<<2)
|
||||||
#define SEC_KEY_4 (1<<3)
|
#define SEC_KEY_4 (1<<3)
|
||||||
|
#define SEC_KEY_MASK (SEC_KEY_1 | SEC_KEY_2 | SEC_KEY_3 | SEC_KEY_4)
|
||||||
#define SEC_ACTIVE_KEY (1<<4)
|
#define SEC_ACTIVE_KEY (1<<4)
|
||||||
#define SEC_AUTH_MODE (1<<5)
|
#define SEC_AUTH_MODE (1<<5)
|
||||||
#define SEC_UNICAST_GROUP (1<<6)
|
#define SEC_UNICAST_GROUP (1<<6)
|
||||||
|
|
|
@ -31,7 +31,7 @@ struct ieee80211_crypto_ops {
|
||||||
/* init new crypto context (e.g., allocate private data space,
|
/* init new crypto context (e.g., allocate private data space,
|
||||||
* select IV, etc.); returns NULL on failure or pointer to allocated
|
* select IV, etc.); returns NULL on failure or pointer to allocated
|
||||||
* private data on success */
|
* private data on success */
|
||||||
void *(*init) (int keyidx);
|
void *(*init) (struct ieee80211_device * ieee, int keyidx);
|
||||||
|
|
||||||
/* deinitialize crypto context and free allocated private data */
|
/* deinitialize crypto context and free allocated private data */
|
||||||
void (*deinit) (void *priv);
|
void (*deinit) (void *priv);
|
||||||
|
|
|
@ -41,7 +41,9 @@ void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, int force)
|
||||||
{
|
{
|
||||||
struct list_head *ptr, *n;
|
struct list_head *ptr, *n;
|
||||||
struct ieee80211_crypt_data *entry;
|
struct ieee80211_crypt_data *entry;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&ieee->lock, flags);
|
||||||
for (ptr = ieee->crypt_deinit_list.next, n = ptr->next;
|
for (ptr = ieee->crypt_deinit_list.next, n = ptr->next;
|
||||||
ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) {
|
ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) {
|
||||||
entry = list_entry(ptr, struct ieee80211_crypt_data, list);
|
entry = list_entry(ptr, struct ieee80211_crypt_data, list);
|
||||||
|
@ -57,14 +59,13 @@ void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, int force)
|
||||||
}
|
}
|
||||||
kfree(entry);
|
kfree(entry);
|
||||||
}
|
}
|
||||||
|
spin_unlock_irqrestore(&ieee->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ieee80211_crypt_deinit_handler(unsigned long data)
|
void ieee80211_crypt_deinit_handler(unsigned long data)
|
||||||
{
|
{
|
||||||
struct ieee80211_device *ieee = (struct ieee80211_device *)data;
|
struct ieee80211_device *ieee = (struct ieee80211_device *)data;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&ieee->lock, flags);
|
|
||||||
ieee80211_crypt_deinit_entries(ieee, 0);
|
ieee80211_crypt_deinit_entries(ieee, 0);
|
||||||
if (!list_empty(&ieee->crypt_deinit_list)) {
|
if (!list_empty(&ieee->crypt_deinit_list)) {
|
||||||
printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
|
printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
|
||||||
|
@ -72,7 +73,6 @@ void ieee80211_crypt_deinit_handler(unsigned long data)
|
||||||
ieee->crypt_deinit_timer.expires = jiffies + HZ;
|
ieee->crypt_deinit_timer.expires = jiffies + HZ;
|
||||||
add_timer(&ieee->crypt_deinit_timer);
|
add_timer(&ieee->crypt_deinit_timer);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ieee->lock, flags);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +182,8 @@ struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *ieee80211_crypt_null_init(int keyidx)
|
static void *ieee80211_crypt_null_init(struct ieee80211_device *ieee,
|
||||||
|
int keyidx)
|
||||||
{
|
{
|
||||||
return (void *)1;
|
return (void *)1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ static void ieee80211_ccmp_aes_encrypt(struct crypto_tfm *tfm,
|
||||||
crypto_cipher_encrypt(tfm, &dst, &src, AES_BLOCK_LEN);
|
crypto_cipher_encrypt(tfm, &dst, &src, AES_BLOCK_LEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *ieee80211_ccmp_init(int key_idx)
|
static void *ieee80211_ccmp_init(struct ieee80211_device *ieee, int key_idx)
|
||||||
{
|
{
|
||||||
struct ieee80211_ccmp_data *priv;
|
struct ieee80211_ccmp_data *priv;
|
||||||
|
|
||||||
|
|
|
@ -59,9 +59,11 @@ struct ieee80211_tkip_data {
|
||||||
|
|
||||||
/* scratch buffers for virt_to_page() (crypto API) */
|
/* scratch buffers for virt_to_page() (crypto API) */
|
||||||
u8 rx_hdr[16], tx_hdr[16];
|
u8 rx_hdr[16], tx_hdr[16];
|
||||||
|
|
||||||
|
struct ieee80211_device *ieee;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void *ieee80211_tkip_init(int key_idx)
|
static void *ieee80211_tkip_init(struct ieee80211_device *ieee, int key_idx)
|
||||||
{
|
{
|
||||||
struct ieee80211_tkip_data *priv;
|
struct ieee80211_tkip_data *priv;
|
||||||
|
|
||||||
|
@ -69,6 +71,9 @@ static void *ieee80211_tkip_init(int key_idx)
|
||||||
if (priv == NULL)
|
if (priv == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
memset(priv, 0, sizeof(*priv));
|
memset(priv, 0, sizeof(*priv));
|
||||||
|
|
||||||
|
priv->ieee = ieee;
|
||||||
|
|
||||||
priv->key_idx = key_idx;
|
priv->key_idx = key_idx;
|
||||||
|
|
||||||
priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0);
|
priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0);
|
||||||
|
@ -264,11 +269,21 @@ static int ieee80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||||
u32 crc;
|
u32 crc;
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
|
hdr = (struct ieee80211_hdr *)skb->data;
|
||||||
|
|
||||||
|
if (tkey->ieee->tkip_countermeasures) {
|
||||||
|
if (net_ratelimit()) {
|
||||||
|
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
|
||||||
|
"TX packet to " MAC_FMT "\n",
|
||||||
|
tkey->ieee->dev->name, MAC_ARG(hdr->addr1));
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 ||
|
if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 ||
|
||||||
skb->len < hdr_len)
|
skb->len < hdr_len)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
hdr = (struct ieee80211_hdr *)skb->data;
|
|
||||||
if (!tkey->tx_phase1_done) {
|
if (!tkey->tx_phase1_done) {
|
||||||
tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
|
tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
|
||||||
tkey->tx_iv32);
|
tkey->tx_iv32);
|
||||||
|
@ -325,10 +340,20 @@ static int ieee80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
int plen;
|
int plen;
|
||||||
|
|
||||||
|
hdr = (struct ieee80211_hdr *)skb->data;
|
||||||
|
|
||||||
|
if (tkey->ieee->tkip_countermeasures) {
|
||||||
|
if (net_ratelimit()) {
|
||||||
|
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
|
||||||
|
"received packet from " MAC_FMT "\n",
|
||||||
|
tkey->ieee->dev->name, MAC_ARG(hdr->addr2));
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (skb->len < hdr_len + 8 + 4)
|
if (skb->len < hdr_len + 8 + 4)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
hdr = (struct ieee80211_hdr *)skb->data;
|
|
||||||
pos = skb->data + hdr_len;
|
pos = skb->data + hdr_len;
|
||||||
keyidx = pos[3];
|
keyidx = pos[3];
|
||||||
if (!(keyidx & (1 << 5))) {
|
if (!(keyidx & (1 << 5))) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ struct prism2_wep_data {
|
||||||
struct crypto_tfm *tfm;
|
struct crypto_tfm *tfm;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void *prism2_wep_init(int keyidx)
|
static void *prism2_wep_init(struct ieee80211_device *ieee, int keyidx)
|
||||||
{
|
{
|
||||||
struct prism2_wep_data *priv;
|
struct prism2_wep_data *priv;
|
||||||
|
|
||||||
|
|
|
@ -280,17 +280,6 @@ ieee80211_rx_frame_decrypt(struct ieee80211_device *ieee, struct sk_buff *skb,
|
||||||
hdr = (struct ieee80211_hdr *)skb->data;
|
hdr = (struct ieee80211_hdr *)skb->data;
|
||||||
hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
|
hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
|
||||||
|
|
||||||
#ifdef CONFIG_IEEE80211_CRYPT_TKIP
|
|
||||||
if (ieee->tkip_countermeasures && strcmp(crypt->ops->name, "TKIP") == 0) {
|
|
||||||
if (net_ratelimit()) {
|
|
||||||
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
|
|
||||||
"received packet from " MAC_FMT "\n",
|
|
||||||
ieee->dev->name, MAC_ARG(hdr->addr2));
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
atomic_inc(&crypt->refcnt);
|
atomic_inc(&crypt->refcnt);
|
||||||
res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
|
res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
|
||||||
atomic_dec(&crypt->refcnt);
|
atomic_dec(&crypt->refcnt);
|
||||||
|
|
|
@ -157,20 +157,6 @@ static inline int ieee80211_encrypt_fragment(struct ieee80211_device *ieee,
|
||||||
struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx];
|
struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx];
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
#ifdef CONFIG_IEEE80211_CRYPT_TKIP
|
|
||||||
struct ieee80211_hdr *header;
|
|
||||||
|
|
||||||
if (ieee->tkip_countermeasures &&
|
|
||||||
crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) {
|
|
||||||
header = (struct ieee80211_hdr *)frag->data;
|
|
||||||
if (net_ratelimit()) {
|
|
||||||
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
|
|
||||||
"TX packet to " MAC_FMT "\n",
|
|
||||||
ieee->dev->name, MAC_ARG(header->addr1));
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* To encrypt, frame format is:
|
/* To encrypt, frame format is:
|
||||||
* IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
|
* IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
|
||||||
|
|
||||||
|
|
|
@ -182,7 +182,7 @@ static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee,
|
||||||
if (iwe.u.data.length)
|
if (iwe.u.data.length)
|
||||||
start = iwe_stream_add_point(start, stop, &iwe, custom);
|
start = iwe_stream_add_point(start, stop, &iwe, custom);
|
||||||
|
|
||||||
if (ieee->wpa_enabled && network->wpa_ie_len) {
|
if (network->wpa_ie_len) {
|
||||||
char buf[MAX_WPA_IE_LEN * 2 + 30];
|
char buf[MAX_WPA_IE_LEN * 2 + 30];
|
||||||
|
|
||||||
u8 *p = buf;
|
u8 *p = buf;
|
||||||
|
@ -197,7 +197,7 @@ static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee,
|
||||||
start = iwe_stream_add_point(start, stop, &iwe, buf);
|
start = iwe_stream_add_point(start, stop, &iwe, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ieee->wpa_enabled && network->rsn_ie_len) {
|
if (network->rsn_ie_len) {
|
||||||
char buf[MAX_WPA_IE_LEN * 2 + 30];
|
char buf[MAX_WPA_IE_LEN * 2 + 30];
|
||||||
|
|
||||||
u8 *p = buf;
|
u8 *p = buf;
|
||||||
|
@ -351,7 +351,7 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
|
if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
|
||||||
new_crypt->priv = new_crypt->ops->init(key);
|
new_crypt->priv = new_crypt->ops->init(ieee, key);
|
||||||
|
|
||||||
if (!new_crypt->ops || !new_crypt->priv) {
|
if (!new_crypt->ops || !new_crypt->priv) {
|
||||||
kfree(new_crypt);
|
kfree(new_crypt);
|
||||||
|
|
Loading…
Reference in a new issue