]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
cmdmon: listen on Unix domain socket
authorMiroslav Lichvar <mlichvar@redhat.com>
Tue, 28 Jul 2015 13:29:30 +0000 (15:29 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Tue, 28 Jul 2015 13:29:30 +0000 (15:29 +0200)
In addition to the IPv4/IPv6 command sockets, create also a Unix domain
socket to process cmdmon requests. For now, there is no difference for
authorized commands, packets from all sockets need to be authenticated.

The default path of the socket is /var/run/chrony/chronyd.sock. It can
be configured with the bindcmdaddress directive with an address starting
with /.

cmdmon.c
conf.c
conf.h

index c55097a5aefc61dd4054d053967c794db3e109df..3af3d9c3a3f145ed5f1e29121c1c2ad6e3fc2555 100644 (file)
--- a/cmdmon.c
+++ b/cmdmon.c
 
 /* ================================================== */
 
-union sockaddr_in46 {
+union sockaddr_all {
   struct sockaddr_in in4;
 #ifdef FEAT_IPV6
   struct sockaddr_in6 in6;
 #endif
-  struct sockaddr u;
+  struct sockaddr_un un;
+  struct sockaddr sa;
 };
 
 /* File descriptors for command and monitoring sockets */
+static int sock_fdu;
 static int sock_fd4;
 #ifdef FEAT_IPV6
 static int sock_fd6;
@@ -184,7 +186,7 @@ prepare_socket(int family, int port_number)
 {
   int sock_fd;
   socklen_t my_addr_len;
-  union sockaddr_in46 my_addr;
+  union sockaddr_all my_addr;
   IPAddr bind_address;
   int on_off = 1;
 
@@ -198,29 +200,31 @@ prepare_socket(int family, int port_number)
   /* Close on exec */
   UTI_FdSetCloexec(sock_fd);
 
-  /* Allow reuse of port number */
-  if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on_off, sizeof(on_off)) < 0) {
-    LOG(LOGS_ERR, LOGF_CmdMon, "Could not set reuseaddr socket options");
-    /* Don't quit - we might survive anyway */
-  }
+  if (family != AF_UNIX) {
+    /* Allow reuse of port number */
+    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on_off, sizeof(on_off)) < 0) {
+      LOG(LOGS_ERR, LOGF_CmdMon, "Could not set reuseaddr socket options");
+      /* Don't quit - we might survive anyway */
+    }
 
 #ifdef IP_FREEBIND
-  /* Allow binding to address that doesn't exist yet */
-  if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
-    LOG(LOGS_ERR, LOGF_CmdMon, "Could not set free bind socket option");
-  }
+    /* Allow binding to address that doesn't exist yet */
+    if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
+      LOG(LOGS_ERR, LOGF_CmdMon, "Could not set free bind socket option");
+    }
 #endif
 
 #ifdef FEAT_IPV6
-  if (family == AF_INET6) {
+    if (family == AF_INET6) {
 #ifdef IPV6_V6ONLY
-    /* Receive IPv6 packets only */
-    if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
-      LOG(LOGS_ERR, LOGF_CmdMon, "Could not request IPV6_V6ONLY socket option");
+      /* Receive IPv6 packets only */
+      if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
+        LOG(LOGS_ERR, LOGF_CmdMon, "Could not request IPV6_V6ONLY socket option");
+      }
+#endif
     }
 #endif
   }
-#endif
 
   memset(&my_addr, 0, sizeof (my_addr));
 
@@ -252,11 +256,19 @@ prepare_socket(int family, int port_number)
         my_addr.in6.sin6_addr = in6addr_loopback;
       break;
 #endif
+    case AF_UNIX:
+      my_addr_len = sizeof (my_addr.un);
+      my_addr.un.sun_family = family;
+      if (snprintf(my_addr.un.sun_path, sizeof (my_addr.un.sun_path), "%s",
+                   CNF_GetBindCommandPath()) >= sizeof (my_addr.un.sun_path))
+        LOG_FATAL(LOGF_CmdMon, "Unix socket path too long");
+      unlink(my_addr.un.sun_path);
+      break;
     default:
       assert(0);
   }
 
-  if (bind(sock_fd, &my_addr.u, my_addr_len) < 0) {
+  if (bind(sock_fd, &my_addr.sa, my_addr_len) < 0) {
     LOG(LOGS_ERR, LOGF_CmdMon, "Could not bind %s command socket : %s",
         UTI_SockaddrFamilyToString(family), strerror(errno));
     close(sock_fd);
@@ -302,6 +314,11 @@ CAM_Initialise(int family)
   free_replies = NULL;
   kept_replies.next = NULL;
 
+  if (CNF_GetBindCommandPath()[0])
+    sock_fdu = prepare_socket(AF_UNIX, 0);
+  else
+    sock_fdu = -1;
+
   port_number = CNF_GetCommandPort();
 
   if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET4))
@@ -332,6 +349,12 @@ CAM_Initialise(int family)
 void
 CAM_Finalise(void)
 {
+  if (sock_fdu >= 0) {
+    SCH_RemoveInputFileHandler(sock_fdu);
+    close(sock_fdu);
+    unlink(CNF_GetBindCommandPath());
+  }
+  sock_fdu = -1;
   if (sock_fd4 >= 0) {
     SCH_RemoveInputFileHandler(sock_fd4);
     close(sock_fd4);
@@ -680,7 +703,7 @@ token_acknowledged(unsigned long token, struct timeval *now)
 /* ================================================== */
 
 static void
-transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len)
+transmit_reply(CMD_Reply *msg, union sockaddr_all *where_to, int auth_len)
 {
   int status;
   int tx_message_length;
@@ -689,9 +712,9 @@ transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len)
   unsigned short port;
   IPAddr ip;
   
-  UTI_SockaddrToIPAndPort(&where_to->u, &ip, &port);
+  UTI_SockaddrToIPAndPort(&where_to->sa, &ip, &port);
 
-  switch (where_to->u.sa_family) {
+  switch (where_to->sa.sa_family) {
     case AF_INET:
       sock_fd = sock_fd4;
       addrlen = sizeof (where_to->in4);
@@ -702,13 +725,17 @@ transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len)
       addrlen = sizeof (where_to->in6);
       break;
 #endif
+    case AF_UNIX:
+      sock_fd = sock_fdu;
+      addrlen = sizeof (where_to->un);
+      break;
     default:
       assert(0);
   }
 
   tx_message_length = PKL_ReplyLength(msg) + auth_len;
   status = sendto(sock_fd, (void *) msg, tx_message_length, 0,
-                  &where_to->u, addrlen);
+                  &where_to->sa, addrlen);
 
   if (status < 0) {
     DEBUG_LOG(LOGF_CmdMon, "Could not send to %s:%hu fd %d : %s",
@@ -1497,7 +1524,7 @@ read_from_cmd_socket(void *anything)
   CMD_Reply tx_message, *prev_tx_message;
   int rx_message_length, tx_message_length;
   int sock_fd;
-  union sockaddr_in46 where_from;
+  union sockaddr_all where_from;
   socklen_t from_length;
   IPAddr remote_ip;
   unsigned short remote_port;
@@ -1523,7 +1550,7 @@ read_from_cmd_socket(void *anything)
 
   sock_fd = (long)anything;
   status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, flags,
-                    &where_from.u, &from_length);
+                    &where_from.sa, &from_length);
 
   if (status < 0) {
     LOG(LOGS_WARN, LOGF_CmdMon, "Error [%s] reading from control socket %d",
@@ -1539,19 +1566,30 @@ read_from_cmd_socket(void *anything)
   /* Get current time cheaply */
   SCH_GetLastEventTime(&cooked_now, NULL, &now);
 
-  UTI_SockaddrToIPAndPort(&where_from.u, &remote_ip, &remote_port);
+  UTI_SockaddrToIPAndPort(&where_from.sa, &remote_ip, &remote_port);
 
-  /* Check if it's a loopback address (127.0.0.1 or ::1) */
+  /* Check if it's from localhost (127.0.0.1, ::1, or Unix domain) */
   switch (remote_ip.family) {
     case IPADDR_INET4:
+      assert(sock_fd == sock_fd4);
       localhost = remote_ip.addr.in4 == INADDR_LOOPBACK;
       break;
 #ifdef FEAT_IPV6
     case IPADDR_INET6:
+      assert(sock_fd == sock_fd6);
       localhost = !memcmp(remote_ip.addr.in6, &in6addr_loopback,
                           sizeof (in6addr_loopback));
       break;
 #endif
+    case IPADDR_UNSPEC:
+      /* Unix domain socket */
+      if (where_from.sa.sa_family != AF_UNIX) {
+        DEBUG_LOG(LOGF_CmdMon, "Read command packet with no address");
+        return;
+      }
+      assert(sock_fd == sock_fdu);
+      localhost = 1;
+      break;
     default:
       assert(0);
   }
@@ -1711,7 +1749,7 @@ read_from_cmd_socket(void *anything)
       /* Just send this message again */
       tx_message_length = PKL_ReplyLength(prev_tx_message);
       status = sendto(sock_fd, (void *) prev_tx_message, tx_message_length, 0,
-                      &where_from.u, from_length);
+                      &where_from.sa, from_length);
       if (status < 0) {
         DEBUG_LOG(LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&remote_ip), remote_port);
       }
diff --git a/conf.c b/conf.c
index a83d937ca0272708c540ad38fabc495a55370a10..6d4ee6707a1bd8494fdd3cda26d78855a906902e 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -182,6 +182,9 @@ static IPAddr bind_acq_address4, bind_acq_address6;
    the loopback address will be used */
 static IPAddr bind_cmd_address4, bind_cmd_address6;
 
+/* Path to the Unix domain command socket. */
+static char *bind_cmd_path;
+
 /* Filename to use for storing pid of running chronyd, to prevent multiple
  * chronyds being started. */
 static char *pidfile;
@@ -320,6 +323,7 @@ CNF_Initialise(int r)
 
   dumpdir = Strdup(".");
   logdir = Strdup(".");
+  bind_cmd_path = Strdup("/var/run/chrony/chronyd.sock");
   pidfile = Strdup("/var/run/chronyd.pid");
   rtc_device = Strdup("/dev/rtc");
   user = Strdup(DEFAULT_USER);
@@ -349,6 +353,7 @@ CNF_Finalise(void)
   Free(keys_file);
   Free(leapsec_tz);
   Free(logdir);
+  Free(bind_cmd_path);
   Free(pidfile);
   Free(rtc_device);
   Free(rtc_file);
@@ -1113,7 +1118,14 @@ parse_bindcmdaddress(char *line)
   IPAddr ip;
 
   check_number_of_args(line, 1);
-  if (UTI_StringToIP(line, &ip)) {
+
+  /* Address starting with / is for the Unix domain socket */
+  if (line[0] == '/') {
+    parse_string(line, &bind_cmd_path);
+    /* / disables the socket */
+    if (!strcmp(bind_cmd_path, "/"))
+        bind_cmd_path[0] = '\0';
+  } else if (UTI_StringToIP(line, &ip)) {
     if (ip.family == IPADDR_INET4)
       bind_cmd_address4 = ip;
     else if (ip.family == IPADDR_INET6)
@@ -1697,6 +1709,14 @@ CNF_GetBindAcquisitionAddress(int family, IPAddr *addr)
 
 /* ================================================== */
 
+char *
+CNF_GetBindCommandPath(void)
+{
+  return bind_cmd_path;
+}
+
+/* ================================================== */
+
 void
 CNF_GetBindCommandAddress(int family, IPAddr *addr)
 {
diff --git a/conf.h b/conf.h
index ec08ded6e63e269e23e76ecad40d6eb62ec44f5e..ff74ad78dc6f0295448b78b131d32fb22780f336 100644 (file)
--- a/conf.h
+++ b/conf.h
@@ -75,6 +75,7 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
 extern void CNF_GetBindAddress(int family, IPAddr *addr);
 extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
 extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
+extern char *CNF_GetBindCommandPath(void);
 extern char *CNF_GetPidFile(void);
 extern REF_LeapMode CNF_GetLeapSecMode(void);
 extern char *CNF_GetLeapSecTimezone(void);