]> git.baikalelectronics.ru Git - kernel.git/commitdiff
netlink: add nested array policy validation
authorJohannes Berg <johannes.berg@intel.com>
Wed, 26 Sep 2018 09:15:34 +0000 (11:15 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 28 Sep 2018 17:24:39 +0000 (10:24 -0700)
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>
include/net/netlink.h
lib/nlattr.c

index 91907852da1c2182a1dcec1e6ff9326dd6786de8..3698ca8ff92c3e3a57c70572d15b76d6ea03ad81 100644 (file)
@@ -172,6 +172,7 @@ enum {
        NLA_FLAG,
        NLA_MSECS,
        NLA_NESTED,
+       NLA_NESTED_ARRAY,
        NLA_NUL_STRING,
        NLA_BINARY,
        NLA_S8,
@@ -200,7 +201,8 @@ enum {
  *    NLA_NUL_STRING       Maximum length of string (excluding NUL)
  *    NLA_FLAG             Unused
  *    NLA_BINARY           Maximum length of attribute payload
- *    NLA_NESTED           Length verification is done by checking len of
+ *    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.
@@ -230,6 +232,12 @@ enum {
  *                         `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:
@@ -255,6 +263,8 @@ struct nla_policy {
 
 #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
index 04750f88477ca3d28eab644fa3d27659a286c941..2f8feff669a7b41be4f6747c978a050ce7ade1f5 100644 (file)
@@ -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;