diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.c | 11 | ||||
-rw-r--r-- | net/wireless/core.h | 32 | ||||
-rw-r--r-- | net/wireless/ibss.c | 79 | ||||
-rw-r--r-- | net/wireless/mlme.c | 16 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 170 | ||||
-rw-r--r-- | net/wireless/sme.c | 97 | ||||
-rw-r--r-- | net/wireless/util.c | 41 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 163 | ||||
-rw-r--r-- | net/wireless/wext-sme.c | 30 |
9 files changed, 495 insertions, 144 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index 1a78b3c70cf..97cc5968b7d 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -666,14 +666,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, wdev_lock(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - if (wdev->wext.ibss.ssid_len) - __cfg80211_join_ibss(rdev, dev, - &wdev->wext.ibss); + cfg80211_ibss_wext_join(rdev, wdev); break; case NL80211_IFTYPE_STATION: - if (wdev->wext.connect.ssid_len) - __cfg80211_connect(rdev, dev, - &wdev->wext.connect); + cfg80211_mgd_wext_connect(rdev, wdev); break; default: break; @@ -690,6 +686,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, } mutex_unlock(&rdev->devlist_mtx); mutex_destroy(&wdev->mtx); +#ifdef CONFIG_WIRELESS_EXT + kfree(wdev->wext.keys); +#endif break; case NETDEV_PRE_UP: if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) diff --git a/net/wireless/core.h b/net/wireless/core.h index e46cd6eb61d..2ec8ddbe57d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -238,6 +238,12 @@ struct cfg80211_event { }; }; +struct cfg80211_cached_keys { + struct key_params params[6]; + u8 data[6][WLAN_MAX_KEY_LEN]; + int def, defmgmt; +}; + /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); @@ -256,14 +262,18 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev, /* IBSS */ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params); + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys); int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params); + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys); void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); +int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); /* MLME */ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, @@ -272,12 +282,14 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len); + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx); int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len); + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx); int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, @@ -310,10 +322,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, /* SME */ int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect); + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect); + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys); int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); @@ -323,11 +337,14 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len); +int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); void cfg80211_conn_work(struct work_struct *work); /* internal helpers */ -int cfg80211_validate_key_settings(struct key_params *params, int key_idx, +int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, + struct key_params *params, int key_idx, const u8 *mac_addr); void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap); @@ -335,5 +352,6 @@ void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); +void cfg80211_upload_connect_keys(struct wireless_dev *wdev); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 99ef9364b7e..9394e78cd11 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -39,6 +39,8 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); + cfg80211_upload_connect_keys(wdev); + nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT @@ -71,7 +73,8 @@ EXPORT_SYMBOL(cfg80211_ibss_joined); int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params) + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -81,13 +84,18 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, if (wdev->ssid_len) return -EALREADY; + if (WARN_ON(wdev->connect_keys)) + kfree(wdev->connect_keys); + wdev->connect_keys = connkeys; + #ifdef CONFIG_WIRELESS_EXT wdev->wext.ibss.channel = params->channel; #endif err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); - - if (err) + if (err) { + wdev->connect_keys = NULL; return err; + } memcpy(wdev->ssid, params->ssid, params->ssid_len); wdev->ssid_len = params->ssid_len; @@ -97,13 +105,14 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params) + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; wdev_lock(wdev); - err = __cfg80211_join_ibss(rdev, dev, params); + err = __cfg80211_join_ibss(rdev, dev, params, connkeys); wdev_unlock(wdev); return err; @@ -112,9 +121,22 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int i; ASSERT_WDEV_LOCK(wdev); + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + + /* + * Delete all the keys ... pairwise keys can't really + * exist any more anyway, but default keys might. + */ + if (rdev->ops->del_key) + for (i = 0; i < 6; i++) + rdev->ops->del_key(wdev->wiphy, dev, i, NULL); + if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); @@ -172,11 +194,14 @@ int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, } #ifdef CONFIG_WIRELESS_EXT -static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) +int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) { + struct cfg80211_cached_keys *ck = NULL; enum ieee80211_band band; - int i; + int i, err; + + ASSERT_WDEV_LOCK(wdev); if (!wdev->wext.ibss.beacon_interval) wdev->wext.ibss.beacon_interval = 100; @@ -216,8 +241,24 @@ static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, if (!netif_running(wdev->netdev)) return 0; - return cfg80211_join_ibss(wiphy_to_dev(wdev->wiphy), - wdev->netdev, &wdev->wext.ibss); + if (wdev->wext.keys) + wdev->wext.keys->def = wdev->wext.default_key; + + wdev->wext.ibss.privacy = wdev->wext.default_key != -1; + + if (wdev->wext.keys) { + ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); + if (!ck) + return -ENOMEM; + for (i = 0; i < 6; i++) + ck->params[i].key = ck->data[i]; + } + err = __cfg80211_join_ibss(rdev, wdev->netdev, + &wdev->wext.ibss, ck); + if (err) + kfree(ck); + + return err; } int cfg80211_ibss_wext_siwfreq(struct net_device *dev, @@ -265,7 +306,11 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, wdev->wext.ibss.channel_fixed = false; } - return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_unlock(wdev); + + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq); @@ -333,7 +378,11 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, memcpy(wdev->wext.ibss.ssid, ssid, len); wdev->wext.ibss.ssid_len = len; - return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_unlock(wdev); + + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid); @@ -414,7 +463,11 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, } else wdev->wext.ibss.bssid = NULL; - return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_unlock(wdev); + + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 1b2ca1fea7a..8e4ce2fdf86 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -328,7 +328,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len) + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req; @@ -337,6 +338,10 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, ASSERT_WDEV_LOCK(wdev); + if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) + if (!key || !key_len || key_idx < 0 || key_idx > 4) + return -EINVAL; + if (wdev->current_bss && memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) return -EALREADY; @@ -359,6 +364,9 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, req.auth_type = auth_type; req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + req.key = key; + req.key_len = key_len; + req.key_idx = key_idx; if (!req.bss) return -ENOENT; @@ -396,13 +404,15 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len) + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx) { int err; wdev_lock(dev->ieee80211_ptr); err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, - ssid, ssid_len, ie, ie_len); + ssid, ssid_len, ie, ie_len, + key, key_len, key_idx); wdev_unlock(dev->ieee80211_ptr); return err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 50cf5931629..45c5f9c8e51 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 = { /* policy for the attributes */ static struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { - [NL80211_KEY_DATA] = { .type = NLA_BINARY, - .len = WLAN_MAX_KEY_LEN }, + [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, [NL80211_KEY_IDX] = { .type = NLA_U8 }, [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, [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) return 0; } +static struct cfg80211_cached_keys * +nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, + struct nlattr *keys) +{ + struct key_parse parse; + struct nlattr *key; + struct cfg80211_cached_keys *result; + int rem, err, def = 0; + + result = kzalloc(sizeof(*result), GFP_KERNEL); + if (!result) + return ERR_PTR(-ENOMEM); + + result->def = -1; + result->defmgmt = -1; + + nla_for_each_nested(key, keys, rem) { + memset(&parse, 0, sizeof(parse)); + parse.idx = -1; + + err = nl80211_parse_key_new(key, &parse); + if (err) + goto error; + err = -EINVAL; + if (!parse.p.key) + goto error; + if (parse.idx < 0 || parse.idx > 4) + goto error; + if (parse.def) { + if (def) + goto error; + def = 1; + result->def = parse.idx; + } else if (parse.defmgmt) + goto error; + err = cfg80211_validate_key_settings(rdev, &parse.p, + parse.idx, NULL); + if (err) + goto error; + result->params[parse.idx].cipher = parse.p.cipher; + result->params[parse.idx].key_len = parse.p.key_len; + result->params[parse.idx].key = result->data[parse.idx]; + memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); + } + + return result; + error: + kfree(result); + return ERR_PTR(err); +} + +static int nl80211_key_allowed(struct wireless_dev *wdev) +{ + ASSERT_WDEV_LOCK(wdev); + + if (!netif_running(wdev->netdev)) + return -ENETDOWN; + + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + break; + case NL80211_IFTYPE_ADHOC: + if (!wdev->current_bss) + return -ENOLINK; + break; + case NL80211_IFTYPE_STATION: + if (wdev->sme_state != CFG80211_SME_CONNECTED) + return -ENOLINK; + break; + default: + return -EINVAL; + } + + return 0; +} + static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *dev) { @@ -1212,7 +1288,11 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = func(&rdev->wiphy, dev, key.idx); + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (!err) + err = func(&rdev->wiphy, dev, key.idx); + #ifdef CONFIG_WIRELESS_EXT if (!err) { if (func == rdev->ops->set_default_key) @@ -1221,6 +1301,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; } #endif + wdev_unlock(dev->ieee80211_ptr); out: cfg80211_unlock_rdev(rdev); @@ -1235,7 +1316,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; - int err, i; + int err; struct net_device *dev; struct key_parse key; u8 *mac_addr = NULL; @@ -1250,29 +1331,28 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (cfg80211_validate_key_settings(&key.p, key.idx, mac_addr)) - return -EINVAL; - rtnl_lock(); err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) - if (key.p.cipher == rdev->wiphy.cipher_suites[i]) - break; - if (i == rdev->wiphy.n_cipher_suites) { - err = -EINVAL; + if (!rdev->ops->add_key) { + err = -EOPNOTSUPP; goto out; } - if (!rdev->ops->add_key) { - err = -EOPNOTSUPP; + if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) { + err = -EINVAL; goto out; } - err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, mac_addr, &key.p); + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (!err) + err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, + mac_addr, &key.p); + wdev_unlock(dev->ieee80211_ptr); out: cfg80211_unlock_rdev(rdev); @@ -1309,7 +1389,10 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (!err) + err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); #ifdef CONFIG_WIRELESS_EXT if (!err) { @@ -1319,6 +1402,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->wext.default_mgmt_key = -1; } #endif + wdev_unlock(dev->ieee80211_ptr); out: cfg80211_unlock_rdev(rdev); @@ -3159,6 +3243,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) const u8 *bssid, *ssid, *ie = NULL; int err, ssid_len, ie_len = 0; enum nl80211_auth_type auth_type; + struct key_parse key; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3175,6 +3260,25 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; + err = nl80211_parse_key(info, &key); + if (err) + return err; + + if (key.idx >= 0) { + if (!key.p.key || !key.p.key_len) + return -EINVAL; + if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || + key.p.key_len != WLAN_KEY_LEN_WEP40) && + (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || + key.p.key_len != WLAN_KEY_LEN_WEP104)) + return -EINVAL; + if (key.idx > 4) + return -EINVAL; + } else { + key.p.key_len = 0; + key.p.key = NULL; + } + rtnl_lock(); 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) } err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, - ssid, ssid_len, ie, ie_len); + ssid, ssid_len, ie, ie_len, + key.p.key, key.p.key_len, key.idx); out: cfg80211_unlock_rdev(rdev); @@ -3506,6 +3611,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) struct net_device *dev; struct cfg80211_ibss_params ibss; struct wiphy *wiphy; + struct cfg80211_cached_keys *connkeys = NULL; int err; memset(&ibss, 0, sizeof(ibss)); @@ -3570,13 +3676,26 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) } ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; + ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; + + if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { + connkeys = nl80211_parse_connkeys(rdev, + info->attrs[NL80211_ATTR_KEYS]); + if (IS_ERR(connkeys)) { + err = PTR_ERR(connkeys); + connkeys = NULL; + goto out; + } + } - err = cfg80211_join_ibss(rdev, dev, &ibss); + err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); out: cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: + if (err) + kfree(connkeys); rtnl_unlock(); return err; } @@ -3746,6 +3865,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) struct net_device *dev; struct cfg80211_connect_params connect; struct wiphy *wiphy; + struct cfg80211_cached_keys *connkeys = NULL; int err; memset(&connect, 0, sizeof(connect)); @@ -3810,12 +3930,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_connect(rdev, dev, &connect); + if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { + connkeys = nl80211_parse_connkeys(rdev, + info->attrs[NL80211_ATTR_KEYS]); + if (IS_ERR(connkeys)) { + err = PTR_ERR(connkeys); + connkeys = NULL; + goto out; + } + } + + err = cfg80211_connect(rdev, dev, &connect, connkeys); out: cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: + if (err) + kfree(connkeys); rtnl_unlock(); return err; } diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 79ca56cbfd3..d635a99dba5 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -125,7 +125,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) params->channel, params->auth_type, params->bssid, params->ssid, params->ssid_len, - NULL, 0); + NULL, 0, + params->key, params->key_len, + params->key_idx); case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; @@ -279,8 +281,12 @@ void cfg80211_sme_rx_auth(struct net_device *dev, /* select automatically between only open, shared, leap */ switch (wdev->conn->params.auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_SHARED_KEY; + if (wdev->connect_keys) + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_SHARED_KEY; + else + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_NETWORK_EAP; break; case NL80211_AUTHTYPE_SHARED_KEY: wdev->conn->params.auth_type = @@ -353,10 +359,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, #endif if (status == WLAN_STATUS_SUCCESS && - wdev->sme_state == CFG80211_SME_IDLE) { - wdev->sme_state = CFG80211_SME_CONNECTED; - return; - } + wdev->sme_state == CFG80211_SME_IDLE) + goto success; if (wdev->sme_state != CFG80211_SME_CONNECTING) return; @@ -370,24 +374,29 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (wdev->conn) wdev->conn->state = CFG80211_CONN_IDLE; - if (status == WLAN_STATUS_SUCCESS) { - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, - WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); - - if (WARN_ON(!bss)) - return; - - cfg80211_hold_bss(bss_from_pub(bss)); - wdev->current_bss = bss_from_pub(bss); - - wdev->sme_state = CFG80211_SME_CONNECTED; - } else { + if (status != WLAN_STATUS_SUCCESS) { wdev->sme_state = CFG80211_SME_IDLE; kfree(wdev->conn); wdev->conn = NULL; + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + return; } + + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + + if (WARN_ON(!bss)) + return; + + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); + + success: + wdev->sme_state = CFG80211_SME_CONNECTED; + cfg80211_upload_connect_keys(wdev); } void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, @@ -516,6 +525,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int i; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif @@ -543,8 +554,15 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->conn = NULL; } - nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, - reason, ie, ie_len, from_ap); + nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); + + /* + * Delete all the keys ... pairwise keys can't really + * exist any more anyway, but default keys might. + */ + if (rdev->ops->del_key) + for (i = 0; i < 6; i++) + rdev->ops->del_key(wdev->wiphy, dev, i, NULL); #ifdef CONFIG_WIRELESS_EXT memset(&wrqu, 0, sizeof(wrqu)); @@ -580,7 +598,8 @@ EXPORT_SYMBOL(cfg80211_disconnected); int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect) + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -590,6 +609,24 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; + if (WARN_ON(wdev->connect_keys)) { + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + } + + if (connkeys && connkeys->def >= 0) { + int idx; + + idx = connkeys->def; + /* If given a WEP key we may need it for shared key auth */ + if (connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP40 || + connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP104) { + connect->key_idx = idx; + connect->key = connkeys->params[idx].key; + connect->key_len = connkeys->params[idx].key_len; + } + } + if (!rdev->ops->connect) { if (!rdev->ops->auth || !rdev->ops->assoc) return -EOPNOTSUPP; @@ -640,6 +677,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, cfg80211_get_conn_bss(wdev); wdev->sme_state = CFG80211_SME_CONNECTING; + wdev->connect_keys = connkeys; /* we're good if we have both BSSID and channel */ if (wdev->conn->params.bssid && wdev->conn->params.channel) { @@ -662,13 +700,16 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, kfree(wdev->conn); wdev->conn = NULL; wdev->sme_state = CFG80211_SME_IDLE; + wdev->connect_keys = NULL; } return err; } else { wdev->sme_state = CFG80211_SME_CONNECTING; + wdev->connect_keys = connkeys; err = rdev->ops->connect(&rdev->wiphy, dev, connect); if (err) { + wdev->connect_keys = NULL; wdev->sme_state = CFG80211_SME_IDLE; return err; } @@ -682,12 +723,13 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect) + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys) { int err; wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_connect(rdev, dev, connect); + err = __cfg80211_connect(rdev, dev, connect, connkeys); wdev_unlock(dev->ieee80211_ptr); return err; @@ -704,6 +746,9 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, if (wdev->sme_state == CFG80211_SME_IDLE) return -EINVAL; + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + if (!rdev->ops->disconnect) { if (!rdev->ops->deauth) return -EOPNOTSUPP; diff --git a/net/wireless/util.c b/net/wireless/util.c index 28f8f96801d..4bab380a120 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -141,9 +141,12 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy) set_mandatory_flags_band(wiphy->bands[band], band); } -int cfg80211_validate_key_settings(struct key_params *params, int key_idx, +int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, + struct key_params *params, int key_idx, const u8 *mac_addr) { + int i; + if (key_idx > 5) return -EINVAL; @@ -197,6 +200,12 @@ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, } } + for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) + if (params->cipher == rdev->wiphy.cipher_suites[i]) + break; + if (i == rdev->wiphy.n_cipher_suites) + return -EINVAL; + return 0; } @@ -523,3 +532,33 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) return NULL; } EXPORT_SYMBOL(ieee80211_bss_get_ie); + +void cfg80211_upload_connect_keys(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct net_device *dev = wdev->netdev; + int i; + + if (!wdev->connect_keys) + return; + + for (i = 0; i < 6; i++) { + if (!wdev->connect_keys->params[i].cipher) + continue; + if (rdev->ops->add_key(wdev->wiphy, dev, i, NULL, + &wdev->connect_keys->params[i])) + printk(KERN_ERR "%s: failed to set key %d\n", + dev->name, i); + if (wdev->connect_keys->def == i) + if (rdev->ops->set_default_key(wdev->wiphy, dev, i)) + printk(KERN_ERR "%s: failed to set defkey %d\n", + dev->name, i); + if (wdev->connect_keys->defmgmt == i) + if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) + printk(KERN_ERR "%s: failed to set mgtdef %d\n", + dev->name, i); + } + + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; +} diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 5088d89a30f..5d017633853 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -453,15 +453,32 @@ int cfg80211_wext_giwretry(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry); -static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *addr, - bool remove, bool tx_key, int idx, - struct key_params *params) +static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *addr, + bool remove, bool tx_key, int idx, + struct key_params *params) { struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; + int err, i; + + if (!wdev->wext.keys) { + wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys), + GFP_KERNEL); + if (!wdev->wext.keys) + return -ENOMEM; + for (i = 0; i < 6; i++) + wdev->wext.keys->params[i].key = + wdev->wext.keys->data[i]; + } + + if (wdev->iftype != NL80211_IFTYPE_ADHOC && + wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + if (!wdev->current_bss) + return -ENOLINK; + if (!rdev->ops->set_default_mgmt_key) return -EOPNOTSUPP; @@ -471,8 +488,14 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, return -EINVAL; if (remove) { - err = rdev->ops->del_key(&rdev->wiphy, dev, idx, addr); + err = 0; + if (wdev->current_bss) + err = rdev->ops->del_key(&rdev->wiphy, dev, idx, addr); if (!err) { + if (!addr) { + wdev->wext.keys->params[idx].key_len = 0; + wdev->wext.keys->params[idx].cipher = 0; + } if (idx == wdev->wext.default_key) wdev->wext.default_key = -1; else if (idx == wdev->wext.default_mgmt_key) @@ -486,36 +509,64 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, return 0; return err; - } else { - if (addr) - tx_key = false; + } - if (cfg80211_validate_key_settings(params, idx, addr)) - return -EINVAL; + if (addr) + tx_key = false; + if (cfg80211_validate_key_settings(rdev, params, idx, addr)) + return -EINVAL; + + err = 0; + if (wdev->current_bss) err = rdev->ops->add_key(&rdev->wiphy, dev, idx, addr, params); - if (err) - return err; + if (err) + return err; + + if (!addr) { + wdev->wext.keys->params[idx] = *params; + memcpy(wdev->wext.keys->data[idx], + params->key, params->key_len); + wdev->wext.keys->params[idx].key = + wdev->wext.keys->data[idx]; + } - if (tx_key || (!addr && wdev->wext.default_key == -1)) { + if (params->cipher != WLAN_CIPHER_SUITE_AES_CMAC && + (tx_key || (!addr && wdev->wext.default_key == -1))) { + if (wdev->current_bss) err = rdev->ops->set_default_key(&rdev->wiphy, dev, idx); - if (!err) - wdev->wext.default_key = idx; - return err; - } + if (!err) + wdev->wext.default_key = idx; + return err; + } - if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC && - (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) { + if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC && + (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) { + if (wdev->current_bss) err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, dev, idx); - if (!err) - wdev->wext.default_mgmt_key = idx; - return err; - } - - return 0; + if (!err) + wdev->wext.default_mgmt_key = idx; + return err; } + + return 0; +} + +static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *addr, + bool remove, bool tx_key, int idx, + struct key_params *params) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_set_encryption(rdev, dev, addr, remove, + tx_key, idx, params); + wdev_unlock(dev->ieee80211_ptr); + + return err; } int cfg80211_wext_siwencode(struct net_device *dev, @@ -528,6 +579,10 @@ int cfg80211_wext_siwencode(struct net_device *dev, bool remove = false; struct key_params params; + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + /* no use -- only MFP (set_default_mgmt_key) is optional */ if (!rdev->ops->del_key || !rdev->ops->add_key || @@ -548,9 +603,14 @@ int cfg80211_wext_siwencode(struct net_device *dev, remove = true; else if (erq->length == 0) { /* No key data - just set the default TX key index */ - err = rdev->ops->set_default_key(&rdev->wiphy, dev, idx); + err = 0; + wdev_lock(wdev); + if (wdev->current_bss) + err = rdev->ops->set_default_key(&rdev->wiphy, + dev, idx); if (!err) wdev->wext.default_key = idx; + wdev_unlock(wdev); return err; } @@ -583,6 +643,10 @@ int cfg80211_wext_siwencodeext(struct net_device *dev, struct key_params params; u32 cipher; + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + /* no use -- only MFP (set_default_mgmt_key) is optional */ if (!rdev->ops->del_key || !rdev->ops->add_key || @@ -656,37 +720,15 @@ int cfg80211_wext_siwencodeext(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_siwencodeext); -struct giwencode_cookie { - size_t buflen; - char *keybuf; -}; - -static void giwencode_get_key_cb(void *cookie, struct key_params *params) -{ - struct giwencode_cookie *data = cookie; - - if (!params->key) { - data->buflen = 0; - return; - } - - data->buflen = min_t(size_t, data->buflen, params->key_len); - memcpy(data->keybuf, params->key, data->buflen); -} - int cfg80211_wext_giwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int idx, err; - struct giwencode_cookie data = { - .keybuf = keybuf, - .buflen = erq->length, - }; + int idx; - if (!rdev->ops->get_key) + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; idx = erq->flags & IW_ENCODE_INDEX; @@ -701,21 +743,18 @@ int cfg80211_wext_giwencode(struct net_device *dev, erq->flags = idx + 1; - err = rdev->ops->get_key(&rdev->wiphy, dev, idx, NULL, &data, - giwencode_get_key_cb); - if (!err) { - erq->length = data.buflen; - erq->flags |= IW_ENCODE_ENABLED; - return 0; - } - - if (err == -ENOENT) { + if (!wdev->wext.keys || !wdev->wext.keys->params[idx].cipher) { erq->flags |= IW_ENCODE_DISABLED; erq->length = 0; return 0; } - return err; + erq->length = min_t(size_t, erq->length, + wdev->wext.keys->params[idx].key_len); + memcpy(keybuf, wdev->wext.keys->params[idx].key, erq->length); + erq->flags |= IW_ENCODE_ENABLED; + + return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwencode); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 6f75aaa7f79..c33ea9a5de7 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -10,10 +10,11 @@ #include <net/cfg80211.h> #include "nl80211.h" -static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) +int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) { - int err; + struct cfg80211_cached_keys *ck = NULL; + int err, i; ASSERT_RDEV_LOCK(rdev); ASSERT_WDEV_LOCK(wdev); @@ -25,10 +26,25 @@ static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, wdev->wext.connect.ie_len = wdev->wext.ie_len; wdev->wext.connect.privacy = wdev->wext.default_key != -1; - err = 0; - if (wdev->wext.connect.ssid_len != 0) - err = __cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect); + if (wdev->wext.keys) { + wdev->wext.keys->def = wdev->wext.default_key; + wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; + } + + if (!wdev->wext.connect.ssid_len) + return 0; + + if (wdev->wext.keys) { + ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); + if (!ck) + return -ENOMEM; + for (i = 0; i < 6; i++) + ck->params[i].key = ck->data[i]; + } + err = __cfg80211_connect(rdev, wdev->netdev, + &wdev->wext.connect, ck); + if (err) + kfree(ck); return err; } |