On Thu, Oct 04, 2018 at 02:33:43PM -0700, David Ahern wrote: > From: David Ahern <dsah...@gmail.com> > > Update rtnl_dump_ifinfo 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 IFA_TARGET_NETNSID, > IFLA_EXT_MASK, IFLA_MASTER, and IFLA_LINKINFO attributes are supported. > > Existing code does not fail the dump if nlmsg_parse fails. That behavior > is kept for non-strict checking. > > Signed-off-by: David Ahern <dsah...@gmail.com> > --- > net/core/rtnetlink.c | 97 > +++++++++++++++++++++++++++++++++++++--------------- > 1 file changed, 69 insertions(+), 28 deletions(-) > > diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c > index c3b434d724ea..4fd27b5db787 100644 > --- a/net/core/rtnetlink.c > +++ b/net/core/rtnetlink.c > @@ -1880,6 +1880,8 @@ EXPORT_SYMBOL_GPL(rtnl_get_net_ns_capable); > > static int rtnl_dump_ifinfo(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 net *tgt_net = net; > int h, s_h; > @@ -1892,44 +1894,84 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, > struct netlink_callback *cb) > unsigned int flags = NLM_F_MULTI; > int master_idx = 0; > int netnsid = -1; > - int err; > + int err, i; > int hdrlen; > > s_h = cb->args[0]; > s_idx = cb->args[1]; > > - /* A hack to preserve kernel<->userspace interface. > - * The correct header is ifinfomsg. It is consistent with rtnl_getlink. > - * However, before Linux v3.9 the code here assumed rtgenmsg and that's > - * what iproute2 < v3.9.0 used. > - * We can detect the old iproute2. Even including the IFLA_EXT_MASK > - * attribute, its netlink message is shorter than struct ifinfomsg. > - */ > - hdrlen = nlmsg_len(cb->nlh) < sizeof(struct ifinfomsg) ? > - sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg); > + if (cb->strict_check) { > + struct ifinfomsg *ifm; > > - if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX, > - ifla_policy, cb->extack) >= 0) { > - if (tb[IFLA_TARGET_NETNSID]) { > - netnsid = nla_get_s32(tb[IFLA_TARGET_NETNSID]); > - tgt_net = rtnl_get_net_ns_capable(skb->sk, netnsid); > - if (IS_ERR(tgt_net)) > - return PTR_ERR(tgt_net); > + hdrlen = sizeof(*ifm); > + if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) { > + NL_SET_ERR_MSG(extack, "Invalid header"); > + return -EINVAL; > } > > - if (tb[IFLA_EXT_MASK]) > - ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); > - > - if (tb[IFLA_MASTER]) > - master_idx = nla_get_u32(tb[IFLA_MASTER]); > + 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 > dump request"); > + return -EINVAL; > + } > + if (ifm->ifi_index) { > + NL_SET_ERR_MSG(extack, "Filter by device index not > supported"); > + return -EINVAL; > + } > + } else { > + /* A hack to preserve kernel<->userspace interface. > + * The correct header is ifinfomsg. It is consistent with > + * rtnl_getlink. However, before Linux v3.9 the code here > + * assumed rtgenmsg and that's what iproute2 < v3.9.0 used. > + * We can detect the old iproute2. Even including the > + * IFLA_EXT_MASK attribute, its netlink message is shorter > + * than struct ifinfomsg. > + */ > + hdrlen = nlmsg_len(nlh) < sizeof(struct ifinfomsg) ? > + sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg); > + } > > - if (tb[IFLA_LINKINFO]) > - kind_ops = linkinfo_to_kind_ops(tb[IFLA_LINKINFO]); > + err = nlmsg_parse(nlh, hdrlen, tb, IFLA_MAX, ifla_policy, extack); > + if (err < 0) { > + if (cb->strict_check) > + return -EINVAL; > + goto walk_entries; > + } > > - if (master_idx || kind_ops) > - flags |= NLM_F_DUMP_FILTERED; > + for (i = 0; i <= IFLA_MAX; ++i) { > + if (!tb[i]) > + continue; > + switch (i) { > + case IFLA_TARGET_NETNSID: > + netnsid = nla_get_s32(tb[i]); > + tgt_net = rtnl_get_net_ns_capable(skb->sk, netnsid); > + if (IS_ERR(tgt_net)) { > + NL_SET_ERR_MSG(extack, "Invalid target > namespace id"); > + return PTR_ERR(tgt_net); > + } > + break; > + case IFLA_EXT_MASK: > + ext_filter_mask = nla_get_u32(tb[i]); > + break; > + case IFLA_MASTER: > + master_idx = nla_get_u32(tb[i]); > + break; > + case IFLA_LINKINFO: > + kind_ops = linkinfo_to_kind_ops(tb[i]); > + break; > + default: > + if (cb->strict_check) { > + NL_SET_ERR_MSG(extack, "Unsupported attribute > in dump request"); > + return -EINVAL; > + } > + }
This might make sense to be split into two helpers for parsing: <blablabla>_strict() and <blablabla>_lenient(). :) > } > > + if (master_idx || kind_ops) > + flags |= NLM_F_DUMP_FILTERED; > + > +walk_entries: > for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { > idx = 0; > head = &tgt_net->dev_index_head[h]; > @@ -1941,8 +1983,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct > netlink_callback *cb) > err = rtnl_fill_ifinfo(skb, dev, net, > RTM_NEWLINK, > NETLINK_CB(cb->skb).portid, > - cb->nlh->nlmsg_seq, 0, > - flags, > + nlh->nlmsg_seq, 0, flags, > ext_filter_mask, 0, NULL, 0, > netnsid); > > -- > 2.11.0 >