From: Stuart Henderson Subject: Re: syslogd separate parent program To: renaud@openbsd.org Cc: tech@openbsd.org Date: Sun, 7 Jun 2026 19:11:33 +0100 On 2026/06/07 19:32, renaud@openbsd.org wrote: > > > 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 does it matter? (apart from anything else the standard interface for doing this would be the rc.d script..) > > 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 > > >