On Sat, Oct 01, 2016 at 07:41:13PM +0200, Rafael Zalamena wrote: > This could be replaced with "closefrom(4);".
Updated diff: - use closefrom(2) - use execvp(3) to allow starting syslogd(8) without full path - add a debug message to make testing easier ok? bluhm Index: usr.sbin/syslogd/privsep.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/privsep.c,v retrieving revision 1.61 diff -u -p -u -p -r1.61 privsep.c --- usr.sbin/syslogd/privsep.c 28 Jun 2016 18:22:50 -0000 1.61 +++ usr.sbin/syslogd/privsep.c 2 Oct 2016 20:57:10 -0000 @@ -2,6 +2,7 @@ /* * Copyright (c) 2003 Anil Madhavapeddy <a...@recoil.org> + * Copyright (c) 2016 Alexander Bluhm <bl...@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 @@ -71,9 +72,6 @@ enum cmd_types { static int priv_fd = -1; static volatile pid_t child_pid = -1; -static char config_file[PATH_MAX]; -static struct stat cf_info; -static int allow_getnameinfo = 0; static volatile sig_atomic_t cur_state = STATE_INIT; /* Queue for the allowed logfiles */ @@ -94,25 +92,12 @@ static void must_read(int, void *, size_ static void must_write(int, void *, size_t); static int may_read(int, void *, size_t); -int -priv_init(char *conf, int numeric, int lockfd, int nullfd, char *argv[]) +void +priv_init(int lockfd, int nullfd, int argc, char *argv[]) { - int i, fd, socks[2], cmd, addr_len, result, restart; - size_t path_len, protoname_len, hostname_len, servname_len; - char path[PATH_MAX], protoname[5]; - char hostname[NI_MAXHOST], servname[NI_MAXSERV]; - struct sockaddr_storage addr; - struct stat cf_stat; + int i, socks[2]; struct passwd *pw; - struct addrinfo hints, *res0; - struct sigaction sa; - - memset(&sa, 0, sizeof(sa)); - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sa.sa_handler = SIG_DFL; - for (i = 1; i < _NSIG; i++) - sigaction(i, &sa, NULL); + char childnum[11], **privargv; /* Create sockets */ if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) @@ -141,12 +126,9 @@ priv_init(char *conf, int numeric, int l err(1, "setresuid() failed"); close(socks[0]); priv_fd = socks[1]; - return 0; + return; } - - if (pledge("stdio rpath wpath cpath dns getpw sendfd id proc exec", - NULL) == -1) - err(1, "pledge priv"); + close(socks[1]); if (!Debug) { close(lockfd); @@ -157,20 +139,6 @@ priv_init(char *conf, int numeric, int l if (nullfd > 2) close(nullfd); - /* Father */ - /* Pass TERM/HUP/INT/QUIT through to child, and accept CHLD */ - sa.sa_handler = sig_pass_to_chld; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGQUIT, &sa, NULL); - sa.sa_handler = sig_got_chld; - sa.sa_flags |= SA_NOCLDSTOP; - sigaction(SIGCHLD, &sa, NULL); - - setproctitle("[priv]"); - close(socks[1]); - /* Close descriptors that only the unpriv child needs */ if (fd_ctlconn != -1) close(fd_ctlconn); @@ -194,38 +162,87 @@ priv_init(char *conf, int numeric, int l if (fd_unix[i] != -1) close(fd_unix[i]); - /* Save the config file specified by the child process */ - if (strlcpy(config_file, conf, sizeof config_file) >= sizeof(config_file)) - errx(1, "config_file truncation"); + if (dup3(socks[0], 3, 0) == -1) + err(1, "dup3 priv sock failed"); + snprintf(childnum, sizeof(childnum), "%d", child_pid); + if ((privargv = reallocarray(NULL, argc + 3, sizeof(char *))) == NULL) + err(1, "alloc priv argv failed"); + for (i = 0; i < argc; i++) + privargv[i] = argv[i]; + privargv[i++] = "-P"; + privargv[i++] = childnum; + privargv[i++] = NULL; + execvp(privargv[0], privargv); + err(1, "exec priv '%s' failed", privargv[0]); +} - if (stat(config_file, &cf_info) < 0) - err(1, "stat config file failed"); +__dead void +priv_exec(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; + char path[PATH_MAX], protoname[5]; + char hostname[NI_MAXHOST], servname[NI_MAXSERV]; + struct sockaddr_storage addr; + struct stat cf_info, cf_stat; + struct addrinfo hints, *res0; + struct sigaction sa; + + if (pledge("stdio rpath wpath cpath dns getpw sendfd id proc exec", + NULL) == -1) + err(1, "pledge priv"); - /* Save whether or not the child can have access to getnameinfo(3) */ - if (numeric > 0) - allow_getnameinfo = 0; - else - allow_getnameinfo = 1; + if (argc <= 2 || strcmp("-P", argv[argc - 2]) != 0) + errx(1, "exec without priv"); + argv[argc -= 2] = NULL; + + sock = 3; + closefrom(4); + + child_pid = child; + + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = SIG_DFL; + for (i = 1; i < _NSIG; i++) + sigaction(i, &sa, NULL); + + /* Pass TERM/HUP/INT/QUIT through to child, and accept CHLD */ + sa.sa_handler = sig_pass_to_chld; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sa.sa_handler = sig_got_chld; + sa.sa_flags |= SA_NOCLDSTOP; + sigaction(SIGCHLD, &sa, NULL); + + setproctitle("[priv]"); + logdebug("[priv]: fork+exec done\n"); + + if (stat(conf, &cf_info) < 0) + err(1, "stat config file failed"); TAILQ_INIT(&lognames); increase_state(STATE_CONFIG); restart = 0; while (cur_state < STATE_QUIT) { - if (may_read(socks[0], &cmd, sizeof(int))) + if (may_read(sock, &cmd, sizeof(int))) break; switch (cmd) { case PRIV_OPEN_TTY: logdebug("[priv]: msg PRIV_OPEN_TTY received\n"); /* Expecting: length, path */ - must_read(socks[0], &path_len, sizeof(size_t)); + must_read(sock, &path_len, sizeof(size_t)); if (path_len == 0 || path_len > sizeof(path)) _exit(1); - must_read(socks[0], &path, path_len); + must_read(sock, &path, path_len); path[path_len - 1] = '\0'; check_tty_name(path, sizeof(path)); fd = open(path, O_WRONLY|O_NONBLOCK, 0); - send_fd(socks[0], fd); + send_fd(sock, fd); if (fd < 0) warnx("priv_open_tty failed"); else @@ -237,10 +254,10 @@ priv_init(char *conf, int numeric, int l logdebug("[priv]: msg PRIV_OPEN_%s received\n", cmd == PRIV_OPEN_PIPE ? "PIPE" : "LOG"); /* Expecting: length, path */ - must_read(socks[0], &path_len, sizeof(size_t)); + must_read(sock, &path_len, sizeof(size_t)); if (path_len == 0 || path_len > sizeof(path)) _exit(1); - must_read(socks[0], &path, path_len); + must_read(sock, &path, path_len); path[path_len - 1] = '\0'; check_log_name(path, sizeof(path)); @@ -251,7 +268,7 @@ priv_init(char *conf, int numeric, int l else errx(1, "invalid cmd"); - send_fd(socks[0], fd); + send_fd(sock, fd); if (fd < 0) warnx("priv_open_log failed"); else @@ -261,7 +278,7 @@ priv_init(char *conf, int numeric, int l case PRIV_OPEN_UTMP: logdebug("[priv]: msg PRIV_OPEN_UTMP received\n"); fd = open(_PATH_UTMP, O_RDONLY|O_NONBLOCK, 0); - send_fd(socks[0], fd); + send_fd(sock, fd); if (fd < 0) warnx("priv_open_utmp failed"); else @@ -270,9 +287,9 @@ priv_init(char *conf, int numeric, int l case PRIV_OPEN_CONFIG: logdebug("[priv]: msg PRIV_OPEN_CONFIG received\n"); - stat(config_file, &cf_info); - fd = open(config_file, O_RDONLY|O_NONBLOCK, 0); - send_fd(socks[0], fd); + stat(conf, &cf_info); + fd = open(conf, O_RDONLY|O_NONBLOCK, 0); + send_fd(sock, fd); if (fd < 0) warnx("priv_open_config failed"); else @@ -281,16 +298,16 @@ priv_init(char *conf, int numeric, int l case PRIV_CONFIG_MODIFIED: logdebug("[priv]: msg PRIV_CONFIG_MODIFIED received\n"); - if (stat(config_file, &cf_stat) < 0 || + if (stat(conf, &cf_stat) < 0 || timespeccmp(&cf_info.st_mtimespec, &cf_stat.st_mtimespec, <) || cf_info.st_size != cf_stat.st_size) { logdebug("config file modified: restarting\n"); restart = result = 1; - must_write(socks[0], &result, sizeof(int)); + must_write(sock, &result, sizeof(int)); } else { result = 0; - must_write(socks[0], &result, sizeof(int)); + must_write(sock, &result, sizeof(int)); } break; @@ -303,25 +320,25 @@ priv_init(char *conf, int numeric, int l case PRIV_GETADDRINFO: logdebug("[priv]: msg PRIV_GETADDRINFO received\n"); /* Expecting: len, proto, len, host, len, serv */ - must_read(socks[0], &protoname_len, sizeof(size_t)); + must_read(sock, &protoname_len, sizeof(size_t)); if (protoname_len == 0 || protoname_len > sizeof(protoname)) _exit(1); - must_read(socks[0], &protoname, protoname_len); + must_read(sock, &protoname, protoname_len); protoname[protoname_len - 1] = '\0'; - must_read(socks[0], &hostname_len, sizeof(size_t)); + must_read(sock, &hostname_len, sizeof(size_t)); if (hostname_len == 0 || hostname_len > sizeof(hostname)) _exit(1); - must_read(socks[0], &hostname, hostname_len); + must_read(sock, &hostname, hostname_len); hostname[hostname_len - 1] = '\0'; - must_read(socks[0], &servname_len, sizeof(size_t)); + must_read(sock, &servname_len, sizeof(size_t)); if (servname_len == 0 || servname_len > sizeof(servname)) _exit(1); - must_read(socks[0], &servname, servname_len); + must_read(sock, &servname, servname_len); servname[servname_len - 1] = '\0'; memset(&hints, 0, sizeof(hints)); @@ -356,34 +373,34 @@ priv_init(char *conf, int numeric, int l i = getaddrinfo(hostname, servname, &hints, &res0); if (i != 0 || res0 == NULL) { addr_len = 0; - must_write(socks[0], &addr_len, sizeof(int)); + must_write(sock, &addr_len, sizeof(int)); } else { /* Just send the first address */ i = res0->ai_addrlen; - must_write(socks[0], &i, sizeof(int)); - must_write(socks[0], res0->ai_addr, i); + must_write(sock, &i, sizeof(int)); + must_write(sock, res0->ai_addr, i); freeaddrinfo(res0); } break; case PRIV_GETNAMEINFO: logdebug("[priv]: msg PRIV_GETNAMEINFO received\n"); - if (!allow_getnameinfo) + if (numeric) errx(1, "rejected attempt to getnameinfo"); /* Expecting: length, sockaddr */ - must_read(socks[0], &addr_len, sizeof(int)); + must_read(sock, &addr_len, sizeof(int)); if (addr_len <= 0 || (size_t)addr_len > sizeof(addr)) _exit(1); - must_read(socks[0], &addr, addr_len); + must_read(sock, &addr, addr_len); if (getnameinfo((struct sockaddr *)&addr, addr_len, hostname, sizeof(hostname), NULL, 0, NI_NOFQDN|NI_NAMEREQD|NI_DGRAM) != 0) { addr_len = 0; - must_write(socks[0], &addr_len, sizeof(int)); + must_write(sock, &addr_len, sizeof(int)); } else { addr_len = strlen(hostname) + 1; - must_write(socks[0], &addr_len, sizeof(int)); - must_write(socks[0], hostname, addr_len); + must_write(sock, &addr_len, sizeof(int)); + must_write(sock, hostname, addr_len); } break; default: @@ -392,23 +409,23 @@ priv_init(char *conf, int numeric, int l } } - close(socks[0]); + close(sock); /* Unlink any domain sockets that have been opened */ for (i = 0; i < nunix; i++) - if (fd_unix[i] != -1) - (void)unlink(path_unix[i]); - if (path_ctlsock != NULL && fd_ctlsock != -1) + (void)unlink(path_unix[i]); + if (path_ctlsock != NULL) (void)unlink(path_ctlsock); if (restart) { - int r; + int status; - wait(&r); + waitpid(child_pid, &status, 0); execvp(argv[0], argv); + err(1, "exec restart '%s' failed", argv[0]); } unlink(_PATH_LOGPID); - _exit(0); + exit(0); } static int Index: usr.sbin/syslogd/syslogd.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/syslogd.c,v retrieving revision 1.215 diff -u -p -u -p -r1.215 syslogd.c --- usr.sbin/syslogd/syslogd.c 23 Sep 2016 15:46:39 -0000 1.215 +++ usr.sbin/syslogd/syslogd.c 29 Sep 2016 16:25:28 -0000 @@ -208,6 +208,7 @@ 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 SecureMode = 1; /* when true, speak only unix domain socks */ int NoDNS = 0; /* when true, will refrain from doing DNS lookups */ int IncludeHostname = 0; /* include RFC 3164 style hostnames when forwarding */ @@ -358,7 +359,7 @@ main(int argc, char *argv[]) int ch, i; int lockpipe[2] = { -1, -1}, pair[2], nullfd, fd; - while ((ch = getopt(argc, argv, "46a:C:c:dFf:hK:k:m:np:S:s:T:U:uV")) + while ((ch = getopt(argc, argv, "46a:C:c:dFf:hK:k:m:nP:p:S:s:T:U:uV")) != -1) switch (ch) { case '4': /* disable IPv6 */ @@ -405,6 +406,11 @@ 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; @@ -441,7 +447,7 @@ main(int argc, char *argv[]) default: usage(); } - if ((argc -= optind) != 0) + if (argc != optind) usage(); if (Debug) @@ -459,6 +465,9 @@ main(int argc, char *argv[]) } } + 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)); @@ -691,8 +700,7 @@ main(int argc, char *argv[]) } /* Privilege separation begins here */ - if (priv_init(ConfFile, NoDNS, lockpipe[1], nullfd, argv) < 0) - errx(1, "unable to privsep"); + priv_init(lockpipe[1], nullfd, argc, argv); if (pledge("stdio unix inet recvfd", NULL) == -1) err(1, "pledge"); Index: usr.sbin/syslogd/syslogd.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/syslogd.h,v retrieving revision 1.23 diff -u -p -u -p -r1.23 syslogd.h --- usr.sbin/syslogd/syslogd.h 23 Oct 2015 16:28:52 -0000 1.23 +++ usr.sbin/syslogd/syslogd.h 29 Sep 2016 16:25:58 -0000 @@ -21,7 +21,8 @@ #include <sys/uio.h> /* Privilege separation */ -int priv_init(char *, int, int, int, char **); +void priv_init(int, int, int, char **); +__dead void priv_exec(char *, int, int, int, char **); int priv_open_tty(const char *); int priv_open_log(const char *); FILE *priv_open_utmp(void);