Re: syslogd separate parent program

[email protected]
Newsgroups gmane.os.openbsd.tech
Message-ID <[email protected]>

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 <[email protected]>
> + *
> + * 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
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.