Commit e266afa9 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-use-strict-checks-in-doit-handlers'



Jakub Kicinski says:

====================
net: use strict checks in doit handlers

This series extends strict argument checking to doit handlers
of the GET* nature.  This is a bit tricky since strict checking
flag has already been released..

iproute2 did not have a release with strick checks enabled,
and it will only need a minor one-liner to pass strick checks
after all the work that DaveA has already done.

Big thanks to Dave Ahern for help and guidence.

v2:
 - remove unnecessary check in patch 5 (Nicolas);
 - add path 7 (DaveA);
 - improve messages in patch 8 (DaveA).
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 133bbb18 0c4056ee
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -126,6 +126,7 @@ void __netlink_clear_multicast_users(struct sock *sk, unsigned int group);
void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
		 const struct netlink_ext_ack *extack);
int netlink_has_listeners(struct sock *sk, unsigned int group);
bool netlink_strict_get_check(struct sk_buff *skb);

int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,
+36 −2
Original line number Diff line number Diff line
@@ -778,6 +778,41 @@ nla_put_failure:
	return -EMSGSIZE;
}

static int rtnl_net_valid_getid_req(struct sk_buff *skb,
				    const struct nlmsghdr *nlh,
				    struct nlattr **tb,
				    struct netlink_ext_ack *extack)
{
	int i, err;

	if (!netlink_strict_get_check(skb))
		return nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX,
				   rtnl_net_policy, extack);

	err = nlmsg_parse_strict(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX,
				 rtnl_net_policy, extack);
	if (err)
		return err;

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

		switch (i) {
		case NETNSA_PID:
		case NETNSA_FD:
		case NETNSA_NSID:
		case NETNSA_TARGET_NSID:
			break;
		default:
			NL_SET_ERR_MSG(extack, "Unsupported attribute in peer netns getid request");
			return -EINVAL;
		}
	}

	return 0;
}

static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
			  struct netlink_ext_ack *extack)
{
@@ -793,8 +828,7 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
	struct sk_buff *msg;
	int err;

	err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX,
			  rtnl_net_policy, extack);
	err = rtnl_net_valid_getid_req(skb, nlh, tb, extack);
	if (err < 0)
		return err;
	if (tb[NETNSA_PID]) {
+89 −22
Original line number Diff line number Diff line
@@ -3242,6 +3242,53 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
	return ret;
}

static int rtnl_valid_getlink_req(struct sk_buff *skb,
				  const struct nlmsghdr *nlh,
				  struct nlattr **tb,
				  struct netlink_ext_ack *extack)
{
	struct ifinfomsg *ifm;
	int i, err;

	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
		NL_SET_ERR_MSG(extack, "Invalid header for get link");
		return -EINVAL;
	}

	if (!netlink_strict_get_check(skb))
		return nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy,
				   extack);

	ifm = nlmsg_data(nlh);
	if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags ||
	    ifm->ifi_change) {
		NL_SET_ERR_MSG(extack, "Invalid values in header for get link request");
		return -EINVAL;
	}

	err = nlmsg_parse_strict(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy,
				 extack);
	if (err)
		return err;

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

		switch (i) {
		case IFLA_IFNAME:
		case IFLA_EXT_MASK:
		case IFLA_TARGET_NETNSID:
			break;
		default:
			NL_SET_ERR_MSG(extack, "Unsupported attribute in get link request");
			return -EINVAL;
		}
	}

	return 0;
}

static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
			struct netlink_ext_ack *extack)
{
@@ -3256,7 +3303,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
	int err;
	u32 ext_filter_mask = 0;

	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, extack);
	err = rtnl_valid_getlink_req(skb, nlh, tb, extack);
	if (err < 0)
		return err;

@@ -4902,6 +4949,40 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev,
	return size;
}

static int rtnl_valid_stats_req(const struct nlmsghdr *nlh, bool strict_check,
				bool is_dump, struct netlink_ext_ack *extack)
{
	struct if_stats_msg *ifsm;

	if (nlh->nlmsg_len < sizeof(*ifsm)) {
		NL_SET_ERR_MSG(extack, "Invalid header for stats dump");
		return -EINVAL;
	}

	if (!strict_check)
		return 0;

	ifsm = nlmsg_data(nlh);

	/* only requests using strict checks can pass data to influence
	 * the dump. The legacy exception is filter_mask.
	 */
	if (ifsm->pad1 || ifsm->pad2 || (is_dump && ifsm->ifindex)) {
		NL_SET_ERR_MSG(extack, "Invalid values in header for stats dump request");
		return -EINVAL;
	}
	if (nlmsg_attrlen(nlh, sizeof(*ifsm))) {
		NL_SET_ERR_MSG(extack, "Invalid attributes after stats header");
		return -EINVAL;
	}
	if (ifsm->filter_mask >= IFLA_STATS_FILTER_BIT(IFLA_STATS_MAX + 1)) {
		NL_SET_ERR_MSG(extack, "Invalid stats requested through filter mask");
		return -EINVAL;
	}

	return 0;
}

static int rtnl_stats_get(struct sk_buff *skb, struct nlmsghdr *nlh,
			  struct netlink_ext_ack *extack)
{
@@ -4913,8 +4994,10 @@ static int rtnl_stats_get(struct sk_buff *skb, struct nlmsghdr *nlh,
	u32 filter_mask;
	int err;

	if (nlmsg_len(nlh) < sizeof(*ifsm))
		return -EINVAL;
	err = rtnl_valid_stats_req(nlh, netlink_strict_get_check(skb),
				   false, extack);
	if (err)
		return err;

	ifsm = nlmsg_data(nlh);
	if (ifsm->ifindex > 0)
@@ -4966,27 +5049,11 @@ static int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb)

	cb->seq = net->dev_base_seq;

	if (nlmsg_len(cb->nlh) < sizeof(*ifsm)) {
		NL_SET_ERR_MSG(extack, "Invalid header for stats dump");
		return -EINVAL;
	}
	err = rtnl_valid_stats_req(cb->nlh, cb->strict_check, true, extack);
	if (err)
		return err;

	ifsm = nlmsg_data(cb->nlh);

	/* only requests using strict checks can pass data to influence
	 * the dump. The legacy exception is filter_mask.
	 */
	if (cb->strict_check) {
		if (ifsm->pad1 || ifsm->pad2 || ifsm->ifindex) {
			NL_SET_ERR_MSG(extack, "Invalid values in header for stats dump request");
			return -EINVAL;
		}
		if (nlmsg_attrlen(cb->nlh, sizeof(*ifsm))) {
			NL_SET_ERR_MSG(extack, "Invalid attributes after stats header");
			return -EINVAL;
		}
	}

	filter_mask = ifsm->filter_mask;
	if (!filter_mask) {
		NL_SET_ERR_MSG(extack, "Filter mask must be set for stats dump");
+39 −4
Original line number Diff line number Diff line
@@ -2063,13 +2063,49 @@ static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
};

static int inet_netconf_valid_get_req(struct sk_buff *skb,
				      const struct nlmsghdr *nlh,
				      struct nlattr **tb,
				      struct netlink_ext_ack *extack)
{
	int i, err;

	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
		return -EINVAL;
	}

	if (!netlink_strict_get_check(skb))
		return nlmsg_parse(nlh, sizeof(struct netconfmsg), tb,
				   NETCONFA_MAX, devconf_ipv4_policy, extack);

	err = nlmsg_parse_strict(nlh, sizeof(struct netconfmsg), tb,
				 NETCONFA_MAX, devconf_ipv4_policy, extack);
	if (err)
		return err;

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

		switch (i) {
		case NETCONFA_IFINDEX:
			break;
		default:
			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
			return -EINVAL;
		}
	}

	return 0;
}

static int inet_netconf_get_devconf(struct sk_buff *in_skb,
				    struct nlmsghdr *nlh,
				    struct netlink_ext_ack *extack)
{
	struct net *net = sock_net(in_skb->sk);
	struct nlattr *tb[NETCONFA_MAX+1];
	struct netconfmsg *ncm;
	struct sk_buff *skb;
	struct ipv4_devconf *devconf;
	struct in_device *in_dev;
@@ -2077,9 +2113,8 @@ static int inet_netconf_get_devconf(struct sk_buff *in_skb,
	int ifindex;
	int err;

	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
			  devconf_ipv4_policy, extack);
	if (err < 0)
	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
	if (err)
		goto errout;

	err = -EINVAL;
+56 −5
Original line number Diff line number Diff line
@@ -2467,6 +2467,61 @@ errout:
	rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE_R, -ENOBUFS);
}

static int ipmr_rtm_valid_getroute_req(struct sk_buff *skb,
				       const struct nlmsghdr *nlh,
				       struct nlattr **tb,
				       struct netlink_ext_ack *extack)
{
	struct rtmsg *rtm;
	int i, err;

	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for multicast route get request");
		return -EINVAL;
	}

	if (!netlink_strict_get_check(skb))
		return nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX,
				   rtm_ipv4_policy, extack);

	rtm = nlmsg_data(nlh);
	if ((rtm->rtm_src_len && rtm->rtm_src_len != 32) ||
	    (rtm->rtm_dst_len && rtm->rtm_dst_len != 32) ||
	    rtm->rtm_tos || rtm->rtm_table || rtm->rtm_protocol ||
	    rtm->rtm_scope || rtm->rtm_type || rtm->rtm_flags) {
		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for multicast route get request");
		return -EINVAL;
	}

	err = nlmsg_parse_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
				 rtm_ipv4_policy, extack);
	if (err)
		return err;

	if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
	    (tb[RTA_DST] && !rtm->rtm_dst_len)) {
		NL_SET_ERR_MSG(extack, "ipv4: rtm_src_len and rtm_dst_len must be 32 for IPv4");
		return -EINVAL;
	}

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

		switch (i) {
		case RTA_SRC:
		case RTA_DST:
		case RTA_TABLE:
			break;
		default:
			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in multicast route get request");
			return -EINVAL;
		}
	}

	return 0;
}

static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
			     struct netlink_ext_ack *extack)
{
@@ -2475,18 +2530,14 @@ static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
	struct sk_buff *skb = NULL;
	struct mfc_cache *cache;
	struct mr_table *mrt;
	struct rtmsg *rtm;
	__be32 src, grp;
	u32 tableid;
	int err;

	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX,
			  rtm_ipv4_policy, extack);
	err = ipmr_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
	if (err < 0)
		goto errout;

	rtm = nlmsg_data(nlh);

	src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0;
	grp = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0;
	tableid = tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : 0;
Loading