commit c26f2460e42aa0822c283c805958989f339e7d8b from: Omar Polo date: Thu Jun 08 13:59:31 2023 UTC 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. commit - 99f1fbb0c73b6f62b966760181b3d97f54bbe73b commit + c26f2460e42aa0822c283c805958989f339e7d8b blob - 171925942359ccc88b8bce2919e568b1b72f3187 blob + 438d806992595df927b2cd8240c4d35e0345b629 --- Makefile +++ Makefile @@ -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} blob - 7a438a5cda87f6c53de9803f4203f6ac80f6c410 blob + feb0ca9ca1c326894639375b7bc49164ef58bfd0 --- config.c +++ config.c @@ -16,8 +16,15 @@ #include "gmid.h" +#include + +#include +#include #include +#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; +} blob - 587d9638283f293076bdfcb15a97d51bc1443f1c blob + c7a1609ebcdae8c0a0375e6713ccb5114f5be65a --- configure +++ configure @@ -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 -` blob - d43f44685d0b814ec2a34ef98ae5301f2a27f3bf blob + 554fd21343d888734c6d43afe3794a095d5970f0 --- ge.c +++ ge.c @@ -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 */ @@ -45,12 +45,6 @@ static const struct option opts[] = { }; void -drop_priv(void) -{ - return; -} - -void load_local_cert(struct vhost *h, const char *hostname, const char *dir) { char *cert, *key; @@ -112,29 +106,6 @@ data_dir(void) mkdirs(t, 0755); 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 @@ -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) { blob - 69f4e7a077acece41a764edc7ca912b8fbebc88e blob + ec3ddb74f9d60f665d0a268af92dddfc8603d478 --- gmid.c +++ gmid.c @@ -32,9 +32,27 @@ #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'}, {"version", no_argument, NULL, 'V'}, @@ -46,20 +64,14 @@ struct fcgi fcgi[FCGI_MAX]; struct vhosthead hosts; int sock4, sock6; +int privsep_process; +int pidfd = -1; -struct imsgbuf logibuf, servibuf[PREFORK_MAX]; - 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 ((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); + } - if (daemon(1, 1) == -1) - fatal("daemon"); + 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); - /* - * 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. - */ - signal(SIGHUP, dummy_handler); - signal(SIGINT, dummy_handler); - signal(SIGTERM, dummy_handler); + sandbox_main_process(); + + event_init(); + + signal(SIGPIPE, SIG_IGN); - /* wait a sighup and reload the daemon */ - for (;;) { - serve(); + 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); - if (!wait_signal()) - break; + signal_add(&ps->ps_evsigint, NULL); + signal_add(&ps->ps_evsigterm, NULL); + signal_add(&ps->ps_evsigchld, NULL); + signal_add(&ps->ps_evsighup, NULL); - log_info("reloading configuration %s", config_path); + proc_connect(ps); - /* 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); - } + if (main_configure(&conf) == -1) + fatal("configuration failed"); - old_ipv6 = conf.ipv6; - old_port = conf.port; + event_dispatch(); + main_shutdown(&conf); + /* NOTREACHED */ + return 0; +} - config_free(); - config_init(); - parse_conf(config_path); +static int +main_configure(struct conf *conf) +{ + struct privsep *ps = conf->ps; - if (old_port != conf.port) { - close(sock4); - close(sock6); - sock4 = -1; - sock6 = -1; - } + conf->reload = conf->prefork; - if (sock6 != -1 && old_ipv6 != conf.ipv6) { - close(sock6); - sock6 = -1; - } + if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_START, NULL, 0) == -1) + return -1; - if (sock4 == -1) - sock4 = make_socket(conf.port, AF_INET); - if (sock6 == -1 && conf.ipv6) - sock6 = make_socket(conf.port, AF_INET6); + 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; } - 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); + 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; } - imsg_compose(&logibuf, IMSG_QUIT, 0, 0, -1, NULL, 0); - imsg_flush(&logibuf); - close(logibuf.fd); + 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; + + /* + * Normal signal handler rules don't apply here because libevent + * decouples for us. + */ + + 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); + } +} + +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; + + switch (imsg->hdr.type) { + case IMSG_RECONF_DONE: + main_configure_done(conf); + break; + default: + return -1; + } + + 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; + } + + 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); } blob - b1bd3bbedd13d669f146ca1f8ac2b19d08ed1282 blob + da5a4e5e6079c9aa958c81ed7f6d546f927a0bcd --- gmid.h +++ gmid.h @@ -81,8 +81,12 @@ #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; char *host; @@ -163,10 +167,19 @@ 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); blob - c509ddbecbcba23be332c649fe3914a4d18546a4 blob + 08aa19e22d04a3f71463493492ee67f764717549 --- logger.c +++ logger.c @@ -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; } -} -static void -handle_dispatch_imsg(int fd, short ev, void *d) -{ - struct imsgbuf *ibuf = d; - dispatch_imsg(ibuf, handlers, sizeof(handlers)); + return 0; } -int -logger_main(int fd, struct imsgbuf *ibuf) +static int +logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg) { - log = stderr; + char *msg; + size_t datalen; - event_init(); + 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; + } - event_set(&imsgev, fd, EV_READ | EV_PERSIST, &handle_dispatch_imsg, ibuf); - event_add(&imsgev, NULL); - - sandbox_logger_process(); - - event_dispatch(); - - closelog(); - return 0; } blob - ae870c297da8ac83186b5ded4c1c47efdd3a05d8 blob + fb6a5061b1f5c86562affef69ceb5b866b06917c --- logger.h +++ logger.h @@ -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 *); blob - db4329e6860dd41950624f18ab357beb09946543 blob + d3de71023238e55fdb4a662a64daa878ec784ce2 --- parse.y +++ parse.y @@ -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; } blob - /dev/null blob + 5d7979c0c00b5a2285a59a31aa51adefae80e482 (mode 644) --- /dev/null +++ proc.c @@ -0,0 +1,838 @@ +/* $OpenBSD: proc.c,v 1.41 2021/12/04 06:52:58 florian Exp $ */ + +/* + * Copyright (c) 2010 - 2016 Reyk Floeter + * Copyright (c) 2008 Pierre-Yves Ritschard + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} blob - /dev/null blob + 4ebacd18de758fca8b539a228d69181f2002a474 (mode 644) --- /dev/null +++ proc.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2010-2015 Reyk Floeter + * + * 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); blob - 52b18c609ace28a269db5f7d5fdbb789fbdf94ee blob + f3dfbd2ea5fd93fedc7ea006d82cbcf592de9fcb --- regress/puny-test.c +++ regress/puny-test.c @@ -21,7 +21,6 @@ /* to make the linker happy */ struct conf conf; -struct imsgbuf logibuf, servibuf[PREFORK_MAX]; const struct suite { const char *src; blob - 63d99ac72f7f238ed2512f819aa34549793daf93 blob + f89b35333dc017a98a957f1c2ac2ee8fbd0335c1 --- regress/regress +++ regress/regress @@ -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 blob - 3216c980a949452ccd4d973ec5b84ebee2dc6373 blob + 8a8cc9e2f5a9bc4d792573b5f878b62f40264649 --- sandbox.c +++ sandbox.c @@ -22,6 +22,13 @@ #include void +sandbox_main_process(void) +{ + if (pledge("stdio rpath inet dns sendfd proc", NULL) == -1) + fatal("pledge"); +} + +void sandbox_server_process(void) { struct vhost *h; blob - d9a42e0ed9fe9b77cb918dbd021c0c27a52b2b3f blob + 49c641f4144ffdf18bc50ba0cb427400a88144c9 --- server.c +++ server.c @@ -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, 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; @@ -1329,117 +1340,38 @@ client_by_id(int id) } 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)