From cadc56abc80b62b328ff345f22a3642e124c4740 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 Apr 2010 15:22:36 -0700 Subject: [PATCH] netlink: loosen sender checks to allow other multicast messages We need the IFLA_PROTINFO messages, and these are apparently sent (still by the kernel) but with our own PID. So the current checks that limit the netlink PID to zero block these messages out. Instead (like udev) we should be checking the actual sender of the message usign unix socket credentials. Second, at least at this point only privileged processes can send netlink multicast messages, so as long as the: * the other end of the socket is UID 0 AND * the netlink PID is 0 OR * the message is multicast and sent to our netlink PID the we accept the message and process it as normal. For another example of this, see 'netlink.c' from the dhcp6s program; do a Google search for "IFLA_PROTINFO" to find it. --- src/nm-netlink-monitor.c | 45 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/nm-netlink-monitor.c b/src/nm-netlink-monitor.c index a984ebe236..2f11ff154e 100644 --- a/src/nm-netlink-monitor.c +++ b/src/nm-netlink-monitor.c @@ -25,6 +25,9 @@ * Copyright (C) 2004 Novell, Inc. */ +/* for struct ucred */ +#include + #include #include #include @@ -209,15 +212,41 @@ out: static int netlink_event_input (struct nl_msg *msg, void *arg) { + NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg); + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); struct nlmsghdr *hdr = nlmsg_hdr (msg); + struct ucred *creds = nlmsg_get_creds (msg); + guint32 local_port; + gboolean accept_msg = FALSE; - if (hdr->nlmsg_pid != 0) + /* Only messages sent from the kernel */ + if (!creds || creds->uid != 0) { + nm_log_dbg (LOGD_HW, "ignoring netlink message from UID %d", + creds ? creds->uid : -1); return NL_STOP; + } + + /* Accept any messages from the kernel */ + if (hdr->nlmsg_pid == 0) + accept_msg = TRUE; + + /* And any multicast message directed to our netlink PID, since multicast + * currently requires CAP_ADMIN to use. + */ + local_port = nl_socket_get_local_port (priv->nlh); + if ((hdr->nlmsg_pid == local_port) && (hdr->nlmsg_flags & NLM_F_MULTI)) + accept_msg = TRUE; + + if (accept_msg == FALSE) { + nm_log_dbg (LOGD_HW, "ignoring netlink message from PID %d (local PID %d, multicast %d)", + hdr->nlmsg_pid, + local_port, + (hdr->nlmsg_flags & NLM_F_MULTI)); + return NL_STOP; + } nl_msg_parse (msg, &netlink_object_message_handler, arg); - - /* Stop processing messages */ - return NL_STOP; + return NL_SKIP; } static gboolean @@ -304,6 +333,14 @@ nm_netlink_monitor_open_connection (NMNetlinkMonitor *monitor, goto error; } + if (nl_set_passcred (priv->nlh, 1) < 0) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT, + _("unable to enable netlink handle credential passing: %s"), + nl_geterror ()); + goto error; + } + if (nl_socket_add_membership (priv->nlh, RTNLGRP_LINK) < 0) { g_set_error (error, NM_NETLINK_MONITOR_ERROR, NM_NETLINK_MONITOR_ERROR_NETLINK_JOIN_GROUP,