2016-12-30 15:21:26 -08:00
|
|
|
/* $OpenBSD: privsep.c,v 1.66 2016/12/30 23:21:26 bluhm Exp $ */
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
|
2016-10-06 06:03:47 -07:00
|
|
|
* Copyright (c) 2016 Alexander Bluhm <bluhm@openbsd.org>
|
2003-07-31 11:20:07 -07:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2015-01-15 22:39:28 -08:00
|
|
|
|
2003-07-31 11:20:07 -07:00
|
|
|
#include <sys/queue.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/wait.h>
|
2015-01-19 08:40:49 -08:00
|
|
|
|
2003-07-31 11:20:07 -07:00
|
|
|
#include <err.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2015-01-19 08:40:49 -08:00
|
|
|
#include <limits.h>
|
2003-07-31 11:20:07 -07:00
|
|
|
#include <netdb.h>
|
|
|
|
#include <paths.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <utmp.h>
|
2015-01-19 08:40:49 -08:00
|
|
|
|
2003-07-31 11:20:07 -07:00
|
|
|
#include "syslogd.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* syslogd can only go forward in these states; each state should represent
|
|
|
|
* less privilege. After STATE_INIT, the child is allowed to parse its
|
|
|
|
* config file once, and communicate the information regarding what logfiles
|
|
|
|
* it needs access to back to the parent. When that is done, it sends a
|
|
|
|
* message to the priv parent revoking this access, moving to STATE_RUNNING.
|
|
|
|
* In this state, any log-files not in the access list are rejected.
|
|
|
|
*
|
|
|
|
* This allows a HUP signal to the child to reopen its log files, and
|
|
|
|
* the config file to be parsed if it hasn't been changed (this is still
|
2004-02-26 03:04:15 -08:00
|
|
|
* useful to force resolution of remote syslog servers again).
|
2003-07-31 11:20:07 -07:00
|
|
|
* If the config file has been modified, then the child dies, and
|
|
|
|
* the priv parent restarts itself.
|
|
|
|
*/
|
|
|
|
enum priv_state {
|
|
|
|
STATE_INIT, /* just started up */
|
|
|
|
STATE_CONFIG, /* parsing config file for first time */
|
|
|
|
STATE_RUNNING, /* running and accepting network traffic */
|
2003-12-29 14:09:36 -08:00
|
|
|
STATE_QUIT /* shutting down */
|
2003-07-31 11:20:07 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
enum cmd_types {
|
|
|
|
PRIV_OPEN_TTY, /* open terminal or console device */
|
|
|
|
PRIV_OPEN_LOG, /* open logfile for appending */
|
2007-02-20 03:24:32 -08:00
|
|
|
PRIV_OPEN_PIPE, /* fork & exec child that gets logs on stdin */
|
2003-07-31 11:20:07 -07:00
|
|
|
PRIV_OPEN_UTMP, /* open utmp for reading only */
|
|
|
|
PRIV_OPEN_CONFIG, /* open config file for reading only */
|
|
|
|
PRIV_CONFIG_MODIFIED, /* check if config file has been modified */
|
2014-08-20 12:16:27 -07:00
|
|
|
PRIV_GETADDRINFO, /* resolve host/service names */
|
2014-08-20 13:10:17 -07:00
|
|
|
PRIV_GETNAMEINFO, /* resolve numeric address into hostname */
|
2016-10-16 15:12:50 -07:00
|
|
|
PRIV_DONE_CONFIG_PARSE /* signal that initial config parse is done */
|
2003-07-31 11:20:07 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static int priv_fd = -1;
|
2004-03-06 11:42:38 -08:00
|
|
|
static volatile pid_t child_pid = -1;
|
2003-07-31 11:20:07 -07:00
|
|
|
static volatile sig_atomic_t cur_state = STATE_INIT;
|
|
|
|
|
|
|
|
/* Queue for the allowed logfiles */
|
|
|
|
struct logname {
|
2015-01-15 22:39:28 -08:00
|
|
|
char path[PATH_MAX];
|
2003-07-31 11:20:07 -07:00
|
|
|
TAILQ_ENTRY(logname) next;
|
|
|
|
};
|
|
|
|
static TAILQ_HEAD(, logname) lognames;
|
|
|
|
|
|
|
|
static void check_log_name(char *, size_t);
|
2007-02-20 03:24:32 -08:00
|
|
|
static int open_file(char *);
|
|
|
|
static int open_pipe(char *);
|
2003-07-31 11:20:07 -07:00
|
|
|
static void check_tty_name(char *, size_t);
|
|
|
|
static void increase_state(int);
|
|
|
|
static void sig_pass_to_chld(int);
|
|
|
|
static void sig_got_chld(int);
|
|
|
|
static void must_read(int, void *, size_t);
|
|
|
|
static void must_write(int, void *, size_t);
|
2003-10-24 14:21:27 -07:00
|
|
|
static int may_read(int, void *, size_t);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
void
|
|
|
|
priv_init(int lockfd, int nullfd, int argc, char *argv[])
|
2003-07-31 11:20:07 -07:00
|
|
|
{
|
2016-10-06 06:03:47 -07:00
|
|
|
int i, socks[2];
|
2003-07-31 11:20:07 -07:00
|
|
|
struct passwd *pw;
|
2016-12-27 11:16:24 -08:00
|
|
|
char *execpath, childnum[11], **privargv;
|
2004-03-06 11:42:38 -08:00
|
|
|
|
2003-07-31 11:20:07 -07:00
|
|
|
/* Create sockets */
|
|
|
|
if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
|
|
|
|
err(1, "socketpair() failed");
|
|
|
|
|
|
|
|
pw = getpwnam("_syslogd");
|
|
|
|
if (pw == NULL)
|
|
|
|
errx(1, "unknown user _syslogd");
|
|
|
|
|
|
|
|
child_pid = fork();
|
|
|
|
if (child_pid < 0)
|
|
|
|
err(1, "fork() failed");
|
|
|
|
|
|
|
|
if (!child_pid) {
|
|
|
|
/* Child - drop privileges and return */
|
|
|
|
if (chroot(pw->pw_dir) != 0)
|
2016-12-27 11:16:24 -08:00
|
|
|
err(1, "chroot %s", pw->pw_dir);
|
2004-03-14 11:17:05 -08:00
|
|
|
if (chdir("/") != 0)
|
2016-12-27 11:16:24 -08:00
|
|
|
err(1, "chdir %s", pw->pw_dir);
|
2003-12-29 14:08:44 -08:00
|
|
|
|
2005-05-02 22:44:35 -07:00
|
|
|
if (setgroups(1, &pw->pw_gid) == -1)
|
2003-10-22 12:37:38 -07:00
|
|
|
err(1, "setgroups() failed");
|
2005-05-02 22:44:35 -07:00
|
|
|
if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
|
|
|
|
err(1, "setresgid() failed");
|
|
|
|
if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
|
|
|
|
err(1, "setresuid() failed");
|
2003-07-31 11:20:07 -07:00
|
|
|
close(socks[0]);
|
|
|
|
priv_fd = socks[1];
|
2016-10-06 06:03:47 -07:00
|
|
|
return;
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
2016-10-06 06:03:47 -07:00
|
|
|
close(socks[1]);
|
2015-10-16 09:10:10 -07:00
|
|
|
|
2016-12-27 11:16:24 -08:00
|
|
|
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 /");
|
|
|
|
|
2003-07-31 11:20:07 -07:00
|
|
|
if (!Debug) {
|
2008-09-29 11:41:56 -07:00
|
|
|
close(lockfd);
|
2003-07-31 11:20:07 -07:00
|
|
|
dup2(nullfd, STDIN_FILENO);
|
|
|
|
dup2(nullfd, STDOUT_FILENO);
|
|
|
|
dup2(nullfd, STDERR_FILENO);
|
|
|
|
}
|
|
|
|
if (nullfd > 2)
|
|
|
|
close(nullfd);
|
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
if (dup3(socks[0], 3, 0) == -1)
|
|
|
|
err(1, "dup3 priv sock failed");
|
2016-10-16 15:00:14 -07:00
|
|
|
if (closefrom(4) == -1)
|
|
|
|
err(1, "closefrom 4 failed");
|
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
snprintf(childnum, sizeof(childnum), "%d", child_pid);
|
|
|
|
if ((privargv = reallocarray(NULL, argc + 3, sizeof(char *))) == NULL)
|
|
|
|
err(1, "alloc priv argv failed");
|
2016-12-27 11:16:24 -08:00
|
|
|
privargv[0] = execpath;
|
|
|
|
for (i = 1; i < argc; i++)
|
2016-10-06 06:03:47 -07:00
|
|
|
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]);
|
|
|
|
}
|
2003-07-31 11:20:07 -07:00
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
__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;
|
2016-12-30 15:21:26 -08:00
|
|
|
sigset_t sigmask;
|
2016-10-06 06:03:47 -07:00
|
|
|
|
|
|
|
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);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
/* 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");
|
|
|
|
|
2016-12-30 15:21:26 -08:00
|
|
|
sigemptyset(&sigmask);
|
|
|
|
if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1)
|
|
|
|
err(1, "sigprocmask priv");
|
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
if (stat(conf, &cf_info) < 0)
|
|
|
|
err(1, "stat config file failed");
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
TAILQ_INIT(&lognames);
|
|
|
|
increase_state(STATE_CONFIG);
|
2003-10-24 14:21:27 -07:00
|
|
|
restart = 0;
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
while (cur_state < STATE_QUIT) {
|
2016-10-06 06:03:47 -07:00
|
|
|
if (may_read(sock, &cmd, sizeof(int)))
|
2003-10-24 14:21:27 -07:00
|
|
|
break;
|
2003-07-31 11:20:07 -07:00
|
|
|
switch (cmd) {
|
|
|
|
case PRIV_OPEN_TTY:
|
2015-10-15 13:26:47 -07:00
|
|
|
logdebug("[priv]: msg PRIV_OPEN_TTY received\n");
|
2003-09-24 16:35:45 -07:00
|
|
|
/* Expecting: length, path */
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &path_len, sizeof(size_t));
|
2003-09-24 16:35:45 -07:00
|
|
|
if (path_len == 0 || path_len > sizeof(path))
|
2014-08-18 17:53:01 -07:00
|
|
|
_exit(1);
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &path, path_len);
|
2003-09-24 16:35:45 -07:00
|
|
|
path[path_len - 1] = '\0';
|
2014-08-25 13:19:14 -07:00
|
|
|
check_tty_name(path, sizeof(path));
|
2003-07-31 11:20:07 -07:00
|
|
|
fd = open(path, O_WRONLY|O_NONBLOCK, 0);
|
2016-10-06 06:03:47 -07:00
|
|
|
send_fd(sock, fd);
|
2003-07-31 11:20:07 -07:00
|
|
|
if (fd < 0)
|
2003-08-01 07:04:35 -07:00
|
|
|
warnx("priv_open_tty failed");
|
2004-04-02 13:44:50 -08:00
|
|
|
else
|
|
|
|
close(fd);
|
2003-07-31 11:20:07 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PRIV_OPEN_LOG:
|
2007-02-20 03:24:32 -08:00
|
|
|
case PRIV_OPEN_PIPE:
|
2015-10-15 13:26:47 -07:00
|
|
|
logdebug("[priv]: msg PRIV_OPEN_%s received\n",
|
2007-02-20 03:24:32 -08:00
|
|
|
cmd == PRIV_OPEN_PIPE ? "PIPE" : "LOG");
|
2003-09-24 16:35:45 -07:00
|
|
|
/* Expecting: length, path */
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &path_len, sizeof(size_t));
|
2003-09-24 16:35:45 -07:00
|
|
|
if (path_len == 0 || path_len > sizeof(path))
|
2014-08-18 17:53:01 -07:00
|
|
|
_exit(1);
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &path, path_len);
|
2003-09-24 16:35:45 -07:00
|
|
|
path[path_len - 1] = '\0';
|
2014-08-25 13:19:14 -07:00
|
|
|
check_log_name(path, sizeof(path));
|
2007-02-20 03:24:32 -08:00
|
|
|
|
|
|
|
if (cmd == PRIV_OPEN_LOG)
|
|
|
|
fd = open_file(path);
|
|
|
|
else if (cmd == PRIV_OPEN_PIPE)
|
|
|
|
fd = open_pipe(path);
|
|
|
|
else
|
|
|
|
errx(1, "invalid cmd");
|
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
send_fd(sock, fd);
|
2003-07-31 11:20:07 -07:00
|
|
|
if (fd < 0)
|
2003-08-01 07:04:35 -07:00
|
|
|
warnx("priv_open_log failed");
|
2004-04-02 13:44:50 -08:00
|
|
|
else
|
|
|
|
close(fd);
|
2003-07-31 11:20:07 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PRIV_OPEN_UTMP:
|
2015-10-15 13:26:47 -07:00
|
|
|
logdebug("[priv]: msg PRIV_OPEN_UTMP received\n");
|
2003-07-31 11:20:07 -07:00
|
|
|
fd = open(_PATH_UTMP, O_RDONLY|O_NONBLOCK, 0);
|
2016-10-06 06:03:47 -07:00
|
|
|
send_fd(sock, fd);
|
2003-07-31 11:20:07 -07:00
|
|
|
if (fd < 0)
|
2003-08-01 07:04:35 -07:00
|
|
|
warnx("priv_open_utmp failed");
|
2004-04-02 13:44:50 -08:00
|
|
|
else
|
|
|
|
close(fd);
|
2003-07-31 11:20:07 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PRIV_OPEN_CONFIG:
|
2015-10-15 13:26:47 -07:00
|
|
|
logdebug("[priv]: msg PRIV_OPEN_CONFIG received\n");
|
2016-10-06 06:03:47 -07:00
|
|
|
stat(conf, &cf_info);
|
|
|
|
fd = open(conf, O_RDONLY|O_NONBLOCK, 0);
|
|
|
|
send_fd(sock, fd);
|
2003-07-31 11:20:07 -07:00
|
|
|
if (fd < 0)
|
2003-08-01 07:04:35 -07:00
|
|
|
warnx("priv_open_config failed");
|
2004-04-02 13:44:50 -08:00
|
|
|
else
|
|
|
|
close(fd);
|
2003-07-31 11:20:07 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PRIV_CONFIG_MODIFIED:
|
2015-10-15 13:26:47 -07:00
|
|
|
logdebug("[priv]: msg PRIV_CONFIG_MODIFIED received\n");
|
2016-10-06 06:03:47 -07:00
|
|
|
if (stat(conf, &cf_stat) < 0 ||
|
2003-07-31 11:20:07 -07:00
|
|
|
timespeccmp(&cf_info.st_mtimespec,
|
|
|
|
&cf_stat.st_mtimespec, <) ||
|
|
|
|
cf_info.st_size != cf_stat.st_size) {
|
2015-10-15 13:26:47 -07:00
|
|
|
logdebug("config file modified: restarting\n");
|
2003-10-24 14:21:27 -07:00
|
|
|
restart = result = 1;
|
2016-10-06 06:03:47 -07:00
|
|
|
must_write(sock, &result, sizeof(int));
|
2003-07-31 11:20:07 -07:00
|
|
|
} else {
|
|
|
|
result = 0;
|
2016-10-06 06:03:47 -07:00
|
|
|
must_write(sock, &result, sizeof(int));
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
2003-07-31 11:25:58 -07:00
|
|
|
break;
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
case PRIV_DONE_CONFIG_PARSE:
|
2015-10-15 13:26:47 -07:00
|
|
|
logdebug("[priv]: msg PRIV_DONE_CONFIG_PARSE "
|
|
|
|
"received\n");
|
2003-07-31 11:20:07 -07:00
|
|
|
increase_state(STATE_RUNNING);
|
|
|
|
break;
|
|
|
|
|
2014-08-20 12:16:27 -07:00
|
|
|
case PRIV_GETADDRINFO:
|
2015-10-15 13:26:47 -07:00
|
|
|
logdebug("[priv]: msg PRIV_GETADDRINFO received\n");
|
2014-08-25 11:19:18 -07:00
|
|
|
/* Expecting: len, proto, len, host, len, serv */
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &protoname_len, sizeof(size_t));
|
2014-08-25 11:19:18 -07:00
|
|
|
if (protoname_len == 0 ||
|
|
|
|
protoname_len > sizeof(protoname))
|
|
|
|
_exit(1);
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &protoname, protoname_len);
|
2014-08-25 11:19:18 -07:00
|
|
|
protoname[protoname_len - 1] = '\0';
|
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &hostname_len, sizeof(size_t));
|
2014-08-20 12:16:27 -07:00
|
|
|
if (hostname_len == 0 ||
|
|
|
|
hostname_len > sizeof(hostname))
|
2014-08-18 17:53:01 -07:00
|
|
|
_exit(1);
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &hostname, hostname_len);
|
2003-09-24 16:35:45 -07:00
|
|
|
hostname[hostname_len - 1] = '\0';
|
2004-07-02 22:32:18 -07:00
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &servname_len, sizeof(size_t));
|
2014-08-20 12:16:27 -07:00
|
|
|
if (servname_len == 0 ||
|
|
|
|
servname_len > sizeof(servname))
|
2014-08-18 17:53:01 -07:00
|
|
|
_exit(1);
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &servname, servname_len);
|
2004-07-02 22:32:18 -07:00
|
|
|
servname[servname_len - 1] = '\0';
|
|
|
|
|
2014-08-21 10:00:34 -07:00
|
|
|
memset(&hints, 0, sizeof(hints));
|
2014-12-31 05:55:57 -08:00
|
|
|
switch (strlen(protoname)) {
|
|
|
|
case 3:
|
2014-08-25 11:19:18 -07:00
|
|
|
hints.ai_family = AF_UNSPEC;
|
2014-12-31 05:55:57 -08:00
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
switch (protoname[3]) {
|
|
|
|
case '4':
|
|
|
|
hints.ai_family = AF_INET;
|
|
|
|
break;
|
|
|
|
case '6':
|
|
|
|
hints.ai_family = AF_INET6;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
errx(1, "bad ip version %s", protoname);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
errx(1, "bad protocol length %s", protoname);
|
|
|
|
}
|
|
|
|
if (strncmp(protoname, "udp", 3) == 0) {
|
|
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
hints.ai_protocol = IPPROTO_UDP;
|
|
|
|
} else if (strncmp(protoname, "tcp", 3) == 0) {
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
hints.ai_protocol = IPPROTO_TCP;
|
2014-08-25 11:19:18 -07:00
|
|
|
} else {
|
|
|
|
errx(1, "unknown protocol %s", protoname);
|
|
|
|
}
|
2004-07-02 22:32:18 -07:00
|
|
|
i = getaddrinfo(hostname, servname, &hints, &res0);
|
|
|
|
if (i != 0 || res0 == NULL) {
|
2003-07-31 11:20:07 -07:00
|
|
|
addr_len = 0;
|
2016-10-06 06:03:47 -07:00
|
|
|
must_write(sock, &addr_len, sizeof(int));
|
2003-07-31 11:20:07 -07:00
|
|
|
} else {
|
2004-07-02 22:32:18 -07:00
|
|
|
/* Just send the first address */
|
|
|
|
i = res0->ai_addrlen;
|
2016-10-06 06:03:47 -07:00
|
|
|
must_write(sock, &i, sizeof(int));
|
|
|
|
must_write(sock, res0->ai_addr, i);
|
2004-07-02 22:32:18 -07:00
|
|
|
freeaddrinfo(res0);
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-08-20 13:10:17 -07:00
|
|
|
case PRIV_GETNAMEINFO:
|
2015-10-15 13:26:47 -07:00
|
|
|
logdebug("[priv]: msg PRIV_GETNAMEINFO received\n");
|
2016-10-06 06:03:47 -07:00
|
|
|
if (numeric)
|
2014-08-20 13:10:17 -07:00
|
|
|
errx(1, "rejected attempt to getnameinfo");
|
|
|
|
/* Expecting: length, sockaddr */
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &addr_len, sizeof(int));
|
2014-08-25 11:05:30 -07:00
|
|
|
if (addr_len <= 0 || (size_t)addr_len > sizeof(addr))
|
2014-08-18 17:53:01 -07:00
|
|
|
_exit(1);
|
2016-10-06 06:03:47 -07:00
|
|
|
must_read(sock, &addr, addr_len);
|
2014-08-20 13:10:17 -07:00
|
|
|
if (getnameinfo((struct sockaddr *)&addr, addr_len,
|
|
|
|
hostname, sizeof(hostname), NULL, 0,
|
|
|
|
NI_NOFQDN|NI_NAMEREQD|NI_DGRAM) != 0) {
|
2003-07-31 11:20:07 -07:00
|
|
|
addr_len = 0;
|
2016-10-06 06:03:47 -07:00
|
|
|
must_write(sock, &addr_len, sizeof(int));
|
2003-07-31 11:20:07 -07:00
|
|
|
} else {
|
2014-08-20 13:10:17 -07:00
|
|
|
addr_len = strlen(hostname) + 1;
|
2016-10-06 06:03:47 -07:00
|
|
|
must_write(sock, &addr_len, sizeof(int));
|
|
|
|
must_write(sock, hostname, addr_len);
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2003-08-01 07:04:35 -07:00
|
|
|
errx(1, "unknown command %d", cmd);
|
2003-07-31 11:20:07 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
close(sock);
|
2005-06-06 16:20:44 -07:00
|
|
|
|
2003-07-31 11:20:07 -07:00
|
|
|
/* Unlink any domain sockets that have been opened */
|
2014-09-10 06:16:20 -07:00
|
|
|
for (i = 0; i < nunix; i++)
|
2016-10-06 06:03:47 -07:00
|
|
|
(void)unlink(path_unix[i]);
|
|
|
|
if (path_ctlsock != NULL)
|
2014-10-03 14:55:22 -07:00
|
|
|
(void)unlink(path_ctlsock);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
2003-10-24 14:21:27 -07:00
|
|
|
if (restart) {
|
2016-10-06 06:03:47 -07:00
|
|
|
int status;
|
2003-07-31 11:20:07 -07:00
|
|
|
|
2016-10-06 06:03:47 -07:00
|
|
|
waitpid(child_pid, &status, 0);
|
2016-12-30 15:21:26 -08:00
|
|
|
sigemptyset(&sigmask);
|
|
|
|
sigaddset(&sigmask, SIGHUP);
|
|
|
|
if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1)
|
|
|
|
err(1, "sigprocmask exec");
|
2003-07-31 11:20:07 -07:00
|
|
|
execvp(argv[0], argv);
|
2016-10-06 06:03:47 -07:00
|
|
|
err(1, "exec restart '%s' failed", argv[0]);
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
2005-12-02 08:50:11 -08:00
|
|
|
unlink(_PATH_LOGPID);
|
2016-10-06 06:03:47 -07:00
|
|
|
exit(0);
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
|
|
|
|
2007-02-20 03:24:32 -08:00
|
|
|
static int
|
|
|
|
open_file(char *path)
|
|
|
|
{
|
|
|
|
/* must not start with | */
|
|
|
|
if (path[0] == '|')
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
return (open(path, O_WRONLY|O_APPEND|O_NONBLOCK, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
open_pipe(char *cmd)
|
|
|
|
{
|
|
|
|
char *argp[] = {"sh", "-c", NULL, NULL};
|
|
|
|
struct passwd *pw;
|
|
|
|
int fd[2];
|
|
|
|
int bsize, flags;
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
/* skip over leading | and whitespace */
|
|
|
|
if (cmd[0] != '|')
|
|
|
|
return (-1);
|
2016-06-28 11:22:50 -07:00
|
|
|
for (cmd++; *cmd && *cmd == ' '; cmd++)
|
2007-02-20 03:24:32 -08:00
|
|
|
; /* nothing */
|
|
|
|
if (!*cmd)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
argp[2] = cmd;
|
|
|
|
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd) == -1) {
|
2007-03-14 22:18:32 -07:00
|
|
|
warnx("open_pipe");
|
2007-02-20 03:24:32 -08:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make the fd on syslogd's side nonblocking */
|
2016-04-02 12:55:10 -07:00
|
|
|
if ((flags = fcntl(fd[1], F_GETFL)) == -1) {
|
2007-03-14 22:18:32 -07:00
|
|
|
warnx("fcntl");
|
2007-02-20 03:24:32 -08:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
flags |= O_NONBLOCK;
|
|
|
|
if ((flags = fcntl(fd[1], F_SETFL, flags)) == -1) {
|
2007-03-14 22:18:32 -07:00
|
|
|
warnx("fcntl");
|
2007-02-20 03:24:32 -08:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (pid = fork()) {
|
|
|
|
case -1:
|
2007-03-14 22:18:32 -07:00
|
|
|
warnx("fork error");
|
2007-02-20 03:24:32 -08:00
|
|
|
return (-1);
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
close(fd[0]);
|
|
|
|
return (fd[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd[1]);
|
|
|
|
|
|
|
|
/* grow receive buffer */
|
|
|
|
bsize = 65535;
|
|
|
|
while (bsize > 0 && setsockopt(fd[0], SOL_SOCKET, SO_RCVBUF,
|
|
|
|
&bsize, sizeof(bsize)) == -1)
|
|
|
|
bsize /= 2;
|
|
|
|
|
|
|
|
if ((pw = getpwnam("_syslogd")) == NULL)
|
|
|
|
errx(1, "unknown user _syslogd");
|
|
|
|
if (setgroups(1, &pw->pw_gid) == -1 ||
|
|
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
|
|
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
|
|
|
|
err(1, "failure dropping privs");
|
|
|
|
endpwent();
|
|
|
|
|
|
|
|
if (dup2(fd[0], STDIN_FILENO) == -1)
|
|
|
|
err(1, "dup2 failed");
|
|
|
|
if (execv("/bin/sh", argp) == -1)
|
|
|
|
err(1, "execv %s", cmd);
|
2007-03-14 22:18:32 -07:00
|
|
|
/* NOTREACHED */
|
|
|
|
return (-1);
|
2007-02-20 03:24:32 -08:00
|
|
|
}
|
|
|
|
|
2003-07-31 11:20:07 -07:00
|
|
|
/* Check that the terminal device is ok, and if not, rewrite to /dev/null.
|
|
|
|
* Either /dev/console or /dev/tty* are allowed.
|
|
|
|
*/
|
|
|
|
static void
|
2014-08-25 13:19:14 -07:00
|
|
|
check_tty_name(char *tty, size_t ttysize)
|
2003-07-31 11:20:07 -07:00
|
|
|
{
|
|
|
|
const char ttypre[] = "/dev/tty";
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
/* Any path containing '..' is invalid. */
|
2014-08-25 13:19:14 -07:00
|
|
|
for (p = tty; p + 1 < tty + ttysize && *p; p++)
|
2003-07-31 11:20:07 -07:00
|
|
|
if (*p == '.' && *(p + 1) == '.')
|
|
|
|
goto bad_path;
|
|
|
|
|
|
|
|
if (strcmp(_PATH_CONSOLE, tty) && strncmp(tty, ttypre, strlen(ttypre)))
|
|
|
|
goto bad_path;
|
|
|
|
return;
|
|
|
|
|
|
|
|
bad_path:
|
|
|
|
warnx ("%s: invalid attempt to open %s: rewriting to /dev/null",
|
2004-09-14 16:41:29 -07:00
|
|
|
"check_tty_name", tty);
|
2014-08-25 13:19:14 -07:00
|
|
|
strlcpy(tty, "/dev/null", ttysize);
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If we are in the initial configuration state, accept a logname and add
|
|
|
|
* it to the list of acceptable logfiles. Otherwise, check against this list
|
|
|
|
* and rewrite to /dev/null if it's a bad path.
|
|
|
|
*/
|
|
|
|
static void
|
2014-08-25 13:19:14 -07:00
|
|
|
check_log_name(char *lognam, size_t logsize)
|
2003-07-31 11:20:07 -07:00
|
|
|
{
|
|
|
|
struct logname *lg;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
/* Any path containing '..' is invalid. */
|
2014-08-25 13:19:14 -07:00
|
|
|
for (p = lognam; p + 1 < lognam + logsize && *p; p++)
|
2003-07-31 11:20:07 -07:00
|
|
|
if (*p == '.' && *(p + 1) == '.')
|
|
|
|
goto bad_path;
|
|
|
|
|
|
|
|
switch (cur_state) {
|
|
|
|
case STATE_CONFIG:
|
|
|
|
lg = malloc(sizeof(struct logname));
|
|
|
|
if (!lg)
|
|
|
|
err(1, "check_log_name() malloc");
|
2015-01-15 22:39:28 -08:00
|
|
|
strlcpy(lg->path, lognam, PATH_MAX);
|
2003-07-31 11:20:07 -07:00
|
|
|
TAILQ_INSERT_TAIL(&lognames, lg, next);
|
|
|
|
break;
|
|
|
|
case STATE_RUNNING:
|
|
|
|
TAILQ_FOREACH(lg, &lognames, next)
|
2004-07-03 16:40:44 -07:00
|
|
|
if (!strcmp(lg->path, lognam))
|
2003-07-31 11:20:07 -07:00
|
|
|
return;
|
|
|
|
goto bad_path;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Any other state should just refuse the request */
|
|
|
|
goto bad_path;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
bad_path:
|
|
|
|
warnx("%s: invalid attempt to open %s: rewriting to /dev/null",
|
2004-09-14 16:41:29 -07:00
|
|
|
"check_log_name", lognam);
|
2014-08-25 13:19:14 -07:00
|
|
|
strlcpy(lognam, "/dev/null", logsize);
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Crank our state into less permissive modes */
|
|
|
|
static void
|
|
|
|
increase_state(int state)
|
|
|
|
{
|
|
|
|
if (state <= cur_state)
|
2003-07-31 11:25:58 -07:00
|
|
|
errx(1, "attempt to decrease or match current state");
|
2003-10-24 14:21:27 -07:00
|
|
|
if (state < STATE_INIT || state > STATE_QUIT)
|
2003-07-31 11:25:58 -07:00
|
|
|
errx(1, "attempt to switch to invalid state");
|
2003-07-31 11:20:07 -07:00
|
|
|
cur_state = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open console or a terminal device for writing */
|
|
|
|
int
|
|
|
|
priv_open_tty(const char *tty)
|
|
|
|
{
|
2015-01-15 22:39:28 -08:00
|
|
|
char path[PATH_MAX];
|
2003-07-31 11:20:07 -07:00
|
|
|
int cmd, fd;
|
2003-09-24 16:35:45 -07:00
|
|
|
size_t path_len;
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
if (priv_fd < 0)
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: called from privileged portion", __func__);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
if (strlcpy(path, tty, sizeof path) >= sizeof(path))
|
|
|
|
return -1;
|
2003-09-24 16:35:45 -07:00
|
|
|
path_len = strlen(path) + 1;
|
|
|
|
|
2003-07-31 11:20:07 -07:00
|
|
|
cmd = PRIV_OPEN_TTY;
|
|
|
|
must_write(priv_fd, &cmd, sizeof(int));
|
2003-09-24 16:35:45 -07:00
|
|
|
must_write(priv_fd, &path_len, sizeof(size_t));
|
|
|
|
must_write(priv_fd, path, path_len);
|
2003-07-31 11:20:07 -07:00
|
|
|
fd = receive_fd(priv_fd);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open log-file */
|
|
|
|
int
|
2004-07-03 16:40:44 -07:00
|
|
|
priv_open_log(const char *lognam)
|
2003-07-31 11:20:07 -07:00
|
|
|
{
|
2015-01-15 22:39:28 -08:00
|
|
|
char path[PATH_MAX];
|
2003-07-31 11:20:07 -07:00
|
|
|
int cmd, fd;
|
2003-09-24 16:35:45 -07:00
|
|
|
size_t path_len;
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
if (priv_fd < 0)
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: called from privileged child", __func__);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
2004-07-03 16:40:44 -07:00
|
|
|
if (strlcpy(path, lognam, sizeof path) >= sizeof(path))
|
2003-07-31 11:20:07 -07:00
|
|
|
return -1;
|
2003-09-24 16:35:45 -07:00
|
|
|
path_len = strlen(path) + 1;
|
|
|
|
|
2007-02-20 03:24:32 -08:00
|
|
|
if (lognam[0] == '|')
|
|
|
|
cmd = PRIV_OPEN_PIPE;
|
|
|
|
else
|
|
|
|
cmd = PRIV_OPEN_LOG;
|
2003-07-31 11:20:07 -07:00
|
|
|
must_write(priv_fd, &cmd, sizeof(int));
|
2003-09-24 16:35:45 -07:00
|
|
|
must_write(priv_fd, &path_len, sizeof(size_t));
|
|
|
|
must_write(priv_fd, path, path_len);
|
2003-07-31 11:20:07 -07:00
|
|
|
fd = receive_fd(priv_fd);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open utmp for reading */
|
|
|
|
FILE *
|
|
|
|
priv_open_utmp(void)
|
|
|
|
{
|
|
|
|
int cmd, fd;
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
if (priv_fd < 0)
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: called from privileged portion", __func__);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
cmd = PRIV_OPEN_UTMP;
|
|
|
|
must_write(priv_fd, &cmd, sizeof(int));
|
|
|
|
fd = receive_fd(priv_fd);
|
|
|
|
if (fd < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
fp = fdopen(fd, "r");
|
|
|
|
if (!fp) {
|
|
|
|
warn("priv_open_utmp: fdopen() failed");
|
2003-07-31 11:25:58 -07:00
|
|
|
close(fd);
|
2003-07-31 11:20:07 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open syslog config file for reading */
|
|
|
|
FILE *
|
|
|
|
priv_open_config(void)
|
|
|
|
{
|
|
|
|
int cmd, fd;
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
if (priv_fd < 0)
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: called from privileged portion", __func__);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
cmd = PRIV_OPEN_CONFIG;
|
|
|
|
must_write(priv_fd, &cmd, sizeof(int));
|
|
|
|
fd = receive_fd(priv_fd);
|
|
|
|
if (fd < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
fp = fdopen(fd, "r");
|
|
|
|
if (!fp) {
|
|
|
|
warn("priv_open_config: fdopen() failed");
|
2003-07-31 11:25:58 -07:00
|
|
|
close(fd);
|
2003-07-31 11:20:07 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ask if config file has been modified since last attempt to read it */
|
|
|
|
int
|
2004-07-09 09:22:02 -07:00
|
|
|
priv_config_modified(void)
|
2003-07-31 11:20:07 -07:00
|
|
|
{
|
|
|
|
int cmd, res;
|
|
|
|
|
|
|
|
if (priv_fd < 0)
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: called from privileged portion", __func__);
|
2003-08-01 07:04:35 -07:00
|
|
|
|
2003-07-31 11:20:07 -07:00
|
|
|
cmd = PRIV_CONFIG_MODIFIED;
|
|
|
|
must_write(priv_fd, &cmd, sizeof(int));
|
|
|
|
|
|
|
|
/* Expect back integer signalling 1 for modification */
|
|
|
|
must_read(priv_fd, &res, sizeof(int));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Child can signal that its initial parsing is done, so that parent
|
|
|
|
* can revoke further logfile permissions. This call only works once. */
|
|
|
|
void
|
|
|
|
priv_config_parse_done(void)
|
|
|
|
{
|
|
|
|
int cmd;
|
|
|
|
|
|
|
|
if (priv_fd < 0)
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: called from privileged portion", __func__);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
cmd = PRIV_DONE_CONFIG_PARSE;
|
|
|
|
must_write(priv_fd, &cmd, sizeof(int));
|
|
|
|
}
|
|
|
|
|
2014-08-20 13:10:17 -07:00
|
|
|
/* Name/service to address translation. Response is placed into addr.
|
|
|
|
* Return 0 for success or < 0 for error like getaddrinfo(3) */
|
2003-07-31 11:20:07 -07:00
|
|
|
int
|
2014-08-25 11:19:18 -07:00
|
|
|
priv_getaddrinfo(char *proto, char *host, char *serv, struct sockaddr *addr,
|
2004-07-02 22:32:18 -07:00
|
|
|
size_t addr_len)
|
2003-07-31 11:20:07 -07:00
|
|
|
{
|
2015-01-19 08:40:49 -08:00
|
|
|
char protocpy[5], hostcpy[NI_MAXHOST], servcpy[NI_MAXSERV];
|
2003-07-31 11:20:07 -07:00
|
|
|
int cmd, ret_len;
|
2014-08-25 11:19:18 -07:00
|
|
|
size_t protoname_len, hostname_len, servname_len;
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
if (priv_fd < 0)
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: called from privileged portion", __func__);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
2014-08-25 11:19:18 -07:00
|
|
|
if (strlcpy(protocpy, proto, sizeof(protocpy)) >= sizeof(protocpy))
|
|
|
|
errx(1, "%s: overflow attempt in protoname", __func__);
|
|
|
|
protoname_len = strlen(protocpy) + 1;
|
|
|
|
if (strlcpy(hostcpy, host, sizeof(hostcpy)) >= sizeof(hostcpy))
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: overflow attempt in hostname", __func__);
|
2003-09-24 16:35:45 -07:00
|
|
|
hostname_len = strlen(hostcpy) + 1;
|
2014-08-25 11:19:18 -07:00
|
|
|
if (strlcpy(servcpy, serv, sizeof(servcpy)) >= sizeof(servcpy))
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: overflow attempt in servname", __func__);
|
2004-07-02 22:32:18 -07:00
|
|
|
servname_len = strlen(servcpy) + 1;
|
2003-09-24 16:35:45 -07:00
|
|
|
|
2014-08-20 12:16:27 -07:00
|
|
|
cmd = PRIV_GETADDRINFO;
|
2003-07-31 11:20:07 -07:00
|
|
|
must_write(priv_fd, &cmd, sizeof(int));
|
2014-08-25 11:19:18 -07:00
|
|
|
must_write(priv_fd, &protoname_len, sizeof(size_t));
|
|
|
|
must_write(priv_fd, protocpy, protoname_len);
|
2003-09-24 16:35:45 -07:00
|
|
|
must_write(priv_fd, &hostname_len, sizeof(size_t));
|
|
|
|
must_write(priv_fd, hostcpy, hostname_len);
|
2004-07-02 22:32:18 -07:00
|
|
|
must_write(priv_fd, &servname_len, sizeof(size_t));
|
|
|
|
must_write(priv_fd, servcpy, servname_len);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
/* Expect back an integer size, and then a string of that length */
|
|
|
|
must_read(priv_fd, &ret_len, sizeof(int));
|
|
|
|
|
|
|
|
/* Check there was no error (indicated by a return of 0) */
|
|
|
|
if (!ret_len)
|
2014-08-20 12:16:27 -07:00
|
|
|
return (-1);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
/* Make sure we aren't overflowing the passed in buffer */
|
2014-08-25 11:05:30 -07:00
|
|
|
if (ret_len < 0 || (size_t)ret_len > addr_len)
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: overflow attempt in return", __func__);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
/* Read the resolved address and make sure we got all of it */
|
2004-07-02 22:32:18 -07:00
|
|
|
memset(addr, '\0', addr_len);
|
2003-07-31 11:20:07 -07:00
|
|
|
must_read(priv_fd, addr, ret_len);
|
2004-07-02 22:32:18 -07:00
|
|
|
|
2014-08-20 12:16:27 -07:00
|
|
|
return (0);
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
|
|
|
|
2014-08-20 13:10:17 -07:00
|
|
|
/* Reverse address resolution; response is placed into host.
|
|
|
|
* Return 0 for success or < 0 for error like getnameinfo(3) */
|
2003-07-31 11:20:07 -07:00
|
|
|
int
|
2014-08-20 13:10:17 -07:00
|
|
|
priv_getnameinfo(struct sockaddr *sa, socklen_t salen, char *host,
|
|
|
|
size_t hostlen)
|
2003-07-31 11:20:07 -07:00
|
|
|
{
|
|
|
|
int cmd, ret_len;
|
|
|
|
|
|
|
|
if (priv_fd < 0)
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s called from privileged portion", __func__);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
2014-08-20 13:10:17 -07:00
|
|
|
cmd = PRIV_GETNAMEINFO;
|
2003-07-31 11:20:07 -07:00
|
|
|
must_write(priv_fd, &cmd, sizeof(int));
|
2014-08-20 13:10:17 -07:00
|
|
|
must_write(priv_fd, &salen, sizeof(int));
|
|
|
|
must_write(priv_fd, sa, salen);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
/* Expect back an integer size, and then a string of that length */
|
|
|
|
must_read(priv_fd, &ret_len, sizeof(int));
|
|
|
|
|
|
|
|
/* Check there was no error (indicated by a return of 0) */
|
2003-08-01 07:04:35 -07:00
|
|
|
if (!ret_len)
|
2014-08-20 13:10:17 -07:00
|
|
|
return (-1);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
/* Check we don't overflow the passed in buffer */
|
2014-08-25 11:05:30 -07:00
|
|
|
if (ret_len < 0 || (size_t)ret_len > hostlen)
|
2014-08-18 17:28:48 -07:00
|
|
|
errx(1, "%s: overflow attempt in return", __func__);
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
/* Read the resolved hostname */
|
2014-08-20 13:10:17 -07:00
|
|
|
must_read(priv_fd, host, ret_len);
|
|
|
|
return (0);
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
|
|
|
|
2004-04-09 13:13:25 -07:00
|
|
|
/* Pass the signal through to child */
|
2003-07-31 11:20:07 -07:00
|
|
|
static void
|
|
|
|
sig_pass_to_chld(int sig)
|
|
|
|
{
|
2008-03-16 09:55:29 -07:00
|
|
|
int save_errno = errno;
|
2004-03-06 11:42:38 -08:00
|
|
|
|
2003-10-24 14:21:27 -07:00
|
|
|
if (child_pid != -1)
|
|
|
|
kill(child_pid, sig);
|
2008-03-16 09:55:29 -07:00
|
|
|
errno = save_errno;
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* When child dies, move into the shutdown state */
|
2004-09-14 16:24:41 -07:00
|
|
|
/* ARGSUSED */
|
2003-07-31 11:20:07 -07:00
|
|
|
static void
|
|
|
|
sig_got_chld(int sig)
|
|
|
|
{
|
2008-03-16 09:55:29 -07:00
|
|
|
int save_errno = errno;
|
2007-02-20 03:24:32 -08:00
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
do {
|
|
|
|
pid = waitpid(WAIT_ANY, NULL, WNOHANG);
|
2008-03-16 08:44:18 -07:00
|
|
|
if (pid == child_pid && cur_state < STATE_QUIT)
|
|
|
|
cur_state = STATE_QUIT;
|
|
|
|
} while (pid > 0 || (pid == -1 && errno == EINTR));
|
2008-03-16 09:55:29 -07:00
|
|
|
errno = save_errno;
|
2003-07-31 11:20:07 -07:00
|
|
|
}
|
|
|
|
|
2003-10-24 14:21:27 -07:00
|
|
|
/* Read all data or return 1 for error. */
|
|
|
|
static int
|
|
|
|
may_read(int fd, void *buf, size_t n)
|
|
|
|
{
|
|
|
|
char *s = buf;
|
2014-08-25 11:05:30 -07:00
|
|
|
ssize_t res;
|
|
|
|
size_t pos = 0;
|
2003-10-24 14:21:27 -07:00
|
|
|
|
|
|
|
while (n > pos) {
|
|
|
|
res = read(fd, s + pos, n - pos);
|
|
|
|
switch (res) {
|
|
|
|
case -1:
|
|
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
|
|
continue;
|
|
|
|
case 0:
|
|
|
|
return (1);
|
|
|
|
default:
|
|
|
|
pos += res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2003-07-31 11:20:07 -07:00
|
|
|
/* Read data with the assertion that it all must come through, or
|
|
|
|
* else abort the process. Based on atomicio() from openssh. */
|
|
|
|
static void
|
|
|
|
must_read(int fd, void *buf, size_t n)
|
|
|
|
{
|
|
|
|
char *s = buf;
|
2014-08-25 11:05:30 -07:00
|
|
|
ssize_t res;
|
|
|
|
size_t pos = 0;
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
while (n > pos) {
|
|
|
|
res = read(fd, s + pos, n - pos);
|
|
|
|
switch (res) {
|
|
|
|
case -1:
|
|
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
|
|
continue;
|
|
|
|
case 0:
|
2014-08-18 17:53:01 -07:00
|
|
|
_exit(1);
|
2003-07-31 11:20:07 -07:00
|
|
|
default:
|
|
|
|
pos += res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write data with the assertion that it all has to be written, or
|
|
|
|
* else abort the process. Based on atomicio() from openssh. */
|
|
|
|
static void
|
|
|
|
must_write(int fd, void *buf, size_t n)
|
|
|
|
{
|
|
|
|
char *s = buf;
|
2014-08-25 11:05:30 -07:00
|
|
|
ssize_t res;
|
|
|
|
size_t pos = 0;
|
2003-07-31 11:20:07 -07:00
|
|
|
|
|
|
|
while (n > pos) {
|
|
|
|
res = write(fd, s + pos, n - pos);
|
|
|
|
switch (res) {
|
|
|
|
case -1:
|
|
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
|
|
continue;
|
|
|
|
case 0:
|
2014-08-18 17:53:01 -07:00
|
|
|
_exit(1);
|
2003-07-31 11:20:07 -07:00
|
|
|
default:
|
|
|
|
pos += res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|