From 13d1fa115053d63294e147ab9588682f332c1c1e Mon Sep 17 00:00:00 2001 From: bluhm Date: Thu, 11 Jun 2026 15:41:33 +0000 Subject: [PATCH] Provide a separate executable file for syslogd parent. syslogd(8) forks and execs its parent process to keep privileged parts separated. This parent process can be easily implemented as a separate program. It gets its own main() and minimal debug logging functions. The splitted parent process image is smaller, especially without additional libs. Use additional directories to build both parts. The rcctl script has to be adopted, as the parent process has a different name. OK deraadt@ --- etc/rc.d/syslogd | 4 +- usr.sbin/syslogd/Makefile | 13 ++- usr.sbin/syslogd/Makefile.inc | 4 + usr.sbin/syslogd/parent.c | 136 ++++++++++++++++++++++++++++++ usr.sbin/syslogd/parent/Makefile | 9 ++ usr.sbin/syslogd/privsep.c | 39 ++++----- usr.sbin/syslogd/syslogd.c | 41 ++++----- usr.sbin/syslogd/syslogd.h | 7 +- usr.sbin/syslogd/syslogd/Makefile | 12 +++ 9 files changed, 204 insertions(+), 61 deletions(-) create mode 100644 usr.sbin/syslogd/Makefile.inc create mode 100644 usr.sbin/syslogd/parent.c create mode 100644 usr.sbin/syslogd/parent/Makefile create mode 100644 usr.sbin/syslogd/syslogd/Makefile diff --git a/etc/rc.d/syslogd b/etc/rc.d/syslogd index 3ac7a1fd24f..c4a91824957 100644 --- a/etc/rc.d/syslogd +++ b/etc/rc.d/syslogd @@ -1,12 +1,12 @@ #!/bin/ksh # -# $OpenBSD: syslogd,v 1.5 2018/01/11 19:52:12 rpe Exp $ +# $OpenBSD: syslogd,v 1.6 2026/06/11 15:41:33 bluhm Exp $ daemon="/usr/sbin/syslogd" . /etc/rc.d/rc.subr -pexp="syslogd: \[priv\]" +pexp="syslogd-parent: \[priv\]" rc_pre() { rm -f /dev/log diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile index 435cdb0d513..2edac6adac7 100644 --- a/usr.sbin/syslogd/Makefile +++ b/usr.sbin/syslogd/Makefile @@ -1,10 +1,7 @@ -# $OpenBSD: Makefile,v 1.9 2022/01/13 10:34:07 martijn Exp $ +# $OpenBSD: Makefile,v 1.10 2026/06/11 15:41:33 bluhm 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} +.include -.include +SUBDIR= syslogd parent + +.include diff --git a/usr.sbin/syslogd/Makefile.inc b/usr.sbin/syslogd/Makefile.inc new file mode 100644 index 00000000000..bd6f9ee0c2f --- /dev/null +++ b/usr.sbin/syslogd/Makefile.inc @@ -0,0 +1,4 @@ +# $OpenBSD: Makefile.inc,v 1.1 2026/06/11 15:41:33 bluhm Exp $ + +BINDIR?= /usr/sbin +COPTS+= -Werror-implicit-function-declaration diff --git a/usr.sbin/syslogd/parent.c b/usr.sbin/syslogd/parent.c new file mode 100644 index 00000000000..319f0412d36 --- /dev/null +++ b/usr.sbin/syslogd/parent.c @@ -0,0 +1,136 @@ +/* $OpenBSD: parent.c,v 1.1 2026/06/11 15:41:33 bluhm Exp $ */ + +/* + * 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'; +} diff --git a/usr.sbin/syslogd/parent/Makefile b/usr.sbin/syslogd/parent/Makefile new file mode 100644 index 00000000000..44718a23e42 --- /dev/null +++ b/usr.sbin/syslogd/parent/Makefile @@ -0,0 +1,9 @@ +# $OpenBSD: Makefile,v 1.1 2026/06/11 15:41:33 bluhm Exp $ + +.PATH: ${.CURDIR}/.. + +PROG= syslogd-parent +SRCS= parent.c privsep.c privsep_fdpass.c +NOMAN= Yes + +.include diff --git a/usr.sbin/syslogd/privsep.c b/usr.sbin/syslogd/privsep.c index 3e89894ab46..9cd25564c1a 100644 --- a/usr.sbin/syslogd/privsep.c +++ b/usr.sbin/syslogd/privsep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: privsep.c,v 1.77 2023/10/12 22:36:54 bluhm Exp $ */ +/* $OpenBSD: privsep.c,v 1.78 2026/06/11 15:41:33 bluhm Exp $ */ /* * Copyright (c) 2003 Anil Madhavapeddy @@ -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 argc, char *argv[]) } 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 argc, char *argv[]) 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 child, int argc, char *argv[]) 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 child, int argc, char *argv[]) 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 child, int argc, char *argv[]) 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 child, int argc, char *argv[]) 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 child, int argc, char *argv[]) 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 child, int argc, char *argv[]) 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 child, int argc, char *argv[]) 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 child, int argc, char *argv[]) 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 child, int argc, char *argv[]) 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); diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c index dfa741c06f8..af76ec2e181 100644 --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: syslogd.c,v 1.287 2025/06/26 19:10:13 bluhm Exp $ */ +/* $OpenBSD: syslogd.c,v 1.288 2026/06/11 15:41:33 bluhm Exp $ */ /* * Copyright (c) 2014-2021 Alexander Bluhm @@ -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 initialized ourselves */ 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"); @@ -1664,18 +1665,6 @@ tcpbuf_countmsg(struct bufferevent *bufev) 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); -} - /* * Take a raw input line, decode the message, and print the message * on the appropriate log files. diff --git a/usr.sbin/syslogd/syslogd.h b/usr.sbin/syslogd/syslogd.h index 0ff88cb3835..b7c630da299 100644 --- a/usr.sbin/syslogd/syslogd.h +++ b/usr.sbin/syslogd/syslogd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syslogd.h,v 1.37 2023/10/12 22:36:54 bluhm Exp $ */ +/* $OpenBSD: syslogd.h,v 1.38 2026/06/11 15:41:33 bluhm Exp $ */ /* * Copyright (c) 2014-2017 Alexander Bluhm @@ -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; diff --git a/usr.sbin/syslogd/syslogd/Makefile b/usr.sbin/syslogd/syslogd/Makefile new file mode 100644 index 00000000000..9a1d3676ac1 --- /dev/null +++ b/usr.sbin/syslogd/syslogd/Makefile @@ -0,0 +1,12 @@ +# $OpenBSD: Makefile,v 1.1 2026/06/11 15:41:33 bluhm Exp $ + +.PATH: ${.CURDIR}/.. + +PROG= syslogd +SRCS= evbuffer_tls.c log.c parsemsg.c privsep.c \ + privsep_fdpass.c ringbuf.c syslogd.c ttymsg.c +LDADD= -levent -ltls -lssl -lcrypto +DPADD= ${LIBEVENT} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO} +MAN= syslogd.8 syslog.conf.5 + +.include