Download raw body.
syslogd separate parent program
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
syslogd separate parent program