net/neighbor: Update neigh_dump_info for strict data checking

Update neigh_dump_info for strict data checking. If the flag is set,
the dump request is expected to have an ndmsg 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 NDA_IFINDEX and
NDA_MASTER 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 <dsahern@gmail.com>
Acked-by: Christian Brauner <christian@brauner.io>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David Ahern 2018-10-07 20:16:36 -07:00 committed by David S. Miller
parent e8ba330ac0
commit 51183d233b

View File

@ -2426,11 +2426,73 @@ out:
}
static int neigh_valid_dump_req(const struct nlmsghdr *nlh,
bool strict_check,
struct neigh_dump_filter *filter,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[NDA_MAX + 1];
int err, i;
if (strict_check) {
struct ndmsg *ndm;
if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) {
NL_SET_ERR_MSG(extack, "Invalid header for neighbor dump request");
return -EINVAL;
}
ndm = nlmsg_data(nlh);
if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_ifindex ||
ndm->ndm_state || ndm->ndm_flags || ndm->ndm_type) {
NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor dump request");
return -EINVAL;
}
err = nlmsg_parse_strict(nlh, sizeof(struct ndmsg), tb, NDA_MAX,
NULL, extack);
} else {
err = nlmsg_parse(nlh, sizeof(struct ndmsg), tb, NDA_MAX,
NULL, extack);
}
if (err < 0)
return err;
for (i = 0; i <= NDA_MAX; ++i) {
if (!tb[i])
continue;
/* all new attributes should require strict_check */
switch (i) {
case NDA_IFINDEX:
if (nla_len(tb[i]) != sizeof(u32)) {
NL_SET_ERR_MSG(extack, "Invalid IFINDEX attribute in neighbor dump request");
return -EINVAL;
}
filter->dev_idx = nla_get_u32(tb[i]);
break;
case NDA_MASTER:
if (nla_len(tb[i]) != sizeof(u32)) {
NL_SET_ERR_MSG(extack, "Invalid MASTER attribute in neighbor dump request");
return -EINVAL;
}
filter->master_idx = nla_get_u32(tb[i]);
break;
default:
if (strict_check) {
NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor dump request");
return -EINVAL;
}
}
}
return 0;
}
static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
{
const struct nlmsghdr *nlh = cb->nlh;
struct neigh_dump_filter filter = {};
struct nlattr *tb[NDA_MAX + 1];
struct neigh_table *tbl;
int t, family, s_t;
int proxy = 0;
@ -2445,20 +2507,10 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
((struct ndmsg *)nlmsg_data(nlh))->ndm_flags == NTF_PROXY)
proxy = 1;
err = nlmsg_parse(nlh, sizeof(struct ndmsg), tb, NDA_MAX, NULL,
cb->extack);
if (!err) {
if (tb[NDA_IFINDEX]) {
if (nla_len(tb[NDA_IFINDEX]) != sizeof(u32))
return -EINVAL;
filter.dev_idx = nla_get_u32(tb[NDA_IFINDEX]);
}
if (tb[NDA_MASTER]) {
if (nla_len(tb[NDA_MASTER]) != sizeof(u32))
return -EINVAL;
filter.master_idx = nla_get_u32(tb[NDA_MASTER]);
}
}
err = neigh_valid_dump_req(nlh, cb->strict_check, &filter, cb->extack);
if (err < 0 && cb->strict_check)
return err;
s_t = cb->args[0];
for (t = 0; t < NEIGH_NR_TABLES; t++) {