]> 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>
Tue, 3 Jun 2025 15:04:42 +0000 (15:04 +0000)
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/

(cherry picked from commit 3eb253e81fade5788e4462f651e28fc20358ebee)

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 fe66a4dff499f07f8fa3e91407e141fb54c7a2dd..36187b213b50e62e6d2321cd780dc747afd006b0 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,74 @@ 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);
+               named_main_earlywarning(
+                       "failed to connect to notification socket '%s': %s",
+                       path, isc_result_totext(result));
+       } else {
+               RUNTIME_CHECK(notify_fd != -1);
+       }
+}
+#else /* __linux__ */
+#define setup_systemd_notify(...)
+#endif /* __linux__ */
+
 static void
 setup_syslog(const char *progname) {
        int options;
@@ -287,6 +357,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 +433,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 +943,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_g_lctx, 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 8a73d1aca64bc4c2560e1387cbbf086e64f33042..b3f1b387665c8893a87a44eb1a9f7d75a56dae50 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>
@@ -10055,13 +10051,10 @@ view_loaded(void *arg) {
                              "FIPS mode is %s",
                              isc_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);
 
@@ -10206,9 +10199,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);
@@ -10706,17 +10698,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));
 
@@ -10733,12 +10719,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;
 }
 
@@ -11189,17 +11173,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));
 
@@ -11216,12 +11194,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 bfb4c734a907322d61aeb5d8dba73c7cf1230f2b..a7427da3f57a7c63cc8d561fb231beb45b9bc868 100644 (file)
@@ -951,26 +951,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.
 #