From: renaud@openbsd.org Subject: Re: syslogd separate parent program To: tech@openbsd.org Date: Sun, 7 Jun 2026 19:32:19 +0200 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 > 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 > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 >