Download raw body.
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 <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
> >
>