netlink: Add new socket option to enable strict checking on dumps
authorDavid Ahern <dsahern@gmail.com>
Mon, 8 Oct 2018 03:16:27 +0000 (20:16 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 8 Oct 2018 17:39:04 +0000 (10:39 -0700)
Add a new socket option, NETLINK_DUMP_STRICT_CHK, that userspace
can use via setsockopt to request strict checking of headers and
attributes on dump requests.

To get dump features such as kernel side filtering based on data in
the header or attributes appended to the dump request, userspace
must call setsockopt() for NETLINK_DUMP_STRICT_CHK and a non-zero
value. Since the netlink sock and its flags are private to the
af_netlink code, the strict checking flag is passed to dump handlers
via a flag in the netlink_callback struct.

For old userspace on new kernel there is no impact as all of the data
checks in later patches are wrapped in a check on the new strict flag.

For new userspace on old kernel, the setsockopt will fail and even if
new userspace sets data in the headers and appended attributes the
kernel will silently ignore it. Moving forward when the setsockopt
succeeds, the new userspace on old kernel means the dump request can
pass an attribute the kernel does not understand. The dump will then
fail as the older kernel does not understand it.

New userspace on new kernel setting the socket option gets the benefit
of the improved data dump.

Kernel side the NETLINK_DUMP_STRICT_CHK uapi is converted to a generic
NETLINK_F_STRICT_CHK flag which can potentially be leveraged for tighter
checking on the NEW, DEL, and SET commands.

Signed-off-by: David Ahern <dsahern@gmail.com>
Acked-by: Christian Brauner <christian@brauner.io>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netlink.h
include/uapi/linux/netlink.h
net/netlink/af_netlink.c
net/netlink/af_netlink.h

index 88c8a2d83eb3c78bed8edb635ed9c41d03e1155f..72580f1a72a224cf5cf53c4b0111d3bf0a42e8a6 100644 (file)
@@ -179,6 +179,7 @@ struct netlink_callback {
        struct netlink_ext_ack  *extack;
        u16                     family;
        u16                     min_dump_alloc;
+       bool                    strict_check;
        unsigned int            prev_seq, seq;
        long                    args[6];
 };
index 776bc92e91180725e75f0291b1635234d6b6875f..486ed1f0c0bc17f48dca895ebf9581aa7d69278d 100644 (file)
@@ -155,6 +155,7 @@ enum nlmsgerr_attrs {
 #define NETLINK_LIST_MEMBERSHIPS       9
 #define NETLINK_CAP_ACK                        10
 #define NETLINK_EXT_ACK                        11
+#define NETLINK_DUMP_STRICT_CHK                12
 
 struct nl_pktinfo {
        __u32   group;
index 7ac585f33a9e6f78b16482509d807169e3d730a2..e613a9f896004ec1c4ca7e1b6b047259b622ebd1 100644 (file)
@@ -1706,6 +1706,13 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
                        nlk->flags &= ~NETLINK_F_EXT_ACK;
                err = 0;
                break;
+       case NETLINK_DUMP_STRICT_CHK:
+               if (val)
+                       nlk->flags |= NETLINK_F_STRICT_CHK;
+               else
+                       nlk->flags &= ~NETLINK_F_STRICT_CHK;
+               err = 0;
+               break;
        default:
                err = -ENOPROTOOPT;
        }
@@ -1799,6 +1806,15 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname,
                        return -EFAULT;
                err = 0;
                break;
+       case NETLINK_DUMP_STRICT_CHK:
+               if (len < sizeof(int))
+                       return -EINVAL;
+               len = sizeof(int);
+               val = nlk->flags & NETLINK_F_STRICT_CHK ? 1 : 0;
+               if (put_user(len, optlen) || put_user(val, optval))
+                       return -EFAULT;
+               err = 0;
+               break;
        default:
                err = -ENOPROTOOPT;
        }
@@ -2282,9 +2298,9 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
                         const struct nlmsghdr *nlh,
                         struct netlink_dump_control *control)
 {
+       struct netlink_sock *nlk, *nlk2;
        struct netlink_callback *cb;
        struct sock *sk;
-       struct netlink_sock *nlk;
        int ret;
 
        refcount_inc(&skb->users);
@@ -2318,6 +2334,9 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
        cb->min_dump_alloc = control->min_dump_alloc;
        cb->skb = skb;
 
+       nlk2 = nlk_sk(NETLINK_CB(skb).sk);
+       cb->strict_check = !!(nlk2->flags & NETLINK_F_STRICT_CHK);
+
        if (control->start) {
                ret = control->start(cb);
                if (ret)
index 962de7b3c023d44e5ab5bc5a62544c181963d9cd..5f454c8de6a4de07996578538d98bfd8ad45b950 100644 (file)
@@ -15,6 +15,7 @@
 #define NETLINK_F_LISTEN_ALL_NSID      0x10
 #define NETLINK_F_CAP_ACK              0x20
 #define NETLINK_F_EXT_ACK              0x40
+#define NETLINK_F_STRICT_CHK           0x80
 
 #define NLGRPSZ(x)     (ALIGN(x, sizeof(unsigned long) * 8) / 8)
 #define NLGRPLONGS(x)  (NLGRPSZ(x)/sizeof(unsigned long))