Commit 31df0cff authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'netlink-nested-policy-validation'



Johannes Berg says:

====================
netlink: nested policy validation

This adds nested policy validation, which lets you specify the
nested attribute type, e.g. NLA_NESTED with sub-policy, or the
new NLA_NESTED_ARRAY with sub-sub-policy.

Changes in v2:
 * move setting the bad attr pointer/message into validate_nla()
 * remove the recursion patch since that's no longer needed
 * simply skip the generic bad attr pointer/message setting in
   case of nested nla_validate() failing since that could fail
   only due to validate_nla() failing inside, which already sets
   the extack information

Changes in v3:
 * fix NLA_REJECT to have an error message if none is in policy
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1042caa7 1501d135
Loading
Loading
Loading
Loading
+22 −5
Original line number Diff line number Diff line
@@ -172,7 +172,7 @@ enum {
	NLA_FLAG,
	NLA_MSECS,
	NLA_NESTED,
	NLA_NESTED_COMPAT,
	NLA_NESTED_ARRAY,
	NLA_NUL_STRING,
	NLA_BINARY,
	NLA_S8,
@@ -201,9 +201,11 @@ enum {
 *    NLA_NUL_STRING       Maximum length of string (excluding NUL)
 *    NLA_FLAG             Unused
 *    NLA_BINARY           Maximum length of attribute payload
 *    NLA_NESTED           Don't use `len' field -- length verification is
 *                         done by checking len of nested header (or empty)
 *    NLA_NESTED_COMPAT    Minimum length of structure payload
 *    NLA_NESTED,
 *    NLA_NESTED_ARRAY     Length verification is done by checking len of
 *                         nested header (or empty); len field is used if
 *                         validation_data is also used, for the max attr
 *                         number in the nested policy.
 *    NLA_U8, NLA_U16,
 *    NLA_U32, NLA_U64,
 *    NLA_S8, NLA_S16,
@@ -226,6 +228,16 @@ enum {
 *    NLA_REJECT           This attribute is always rejected and validation data
 *                         may point to a string to report as the error instead
 *                         of the generic one in extended ACK.
 *    NLA_NESTED           Points to a nested policy to validate, must also set
 *                         `len' to the max attribute number.
 *                         Note that nla_parse() will validate, but of course not
 *                         parse, the nested sub-policies.
 *    NLA_NESTED_ARRAY     Points to a nested policy to validate, must also set
 *                         `len' to the max attribute number. The difference to
 *                         NLA_NESTED is the structure - NLA_NESTED has the
 *                         nested attributes directly inside, while an array has
 *                         the nested attributes at another level down and the
 *                         attributes directly in the nesting don't matter.
 *    All other            Unused
 *
 * Example:
@@ -239,7 +251,7 @@ enum {
struct nla_policy {
	u16		type;
	u16		len;
	void            *validation_data;
	const void     *validation_data;
};

#define NLA_POLICY_EXACT_LEN(_len)	{ .type = NLA_EXACT_LEN, .len = _len }
@@ -249,6 +261,11 @@ struct nla_policy {
#define NLA_POLICY_ETH_ADDR		NLA_POLICY_EXACT_LEN(ETH_ALEN)
#define NLA_POLICY_ETH_ADDR_COMPAT	NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN)

#define NLA_POLICY_NESTED(maxattr, policy) \
	{ .type = NLA_NESTED, .validation_data = policy, .len = maxattr }
#define NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
	{ .type = NLA_NESTED_ARRAY, .validation_data = policy, .len = maxattr }

/**
 * struct nl_info - netlink source information
 * @nlh: Netlink message header of original request
+103 −46
Original line number Diff line number Diff line
@@ -45,12 +45,11 @@ static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = {
};

static int validate_nla_bitfield32(const struct nlattr *nla,
				   u32 *valid_flags_allowed)
				   const u32 *valid_flags_mask)
{
	const struct nla_bitfield32 *bf = nla_data(nla);
	u32 *valid_flags_mask = valid_flags_allowed;

	if (!valid_flags_allowed)
	if (!valid_flags_mask)
		return -EINVAL;

	/*disallow invalid bit selector */
@@ -68,12 +67,41 @@ static int validate_nla_bitfield32(const struct nlattr *nla,
	return 0;
}

static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
			      const struct nla_policy *policy,
			      struct netlink_ext_ack *extack)
{
	const struct nlattr *entry;
	int rem;

	nla_for_each_attr(entry, head, len, rem) {
		int ret;

		if (nla_len(entry) == 0)
			continue;

		if (nla_len(entry) < NLA_HDRLEN) {
			NL_SET_ERR_MSG_ATTR(extack, entry,
					    "Array element too short");
			return -ERANGE;
		}

		ret = nla_validate(nla_data(entry), nla_len(entry),
				   maxtype, policy, extack);
		if (ret < 0)
			return ret;
	}

	return 0;
}

static int validate_nla(const struct nlattr *nla, int maxtype,
			const struct nla_policy *policy,
			const char **error_msg)
			struct netlink_ext_ack *extack)
{
	const struct nla_policy *pt;
	int minlen = 0, attrlen = nla_len(nla), type = nla_type(nla);
	int err = -ERANGE;

	if (type <= 0 || type > maxtype)
		return 0;
@@ -91,24 +119,31 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
	switch (pt->type) {
	case NLA_EXACT_LEN:
		if (attrlen != pt->len)
			return -ERANGE;
			goto out_err;
		break;

	case NLA_REJECT:
		if (pt->validation_data && error_msg)
			*error_msg = pt->validation_data;
		if (extack && pt->validation_data) {
			NL_SET_BAD_ATTR(extack, nla);
			extack->_msg = pt->validation_data;
			return -EINVAL;
		}
		err = -EINVAL;
		goto out_err;

	case NLA_FLAG:
		if (attrlen > 0)
			return -ERANGE;
			goto out_err;
		break;

	case NLA_BITFIELD32:
		if (attrlen != sizeof(struct nla_bitfield32))
			return -ERANGE;
			goto out_err;

		return validate_nla_bitfield32(nla, pt->validation_data);
		err = validate_nla_bitfield32(nla, pt->validation_data);
		if (err)
			goto out_err;
		break;

	case NLA_NUL_STRING:
		if (pt->len)
@@ -116,13 +151,15 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
		else
			minlen = attrlen;

		if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL)
			return -EINVAL;
		if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL) {
			err = -EINVAL;
			goto out_err;
		}
		/* fall through */

	case NLA_STRING:
		if (attrlen < 1)
			return -ERANGE;
			goto out_err;

		if (pt->len) {
			char *buf = nla_data(nla);
@@ -131,32 +168,58 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
				attrlen--;

			if (attrlen > pt->len)
				return -ERANGE;
				goto out_err;
		}
		break;

	case NLA_BINARY:
		if (pt->len && attrlen > pt->len)
			return -ERANGE;
			goto out_err;
		break;

	case NLA_NESTED_COMPAT:
		if (attrlen < pt->len)
			return -ERANGE;
		if (attrlen < NLA_ALIGN(pt->len))
			break;
		if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN)
			return -ERANGE;
		nla = nla_data(nla) + NLA_ALIGN(pt->len);
		if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla))
			return -ERANGE;
		break;
	case NLA_NESTED:
		/* a nested attributes is allowed to be empty; if its not,
		 * it must have a size of at least NLA_HDRLEN.
		 */
		if (attrlen == 0)
			break;
		if (attrlen < NLA_HDRLEN)
			goto out_err;
		if (pt->validation_data) {
			err = nla_validate(nla_data(nla), nla_len(nla), pt->len,
					   pt->validation_data, extack);
			if (err < 0) {
				/*
				 * return directly to preserve the inner
				 * error message/attribute pointer
				 */
				return err;
			}
		}
		break;
	case NLA_NESTED_ARRAY:
		/* a nested array attribute is allowed to be empty; if its not,
		 * it must have a size of at least NLA_HDRLEN.
		 */
		if (attrlen == 0)
			break;
		if (attrlen < NLA_HDRLEN)
			goto out_err;
		if (pt->validation_data) {
			int err;

			err = nla_validate_array(nla_data(nla), nla_len(nla),
						 pt->len, pt->validation_data,
						 extack);
			if (err < 0) {
				/*
				 * return directly to preserve the inner
				 * error message/attribute pointer
				 */
				return err;
			}
		}
		break;
	default:
		if (pt->len)
			minlen = pt->len;
@@ -164,10 +227,13 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
			minlen = nla_attr_minlen[pt->type];

		if (attrlen < minlen)
			return -ERANGE;
			goto out_err;
	}

	return 0;
out_err:
	NL_SET_ERR_MSG_ATTR(extack, nla, "Attribute failed policy validation");
	return err;
}

/**
@@ -192,13 +258,11 @@ int nla_validate(const struct nlattr *head, int len, int maxtype,
	int rem;

	nla_for_each_attr(nla, head, len, rem) {
		int err = validate_nla(nla, maxtype, policy, NULL);
		int err = validate_nla(nla, maxtype, policy, extack);

		if (err < 0) {
			NL_SET_BAD_ATTR(extack, nla);
		if (err < 0)
			return err;
	}
	}

	return 0;
}
@@ -253,7 +317,7 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
	      struct netlink_ext_ack *extack)
{
	const struct nlattr *nla;
	int rem, err;
	int rem;

	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));

@@ -261,17 +325,12 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
		u16 type = nla_type(nla);

		if (type > 0 && type <= maxtype) {
			static const char _msg[] = "Attribute failed policy validation";
			const char *msg = _msg;

			if (policy) {
				err = validate_nla(nla, maxtype, policy, &msg);
				if (err < 0) {
					NL_SET_BAD_ATTR(extack, nla);
					if (extack)
						extack->_msg = msg;
					goto errout;
				}
				int err = validate_nla(nla, maxtype, policy,
						       extack);

				if (err < 0)
					return err;
			}

			tb[type] = (struct nlattr *)nla;
@@ -282,9 +341,7 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
		pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
				    rem, current->comm);

	err = 0;
errout:
	return err;
	return 0;
}
EXPORT_SYMBOL(nla_parse);