Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -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 */
+156 −47
Original line number Diff line number Diff line
@@ -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])
		return -EINVAL;

	key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
	err = nl80211_parse_key(info, &key);
	if (err)
		return err;

	if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
		if (key_idx < 4 || key_idx > 5)
			return -EINVAL;
	} else if (key_idx > 3)
	if (key.idx < 0)
		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(&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;

	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(&params, 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, &params);
	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