diff mbox series

[net-next,09/20] rtnetlink: Update rtnl_bridge_getlink for strict data checking

Message ID 20181004213355.14899-10-dsahern@kernel.org
State Changes Requested, archived
Delegated to: David Miller
Headers show
Series rtnetlink: Add support for rigid checking of data in dump request | expand

Commit Message

David Ahern Oct. 4, 2018, 9:33 p.m. UTC
From: David Ahern <dsahern@gmail.com>

Update rtnl_bridge_getlink for strict data checking. If the flag is set,
the dump request is expected to have an ifinfomsg struct as the header
potentially followed by one or more attributes. Any data passed in the
header or as an attribute is taken as a request to influence the data
returned. Only values supported by the dump handler are allowed to be
non-0 or set in the request. At the moment only the IFLA_EXT_MASK
attribute is supported.

Signed-off-by: David Ahern <dsahern@gmail.com>
---
 net/core/rtnetlink.c | 50 ++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 40 insertions(+), 10 deletions(-)

Comments

Christian Brauner Oct. 7, 2018, 10:36 a.m. UTC | #1
On Thu, Oct 04, 2018 at 02:33:44PM -0700, David Ahern wrote:
> From: David Ahern <dsahern@gmail.com>
> 
> Update rtnl_bridge_getlink for strict data checking. If the flag is set,
> the dump request is expected to have an ifinfomsg struct as the header
> potentially followed by one or more attributes. Any data passed in the
> header or as an attribute is taken as a request to influence the data
> returned. Only values supported by the dump handler are allowed to be
> non-0 or set in the request. At the moment only the IFLA_EXT_MASK
> attribute is supported.
> 
> Signed-off-by: David Ahern <dsahern@gmail.com>
> ---
>  net/core/rtnetlink.c | 50 ++++++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 40 insertions(+), 10 deletions(-)
> 
> diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
> index 4fd27b5db787..d2c8d41a6fbc 100644
> --- a/net/core/rtnetlink.c
> +++ b/net/core/rtnetlink.c
> @@ -4000,27 +4000,57 @@ EXPORT_SYMBOL_GPL(ndo_dflt_bridge_getlink);
>  
>  static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
>  {
> +	struct netlink_ext_ack *extack = cb->extack;
> +	const struct nlmsghdr *nlh = cb->nlh;
>  	struct net *net = sock_net(skb->sk);
> +	struct nlattr *tb[IFLA_MAX+1];
>  	struct net_device *dev;
>  	int idx = 0;
>  	u32 portid = NETLINK_CB(cb->skb).portid;
> -	u32 seq = cb->nlh->nlmsg_seq;
> +	u32 seq = nlh->nlmsg_seq;
>  	u32 filter_mask = 0;
> -	int err;
> +	int err, i;
>  
> -	if (nlmsg_len(cb->nlh) > sizeof(struct ifinfomsg)) {
> -		struct nlattr *extfilt;
> +	if (cb->strict_check) {
> +		struct ifinfomsg *ifm;
>  
> -		extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg),
> -					  IFLA_EXT_MASK);
> -		if (extfilt) {
> -			if (nla_len(extfilt) < sizeof(filter_mask))
> -				return -EINVAL;
> +		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
> +			NL_SET_ERR_MSG(extack, "Invalid header");
> +			return -EINVAL;
> +		}
> +
> +		ifm = nlmsg_data(nlh);
> +		if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags ||
> +		    ifm->ifi_change || ifm->ifi_index) {
> +			NL_SET_ERR_MSG(extack, "Invalid values in header for dump request");
> +			return -EINVAL;
> +		}
> +	}
>  
> -			filter_mask = nla_get_u32(extfilt);
> +	err = nlmsg_parse(nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
> +			  ifla_policy, extack);
> +	if (err < 0) {
> +		if (cb->strict_check)
> +			return -EINVAL;
> +		goto walk_entries;
> +	}

What's the point of moving this out of the
if (cb->strict_check) {} branch above? This looks like it would cause
the same parse warnings that we're trying to get rid of in inet{4,6}
dumps.
Seems to make more sense to make the nlmsg_parse() itself conditional as
well unless I'm lacking context.

> +
> +	for (i = 0; i <= IFLA_MAX; ++i) {
> +		if (!tb[i])
> +			continue;
> +		switch (i) {

I'm a fan of \n between different conditions etc. so can we please have
one after the continue. :)

> +		case IFLA_EXT_MASK:
> +			filter_mask = nla_get_u32(tb[i]);
> +			break;
> +		default:
> +			if (cb->strict_check) {
> +				NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request");
> +				return -EINVAL;
> +			}
>  		}
>  	}
>  
> +walk_entries:
>  	rcu_read_lock();
>  	for_each_netdev_rcu(net, dev) {
>  		const struct net_device_ops *ops = dev->netdev_ops;
> -- 
> 2.11.0
>
David Ahern Oct. 8, 2018, 1:31 a.m. UTC | #2
On 10/7/18 4:36 AM, Christian Brauner wrote:
>> +	if (cb->strict_check) {
>> +		struct ifinfomsg *ifm;
>>  
>> -		extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg),
>> -					  IFLA_EXT_MASK);
>> -		if (extfilt) {
>> -			if (nla_len(extfilt) < sizeof(filter_mask))
>> -				return -EINVAL;
>> +		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
>> +			NL_SET_ERR_MSG(extack, "Invalid header");
>> +			return -EINVAL;
>> +		}
>> +
>> +		ifm = nlmsg_data(nlh);
>> +		if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags ||
>> +		    ifm->ifi_change || ifm->ifi_index) {
>> +			NL_SET_ERR_MSG(extack, "Invalid values in header for dump request");
>> +			return -EINVAL;
>> +		}
>> +	}
>>  
>> -			filter_mask = nla_get_u32(extfilt);
>> +	err = nlmsg_parse(nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
>> +			  ifla_policy, extack);
>> +	if (err < 0) {
>> +		if (cb->strict_check)
>> +			return -EINVAL;
>> +		goto walk_entries;
>> +	}
> 
> What's the point of moving this out of the
> if (cb->strict_check) {} branch above? This looks like it would cause
> the same parse warnings that we're trying to get rid of in inet{4,6}
> dumps.

Link messages don't have the problem in general because they use
ifinfomsg as the header - which is the one abused for other message
types. That said ...

> Seems to make more sense to make the nlmsg_parse() itself conditional as
> well unless I'm lacking context.

... I now have nlmsg_parse and nlmsg_parse_strict.
diff mbox series

Patch

diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 4fd27b5db787..d2c8d41a6fbc 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -4000,27 +4000,57 @@  EXPORT_SYMBOL_GPL(ndo_dflt_bridge_getlink);
 
 static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
 {
+	struct netlink_ext_ack *extack = cb->extack;
+	const struct nlmsghdr *nlh = cb->nlh;
 	struct net *net = sock_net(skb->sk);
+	struct nlattr *tb[IFLA_MAX+1];
 	struct net_device *dev;
 	int idx = 0;
 	u32 portid = NETLINK_CB(cb->skb).portid;
-	u32 seq = cb->nlh->nlmsg_seq;
+	u32 seq = nlh->nlmsg_seq;
 	u32 filter_mask = 0;
-	int err;
+	int err, i;
 
-	if (nlmsg_len(cb->nlh) > sizeof(struct ifinfomsg)) {
-		struct nlattr *extfilt;
+	if (cb->strict_check) {
+		struct ifinfomsg *ifm;
 
-		extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg),
-					  IFLA_EXT_MASK);
-		if (extfilt) {
-			if (nla_len(extfilt) < sizeof(filter_mask))
-				return -EINVAL;
+		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
+			NL_SET_ERR_MSG(extack, "Invalid header");
+			return -EINVAL;
+		}
+
+		ifm = nlmsg_data(nlh);
+		if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags ||
+		    ifm->ifi_change || ifm->ifi_index) {
+			NL_SET_ERR_MSG(extack, "Invalid values in header for dump request");
+			return -EINVAL;
+		}
+	}
 
-			filter_mask = nla_get_u32(extfilt);
+	err = nlmsg_parse(nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
+			  ifla_policy, extack);
+	if (err < 0) {
+		if (cb->strict_check)
+			return -EINVAL;
+		goto walk_entries;
+	}
+
+	for (i = 0; i <= IFLA_MAX; ++i) {
+		if (!tb[i])
+			continue;
+		switch (i) {
+		case IFLA_EXT_MASK:
+			filter_mask = nla_get_u32(tb[i]);
+			break;
+		default:
+			if (cb->strict_check) {
+				NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request");
+				return -EINVAL;
+			}
 		}
 	}
 
+walk_entries:
 	rcu_read_lock();
 	for_each_netdev_rcu(net, dev) {
 		const struct net_device_ops *ops = dev->netdev_ops;