Commit b9454e83 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

nl80211: introduce new key attributes

We will soon want to nest key attributes into
some new attribute for configuring static WEP
keys at connect() and ibss_join() time, so we
need nested attributes for that. However, key
attributes right now are 'global'. This patch
thus introduces new nested attributes for the
key settings and functions for parsing them.
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 10c836d7
...@@ -567,6 +567,9 @@ enum nl80211_commands { ...@@ -567,6 +567,9 @@ enum nl80211_commands {
* @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE
* commands to specify using a reassociate frame * commands to specify using a reassociate frame
* *
* @NL80211_ATTR_KEY: key information in a nested attribute with
* %NL80211_KEY_* sub-attributes
*
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
...@@ -692,6 +695,8 @@ enum nl80211_attrs { ...@@ -692,6 +695,8 @@ enum nl80211_attrs {
NL80211_ATTR_PREV_BSSID, NL80211_ATTR_PREV_BSSID,
NL80211_ATTR_KEY,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
...@@ -720,6 +725,7 @@ enum nl80211_attrs { ...@@ -720,6 +725,7 @@ enum nl80211_attrs {
#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP #define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP
#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS #define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS
#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES #define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES
#define NL80211_ATTR_KEY NL80211_ATTR_KEY
#define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_REG_RULES 32 #define NL80211_MAX_SUPP_REG_RULES 32
...@@ -1320,4 +1326,34 @@ enum nl80211_wpa_versions { ...@@ -1320,4 +1326,34 @@ enum nl80211_wpa_versions {
NL80211_WPA_VERSION_2 = 1 << 1, NL80211_WPA_VERSION_2 = 1 << 1,
}; };
/**
* enum nl80211_key_attributes - key attributes
* @__NL80211_KEY_INVALID: invalid
* @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of
* 16 bytes encryption key followed by 8 bytes each for TX and RX MIC
* keys
* @NL80211_KEY_IDX: key ID (u8, 0-3)
* @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
* section 7.3.2.25.1, e.g. 0x000FAC04)
* @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
* CCMP keys, each six bytes in little endian
* @NL80211_KEY_DEFAULT: flag indicating default key
* @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key
* @__NL80211_KEY_AFTER_LAST: internal
* @NL80211_KEY_MAX: highest key attribute
*/
enum nl80211_key_attributes {
__NL80211_KEY_INVALID,
NL80211_KEY_DATA,
NL80211_KEY_IDX,
NL80211_KEY_CIPHER,
NL80211_KEY_SEQ,
NL80211_KEY_DEFAULT,
NL80211_KEY_DEFAULT_MGMT,
/* keep last */
__NL80211_KEY_AFTER_LAST,
NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1
};
#endif /* __LINUX_NL80211_H */ #endif /* __LINUX_NL80211_H */
...@@ -73,6 +73,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { ...@@ -73,6 +73,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN }, [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
[NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN }, [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
[NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
[NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
.len = WLAN_MAX_KEY_LEN }, .len = WLAN_MAX_KEY_LEN },
[NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
...@@ -134,6 +135,18 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { ...@@ -134,6 +135,18 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
}; };
/* 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_IDX] = { .type = NLA_U8 },
[NL80211_KEY_CIPHER] = { .type = NLA_U32 },
[NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
[NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
[NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
};
/* IE validation */ /* IE validation */
static bool is_valid_ie_attr(const struct nlattr *attr) static bool is_valid_ie_attr(const struct nlattr *attr)
{ {
...@@ -198,6 +211,100 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, ...@@ -198,6 +211,100 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
/* netlink command implementations */ /* netlink command implementations */
struct key_parse {
struct key_params p;
int idx;
bool def, defmgmt;
};
static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
{
struct nlattr *tb[NL80211_KEY_MAX + 1];
int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
nl80211_key_policy);
if (err)
return err;
k->def = !!tb[NL80211_KEY_DEFAULT];
k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
if (tb[NL80211_KEY_IDX])
k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
if (tb[NL80211_KEY_DATA]) {
k->p.key = nla_data(tb[NL80211_KEY_DATA]);
k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
}
if (tb[NL80211_KEY_SEQ]) {
k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
}
if (tb[NL80211_KEY_CIPHER])
k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
return 0;
}
static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
{
if (info->attrs[NL80211_ATTR_KEY_DATA]) {
k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
}
if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
}
if (info->attrs[NL80211_ATTR_KEY_IDX])
k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
if (info->attrs[NL80211_ATTR_KEY_CIPHER])
k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
return 0;
}
static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
{
int err;
memset(k, 0, sizeof(*k));
k->idx = -1;
if (info->attrs[NL80211_ATTR_KEY])
err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
else
err = nl80211_parse_key_old(info, k);
if (err)
return err;
if (k->def && k->defmgmt)
return -EINVAL;
if (k->idx != -1) {
if (k->defmgmt) {
if (k->idx < 4 || k->idx > 5)
return -EINVAL;
} else if (k->def) {
if (k->idx < 0 || k->idx > 3)
return -EINVAL;
} else {
if (k->idx < 0 || k->idx > 5)
return -EINVAL;
}
}
return 0;
}
static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
struct cfg80211_registered_device *dev) struct cfg80211_registered_device *dev)
{ {
...@@ -943,10 +1050,12 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) ...@@ -943,10 +1050,12 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
struct get_key_cookie { struct get_key_cookie {
struct sk_buff *msg; struct sk_buff *msg;
int error; int error;
int idx;
}; };
static void get_key_callback(void *c, struct key_params *params) static void get_key_callback(void *c, struct key_params *params)
{ {
struct nlattr *key;
struct get_key_cookie *cookie = c; struct get_key_cookie *cookie = c;
if (params->key) if (params->key)
...@@ -961,6 +1070,26 @@ static void get_key_callback(void *c, struct key_params *params) ...@@ -961,6 +1070,26 @@ static void get_key_callback(void *c, struct key_params *params)
NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER, NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
params->cipher); params->cipher);
key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
if (!key)
goto nla_put_failure;
if (params->key)
NLA_PUT(cookie->msg, NL80211_KEY_DATA,
params->key_len, params->key);
if (params->seq)
NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
params->seq_len, params->seq);
if (params->cipher)
NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
params->cipher);
NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
nla_nest_end(cookie->msg, key);
return; return;
nla_put_failure: nla_put_failure:
cookie->error = 1; cookie->error = 1;
...@@ -1014,6 +1143,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1014,6 +1143,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
} }
cookie.msg = msg; cookie.msg = msg;
cookie.idx = key_idx;
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
...@@ -1049,26 +1179,21 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1049,26 +1179,21 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
{ {
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
struct key_parse key;
int err; int err;
struct net_device *dev; struct net_device *dev;
u8 key_idx;
int (*func)(struct wiphy *wiphy, struct net_device *netdev, int (*func)(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index); u8 key_index);
if (!info->attrs[NL80211_ATTR_KEY_IDX]) err = nl80211_parse_key(info, &key);
return -EINVAL; if (err)
return err;
key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) { if (key.idx < 0)
if (key_idx < 4 || key_idx > 5)
return -EINVAL;
} else if (key_idx > 3)
return -EINVAL; return -EINVAL;
/* currently only support setting default key */ /* only support setting default key */
if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] && if (!key.def && !key.defmgmt)
!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
return -EINVAL; return -EINVAL;
rtnl_lock(); rtnl_lock();
...@@ -1077,7 +1202,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1077,7 +1202,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
if (err) if (err)
goto unlock_rtnl; goto unlock_rtnl;
if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) if (key.def)
func = rdev->ops->set_default_key; func = rdev->ops->set_default_key;
else else
func = rdev->ops->set_default_mgmt_key; func = rdev->ops->set_default_mgmt_key;
...@@ -1087,13 +1212,13 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1087,13 +1212,13 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
goto out; goto out;
} }
err = func(&rdev->wiphy, dev, key_idx); err = func(&rdev->wiphy, dev, key.idx);
#ifdef CONFIG_WIRELESS_EXT #ifdef CONFIG_WIRELESS_EXT
if (!err) { if (!err) {
if (func == rdev->ops->set_default_key) if (func == rdev->ops->set_default_key)
dev->ieee80211_ptr->wext.default_key = key_idx; dev->ieee80211_ptr->wext.default_key = key.idx;
else else
dev->ieee80211_ptr->wext.default_mgmt_key = key_idx; dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
} }
#endif #endif
...@@ -1112,34 +1237,20 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1112,34 +1237,20 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
int err, i; int err, i;
struct net_device *dev; struct net_device *dev;
struct key_params params; struct key_parse key;
u8 key_idx = 0;
u8 *mac_addr = NULL; u8 *mac_addr = NULL;
memset(&params, 0, sizeof(params)); err = nl80211_parse_key(info, &key);
if (err)
return err;
if (!info->attrs[NL80211_ATTR_KEY_CIPHER]) if (!key.p.key)
return -EINVAL; return -EINVAL;
if (info->attrs[NL80211_ATTR_KEY_DATA]) {
params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
}
if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
params.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
params.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
}
if (info->attrs[NL80211_ATTR_KEY_IDX])
key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
if (info->attrs[NL80211_ATTR_MAC]) if (info->attrs[NL80211_ATTR_MAC])
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
if (cfg80211_validate_key_settings(&params, key_idx, mac_addr)) if (cfg80211_validate_key_settings(&key.p, key.idx, mac_addr))
return -EINVAL; return -EINVAL;
rtnl_lock(); rtnl_lock();
...@@ -1149,7 +1260,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1149,7 +1260,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
goto unlock_rtnl; goto unlock_rtnl;
for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) for (i = 0; i < rdev->wiphy.n_cipher_suites; i++)
if (params.cipher == rdev->wiphy.cipher_suites[i]) if (key.p.cipher == rdev->wiphy.cipher_suites[i])
break; break;
if (i == rdev->wiphy.n_cipher_suites) { if (i == rdev->wiphy.n_cipher_suites) {
err = -EINVAL; err = -EINVAL;
...@@ -1161,7 +1272,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1161,7 +1272,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
goto out; goto out;
} }
err = rdev->ops->add_key(&rdev->wiphy, dev, key_idx, mac_addr, &params); err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, mac_addr, &key.p);
out: out:
cfg80211_unlock_rdev(rdev); cfg80211_unlock_rdev(rdev);
...@@ -1177,14 +1288,12 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1177,14 +1288,12 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
int err; int err;
struct net_device *dev; struct net_device *dev;
u8 key_idx = 0;
u8 *mac_addr = NULL; u8 *mac_addr = NULL;
struct key_parse key;
if (info->attrs[NL80211_ATTR_KEY_IDX]) err = nl80211_parse_key(info, &key);
key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); if (err)
return err;
if (key_idx > 5)
return -EINVAL;
if (info->attrs[NL80211_ATTR_MAC]) if (info->attrs[NL80211_ATTR_MAC])
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
...@@ -1200,13 +1309,13 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1200,13 +1309,13 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
goto out; goto out;
} }
err = rdev->ops->del_key(&rdev->wiphy, dev, key_idx, mac_addr); err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
#ifdef CONFIG_WIRELESS_EXT #ifdef CONFIG_WIRELESS_EXT
if (!err) { if (!err) {
if (key_idx == dev->ieee80211_ptr->wext.default_key) if (key.idx == dev->ieee80211_ptr->wext.default_key)
dev->ieee80211_ptr->wext.default_key = -1; dev->ieee80211_ptr->wext.default_key = -1;
else if (key_idx == dev->ieee80211_ptr->wext.default_mgmt_key) else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
dev->ieee80211_ptr->wext.default_mgmt_key = -1; dev->ieee80211_ptr->wext.default_mgmt_key = -1;
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment