]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - lib/nlattr.c
netlink: re-add parse/validate functions in strict mode
[mirror_ubuntu-hirsute-kernel.git] / lib / nlattr.c
index d26de6156b97db73803cb617bcd81fd7415ac7b1..af0f8b0309c663dec8cbc32cf9c72873b40c7e12 100644 (file)
@@ -69,7 +69,8 @@ static int validate_nla_bitfield32(const struct nlattr *nla,
 
 static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
                              const struct nla_policy *policy,
-                             struct netlink_ext_ack *extack)
+                             struct netlink_ext_ack *extack,
+                             unsigned int validate)
 {
        const struct nlattr *entry;
        int rem;
@@ -86,8 +87,8 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
                        return -ERANGE;
                }
 
-               ret = nla_validate(nla_data(entry), nla_len(entry),
-                                  maxtype, policy, extack);
+               ret = __nla_validate(nla_data(entry), nla_len(entry),
+                                    maxtype, policy, validate, extack);
                if (ret < 0)
                        return ret;
        }
@@ -154,7 +155,7 @@ static int nla_validate_int_range(const struct nla_policy *pt,
 }
 
 static int validate_nla(const struct nlattr *nla, int maxtype,
-                       const struct nla_policy *policy,
+                       const struct nla_policy *policy, unsigned int validate,
                        struct netlink_ext_ack *extack)
 {
        const struct nla_policy *pt;
@@ -172,6 +173,11 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
            (pt->type == NLA_EXACT_LEN_WARN && attrlen != pt->len)) {
                pr_warn_ratelimited("netlink: '%s': attribute type %d has an invalid length.\n",
                                    current->comm, type);
+               if (validate & NL_VALIDATE_STRICT_ATTRS) {
+                       NL_SET_ERR_MSG_ATTR(extack, nla,
+                                           "invalid attribute length");
+                       return -EINVAL;
+               }
        }
 
        switch (pt->type) {
@@ -244,8 +250,9 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
                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);
+                       err = __nla_validate(nla_data(nla), nla_len(nla), pt->len,
+                                            pt->validation_data, validate,
+                                            extack);
                        if (err < 0) {
                                /*
                                 * return directly to preserve the inner
@@ -268,7 +275,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 
                        err = nla_validate_array(nla_data(nla), nla_len(nla),
                                                 pt->len, pt->validation_data,
-                                                extack);
+                                                extack, validate);
                        if (err < 0) {
                                /*
                                 * return directly to preserve the inner
@@ -278,10 +285,23 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
                        }
                }
                break;
+
+       case NLA_UNSPEC:
+               if (validate & NL_VALIDATE_UNSPEC) {
+                       NL_SET_ERR_MSG_ATTR(extack, nla,
+                                           "Unsupported attribute");
+                       return -EINVAL;
+               }
+               /* fall through */
+       case NLA_MIN_LEN:
+               if (attrlen < pt->len)
+                       goto out_err;
+               break;
+
        default:
                if (pt->len)
                        minlen = pt->len;
-               else if (pt->type != NLA_UNSPEC)
+               else
                        minlen = nla_attr_minlen[pt->type];
 
                if (attrlen < minlen)
@@ -315,37 +335,75 @@ out_err:
        return err;
 }
 
+static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
+                               const struct nla_policy *policy,
+                               unsigned int validate,
+                               struct netlink_ext_ack *extack,
+                               struct nlattr **tb)
+{
+       const struct nlattr *nla;
+       int rem;
+
+       if (tb)
+               memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
+
+       nla_for_each_attr(nla, head, len, rem) {
+               u16 type = nla_type(nla);
+
+               if (type == 0 || type > maxtype) {
+                       if (validate & NL_VALIDATE_MAXTYPE) {
+                               NL_SET_ERR_MSG(extack, "Unknown attribute type");
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (policy) {
+                       int err = validate_nla(nla, maxtype, policy,
+                                              validate, extack);
+
+                       if (err < 0)
+                               return err;
+               }
+
+               if (tb)
+                       tb[type] = (struct nlattr *)nla;
+       }
+
+       if (unlikely(rem > 0)) {
+               pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
+                                   rem, current->comm);
+               NL_SET_ERR_MSG(extack, "bytes leftover after parsing attributes");
+               if (validate & NL_VALIDATE_TRAILING)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
 /**
- * nla_validate - Validate a stream of attributes
+ * __nla_validate - Validate a stream of attributes
  * @head: head of attribute stream
  * @len: length of attribute stream
  * @maxtype: maximum attribute type to be expected
  * @policy: validation policy
+ * @validate: validation strictness
  * @extack: extended ACK report struct
  *
  * Validates all attributes in the specified attribute stream against the
- * specified policy. Attributes with a type exceeding maxtype will be
- * ignored. See documenation of struct nla_policy for more details.
+ * specified policy. Validation depends on the validate flags passed, see
+ * &enum netlink_validation for more details on that.
+ * See documenation of struct nla_policy for more details.
  *
  * Returns 0 on success or a negative error code.
  */
-int nla_validate(const struct nlattr *head, int len, int maxtype,
-                const struct nla_policy *policy,
-                struct netlink_ext_ack *extack)
+int __nla_validate(const struct nlattr *head, int len, int maxtype,
+                  const struct nla_policy *policy, unsigned int validate,
+                  struct netlink_ext_ack *extack)
 {
-       const struct nlattr *nla;
-       int rem;
-
-       nla_for_each_attr(nla, head, len, rem) {
-               int err = validate_nla(nla, maxtype, policy, extack);
-
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
+       return __nla_validate_parse(head, len, maxtype, policy, validate,
+                                   extack, NULL);
 }
-EXPORT_SYMBOL(nla_validate);
+EXPORT_SYMBOL(__nla_validate);
 
 /**
  * nla_policy_len - Determin the max. length of a policy
@@ -377,76 +435,30 @@ nla_policy_len(const struct nla_policy *p, int n)
 EXPORT_SYMBOL(nla_policy_len);
 
 /**
- * nla_parse - Parse a stream of attributes into a tb buffer
+ * __nla_parse - Parse a stream of attributes into a tb buffer
  * @tb: destination array with maxtype+1 elements
  * @maxtype: maximum attribute type to be expected
  * @head: head of attribute stream
  * @len: length of attribute stream
  * @policy: validation policy
+ * @validate: validation strictness
+ * @extack: extended ACK pointer
  *
  * Parses a stream of attributes and stores a pointer to each attribute in
- * the tb array accessible via the attribute type. Attributes with a type
- * exceeding maxtype will be silently ignored for backwards compatibility
- * reasons. policy may be set to NULL if no validation is required.
+ * the tb array accessible via the attribute type.
+ * Validation is controlled by the @validate parameter.
  *
  * Returns 0 on success or a negative error code.
  */
-static int __nla_parse(struct nlattr **tb, int maxtype,
-                      const struct nlattr *head, int len,
-                      bool strict, const struct nla_policy *policy,
-                      struct netlink_ext_ack *extack)
-{
-       const struct nlattr *nla;
-       int rem;
-
-       memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
-
-       nla_for_each_attr(nla, head, len, rem) {
-               u16 type = nla_type(nla);
-
-               if (type == 0 || type > maxtype) {
-                       if (strict) {
-                               NL_SET_ERR_MSG(extack, "Unknown attribute type");
-                               return -EINVAL;
-                       }
-                       continue;
-               }
-               if (policy) {
-                       int err = validate_nla(nla, maxtype, policy, extack);
-
-                       if (err < 0)
-                               return err;
-               }
-
-               tb[type] = (struct nlattr *)nla;
-       }
-
-       if (unlikely(rem > 0)) {
-               pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
-                                   rem, current->comm);
-               NL_SET_ERR_MSG(extack, "bytes leftover after parsing attributes");
-               if (strict)
-                       return -EINVAL;
-       }
-
-       return 0;
-}
-
-int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
-             int len, const struct nla_policy *policy,
-             struct netlink_ext_ack *extack)
-{
-       return __nla_parse(tb, maxtype, head, len, false, policy, extack);
-}
-EXPORT_SYMBOL(nla_parse);
-
-int nla_parse_strict(struct nlattr **tb, int maxtype, const struct nlattr *head,
-                    int len, const struct nla_policy *policy,
-                    struct netlink_ext_ack *extack)
+int __nla_parse(struct nlattr **tb, int maxtype,
+               const struct nlattr *head, int len,
+               const struct nla_policy *policy, unsigned int validate,
+               struct netlink_ext_ack *extack)
 {
-       return __nla_parse(tb, maxtype, head, len, true, policy, extack);
+       return __nla_validate_parse(head, len, maxtype, policy, validate,
+                                   extack, tb);
 }
-EXPORT_SYMBOL(nla_parse_strict);
+EXPORT_SYMBOL(__nla_parse);
 
 /**
  * nla_find - Find a specific attribute in a stream of attributes