Index | Thread | Search

From:
Alexander Bluhm <bluhm@openbsd.org>
Subject:
syslogd separate parent program
To:
tech@openbsd.org
Date:
Sun, 7 Jun 2026 18:29:25 +0200

Download raw body.

Thread
Hi,

syslogd forks and execs its parent process to keep privileged parts
separated.  This parent process can be easily implemented as a
separate program.  Then its process image is smaller, especially
without additional libs.

The new parent got its own main() and a minimal debug log function.

ok?

bluhm

Index: usr.sbin/syslogd/Makefile
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/Makefile,v
diff -u -p -r1.9 Makefile
--- usr.sbin/syslogd/Makefile	13 Jan 2022 10:34:07 -0000	1.9
+++ usr.sbin/syslogd/Makefile	6 Jun 2026 20:42:37 -0000
@@ -1,10 +1,14 @@
 #	$OpenBSD: Makefile,v 1.9 2022/01/13 10:34:07 martijn Exp $
 
-PROG=	syslogd
-SRCS=	evbuffer_tls.c log.c parsemsg.c privsep.c privsep_fdpass.c ringbuf.c \
-	syslogd.c ttymsg.c
-MAN=	syslogd.8 syslog.conf.5
-LDADD=	-levent -ltls -lssl -lcrypto
-DPADD=	${LIBEVENT} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO}
+PROGS=		syslogd syslogd-parent
+
+SRCS_syslogd=	evbuffer_tls.c log.c parsemsg.c privsep.c \
+		privsep_fdpass.c ringbuf.c syslogd.c ttymsg.c
+LDADD_syslogd=	-levent -ltls -lssl -lcrypto
+DPADD_syslogd=	${LIBEVENT} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO}
+
+SRCS_syslogd-parent=	parent.c privsep.c privsep_fdpass.c
+
+MAN=		syslogd.8 syslog.conf.5
 
 .include <bsd.prog.mk>
Index: usr.sbin/syslogd/parent.c
===================================================================
RCS file: usr.sbin/syslogd/parent.c
diff -N usr.sbin/syslogd/parent.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.sbin/syslogd/parent.c	7 Jun 2026 16:13:52 -0000
@@ -0,0 +1,136 @@
+/*	$OpenBSD$	*/
+
+/*
+ * Copyright (c) 2026 Alexander Bluhm <bluhm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "syslogd.h"
+
+static int	 verbose;
+static char	*debug_ebuf;
+
+/* parent process is re-execed with reduced arguments, others ignored */
+
+static __dead void
+usage(void)
+{
+	(void)fprintf(stderr,
+	    "usage: syslogd-parent [-dn] [-f config_file] -P child_pid\n");
+	exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	char *ConfFile = _PATH_LOGCONF;
+	int Debug = 0;
+	int PrivChild = 0;
+	int NoDNS = 0;
+	const char *errstr;
+	int ch;
+
+	while ((ch = getopt(argc, argv,
+	    "46a:C:c:dFf:hK:k:m:nP:p:rS:s:T:U:uVZ")) != -1) {
+		switch (ch) {
+		case '4':
+		case '6':
+		case 'a':
+		case 'C':
+		case 'c':
+		case 'd':		/* debug */
+			Debug++;
+			break;
+		case 'F':
+			break;
+		case 'f':		/* configuration file */
+			ConfFile = optarg;
+			break;
+		case 'h':
+		case 'K':
+		case 'k':
+		case 'm':
+			break;
+		case 'n':		/* don't do DNS lookups */
+			NoDNS = 1;
+			break;
+		case 'P':		/* used internally, exec the parent */
+			PrivChild = strtonum(optarg, 2, INT_MAX, &errstr);
+			if (errstr)
+				errx(1, "priv child %s: %s", errstr, optarg);
+			break;
+		case 'p':
+		case 'r':
+		case 'S':
+		case 's':
+		case 'T':
+		case 'U':
+		case 'u':
+		case 'V':
+		case 'Z':
+			break;
+		default:
+			usage();
+		}
+	}
+	if (argc != optind)
+		usage();
+	if (PrivChild < 2)
+		errx(1, "parent requires -P child_pid");
+
+	log_init(Debug, LOG_SYSLOG);
+	priv_exec(ConfFile, NoDNS, PrivChild, argc, argv);
+
+	/* NOTREACHED */
+	return 1;
+}
+
+void
+log_init(int n_debug, int fac)
+{
+	verbose = n_debug;
+
+	if (debug_ebuf == NULL)
+		if ((debug_ebuf = malloc(ERRBUFSIZE)) == NULL)
+		    err(1, "allocate debug buffer");
+	debug_ebuf[0] = '\0';
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+	va_list ap;
+	int saved_errno;
+
+	if (verbose) {
+		saved_errno = errno;
+		va_start(ap, emsg);
+		vsnprintf(debug_ebuf, ERRBUFSIZE, emsg, ap);
+		fprintf(stderr, "[priv]: %s\n", debug_ebuf);
+		fflush(stderr);
+		va_end(ap);
+		errno = saved_errno;
+	}
+	debug_ebuf[0] = '\0';
+}
Index: usr.sbin/syslogd/privsep.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/privsep.c,v
diff -u -p -r1.77 privsep.c
--- usr.sbin/syslogd/privsep.c	12 Oct 2023 22:36:54 -0000	1.77
+++ usr.sbin/syslogd/privsep.c	7 Jun 2026 16:14:05 -0000
@@ -96,10 +96,10 @@ static int  may_read(int, void *, size_t
 static struct passwd *pw;
 
 void
-priv_init(int lockfd, int nullfd, int argc, char *argv[])
+priv_init(int debug, int lockfd, int nullfd, int argc, char *argv[])
 {
 	int i, socks[2];
-	char *execpath, childnum[11], **privargv;
+	char childnum[11], **privargv;
 
 	/* Create sockets */
 	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
@@ -132,14 +132,10 @@ priv_init(int lockfd, int nullfd, int ar
 	}
 	close(socks[1]);
 
-	if (strchr(argv[0], '/') == NULL)
-		execpath = argv[0];
-	else if ((execpath = realpath(argv[0], NULL)) == NULL)
-		err(1, "realpath %s", argv[0]);
 	if (chdir("/") != 0)
 		err(1, "chdir /");
 
-	if (!Debug) {
+	if (!debug) {
 		close(lockfd);
 		dup2(nullfd, STDIN_FILENO);
 		dup2(nullfd, STDOUT_FILENO);
@@ -156,18 +152,18 @@ priv_init(int lockfd, int nullfd, int ar
 	snprintf(childnum, sizeof(childnum), "%d", child_pid);
 	if ((privargv = reallocarray(NULL, argc + 3, sizeof(char *))) == NULL)
 		err(1, "alloc priv argv failed");
-	privargv[0] = execpath;
+	privargv[0] = "/usr/sbin/syslogd-parent";
 	for (i = 1; i < argc; i++)
 		privargv[i] = argv[i];
 	privargv[i++] = "-P";
 	privargv[i++] = childnum;
 	privargv[i++] = NULL;
-	execvp(privargv[0], privargv);
+	execv(privargv[0], privargv);
 	err(1, "exec priv '%s' failed", privargv[0]);
 }
 
 __dead void
-priv_exec(char *conf, int numeric, int child, int argc, char *argv[])
+priv_exec(const char *conf, int numeric, int child, int argc, char *argv[])
 {
 	int i, fd, sock, cmd, addr_len, result, restart;
 	size_t path_len, protoname_len, hostname_len, servname_len;
@@ -235,7 +231,7 @@ priv_exec(char *conf, int numeric, int c
 	sigaction(SIGCHLD, &sa, NULL);
 
 	setproctitle("[priv]");
-	log_debug("[priv]: fork+exec done");
+	log_debug("fork+exec done");
 
 	sigemptyset(&sigmask);
 	if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1)
@@ -253,7 +249,7 @@ priv_exec(char *conf, int numeric, int c
 			break;
 		switch (cmd) {
 		case PRIV_OPEN_TTY:
-			log_debug("[priv]: msg PRIV_OPEN_TTY received");
+			log_debug("msg PRIV_OPEN_TTY received");
 			/* Expecting: length, path */
 			must_read(sock, &path_len, sizeof(size_t));
 			if (path_len == 0 || path_len > sizeof(path))
@@ -271,7 +267,7 @@ priv_exec(char *conf, int numeric, int c
 
 		case PRIV_OPEN_LOG:
 		case PRIV_OPEN_PIPE:
-			log_debug("[priv]: msg PRIV_OPEN_%s received",
+			log_debug("msg PRIV_OPEN_%s received",
 			    cmd == PRIV_OPEN_PIPE ? "PIPE" : "LOG");
 			/* Expecting: length, path */
 			must_read(sock, &path_len, sizeof(size_t));
@@ -296,7 +292,7 @@ priv_exec(char *conf, int numeric, int c
 			break;
 
 		case PRIV_OPEN_UTMP:
-			log_debug("[priv]: msg PRIV_OPEN_UTMP received");
+			log_debug("msg PRIV_OPEN_UTMP received");
 			fd = open(_PATH_UTMP, O_RDONLY|O_NONBLOCK);
 			send_fd(sock, fd);
 			if (fd == -1)
@@ -306,7 +302,7 @@ priv_exec(char *conf, int numeric, int c
 			break;
 
 		case PRIV_OPEN_CONFIG:
-			log_debug("[priv]: msg PRIV_OPEN_CONFIG received");
+			log_debug("msg PRIV_OPEN_CONFIG received");
 			stat(conf, &cf_info);
 			fd = open(conf, O_RDONLY|O_NONBLOCK);
 			send_fd(sock, fd);
@@ -317,7 +313,7 @@ priv_exec(char *conf, int numeric, int c
 			break;
 
 		case PRIV_CONFIG_MODIFIED:
-			log_debug("[priv]: msg PRIV_CONFIG_MODIFIED received");
+			log_debug("msg PRIV_CONFIG_MODIFIED received");
 			if (stat(conf, &cf_stat) == -1 ||
 			    timespeccmp(&cf_info.st_mtim,
 			    &cf_stat.st_mtim, <) ||
@@ -335,13 +331,13 @@ priv_exec(char *conf, int numeric, int c
 			if (pledge("stdio rpath wpath cpath dns sendfd id proc exec",
 			    NULL) == -1)
 				err(1, "pledge done config");
-			log_debug("[priv]: msg PRIV_DONE_CONFIG_PARSE "
+			log_debug("msg PRIV_DONE_CONFIG_PARSE "
 			    "received");
 			increase_state(STATE_RUNNING);
 			break;
 
 		case PRIV_GETADDRINFO:
-			log_debug("[priv]: msg PRIV_GETADDRINFO received");
+			log_debug("msg PRIV_GETADDRINFO received");
 			/* Expecting: len, proto, len, host, len, serv */
 			must_read(sock, &protoname_len, sizeof(size_t));
 			if (protoname_len == 0 ||
@@ -407,7 +403,7 @@ priv_exec(char *conf, int numeric, int c
 			break;
 
 		case PRIV_GETNAMEINFO:
-			log_debug("[priv]: msg PRIV_GETNAMEINFO received");
+			log_debug("msg PRIV_GETNAMEINFO received");
 			if (numeric)
 				errx(1, "rejected attempt to getnameinfo");
 			/* Expecting: length, sockaddr */
@@ -442,7 +438,8 @@ priv_exec(char *conf, int numeric, int c
 		sigaddset(&sigmask, SIGHUP);
 		if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1)
 			err(1, "sigprocmask exec");
-		execvp(argv[0], argv);
+		argv[0] = "/usr/sbin/syslogd";
+		execv(argv[0], argv);
 		err(1, "exec restart '%s' failed", argv[0]);
 	}
 	unlink(_PATH_LOGPID);
Index: usr.sbin/syslogd/syslogd.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/syslogd.c,v
diff -u -p -r1.287 syslogd.c
--- usr.sbin/syslogd/syslogd.c	26 Jun 2025 19:10:13 -0000	1.287
+++ usr.sbin/syslogd/syslogd.c	7 Jun 2026 16:14:05 -0000
@@ -121,7 +121,7 @@
 #include "evbuffer_tls.h"
 #include "parsemsg.h"
 
-char *ConfFile = _PATH_LOGCONF;
+const char *ConfFile = _PATH_LOGCONF;
 const char ctty[] = _PATH_CONSOLE;
 
 #define MAXUNAMES	20	/* maximum number of user names */
@@ -226,7 +226,6 @@ int	Initialized = 0;	/* set when we have
 
 int	MarkInterval = 20 * 60;	/* interval between marks in seconds */
 int	MarkSeq = 0;		/* mark sequence number */
-int	PrivChild = 0;		/* Exec the privileged parent process */
 int	Repeat = 0;		/* 0 msg repeated, 1 in files only, 2 never */
 int	SecureMode = 1;		/* when true, speak only unix domain socks */
 int	NoDNS = 0;		/* when true, refrain from doing DNS lookups */
@@ -350,7 +349,6 @@ struct filed *find_dup(struct filed *);
 void	printline(char *, char *);
 void	printsys(char *);
 void	current_time(char *);
-void	usage(void);
 void	wallmsg(struct filed *, struct iovec *);
 int	loghost_parse(char *, char **, char **, char **);
 int	getmsgbufsize(void);
@@ -363,6 +361,17 @@ void	set_sockbuf(int);
 void	set_keepalive(int);
 void	tailify_replytext(char *, int);
 
+static __dead void
+usage(void)
+{
+	(void)fprintf(stderr,
+	    "usage: syslogd [-46dFhnruVZ] [-a path] [-C CAfile]\n"
+	    "\t[-c cert_file] [-f config_file] [-K CAfile] [-k key_file]\n"
+	    "\t[-m mark_interval] [-p log_socket] [-S listen_address]\n"
+	    "\t[-s reporting_socket] [-T listen_address] [-U bind_address]\n");
+	exit(1);
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -399,7 +408,7 @@ main(int argc, char *argv[])
 	nbind = nlisten = ntls = 0;
 
 	while ((ch = getopt(argc, argv,
-	    "46a:C:c:dFf:hK:k:m:nP:p:rS:s:T:U:uVZ")) != -1) {
+	    "46a:C:c:dFf:hK:k:m:np:rS:s:T:U:uVZ")) != -1) {
 		switch (ch) {
 		case '4':		/* disable IPv6 */
 			Family = PF_INET;
@@ -446,11 +455,6 @@ main(int argc, char *argv[])
 		case 'n':		/* don't do DNS lookups */
 			NoDNS = 1;
 			break;
-		case 'P':		/* used internally, exec the parent */
-			PrivChild = strtonum(optarg, 2, INT_MAX, &errstr);
-			if (errstr)
-				errx(1, "priv child %s: %s", errstr, optarg);
-			break;
 		case 'p':		/* path */
 			path_unix[0] = optarg;
 			break;
@@ -503,9 +507,6 @@ main(int argc, char *argv[])
 				fatal("dup2 null");
 	}
 
-	if (PrivChild > 1)
-		priv_exec(ConfFile, NoDNS, PrivChild, argc, argv);
-
 	consfile.f_type = F_CONSOLE;
 	(void)strlcpy(consfile.f_un.f_fname, ctty,
 	    sizeof(consfile.f_un.f_fname));
@@ -757,7 +758,7 @@ main(int argc, char *argv[])
 	}
 
 	/* Privilege separation begins here */
-	priv_init(lockpipe[1], nullfd, argc, argv);
+	priv_init(Debug, lockpipe[1], nullfd, argc, argv);
 
 	if (pledge("stdio unix inet recvfd", NULL) == -1)
 		err(1, "pledge");
@@ -1662,18 +1663,6 @@ tcpbuf_countmsg(struct bufferevent *bufe
 			i++;
 	}
 	return (i);
-}
-
-void
-usage(void)
-{
-
-	(void)fprintf(stderr,
-	    "usage: syslogd [-46dFhnruVZ] [-a path] [-C CAfile]\n"
-	    "\t[-c cert_file] [-f config_file] [-K CAfile] [-k key_file]\n"
-	    "\t[-m mark_interval] [-p log_socket] [-S listen_address]\n"
-	    "\t[-s reporting_socket] [-T listen_address] [-U bind_address]\n");
-	exit(1);
 }
 
 /*
Index: usr.sbin/syslogd/syslogd.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/syslogd.h,v
diff -u -p -r1.37 syslogd.h
--- usr.sbin/syslogd/syslogd.h	12 Oct 2023 22:36:54 -0000	1.37
+++ usr.sbin/syslogd/syslogd.h	7 Jun 2026 16:14:05 -0000
@@ -26,8 +26,8 @@
 extern int	ZuluTime;
 
 /* Privilege separation */
-void  priv_init(int, int, int, char **);
-__dead void priv_exec(char *, int, int, int, char **);
+void  priv_init(int, int, int, int, char **);
+__dead void priv_exec(const char *, int, int, int, char **);
 int   priv_open_tty(const char *);
 int   priv_open_log(const char *);
 FILE *priv_open_utmp(void);
@@ -52,7 +52,6 @@ int  receive_fd(int);
 #define ERRBUFSIZE	256
 void vlogmsg(int pri, const char *, const char *, va_list);
 __dead void die(int);
-extern int Debug;
 
 struct ringbuf {
 	char *buf;
Index: etc/rc.d/syslogd
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/etc/rc.d/syslogd,v
diff -u -p -r1.5 syslogd
--- etc/rc.d/syslogd	11 Jan 2018 19:52:12 -0000	1.5
+++ etc/rc.d/syslogd	7 Jun 2026 08:58:14 -0000
@@ -6,7 +6,7 @@ daemon="/usr/sbin/syslogd"
 
 . /etc/rc.d/rc.subr
 
-pexp="syslogd: \[priv\]"
+pexp="syslogd-parent: \[priv\]"
 
 rc_pre() {
 	rm -f /dev/log