diff mbox series

[RFC,1/6] netlink: add nlmsg_validate_strict() & nla_validate_strict()

Message ID 20190321220504.7642-2-johannes@sipsolutions.net
State RFC
Delegated to: David Miller
Headers show
Series netlink/genetlink: stricter parsing | expand

Commit Message

Johannes Berg March 21, 2019, 10:04 p.m. UTC
From: Johannes Berg <johannes.berg@intel.com>

These are needed since we want to separate validation and parsing
in some cases, e.g. in generic netlink to ensure that dump messages
are valid, but we don't typically parse them.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/netlink.h | 26 +++++++++++++++++++++++++
 lib/nlattr.c          | 45 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 71 insertions(+)

Comments

Florian Westphal March 21, 2019, 10:50 p.m. UTC | #1
Johannes Berg <johannes@sipsolutions.net> wrote:
> + * nla_validate_strict - Strictly 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
> + * @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.
      ^^^^^^^^
rejected?

> +int nla_validate_strict(const struct nlattr *head, int len, int maxtype,
> +			const struct nla_policy *policy,
> +			struct netlink_ext_ack *extack)
> +{
> +	const struct nlattr *nla;
> +	int rem;
> +
> +	nla_for_each_attr(nla, head, len, rem) {
> +		u16 type = nla_type(nla);
> +		int err;
> +
> +		if (type == 0 || type > maxtype) {
> +			NL_SET_ERR_MSG(extack, "Unknown attribute type");

Sure looks like it, and thats good.
Johannes Berg March 22, 2019, 6:23 a.m. UTC | #2
On Thu, 2019-03-21 at 23:50 +0100, Florian Westphal wrote:
> Johannes Berg <johannes@sipsolutions.net> wrote:
> > + * nla_validate_strict - Strictly 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
> > + * @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.
> 
>       ^^^^^^^^
> rejected?

Oops, right, I didn't pay attentino to the docs at all.

But anyway, I don't think I want to do this.

I'm tempted to do the following:

 * add an
   enum netlink_validation {
       NETLINK_VALIDATION_LIBERAL,    // old behaviour
       NETLINK_VALIDATION_STRICT_MSG, // current strict
       NETLINK_VALIDATION_STRICT,     // strict message & attribute
   };
 * add __*_parse()/__*_validate() that get a new argument from this enum
 * for all existing callers of *_parse()/*_validate() add a new inline
   *_parse_liberal()/*_validate_liberal() and replace all calls, using
   _LIBERAL
 * change all existing *_parse_strict() to a new *_parse_strict_msg()
   inline using _STRICT_MSG
 * re-introduce *_parse()/*_validate() as being fully _STRICT

Also, do this before the generic netlink changes, so generic netlink
never gets the intermediate "STRICT_MSG" level.

That addresses two things:

 1) my table from the cover letter would be - at least for genl - what
    I want it to be, for some rtnetlink commands we'd have "strict_msg"
    semantics

 2) Default of *_parse()/*_validate() becomes to be strict for new
    code, so we don't need to pay as much attention to it - it'll be
    easier to see if somebody adds a call explicitly calling the more
   liberal versions. I'm tempted to not even add inline wrappers for
   this reason but to just open-code the __*() versions with the enum
   value instead (it's spatch, after all).



PS: STRICT_MSG (currently _strict()) semantics are a bit strange because
an attribute type that's out of range is rejected, while one that's in
range but has no policy is accepted; yet the range is prone to change
all the time... The "strict_start_type" fixes that though, if applied.

johannes
diff mbox series

Patch

diff --git a/include/net/netlink.h b/include/net/netlink.h
index 23f27b0b3cef..e211b745008a 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -66,6 +66,7 @@ 
  *   nlmsg_find_attr()			find an attribute in a message
  *   nlmsg_for_each_msg()		loop over all messages
  *   nlmsg_validate()			validate netlink message incl. attrs
+ *   nlmsg_validate_strict()		strictly validate netlink message/attrs
  *   nlmsg_for_each_attr()		loop over all attributes
  *
  * Misc:
@@ -149,6 +150,7 @@ 
  *   nla_ok(nla, remaining)		does nla fit into remaining bytes?
  *   nla_next(nla, remaining)		get next netlink attribute
  *   nla_validate()			validate a stream of attributes
+ *   nla_validate_strict()		strictly validate a stream of attributes
  *   nla_validate_nested()		validate a stream of nested attributes
  *   nla_find()				find attribute in stream of attributes
  *   nla_find_nested()			find attribute in nested attributes
@@ -374,6 +376,9 @@  int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid,
 int nla_validate(const struct nlattr *head, int len, int maxtype,
 		 const struct nla_policy *policy,
 		 struct netlink_ext_ack *extack);
+int nla_validate_strict(const struct nlattr *head, int len, int maxtype,
+			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,
 	      struct netlink_ext_ack *extack);
@@ -582,6 +587,27 @@  static inline int nlmsg_validate(const struct nlmsghdr *nlh,
 			    extack);
 }
 
+/**
+ * nlmsg_validate_strict - strictly validate a netlink message including attrs
+ * @nlh: netlinket message header
+ * @hdrlen: length of familiy specific header
+ * @maxtype: maximum attribute type to be expected
+ * @policy: validation policy
+ * @extack: extended ACK report struct
+ */
+static inline int nlmsg_validate_strict(const struct nlmsghdr *nlh,
+					int hdrlen, int maxtype,
+					const struct nla_policy *policy,
+					struct netlink_ext_ack *extack)
+{
+	if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
+		return -EINVAL;
+
+	return nla_validate_strict(nlmsg_attrdata(nlh, hdrlen),
+				   nlmsg_attrlen(nlh, hdrlen), maxtype,
+				   policy, extack);
+}
+
 /**
  * nlmsg_report - need to report back to application?
  * @nlh: netlink message header
diff --git a/lib/nlattr.c b/lib/nlattr.c
index d26de6156b97..3ccef39b89de 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -347,6 +347,51 @@  int nla_validate(const struct nlattr *head, int len, int maxtype,
 }
 EXPORT_SYMBOL(nla_validate);
 
+/**
+ * nla_validate_strict - Strictly 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
+ * @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.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int nla_validate_strict(const struct nlattr *head, int len, int maxtype,
+			const struct nla_policy *policy,
+			struct netlink_ext_ack *extack)
+{
+	const struct nlattr *nla;
+	int rem;
+
+	nla_for_each_attr(nla, head, len, rem) {
+		u16 type = nla_type(nla);
+		int err;
+
+		if (type == 0 || type > maxtype) {
+			NL_SET_ERR_MSG(extack, "Unknown attribute type");
+			return -EINVAL;
+		}
+
+		err = validate_nla(nla, maxtype, policy, extack);
+
+		if (err < 0)
+			return err;
+	}
+
+	if (unlikely(rem > 0)) {
+		NL_SET_ERR_MSG(extack, "bytes leftover after parsing attributes");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(nla_validate_strict);
+
 /**
  * nla_policy_len - Determin the max. length of a policy
  * @policy: policy to use