diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 170 |
1 files changed, 151 insertions, 19 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 50cf59316292..45c5f9c8e51b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -138,8 +138,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
138 | /* policy for the attributes */ | 138 | /* policy for the attributes */ |
139 | static struct nla_policy | 139 | static struct nla_policy |
140 | nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { | 140 | nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { |
141 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, | 141 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, |
142 | .len = WLAN_MAX_KEY_LEN }, | ||
143 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, | 142 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, |
144 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, | 143 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, |
145 | [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, | 144 | [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, |
@@ -305,6 +304,83 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) | |||
305 | return 0; | 304 | return 0; |
306 | } | 305 | } |
307 | 306 | ||
307 | static struct cfg80211_cached_keys * | ||
308 | nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, | ||
309 | struct nlattr *keys) | ||
310 | { | ||
311 | struct key_parse parse; | ||
312 | struct nlattr *key; | ||
313 | struct cfg80211_cached_keys *result; | ||
314 | int rem, err, def = 0; | ||
315 | |||
316 | result = kzalloc(sizeof(*result), GFP_KERNEL); | ||
317 | if (!result) | ||
318 | return ERR_PTR(-ENOMEM); | ||
319 | |||
320 | result->def = -1; | ||
321 | result->defmgmt = -1; | ||
322 | |||
323 | nla_for_each_nested(key, keys, rem) { | ||
324 | memset(&parse, 0, sizeof(parse)); | ||
325 | parse.idx = -1; | ||
326 | |||
327 | err = nl80211_parse_key_new(key, &parse); | ||
328 | if (err) | ||
329 | goto error; | ||
330 | err = -EINVAL; | ||
331 | if (!parse.p.key) | ||
332 | goto error; | ||
333 | if (parse.idx < 0 || parse.idx > 4) | ||
334 | goto error; | ||
335 | if (parse.def) { | ||
336 | if (def) | ||
337 | goto error; | ||
338 | def = 1; | ||
339 | result->def = parse.idx; | ||
340 | } else if (parse.defmgmt) | ||
341 | goto error; | ||
342 | err = cfg80211_validate_key_settings(rdev, &parse.p, | ||
343 | parse.idx, NULL); | ||
344 | if (err) | ||
345 | goto error; | ||
346 | result->params[parse.idx].cipher = parse.p.cipher; | ||
347 | result->params[parse.idx].key_len = parse.p.key_len; | ||
348 | result->params[parse.idx].key = result->data[parse.idx]; | ||
349 | memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); | ||
350 | } | ||
351 | |||
352 | return result; | ||
353 | error: | ||
354 | kfree(result); | ||
355 | return ERR_PTR(err); | ||
356 | } | ||
357 | |||
358 | static int nl80211_key_allowed(struct wireless_dev *wdev) | ||
359 | { | ||
360 | ASSERT_WDEV_LOCK(wdev); | ||
361 | |||
362 | if (!netif_running(wdev->netdev)) | ||
363 | return -ENETDOWN; | ||
364 | |||
365 | switch (wdev->iftype) { | ||
366 | case NL80211_IFTYPE_AP: | ||
367 | case NL80211_IFTYPE_AP_VLAN: | ||
368 | break; | ||
369 | case NL80211_IFTYPE_ADHOC: | ||
370 | if (!wdev->current_bss) | ||
371 | return -ENOLINK; | ||
372 | break; | ||
373 | case NL80211_IFTYPE_STATION: | ||
374 | if (wdev->sme_state != CFG80211_SME_CONNECTED) | ||
375 | return -ENOLINK; | ||
376 | break; | ||
377 | default: | ||
378 | return -EINVAL; | ||
379 | } | ||
380 | |||
381 | return 0; | ||
382 | } | ||
383 | |||
308 | static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | 384 | static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, |
309 | struct cfg80211_registered_device *dev) | 385 | struct cfg80211_registered_device *dev) |
310 | { | 386 | { |
@@ -1212,7 +1288,11 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | |||
1212 | goto out; | 1288 | goto out; |
1213 | } | 1289 | } |
1214 | 1290 | ||
1215 | err = func(&rdev->wiphy, dev, key.idx); | 1291 | wdev_lock(dev->ieee80211_ptr); |
1292 | err = nl80211_key_allowed(dev->ieee80211_ptr); | ||
1293 | if (!err) | ||
1294 | err = func(&rdev->wiphy, dev, key.idx); | ||
1295 | |||
1216 | #ifdef CONFIG_WIRELESS_EXT | 1296 | #ifdef CONFIG_WIRELESS_EXT |
1217 | if (!err) { | 1297 | if (!err) { |
1218 | if (func == rdev->ops->set_default_key) | 1298 | if (func == rdev->ops->set_default_key) |
@@ -1221,6 +1301,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | |||
1221 | dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; | 1301 | dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; |
1222 | } | 1302 | } |
1223 | #endif | 1303 | #endif |
1304 | wdev_unlock(dev->ieee80211_ptr); | ||
1224 | 1305 | ||
1225 | out: | 1306 | out: |
1226 | cfg80211_unlock_rdev(rdev); | 1307 | cfg80211_unlock_rdev(rdev); |
@@ -1235,7 +1316,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | |||
1235 | static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | 1316 | static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) |
1236 | { | 1317 | { |
1237 | struct cfg80211_registered_device *rdev; | 1318 | struct cfg80211_registered_device *rdev; |
1238 | int err, i; | 1319 | int err; |
1239 | struct net_device *dev; | 1320 | struct net_device *dev; |
1240 | struct key_parse key; | 1321 | struct key_parse key; |
1241 | u8 *mac_addr = NULL; | 1322 | u8 *mac_addr = NULL; |
@@ -1250,29 +1331,28 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | |||
1250 | if (info->attrs[NL80211_ATTR_MAC]) | 1331 | if (info->attrs[NL80211_ATTR_MAC]) |
1251 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | 1332 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); |
1252 | 1333 | ||
1253 | if (cfg80211_validate_key_settings(&key.p, key.idx, mac_addr)) | ||
1254 | return -EINVAL; | ||
1255 | |||
1256 | rtnl_lock(); | 1334 | rtnl_lock(); |
1257 | 1335 | ||
1258 | err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); | 1336 | err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); |
1259 | if (err) | 1337 | if (err) |
1260 | goto unlock_rtnl; | 1338 | goto unlock_rtnl; |
1261 | 1339 | ||
1262 | for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) | 1340 | if (!rdev->ops->add_key) { |
1263 | if (key.p.cipher == rdev->wiphy.cipher_suites[i]) | 1341 | err = -EOPNOTSUPP; |
1264 | break; | ||
1265 | if (i == rdev->wiphy.n_cipher_suites) { | ||
1266 | err = -EINVAL; | ||
1267 | goto out; | 1342 | goto out; |
1268 | } | 1343 | } |
1269 | 1344 | ||
1270 | if (!rdev->ops->add_key) { | 1345 | if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) { |
1271 | err = -EOPNOTSUPP; | 1346 | err = -EINVAL; |
1272 | goto out; | 1347 | goto out; |
1273 | } | 1348 | } |
1274 | 1349 | ||
1275 | err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, mac_addr, &key.p); | 1350 | wdev_lock(dev->ieee80211_ptr); |
1351 | err = nl80211_key_allowed(dev->ieee80211_ptr); | ||
1352 | if (!err) | ||
1353 | err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, | ||
1354 | mac_addr, &key.p); | ||
1355 | wdev_unlock(dev->ieee80211_ptr); | ||
1276 | 1356 | ||
1277 | out: | 1357 | out: |
1278 | cfg80211_unlock_rdev(rdev); | 1358 | cfg80211_unlock_rdev(rdev); |
@@ -1309,7 +1389,10 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) | |||
1309 | goto out; | 1389 | goto out; |
1310 | } | 1390 | } |
1311 | 1391 | ||
1312 | err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); | 1392 | wdev_lock(dev->ieee80211_ptr); |
1393 | err = nl80211_key_allowed(dev->ieee80211_ptr); | ||
1394 | if (!err) | ||
1395 | err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); | ||
1313 | 1396 | ||
1314 | #ifdef CONFIG_WIRELESS_EXT | 1397 | #ifdef CONFIG_WIRELESS_EXT |
1315 | if (!err) { | 1398 | if (!err) { |
@@ -1319,6 +1402,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) | |||
1319 | dev->ieee80211_ptr->wext.default_mgmt_key = -1; | 1402 | dev->ieee80211_ptr->wext.default_mgmt_key = -1; |
1320 | } | 1403 | } |
1321 | #endif | 1404 | #endif |
1405 | wdev_unlock(dev->ieee80211_ptr); | ||
1322 | 1406 | ||
1323 | out: | 1407 | out: |
1324 | cfg80211_unlock_rdev(rdev); | 1408 | cfg80211_unlock_rdev(rdev); |
@@ -3159,6 +3243,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) | |||
3159 | const u8 *bssid, *ssid, *ie = NULL; | 3243 | const u8 *bssid, *ssid, *ie = NULL; |
3160 | int err, ssid_len, ie_len = 0; | 3244 | int err, ssid_len, ie_len = 0; |
3161 | enum nl80211_auth_type auth_type; | 3245 | enum nl80211_auth_type auth_type; |
3246 | struct key_parse key; | ||
3162 | 3247 | ||
3163 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | 3248 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) |
3164 | return -EINVAL; | 3249 | return -EINVAL; |
@@ -3175,6 +3260,25 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) | |||
3175 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) | 3260 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) |
3176 | return -EINVAL; | 3261 | return -EINVAL; |
3177 | 3262 | ||
3263 | err = nl80211_parse_key(info, &key); | ||
3264 | if (err) | ||
3265 | return err; | ||
3266 | |||
3267 | if (key.idx >= 0) { | ||
3268 | if (!key.p.key || !key.p.key_len) | ||
3269 | return -EINVAL; | ||
3270 | if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || | ||
3271 | key.p.key_len != WLAN_KEY_LEN_WEP40) && | ||
3272 | (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || | ||
3273 | key.p.key_len != WLAN_KEY_LEN_WEP104)) | ||
3274 | return -EINVAL; | ||
3275 | if (key.idx > 4) | ||
3276 | return -EINVAL; | ||
3277 | } else { | ||
3278 | key.p.key_len = 0; | ||
3279 | key.p.key = NULL; | ||
3280 | } | ||
3281 | |||
3178 | rtnl_lock(); | 3282 | rtnl_lock(); |
3179 | 3283 | ||
3180 | err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); | 3284 | err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); |
@@ -3219,7 +3323,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) | |||
3219 | } | 3323 | } |
3220 | 3324 | ||
3221 | err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, | 3325 | err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, |
3222 | ssid, ssid_len, ie, ie_len); | 3326 | ssid, ssid_len, ie, ie_len, |
3327 | key.p.key, key.p.key_len, key.idx); | ||
3223 | 3328 | ||
3224 | out: | 3329 | out: |
3225 | cfg80211_unlock_rdev(rdev); | 3330 | cfg80211_unlock_rdev(rdev); |
@@ -3506,6 +3611,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) | |||
3506 | struct net_device *dev; | 3611 | struct net_device *dev; |
3507 | struct cfg80211_ibss_params ibss; | 3612 | struct cfg80211_ibss_params ibss; |
3508 | struct wiphy *wiphy; | 3613 | struct wiphy *wiphy; |
3614 | struct cfg80211_cached_keys *connkeys = NULL; | ||
3509 | int err; | 3615 | int err; |
3510 | 3616 | ||
3511 | memset(&ibss, 0, sizeof(ibss)); | 3617 | memset(&ibss, 0, sizeof(ibss)); |
@@ -3570,13 +3676,26 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) | |||
3570 | } | 3676 | } |
3571 | 3677 | ||
3572 | ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; | 3678 | ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; |
3679 | ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; | ||
3680 | |||
3681 | if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { | ||
3682 | connkeys = nl80211_parse_connkeys(rdev, | ||
3683 | info->attrs[NL80211_ATTR_KEYS]); | ||
3684 | if (IS_ERR(connkeys)) { | ||
3685 | err = PTR_ERR(connkeys); | ||
3686 | connkeys = NULL; | ||
3687 | goto out; | ||
3688 | } | ||
3689 | } | ||
3573 | 3690 | ||
3574 | err = cfg80211_join_ibss(rdev, dev, &ibss); | 3691 | err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); |
3575 | 3692 | ||
3576 | out: | 3693 | out: |
3577 | cfg80211_unlock_rdev(rdev); | 3694 | cfg80211_unlock_rdev(rdev); |
3578 | dev_put(dev); | 3695 | dev_put(dev); |
3579 | unlock_rtnl: | 3696 | unlock_rtnl: |
3697 | if (err) | ||
3698 | kfree(connkeys); | ||
3580 | rtnl_unlock(); | 3699 | rtnl_unlock(); |
3581 | return err; | 3700 | return err; |
3582 | } | 3701 | } |
@@ -3746,6 +3865,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) | |||
3746 | struct net_device *dev; | 3865 | struct net_device *dev; |
3747 | struct cfg80211_connect_params connect; | 3866 | struct cfg80211_connect_params connect; |
3748 | struct wiphy *wiphy; | 3867 | struct wiphy *wiphy; |
3868 | struct cfg80211_cached_keys *connkeys = NULL; | ||
3749 | int err; | 3869 | int err; |
3750 | 3870 | ||
3751 | memset(&connect, 0, sizeof(connect)); | 3871 | memset(&connect, 0, sizeof(connect)); |
@@ -3810,12 +3930,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) | |||
3810 | } | 3930 | } |
3811 | } | 3931 | } |
3812 | 3932 | ||
3813 | err = cfg80211_connect(rdev, dev, &connect); | 3933 | if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { |
3934 | connkeys = nl80211_parse_connkeys(rdev, | ||
3935 | info->attrs[NL80211_ATTR_KEYS]); | ||
3936 | if (IS_ERR(connkeys)) { | ||
3937 | err = PTR_ERR(connkeys); | ||
3938 | connkeys = NULL; | ||
3939 | goto out; | ||
3940 | } | ||
3941 | } | ||
3942 | |||
3943 | err = cfg80211_connect(rdev, dev, &connect, connkeys); | ||
3814 | 3944 | ||
3815 | out: | 3945 | out: |
3816 | cfg80211_unlock_rdev(rdev); | 3946 | cfg80211_unlock_rdev(rdev); |
3817 | dev_put(dev); | 3947 | dev_put(dev); |
3818 | unlock_rtnl: | 3948 | unlock_rtnl: |
3949 | if (err) | ||
3950 | kfree(connkeys); | ||
3819 | rtnl_unlock(); | 3951 | rtnl_unlock(); |
3820 | return err; | 3952 | return err; |
3821 | } | 3953 | } |