diff options
author | Jouni Malinen <j@w1.fi> | 2009-01-08 06:32:02 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-01-29 16:00:03 -0500 |
commit | 3cfcf6ac6d69dc290e96416731eea5c88ac7d426 (patch) | |
tree | 35bc626e2e3f7c37a7eb50c1f057adb4830eccc6 /net | |
parent | 765cb46a3fc856245ea68a7c961ac87c77e4ae2d (diff) |
mac80211: 802.11w - Use BIP (AES-128-CMAC)
Add mechanism for managing BIP keys (IGTK) and integrate BIP into the
TX/RX paths.
Signed-off-by: Jouni Malinen <j@w1.fi>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 31 | ||||
-rw-r--r-- | net/mac80211/debugfs_key.c | 79 | ||||
-rw-r--r-- | net/mac80211/debugfs_key.h | 10 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 5 | ||||
-rw-r--r-- | net/mac80211/key.c | 62 | ||||
-rw-r--r-- | net/mac80211/key.h | 6 | ||||
-rw-r--r-- | net/mac80211/rx.c | 90 | ||||
-rw-r--r-- | net/mac80211/tx.c | 9 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 29 |
9 files changed, 302 insertions, 19 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 309d9189aa49..72c106915433 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -133,6 +133,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, | |||
133 | case WLAN_CIPHER_SUITE_CCMP: | 133 | case WLAN_CIPHER_SUITE_CCMP: |
134 | alg = ALG_CCMP; | 134 | alg = ALG_CCMP; |
135 | break; | 135 | break; |
136 | case WLAN_CIPHER_SUITE_AES_CMAC: | ||
137 | alg = ALG_AES_CMAC; | ||
138 | break; | ||
136 | default: | 139 | default: |
137 | return -EINVAL; | 140 | return -EINVAL; |
138 | } | 141 | } |
@@ -275,6 +278,17 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, | |||
275 | else | 278 | else |
276 | params.cipher = WLAN_CIPHER_SUITE_WEP104; | 279 | params.cipher = WLAN_CIPHER_SUITE_WEP104; |
277 | break; | 280 | break; |
281 | case ALG_AES_CMAC: | ||
282 | params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; | ||
283 | seq[0] = key->u.aes_cmac.tx_pn[5]; | ||
284 | seq[1] = key->u.aes_cmac.tx_pn[4]; | ||
285 | seq[2] = key->u.aes_cmac.tx_pn[3]; | ||
286 | seq[3] = key->u.aes_cmac.tx_pn[2]; | ||
287 | seq[4] = key->u.aes_cmac.tx_pn[1]; | ||
288 | seq[5] = key->u.aes_cmac.tx_pn[0]; | ||
289 | params.seq = seq; | ||
290 | params.seq_len = 6; | ||
291 | break; | ||
278 | } | 292 | } |
279 | 293 | ||
280 | params.key = key->conf.key; | 294 | params.key = key->conf.key; |
@@ -304,6 +318,22 @@ static int ieee80211_config_default_key(struct wiphy *wiphy, | |||
304 | return 0; | 318 | return 0; |
305 | } | 319 | } |
306 | 320 | ||
321 | static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy, | ||
322 | struct net_device *dev, | ||
323 | u8 key_idx) | ||
324 | { | ||
325 | struct ieee80211_sub_if_data *sdata; | ||
326 | |||
327 | rcu_read_lock(); | ||
328 | |||
329 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
330 | ieee80211_set_default_mgmt_key(sdata, key_idx); | ||
331 | |||
332 | rcu_read_unlock(); | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
307 | static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) | 337 | static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) |
308 | { | 338 | { |
309 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 339 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
@@ -1153,6 +1183,7 @@ struct cfg80211_ops mac80211_config_ops = { | |||
1153 | .del_key = ieee80211_del_key, | 1183 | .del_key = ieee80211_del_key, |
1154 | .get_key = ieee80211_get_key, | 1184 | .get_key = ieee80211_get_key, |
1155 | .set_default_key = ieee80211_config_default_key, | 1185 | .set_default_key = ieee80211_config_default_key, |
1186 | .set_default_mgmt_key = ieee80211_config_default_mgmt_key, | ||
1156 | .add_beacon = ieee80211_add_beacon, | 1187 | .add_beacon = ieee80211_add_beacon, |
1157 | .set_beacon = ieee80211_set_beacon, | 1188 | .set_beacon = ieee80211_set_beacon, |
1158 | .del_beacon = ieee80211_del_beacon, | 1189 | .del_beacon = ieee80211_del_beacon, |
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 6424ac565ae0..99c752588b30 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c | |||
@@ -76,6 +76,9 @@ static ssize_t key_algorithm_read(struct file *file, | |||
76 | case ALG_CCMP: | 76 | case ALG_CCMP: |
77 | alg = "CCMP\n"; | 77 | alg = "CCMP\n"; |
78 | break; | 78 | break; |
79 | case ALG_AES_CMAC: | ||
80 | alg = "AES-128-CMAC\n"; | ||
81 | break; | ||
79 | default: | 82 | default: |
80 | return 0; | 83 | return 0; |
81 | } | 84 | } |
@@ -105,6 +108,12 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, | |||
105 | len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", | 108 | len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", |
106 | tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); | 109 | tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); |
107 | break; | 110 | break; |
111 | case ALG_AES_CMAC: | ||
112 | tpn = key->u.aes_cmac.tx_pn; | ||
113 | len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", | ||
114 | tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], | ||
115 | tpn[5]); | ||
116 | break; | ||
108 | default: | 117 | default: |
109 | return 0; | 118 | return 0; |
110 | } | 119 | } |
@@ -142,6 +151,14 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, | |||
142 | } | 151 | } |
143 | len = p - buf; | 152 | len = p - buf; |
144 | break; | 153 | break; |
154 | case ALG_AES_CMAC: | ||
155 | rpn = key->u.aes_cmac.rx_pn; | ||
156 | p += scnprintf(p, sizeof(buf)+buf-p, | ||
157 | "%02x%02x%02x%02x%02x%02x\n", | ||
158 | rpn[0], rpn[1], rpn[2], | ||
159 | rpn[3], rpn[4], rpn[5]); | ||
160 | len = p - buf; | ||
161 | break; | ||
145 | default: | 162 | default: |
146 | return 0; | 163 | return 0; |
147 | } | 164 | } |
@@ -156,13 +173,40 @@ static ssize_t key_replays_read(struct file *file, char __user *userbuf, | |||
156 | char buf[20]; | 173 | char buf[20]; |
157 | int len; | 174 | int len; |
158 | 175 | ||
159 | if (key->conf.alg != ALG_CCMP) | 176 | switch (key->conf.alg) { |
177 | case ALG_CCMP: | ||
178 | len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); | ||
179 | break; | ||
180 | case ALG_AES_CMAC: | ||
181 | len = scnprintf(buf, sizeof(buf), "%u\n", | ||
182 | key->u.aes_cmac.replays); | ||
183 | break; | ||
184 | default: | ||
160 | return 0; | 185 | return 0; |
161 | len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); | 186 | } |
162 | return simple_read_from_buffer(userbuf, count, ppos, buf, len); | 187 | return simple_read_from_buffer(userbuf, count, ppos, buf, len); |
163 | } | 188 | } |
164 | KEY_OPS(replays); | 189 | KEY_OPS(replays); |
165 | 190 | ||
191 | static ssize_t key_icverrors_read(struct file *file, char __user *userbuf, | ||
192 | size_t count, loff_t *ppos) | ||
193 | { | ||
194 | struct ieee80211_key *key = file->private_data; | ||
195 | char buf[20]; | ||
196 | int len; | ||
197 | |||
198 | switch (key->conf.alg) { | ||
199 | case ALG_AES_CMAC: | ||
200 | len = scnprintf(buf, sizeof(buf), "%u\n", | ||
201 | key->u.aes_cmac.icverrors); | ||
202 | break; | ||
203 | default: | ||
204 | return 0; | ||
205 | } | ||
206 | return simple_read_from_buffer(userbuf, count, ppos, buf, len); | ||
207 | } | ||
208 | KEY_OPS(icverrors); | ||
209 | |||
166 | static ssize_t key_key_read(struct file *file, char __user *userbuf, | 210 | static ssize_t key_key_read(struct file *file, char __user *userbuf, |
167 | size_t count, loff_t *ppos) | 211 | size_t count, loff_t *ppos) |
168 | { | 212 | { |
@@ -222,6 +266,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key) | |||
222 | DEBUGFS_ADD(tx_spec); | 266 | DEBUGFS_ADD(tx_spec); |
223 | DEBUGFS_ADD(rx_spec); | 267 | DEBUGFS_ADD(rx_spec); |
224 | DEBUGFS_ADD(replays); | 268 | DEBUGFS_ADD(replays); |
269 | DEBUGFS_ADD(icverrors); | ||
225 | DEBUGFS_ADD(key); | 270 | DEBUGFS_ADD(key); |
226 | DEBUGFS_ADD(ifindex); | 271 | DEBUGFS_ADD(ifindex); |
227 | }; | 272 | }; |
@@ -243,6 +288,7 @@ void ieee80211_debugfs_key_remove(struct ieee80211_key *key) | |||
243 | DEBUGFS_DEL(tx_spec); | 288 | DEBUGFS_DEL(tx_spec); |
244 | DEBUGFS_DEL(rx_spec); | 289 | DEBUGFS_DEL(rx_spec); |
245 | DEBUGFS_DEL(replays); | 290 | DEBUGFS_DEL(replays); |
291 | DEBUGFS_DEL(icverrors); | ||
246 | DEBUGFS_DEL(key); | 292 | DEBUGFS_DEL(key); |
247 | DEBUGFS_DEL(ifindex); | 293 | DEBUGFS_DEL(ifindex); |
248 | 294 | ||
@@ -280,6 +326,35 @@ void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata) | |||
280 | sdata->common_debugfs.default_key = NULL; | 326 | sdata->common_debugfs.default_key = NULL; |
281 | } | 327 | } |
282 | 328 | ||
329 | void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) | ||
330 | { | ||
331 | char buf[50]; | ||
332 | struct ieee80211_key *key; | ||
333 | |||
334 | if (!sdata->debugfsdir) | ||
335 | return; | ||
336 | |||
337 | /* this is running under the key lock */ | ||
338 | |||
339 | key = sdata->default_mgmt_key; | ||
340 | if (key) { | ||
341 | sprintf(buf, "../keys/%d", key->debugfs.cnt); | ||
342 | sdata->common_debugfs.default_mgmt_key = | ||
343 | debugfs_create_symlink("default_mgmt_key", | ||
344 | sdata->debugfsdir, buf); | ||
345 | } else | ||
346 | ieee80211_debugfs_key_remove_mgmt_default(sdata); | ||
347 | } | ||
348 | |||
349 | void ieee80211_debugfs_key_remove_mgmt_default(struct ieee80211_sub_if_data *sdata) | ||
350 | { | ||
351 | if (!sdata) | ||
352 | return; | ||
353 | |||
354 | debugfs_remove(sdata->common_debugfs.default_mgmt_key); | ||
355 | sdata->common_debugfs.default_mgmt_key = NULL; | ||
356 | } | ||
357 | |||
283 | void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, | 358 | void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, |
284 | struct sta_info *sta) | 359 | struct sta_info *sta) |
285 | { | 360 | { |
diff --git a/net/mac80211/debugfs_key.h b/net/mac80211/debugfs_key.h index b1a3754ee240..54717b4e1371 100644 --- a/net/mac80211/debugfs_key.h +++ b/net/mac80211/debugfs_key.h | |||
@@ -6,6 +6,10 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key); | |||
6 | void ieee80211_debugfs_key_remove(struct ieee80211_key *key); | 6 | void ieee80211_debugfs_key_remove(struct ieee80211_key *key); |
7 | void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata); | 7 | void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata); |
8 | void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata); | 8 | void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata); |
9 | void ieee80211_debugfs_key_add_mgmt_default( | ||
10 | struct ieee80211_sub_if_data *sdata); | ||
11 | void ieee80211_debugfs_key_remove_mgmt_default( | ||
12 | struct ieee80211_sub_if_data *sdata); | ||
9 | void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, | 13 | void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, |
10 | struct sta_info *sta); | 14 | struct sta_info *sta); |
11 | #else | 15 | #else |
@@ -19,6 +23,12 @@ static inline void ieee80211_debugfs_key_add_default( | |||
19 | static inline void ieee80211_debugfs_key_remove_default( | 23 | static inline void ieee80211_debugfs_key_remove_default( |
20 | struct ieee80211_sub_if_data *sdata) | 24 | struct ieee80211_sub_if_data *sdata) |
21 | {} | 25 | {} |
26 | static inline void ieee80211_debugfs_key_add_mgmt_default( | ||
27 | struct ieee80211_sub_if_data *sdata) | ||
28 | {} | ||
29 | static inline void ieee80211_debugfs_key_remove_mgmt_default( | ||
30 | struct ieee80211_sub_if_data *sdata) | ||
31 | {} | ||
22 | static inline void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, | 32 | static inline void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, |
23 | struct sta_info *sta) | 33 | struct sta_info *sta) |
24 | {} | 34 | {} |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 20af92abd61d..8c3245717c55 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -409,8 +409,10 @@ struct ieee80211_sub_if_data { | |||
409 | unsigned int fragment_next; | 409 | unsigned int fragment_next; |
410 | 410 | ||
411 | #define NUM_DEFAULT_KEYS 4 | 411 | #define NUM_DEFAULT_KEYS 4 |
412 | struct ieee80211_key *keys[NUM_DEFAULT_KEYS]; | 412 | #define NUM_DEFAULT_MGMT_KEYS 2 |
413 | struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; | ||
413 | struct ieee80211_key *default_key; | 414 | struct ieee80211_key *default_key; |
415 | struct ieee80211_key *default_mgmt_key; | ||
414 | 416 | ||
415 | u16 sequence_number; | 417 | u16 sequence_number; |
416 | 418 | ||
@@ -482,6 +484,7 @@ struct ieee80211_sub_if_data { | |||
482 | } debugfs; | 484 | } debugfs; |
483 | struct { | 485 | struct { |
484 | struct dentry *default_key; | 486 | struct dentry *default_key; |
487 | struct dentry *default_mgmt_key; | ||
485 | } common_debugfs; | 488 | } common_debugfs; |
486 | 489 | ||
487 | #ifdef CONFIG_MAC80211_MESH | 490 | #ifdef CONFIG_MAC80211_MESH |
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index b0a025c9b615..19b480de4bbc 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 | /** |
@@ -215,13 +216,38 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) | |||
215 | spin_unlock_irqrestore(&sdata->local->key_lock, flags); | 216 | spin_unlock_irqrestore(&sdata->local->key_lock, flags); |
216 | } | 217 | } |
217 | 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 | |||
218 | 244 | ||
219 | static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | 245 | static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, |
220 | struct sta_info *sta, | 246 | struct sta_info *sta, |
221 | struct ieee80211_key *old, | 247 | struct ieee80211_key *old, |
222 | struct ieee80211_key *new) | 248 | struct ieee80211_key *new) |
223 | { | 249 | { |
224 | int idx, defkey; | 250 | int idx, defkey, defmgmtkey; |
225 | 251 | ||
226 | if (new) | 252 | if (new) |
227 | list_add(&new->list, &sdata->key_list); | 253 | list_add(&new->list, &sdata->key_list); |
@@ -237,13 +263,19 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | |||
237 | idx = new->conf.keyidx; | 263 | idx = new->conf.keyidx; |
238 | 264 | ||
239 | defkey = old && sdata->default_key == old; | 265 | defkey = old && sdata->default_key == old; |
266 | defmgmtkey = old && sdata->default_mgmt_key == old; | ||
240 | 267 | ||
241 | if (defkey && !new) | 268 | if (defkey && !new) |
242 | __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); | ||
243 | 272 | ||
244 | rcu_assign_pointer(sdata->keys[idx], new); | 273 | rcu_assign_pointer(sdata->keys[idx], new); |
245 | if (defkey && new) | 274 | if (defkey && new) |
246 | __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); | ||
247 | } | 279 | } |
248 | 280 | ||
249 | if (old) { | 281 | if (old) { |
@@ -262,7 +294,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | |||
262 | { | 294 | { |
263 | struct ieee80211_key *key; | 295 | struct ieee80211_key *key; |
264 | 296 | ||
265 | BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS); | 297 | BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS); |
266 | 298 | ||
267 | key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); | 299 | key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); |
268 | if (!key) | 300 | if (!key) |
@@ -291,6 +323,10 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | |||
291 | key->conf.iv_len = CCMP_HDR_LEN; | 323 | key->conf.iv_len = CCMP_HDR_LEN; |
292 | key->conf.icv_len = CCMP_MIC_LEN; | 324 | key->conf.icv_len = CCMP_MIC_LEN; |
293 | 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; | ||
294 | } | 330 | } |
295 | memcpy(key->conf.key, key_data, key_len); | 331 | memcpy(key->conf.key, key_data, key_len); |
296 | INIT_LIST_HEAD(&key->list); | 332 | INIT_LIST_HEAD(&key->list); |
@@ -308,6 +344,19 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | |||
308 | } | 344 | } |
309 | } | 345 | } |
310 | 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 | |||
311 | return key; | 360 | return key; |
312 | } | 361 | } |
313 | 362 | ||
@@ -461,6 +510,8 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) | |||
461 | 510 | ||
462 | if (key->conf.alg == ALG_CCMP) | 511 | if (key->conf.alg == ALG_CCMP) |
463 | 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); | ||
464 | ieee80211_debugfs_key_remove(key); | 515 | ieee80211_debugfs_key_remove(key); |
465 | 516 | ||
466 | kfree(key); | 517 | kfree(key); |
@@ -483,6 +534,7 @@ static void __ieee80211_key_todo(void) | |||
483 | list_del_init(&key->todo); | 534 | list_del_init(&key->todo); |
484 | todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | | 535 | todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | |
485 | KEY_FLAG_TODO_DEFKEY | | 536 | KEY_FLAG_TODO_DEFKEY | |
537 | KEY_FLAG_TODO_DEFMGMTKEY | | ||
486 | KEY_FLAG_TODO_HWACCEL_ADD | | 538 | KEY_FLAG_TODO_HWACCEL_ADD | |
487 | KEY_FLAG_TODO_HWACCEL_REMOVE | | 539 | KEY_FLAG_TODO_HWACCEL_REMOVE | |
488 | KEY_FLAG_TODO_DELETE); | 540 | KEY_FLAG_TODO_DELETE); |
@@ -500,6 +552,11 @@ static void __ieee80211_key_todo(void) | |||
500 | ieee80211_debugfs_key_add_default(key->sdata); | 552 | ieee80211_debugfs_key_add_default(key->sdata); |
501 | work_done = true; | 553 | work_done = true; |
502 | } | 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 | } | ||
503 | if (todoflags & KEY_FLAG_TODO_HWACCEL_ADD) { | 560 | if (todoflags & KEY_FLAG_TODO_HWACCEL_ADD) { |
504 | ieee80211_key_enable_hw_accel(key); | 561 | ieee80211_key_enable_hw_accel(key); |
505 | work_done = true; | 562 | work_done = true; |
@@ -535,6 +592,7 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) | |||
535 | ieee80211_key_lock(); | 592 | ieee80211_key_lock(); |
536 | 593 | ||
537 | ieee80211_debugfs_key_remove_default(sdata); | 594 | ieee80211_debugfs_key_remove_default(sdata); |
595 | ieee80211_debugfs_key_remove_mgmt_default(sdata); | ||
538 | 596 | ||
539 | spin_lock_irqsave(&sdata->local->key_lock, flags); | 597 | spin_lock_irqsave(&sdata->local->key_lock, flags); |
540 | list_for_each_entry_safe(key, tmp, &sdata->key_list, list) | 598 | list_for_each_entry_safe(key, tmp, &sdata->key_list, list) |
diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 73ac28ca2ede..215d3ef42a4f 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h | |||
@@ -46,6 +46,8 @@ struct sta_info; | |||
46 | * acceleration. | 46 | * acceleration. |
47 | * @KEY_FLAG_TODO_DEFKEY: Key is default key and debugfs needs to be updated. | 47 | * @KEY_FLAG_TODO_DEFKEY: Key is default key and debugfs needs to be updated. |
48 | * @KEY_FLAG_TODO_ADD_DEBUGFS: Key needs to be added to debugfs. | 48 | * @KEY_FLAG_TODO_ADD_DEBUGFS: Key needs to be added to debugfs. |
49 | * @KEY_FLAG_TODO_DEFMGMTKEY: Key is default management key and debugfs needs | ||
50 | * to be updated. | ||
49 | */ | 51 | */ |
50 | enum ieee80211_internal_key_flags { | 52 | enum ieee80211_internal_key_flags { |
51 | KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0), | 53 | KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0), |
@@ -54,6 +56,7 @@ enum ieee80211_internal_key_flags { | |||
54 | KEY_FLAG_TODO_HWACCEL_REMOVE = BIT(3), | 56 | KEY_FLAG_TODO_HWACCEL_REMOVE = BIT(3), |
55 | KEY_FLAG_TODO_DEFKEY = BIT(4), | 57 | KEY_FLAG_TODO_DEFKEY = BIT(4), |
56 | KEY_FLAG_TODO_ADD_DEBUGFS = BIT(5), | 58 | KEY_FLAG_TODO_ADD_DEBUGFS = BIT(5), |
59 | KEY_FLAG_TODO_DEFMGMTKEY = BIT(6), | ||
57 | }; | 60 | }; |
58 | 61 | ||
59 | struct tkip_ctx { | 62 | struct tkip_ctx { |
@@ -124,6 +127,7 @@ struct ieee80211_key { | |||
124 | struct dentry *tx_spec; | 127 | struct dentry *tx_spec; |
125 | struct dentry *rx_spec; | 128 | struct dentry *rx_spec; |
126 | struct dentry *replays; | 129 | struct dentry *replays; |
130 | struct dentry *icverrors; | ||
127 | struct dentry *key; | 131 | struct dentry *key; |
128 | struct dentry *ifindex; | 132 | struct dentry *ifindex; |
129 | int cnt; | 133 | int cnt; |
@@ -150,6 +154,8 @@ void ieee80211_key_link(struct ieee80211_key *key, | |||
150 | struct sta_info *sta); | 154 | struct sta_info *sta); |
151 | void ieee80211_key_free(struct ieee80211_key *key); | 155 | void ieee80211_key_free(struct ieee80211_key *key); |
152 | void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx); | 156 | void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx); |
157 | void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, | ||
158 | int idx); | ||
153 | void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata); | 159 | void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata); |
154 | void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata); | 160 | void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata); |
155 | void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata); | 161 | void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata); |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b68e082e99ce..abc3aa583ca6 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -446,6 +446,52 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) | |||
446 | return RX_CONTINUE; | 446 | return RX_CONTINUE; |
447 | } | 447 | } |
448 | 448 | ||
449 | |||
450 | static int ieee80211_is_unicast_robust_mgmt_frame(struct sk_buff *skb) | ||
451 | { | ||
452 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
453 | |||
454 | if (skb->len < 24 || is_multicast_ether_addr(hdr->addr1)) | ||
455 | return 0; | ||
456 | |||
457 | return ieee80211_is_robust_mgmt_frame(hdr); | ||
458 | } | ||
459 | |||
460 | |||
461 | static int ieee80211_is_multicast_robust_mgmt_frame(struct sk_buff *skb) | ||
462 | { | ||
463 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
464 | |||
465 | if (skb->len < 24 || !is_multicast_ether_addr(hdr->addr1)) | ||
466 | return 0; | ||
467 | |||
468 | return ieee80211_is_robust_mgmt_frame(hdr); | ||
469 | } | ||
470 | |||
471 | |||
472 | /* Get the BIP key index from MMIE; return -1 if this is not a BIP frame */ | ||
473 | static int ieee80211_get_mmie_keyidx(struct sk_buff *skb) | ||
474 | { | ||
475 | struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data; | ||
476 | struct ieee80211_mmie *mmie; | ||
477 | |||
478 | if (skb->len < 24 + sizeof(*mmie) || | ||
479 | !is_multicast_ether_addr(hdr->da)) | ||
480 | return -1; | ||
481 | |||
482 | if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr)) | ||
483 | return -1; /* not a robust management frame */ | ||
484 | |||
485 | mmie = (struct ieee80211_mmie *) | ||
486 | (skb->data + skb->len - sizeof(*mmie)); | ||
487 | if (mmie->element_id != WLAN_EID_MMIE || | ||
488 | mmie->length != sizeof(*mmie) - 2) | ||
489 | return -1; | ||
490 | |||
491 | return le16_to_cpu(mmie->key_id); | ||
492 | } | ||
493 | |||
494 | |||
449 | static ieee80211_rx_result | 495 | static ieee80211_rx_result |
450 | ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) | 496 | ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) |
451 | { | 497 | { |
@@ -561,21 +607,23 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) | |||
561 | int hdrlen; | 607 | int hdrlen; |
562 | ieee80211_rx_result result = RX_DROP_UNUSABLE; | 608 | ieee80211_rx_result result = RX_DROP_UNUSABLE; |
563 | struct ieee80211_key *stakey = NULL; | 609 | struct ieee80211_key *stakey = NULL; |
610 | int mmie_keyidx = -1; | ||
564 | 611 | ||
565 | /* | 612 | /* |
566 | * Key selection 101 | 613 | * Key selection 101 |
567 | * | 614 | * |
568 | * There are three types of keys: | 615 | * There are four types of keys: |
569 | * - GTK (group keys) | 616 | * - GTK (group keys) |
617 | * - IGTK (group keys for management frames) | ||
570 | * - PTK (pairwise keys) | 618 | * - PTK (pairwise keys) |
571 | * - STK (station-to-station pairwise keys) | 619 | * - STK (station-to-station pairwise keys) |
572 | * | 620 | * |
573 | * When selecting a key, we have to distinguish between multicast | 621 | * When selecting a key, we have to distinguish between multicast |
574 | * (including broadcast) and unicast frames, the latter can only | 622 | * (including broadcast) and unicast frames, the latter can only |
575 | * use PTKs and STKs while the former always use GTKs. Unless, of | 623 | * use PTKs and STKs while the former always use GTKs and IGTKs. |
576 | * course, actual WEP keys ("pre-RSNA") are used, then unicast | 624 | * Unless, of course, actual WEP keys ("pre-RSNA") are used, then |
577 | * frames can also use key indizes like GTKs. Hence, if we don't | 625 | * unicast frames can also use key indices like GTKs. Hence, if we |
578 | * have a PTK/STK we check the key index for a WEP key. | 626 | * don't have a PTK/STK we check the key index for a WEP key. |
579 | * | 627 | * |
580 | * Note that in a regular BSS, multicast frames are sent by the | 628 | * Note that in a regular BSS, multicast frames are sent by the |
581 | * AP only, associated stations unicast the frame to the AP first | 629 | * AP only, associated stations unicast the frame to the AP first |
@@ -588,8 +636,14 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) | |||
588 | * possible. | 636 | * possible. |
589 | */ | 637 | */ |
590 | 638 | ||
591 | if (!ieee80211_has_protected(hdr->frame_control)) | 639 | if (!ieee80211_has_protected(hdr->frame_control)) { |
592 | return RX_CONTINUE; | 640 | if (!ieee80211_is_mgmt(hdr->frame_control) || |
641 | rx->sta == NULL || !test_sta_flags(rx->sta, WLAN_STA_MFP)) | ||
642 | return RX_CONTINUE; | ||
643 | mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); | ||
644 | if (mmie_keyidx < 0) | ||
645 | return RX_CONTINUE; | ||
646 | } | ||
593 | 647 | ||
594 | /* | 648 | /* |
595 | * No point in finding a key and decrypting if the frame is neither | 649 | * No point in finding a key and decrypting if the frame is neither |
@@ -603,6 +657,16 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) | |||
603 | 657 | ||
604 | if (!is_multicast_ether_addr(hdr->addr1) && stakey) { | 658 | if (!is_multicast_ether_addr(hdr->addr1) && stakey) { |
605 | rx->key = stakey; | 659 | rx->key = stakey; |
660 | } else if (mmie_keyidx >= 0) { | ||
661 | /* Broadcast/multicast robust management frame / BIP */ | ||
662 | if ((rx->status->flag & RX_FLAG_DECRYPTED) && | ||
663 | (rx->status->flag & RX_FLAG_IV_STRIPPED)) | ||
664 | return RX_CONTINUE; | ||
665 | |||
666 | if (mmie_keyidx < NUM_DEFAULT_KEYS || | ||
667 | mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) | ||
668 | return RX_DROP_MONITOR; /* unexpected BIP keyidx */ | ||
669 | rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]); | ||
606 | } else { | 670 | } else { |
607 | /* | 671 | /* |
608 | * The device doesn't give us the IV so we won't be | 672 | * The device doesn't give us the IV so we won't be |
@@ -665,6 +729,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) | |||
665 | case ALG_CCMP: | 729 | case ALG_CCMP: |
666 | result = ieee80211_crypto_ccmp_decrypt(rx); | 730 | result = ieee80211_crypto_ccmp_decrypt(rx); |
667 | break; | 731 | break; |
732 | case ALG_AES_CMAC: | ||
733 | result = ieee80211_crypto_aes_cmac_decrypt(rx); | ||
734 | break; | ||
668 | } | 735 | } |
669 | 736 | ||
670 | /* either the frame has been decrypted or will be dropped */ | 737 | /* either the frame has been decrypted or will be dropped */ |
@@ -1112,6 +1179,15 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) | |||
1112 | /* Drop unencrypted frames if key is set. */ | 1179 | /* Drop unencrypted frames if key is set. */ |
1113 | if (unlikely(!ieee80211_has_protected(fc) && | 1180 | if (unlikely(!ieee80211_has_protected(fc) && |
1114 | !ieee80211_is_nullfunc(fc) && | 1181 | !ieee80211_is_nullfunc(fc) && |
1182 | (!ieee80211_is_mgmt(fc) || | ||
1183 | (ieee80211_is_unicast_robust_mgmt_frame(rx->skb) && | ||
1184 | rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP))) && | ||
1185 | (rx->key || rx->sdata->drop_unencrypted))) | ||
1186 | return -EACCES; | ||
1187 | /* BIP does not use Protected field, so need to check MMIE */ | ||
1188 | if (unlikely(rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP) && | ||
1189 | ieee80211_is_multicast_robust_mgmt_frame(rx->skb) && | ||
1190 | ieee80211_get_mmie_keyidx(rx->skb) < 0 && | ||
1115 | (rx->key || rx->sdata->drop_unencrypted))) | 1191 | (rx->key || rx->sdata->drop_unencrypted))) |
1116 | return -EACCES; | 1192 | return -EACCES; |
1117 | 1193 | ||
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 50c6c4fabea5..ad53ea9e9c77 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -425,6 +425,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) | |||
425 | tx->key = NULL; | 425 | tx->key = NULL; |
426 | else if (tx->sta && (key = rcu_dereference(tx->sta->key))) | 426 | else if (tx->sta && (key = rcu_dereference(tx->sta->key))) |
427 | tx->key = key; | 427 | tx->key = key; |
428 | else if (ieee80211_is_mgmt(hdr->frame_control) && | ||
429 | (key = rcu_dereference(tx->sdata->default_mgmt_key))) | ||
430 | tx->key = key; | ||
428 | else if ((key = rcu_dereference(tx->sdata->default_key))) | 431 | else if ((key = rcu_dereference(tx->sdata->default_key))) |
429 | tx->key = key; | 432 | tx->key = key; |
430 | else if (tx->sdata->drop_unencrypted && | 433 | else if (tx->sdata->drop_unencrypted && |
@@ -453,6 +456,10 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) | |||
453 | tx->skb)) | 456 | tx->skb)) |
454 | tx->key = NULL; | 457 | tx->key = NULL; |
455 | break; | 458 | break; |
459 | case ALG_AES_CMAC: | ||
460 | if (!ieee80211_is_mgmt(hdr->frame_control)) | ||
461 | tx->key = NULL; | ||
462 | break; | ||
456 | } | 463 | } |
457 | } | 464 | } |
458 | 465 | ||
@@ -808,6 +815,8 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) | |||
808 | return ieee80211_crypto_tkip_encrypt(tx); | 815 | return ieee80211_crypto_tkip_encrypt(tx); |
809 | case ALG_CCMP: | 816 | case ALG_CCMP: |
810 | return ieee80211_crypto_ccmp_encrypt(tx); | 817 | return ieee80211_crypto_ccmp_encrypt(tx); |
818 | case ALG_AES_CMAC: | ||
819 | return ieee80211_crypto_aes_cmac_encrypt(tx); | ||
811 | } | 820 | } |
812 | 821 | ||
813 | /* not reached */ | 822 | /* not reached */ |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1e728fff474e..123d3b160fad 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -738,7 +738,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) | |||
738 | if (info->attrs[NL80211_ATTR_KEY_IDX]) | 738 | if (info->attrs[NL80211_ATTR_KEY_IDX]) |
739 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | 739 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); |
740 | 740 | ||
741 | if (key_idx > 3) | 741 | if (key_idx > 5) |
742 | return -EINVAL; | 742 | return -EINVAL; |
743 | 743 | ||
744 | if (info->attrs[NL80211_ATTR_MAC]) | 744 | if (info->attrs[NL80211_ATTR_MAC]) |
@@ -804,30 +804,41 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | |||
804 | int err; | 804 | int err; |
805 | struct net_device *dev; | 805 | struct net_device *dev; |
806 | u8 key_idx; | 806 | u8 key_idx; |
807 | int (*func)(struct wiphy *wiphy, struct net_device *netdev, | ||
808 | u8 key_index); | ||
807 | 809 | ||
808 | if (!info->attrs[NL80211_ATTR_KEY_IDX]) | 810 | if (!info->attrs[NL80211_ATTR_KEY_IDX]) |
809 | return -EINVAL; | 811 | return -EINVAL; |
810 | 812 | ||
811 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | 813 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); |
812 | 814 | ||
813 | if (key_idx > 3) | 815 | if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) { |
816 | if (key_idx < 4 || key_idx > 5) | ||
817 | return -EINVAL; | ||
818 | } else if (key_idx > 3) | ||
814 | return -EINVAL; | 819 | return -EINVAL; |
815 | 820 | ||
816 | /* currently only support setting default key */ | 821 | /* currently only support setting default key */ |
817 | if (!info->attrs[NL80211_ATTR_KEY_DEFAULT]) | 822 | if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] && |
823 | !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) | ||
818 | return -EINVAL; | 824 | return -EINVAL; |
819 | 825 | ||
820 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | 826 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); |
821 | if (err) | 827 | if (err) |
822 | return err; | 828 | return err; |
823 | 829 | ||
824 | if (!drv->ops->set_default_key) { | 830 | if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) |
831 | func = drv->ops->set_default_key; | ||
832 | else | ||
833 | func = drv->ops->set_default_mgmt_key; | ||
834 | |||
835 | if (!func) { | ||
825 | err = -EOPNOTSUPP; | 836 | err = -EOPNOTSUPP; |
826 | goto out; | 837 | goto out; |
827 | } | 838 | } |
828 | 839 | ||
829 | rtnl_lock(); | 840 | rtnl_lock(); |
830 | err = drv->ops->set_default_key(&drv->wiphy, dev, key_idx); | 841 | err = func(&drv->wiphy, dev, key_idx); |
831 | rtnl_unlock(); | 842 | rtnl_unlock(); |
832 | 843 | ||
833 | out: | 844 | out: |
@@ -863,7 +874,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | |||
863 | if (info->attrs[NL80211_ATTR_MAC]) | 874 | if (info->attrs[NL80211_ATTR_MAC]) |
864 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | 875 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); |
865 | 876 | ||
866 | if (key_idx > 3) | 877 | if (key_idx > 5) |
867 | return -EINVAL; | 878 | return -EINVAL; |
868 | 879 | ||
869 | /* | 880 | /* |
@@ -894,6 +905,10 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | |||
894 | if (params.key_len != 13) | 905 | if (params.key_len != 13) |
895 | return -EINVAL; | 906 | return -EINVAL; |
896 | break; | 907 | break; |
908 | case WLAN_CIPHER_SUITE_AES_CMAC: | ||
909 | if (params.key_len != 16) | ||
910 | return -EINVAL; | ||
911 | break; | ||
897 | default: | 912 | default: |
898 | return -EINVAL; | 913 | return -EINVAL; |
899 | } | 914 | } |
@@ -928,7 +943,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) | |||
928 | if (info->attrs[NL80211_ATTR_KEY_IDX]) | 943 | if (info->attrs[NL80211_ATTR_KEY_IDX]) |
929 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | 944 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); |
930 | 945 | ||
931 | if (key_idx > 3) | 946 | if (key_idx > 5) |
932 | return -EINVAL; | 947 | return -EINVAL; |
933 | 948 | ||
934 | if (info->attrs[NL80211_ATTR_MAC]) | 949 | if (info->attrs[NL80211_ATTR_MAC]) |