mirror of
https://github.com/openbsd/src.git
synced 2024-12-22 16:42:56 -08:00
Do an exec on itself in the privileged syslogd(8) parent process
to reshuffle its memory layout. Input rzalamena@; OK deraadt@
This commit is contained in:
parent
ee18520d56
commit
ece520b1dd
@ -1,7 +1,8 @@
|
||||
/* $OpenBSD: privsep.c,v 1.61 2016/06/28 18:22:50 jca Exp $ */
|
||||
/* $OpenBSD: privsep.c,v 1.62 2016/10/06 13:03:47 bluhm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
|
||||
* Copyright (c) 2016 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
|
||||
@ -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_t);
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
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)
|
||||
__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");
|
||||
|
||||
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");
|
||||
|
||||
/* Save whether or not the child can have access to getnameinfo(3) */
|
||||
if (numeric > 0)
|
||||
allow_getnameinfo = 0;
|
||||
else
|
||||
allow_getnameinfo = 1;
|
||||
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
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 lockfd, int nullfd, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: syslogd.c,v 1.217 2016/10/06 11:24:04 bluhm Exp $ */
|
||||
/* $OpenBSD: syslogd.c,v 1.218 2016/10/06 13:03:47 bluhm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1983, 1988, 1993, 1994
|
||||
@ -208,6 +208,7 @@ 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 SecureMode = 1; /* when true, speak only unix domain socks */
|
||||
int NoDNS = 0; /* when true, will refrain from doing DNS lookups */
|
||||
int ZuluTime = 0; /* display date and time in UTC ISO format */
|
||||
@ -359,7 +360,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:uVZ"))
|
||||
while ((ch = getopt(argc, argv, "46a:C:c:dFf:hK:k:m:nP:p:S:s:T:U:uVZ"))
|
||||
!= -1)
|
||||
switch (ch) {
|
||||
case '4': /* disable IPv6 */
|
||||
@ -406,6 +407,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;
|
||||
@ -445,7 +451,7 @@ main(int argc, char *argv[])
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
if ((argc -= optind) != 0)
|
||||
if (argc != optind)
|
||||
usage();
|
||||
|
||||
if (Debug)
|
||||
@ -463,6 +469,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));
|
||||
@ -695,8 +704,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");
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: syslogd.h,v 1.23 2015/10/23 16:28:52 bluhm Exp $ */
|
||||
/* $OpenBSD: syslogd.h,v 1.24 2016/10/06 13:03:47 bluhm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
|
||||
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user