diff options
author | Omar Polo <op@omarpolo.com> | 2023-06-08 13:59:31 +0000 |
---|---|---|
committer | Omar Polo <op@omarpolo.com> | 2023-06-08 13:59:31 +0000 |
commit | c26f2460e42aa0822c283c805958989f339e7d8b (patch) | |
tree | 423410fae4de90b2b753ccad621d364c4641bed2 /config.c | |
parent | 99f1fbb0c73b6f62b966760181b3d97f54bbe73b (diff) |
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.
Diffstat (limited to 'config.c')
-rw-r--r-- | config.c | 405 |
1 files changed, 405 insertions, 0 deletions
@@ -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; +} |