rework the daemon to do fork+exec

It uses the 'common' proc.c from various OpenBSD-daemons.

gmid grew organically bit by bit and it was also the first place where I
tried to implement privsep.  It wasn't done very well, in fact the
parent process (that retains root privileges) just fork()s a generation
of servers, all sharing *exactly* the same address space.  No good!

Now, we fork() and re-exec() ourselves, so that each process has a fresh
address space.

Some features (require client ca for example) are temporarly disabled,
will be fixed in subsequent commits.  The "ge" program is also
temporarly disabled as it needs tweaks to do privsep too.
This commit is contained in:
Omar Polo 2023-06-08 13:59:31 +00:00
parent 99f1fbb0c7
commit c26f2460e4
15 changed files with 1838 additions and 410 deletions

View File

@ -26,6 +26,7 @@ GMID_SRCS = config.c \
log.c \
logger.c \
mime.c \
proc.c \
proxy.c \
puny.c \
sandbox.c \
@ -63,6 +64,7 @@ SRCS = gmid.h \
log.h \
logger.h \
parse.y \
proc.h \
${GMID_SRCS} \
${GE_SRCS} \
${GG_SRCS}
@ -88,8 +90,10 @@ y.tab.c: parse.y
gmid: ${GMID_OBJS}
${CC} ${GMID_OBJS} -o $@ ${LDFLAGS}
ge: ${GE_OBJS}
${CC} ${GE_OBJS} -o $@ ${LDFLAGS}
#ge: ${GE_OBJS}
# ${CC} ${GE_OBJS} -o $@ ${LDFLAGS}
ge:
:
gg: ${GG_OBJS}
${CC} ${GG_OBJS} -o $@ ${LDFLAGS}

405
config.c
View File

@ -16,8 +16,15 @@
#include "gmid.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include "log.h"
#include "proc.h"
void
config_init(void)
{
@ -30,11 +37,15 @@ config_init(void)
init_mime(&conf.mime);
conf.prefork = 3;
conf.sock4 = -1;
conf.sock6 = -1;
}
void
config_free(void)
{
struct privsep *ps;
struct vhost *h, *th;
struct location *l, *tl;
struct proxy *p, *tp;
@ -42,14 +53,33 @@ config_free(void)
struct alist *a, *ta;
int v;
ps = conf.ps;
v = conf.verbose;
if (conf.sock4 != -1) {
event_del(&conf.evsock4);
close(conf.sock4);
}
if (conf.sock6 != -1) {
event_del(&conf.evsock6);
close(conf.sock6);
}
free_mime(&conf.mime);
memset(&conf, 0, sizeof(conf));
conf.ps = ps;
conf.verbose = v;
conf.sock4 = conf.sock6 = -1;
conf.protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3;
init_mime(&conf.mime);
TAILQ_FOREACH_SAFE(h, &hosts, vhosts, th) {
free(h->cert);
free(h->key);
free(h->ocsp);
TAILQ_FOREACH_SAFE(l, &h->locations, locations, tl) {
TAILQ_REMOVE(&h->locations, l, locations);
@ -82,3 +112,378 @@ config_free(void)
memset(fcgi, 0, sizeof(fcgi));
}
static int
config_send_file(struct privsep *ps, int fd, int type)
{
int n, m, id, d;
id = PROC_SERVER;
n = -1;
proc_range(ps, id, &n, &m);
for (n = 0; n < m; ++n) {
if ((d = dup(fd)) == -1)
fatal("dup");
if (proc_compose_imsg(ps, id, n, type, -1, d, NULL, 0)
== -1)
return -1;
}
close(fd);
return 0;
}
static int
config_send_socks(struct conf *conf)
{
struct privsep *ps = conf->ps;
int sock;
if ((sock = make_socket(conf->port, AF_INET)) == -1)
return -1;
if (config_send_file(ps, sock, IMSG_RECONF_SOCK4) == -1)
return -1;
if (!conf->ipv6)
return 0;
if ((sock = make_socket(conf->port, AF_INET6)) == -1)
return -1;
if (config_send_file(ps, sock, IMSG_RECONF_SOCK6) == -1)
return -1;
return 0;
}
int
config_send(struct conf *conf, struct fcgi *fcgi, struct vhosthead *hosts)
{
struct privsep *ps = conf->ps;
struct etm *m;
struct vhost *h;
struct location *l;
struct proxy *p;
struct envlist *e;
struct alist *a;
size_t i;
int fd;
for (i = 0; i < conf->mime.len; ++i) {
m = &conf->mime.t[i];
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_MIME,
m, sizeof(*m)) == -1)
return -1;
}
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_PROTOS,
&conf->protos, sizeof(conf->protos)) == -1)
return -1;
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_PORT,
&conf->port, sizeof(conf->port)) == -1)
return -1;
if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
return -1;
if (config_send_socks(conf) == -1)
return -1;
if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
return -1;
for (i = 0; i < FCGI_MAX; ++i) {
if (*fcgi[i].path == '\0')
break;
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_FCGI,
&fcgi[i], sizeof(fcgi[i])) == -1)
return -1;
}
TAILQ_FOREACH(h, hosts, vhosts) {
log_debug("sending host %s", h->domain);
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_HOST,
h, sizeof(*h)) == -1)
return -1;
log_debug("sending certificate %s", h->cert_path);
if ((fd = open(h->cert_path, O_RDONLY)) == -1)
fatal("can't open %s", h->cert_path);
if (config_send_file(ps, fd, IMSG_RECONF_CERT) == -1)
return -1;
log_debug("sending key %s", h->key_path);
if ((fd = open(h->key_path, O_RDONLY)) == -1)
fatal("can't open %s", h->key_path);
if (config_send_file(ps, fd, IMSG_RECONF_KEY) == -1)
return -1;
if (*h->ocsp_path != '\0') {
log_debug("sending ocsp %s", h->ocsp_path);
if ((fd = open(h->ocsp_path, O_RDONLY)) == -1)
fatal("can't open %s", h->ocsp_path);
if (config_send_file(ps, fd, IMSG_RECONF_OCSP) == -1)
return -1;
}
TAILQ_FOREACH(l, &h->locations, locations) {
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_LOC,
l, sizeof(*l)) == -1)
return -1;
}
if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
return -1;
TAILQ_FOREACH(e, &h->params, envs) {
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_ENV,
e, sizeof(*e)) == -1)
return -1;
}
if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
return -1;
TAILQ_FOREACH(a, &h->aliases, aliases) {
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_ALIAS,
a, sizeof(*a)) == -1)
return -1;
}
if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
return -1;
TAILQ_FOREACH(p, &h->proxies, proxies) {
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_PROXY,
p, sizeof(*p)) == -1)
return -1;
}
if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
return -1;
}
return 0;
}
static int
load_file(int fd, uint8_t **data, size_t *len)
{
struct stat sb;
FILE *fp;
size_t r;
if (fstat(fd, &sb) == -1)
fatal("fstat");
if ((fp = fdopen(fd, "r")) == NULL)
fatal("fdopen");
if (sb.st_size < 0 /* || sb.st_size > SIZE_MAX */) {
log_warnx("file too large");
fclose(fp);
return -1;
}
*len = sb.st_size;
if ((*data = malloc(*len)) == NULL)
fatal("malloc");
r = fread(*data, 1, *len, fp);
if (r != *len) {
log_warn("read");
fclose(fp);
free(*data);
return -1;
}
fclose(fp);
return 0;
}
int
config_recv(struct conf *conf, struct imsg *imsg)
{
static struct vhost *h;
struct privsep *ps = conf->ps;
struct etm m;
struct fcgi *f;
struct vhost *vh, vht;
struct location *loc;
struct envlist *env;
struct alist *alias;
struct proxy *proxy;
size_t i, datalen;
datalen = IMSG_DATA_SIZE(imsg);
switch (imsg->hdr.type) {
case IMSG_RECONF_START:
config_free();
h = NULL;
break;
case IMSG_RECONF_MIME:
IMSG_SIZE_CHECK(imsg, &m);
memcpy(&m, imsg->data, datalen);
if (m.mime[sizeof(m.mime) - 1] != '\0' ||
m.ext[sizeof(m.ext) - 1] != '\0')
fatal("received corrupted IMSG_RECONF_MIME");
if (add_mime(&conf->mime, m.mime, m.ext) == -1)
fatal("failed to add mime mapping %s -> %s",
m.mime, m.ext);
break;
case IMSG_RECONF_PROTOS:
IMSG_SIZE_CHECK(imsg, &conf->protos);
memcpy(&conf->protos, imsg->data, datalen);
break;
case IMSG_RECONF_PORT:
IMSG_SIZE_CHECK(imsg, &conf->port);
memcpy(&conf->port, imsg->data, datalen);
break;
case IMSG_RECONF_SOCK4:
if (conf->sock4 != -1)
fatalx("socket ipv4 already recv'd");
if (imsg->fd == -1)
fatalx("missing socket for IMSG_RECONF_SOCK4");
conf->sock4 = imsg->fd;
event_set(&conf->evsock4, conf->sock4, EV_READ|EV_PERSIST,
do_accept, NULL);
break;
case IMSG_RECONF_SOCK6:
if (conf->sock6 != -1)
fatalx("socket ipv6 already recv'd");
if (imsg->fd == -1)
fatalx("missing socket for IMSG_RECONF_SOCK6");
conf->sock6 = imsg->fd;
event_set(&conf->evsock6, conf->sock6, EV_READ|EV_PERSIST,
do_accept, NULL);
break;
case IMSG_RECONF_FCGI:
for (i = 0; i < FCGI_MAX; ++i) {
f = &fcgi[i];
if (*f->path != '\0')
continue;
IMSG_SIZE_CHECK(imsg, f);
memcpy(f, imsg->data, datalen);
break;
}
if (i == FCGI_MAX)
fatalx("recv too many fcgi");
break;
case IMSG_RECONF_HOST:
IMSG_SIZE_CHECK(imsg, &vht);
memcpy(&vht, imsg->data, datalen);
vh = new_vhost();
strlcpy(vh->domain, vht.domain, sizeof(vh->domain));
h = vh;
TAILQ_INSERT_TAIL(&hosts, h, vhosts);
break;
case IMSG_RECONF_CERT:
log_debug("receiving cert");
if (h == NULL)
fatalx("recv'd cert without host");
if (h->cert != NULL)
fatalx("cert already received");
if (imsg->fd == -1)
fatalx("no fd for IMSG_RECONF_CERT");
if (load_file(imsg->fd, &h->cert, &h->certlen) == -1)
fatalx("failed to load cert for %s",
h->domain);
break;
case IMSG_RECONF_KEY:
log_debug("receiving key");
if (h == NULL)
fatalx("recv'd key without host");
if (h->key != NULL)
fatalx("key already received");
if (imsg->fd == -1)
fatalx("no fd for IMSG_RECONF_KEY");
if (load_file(imsg->fd, &h->key, &h->keylen) == -1)
fatalx("failed to load key for %s",
h->domain);
break;
case IMSG_RECONF_OCSP:
log_debug("receiving ocsp");
if (h == NULL)
fatalx("recv'd ocsp without host");
if (h->ocsp != NULL)
fatalx("ocsp already received");
if (imsg->fd == -1)
fatalx("no fd for IMSG_RECONF_OCSP");
if (load_file(imsg->fd, &h->ocsp, &h->ocsplen) == -1)
fatalx("failed to load ocsp for %s",
h->domain);
break;
case IMSG_RECONF_LOC:
if (h == NULL)
fatalx("recv'd location without host");
IMSG_SIZE_CHECK(imsg, loc);
//loc = new_location();
loc = xcalloc(1, sizeof(*loc));
loc->dirfd = -1;
loc->fcgi = -1;
memcpy(loc, imsg->data, datalen);
loc->dirfd = -1; /* XXX */
loc->reqca = NULL; /* XXX */
TAILQ_INSERT_TAIL(&h->locations, loc, locations);
break;
case IMSG_RECONF_ENV:
if (h == NULL)
fatalx("recv'd env without host");
IMSG_SIZE_CHECK(imsg, env);
env = xcalloc(1, sizeof(*env));
memcpy(env, imsg->data, datalen);
TAILQ_INSERT_TAIL(&h->params, env, envs);
break;
case IMSG_RECONF_ALIAS:
if (h == NULL)
fatalx("recv'd alias without host");
IMSG_SIZE_CHECK(imsg, alias);
alias = xcalloc(1, sizeof(*alias));
memcpy(alias, imsg->data, datalen);
TAILQ_INSERT_TAIL(&h->aliases, alias, aliases);
break;
case IMSG_RECONF_PROXY:
log_debug("receiving proxy");
if (h == NULL)
fatalx("recv'd proxy without host");
IMSG_SIZE_CHECK(imsg, proxy);
proxy = xcalloc(1, sizeof(*proxy));
memcpy(proxy, imsg->data, datalen);
proxy->reqca = NULL; /* XXX */
proxy->cert = proxy->key = NULL; /* XXX */
proxy->certlen = proxy->keylen = 0; /* XXX */
TAILQ_INSERT_TAIL(&h->proxies, proxy, proxies);
break;
case IMSG_RECONF_END:
if (proc_compose(ps, PROC_PARENT, IMSG_RECONF_DONE,
NULL, 0) == -1)
return -1;
break;
default:
return -1;
}
return 0;
}

1
configure vendored
View File

@ -43,6 +43,7 @@ fi
CFLAGS="${CFLAGS} -W -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes"
CFLAGS="${CFLAGS} -Wwrite-strings -Wno-unused-parameter"
CFLAGS="${CFLAGS} -Wno-missing-field-initializers"
if [ -z "${LDFLAGS}" ]; then
LDFLAGS=`printf "all:\\n\\t@echo \\\$(LDFLAGS)\\n" | make ${MAKE_FLAGS} -sf -`

32
ge.c
View File

@ -32,7 +32,7 @@
#include "logger.h"
#include "log.h"
struct imsgbuf ibuf, logibuf;
struct imsgbuf ibuf;
struct conf conf;
struct fcgi fcgi[FCGI_MAX]; /* just because it's referenced */
@ -44,12 +44,6 @@ static const struct option opts[] = {
{NULL, 0, NULL, 0},
};
void
drop_priv(void)
{
return;
}
void
load_local_cert(struct vhost *h, const char *hostname, const char *dir)
{
@ -114,29 +108,6 @@ data_dir(void)
return t;
}
static void
logger_init(void)
{
int p[2];
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1)
fatal("socketpair");
switch (fork()) {
case -1:
fatal("fork");
case 0:
close(p[0]);
setproctitle("logger");
imsg_init(&logibuf, p[1]);
_exit(logger_main(p[1], &logibuf));
default:
close(p[1]);
imsg_init(&logibuf, p[0]);
return;
}
}
static int
serve(const char *host, int port, const char *dir)
{
@ -213,7 +184,6 @@ main(int argc, char **argv)
log_init(1, LOG_DAEMON);
log_setverbose(0);
logger_init();
config_init();
while ((ch = getopt_long(argc, argv, "d:H:hp:Vv", opts, NULL)) != -1) {

384
gmid.c
View File

@ -32,8 +32,26 @@
#include "logger.h"
#include "log.h"
#include "proc.h"
static const char *opts = "c:D:fhnP:Vv";
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
static int main_configure(struct conf *);
static void main_configure_done(struct conf *);
static void main_reload(struct conf *);
static void main_sig_handler(int, short, void *);
static int main_dispatch_server(int, struct privsep_proc *, struct imsg *);
static int main_dispatch_logger(int, struct privsep_proc *, struct imsg *);
static void __dead main_shutdown(struct conf *);
static struct privsep_proc procs[] = {
{ "server", PROC_SERVER, main_dispatch_server, server },
{ "logger", PROC_LOGGER, main_dispatch_logger, logger },
};
static const char *opts = "c:D:fI:hnP:T:Vv";
static const struct option longopts[] = {
{"help", no_argument, NULL, 'h'},
@ -46,20 +64,14 @@ struct fcgi fcgi[FCGI_MAX];
struct vhosthead hosts;
int sock4, sock6;
struct imsgbuf logibuf, servibuf[PREFORK_MAX];
int privsep_process;
int pidfd = -1;
const char *config_path = "/etc/gmid.conf";
const char *pidfile;
struct conf conf;
static void
dummy_handler(int signo)
{
return;
}
int
make_socket(int port, int family)
{
@ -115,50 +127,6 @@ make_socket(int port, int family)
return sock;
}
static int
wait_signal(void)
{
sigset_t mask;
int signo;
sigemptyset(&mask);
sigaddset(&mask, SIGHUP);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigwait(&mask, &signo);
return signo == SIGHUP;
}
void
drop_priv(void)
{
struct passwd *pw = NULL;
if (*conf.chroot != '\0' && *conf.user == '\0')
fatalx("can't chroot without an user to switch to after.");
if (*conf.user != '\0') {
if ((pw = getpwnam(conf.user)) == NULL)
fatalx("can't find user %s", conf.user);
}
if (*conf.chroot != '\0') {
if (chroot(conf.chroot) != 0 || chdir("/") != 0)
fatal("%s", conf.chroot);
}
if (pw != NULL) {
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)
fatal("cannot drop privileges");
}
if (getuid() == 0)
log_warnx("not a good idea to run a network daemon as root");
}
static void
usage(void)
{
@ -168,56 +136,6 @@ usage(void)
getprogname());
}
static void
logger_init(void)
{
int p[2];
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1)
err(1, "socketpair");
switch (fork()) {
case -1:
err(1, "fork");
case 0:
signal(SIGHUP, SIG_IGN);
close(p[0]);
setproctitle("logger");
imsg_init(&logibuf, p[1]);
drop_priv();
_exit(logger_main(p[1], &logibuf));
default:
close(p[1]);
imsg_init(&logibuf, p[0]);
return;
}
}
static void
serve(void)
{
int i, p[2];
for (i = 0; i < conf.prefork; ++i) {
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC,
PF_UNSPEC, p) == -1)
fatal("socketpair");
switch (fork()) {
case -1:
fatal("fork");
case 0: /* child */
close(p[0]);
imsg_init(&servibuf[i], p[1]);
setproctitle("server");
_exit(server_main(&servibuf[i], sock4, sock6));
default:
close(p[1]);
imsg_init(&servibuf[i], p[0]);
}
}
}
static int
write_pidfile(const char *pidfile)
{
@ -249,14 +167,18 @@ write_pidfile(const char *pidfile)
int
main(int argc, char **argv)
{
int i, ch, conftest = 0;
int pidfd, old_ipv6, old_port;
struct privsep *ps;
const char *errstr, *title = NULL;
size_t i;
int ch, conftest = 0;
int proc_instance = 0;
int proc_id = PROC_PARENT;
int argc0 = argc;
setlocale(LC_CTYPE, "");
/* log to stderr until daemonized */
log_init(1, LOG_DAEMON);
logger_init();
config_init();
while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) {
@ -275,12 +197,24 @@ main(int argc, char **argv)
case 'h':
usage();
return 0;
case 'I':
proc_instance = strtonum(optarg, 0, PROC_MAX_INSTANCES,
&errstr);
if (errstr != NULL)
fatalx("invalid process instance");
break;
case 'n':
conftest++;
break;
case 'P':
pidfile = absolutify_path(optarg);
break;
case 'T':
title = optarg;
proc_id = proc_getid(procs, nitems(procs), title);
if (proc_id == PROC_MAX)
fatalx("invalid process name");
break;
case 'V':
puts("Version: " GMID_STRING);
return 0;
@ -292,13 +226,13 @@ main(int argc, char **argv)
return 1;
}
}
argc -= optind;
argv += optind;
if (argc != 0)
if (argc - optind != 0)
usage();
parse_conf(config_path);
if (*conf.chroot != '\0' && *conf.user == '\0')
fatalx("can't chroot without a user to switch to after.");
if (conftest) {
fprintf(stderr, "config OK\n");
@ -307,89 +241,191 @@ main(int argc, char **argv)
return 0;
}
if (!conf.foreground) {
/* log to syslog */
imsg_compose(&logibuf, IMSG_LOG_TYPE, 0, 0, -1, NULL, 0);
imsg_flush(&logibuf);
log_init(0, LOG_DAEMON);
if (daemon(1, 1) == -1)
fatal("daemon");
if ((ps = calloc(1, sizeof(*ps))) == NULL)
fatal("calloc");
ps->ps_env = &conf;
conf.ps = ps;
if (*conf.user) {
if (geteuid())
fatalx("need root privileges");
if ((ps->ps_pw = getpwnam(conf.user)) == NULL)
fatalx("unknown user %s", conf.user);
}
ps->ps_instances[PROC_SERVER] = conf.prefork;
ps->ps_instance = proc_instance;
if (title != NULL)
ps->ps_title[proc_id] = title;
if (*conf.chroot != '\0') {
for (i = 0; i < nitems(procs); ++i)
procs[i].p_chroot = conf.chroot;
}
log_init(conf.foreground, LOG_DAEMON);
log_setverbose(conf.verbose);
if (title != NULL)
log_procinit(title);
sock4 = make_socket(conf.port, AF_INET);
sock6 = -1;
if (conf.ipv6)
sock6 = make_socket(conf.port, AF_INET6);
/* only the parent returns */
proc_init(ps, procs, nitems(procs), conf.foreground,
argc0, argv, proc_id);
signal(SIGPIPE, SIG_IGN);
log_procinit("main");
if (!conf.foreground && daemon(0, 0) == -1)
fatal("daemon");
pidfd = write_pidfile(pidfile);
sandbox_main_process();
event_init();
signal(SIGPIPE, SIG_IGN);
signal_set(&ps->ps_evsigint, SIGINT, main_sig_handler, ps);
signal_set(&ps->ps_evsigterm, SIGTERM, main_sig_handler, ps);
signal_set(&ps->ps_evsigchld, SIGCHLD, main_sig_handler, ps);
signal_set(&ps->ps_evsighup, SIGHUP, main_sig_handler, ps);
signal_add(&ps->ps_evsigint, NULL);
signal_add(&ps->ps_evsigterm, NULL);
signal_add(&ps->ps_evsigchld, NULL);
signal_add(&ps->ps_evsighup, NULL);
proc_connect(ps);
if (main_configure(&conf) == -1)
fatal("configuration failed");
event_dispatch();
main_shutdown(&conf);
/* NOTREACHED */
return 0;
}
static int
main_configure(struct conf *conf)
{
struct privsep *ps = conf->ps;
conf->reload = conf->prefork;
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_START, NULL, 0) == -1)
return -1;
if (config_send(conf, fcgi, &hosts) == -1)
return -1;
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_END, NULL, 0) == -1)
return -1;
return 0;
}
static void
main_configure_done(struct conf *conf)
{
if (conf->reload == 0) {
log_warnx("configuration already done");
return;
}
conf->reload--;
/* send IMSG_CTL_START? */
}
static void
main_reload(struct conf *conf)
{
if (conf->reload) {
log_debug("%s: already in progress: %d pending",
__func__, conf->reload);
return;
}
log_debug("%s: config file %s", __func__, config_path);
config_free();
parse_conf(config_path); /* XXX should handle error here */
main_configure(conf);
}
static void
main_sig_handler(int sig, short ev, void *arg)
{
struct privsep *ps = arg;
/*
* Linux seems to call the event handlers even when we're
* doing a sigwait. These dummy handlers are here to avoid
* being terminated on SIGHUP, SIGINT or SIGTERM.
* Normal signal handler rules don't apply here because libevent
* decouples for us.
*/
signal(SIGHUP, dummy_handler);
signal(SIGINT, dummy_handler);
signal(SIGTERM, dummy_handler);
/* wait a sighup and reload the daemon */
for (;;) {
serve();
switch (sig) {
case SIGHUP:
if (privsep_process != PROC_PARENT)
return;
log_info("reload requested with SIGHUP");
main_reload(ps->ps_env);
break;
case SIGCHLD:
log_warnx("one child died, quitting");
/* fallthrough */
case SIGTERM:
case SIGINT:
main_shutdown(ps->ps_env);
break;
default:
fatalx("unexpected signal %d", sig);
}
}
if (!wait_signal())
break;
static int
main_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct privsep *ps = p->p_ps;
struct conf *conf = ps->ps_env;
log_info("reloading configuration %s", config_path);
/* close the servers */
for (i = 0; i < conf.prefork; ++i) {
imsg_compose(&servibuf[i], IMSG_QUIT, 0, 0, -1,
NULL, 0);
imsg_flush(&servibuf[i]);
close(servibuf[i].fd);
}
old_ipv6 = conf.ipv6;
old_port = conf.port;
config_free();
config_init();
parse_conf(config_path);
if (old_port != conf.port) {
close(sock4);
close(sock6);
sock4 = -1;
sock6 = -1;
}
if (sock6 != -1 && old_ipv6 != conf.ipv6) {
close(sock6);
sock6 = -1;
}
if (sock4 == -1)
sock4 = make_socket(conf.port, AF_INET);
if (sock6 == -1 && conf.ipv6)
sock6 = make_socket(conf.port, AF_INET6);
switch (imsg->hdr.type) {
case IMSG_RECONF_DONE:
main_configure_done(conf);
break;
default:
return -1;
}
for (i = 0; i < conf.prefork; ++i) {
imsg_compose(&servibuf[i], IMSG_QUIT, 0, 0, -1, NULL, 0);
imsg_flush(&servibuf[i]);
close(servibuf[i].fd);
return 0;
}
static int
main_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct privsep *ps = p->p_ps;
struct conf *conf = ps->ps_env;
switch (imsg->hdr.type) {
case IMSG_RECONF_DONE:
main_configure_done(conf);
break;
default:
return -1;
}
imsg_compose(&logibuf, IMSG_QUIT, 0, 0, -1, NULL, 0);
imsg_flush(&logibuf);
close(logibuf.fd);
return 0;
}
static void __dead
main_shutdown(struct conf *conf)
{
proc_kill(conf->ps);
config_free();
free(conf->ps);
/* free(conf); */
log_info("parent terminating, pid %d", getpid());
if (pidfd != -1)
close(pidfd);
return 0;
exit(0);
}

67
gmid.h
View File

@ -81,7 +81,11 @@
#define FCGI_VAL_MAX 511
#define FCGI_MAX 32
#define PREFORK_MAX 16
#define PROC_MAX_INSTANCES 16
/* forward declaration */
struct privsep;
struct privsep_proc;
struct iri {
char *schema;
@ -163,9 +167,18 @@ struct alist {
extern TAILQ_HEAD(vhosthead, vhost) hosts;
struct vhost {
char domain[HOST_NAME_MAX + 1];
char cert[PATH_MAX];
char key[PATH_MAX];
char ocsp[PATH_MAX];
char cert_path[PATH_MAX];
char key_path[PATH_MAX];
char ocsp_path[PATH_MAX];
uint8_t *cert;
size_t certlen;
uint8_t *key;
size_t keylen;
uint8_t *ocsp;
size_t ocsplen;
TAILQ_ENTRY(vhost) vhosts;
@ -193,11 +206,9 @@ struct mime {
};
struct conf {
/* from command line */
struct privsep *ps;
int foreground;
int verbose;
/* in the config */
int port;
int ipv6;
uint32_t protos;
@ -205,14 +216,19 @@ struct conf {
char chroot[PATH_MAX];
char user[LOGIN_NAME_MAX];
int prefork;
int reload;
int sock4;
struct event evsock4;
int sock6;
struct event evsock6;
};
extern const char *config_path;
extern struct conf conf;
extern struct imsgbuf logibuf, servibuf[PREFORK_MAX];
extern int servpipes[PREFORK_MAX];
extern int servpipes[PROC_MAX_INSTANCES];
extern int privsep_process;
typedef void (imsg_handlerfn)(struct imsgbuf*, struct imsg*, size_t);
@ -286,24 +302,47 @@ enum imsg_type {
IMSG_LOG,
IMSG_LOG_REQUEST,
IMSG_LOG_TYPE,
IMSG_QUIT,
IMSG_RECONF_START, /* 7 */
IMSG_RECONF_MIME,
IMSG_RECONF_PROTOS,
IMSG_RECONF_PORT,
IMSG_RECONF_SOCK4,
IMSG_RECONF_SOCK6,
IMSG_RECONF_FCGI,
IMSG_RECONF_HOST,
IMSG_RECONF_CERT,
IMSG_RECONF_KEY,
IMSG_RECONF_OCSP,
IMSG_RECONF_LOC,
IMSG_RECONF_ENV,
IMSG_RECONF_ALIAS,
IMSG_RECONF_PROXY,
IMSG_RECONF_END,
IMSG_RECONF_DONE,
IMSG_CTL_PROCFD,
};
/* gmid.c */
char *data_dir(void);
void load_local_cert(struct vhost*, const char*, const char*);
int make_socket(int, int);
void drop_priv(void);
/* config.c */
void config_init(void);
void config_free(void);
int config_send(struct conf *, struct fcgi *, struct vhosthead *);
int config_recv(struct conf *, struct imsg *);
/* parse.y */
void yyerror(const char*, ...);
void parse_conf(const char*);
void print_conf(void);
int cmdline_symset(char *);
struct vhost *new_vhost(void);
struct location *new_location(void);
struct proxy *new_proxy(void);
/* mime.c */
void init_mime(struct mime*);
@ -331,7 +370,8 @@ void client_write(struct bufferevent *, void *);
void start_reply(struct client*, int, const char*);
void client_close(struct client *);
struct client *client_by_id(int);
int server_main(struct imsgbuf *, int, int);
void do_accept(int, short, void *);
void server(struct privsep *ps, struct privsep_proc *);
int client_tree_cmp(struct client *, struct client *);
SPLAY_PROTOTYPE(client_tree_id, client, entry, client_tree_cmp);
@ -349,6 +389,7 @@ void fcgi_error(struct bufferevent *, short, void *);
void fcgi_req(struct client *);
/* sandbox.c */
void sandbox_main_process(void);
void sandbox_server_process(void);
void sandbox_logger_process(void);

123
logger.c
View File

@ -32,21 +32,22 @@
#include "logger.h"
#include "log.h"
#include "proc.h"
static struct event imsgev;
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
static FILE *log;
static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t);
static void handle_imsg_log(struct imsgbuf*, struct imsg*, size_t);
static void handle_imsg_log_type(struct imsgbuf*, struct imsg*, size_t);
static void handle_dispatch_imsg(int, short, void*);
static void logger_init(struct privsep *, struct privsep_proc *, void *);
static void logger_shutdown(void);
static int logger_dispatch_parent(int, struct privsep_proc *, struct imsg *);
static int logger_dispatch_server(int, struct privsep_proc *, struct imsg *);
static imsg_handlerfn *handlers[] = {
[IMSG_QUIT] = handle_imsg_quit,
[IMSG_LOG] = handle_imsg_log,
[IMSG_LOG_REQUEST] = handle_imsg_log,
[IMSG_LOG_TYPE] = handle_imsg_log_type,
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT, logger_dispatch_parent },
{ "server", PROC_SERVER, logger_dispatch_server },
};
void
@ -99,74 +100,82 @@ log_request(struct client *c, char *meta, size_t l)
if (ec == -1)
err(1, "asprintf");
imsg_compose(&logibuf, IMSG_LOG_REQUEST, 0, 0, -1, fmted, ec + 1);
imsg_flush(&logibuf);
proc_compose(conf.ps, PROC_LOGGER, IMSG_LOG_REQUEST,
fmted, ec + 1);
free(fmted);
}
static void
handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
void
logger(struct privsep *ps, struct privsep_proc *p)
{
event_loopbreak();
proc_run(ps, p, procs, nitems(procs), logger_init, NULL);
}
static void
handle_imsg_log(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
logger_init(struct privsep *ps, struct privsep_proc *p, void *arg)
{
char *msg;
msg = imsg->data;
msg[datalen-1] = '\0';
if (log != NULL)
fprintf(log, "%s\n", msg);
else
syslog(LOG_DAEMON | LOG_NOTICE, "%s", msg);
p->p_shutdown = logger_shutdown;
log = stderr;
sandbox_logger_process();
}
static void
handle_imsg_log_type(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
logger_shutdown(void)
{
if (log != NULL && log != stderr) {
closelog();
if (log && log != stderr) {
fflush(log);
fclose(log);
}
log = NULL;
}
if (imsg->fd != -1) {
if ((log = fdopen(imsg->fd, "a")) == NULL) {
syslog(LOG_DAEMON | LOG_ERR, "fdopen: %s",
strerror(errno));
exit(1);
static int
logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{
switch (imsg->hdr.type) {
case IMSG_LOG_TYPE:
if (log != NULL && log != stderr) {
fflush(log);
fclose(log);
}
log = NULL;
if (imsg->fd != -1) {
if ((log = fdopen(imsg->fd, "a")) == NULL)
fatal("fdopen");
}
break;
default:
return -1;
}
return 0;
}
static int
logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
{
char *msg;
size_t datalen;
switch (imsg->hdr.type) {
case IMSG_LOG_REQUEST:
msg = imsg->data;
datalen = IMSG_DATA_SIZE(imsg);
if (datalen == 0)
fatal("got invalid IMSG_LOG_REQUEST");
msg[datalen - 1] = '\0';
if (log != NULL)
fprintf(log, "%s\n", msg);
else
syslog(LOG_DAEMON | LOG_NOTICE, "%s", msg);
break;
default:
return -1;
}
}
static void
handle_dispatch_imsg(int fd, short ev, void *d)
{
struct imsgbuf *ibuf = d;
dispatch_imsg(ibuf, handlers, sizeof(handlers));
}
int
logger_main(int fd, struct imsgbuf *ibuf)
{
log = stderr;
event_init();
event_set(&imsgev, fd, EV_READ | EV_PERSIST, &handle_dispatch_imsg, ibuf);
event_add(&imsgev, NULL);
sandbox_logger_process();
event_dispatch();
closelog();
return 0;
}

View File

@ -15,4 +15,4 @@
*/
void log_request(struct client *, char *, size_t);
int logger_main(int, struct imsgbuf *);
void logger(struct privsep *, struct privsep_proc *);

14
parse.y
View File

@ -254,7 +254,8 @@ vhost : SERVER string {
free($2);
} '{' optnl servbody '}' {
if (*host->cert == '\0' || *host->key == '\0')
if (*host->cert_path == '\0' ||
*host->key_path == '\0')
yyerror("invalid vhost definition: %s", $2);
}
| error '}' { yyerror("bad server directive"); }
@ -276,17 +277,20 @@ servopt : ALIAS string {
}
| CERT string {
ensure_absolute_path($2);
(void) strlcpy(host->cert, $2, sizeof(host->cert));
(void) strlcpy(host->cert_path, $2,
sizeof(host->cert_path));
free($2);
}
| KEY string {
ensure_absolute_path($2);
(void) strlcpy(host->key, $2, sizeof(host->key));
(void) strlcpy(host->key_path, $2,
sizeof(host->key_path));
free($2);
}
| OCSP string {
ensure_absolute_path($2);
(void) strlcpy(host->ocsp, $2, sizeof(host->ocsp));
(void) strlcpy(host->ocsp_path, $2,
sizeof(host->ocsp_path));
free($2);
}
| PARAM string '=' string {
@ -1125,7 +1129,7 @@ check_port_num(int n)
int
check_prefork_num(int n)
{
if (n <= 0 || n >= PREFORK_MAX)
if (n <= 0 || n >= PROC_MAX_INSTANCES)
yyerror("invalid prefork number %d", n);
return n;
}

838
proc.c Normal file
View File

@ -0,0 +1,838 @@
/* $OpenBSD: proc.c,v 1.41 2021/12/04 06:52:58 florian Exp $ */
/*
* Copyright (c) 2010 - 2016 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@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
* 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 <sys/types.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <grp.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <paths.h>
#include <pwd.h>
#include <event.h>
#include <imsg.h>
#include "gmid.h"
#include "log.h"
#include "proc.h"
void proc_exec(struct privsep *, struct privsep_proc *, unsigned int, int,
int, char **);
void proc_setup(struct privsep *, struct privsep_proc *, unsigned int);
void proc_open(struct privsep *, int, int);
void proc_accept(struct privsep *, int, enum privsep_procid,
unsigned int);
void proc_close(struct privsep *);
int proc_ispeer(struct privsep_proc *, unsigned int, enum privsep_procid);
void proc_shutdown(struct privsep_proc *);
void proc_sig_handler(int, short, void *);
void proc_range(struct privsep *, enum privsep_procid, int *, int *);
int proc_dispatch_null(int, struct privsep_proc *, struct imsg *);
int
proc_ispeer(struct privsep_proc *procs, unsigned int nproc,
enum privsep_procid type)
{
unsigned int i;
for (i = 0; i < nproc; i++)
if (procs[i].p_id == type)
return (1);
return (0);
}
enum privsep_procid
proc_getid(struct privsep_proc *procs, unsigned int nproc,
const char *proc_name)
{
struct privsep_proc *p;
unsigned int proc;
for (proc = 0; proc < nproc; proc++) {
p = &procs[proc];
if (strcmp(p->p_title, proc_name))
continue;
return (p->p_id);
}
return (PROC_MAX);
}
void
proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
int debug, int argc, char **argv)
{
unsigned int proc, nargc, i, proc_i;
const char **nargv;
struct privsep_proc *p;
char num[32];
int fd;
/* Prepare the new process argv. */
nargv = calloc(argc + 5, sizeof(char *));
if (nargv == NULL)
fatal("%s: calloc", __func__);
/* Copy call argument first. */
nargc = 0;
nargv[nargc++] = argv[0];
/* Set process name argument and save the position. */
nargv[nargc++] = "-T";
proc_i = nargc;
nargc++;
/* Point process instance arg to stack and copy the original args. */
nargv[nargc++] = "-I";
nargv[nargc++] = num;
for (i = 1; i < (unsigned int) argc; i++)
nargv[nargc++] = argv[i];
nargv[nargc] = NULL;
for (proc = 0; proc < nproc; proc++) {
p = &procs[proc];
/* Update args with process title. */
nargv[proc_i] = (char *)(uintptr_t)p->p_title;
/* Fire children processes. */
for (i = 0; i < ps->ps_instances[p->p_id]; i++) {
/* Update the process instance number. */
snprintf(num, sizeof(num), "%u", i);
fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0];
ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1;
switch (fork()) {
case -1:
fatal("%s: fork", __func__);
break;
case 0:
/* First create a new session */
if (setsid() == -1)
fatal("setsid");
/* Prepare parent socket. */
if (fd != PROC_PARENT_SOCK_FILENO) {
if (dup2(fd, PROC_PARENT_SOCK_FILENO)
== -1)
fatal("dup2");
} else if (fcntl(fd, F_SETFD, 0) == -1)
fatal("fcntl");
/* Daemons detach from terminal. */
if (!debug && (fd =
open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
(void)dup2(fd, STDIN_FILENO);
(void)dup2(fd, STDOUT_FILENO);
(void)dup2(fd, STDERR_FILENO);
if (fd > 2)
(void)close(fd);
}
/* obnoxious casts */
execvp(argv[0], (char *const *)nargv);
fatal("%s: execvp", __func__);
break;
default:
/* Close child end. */
close(fd);
break;
}
}
}
free(nargv);
}
void
proc_connect(struct privsep *ps)
{
struct imsgev *iev;
unsigned int src, dst, inst;
/* Don't distribute any sockets if we are not really going to run. */
if (ps->ps_noaction)
return;
for (dst = 0; dst < PROC_MAX; dst++) {
/* We don't communicate with ourselves. */
if (dst == PROC_PARENT)
continue;
for (inst = 0; inst < ps->ps_instances[dst]; inst++) {
iev = &ps->ps_ievs[dst][inst];
imsg_init(&iev->ibuf, ps->ps_pp->pp_pipes[dst][inst]);
event_set(&iev->ev, iev->ibuf.fd, iev->events,
iev->handler, iev->data);
event_add(&iev->ev, NULL);
}
}
/* Distribute the socketpair()s for everyone. */
for (src = 0; src < PROC_MAX; src++)
for (dst = src; dst < PROC_MAX; dst++) {
/* Parent already distributed its fds. */
if (src == PROC_PARENT || dst == PROC_PARENT)
continue;
proc_open(ps, src, dst);
}
}
void
proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
int debug, int argc, char **argv, enum privsep_procid proc_id)
{
struct privsep_proc *p = NULL;
struct privsep_pipes *pa, *pb;
unsigned int proc;
unsigned int dst;
int fds[2];
/* Don't initiate anything if we are not really going to run. */
if (ps->ps_noaction)
return;
if (proc_id == PROC_PARENT) {
privsep_process = PROC_PARENT;
proc_setup(ps, procs, nproc);
/*
* Create the children sockets so we can use them
* to distribute the rest of the socketpair()s using
* proc_connect() later.
*/
for (dst = 0; dst < PROC_MAX; dst++) {
/* Don't create socket for ourselves. */
if (dst == PROC_PARENT)
continue;
for (proc = 0; proc < ps->ps_instances[dst]; proc++) {
pa = &ps->ps_pipes[PROC_PARENT][0];
pb = &ps->ps_pipes[dst][proc];
if (socketpair(AF_UNIX,
SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
PF_UNSPEC, fds) == -1)
fatal("%s: socketpair", __func__);
pa->pp_pipes[dst][proc] = fds[0];
pb->pp_pipes[PROC_PARENT][0] = fds[1];
}
}
/* Engage! */
proc_exec(ps, procs, nproc, debug, argc, argv);
return;
}
/* Initialize a child */
for (proc = 0; proc < nproc; proc++) {
if (procs[proc].p_id != proc_id)
continue;
p = &procs[proc];
break;
}
if (p == NULL || p->p_init == NULL)
fatalx("%s: process %d missing process initialization",
__func__, proc_id);
p->p_init(ps, p);
fatalx("failed to initiate child process");
}
void
proc_accept(struct privsep *ps, int fd, enum privsep_procid dst,
unsigned int n)
{
struct privsep_pipes *pp = ps->ps_pp;
struct imsgev *iev;
if (ps->ps_ievs[dst] == NULL) {
#if DEBUG > 1
log_debug("%s: %s src %d %d to dst %d %d not connected",
__func__, ps->ps_title[privsep_process],
privsep_process, ps->ps_instance + 1,
dst, n + 1);
#endif
close(fd);
return;
}
if (pp->pp_pipes[dst][n] != -1) {
log_warnx("%s: duplicated descriptor", __func__);
close(fd);
return;
} else
pp->pp_pipes[dst][n] = fd;
iev = &ps->ps_ievs[dst][n];
imsg_init(&iev->ibuf, fd);
event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
event_add(&iev->ev, NULL);
}
void
proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
{
unsigned int i, j, src, dst, id;
struct privsep_pipes *pp;
/* Initialize parent title, ps_instances and procs. */
ps->ps_title[PROC_PARENT] = "parent";
for (src = 0; src < PROC_MAX; src++)
/* Default to 1 process instance */
if (ps->ps_instances[src] < 1)
ps->ps_instances[src] = 1;
for (src = 0; src < nproc; src++) {
procs[src].p_ps = ps;
if (procs[src].p_cb == NULL)
procs[src].p_cb = proc_dispatch_null;
id = procs[src].p_id;
ps->ps_title[id] = procs[src].p_title;
if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id],
sizeof(struct imsgev))) == NULL)
fatal("%s: calloc", __func__);
/* With this set up, we are ready to call imsg_init(). */
for (i = 0; i < ps->ps_instances[id]; i++) {
ps->ps_ievs[id][i].handler = proc_dispatch;
ps->ps_ievs[id][i].events = EV_READ;
ps->ps_ievs[id][i].proc = &procs[src];
ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i];
}
}
/*
* Allocate pipes for all process instances (incl. parent)
*
* - ps->ps_pipes: N:M mapping
* N source processes connected to M destination processes:
* [src][instances][dst][instances], for example
* [PROC_RELAY][3][PROC_CA][3]
*
* - ps->ps_pp: per-process 1:M part of ps->ps_pipes
* Each process instance has a destination array of socketpair fds:
* [dst][instances], for example
* [PROC_PARENT][0]
*/
for (src = 0; src < PROC_MAX; src++) {
/* Allocate destination array for each process */
if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src],
sizeof(struct privsep_pipes))) == NULL)
fatal("%s: calloc", __func__);
for (i = 0; i < ps->ps_instances[src]; i++) {
pp = &ps->ps_pipes[src][i];
for (dst = 0; dst < PROC_MAX; dst++) {
/* Allocate maximum fd integers */
if ((pp->pp_pipes[dst] =
calloc(ps->ps_instances[dst],
sizeof(int))) == NULL)
fatal("%s: calloc", __func__);
/* Mark fd as unused */
for (j = 0; j < ps->ps_instances[dst]; j++)
pp->pp_pipes[dst][j] = -1;
}
}
}
ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance];
}
void
proc_kill(struct privsep *ps)
{
char *cause;
pid_t pid;
int len, status;
if (privsep_process != PROC_PARENT)
return;
proc_close(ps);
do {
pid = waitpid(WAIT_ANY, &status, 0);
if (pid <= 0)
continue;
if (WIFSIGNALED(status)) {
len = asprintf(&cause, "terminated; signal %d",
WTERMSIG(status));
} else if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != 0)
len = asprintf(&cause, "exited abnormally");
else
len = 0;
} else
len = -1;
if (len == 0) {
/* child exited OK, don't print a warning message */
} else if (len != -1) {
log_warnx("lost child: pid %u %s", pid, cause);
free(cause);
} else
log_warnx("lost child: pid %u", pid);
} while (pid != -1 || errno == EINTR);
}
void
proc_open(struct privsep *ps, int src, int dst)
{
struct privsep_pipes *pa, *pb;
struct privsep_fd pf;
int fds[2];
unsigned int i, j;
/* Exchange pipes between process. */
for (i = 0; i < ps->ps_instances[src]; i++) {
for (j = 0; j < ps->ps_instances[dst]; j++) {
/* Don't create sockets for ourself. */
if (src == dst && i == j)
continue;
/* Servers don't talk to each other. */
if (src == PROC_SERVER && dst == PROC_SERVER)
continue;
pa = &ps->ps_pipes[src][i];
pb = &ps->ps_pipes[dst][j];
if (socketpair(AF_UNIX,
SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
PF_UNSPEC, fds) == -1)
fatal("%s: socketpair", __func__);
pa->pp_pipes[dst][j] = fds[0];
pb->pp_pipes[src][i] = fds[1];
pf.pf_procid = src;
pf.pf_instance = i;
if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD,
-1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1)
fatal("%s: proc_compose_imsg", __func__);
pf.pf_procid = dst;
pf.pf_instance = j;
if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD,
-1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1)
fatal("%s: proc_compose_imsg", __func__);
/*
* We have to flush to send the descriptors and close
* them to avoid the fd ramp on startup.
*/
if (proc_flush_imsg(ps, src, i) == -1 ||
proc_flush_imsg(ps, dst, j) == -1)
fatal("%s: imsg_flush", __func__);
}
}
}
void
proc_close(struct privsep *ps)
{
unsigned int dst, n;
struct privsep_pipes *pp;
if (ps == NULL)
return;
pp = ps->ps_pp;
for (dst = 0; dst < PROC_MAX; dst++) {
if (ps->ps_ievs[dst] == NULL)
continue;
for (n = 0; n < ps->ps_instances[dst]; n++) {
if (pp->pp_pipes[dst][n] == -1)
continue;
/* Cancel the fd, close and invalidate the fd */
event_del(&(ps->ps_ievs[dst][n].ev));
imsg_clear(&(ps->ps_ievs[dst][n].ibuf));
close(pp->pp_pipes[dst][n]);
pp->pp_pipes[dst][n] = -1;
}
free(ps->ps_ievs[dst]);
}
}
void
proc_shutdown(struct privsep_proc *p)
{
struct privsep *ps = p->p_ps;
if (p->p_shutdown != NULL)
(*p->p_shutdown)();
proc_close(ps);
log_info("%s exiting, pid %d", p->p_title, getpid());
exit(0);
}
void
proc_sig_handler(int sig, short event, void *arg)
{
struct privsep_proc *p = arg;
switch (sig) {
case SIGINT:
case SIGTERM:
proc_shutdown(p);
break;
case SIGCHLD:
case SIGHUP:
/* ignore */
break;
default:
fatalx("%s: unexpected signal", __func__);
/* NOTREACHED */
}
}
void
proc_run(struct privsep *ps, struct privsep_proc *p,
struct privsep_proc *procs, unsigned int nproc,
void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg)
{
struct passwd *pw;
const char *root;
log_procinit(p->p_title);
if (ps->ps_pw == NULL)
goto init;
/* Set the process group of the current process */
setpgid(0, 0);
/* Use non-standard user */
if (p->p_pw != NULL)
pw = p->p_pw;
else
pw = ps->ps_pw;
/* Change root directory */
if (p->p_chroot != NULL)
root = p->p_chroot;
else
root = pw->pw_dir;
if (chroot(root) == -1)
fatal("%s: chroot", __func__);
if (chdir("/") == -1)
fatal("%s: chdir(\"/\")", __func__);
privsep_process = p->p_id;
setproctitle("%s", p->p_title);
if (setgroups(1, &pw->pw_gid) ||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("%s: cannot drop privileges", __func__);
init:
event_init();
signal(SIGPIPE, SIG_IGN);
signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p);
signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
signal_add(&ps->ps_evsigint, NULL);
signal_add(&ps->ps_evsigterm, NULL);
signal_add(&ps->ps_evsigchld, NULL);
signal_add(&ps->ps_evsighup, NULL);
proc_setup(ps, procs, nproc);
proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0);
log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
if (run != NULL)
run(ps, p, arg);
event_dispatch();
proc_shutdown(p);
}
void
proc_dispatch(int fd, short event, void *arg)
{
struct imsgev *iev = arg;
struct privsep_proc *p = iev->proc;
struct privsep *ps = p->p_ps;
struct imsgbuf *ibuf;
struct imsg imsg;
ssize_t n;
const char *title;
struct privsep_fd pf;
title = ps->ps_title[privsep_process];
ibuf = &iev->ibuf;
if (event & EV_READ) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
fatal("%s: imsg_read", __func__);
if (n == 0) {
/* this pipe is dead, so remove the event handler */
event_del(&iev->ev);
event_loopexit(NULL);
return;
}
}
if (event & EV_WRITE) {
if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
fatal("%s: msgbuf_write", __func__);
if (n == 0) {
/* this pipe is dead, so remove the event handler */
event_del(&iev->ev);
event_loopexit(NULL);
return;
}
}
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
fatal("%s: imsg_get", __func__);
if (n == 0)
break;
#if DEBUG > 1
log_debug("%s: %s %d got imsg %d peerid %d from %s %d",
__func__, title, ps->ps_instance + 1,
imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid);
#endif
/*
* Check the message with the program callback
*/
if ((p->p_cb)(fd, p, &imsg) == 0) {
/* Message was handled by the callback, continue */
imsg_free(&imsg);
continue;
}
/*
* Generic message handling
*/
switch (imsg.hdr.type) {
case IMSG_CTL_PROCFD:
IMSG_SIZE_CHECK(&imsg, &pf);
memcpy(&pf, imsg.data, sizeof(pf));
proc_accept(ps, imsg.fd, pf.pf_procid,
pf.pf_instance);
break;
default:
fatalx("%s: %s %d got invalid imsg %d peerid %d "
"from %s %d",
__func__, title, ps->ps_instance + 1,
imsg.hdr.type, imsg.hdr.peerid,
p->p_title, imsg.hdr.pid);
}
imsg_free(&imsg);
}
imsg_event_add(iev);
}
int
proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg)
{
return (-1);
}
/*
* imsg helper functions
*/
void
imsg_event_add(struct imsgev *iev)
{
if (iev->handler == NULL) {
imsg_flush(&iev->ibuf);
return;
}
iev->events = EV_READ;
if (iev->ibuf.w.queued)
iev->events |= EV_WRITE;
event_del(&iev->ev);
event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
event_add(&iev->ev, NULL);
}
int
imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
pid_t pid, int fd, void *data, uint16_t datalen)
{
int ret;
if ((ret = imsg_compose(&iev->ibuf, type, peerid,
pid, fd, data, datalen)) == -1)
return (ret);
imsg_event_add(iev);
return (ret);
}
int
imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
pid_t pid, int fd, const struct iovec *iov, int iovcnt)
{
int ret;
if ((ret = imsg_composev(&iev->ibuf, type, peerid,
pid, fd, iov, iovcnt)) == -1)
return (ret);
imsg_event_add(iev);
return (ret);
}
void
proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
{
if (*n == -1) {
/* Use a range of all target instances */
*n = 0;
*m = ps->ps_instances[id];
} else {
/* Use only a single slot of the specified peer process */
*m = *n + 1;
}
}
int
proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen)
{
int m;
proc_range(ps, id, &n, &m);
for (; n < m; n++) {
if (imsg_compose_event(&ps->ps_ievs[id][n],
type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1)
return (-1);
}
return (0);
}
int
proc_compose(struct privsep *ps, enum privsep_procid id,
uint16_t type, void *data, uint16_t datalen)
{
return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen));
}
int
proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt)
{
int m;
proc_range(ps, id, &n, &m);
for (; n < m; n++)
if (imsg_composev_event(&ps->ps_ievs[id][n],
type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1)
return (-1);
return (0);
}
int
proc_composev(struct privsep *ps, enum privsep_procid id,
uint16_t type, const struct iovec *iov, int iovcnt)
{
return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt));
}
int
proc_forward_imsg(struct privsep *ps, struct imsg *imsg,
enum privsep_procid id, int n)
{
return (proc_compose_imsg(ps, id, n, imsg->hdr.type,
imsg->hdr.peerid, imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg)));
}
struct imsgbuf *
proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
{
int m;
proc_range(ps, id, &n, &m);
return (&ps->ps_ievs[id][n].ibuf);
}
struct imsgev *
proc_iev(struct privsep *ps, enum privsep_procid id, int n)
{
int m;
proc_range(ps, id, &n, &m);
return (&ps->ps_ievs[id][n]);
}
/* This function should only be called with care as it breaks async I/O */
int
proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n)
{
struct imsgbuf *ibuf;
int m, ret = 0;
proc_range(ps, id, &n, &m);
for (; n < m; n++) {
if ((ibuf = proc_ibuf(ps, id, n)) == NULL)
return (-1);
do {
ret = imsg_flush(ibuf);
} while (ret == -1 && errno == EAGAIN);
if (ret == -1)
break;
imsg_event_add(&ps->ps_ievs[id][n]);
}
return (ret);
}

123
proc.h Normal file
View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2010-2015 Reyk Floeter <reyk@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
* 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.
*/
/* imsg */
struct imsgev {
struct imsgbuf ibuf;
void (*handler)(int, short, void *);
struct event ev;
struct privsep_proc *proc;
void *data;
short events;
};
#define IMSG_SIZE_CHECK(imsg, p) do { \
if (IMSG_DATA_SIZE(imsg) < sizeof(*p)) \
fatalx("bad length imsg received (%s)", #p); \
} while (0)
#define IMSG_DATA_SIZE(imsg) ((imsg)->hdr.len - IMSG_HEADER_SIZE)
#define PROC_PARENT_SOCK_FILENO 3
/* privsep */
enum privsep_procid {
PROC_PARENT,
PROC_SERVER,
PROC_LOGGER,
PROC_MAX,
};
#define CONFIG_RELOAD 0x00
#define CONFIG_SOCKS 0x01
#define CONFIG_ALL 0xff
struct privsep_pipes {
int *pp_pipes[PROC_MAX];
};
struct privsep {
struct privsep_pipes *ps_pipes[PROC_MAX];
struct privsep_pipes *ps_pp;
struct imsgev *ps_ievs[PROC_MAX];
const char *ps_title[PROC_MAX];
uint8_t ps_what[PROC_MAX];
struct passwd *ps_pw;
int ps_noaction;
unsigned int ps_instances[PROC_MAX];
unsigned int ps_instance;
/* Event and signal handlers */
struct event ps_evsigint;
struct event ps_evsigterm;
struct event ps_evsigchld;
struct event ps_evsighup;
void *ps_env;
};
struct privsep_proc {
const char *p_title;
enum privsep_procid p_id;
int (*p_cb)(int, struct privsep_proc *,
struct imsg *);
void (*p_init)(struct privsep *,
struct privsep_proc *);
void (*p_shutdown)(void);
const char *p_chroot;
struct passwd *p_pw;
struct privsep *p_ps;
};
struct privsep_fd {
enum privsep_procid pf_procid;
unsigned int pf_instance;
};
/* proc.c */
void proc_init(struct privsep *, struct privsep_proc *, unsigned int,
int, int, char **, enum privsep_procid);
void proc_kill(struct privsep *);
void proc_connect(struct privsep *ps);
void proc_dispatch(int, short event, void *);
void proc_range(struct privsep *, enum privsep_procid, int *, int *);
void proc_run(struct privsep *, struct privsep_proc *,
struct privsep_proc *, unsigned int,
void (*)(struct privsep *, struct privsep_proc *, void *), void *);
void imsg_event_add(struct imsgev *);
int imsg_compose_event(struct imsgev *, uint16_t, uint32_t,
pid_t, int, void *, uint16_t);
int imsg_composev_event(struct imsgev *, uint16_t, uint32_t,
pid_t, int, const struct iovec *, int);
int proc_compose_imsg(struct privsep *, enum privsep_procid, int,
uint16_t, uint32_t, int, void *, uint16_t);
int proc_compose(struct privsep *, enum privsep_procid,
uint16_t, void *data, uint16_t);
int proc_composev_imsg(struct privsep *, enum privsep_procid, int,
uint16_t, uint32_t, int, const struct iovec *, int);
int proc_composev(struct privsep *, enum privsep_procid,
uint16_t, const struct iovec *, int);
int proc_forward_imsg(struct privsep *, struct imsg *,
enum privsep_procid, int);
struct imsgbuf *
proc_ibuf(struct privsep *, enum privsep_procid, int);
struct imsgev *
proc_iev(struct privsep *, enum privsep_procid, int);
enum privsep_procid
proc_getid(struct privsep_proc *, unsigned int, const char *);
int proc_flush_imsg(struct privsep *, enum privsep_procid, int);

View File

@ -21,7 +21,6 @@
/* to make the linker happy */
struct conf conf;
struct imsgbuf logibuf, servibuf[PREFORK_MAX];
const struct suite {
const char *src;

View File

@ -31,7 +31,7 @@ if [ "${SKIP_RUNTIME_TESTS:-0}" -eq 1 ]; then
fi
# Run regression tests for the ge binary.
run_test test_ge
#run_test test_ge XXX
# Run regression tests for the gmid binary.
run_test test_static_files
@ -47,14 +47,16 @@ run_test test_custom_index_default_type_per_location
run_test test_auto_index
run_test test_block
run_test test_block_return_fmt
run_test test_require_client_ca
# run_test test_require_client_ca # XXX: needs to be readded
run_test test_root_inside_location
run_test test_root_inside_location_with_redirect
# run_test test_fastcgi XXX: needs to be fixed
run_test test_macro_expansion
run_test test_proxy_relay_to
run_test test_proxy_with_certs
# run_test test_proxy_with_certs# XXX: needs to be readded
# run_test test_unknown_host # XXX: breaks on some distro
run_test test_include_mime
# TODO: add test that uses only a TLSv1.2 or TLSv1.3
tests_done

View File

@ -21,6 +21,13 @@
#include <unistd.h>
void
sandbox_main_process(void)
{
if (pledge("stdio rpath inet dns sendfd proc", NULL) == -1)
fatal("pledge");
}
void
sandbox_server_process(void)
{

235
server.c
View File

@ -30,15 +30,20 @@
#include "logger.h"
#include "log.h"
#include "proc.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
int shutting_down;
static struct tls *ctx;
static struct event e4, e6, imsgev, siginfo, sigusr2;
static int has_ipv6, has_siginfo;
static struct event siginfo, sigusr2;
static int has_siginfo;
int connected_clients;
@ -66,11 +71,17 @@ static void client_error(struct bufferevent *, short, void *);
static void client_close_ev(int, short, void *);
static void do_accept(int, short, void*);
static void handle_dispatch_imsg(int, short, void *);
static void handle_siginfo(int, short, void*);
static void server_init(struct privsep *, struct privsep_proc *, void *);
static int server_dispatch_parent(int, struct privsep_proc *, struct imsg *);
static int server_dispatch_logger(int, struct privsep_proc *, struct imsg *);
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT, server_dispatch_parent },
{ "logger", PROC_LOGGER, server_dispatch_logger },
};
static uint32_t server_client_id;
struct client_tree_id clients;
@ -1281,7 +1292,7 @@ client_close(struct client *c)
client_close_ev(c->fd, 0, c);
}
static void
void
do_accept(int sock, short et, void *d)
{
struct client *c;
@ -1328,118 +1339,39 @@ client_by_id(int id)
return SPLAY_FIND(client_tree_id, &clients, &find);
}
static void
handle_dispatch_imsg(int fd, short ev, void *d)
{
struct imsgbuf *ibuf = d;
struct imsg imsg;
ssize_t n;
if ((n = imsg_read(ibuf)) == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return;
fatal("imsg_read");
}
if (n == 0)
fatalx("connection closed.");
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
fatal("imsg_get");
if (n == 0)
return;
switch (imsg.hdr.type) {
case IMSG_QUIT:
/*
* Don't call event_loopbreak since we want to
* finish handling the ongoing connections.
*/
shutting_down = 1;
event_del(&e4);
if (has_ipv6)
event_del(&e6);
if (has_siginfo)
signal_del(&siginfo);
event_del(&imsgev);
signal_del(&sigusr2);
break;
default:
fatalx("Unknown message %d", imsg.hdr.type);
}
imsg_free(&imsg);
}
}
static void
handle_siginfo(int fd, short ev, void *d)
{
log_info("%d connected clients", connected_clients);
}
static void
loop(int sock4, int sock6, struct imsgbuf *ibuf)
{
SPLAY_INIT(&clients);
event_init();
event_set(&e4, sock4, EV_READ | EV_PERSIST, &do_accept, NULL);
event_add(&e4, NULL);
if (sock6 != -1) {
has_ipv6 = 1;
event_set(&e6, sock6, EV_READ | EV_PERSIST, &do_accept, NULL);
event_add(&e6, NULL);
}
if (ibuf) {
event_set(&imsgev, ibuf->fd, EV_READ | EV_PERSIST,
handle_dispatch_imsg, ibuf);
event_add(&imsgev, NULL);
}
#ifdef SIGINFO
has_siginfo = 1;
signal_set(&siginfo, SIGINFO, &handle_siginfo, NULL);
signal_add(&siginfo, NULL);
#endif
signal_set(&sigusr2, SIGUSR2, &handle_siginfo, NULL);
signal_add(&sigusr2, NULL);
sandbox_server_process();
event_dispatch();
_exit(0);
}
static void
add_keypair(struct vhost *h, struct tls_config *conf)
{
if (*h->ocsp == '\0') {
if (tls_config_add_keypair_file(conf, h->cert, h->key) == -1)
fatalx("failed to load the keypair (%s, %s): %s",
h->cert, h->key, tls_config_error(conf));
if (h->ocsp == NULL) {
if (tls_config_add_keypair_mem(conf, h->cert, h->certlen,
h->key, h->keylen) == -1)
fatalx("failed to load the keypair: %s",
tls_config_error(conf));
} else {
if (tls_config_add_keypair_ocsp_file(conf, h->cert, h->key,
h->ocsp) == -1)
fatalx("failed to load the keypair (%s, %s, %s): %s",
h->cert, h->key, h->ocsp,
if (tls_config_add_keypair_ocsp_mem(conf, h->cert, h->certlen,
h->key, h->keylen, h->ocsp, h->ocsplen) == -1)
fatalx("failed to load the keypair: %s",
tls_config_error(conf));
}
}
/*
* XXX: in a ideal privsep world, this is done by the parent process
* and its content sent to us.
*/
static void
setup_tls(void)
{
struct tls_config *tlsconf;
struct vhost *h;
if (ctx == NULL) {
if ((ctx = tls_server()) == NULL)
fatal("tls_server failure");
}
if ((tlsconf = tls_config_new()) == NULL)
fatal("tls_config_new");
@ -1453,25 +1385,23 @@ setup_tls(void)
h = TAILQ_FIRST(&hosts);
log_info("loading %s, %s, %s", h->cert, h->key, h->ocsp);
/* we need to set something, then we can add how many key we want */
if (tls_config_set_keypair_file(tlsconf, h->cert, h->key))
fatalx("tls_config_set_keypair_file failed for (%s, %s): %s",
h->cert, h->key, tls_config_error(tlsconf));
if (tls_config_set_keypair_mem(tlsconf, h->cert, h->certlen,
h->key, h->keylen) == -1)
fatalx("tls_config_set_keypair_mem failed: %s",
tls_config_error(tlsconf));
/* same for OCSP */
if (*h->ocsp != '\0' &&
tls_config_set_ocsp_staple_file(tlsconf, h->ocsp) == -1)
fatalx("tls_config_set_ocsp_staple_file failed for (%s): %s",
h->ocsp, tls_config_error(tlsconf));
if (h->ocsp != NULL &&
tls_config_set_ocsp_staple_mem(tlsconf, h->ocsp, h->ocsplen)
== -1)
fatalx("tls_config_set_ocsp_staple_file failed: %s",
tls_config_error(tlsconf));
while ((h = TAILQ_NEXT(h, vhosts)) != NULL)
add_keypair(h, tlsconf);
if ((ctx = tls_server()) == NULL)
fatal("tls_server failure");
tls_reset(ctx);
if (tls_configure(ctx, tlsconf) == -1)
fatalx("tls_configure: %s", tls_error(ctx));
@ -1496,22 +1426,81 @@ load_vhosts(void)
}
}
int
server_main(struct imsgbuf *ibuf, int sock4, int sock6)
void
server(struct privsep *ps, struct privsep_proc *p)
{
/*
* setup tls before dropping privileges: we don't want user
* to put private certs inside the chroot.
*/
setup_tls();
drop_priv();
if (load_default_mime(&conf.mime) == -1)
fatal("can't load default mime");
sort_mime(&conf.mime);
load_vhosts();
loop(sock4, sock6, ibuf);
proc_run(ps, p, procs, nitems(procs), server_init, NULL);
}
static void
server_init(struct privsep *ps, struct privsep_proc *p, void *arg)
{
#if 0
static volatile int attached = 0;
while (!attached)
sleep(1);
#endif
SPLAY_INIT(&clients);
#ifdef SIGINFO
has_siginfo = 1;
signal_set(&siginfo, SIGINFO, &handle_siginfo, NULL);
signal_add(&siginfo, NULL);
#endif
signal_set(&sigusr2, SIGUSR2, &handle_siginfo, NULL);
signal_add(&sigusr2, NULL);
sandbox_server_process();
}
static int
server_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct privsep *ps = p->p_ps;
struct conf *conf = ps->ps_env;
switch (imsg->hdr.type) {
case IMSG_RECONF_START:
case IMSG_RECONF_MIME:
case IMSG_RECONF_PROTOS:
case IMSG_RECONF_PORT:
case IMSG_RECONF_SOCK4:
case IMSG_RECONF_SOCK6:
case IMSG_RECONF_FCGI:
case IMSG_RECONF_HOST:
case IMSG_RECONF_CERT:
case IMSG_RECONF_KEY:
case IMSG_RECONF_OCSP:
case IMSG_RECONF_LOC:
case IMSG_RECONF_ENV:
case IMSG_RECONF_ALIAS:
case IMSG_RECONF_PROXY:
return config_recv(conf, imsg);
case IMSG_RECONF_END:
if (config_recv(conf, imsg) == -1)
return -1;
if (load_default_mime(&conf->mime) == -1)
fatal("can't load default mime");
sort_mime(&conf->mime);
setup_tls();
load_vhosts();
if (conf->sock4 != -1)
event_add(&conf->evsock4, NULL);
if (conf->sock6 != -1)
event_add(&conf->evsock6, NULL);
break;
default:
return -1;
}
return 0;
}
static int
server_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
{
return -1;
}
int
client_tree_cmp(struct client *a, struct client *b)