diff options
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r-- | net/mac80211/key.c | 168 |
1 files changed, 105 insertions, 63 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 1b9d87ed143a..ccd676b2f599 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -49,7 +49,7 @@ static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | |||
49 | 49 | ||
50 | static void assert_key_lock(struct ieee80211_local *local) | 50 | static void assert_key_lock(struct ieee80211_local *local) |
51 | { | 51 | { |
52 | WARN_ON(!mutex_is_locked(&local->key_mtx)); | 52 | lockdep_assert_held(&local->key_mtx); |
53 | } | 53 | } |
54 | 54 | ||
55 | static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key) | 55 | static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key) |
@@ -60,7 +60,7 @@ static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key) | |||
60 | return NULL; | 60 | return NULL; |
61 | } | 61 | } |
62 | 62 | ||
63 | static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | 63 | static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) |
64 | { | 64 | { |
65 | struct ieee80211_sub_if_data *sdata; | 65 | struct ieee80211_sub_if_data *sdata; |
66 | struct ieee80211_sta *sta; | 66 | struct ieee80211_sta *sta; |
@@ -69,12 +69,20 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | |||
69 | might_sleep(); | 69 | might_sleep(); |
70 | 70 | ||
71 | if (!key->local->ops->set_key) | 71 | if (!key->local->ops->set_key) |
72 | return; | 72 | goto out_unsupported; |
73 | 73 | ||
74 | assert_key_lock(key->local); | 74 | assert_key_lock(key->local); |
75 | 75 | ||
76 | sta = get_sta_for_key(key); | 76 | sta = get_sta_for_key(key); |
77 | 77 | ||
78 | /* | ||
79 | * If this is a per-STA GTK, check if it | ||
80 | * is supported; if not, return. | ||
81 | */ | ||
82 | if (sta && !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE) && | ||
83 | !(key->local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)) | ||
84 | goto out_unsupported; | ||
85 | |||
78 | sdata = key->sdata; | 86 | sdata = key->sdata; |
79 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | 87 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) |
80 | sdata = container_of(sdata->bss, | 88 | sdata = container_of(sdata->bss, |
@@ -83,14 +91,28 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | |||
83 | 91 | ||
84 | ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf); | 92 | ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf); |
85 | 93 | ||
86 | if (!ret) | 94 | if (!ret) { |
87 | key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; | 95 | key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; |
96 | return 0; | ||
97 | } | ||
88 | 98 | ||
89 | if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP) | 99 | if (ret != -ENOSPC && ret != -EOPNOTSUPP) |
90 | printk(KERN_ERR "mac80211-%s: failed to set key " | 100 | wiphy_err(key->local->hw.wiphy, |
91 | "(%d, %pM) to hardware (%d)\n", | 101 | "failed to set key (%d, %pM) to hardware (%d)\n", |
92 | wiphy_name(key->local->hw.wiphy), | 102 | key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); |
93 | key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); | 103 | |
104 | out_unsupported: | ||
105 | switch (key->conf.cipher) { | ||
106 | case WLAN_CIPHER_SUITE_WEP40: | ||
107 | case WLAN_CIPHER_SUITE_WEP104: | ||
108 | case WLAN_CIPHER_SUITE_TKIP: | ||
109 | case WLAN_CIPHER_SUITE_CCMP: | ||
110 | case WLAN_CIPHER_SUITE_AES_CMAC: | ||
111 | /* all of these we can do in software */ | ||
112 | return 0; | ||
113 | default: | ||
114 | return -EINVAL; | ||
115 | } | ||
94 | } | 116 | } |
95 | 117 | ||
96 | static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) | 118 | static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) |
@@ -121,14 +143,33 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) | |||
121 | sta, &key->conf); | 143 | sta, &key->conf); |
122 | 144 | ||
123 | if (ret) | 145 | if (ret) |
124 | printk(KERN_ERR "mac80211-%s: failed to remove key " | 146 | wiphy_err(key->local->hw.wiphy, |
125 | "(%d, %pM) from hardware (%d)\n", | 147 | "failed to remove key (%d, %pM) from hardware (%d)\n", |
126 | wiphy_name(key->local->hw.wiphy), | 148 | key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); |
127 | key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); | ||
128 | 149 | ||
129 | key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; | 150 | key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; |
130 | } | 151 | } |
131 | 152 | ||
153 | void ieee80211_key_removed(struct ieee80211_key_conf *key_conf) | ||
154 | { | ||
155 | struct ieee80211_key *key; | ||
156 | |||
157 | key = container_of(key_conf, struct ieee80211_key, conf); | ||
158 | |||
159 | might_sleep(); | ||
160 | assert_key_lock(key->local); | ||
161 | |||
162 | key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; | ||
163 | |||
164 | /* | ||
165 | * Flush TX path to avoid attempts to use this key | ||
166 | * after this function returns. Until then, drivers | ||
167 | * must be prepared to handle the key. | ||
168 | */ | ||
169 | synchronize_rcu(); | ||
170 | } | ||
171 | EXPORT_SYMBOL_GPL(ieee80211_key_removed); | ||
172 | |||
132 | static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, | 173 | static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, |
133 | int idx) | 174 | int idx) |
134 | { | 175 | { |
@@ -184,6 +225,7 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, | |||
184 | 225 | ||
185 | static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | 226 | static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, |
186 | struct sta_info *sta, | 227 | struct sta_info *sta, |
228 | bool pairwise, | ||
187 | struct ieee80211_key *old, | 229 | struct ieee80211_key *old, |
188 | struct ieee80211_key *new) | 230 | struct ieee80211_key *new) |
189 | { | 231 | { |
@@ -192,8 +234,14 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | |||
192 | if (new) | 234 | if (new) |
193 | list_add(&new->list, &sdata->key_list); | 235 | list_add(&new->list, &sdata->key_list); |
194 | 236 | ||
195 | if (sta) { | 237 | if (sta && pairwise) { |
196 | rcu_assign_pointer(sta->key, new); | 238 | rcu_assign_pointer(sta->ptk, new); |
239 | } else if (sta) { | ||
240 | if (old) | ||
241 | idx = old->conf.keyidx; | ||
242 | else | ||
243 | idx = new->conf.keyidx; | ||
244 | rcu_assign_pointer(sta->gtk[idx], new); | ||
197 | } else { | 245 | } else { |
198 | WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); | 246 | WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); |
199 | 247 | ||
@@ -227,20 +275,18 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | |||
227 | } | 275 | } |
228 | } | 276 | } |
229 | 277 | ||
230 | struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | 278 | struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, |
231 | int idx, | ||
232 | size_t key_len, | ||
233 | const u8 *key_data, | 279 | const u8 *key_data, |
234 | size_t seq_len, const u8 *seq) | 280 | size_t seq_len, const u8 *seq) |
235 | { | 281 | { |
236 | struct ieee80211_key *key; | 282 | struct ieee80211_key *key; |
237 | int i, j; | 283 | int i, j, err; |
238 | 284 | ||
239 | BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS); | 285 | BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS); |
240 | 286 | ||
241 | key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); | 287 | key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); |
242 | if (!key) | 288 | if (!key) |
243 | return NULL; | 289 | return ERR_PTR(-ENOMEM); |
244 | 290 | ||
245 | /* | 291 | /* |
246 | * Default to software encryption; we'll later upload the | 292 | * Default to software encryption; we'll later upload the |
@@ -249,15 +295,16 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | |||
249 | key->conf.flags = 0; | 295 | key->conf.flags = 0; |
250 | key->flags = 0; | 296 | key->flags = 0; |
251 | 297 | ||
252 | key->conf.alg = alg; | 298 | key->conf.cipher = cipher; |
253 | key->conf.keyidx = idx; | 299 | key->conf.keyidx = idx; |
254 | key->conf.keylen = key_len; | 300 | key->conf.keylen = key_len; |
255 | switch (alg) { | 301 | switch (cipher) { |
256 | case ALG_WEP: | 302 | case WLAN_CIPHER_SUITE_WEP40: |
303 | case WLAN_CIPHER_SUITE_WEP104: | ||
257 | key->conf.iv_len = WEP_IV_LEN; | 304 | key->conf.iv_len = WEP_IV_LEN; |
258 | key->conf.icv_len = WEP_ICV_LEN; | 305 | key->conf.icv_len = WEP_ICV_LEN; |
259 | break; | 306 | break; |
260 | case ALG_TKIP: | 307 | case WLAN_CIPHER_SUITE_TKIP: |
261 | key->conf.iv_len = TKIP_IV_LEN; | 308 | key->conf.iv_len = TKIP_IV_LEN; |
262 | key->conf.icv_len = TKIP_ICV_LEN; | 309 | key->conf.icv_len = TKIP_ICV_LEN; |
263 | if (seq) { | 310 | if (seq) { |
@@ -269,7 +316,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | |||
269 | } | 316 | } |
270 | } | 317 | } |
271 | break; | 318 | break; |
272 | case ALG_CCMP: | 319 | case WLAN_CIPHER_SUITE_CCMP: |
273 | key->conf.iv_len = CCMP_HDR_LEN; | 320 | key->conf.iv_len = CCMP_HDR_LEN; |
274 | key->conf.icv_len = CCMP_MIC_LEN; | 321 | key->conf.icv_len = CCMP_MIC_LEN; |
275 | if (seq) { | 322 | if (seq) { |
@@ -278,42 +325,38 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | |||
278 | key->u.ccmp.rx_pn[i][j] = | 325 | key->u.ccmp.rx_pn[i][j] = |
279 | seq[CCMP_PN_LEN - j - 1]; | 326 | seq[CCMP_PN_LEN - j - 1]; |
280 | } | 327 | } |
281 | break; | ||
282 | case ALG_AES_CMAC: | ||
283 | key->conf.iv_len = 0; | ||
284 | key->conf.icv_len = sizeof(struct ieee80211_mmie); | ||
285 | if (seq) | ||
286 | for (j = 0; j < 6; j++) | ||
287 | key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1]; | ||
288 | break; | ||
289 | } | ||
290 | memcpy(key->conf.key, key_data, key_len); | ||
291 | INIT_LIST_HEAD(&key->list); | ||
292 | |||
293 | if (alg == ALG_CCMP) { | ||
294 | /* | 328 | /* |
295 | * Initialize AES key state here as an optimization so that | 329 | * Initialize AES key state here as an optimization so that |
296 | * it does not need to be initialized for every packet. | 330 | * it does not need to be initialized for every packet. |
297 | */ | 331 | */ |
298 | key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data); | 332 | key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data); |
299 | if (!key->u.ccmp.tfm) { | 333 | if (IS_ERR(key->u.ccmp.tfm)) { |
334 | err = PTR_ERR(key->u.ccmp.tfm); | ||
300 | kfree(key); | 335 | kfree(key); |
301 | return NULL; | 336 | key = ERR_PTR(err); |
302 | } | 337 | } |
303 | } | 338 | break; |
304 | 339 | case WLAN_CIPHER_SUITE_AES_CMAC: | |
305 | if (alg == ALG_AES_CMAC) { | 340 | key->conf.iv_len = 0; |
341 | key->conf.icv_len = sizeof(struct ieee80211_mmie); | ||
342 | if (seq) | ||
343 | for (j = 0; j < 6; j++) | ||
344 | key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1]; | ||
306 | /* | 345 | /* |
307 | * Initialize AES key state here as an optimization so that | 346 | * Initialize AES key state here as an optimization so that |
308 | * it does not need to be initialized for every packet. | 347 | * it does not need to be initialized for every packet. |
309 | */ | 348 | */ |
310 | key->u.aes_cmac.tfm = | 349 | key->u.aes_cmac.tfm = |
311 | ieee80211_aes_cmac_key_setup(key_data); | 350 | ieee80211_aes_cmac_key_setup(key_data); |
312 | if (!key->u.aes_cmac.tfm) { | 351 | if (IS_ERR(key->u.aes_cmac.tfm)) { |
352 | err = PTR_ERR(key->u.aes_cmac.tfm); | ||
313 | kfree(key); | 353 | kfree(key); |
314 | return NULL; | 354 | key = ERR_PTR(err); |
315 | } | 355 | } |
356 | break; | ||
316 | } | 357 | } |
358 | memcpy(key->conf.key, key_data, key_len); | ||
359 | INIT_LIST_HEAD(&key->list); | ||
317 | 360 | ||
318 | return key; | 361 | return key; |
319 | } | 362 | } |
@@ -326,9 +369,9 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) | |||
326 | if (key->local) | 369 | if (key->local) |
327 | ieee80211_key_disable_hw_accel(key); | 370 | ieee80211_key_disable_hw_accel(key); |
328 | 371 | ||
329 | if (key->conf.alg == ALG_CCMP) | 372 | if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP) |
330 | ieee80211_aes_key_free(key->u.ccmp.tfm); | 373 | ieee80211_aes_key_free(key->u.ccmp.tfm); |
331 | if (key->conf.alg == ALG_AES_CMAC) | 374 | if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) |
332 | ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); | 375 | ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); |
333 | if (key->local) | 376 | if (key->local) |
334 | ieee80211_debugfs_key_remove(key); | 377 | ieee80211_debugfs_key_remove(key); |
@@ -336,12 +379,13 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) | |||
336 | kfree(key); | 379 | kfree(key); |
337 | } | 380 | } |
338 | 381 | ||
339 | void ieee80211_key_link(struct ieee80211_key *key, | 382 | int ieee80211_key_link(struct ieee80211_key *key, |
340 | struct ieee80211_sub_if_data *sdata, | 383 | struct ieee80211_sub_if_data *sdata, |
341 | struct sta_info *sta) | 384 | struct sta_info *sta) |
342 | { | 385 | { |
343 | struct ieee80211_key *old_key; | 386 | struct ieee80211_key *old_key; |
344 | int idx; | 387 | int idx, ret; |
388 | bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; | ||
345 | 389 | ||
346 | BUG_ON(!sdata); | 390 | BUG_ON(!sdata); |
347 | BUG_ON(!key); | 391 | BUG_ON(!key); |
@@ -358,13 +402,6 @@ void ieee80211_key_link(struct ieee80211_key *key, | |||
358 | */ | 402 | */ |
359 | if (test_sta_flags(sta, WLAN_STA_WME)) | 403 | if (test_sta_flags(sta, WLAN_STA_WME)) |
360 | key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; | 404 | key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; |
361 | |||
362 | /* | ||
363 | * This key is for a specific sta interface, | ||
364 | * inform the driver that it should try to store | ||
365 | * this key as pairwise key. | ||
366 | */ | ||
367 | key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE; | ||
368 | } else { | 405 | } else { |
369 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | 406 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
370 | struct sta_info *ap; | 407 | struct sta_info *ap; |
@@ -386,19 +423,23 @@ void ieee80211_key_link(struct ieee80211_key *key, | |||
386 | 423 | ||
387 | mutex_lock(&sdata->local->key_mtx); | 424 | mutex_lock(&sdata->local->key_mtx); |
388 | 425 | ||
389 | if (sta) | 426 | if (sta && pairwise) |
390 | old_key = sta->key; | 427 | old_key = sta->ptk; |
428 | else if (sta) | ||
429 | old_key = sta->gtk[idx]; | ||
391 | else | 430 | else |
392 | old_key = sdata->keys[idx]; | 431 | old_key = sdata->keys[idx]; |
393 | 432 | ||
394 | __ieee80211_key_replace(sdata, sta, old_key, key); | 433 | __ieee80211_key_replace(sdata, sta, pairwise, old_key, key); |
395 | __ieee80211_key_destroy(old_key); | 434 | __ieee80211_key_destroy(old_key); |
396 | 435 | ||
397 | ieee80211_debugfs_key_add(key); | 436 | ieee80211_debugfs_key_add(key); |
398 | 437 | ||
399 | ieee80211_key_enable_hw_accel(key); | 438 | ret = ieee80211_key_enable_hw_accel(key); |
400 | 439 | ||
401 | mutex_unlock(&sdata->local->key_mtx); | 440 | mutex_unlock(&sdata->local->key_mtx); |
441 | |||
442 | return ret; | ||
402 | } | 443 | } |
403 | 444 | ||
404 | static void __ieee80211_key_free(struct ieee80211_key *key) | 445 | static void __ieee80211_key_free(struct ieee80211_key *key) |
@@ -408,7 +449,8 @@ static void __ieee80211_key_free(struct ieee80211_key *key) | |||
408 | */ | 449 | */ |
409 | if (key->sdata) | 450 | if (key->sdata) |
410 | __ieee80211_key_replace(key->sdata, key->sta, | 451 | __ieee80211_key_replace(key->sdata, key->sta, |
411 | key, NULL); | 452 | key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, |
453 | key, NULL); | ||
412 | __ieee80211_key_destroy(key); | 454 | __ieee80211_key_destroy(key); |
413 | } | 455 | } |
414 | 456 | ||