diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index e496a2daf7e..48e0913c220 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -567,6 +567,9 @@ enum nl80211_commands { * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE * 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_AFTER_LAST: internal use */ @@ -692,6 +695,8 @@ enum nl80211_attrs { NL80211_ATTR_PREV_BSSID, + NL80211_ATTR_KEY, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -720,6 +725,7 @@ enum nl80211_attrs { #define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP #define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS #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_REG_RULES 32 @@ -1320,4 +1326,34 @@ enum nl80211_wpa_versions { 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 */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a00fd644370..50cf5931629 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -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_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN }, + [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, @@ -134,6 +135,18 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [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 */ static bool is_valid_ie_attr(const struct nlattr *attr) { @@ -198,6 +211,100 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, /* 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, struct cfg80211_registered_device *dev) { @@ -943,10 +1050,12 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) struct get_key_cookie { struct sk_buff *msg; int error; + int idx; }; static void get_key_callback(void *c, struct key_params *params) { + struct nlattr *key; struct get_key_cookie *cookie = c; if (params->key) @@ -961,6 +1070,26 @@ static void get_key_callback(void *c, struct key_params *params) NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_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; nla_put_failure: cookie->error = 1; @@ -1014,6 +1143,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) } cookie.msg = msg; + cookie.idx = key_idx; NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); 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) static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; + struct key_parse key; int err; struct net_device *dev; - u8 key_idx; int (*func)(struct wiphy *wiphy, struct net_device *netdev, u8 key_index); - if (!info->attrs[NL80211_ATTR_KEY_IDX]) + err = nl80211_parse_key(info, &key); + if (err) + return err; + + if (key.idx < 0) return -EINVAL; - key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - - if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) { - if (key_idx < 4 || key_idx > 5) - return -EINVAL; - } else if (key_idx > 3) - return -EINVAL; - - /* currently only support setting default key */ - if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] && - !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) + /* only support setting default key */ + if (!key.def && !key.defmgmt) return -EINVAL; rtnl_lock(); @@ -1077,7 +1202,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (err) goto unlock_rtnl; - if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) + if (key.def) func = rdev->ops->set_default_key; else func = rdev->ops->set_default_mgmt_key; @@ -1087,13 +1212,13 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = func(&rdev->wiphy, dev, key_idx); + err = func(&rdev->wiphy, dev, key.idx); #ifdef CONFIG_WIRELESS_EXT if (!err) { if (func == rdev->ops->set_default_key) - dev->ieee80211_ptr->wext.default_key = key_idx; + dev->ieee80211_ptr->wext.default_key = key.idx; else - dev->ieee80211_ptr->wext.default_mgmt_key = key_idx; + dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; } #endif @@ -1112,34 +1237,20 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev; int err, i; struct net_device *dev; - struct key_params params; - u8 key_idx = 0; + struct key_parse key; u8 *mac_addr = NULL; - memset(¶ms, 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; - 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]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (cfg80211_validate_key_settings(¶ms, key_idx, mac_addr)) + if (cfg80211_validate_key_settings(&key.p, key.idx, mac_addr)) return -EINVAL; rtnl_lock(); @@ -1149,7 +1260,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) goto unlock_rtnl; 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; if (i == rdev->wiphy.n_cipher_suites) { err = -EINVAL; @@ -1161,7 +1272,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = rdev->ops->add_key(&rdev->wiphy, dev, key_idx, mac_addr, ¶ms); + err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, mac_addr, &key.p); out: cfg80211_unlock_rdev(rdev); @@ -1177,14 +1288,12 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev; int err; struct net_device *dev; - u8 key_idx = 0; u8 *mac_addr = NULL; + struct key_parse key; - if (info->attrs[NL80211_ATTR_KEY_IDX]) - key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - - if (key_idx > 5) - return -EINVAL; + err = nl80211_parse_key(info, &key); + if (err) + return err; if (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) 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 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; - 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; } #endif