aboutsummaryrefslogtreecommitdiff
path: root/httpd/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'httpd/config.c')
-rw-r--r--httpd/config.c589
1 files changed, 589 insertions, 0 deletions
diff --git a/httpd/config.c b/httpd/config.c
new file mode 100644
index 0000000..2dc9159
--- /dev/null
+++ b/httpd/config.c
@@ -0,0 +1,589 @@
+/* $OpenBSD: config.c,v 1.36 2015/02/23 11:48:41 reyk Exp $ */
+
+/*
+ * Copyright (c) 2011 - 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.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <imsg.h>
+
+#include "httpd.h"
+
+int config_getserver_config(struct httpd *, struct server *,
+ struct imsg *);
+int config_getserver_auth(struct httpd *, struct server_config *);
+
+int
+config_init(struct httpd *env)
+{
+ struct privsep *ps = env->sc_ps;
+ u_int what;
+
+ /* Global configuration */
+ if (privsep_process == PROC_PARENT) {
+ env->sc_prefork_server = SERVER_NUMPROC;
+
+ ps->ps_what[PROC_PARENT] = CONFIG_ALL;
+ ps->ps_what[PROC_SERVER] =
+ CONFIG_SERVERS|CONFIG_MEDIA|CONFIG_AUTH;
+ ps->ps_what[PROC_LOGGER] = CONFIG_SERVERS;
+ }
+
+ /* Other configuration */
+ what = ps->ps_what[privsep_process];
+
+ if (what & CONFIG_SERVERS) {
+ if ((env->sc_servers =
+ calloc(1, sizeof(*env->sc_servers))) == NULL)
+ return (-1);
+ TAILQ_INIT(env->sc_servers);
+ }
+
+ if (what & CONFIG_MEDIA) {
+ if ((env->sc_mediatypes =
+ calloc(1, sizeof(*env->sc_mediatypes))) == NULL)
+ return (-1);
+ RB_INIT(env->sc_mediatypes);
+ }
+
+ if (what & CONFIG_AUTH) {
+ if ((env->sc_auth =
+ calloc(1, sizeof(*env->sc_auth))) == NULL)
+ return (-1);
+ TAILQ_INIT(env->sc_auth);
+ }
+
+ return (0);
+}
+
+void
+config_purge(struct httpd *env, u_int reset)
+{
+ struct privsep *ps = env->sc_ps;
+ struct server *srv;
+ struct auth *auth;
+ u_int what;
+
+ what = ps->ps_what[privsep_process] & reset;
+
+ if (what & CONFIG_SERVERS && env->sc_servers != NULL) {
+ while ((srv = TAILQ_FIRST(env->sc_servers)) != NULL)
+ server_purge(srv);
+ }
+
+ if (what & CONFIG_MEDIA && env->sc_mediatypes != NULL)
+ media_purge(env->sc_mediatypes);
+
+ if (what & CONFIG_AUTH && env->sc_auth != NULL) {
+ while ((auth = TAILQ_FIRST(env->sc_auth)) != NULL) {
+ auth_free(env->sc_auth, auth);
+ free(auth);
+ }
+ }
+}
+
+int
+config_setreset(struct httpd *env, u_int reset)
+{
+ struct privsep *ps = env->sc_ps;
+ int id;
+
+ for (id = 0; id < PROC_MAX; id++) {
+ if ((reset & ps->ps_what[id]) == 0 ||
+ id == privsep_process)
+ continue;
+ proc_compose_imsg(ps, id, -1, IMSG_CTL_RESET, -1,
+ &reset, sizeof(reset));
+ }
+
+ return (0);
+}
+
+int
+config_getreset(struct httpd *env, struct imsg *imsg)
+{
+ u_int mode;
+
+ IMSG_SIZE_CHECK(imsg, &mode);
+ memcpy(&mode, imsg->data, sizeof(mode));
+
+ config_purge(env, mode);
+
+ return (0);
+}
+
+int
+config_getcfg(struct httpd *env, struct imsg *imsg)
+{
+ struct privsep *ps = env->sc_ps;
+ struct ctl_flags cf;
+ u_int what;
+
+ if (IMSG_DATA_SIZE(imsg) != sizeof(cf))
+ return (0); /* ignore */
+
+ /* Update runtime flags */
+ memcpy(&cf, imsg->data, sizeof(cf));
+ env->sc_opts = cf.cf_opts;
+ env->sc_flags = cf.cf_flags;
+
+ what = ps->ps_what[privsep_process];
+
+ if (privsep_process != PROC_PARENT)
+ proc_compose_imsg(env->sc_ps, PROC_PARENT, -1,
+ IMSG_CFG_DONE, -1, NULL, 0);
+
+ return (0);
+}
+
+int
+config_setserver(struct httpd *env, struct server *srv)
+{
+ struct privsep *ps = env->sc_ps;
+ struct server_config s;
+ int id;
+ int fd, n, m;
+ struct iovec iov[6];
+ size_t c;
+ u_int what;
+
+ /* opens listening sockets etc. */
+ if (server_privinit(srv) == -1)
+ return (-1);
+
+ for (id = 0; id < PROC_MAX; id++) {
+ what = ps->ps_what[id];
+
+ if ((what & CONFIG_SERVERS) == 0 || id == privsep_process)
+ continue;
+
+ DPRINTF("%s: sending %s \"%s[%u]\" to %s fd %d", __func__,
+ (srv->srv_conf.flags & SRVFLAG_LOCATION) ?
+ "location" : "server",
+ srv->srv_conf.name, srv->srv_conf.id,
+ ps->ps_title[id], srv->srv_s);
+
+ memcpy(&s, &srv->srv_conf, sizeof(s));
+
+ c = 0;
+ iov[c].iov_base = &s;
+ iov[c++].iov_len = sizeof(s);
+ if (srv->srv_conf.return_uri_len != 0) {
+ iov[c].iov_base = srv->srv_conf.return_uri;
+ iov[c++].iov_len = srv->srv_conf.return_uri_len;
+ }
+ if (srv->srv_conf.tls_cert_len != 0) {
+ iov[c].iov_base = srv->srv_conf.tls_cert;
+ iov[c++].iov_len = srv->srv_conf.tls_cert_len;
+ }
+ if (srv->srv_conf.tls_key_len != 0) {
+ iov[c].iov_base = srv->srv_conf.tls_key;
+ iov[c++].iov_len = srv->srv_conf.tls_key_len;
+ }
+
+ if (id == PROC_SERVER &&
+ (srv->srv_conf.flags & SRVFLAG_LOCATION) == 0) {
+ /* XXX imsg code will close the fd after 1st call */
+ n = -1;
+ proc_range(ps, id, &n, &m);
+ for (n = 0; n < m; n++) {
+ if (srv->srv_s == -1)
+ fd = -1;
+ else if ((fd = dup(srv->srv_s)) == -1)
+ return (-1);
+ proc_composev_imsg(ps, id, n,
+ IMSG_CFG_SERVER, fd, iov, c);
+ }
+ } else {
+ proc_composev_imsg(ps, id, -1, IMSG_CFG_SERVER, -1,
+ iov, c);
+ }
+ }
+
+ return (0);
+}
+
+int
+config_getserver_auth(struct httpd *env, struct server_config *srv_conf)
+{
+ struct privsep *ps = env->sc_ps;
+
+ if ((ps->ps_what[privsep_process] & CONFIG_AUTH) == 0 ||
+ (srv_conf->flags & SRVFLAG_AUTH) == 0)
+ return (0);
+
+ if ((srv_conf->auth = auth_byid(env->sc_auth,
+ srv_conf->auth_id)) == NULL)
+ return (-1);
+
+ return (0);
+}
+
+int
+config_getserver_config(struct httpd *env, struct server *srv,
+ struct imsg *imsg)
+{
+#ifdef DEBUG
+ struct privsep *ps = env->sc_ps;
+#endif
+ struct server_config *srv_conf, *parent;
+ u_int8_t *p = imsg->data;
+ u_int f;
+ size_t s;
+
+ if ((srv_conf = calloc(1, sizeof(*srv_conf))) == NULL)
+ return (-1);
+
+ IMSG_SIZE_CHECK(imsg, srv_conf);
+ memcpy(srv_conf, p, sizeof(*srv_conf));
+ s = sizeof(*srv_conf);
+
+ /* Reset these variables to avoid free'ing invalid pointers */
+ serverconfig_reset(srv_conf);
+
+ TAILQ_FOREACH(parent, &srv->srv_hosts, entry) {
+ if (strcmp(parent->name, srv_conf->name) == 0)
+ break;
+ }
+ if (parent == NULL)
+ parent = &srv->srv_conf;
+
+ if (config_getserver_auth(env, srv_conf) != 0)
+ goto fail;
+
+ /*
+ * Get variable-length values for the virtual host. The tls_* ones
+ * aren't needed in the virtual hosts unless we implement SNI.
+ */
+ if (srv_conf->return_uri_len != 0) {
+ if ((srv_conf->return_uri = get_data(p + s,
+ srv_conf->return_uri_len)) == NULL)
+ goto fail;
+ s += srv_conf->return_uri_len;
+ }
+
+ if (srv_conf->flags & SRVFLAG_LOCATION) {
+ /* Inherit configuration from the parent */
+ f = SRVFLAG_INDEX|SRVFLAG_NO_INDEX;
+ if ((srv_conf->flags & f) == 0) {
+ srv_conf->flags |= parent->flags & f;
+ (void)strlcpy(srv_conf->index, parent->index,
+ sizeof(srv_conf->index));
+ }
+
+ f = SRVFLAG_AUTO_INDEX|SRVFLAG_NO_AUTO_INDEX;
+ if ((srv_conf->flags & f) == 0)
+ srv_conf->flags |= parent->flags & f;
+
+ f = SRVFLAG_SOCKET|SRVFLAG_FCGI;
+ if ((srv_conf->flags & f) == SRVFLAG_FCGI) {
+ srv_conf->flags |= f;
+ (void)strlcpy(srv_conf->socket, HTTPD_FCGI_SOCKET,
+ sizeof(srv_conf->socket));
+ }
+
+ f = SRVFLAG_ROOT;
+ if ((srv_conf->flags & f) == 0) {
+ srv_conf->flags |= parent->flags & f;
+ (void)strlcpy(srv_conf->root, parent->root,
+ sizeof(srv_conf->root));
+ }
+
+ f = SRVFLAG_FCGI|SRVFLAG_NO_FCGI;
+ if ((srv_conf->flags & f) == 0)
+ srv_conf->flags |= parent->flags & f;
+
+ f = SRVFLAG_LOG|SRVFLAG_NO_LOG;
+ if ((srv_conf->flags & f) == 0) {
+ srv_conf->flags |= parent->flags & f;
+ srv_conf->logformat = parent->logformat;
+ }
+
+ f = SRVFLAG_SYSLOG|SRVFLAG_NO_SYSLOG;
+ if ((srv_conf->flags & f) == 0)
+ srv_conf->flags |= parent->flags & f;
+
+ f = SRVFLAG_AUTH|SRVFLAG_NO_AUTH;
+ if ((srv_conf->flags & f) == 0) {
+ srv_conf->flags |= parent->flags & f;
+ srv_conf->auth = parent->auth;
+ srv_conf->auth_id = parent->auth_id;
+ (void)strlcpy(srv_conf->auth_realm,
+ parent->auth_realm,
+ sizeof(srv_conf->auth_realm));
+ }
+
+ f = SRVFLAG_TLS;
+ srv_conf->flags |= parent->flags & f;
+
+ f = SRVFLAG_ACCESS_LOG;
+ if ((srv_conf->flags & f) == 0) {
+ srv_conf->flags |= parent->flags & f;
+ (void)strlcpy(srv_conf->accesslog,
+ parent->accesslog,
+ sizeof(srv_conf->accesslog));
+ }
+
+ f = SRVFLAG_ERROR_LOG;
+ if ((srv_conf->flags & f) == 0) {
+ srv_conf->flags |= parent->flags & f;
+ (void)strlcpy(srv_conf->errorlog,
+ parent->errorlog,
+ sizeof(srv_conf->errorlog));
+ }
+
+ f = SRVFLAG_BLOCK|SRVFLAG_NO_BLOCK;
+ if ((srv_conf->flags & f) == 0) {
+ free(srv_conf->return_uri);
+ srv_conf->flags |= parent->flags & f;
+ srv_conf->return_code = parent->return_code;
+ srv_conf->return_uri_len = parent->return_uri_len;
+ if (srv_conf->return_uri_len &&
+ (srv_conf->return_uri =
+ strdup(parent->return_uri)) == NULL)
+ goto fail;
+ }
+
+ memcpy(&srv_conf->timeout, &parent->timeout,
+ sizeof(srv_conf->timeout));
+ srv_conf->maxrequests = parent->maxrequests;
+ srv_conf->maxrequestbody = parent->maxrequestbody;
+
+ DPRINTF("%s: %s %d location \"%s\", "
+ "parent \"%s[%u]\", flags: %s",
+ __func__, ps->ps_title[privsep_process], ps->ps_instance,
+ srv_conf->location, parent->name, parent->id,
+ printb_flags(srv_conf->flags, SRVFLAG_BITS));
+ } else {
+ /* Add a new "virtual" server */
+ DPRINTF("%s: %s %d server \"%s[%u]\", parent \"%s[%u]\", "
+ "flags: %s", __func__,
+ ps->ps_title[privsep_process], ps->ps_instance,
+ srv_conf->name, srv_conf->id, parent->name, parent->id,
+ printb_flags(srv_conf->flags, SRVFLAG_BITS));
+ }
+
+ TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry);
+
+ return (0);
+
+ fail:
+ serverconfig_free(srv_conf);
+ free(srv_conf);
+ return (-1);
+}
+
+int
+config_getserver(struct httpd *env, struct imsg *imsg)
+{
+#ifdef DEBUG
+ struct privsep *ps = env->sc_ps;
+#endif
+ struct server *srv = NULL;
+ struct server_config srv_conf;
+ u_int8_t *p = imsg->data;
+ size_t s;
+
+ IMSG_SIZE_CHECK(imsg, &srv_conf);
+ memcpy(&srv_conf, p, sizeof(srv_conf));
+ s = sizeof(srv_conf);
+
+ /* Reset these variables to avoid free'ing invalid pointers */
+ serverconfig_reset(&srv_conf);
+
+ if ((IMSG_DATA_SIZE(imsg) - s) <
+ (srv_conf.tls_cert_len + srv_conf.tls_key_len +
+ srv_conf.return_uri_len)) {
+ log_debug("%s: invalid message length", __func__);
+ goto fail;
+ }
+
+ /* Check if server with matching listening socket already exists */
+ if ((srv = server_byaddr((struct sockaddr *)
+ &srv_conf.ss, srv_conf.port)) != NULL) {
+ /* Add "host" to existing listening server */
+ if (imsg->fd != -1) {
+ if (srv->srv_s == -1)
+ srv->srv_s = imsg->fd;
+ else
+ close(imsg->fd);
+ }
+ return (config_getserver_config(env, srv, imsg));
+ }
+
+ if (srv_conf.flags & SRVFLAG_LOCATION)
+ fatalx("invalid location");
+
+ /* Otherwise create a new server */
+ if ((srv = calloc(1, sizeof(*srv))) == NULL)
+ goto fail;
+
+ memcpy(&srv->srv_conf, &srv_conf, sizeof(srv->srv_conf));
+ srv->srv_s = imsg->fd;
+
+ if (config_getserver_auth(env, &srv->srv_conf) != 0)
+ goto fail;
+
+ SPLAY_INIT(&srv->srv_clients);
+ TAILQ_INIT(&srv->srv_hosts);
+
+ TAILQ_INSERT_TAIL(&srv->srv_hosts, &srv->srv_conf, entry);
+ TAILQ_INSERT_TAIL(env->sc_servers, srv, srv_entry);
+
+ DPRINTF("%s: %s %d configuration \"%s[%u]\", flags: %s", __func__,
+ ps->ps_title[privsep_process], ps->ps_instance,
+ srv->srv_conf.name, srv->srv_conf.id,
+ printb_flags(srv->srv_conf.flags, SRVFLAG_BITS));
+
+ /*
+ * Get all variable-length values for the parent server.
+ */
+ if (srv->srv_conf.return_uri_len != 0) {
+ if ((srv->srv_conf.return_uri = get_data(p + s,
+ srv->srv_conf.return_uri_len)) == NULL)
+ goto fail;
+ s += srv->srv_conf.return_uri_len;
+ }
+ if (srv->srv_conf.tls_cert_len != 0) {
+ if ((srv->srv_conf.tls_cert = get_data(p + s,
+ srv->srv_conf.tls_cert_len)) == NULL)
+ goto fail;
+ s += srv->srv_conf.tls_cert_len;
+ }
+ if (srv->srv_conf.tls_key_len != 0) {
+ if ((srv->srv_conf.tls_key = get_data(p + s,
+ srv->srv_conf.tls_key_len)) == NULL)
+ goto fail;
+ s += srv->srv_conf.tls_key_len;
+ }
+
+ return (0);
+
+ fail:
+ if (imsg->fd != -1)
+ close(imsg->fd);
+ if (srv != NULL) {
+ free(srv->srv_conf.tls_cert);
+ free(srv->srv_conf.tls_key);
+ }
+ free(srv);
+
+ return (-1);
+}
+
+int
+config_setmedia(struct httpd *env, struct media_type *media)
+{
+ struct privsep *ps = env->sc_ps;
+ int id;
+ u_int what;
+
+ for (id = 0; id < PROC_MAX; id++) {
+ what = ps->ps_what[id];
+
+ if ((what & CONFIG_MEDIA) == 0 || id == privsep_process)
+ continue;
+
+ DPRINTF("%s: sending media \"%s\" to %s", __func__,
+ media->media_name, ps->ps_title[id]);
+
+ proc_compose_imsg(ps, id, -1, IMSG_CFG_MEDIA, -1,
+ media, sizeof(*media));
+ }
+
+ return (0);
+}
+
+int
+config_getmedia(struct httpd *env, struct imsg *imsg)
+{
+#ifdef DEBUG
+ struct privsep *ps = env->sc_ps;
+#endif
+ struct media_type media;
+ u_int8_t *p = imsg->data;
+
+ IMSG_SIZE_CHECK(imsg, &media);
+ memcpy(&media, p, sizeof(media));
+
+ if (media_add(env->sc_mediatypes, &media) == NULL) {
+ log_debug("%s: failed to add media \"%s\"",
+ __func__, media.media_name);
+ return (-1);
+ }
+
+ DPRINTF("%s: %s %d received media \"%s\"", __func__,
+ ps->ps_title[privsep_process], ps->ps_instance,
+ media.media_name);
+
+ return (0);
+}
+
+int
+config_setauth(struct httpd *env, struct auth *auth)
+{
+ struct privsep *ps = env->sc_ps;
+ int id;
+ u_int what;
+
+ for (id = 0; id < PROC_MAX; id++) {
+ what = ps->ps_what[id];
+
+ if ((what & CONFIG_AUTH) == 0 || id == privsep_process)
+ continue;
+
+ DPRINTF("%s: sending auth \"%s[%u]\" to %s", __func__,
+ auth->auth_htpasswd, auth->auth_id, ps->ps_title[id]);
+
+ proc_compose_imsg(ps, id, -1, IMSG_CFG_AUTH, -1,
+ auth, sizeof(*auth));
+ }
+
+ return (0);
+}
+
+int
+config_getauth(struct httpd *env, struct imsg *imsg)
+{
+#ifdef DEBUG
+ struct privsep *ps = env->sc_ps;
+#endif
+ struct auth auth;
+ u_int8_t *p = imsg->data;
+
+ IMSG_SIZE_CHECK(imsg, &auth);
+ memcpy(&auth, p, sizeof(auth));
+
+ if (auth_add(env->sc_auth, &auth) == NULL) {
+ log_debug("%s: failed to add auth \"%s[%u]\"",
+ __func__, auth.auth_htpasswd, auth.auth_id);
+ return (-1);
+ }
+
+ DPRINTF("%s: %s %d received auth \"%s[%u]\"", __func__,
+ ps->ps_title[privsep_process], ps->ps_instance,
+ auth.auth_htpasswd, auth.auth_id);
+
+ return (0);
+}