diff options
author | Johannes Berg <johannes.berg@intel.com> | 2018-09-26 11:15:34 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-09-28 19:24:39 +0200 |
commit | 1501d13596b92d6d1f0ea5e104be838188b6e026 (patch) | |
tree | 8cce360cee760f94af43ef1c236cdbbbbf57a4d3 /lib | |
parent | netlink: allow NLA_NESTED to specify nested policy to validate (diff) | |
download | linux-1501d13596b92d6d1f0ea5e104be838188b6e026.tar.xz linux-1501d13596b92d6d1f0ea5e104be838188b6e026.zip |
netlink: add nested array policy validation
Sometimes nested netlink attributes are just used as arrays, with
the nla_type() of each not being used; we have this in nl80211 and
e.g. NFTA_SET_ELEM_LIST_ELEMENTS.
Add the ability to validate this type of message directly in the
policy, by adding the type NLA_NESTED_ARRAY which does exactly
this: require a first level of nesting but ignore the attribute
type, and then inside each require a second level of nested and
validate those attributes against a given policy (if present).
Note that some nested array types actually require that all of
the entries have the same index, this is possible to express in
a nested policy already, apart from the validation that only the
one allowed type is used.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/nlattr.c | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/lib/nlattr.c b/lib/nlattr.c index 04750f88477c..2f8feff669a7 100644 --- a/lib/nlattr.c +++ b/lib/nlattr.c @@ -67,6 +67,34 @@ 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, struct netlink_ext_ack *extack) @@ -169,6 +197,29 @@ static int validate_nla(const struct nlattr *nla, int maxtype, } } 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; |