netlink: loosen sender checks to allow other multicast messages
authorDan Williams <dcbw@redhat.com>
Tue, 20 Apr 2010 22:22:36 +0000 (15:22 -0700)
committerDan Williams <dcbw@redhat.com>
Tue, 20 Apr 2010 22:22:36 +0000 (15:22 -0700)
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

index a984ebe..2f11ff1 100644 (file)
@@ -25,6 +25,9 @@
  * Copyright (C) 2004 Novell, Inc.
  */
 
+/* for struct ucred */
+#include <config.h>
+
 #include <errno.h>
 #include <string.h>
 #include <sys/types.h>
@@ -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;
+
+       /* 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;
 
-       if (hdr->nlmsg_pid != 0)
+       /* 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,