3

I wrote a kernel module that sends generic Netlink multicasts, and wrote a userland client using libmnl that receives them.

That all works fine, but my client works even when it's not the root user, and I want to prevent that.

man 7 netlink says:

Only processes with an effective UID of 0 or the CAP_NET_ADMIN capability
may send or listen to a netlink multicast group.

Obviously the listen part of this is not true. I've tried CentOS 5 (2.6.18), CentOS 6 (2.6.32) and Ubuntu 14.04 (3.13).

I know that restricting receiving generic netlink commands incoming to the kernel to being only from root is possible with GENL_ADMIN_PERM flag, but is it possible to send multicasts from the kernel that can only be received by root?

EDIT: I shared some code for a kernel module that sends netlink multicasts, and client that receives them, at https://github.com/craig65535/mcast-exmpl. Build instructions are in the README.md, but I'll paste them here.

In one terminal:

$ make
$ sudo insmod mcast-exmpl.ko
$ cd client
$ make
$ ./client
genl ctrl msg
Family ID: 26
Mcast group ID: 4

(IDs may be different)

In another terminal, run a command that will do a TCP connect. mcast-exmpl hooks connects via a jprobe, so doing this will cause it to send a netlink multicast.

$ nc yahoo.com 80
^C
$

In the first terminal, you'll see a netlink multicast was received, even if you did not run client as root:

mcast-exmpl msg
SEND_NUM 55555

I'd like to modify this so the multicasts are only received when client is running as root, or, failing that, confirmation that I've found a bug either in the Linux documentation or Linux itself.

craig65535
  • 3,439
  • 1
  • 23
  • 49

1 Answers1

2

First off, I'm not familiar with the netlink IPC mechanism. However, I believe I have some useful information to share.

The reason you're able to receive despite being non-root is because of this change made to the kernel some time back which allowed non-root users to be able to read uevents.

Looking at Multicast from kernel to user space via Netlink in C, it appears that the kernel module can/should specify a struct netlink_kernel_cfg.

From LXR, it seems to be defined as

struct netlink_kernel_cfg {
        unsigned int    groups;
        unsigned int    flags;
        void            (*input)(struct sk_buff *skb);
        struct mutex    *cb_mutex;
        int             (*bind)(struct net *net, int group);
        void            (*unbind)(struct net *net, int group);
        bool            (*compare)(struct net *net, struct sock *sk);
};

Googling for the flags attribute yielded this result which says

The flags member can be NL_CFG_F_NONROOT_RECV or NL_CFG_F_NONROOT_SEND. When NL_CFG_F_NONROOT_RECV is set, a non-superuser can bind to a multicast group.

Community
  • 1
  • 1
Guru Prasad
  • 4,053
  • 2
  • 25
  • 43
  • Thank you for the explanation. In net/netlink/genetlink.c, the netlink_kernel_cfg has `.flags = NL_CFG_F_NONROOT_RECV`. So I suppose this is expected behaviour for generic netlink. – craig65535 Mar 09 '15 at 19:43
  • I would suggest you try to initialize `flags` to `0` and see what happens. Maybe the kernel defaults to `NL_CFG_F_NONROOT_RECV`, but it might be worth exploring. – Guru Prasad Mar 09 '15 at 20:19
  • Unfortunately I can't change the configuration for generic netlink. Other code uses it and would expect certain behaviour, and of course the kernel socket will have been created long before my module is loaded. I think ultimately I'm going to stop doing multicasts from my module, and instead unicast to a specific portid that I know is being used by a root-owned process. – craig65535 Mar 10 '15 at 00:37