]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
implement the systemd notification protocol manually, drop libsystemd
authorAydın Mercan <aydin@isc.org>
Sun, 16 Mar 2025 16:54:18 +0000 (19:54 +0300)
committerAydın Mercan <aydin@isc.org>
Thu, 27 Mar 2025 19:52:41 +0000 (22:52 +0300)
libsystemd, despite being useful, adds a huge surface area for just
using the sd_notify API. libsystemd's surface has been exploited in the
past [1].

Implement the systemd notification protocol by hand since it is just
sending newline-delimited datagrams to a UNIX socket. The code shouldn't
need more attention in the future since the notification protocol is
covered under systemd's stability promise [2].

We don't need to support VSOCK-backed service notifications since they
are only intended for virtual machine inits.

[1]: https://www.openwall.com/lists/oss-security/2024/03/29/4
[2]: https://systemd.io/PORTABILITY_AND_STABILITY/

bin/named/Makefile.am
bin/named/include/named/os.h
bin/named/os.c
bin/named/server.c
configure.ac

index 291bab5e2a58f5a5841295f9542bb1d3b9c61af8..aec650875ecf1ecb4b5d5100bab8665aef10119b 100644 (file)
@@ -13,7 +13,6 @@ AM_CPPFLAGS +=                                \
        $(MAXMINDDB_CFLAGS)             \
        $(DNSTAP_CFLAGS)                \
        $(LIBUV_CFLAGS)                 \
-       $(LIBSYSTEMD_CFLAGS)            \
        $(ZLIB_CFLAGS)
 
 if HAVE_JSON_C
@@ -108,7 +107,6 @@ named_LDADD =                                       \
        $(MAXMINDDB_LIBS)                       \
        $(DNSTAP_LIBS)                          \
        $(LIBUV_LIBS)                           \
-       $(LIBSYSTEMD_LIBS)                      \
        $(ZLIB_LIBS)
 
 if HAVE_JSON_C
index a150872f47d6ec8e895bdfef931377a19d941ac2..b38ebfad72212ef9c9bf65a6ab23d5acc8ade30a 100644 (file)
@@ -18,6 +18,7 @@
 #include <pwd.h>
 #include <stdbool.h>
 
+#include <isc/formatcheck.h>
 #include <isc/types.h>
 
 void
@@ -73,3 +74,15 @@ named_os_started(void);
 
 const char *
 named_os_uname(void);
+
+#ifdef __linux__
+void
+named_os_notify_systemd(const char *restrict format, ...)
+       ISC_FORMAT_PRINTF(1, 2);
+
+void
+named_os_notify_close(void);
+#else /* __linux__ */
+#define named_os_notify_systemd(...)
+#define named_os_notify_close(...)
+#endif /* __linux__ */
index 91f2d52ead00c04392e38d93ef39343a3a6d5fe2..52e13680a9225bad27422db94344b3fc8d518b99 100644 (file)
@@ -36,6 +36,7 @@
 #include <unistd.h>
 
 #include <isc/buffer.h>
+#include <isc/errno.h>
 #include <isc/file.h>
 #include <isc/result.h>
 #include <isc/strerr.h>
@@ -60,6 +61,7 @@ static int devnullfd = -1;
 static struct passwd *runas_pw = NULL;
 static bool done_setuid = false;
 static int dfd[2] = { -1, -1 };
+static int notify_fd = -1;
 
 static uid_t saved_uid = (uid_t)-1;
 static gid_t saved_gid = (gid_t)-1;
@@ -273,6 +275,79 @@ setperms(uid_t uid, gid_t gid) {
        }
 }
 
+#ifdef __linux__
+static isc_result_t
+connect_systemd_notify_unix(char *path) {
+       struct sockaddr_un addr;
+       isc_result_t result = ISC_R_SUCCESS;
+       size_t len;
+
+       addr.sun_family = AF_UNIX;
+
+       len = strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
+       if (len >= sizeof(addr.sun_path)) {
+               return ISC_R_NOSPACE;
+       }
+
+       /* Convert abstract unix sockets */
+       if (addr.sun_path[0] == '@') {
+               addr.sun_path[0] = '\0';
+       }
+
+       notify_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (notify_fd < 0) {
+               return isc_errno_toresult(errno);
+       }
+
+       if (connect(notify_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+               result = isc_errno_toresult(errno);
+               close(notify_fd);
+               notify_fd = -1;
+       }
+
+       return result;
+}
+
+static void
+setup_systemd_notify(void) {
+       isc_result_t result;
+       char *path;
+
+       REQUIRE(notify_fd == -1);
+
+       path = getenv("NOTIFY_SOCKET");
+
+       /* systemd notification is not set, oh well. */
+       if (path == NULL) {
+               return;
+       }
+
+       if (path[0] == '/' || path[0] == '@') {
+               result = connect_systemd_notify_unix(path);
+       } else if (strncmp(path, "vsock", 5) == 0) {
+               result = ISC_R_FAMILYNOSUPPORT;
+       } else {
+               result = ISC_R_INVALIDPROTO;
+       }
+
+       if (result != ISC_R_SUCCESS) {
+               RUNTIME_CHECK(notify_fd == -1);
+               isc_log_write(
+                       NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_MAIN,
+                       ISC_LOG_WARNING,
+                       "failed to connect to notification socket '%s': %s",
+                       path, isc_result_totext(result));
+       } else {
+               RUNTIME_CHECK(notify_fd != -1);
+               isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_MAIN,
+                             ISC_LOG_INFO,
+                             "connected to notification socket '%s'", path);
+       }
+}
+#else /* __linux__ */
+#define setup_systemd_notify(...)
+#endif /* __linux__ */
+
 static void
 setup_syslog(const char *progname) {
        int options;
@@ -287,6 +362,7 @@ setup_syslog(const char *progname) {
 void
 named_os_init(const char *progname) {
        setup_syslog(progname);
+       setup_systemd_notify();
 #if HAVE_LIBCAP
        linux_initialprivs();
 #endif /* HAVE_LIBCAP */
@@ -362,6 +438,13 @@ named_os_daemonize(void) {
                        (void)dup2(devnullfd, STDERR_FILENO);
                }
        }
+
+       /*
+        * Will be closed from SOCK_CLOEXEC but should be set to -1 again before
+        * reconnecting to the notification socket.
+        */
+       notify_fd = -1;
+       setup_systemd_notify();
 }
 
 void
@@ -865,3 +948,38 @@ named_os_uname(void) {
        }
        return unamep;
 }
+
+#ifdef __linux__
+void
+named_os_notify_systemd(const char *restrict format, ...) {
+       char buffer[512];
+       va_list ap;
+       int len;
+
+       if (notify_fd == -1 || format == NULL) {
+               return;
+       }
+
+       va_start(ap, format);
+       len = vsnprintf(buffer, sizeof(buffer), format, ap);
+       va_end(ap);
+
+       /* Be very loud if notification is too long */
+       RUNTIME_CHECK(len > 0 && len < (int)sizeof(buffer));
+
+       if (write(notify_fd, buffer, len) != len) {
+               isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_MAIN,
+                             ISC_LOG_ERROR,
+                             "failed writing to notification socket '%s'",
+                             isc_result_totext(isc_errno_toresult(errno)));
+       }
+}
+
+void
+named_os_notify_close(void) {
+       if (notify_fd != -1) {
+               close(notify_fd);
+               notify_fd = -1;
+       }
+}
+#endif /* __linux__ */
index bc80f14d6986559d4aa7ca89b0ba50b940c524c6..b67773bd4fdc87aa70aa16fa3c29be7dc1429163 100644 (file)
 #include <fstrm.h>
 #endif
 
-#ifdef HAVE_LIBSYSTEMD
-#include <systemd/sd-daemon.h>
-#endif
-
 #include <isc/async.h>
 #include <isc/attributes.h>
 #include <isc/base64.h>
@@ -9402,13 +9398,10 @@ view_loaded(void *arg) {
                              ISC_LOG_NOTICE, "FIPS mode is %s",
                              isc_crypto_fips_mode() ? "enabled" : "disabled");
 
-#if HAVE_LIBSYSTEMD
-               sd_notifyf(0,
-                          "READY=1\n"
-                          "STATUS=running\n"
-                          "MAINPID=%" PRId64 "\n",
-                          (int64_t)getpid());
-#endif /* HAVE_LIBSYSTEMD */
+               named_os_notify_systemd("READY=1\n"
+                                       "STATUS=running\n"
+                                       "MAINPID=%" PRId64 "\n",
+                                       (int64_t)getpid());
 
                atomic_store(&server->reload_status, NAMED_RELOAD_DONE);
 
@@ -9547,9 +9540,8 @@ shutdown_server(void *arg) {
        bool flush = server->flushonshutdown;
        named_cache_t *nsc = NULL;
 
-#if HAVE_LIBSYSTEMD
-       sd_notify(0, "STOPPING=1\n");
-#endif /* HAVE_LIBSYSTEMD */
+       named_os_notify_systemd("STOPPING=1\n");
+       named_os_notify_close();
 
        isc_signal_stop(server->sighup);
        isc_signal_destroy(&server->sighup);
@@ -10041,17 +10033,11 @@ reload(named_server_t *server) {
        isc_result_t result;
 
        atomic_store(&server->reload_status, NAMED_RELOAD_IN_PROGRESS);
-#if HAVE_LIBSYSTEMD
-       char buf[512];
-       int n = snprintf(buf, sizeof(buf),
-                        "RELOADING=1\n"
-                        "MONOTONIC_USEC=%" PRIu64 "\n"
-                        "STATUS=reload command received\n",
-                        (uint64_t)isc_time_monotonic() / NS_PER_US);
-       if (n > 0 && (size_t)n < sizeof(buf)) {
-               sd_notify(0, buf);
-       }
-#endif /* HAVE_LIBSYSTEMD */
+
+       named_os_notify_systemd("RELOADING=1\n"
+                               "MONOTONIC_USEC=%" PRIu64 "\n"
+                               "STATUS=reload command received\n",
+                               (uint64_t)isc_time_monotonic() / NS_PER_US);
 
        CHECK(loadconfig(server));
 
@@ -10066,12 +10052,10 @@ reload(named_server_t *server) {
                atomic_store(&server->reload_status, NAMED_RELOAD_FAILED);
        }
 cleanup:
-#if HAVE_LIBSYSTEMD
-       sd_notifyf(0,
-                  "READY=1\n"
-                  "STATUS=reload command finished: %s\n",
-                  isc_result_totext(result));
-#endif /* HAVE_LIBSYSTEMD */
+       named_os_notify_systemd("READY=1\n"
+                               "STATUS=reload command finished: %s\n",
+                               isc_result_totext(result));
+
        return result;
 }
 
@@ -10542,17 +10526,11 @@ isc_result_t
 named_server_reconfigcommand(named_server_t *server) {
        isc_result_t result;
        atomic_store(&server->reload_status, NAMED_RELOAD_IN_PROGRESS);
-#if HAVE_LIBSYSTEMD
-       char buf[512];
-       int n = snprintf(buf, sizeof(buf),
-                        "RELOADING=1\n"
-                        "MONOTONIC_USEC=%" PRIu64 "\n"
-                        "STATUS=reconfig command received\n",
-                        (uint64_t)isc_time_monotonic() / NS_PER_US);
-       if (n > 0 && (size_t)n < sizeof(buf)) {
-               sd_notify(0, buf);
-       }
-#endif /* HAVE_LIBSYSTEMD */
+
+       named_os_notify_systemd("RELOADING=1\n"
+                               "MONOTONIC_USEC=%" PRIu64 "\n"
+                               "STATUS=reconfig command received\n",
+                               (uint64_t)isc_time_monotonic() / NS_PER_US);
 
        CHECK(loadconfig(server));
 
@@ -10567,12 +10545,10 @@ named_server_reconfigcommand(named_server_t *server) {
                atomic_store(&server->reload_status, NAMED_RELOAD_FAILED);
        }
 cleanup:
-#if HAVE_LIBSYSTEMD
-       sd_notifyf(0,
-                  "READY=1\n"
-                  "STATUS=reconfig command finished: %s\n",
-                  isc_result_totext(result));
-#endif /* HAVE_LIBSYSTEMD */
+       named_os_notify_systemd("READY=1\n"
+                               "STATUS=reconfig command finished: %s\n",
+                               isc_result_totext(result));
+
        return result;
 }
 
index b742274f0049a8742e44783aa01a89eb261b6724..c3c16f69db3a2cbb9881ef0f602a797ac9c5192b 100644 (file)
@@ -859,26 +859,6 @@ AS_CASE([$with_zlib],
 AC_SUBST([ZLIB_CFLAGS])
 AC_SUBST([ZLIB_LIBS])
 
-#
-# was --with-libsystemd specified?
-#
-# [pairwise: --with-libsystemd=auto, --with-libsystemd=yes, --without-libsystemd]
-AC_ARG_WITH([libsystemd],
-           [AS_HELP_STRING([--with-libsystemd],
-                           [build with libsystemd integration [default=auto]])],
-           [], [with_libsystemd=auto])
-
-AS_CASE([$with_libsystemd],
-       [no],[],
-       [auto],[PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd],
-                                 [AC_DEFINE([HAVE_LIBSYSTEMD], [1], [Use libsystemd library])],
-                                 [:])],
-       [yes],[PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd],
-                                [AC_DEFINE([HAVE_LIBSYSTEMD], [1], [Use libsystemd library])])],
-       [AC_MSG_ERROR([Specifying libsystemd installation path is not supported, adjust PKG_CONFIG_PATH instead])])
-AC_SUBST([LIBSYSTEMD_CFLAGS])
-AC_SUBST([LIBSYSTEMD_LIBS])
-
 #
 # Check if the system supports glibc-compatible backtrace() function.
 #