Download raw body.
syslogd separate parent program
On 07/06/2026 18:29, Alexander Bluhm wrote:
> 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?
>
Maybe just a small warning in release notes that scripts using
pkill/pgrep -x won't match syslogd-parent
> 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