Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netlink: Add new socket option to enable strict checking on dumps

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>

authored by

David Ahern and committed by
David S. Miller
89d35528 6ba1e6e8

+23 -1
+1
include/linux/netlink.h
··· 179 179 struct netlink_ext_ack *extack; 180 180 u16 family; 181 181 u16 min_dump_alloc; 182 + bool strict_check; 182 183 unsigned int prev_seq, seq; 183 184 long args[6]; 184 185 };
+1
include/uapi/linux/netlink.h
··· 155 155 #define NETLINK_LIST_MEMBERSHIPS 9 156 156 #define NETLINK_CAP_ACK 10 157 157 #define NETLINK_EXT_ACK 11 158 + #define NETLINK_DUMP_STRICT_CHK 12 158 159 159 160 struct nl_pktinfo { 160 161 __u32 group;
+20 -1
net/netlink/af_netlink.c
··· 1706 1706 nlk->flags &= ~NETLINK_F_EXT_ACK; 1707 1707 err = 0; 1708 1708 break; 1709 + case NETLINK_DUMP_STRICT_CHK: 1710 + if (val) 1711 + nlk->flags |= NETLINK_F_STRICT_CHK; 1712 + else 1713 + nlk->flags &= ~NETLINK_F_STRICT_CHK; 1714 + err = 0; 1715 + break; 1709 1716 default: 1710 1717 err = -ENOPROTOOPT; 1711 1718 } ··· 1802 1795 return -EINVAL; 1803 1796 len = sizeof(int); 1804 1797 val = nlk->flags & NETLINK_F_EXT_ACK ? 1 : 0; 1798 + if (put_user(len, optlen) || put_user(val, optval)) 1799 + return -EFAULT; 1800 + err = 0; 1801 + break; 1802 + case NETLINK_DUMP_STRICT_CHK: 1803 + if (len < sizeof(int)) 1804 + return -EINVAL; 1805 + len = sizeof(int); 1806 + val = nlk->flags & NETLINK_F_STRICT_CHK ? 1 : 0; 1805 1807 if (put_user(len, optlen) || put_user(val, optval)) 1806 1808 return -EFAULT; 1807 1809 err = 0; ··· 2298 2282 const struct nlmsghdr *nlh, 2299 2283 struct netlink_dump_control *control) 2300 2284 { 2285 + struct netlink_sock *nlk, *nlk2; 2301 2286 struct netlink_callback *cb; 2302 2287 struct sock *sk; 2303 - struct netlink_sock *nlk; 2304 2288 int ret; 2305 2289 2306 2290 refcount_inc(&skb->users); ··· 2333 2317 cb->module = control->module; 2334 2318 cb->min_dump_alloc = control->min_dump_alloc; 2335 2319 cb->skb = skb; 2320 + 2321 + nlk2 = nlk_sk(NETLINK_CB(skb).sk); 2322 + cb->strict_check = !!(nlk2->flags & NETLINK_F_STRICT_CHK); 2336 2323 2337 2324 if (control->start) { 2338 2325 ret = control->start(cb);
+1
net/netlink/af_netlink.h
··· 15 15 #define NETLINK_F_LISTEN_ALL_NSID 0x10 16 16 #define NETLINK_F_CAP_ACK 0x20 17 17 #define NETLINK_F_EXT_ACK 0x40 18 + #define NETLINK_F_STRICT_CHK 0x80 18 19 19 20 #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) 20 21 #define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long))