diff options
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r-- | net/mac80211/key.c | 115 |
1 files changed, 86 insertions, 29 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 999f7aa42326..687acf23054d 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include "ieee80211_i.h" | 18 | #include "ieee80211_i.h" |
19 | #include "debugfs_key.h" | 19 | #include "debugfs_key.h" |
20 | #include "aes_ccm.h" | 20 | #include "aes_ccm.h" |
21 | #include "aes_cmac.h" | ||
21 | 22 | ||
22 | 23 | ||
23 | /** | 24 | /** |
@@ -47,7 +48,6 @@ | |||
47 | */ | 48 | */ |
48 | 49 | ||
49 | static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | 50 | static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
50 | static const u8 zero_addr[ETH_ALEN]; | ||
51 | 51 | ||
52 | /* key mutex: used to synchronise todo runners */ | 52 | /* key mutex: used to synchronise todo runners */ |
53 | static DEFINE_MUTEX(key_mutex); | 53 | static DEFINE_MUTEX(key_mutex); |
@@ -108,29 +108,18 @@ static void assert_key_lock(void) | |||
108 | WARN_ON(!mutex_is_locked(&key_mutex)); | 108 | WARN_ON(!mutex_is_locked(&key_mutex)); |
109 | } | 109 | } |
110 | 110 | ||
111 | static const u8 *get_mac_for_key(struct ieee80211_key *key) | 111 | static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key) |
112 | { | 112 | { |
113 | const u8 *addr = bcast_addr; | ||
114 | |||
115 | /* | ||
116 | * If we're an AP we won't ever receive frames with a non-WEP | ||
117 | * group key so we tell the driver that by using the zero MAC | ||
118 | * address to indicate a transmit-only key. | ||
119 | */ | ||
120 | if (key->conf.alg != ALG_WEP && | ||
121 | (key->sdata->vif.type == NL80211_IFTYPE_AP || | ||
122 | key->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) | ||
123 | addr = zero_addr; | ||
124 | |||
125 | if (key->sta) | 113 | if (key->sta) |
126 | addr = key->sta->sta.addr; | 114 | return &key->sta->sta; |
127 | 115 | ||
128 | return addr; | 116 | return NULL; |
129 | } | 117 | } |
130 | 118 | ||
131 | static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | 119 | static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) |
132 | { | 120 | { |
133 | const u8 *addr; | 121 | struct ieee80211_sub_if_data *sdata; |
122 | struct ieee80211_sta *sta; | ||
134 | int ret; | 123 | int ret; |
135 | 124 | ||
136 | assert_key_lock(); | 125 | assert_key_lock(); |
@@ -139,11 +128,16 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | |||
139 | if (!key->local->ops->set_key) | 128 | if (!key->local->ops->set_key) |
140 | return; | 129 | return; |
141 | 130 | ||
142 | addr = get_mac_for_key(key); | 131 | sta = get_sta_for_key(key); |
132 | |||
133 | sdata = key->sdata; | ||
134 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | ||
135 | sdata = container_of(sdata->bss, | ||
136 | struct ieee80211_sub_if_data, | ||
137 | u.ap); | ||
143 | 138 | ||
144 | ret = key->local->ops->set_key(local_to_hw(key->local), SET_KEY, | 139 | ret = key->local->ops->set_key(local_to_hw(key->local), SET_KEY, |
145 | key->sdata->dev->dev_addr, addr, | 140 | &sdata->vif, sta, &key->conf); |
146 | &key->conf); | ||
147 | 141 | ||
148 | if (!ret) { | 142 | if (!ret) { |
149 | spin_lock(&todo_lock); | 143 | spin_lock(&todo_lock); |
@@ -155,12 +149,13 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | |||
155 | printk(KERN_ERR "mac80211-%s: failed to set key " | 149 | printk(KERN_ERR "mac80211-%s: failed to set key " |
156 | "(%d, %pM) to hardware (%d)\n", | 150 | "(%d, %pM) to hardware (%d)\n", |
157 | wiphy_name(key->local->hw.wiphy), | 151 | wiphy_name(key->local->hw.wiphy), |
158 | key->conf.keyidx, addr, ret); | 152 | key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); |
159 | } | 153 | } |
160 | 154 | ||
161 | static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) | 155 | static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) |
162 | { | 156 | { |
163 | const u8 *addr; | 157 | struct ieee80211_sub_if_data *sdata; |
158 | struct ieee80211_sta *sta; | ||
164 | int ret; | 159 | int ret; |
165 | 160 | ||
166 | assert_key_lock(); | 161 | assert_key_lock(); |
@@ -176,17 +171,22 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) | |||
176 | } | 171 | } |
177 | spin_unlock(&todo_lock); | 172 | spin_unlock(&todo_lock); |
178 | 173 | ||
179 | addr = get_mac_for_key(key); | 174 | sta = get_sta_for_key(key); |
175 | sdata = key->sdata; | ||
176 | |||
177 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | ||
178 | sdata = container_of(sdata->bss, | ||
179 | struct ieee80211_sub_if_data, | ||
180 | u.ap); | ||
180 | 181 | ||
181 | ret = key->local->ops->set_key(local_to_hw(key->local), DISABLE_KEY, | 182 | ret = key->local->ops->set_key(local_to_hw(key->local), DISABLE_KEY, |
182 | key->sdata->dev->dev_addr, addr, | 183 | &sdata->vif, sta, &key->conf); |
183 | &key->conf); | ||
184 | 184 | ||
185 | if (ret) | 185 | if (ret) |
186 | printk(KERN_ERR "mac80211-%s: failed to remove key " | 186 | printk(KERN_ERR "mac80211-%s: failed to remove key " |
187 | "(%d, %pM) from hardware (%d)\n", | 187 | "(%d, %pM) from hardware (%d)\n", |
188 | wiphy_name(key->local->hw.wiphy), | 188 | wiphy_name(key->local->hw.wiphy), |
189 | key->conf.keyidx, addr, ret); | 189 | key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); |
190 | 190 | ||
191 | spin_lock(&todo_lock); | 191 | spin_lock(&todo_lock); |
192 | key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; | 192 | key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; |
@@ -216,13 +216,38 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) | |||
216 | spin_unlock_irqrestore(&sdata->local->key_lock, flags); | 216 | spin_unlock_irqrestore(&sdata->local->key_lock, flags); |
217 | } | 217 | } |
218 | 218 | ||
219 | static void | ||
220 | __ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx) | ||
221 | { | ||
222 | struct ieee80211_key *key = NULL; | ||
223 | |||
224 | if (idx >= NUM_DEFAULT_KEYS && | ||
225 | idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) | ||
226 | key = sdata->keys[idx]; | ||
227 | |||
228 | rcu_assign_pointer(sdata->default_mgmt_key, key); | ||
229 | |||
230 | if (key) | ||
231 | add_todo(key, KEY_FLAG_TODO_DEFMGMTKEY); | ||
232 | } | ||
233 | |||
234 | void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, | ||
235 | int idx) | ||
236 | { | ||
237 | unsigned long flags; | ||
238 | |||
239 | spin_lock_irqsave(&sdata->local->key_lock, flags); | ||
240 | __ieee80211_set_default_mgmt_key(sdata, idx); | ||
241 | spin_unlock_irqrestore(&sdata->local->key_lock, flags); | ||
242 | } | ||
243 | |||
219 | 244 | ||
220 | static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | 245 | static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, |
221 | struct sta_info *sta, | 246 | struct sta_info *sta, |
222 | struct ieee80211_key *old, | 247 | struct ieee80211_key *old, |
223 | struct ieee80211_key *new) | 248 | struct ieee80211_key *new) |
224 | { | 249 | { |
225 | int idx, defkey; | 250 | int idx, defkey, defmgmtkey; |
226 | 251 | ||
227 | if (new) | 252 | if (new) |
228 | list_add(&new->list, &sdata->key_list); | 253 | list_add(&new->list, &sdata->key_list); |
@@ -238,13 +263,19 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | |||
238 | idx = new->conf.keyidx; | 263 | idx = new->conf.keyidx; |
239 | 264 | ||
240 | defkey = old && sdata->default_key == old; | 265 | defkey = old && sdata->default_key == old; |
266 | defmgmtkey = old && sdata->default_mgmt_key == old; | ||
241 | 267 | ||
242 | if (defkey && !new) | 268 | if (defkey && !new) |
243 | __ieee80211_set_default_key(sdata, -1); | 269 | __ieee80211_set_default_key(sdata, -1); |
270 | if (defmgmtkey && !new) | ||
271 | __ieee80211_set_default_mgmt_key(sdata, -1); | ||
244 | 272 | ||
245 | rcu_assign_pointer(sdata->keys[idx], new); | 273 | rcu_assign_pointer(sdata->keys[idx], new); |
246 | if (defkey && new) | 274 | if (defkey && new) |
247 | __ieee80211_set_default_key(sdata, new->conf.keyidx); | 275 | __ieee80211_set_default_key(sdata, new->conf.keyidx); |
276 | if (defmgmtkey && new) | ||
277 | __ieee80211_set_default_mgmt_key(sdata, | ||
278 | new->conf.keyidx); | ||
248 | } | 279 | } |
249 | 280 | ||
250 | if (old) { | 281 | if (old) { |
@@ -263,7 +294,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | |||
263 | { | 294 | { |
264 | struct ieee80211_key *key; | 295 | struct ieee80211_key *key; |
265 | 296 | ||
266 | BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS); | 297 | BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS); |
267 | 298 | ||
268 | key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); | 299 | key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); |
269 | if (!key) | 300 | if (!key) |
@@ -292,6 +323,10 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | |||
292 | key->conf.iv_len = CCMP_HDR_LEN; | 323 | key->conf.iv_len = CCMP_HDR_LEN; |
293 | key->conf.icv_len = CCMP_MIC_LEN; | 324 | key->conf.icv_len = CCMP_MIC_LEN; |
294 | break; | 325 | break; |
326 | case ALG_AES_CMAC: | ||
327 | key->conf.iv_len = 0; | ||
328 | key->conf.icv_len = sizeof(struct ieee80211_mmie); | ||
329 | break; | ||
295 | } | 330 | } |
296 | memcpy(key->conf.key, key_data, key_len); | 331 | memcpy(key->conf.key, key_data, key_len); |
297 | INIT_LIST_HEAD(&key->list); | 332 | INIT_LIST_HEAD(&key->list); |
@@ -309,6 +344,19 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | |||
309 | } | 344 | } |
310 | } | 345 | } |
311 | 346 | ||
347 | if (alg == ALG_AES_CMAC) { | ||
348 | /* | ||
349 | * Initialize AES key state here as an optimization so that | ||
350 | * it does not need to be initialized for every packet. | ||
351 | */ | ||
352 | key->u.aes_cmac.tfm = | ||
353 | ieee80211_aes_cmac_key_setup(key_data); | ||
354 | if (!key->u.aes_cmac.tfm) { | ||
355 | kfree(key); | ||
356 | return NULL; | ||
357 | } | ||
358 | } | ||
359 | |||
312 | return key; | 360 | return key; |
313 | } | 361 | } |
314 | 362 | ||
@@ -352,7 +400,7 @@ void ieee80211_key_link(struct ieee80211_key *key, | |||
352 | */ | 400 | */ |
353 | 401 | ||
354 | /* same here, the AP could be using QoS */ | 402 | /* same here, the AP could be using QoS */ |
355 | ap = sta_info_get(key->local, key->sdata->u.sta.bssid); | 403 | ap = sta_info_get(key->local, key->sdata->u.mgd.bssid); |
356 | if (ap) { | 404 | if (ap) { |
357 | if (test_sta_flags(ap, WLAN_STA_WME)) | 405 | if (test_sta_flags(ap, WLAN_STA_WME)) |
358 | key->conf.flags |= | 406 | key->conf.flags |= |
@@ -462,6 +510,8 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) | |||
462 | 510 | ||
463 | if (key->conf.alg == ALG_CCMP) | 511 | if (key->conf.alg == ALG_CCMP) |
464 | ieee80211_aes_key_free(key->u.ccmp.tfm); | 512 | ieee80211_aes_key_free(key->u.ccmp.tfm); |
513 | if (key->conf.alg == ALG_AES_CMAC) | ||
514 | ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); | ||
465 | ieee80211_debugfs_key_remove(key); | 515 | ieee80211_debugfs_key_remove(key); |
466 | 516 | ||
467 | kfree(key); | 517 | kfree(key); |
@@ -484,6 +534,7 @@ static void __ieee80211_key_todo(void) | |||
484 | list_del_init(&key->todo); | 534 | list_del_init(&key->todo); |
485 | todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | | 535 | todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | |
486 | KEY_FLAG_TODO_DEFKEY | | 536 | KEY_FLAG_TODO_DEFKEY | |
537 | KEY_FLAG_TODO_DEFMGMTKEY | | ||
487 | KEY_FLAG_TODO_HWACCEL_ADD | | 538 | KEY_FLAG_TODO_HWACCEL_ADD | |
488 | KEY_FLAG_TODO_HWACCEL_REMOVE | | 539 | KEY_FLAG_TODO_HWACCEL_REMOVE | |
489 | KEY_FLAG_TODO_DELETE); | 540 | KEY_FLAG_TODO_DELETE); |
@@ -501,6 +552,11 @@ static void __ieee80211_key_todo(void) | |||
501 | ieee80211_debugfs_key_add_default(key->sdata); | 552 | ieee80211_debugfs_key_add_default(key->sdata); |
502 | work_done = true; | 553 | work_done = true; |
503 | } | 554 | } |
555 | if (todoflags & KEY_FLAG_TODO_DEFMGMTKEY) { | ||
556 | ieee80211_debugfs_key_remove_mgmt_default(key->sdata); | ||
557 | ieee80211_debugfs_key_add_mgmt_default(key->sdata); | ||
558 | work_done = true; | ||
559 | } | ||
504 | if (todoflags & KEY_FLAG_TODO_HWACCEL_ADD) { | 560 | if (todoflags & KEY_FLAG_TODO_HWACCEL_ADD) { |
505 | ieee80211_key_enable_hw_accel(key); | 561 | ieee80211_key_enable_hw_accel(key); |
506 | work_done = true; | 562 | work_done = true; |
@@ -536,6 +592,7 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) | |||
536 | ieee80211_key_lock(); | 592 | ieee80211_key_lock(); |
537 | 593 | ||
538 | ieee80211_debugfs_key_remove_default(sdata); | 594 | ieee80211_debugfs_key_remove_default(sdata); |
595 | ieee80211_debugfs_key_remove_mgmt_default(sdata); | ||
539 | 596 | ||
540 | spin_lock_irqsave(&sdata->local->key_lock, flags); | 597 | spin_lock_irqsave(&sdata->local->key_lock, flags); |
541 | list_for_each_entry_safe(key, tmp, &sdata->key_list, list) | 598 | list_for_each_entry_safe(key, tmp, &sdata->key_list, list) |