aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--httpd/Makefile6
-rw-r--r--httpd/config.c199
-rw-r--r--httpd/control.c20
-rw-r--r--httpd/httpd.85
-rw-r--r--httpd/httpd.c150
-rw-r--r--httpd/httpd.conf.548
-rw-r--r--httpd/httpd.h146
-rw-r--r--httpd/log.c52
-rw-r--r--httpd/logger.c29
-rw-r--r--httpd/parse.y97
-rw-r--r--httpd/patterns.78
-rw-r--r--httpd/proc.c602
-rw-r--r--httpd/server.c288
-rw-r--r--httpd/server_fcgi.c117
-rw-r--r--httpd/server_file.c171
-rw-r--r--httpd/server_http.c157
-rw-r--r--regress/tests/Httpd.pm5
-rw-r--r--regress/tests/LICENSE2
-rw-r--r--regress/tests/Makefile34
-rw-r--r--regress/tests/README10
-rw-r--r--regress/tests/args-get-1048576.pl2
-rw-r--r--regress/tests/args-get-1073741824.pl2
-rw-r--r--regress/tests/args-get-512.pl2
-rw-r--r--regress/tests/args-get-slash.pl4
-rw-r--r--regress/tests/funcs.pl182
25 files changed, 1496 insertions, 842 deletions
diff --git a/httpd/Makefile b/httpd/Makefile
index e01dec1..3766675 100644
--- a/httpd/Makefile
+++ b/httpd/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.28 2015/06/23 15:23:14 reyk Exp $
+# $OpenBSD: Makefile,v 1.30 2017/07/03 22:21:47 espie Exp $
PROG= httpd
SRCS= parse.y
@@ -16,7 +16,7 @@ CFLAGS+= -Wall -I${.CURDIR}
CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
CFLAGS+= -Wmissing-declarations
CFLAGS+= -Wshadow -Wpointer-arith
-CFLAGS+= -Wsign-compare
-CLEANFILES+= y.tab.h
+CFLAGS+= -Wsign-compare -Wcast-qual
+YFLAGS=
.include <bsd.prog.mk>
diff --git a/httpd/config.c b/httpd/config.c
index 4f8ef4c..3c31c3d 100644
--- a/httpd/config.c
+++ b/httpd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.47 2016/08/15 14:14:55 jsing Exp $ */
+/* $OpenBSD: config.c,v 1.53 2017/07/19 17:36:25 jsing Exp $ */
/*
* Copyright (c) 2011 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -41,14 +41,13 @@ config_init(struct httpd *env)
unsigned int what;
/* Global configuration */
- if (privsep_process == PROC_PARENT) {
+ 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;
- }
+ 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];
@@ -147,6 +146,7 @@ config_getcfg(struct httpd *env, struct imsg *imsg)
memcpy(&cf, imsg->data, sizeof(cf));
env->sc_opts = cf.cf_opts;
env->sc_flags = cf.cf_flags;
+ memcpy(env->sc_tls_sid, cf.cf_tls_sid, sizeof(env->sc_tls_sid));
what = ps->ps_what[privsep_process];
@@ -211,10 +211,18 @@ config_setserver(struct httpd *env, struct server *srv)
__func__, srv->srv_conf.name);
return (-1);
}
+
+ /* Prevent fd exhaustion in the parent. */
+ if (proc_flush_imsg(ps, id, n) == -1) {
+ log_warn("%s: failed to flush "
+ "IMSG_CFG_SERVER imsg for `%s'",
+ __func__, srv->srv_conf.name);
+ return (-1);
+ }
}
/* Configure TLS if necessary. */
- config_settls(env, srv);
+ config_setserver_tls(env, srv);
} else {
if (proc_composev(ps, id, IMSG_CFG_SERVER,
iov, c) != 0) {
@@ -226,11 +234,21 @@ config_setserver(struct httpd *env, struct server *srv)
}
}
+ /* Close server socket early to prevent fd exhaustion in the parent. */
+ if (srv->srv_s != -1) {
+ close(srv->srv_s);
+ srv->srv_s = -1;
+ }
+
+ explicit_bzero(&srv->srv_conf.tls_ticket_key,
+ sizeof(srv->srv_conf.tls_ticket_key));
+
return (0);
}
-int
-config_settls(struct httpd *env, struct server *srv)
+static int
+config_settls(struct httpd *env, struct server *srv, enum tls_config_type type,
+ const char *label, uint8_t *data, size_t len)
{
struct privsep *ps = env->sc_ps;
struct server_config *srv_conf = &srv->srv_conf;
@@ -238,54 +256,65 @@ config_settls(struct httpd *env, struct server *srv)
struct iovec iov[2];
size_t c;
- if ((srv_conf->flags & SRVFLAG_TLS) == 0)
+ if (data == NULL || len == 0)
return (0);
- log_debug("%s: configuring tls for %s", __func__, srv_conf->name);
+ DPRINTF("%s: sending tls %s for \"%s[%u]\" to %s fd %d", __func__,
+ label, srv_conf->name, srv_conf->id, ps->ps_title[PROC_SERVER],
+ srv->srv_s);
- if (srv_conf->tls_cert_len != 0) {
- DPRINTF("%s: sending tls cert \"%s[%u]\" to %s fd %d", __func__,
- srv_conf->name, srv_conf->id, ps->ps_title[PROC_SERVER],
- srv->srv_s);
+ memset(&tls, 0, sizeof(tls));
+ tls.id = srv_conf->id;
+ tls.tls_type = type;
+ tls.tls_len = len;
+ tls.tls_chunk_offset = 0;
- memset(&tls, 0, sizeof(tls));
- tls.id = srv_conf->id;
- tls.tls_cert_len = srv_conf->tls_cert_len;
+ while (len > 0) {
+ tls.tls_chunk_len = len;
+ if (tls.tls_chunk_len > (MAX_IMSG_DATA_SIZE - sizeof(tls)))
+ tls.tls_chunk_len = MAX_IMSG_DATA_SIZE - sizeof(tls);
c = 0;
iov[c].iov_base = &tls;
iov[c++].iov_len = sizeof(tls);
- iov[c].iov_base = srv_conf->tls_cert;
- iov[c++].iov_len = srv_conf->tls_cert_len;
+ iov[c].iov_base = data;
+ iov[c++].iov_len = tls.tls_chunk_len;
if (proc_composev(ps, PROC_SERVER, IMSG_CFG_TLS, iov, c) != 0) {
log_warn("%s: failed to compose IMSG_CFG_TLS imsg for "
"`%s'", __func__, srv_conf->name);
return (-1);
}
+
+ tls.tls_chunk_offset += tls.tls_chunk_len;
+ data += tls.tls_chunk_len;
+ len -= tls.tls_chunk_len;
}
- if (srv_conf->tls_key_len != 0) {
- DPRINTF("%s: sending tls key \"%s[%u]\" to %s fd %d", __func__,
- srv_conf->name, srv_conf->id, ps->ps_title[PROC_SERVER],
- srv->srv_s);
+ return (0);
+}
- memset(&tls, 0, sizeof(tls));
- tls.id = srv_conf->id;
- tls.tls_key_len = srv_conf->tls_key_len;
+int
+config_setserver_tls(struct httpd *env, struct server *srv)
+{
+ struct server_config *srv_conf = &srv->srv_conf;
- c = 0;
- iov[c].iov_base = &tls;
- iov[c++].iov_len = sizeof(tls);
- iov[c].iov_base = srv_conf->tls_key;
- iov[c++].iov_len = srv_conf->tls_key_len;
+ if ((srv_conf->flags & SRVFLAG_TLS) == 0)
+ return (0);
- if (proc_composev(ps, PROC_SERVER, IMSG_CFG_TLS, iov, c) != 0) {
- log_warn("%s: failed to compose IMSG_CFG_TLS imsg for "
- "`%s'", __func__, srv_conf->name);
- return (-1);
- }
- }
+ log_debug("%s: configuring tls for %s", __func__, srv_conf->name);
+
+ if (config_settls(env, srv, TLS_CFG_CERT, "cert", srv_conf->tls_cert,
+ srv_conf->tls_cert_len) != 0)
+ return (-1);
+
+ if (config_settls(env, srv, TLS_CFG_KEY, "key", srv_conf->tls_key,
+ srv_conf->tls_key_len) != 0)
+ return (-1);
+
+ if (config_settls(env, srv, TLS_CFG_OCSP_STAPLE, "ocsp staple",
+ srv_conf->tls_ocsp_staple, srv_conf->tls_ocsp_staple_len) != 0)
+ return (-1);
return (0);
}
@@ -554,49 +583,101 @@ config_getserver(struct httpd *env, struct imsg *imsg)
return (-1);
}
-int
-config_gettls(struct httpd *env, struct imsg *imsg)
+static int
+config_gettls(struct httpd *env, struct server_config *srv_conf,
+ struct tls_config *tls_conf, const char *label, uint8_t *data, size_t len,
+ uint8_t **outdata, size_t *outlen)
{
#ifdef DEBUG
struct privsep *ps = env->sc_ps;
#endif
+
+ DPRINTF("%s: %s %d getting tls %s (%zu:%zu@%zu) for \"%s[%u]\"",
+ __func__, ps->ps_title[privsep_process], ps->ps_instance, label,
+ tls_conf->tls_len, len, tls_conf->tls_chunk_offset, srv_conf->name,
+ srv_conf->id);
+
+ if (tls_conf->tls_chunk_offset == 0) {
+ free(*outdata);
+ *outlen = 0;
+ if ((*outdata = calloc(1, tls_conf->tls_len)) == NULL)
+ goto fail;
+ *outlen = tls_conf->tls_len;
+ }
+
+ if (*outdata == NULL) {
+ log_debug("%s: tls config invalid chunk sequence", __func__);
+ goto fail;
+ }
+
+ if (*outlen != tls_conf->tls_len) {
+ log_debug("%s: tls config length mismatch (%zu != %zu)",
+ __func__, *outlen, tls_conf->tls_len);
+ goto fail;
+ }
+
+ if (len > (tls_conf->tls_len - tls_conf->tls_chunk_offset)) {
+ log_debug("%s: tls config invalid chunk length", __func__);
+ goto fail;
+ }
+
+ memcpy(*outdata + tls_conf->tls_chunk_offset, data, len);
+
+ return (0);
+
+ fail:
+ return (-1);
+}
+
+int
+config_getserver_tls(struct httpd *env, struct imsg *imsg)
+{
struct server_config *srv_conf = NULL;
struct tls_config tls_conf;
uint8_t *p = imsg->data;
- size_t s;
+ size_t len;
IMSG_SIZE_CHECK(imsg, &tls_conf);
memcpy(&tls_conf, p, sizeof(tls_conf));
- s = sizeof(tls_conf);
- if ((IMSG_DATA_SIZE(imsg) - s) <
- (tls_conf.tls_cert_len + tls_conf.tls_key_len)) {
+ len = tls_conf.tls_chunk_len;
+
+ if ((IMSG_DATA_SIZE(imsg) - sizeof(tls_conf)) < len) {
log_debug("%s: invalid message length", __func__);
goto fail;
}
+ p += sizeof(tls_conf);
+
if ((srv_conf = serverconfig_byid(tls_conf.id)) == NULL) {
log_debug("%s: server not found", __func__);
goto fail;
}
- DPRINTF("%s: %s %d tls configuration \"%s[%u]\"", __func__,
- ps->ps_title[privsep_process], ps->ps_instance,
- srv_conf->name, srv_conf->id);
+ switch (tls_conf.tls_type) {
+ case TLS_CFG_CERT:
+ if (config_gettls(env, srv_conf, &tls_conf, "cert", p, len,
+ &srv_conf->tls_cert, &srv_conf->tls_cert_len) != 0)
+ goto fail;
+ break;
- if (tls_conf.tls_cert_len != 0) {
- srv_conf->tls_cert_len = tls_conf.tls_cert_len;
- if ((srv_conf->tls_cert = get_data(p + s,
- tls_conf.tls_cert_len)) == NULL)
+ case TLS_CFG_KEY:
+ if (config_gettls(env, srv_conf, &tls_conf, "key", p, len,
+ &srv_conf->tls_key, &srv_conf->tls_key_len) != 0)
goto fail;
- s += tls_conf.tls_cert_len;
- }
- if (tls_conf.tls_key_len != 0) {
- srv_conf->tls_key_len = tls_conf.tls_key_len;
- if ((srv_conf->tls_key = get_data(p + s,
- tls_conf.tls_key_len)) == NULL)
+ break;
+
+ case TLS_CFG_OCSP_STAPLE:
+ if (config_gettls(env, srv_conf, &tls_conf, "ocsp staple",
+ p, len, &srv_conf->tls_ocsp_staple,
+ &srv_conf->tls_ocsp_staple_len) != 0)
goto fail;
- s += tls_conf.tls_key_len;
+ break;
+
+ default:
+ log_debug("%s: unknown tls config type %i\n",
+ __func__, tls_conf.tls_type);
+ goto fail;
}
return (0);
diff --git a/httpd/control.c b/httpd/control.c
index c29cd5b..7eea19a 100644
--- a/httpd/control.c
+++ b/httpd/control.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: control.c,v 1.9 2015/12/05 13:15:27 claudio Exp $ */
+/* $OpenBSD: control.c,v 1.13 2017/01/09 14:49:22 reyk Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -181,9 +181,10 @@ control_connbyfd(int fd)
{
struct ctl_conn *c;
- for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd;
- c = TAILQ_NEXT(c, entry))
- ; /* nothing */
+ TAILQ_FOREACH(c, &ctl_conns, entry) {
+ if (c->iev.ibuf.fd == fd)
+ break;
+ }
return (c);
}
@@ -273,7 +274,8 @@ control_dispatch_imsg(int fd, short event, void *arg)
"client requested notify more than once",
__func__);
imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
- 0, 0, -1, NULL, 0);
+ 0, env->sc_ps->ps_instance + 1, -1,
+ NULL, 0);
break;
}
c->flags |= CTL_CONN_NOTIFY;
@@ -287,8 +289,8 @@ control_dispatch_imsg(int fd, short event, void *arg)
proc_forward_imsg(env->sc_ps, &imsg, PROC_SERVER, -1);
memcpy(imsg.data, &verbose, sizeof(verbose));
- control_imsg_forward(&imsg);
- log_verbose(verbose);
+ control_imsg_forward(env->sc_ps, &imsg);
+ log_setverbose(verbose);
break;
default:
log_debug("%s: error handling imsg %d",
@@ -302,13 +304,13 @@ control_dispatch_imsg(int fd, short event, void *arg)
}
void
-control_imsg_forward(struct imsg *imsg)
+control_imsg_forward(struct privsep *ps, struct imsg *imsg)
{
struct ctl_conn *c;
TAILQ_FOREACH(c, &ctl_conns, entry)
if (c->flags & CTL_CONN_NOTIFY)
imsg_compose_event(&c->iev, imsg->hdr.type,
- 0, imsg->hdr.pid, -1, imsg->data,
+ 0, ps->ps_instance + 1, -1, imsg->data,
imsg->hdr.len - IMSG_HEADER_SIZE);
}
diff --git a/httpd/httpd.8 b/httpd/httpd.8
index 7084120..f65b5d5 100644
--- a/httpd/httpd.8
+++ b/httpd/httpd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: httpd.8,v 1.52 2016/06/10 18:32:40 jmc Exp $
+.\" $OpenBSD: httpd.8,v 1.53 2016/09/15 20:57:07 jmc Exp $
.\"
.\" Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: June 10 2016 $
+.Dd $Mdocdate: September 15 2016 $
.Dt HTTPD 8
.Os
.Sh NAME
@@ -85,6 +85,7 @@ Default access log file.
Default error log file.
.El
.Sh SEE ALSO
+.Xr acme-client 1 ,
.Xr httpd.conf 5 ,
.Xr slowcgi 8
.Sh HISTORY
diff --git a/httpd/httpd.c b/httpd/httpd.c
index fae7c53..6d1d1ff 100644
--- a/httpd/httpd.c
+++ b/httpd/httpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.c,v 1.56 2016/06/10 12:09:48 florian Exp $ */
+/* $OpenBSD: httpd.c,v 1.67 2017/05/28 10:37:26 benno Exp $ */
/*
* Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
@@ -16,12 +16,10 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <sys/param.h> /* nitems */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/stat.h>
-#include <sys/wait.h>
#include <sys/resource.h>
#include <netinet/in.h>
@@ -59,6 +57,8 @@ int parent_dispatch_server(int, struct privsep_proc *,
struct imsg *);
int parent_dispatch_logger(int, struct privsep_proc *,
struct imsg *);
+void parent_tls_ticket_rekey_start(struct server *);
+void parent_tls_ticket_rekey(int, short, void *);
struct httpd *httpd_env;
@@ -71,56 +71,11 @@ void
parent_sig_handler(int sig, short event, void *arg)
{
struct privsep *ps = arg;
- int die = 0, status, fail, id;
- pid_t pid;
- char *cause;
switch (sig) {
case SIGTERM:
case SIGINT:
- die = 1;
- /* FALLTHROUGH */
- case SIGCHLD:
- do {
- int len;
-
- pid = waitpid(WAIT_ANY, &status, WNOHANG);
- if (pid <= 0)
- continue;
-
- fail = 0;
- if (WIFSIGNALED(status)) {
- fail = 1;
- len = asprintf(&cause, "terminated; signal %d",
- WTERMSIG(status));
- } else if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != 0) {
- fail = 1;
- len = asprintf(&cause,
- "exited abnormally");
- } else
- len = asprintf(&cause, "exited okay");
- } else
- fatalx("unexpected cause of SIGCHLD");
-
- if (len == -1)
- fatal("asprintf");
-
- die = 1;
-
- for (id = 0; id < PROC_MAX; id++)
- if (pid == ps->ps_pid[id]) {
- if (fail)
- log_warnx("lost child: %s %s",
- ps->ps_title[id], cause);
- break;
- }
-
- free(cause);
- } while (pid > 0 || (pid == -1 && errno == EINTR));
-
- if (die)
- parent_shutdown(ps->ps_env);
+ parent_shutdown(ps->ps_env);
break;
case SIGHUP:
log_info("%s: reload requested with SIGHUP", __func__);
@@ -164,8 +119,12 @@ main(int argc, char *argv[])
struct httpd *env;
struct privsep *ps;
const char *conffile = CONF_FILE;
+ enum privsep_procid proc_id = PROC_PARENT;
+ int proc_instance = 0;
+ const char *errp, *title = NULL;
+ int argc0 = argc;
- while ((c = getopt(argc, argv, "dD:nf:v")) != -1) {
+ while ((c = getopt(argc, argv, "dD:nf:I:P:v")) != -1) {
switch (c) {
case 'd':
debug = 2;
@@ -186,6 +145,18 @@ main(int argc, char *argv[])
verbose++;
opts |= HTTPD_OPT_VERBOSE;
break;
+ case 'P':
+ title = optarg;
+ proc_id = proc_getid(procs, nitems(procs), title);
+ if (proc_id == PROC_MAX)
+ fatalx("invalid process name");
+ break;
+ case 'I':
+ proc_instance = strtonum(optarg, 0,
+ PROC_MAX_INSTANCES, &errp);
+ if (errp)
+ fatalx("invalid process instance");
+ break;
default:
usage();
}
@@ -222,18 +193,15 @@ main(int argc, char *argv[])
ps->ps_csock.cs_name = NULL;
log_init(debug, LOG_DAEMON);
- log_verbose(verbose);
-
- if (!debug && daemon(1, 0) == -1)
- err(1, "failed to daemonize");
+ log_setverbose(verbose);
if (env->sc_opts & HTTPD_OPT_NOACTION)
ps->ps_noaction = 1;
- else
- log_info("startup");
ps->ps_instances[PROC_SERVER] = env->sc_prefork_server;
- ps->ps_ninstances = env->sc_prefork_server;
+ ps->ps_instance = proc_instance;
+ if (title != NULL)
+ ps->ps_title[proc_id] = title;
if (env->sc_chroot == NULL)
env->sc_chroot = ps->ps_pw->pw_dir;
@@ -246,30 +214,34 @@ main(int argc, char *argv[])
errx(1, "malloc failed");
}
- proc_init(ps, procs, nitems(procs));
+ /* only the parent returns */
+ proc_init(ps, procs, nitems(procs), argc0, argv, proc_id);
+
log_procinit("parent");
+ if (!debug && daemon(1, 0) == -1)
+ err(1, "failed to daemonize");
- if (pledge("stdio rpath wpath cpath inet dns proc ioctl sendfd",
- NULL) == -1)
+ if (ps->ps_noaction == 0)
+ log_info("startup");
+
+ if (pledge("stdio rpath wpath cpath inet dns sendfd", NULL) == -1)
fatal("pledge");
event_init();
signal_set(&ps->ps_evsigint, SIGINT, parent_sig_handler, ps);
signal_set(&ps->ps_evsigterm, SIGTERM, parent_sig_handler, ps);
- signal_set(&ps->ps_evsigchld, SIGCHLD, parent_sig_handler, ps);
signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps);
signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps);
signal_set(&ps->ps_evsigusr1, SIGUSR1, parent_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);
signal_add(&ps->ps_evsigpipe, NULL);
signal_add(&ps->ps_evsigusr1, NULL);
- proc_listen(ps, procs, nitems(procs));
+ proc_connect(ps);
if (load_config(env->sc_conffile, env) == -1) {
proc_kill(env->sc_ps);
@@ -282,6 +254,9 @@ main(int argc, char *argv[])
exit(0);
}
+ /* initialize the TLS session id to a random key for all procs */
+ arc4random_buf(env->sc_tls_sid, sizeof(env->sc_tls_sid));
+
if (parent_configure(env) == -1)
fatalx("configuration failed");
@@ -317,6 +292,10 @@ parent_configure(struct httpd *env)
TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
if (srv->srv_conf.flags & SRVFLAG_LOCATION)
continue;
+ /* start the rekey of the tls ticket keys */
+ if (srv->srv_conf.flags & SRVFLAG_TLS &&
+ srv->srv_conf.tls_ticket_lifetime)
+ parent_tls_ticket_rekey_start(srv);
if (config_setserver(env, srv) == -1)
fatal("send server");
}
@@ -336,13 +315,14 @@ parent_configure(struct httpd *env)
continue;
cf.cf_opts = env->sc_opts;
cf.cf_flags = env->sc_flags;
+ memcpy(cf.cf_tls_sid, env->sc_tls_sid, sizeof(cf.cf_tls_sid));
proc_compose(env->sc_ps, id, IMSG_CFG_DONE, &cf, sizeof(cf));
}
ret = 0;
- config_purge(env, CONFIG_ALL);
+ config_purge(env, CONFIG_ALL & ~CONFIG_SERVERS);
return (ret);
}
@@ -427,7 +407,8 @@ parent_shutdown(struct httpd *env)
int
parent_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
{
- struct httpd *env = p->p_env;
+ struct privsep *ps = p->p_ps;
+ struct httpd *env = ps->ps_env;
switch (imsg->hdr.type) {
case IMSG_CFG_DONE:
@@ -443,7 +424,8 @@ parent_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
int
parent_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
{
- struct httpd *env = p->p_env;
+ struct privsep *ps = p->p_ps;
+ struct httpd *env = ps->ps_env;
unsigned int v;
char *str = NULL;
@@ -479,6 +461,38 @@ parent_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
return (0);
}
+void
+parent_tls_ticket_rekey_start(struct server *srv)
+{
+ struct timeval tv;
+
+ server_generate_ticket_key(&srv->srv_conf);
+
+ evtimer_set(&srv->srv_evt, parent_tls_ticket_rekey, srv);
+ timerclear(&tv);
+ tv.tv_sec = srv->srv_conf.tls_ticket_lifetime / 4;
+ evtimer_add(&srv->srv_evt, &tv);
+}
+
+void
+parent_tls_ticket_rekey(int fd, short events, void *arg)
+{
+ struct server *srv = arg;
+ struct timeval tv;
+
+ server_generate_ticket_key(&srv->srv_conf);
+ proc_compose_imsg(httpd_env->sc_ps, PROC_SERVER, -1,
+ IMSG_TLSTICKET_REKEY, -1, -1, &srv->srv_conf.tls_ticket_key,
+ sizeof(srv->srv_conf.tls_ticket_key));
+ explicit_bzero(&srv->srv_conf.tls_ticket_key,
+ sizeof(srv->srv_conf.tls_ticket_key));
+
+ evtimer_set(&srv->srv_evt, parent_tls_ticket_rekey, srv);
+ timerclear(&tv);
+ tv.tv_sec = srv->srv_conf.tls_ticket_lifetime / 4;
+ evtimer_add(&srv->srv_evt, &tv);
+}
+
/*
* Utility functions
*/
@@ -777,7 +791,7 @@ socket_rlimit(int maxfd)
struct rlimit rl;
if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
- fatal("socket_rlimit: failed to get resource limit");
+ fatal("%s: failed to get resource limit", __func__);
log_debug("%s: max open files %llu", __func__, rl.rlim_max);
/*
@@ -789,7 +803,7 @@ socket_rlimit(int maxfd)
else
rl.rlim_cur = MAXIMUM(rl.rlim_max, (rlim_t)maxfd);
if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
- fatal("socket_rlimit: failed to set resource limit");
+ fatal("%s: failed to set resource limit", __func__);
}
char *
diff --git a/httpd/httpd.conf.5 b/httpd/httpd.conf.5
index 2bd3ec7..a3c9762 100644
--- a/httpd/httpd.conf.5
+++ b/httpd/httpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: httpd.conf.5,v 1.73 2016/05/09 19:36:54 tj Exp $
+.\" $OpenBSD: httpd.conf.5,v 1.84 2017/08/11 20:30:45 jmc Exp $
.\"
.\" Copyright (c) 2014, 2015 Reyk Floeter <reyk@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: May 9 2016 $
+.Dd $Mdocdate: August 11 2017 $
.Dt HTTPD.CONF 5
.Os
.Sh NAME
@@ -221,6 +221,8 @@ The configured IP address of the server.
The configured TCP server port of the server.
.It Ic $SERVER_NAME
The name of the server.
+.It Ic $HTTP_HOST
+The host from the HTTP Host header.
.It Pf % Ar n
The capture index
.Ar n
@@ -240,8 +242,14 @@ Set the maximum number of requests per persistent HTTP connection.
Persistent connections are negotiated using the Keep-Alive header in
HTTP/1.0 and enabled by default in HTTP/1.1.
The default maximum number of requests per connection is 100.
+.It Ic request timeout Ar seconds
+Specify the inactivity timeout for HTTP operations between client and server,
+for example the maximum time to wait for a request from the client.
+The default timeout is 60 seconds (1 minute).
+The maximum is 2147483647 seconds (68 years).
.It Ic timeout Ar seconds
-Specify the inactivity timeout in seconds for accepted sessions.
+Specify the inactivity timeout in seconds for accepted sessions,
+for example the maximum time to wait for I/O from the FastCGI backend.
The default timeout is 600 seconds (10 minutes).
The maximum is 2147483647 seconds (68 years).
.El
@@ -358,6 +366,11 @@ Specify server configuration rules for a specific location.
The
.Ar path
argument will be matched against the request path with shell globbing rules.
+In case of multiple location statements in the same context, the
+first matching location statement will be put into effect, while all
+later ones will be ignored.
+Therefore it is advisable to match for more specific paths first
+and for generic ones later on.
A location section may include most of the server configuration rules
except
.Ic alias ,
@@ -519,10 +532,12 @@ Valid parameter values are none, legacy and auto.
For legacy a fixed key length of 1024 bits is used, whereas for auto the key
length is determined automatically.
The default is none, which disables DHE cipher suites.
-.It Ic ecdhe Ar curve
-Specify the ECDHE curve to use for ECDHE cipher suites.
-Valid parameter values are none, auto and the short name of any known curve.
-The default is auto.
+.It Ic ecdhe Ar curves
+Specify a comma separated list of elliptic curves to use for ECDHE cipher suites,
+in order of preference.
+The special value of "default" will use the default curves; see
+.Xr tls_config_set_ecdhecurves 3
+for further details.
.It Ic key Ar file
Specify the private key to use for this server.
The
@@ -533,6 +548,16 @@ root directory of
.Nm httpd .
The default is
.Pa /etc/ssl/private/server.key .
+.It Ic ocsp Ar file
+Specify an OCSP response to be stapled during TLS handshakes
+with this server.
+The
+.Ar file
+should contain a DER-format OCSP response retrieved from an
+OCSP server for the
+.Ar certificate
+in use.
+The default is to not use OCSP stapling.
.It Ic protocols Ar string
Specify the TLS protocols to enable for this server.
If not specified, the value
@@ -541,6 +566,13 @@ will be used (secure protocols; TLSv1.2-only).
Refer to the
.Xr tls_config_parse_protocols 3
function for other valid protocol string values.
+.It Ic ticket Ic lifetime Ar seconds
+Enable TLS session tickets with a
+.Ar seconds
+session lifetime.
+It is possible to set
+.Ar seconds
+to default to use the httpd default timeout of 2 hours.
.El
.El
.Sh TYPES
@@ -560,6 +592,7 @@ will use built-in media types for
.Ar image/gif ,
.Ar image/png ,
.Ar image/jpeg ,
+.Ar image/svg+xml ,
and
.Ar application/javascript .
.Pp
@@ -659,6 +692,7 @@ server "www.example.com" {
.Xr htpasswd 1 ,
.Xr patterns 7 ,
.Xr httpd 8 ,
+.Xr ocspcheck 8 ,
.Xr slowcgi 8
.Sh AUTHORS
.An -nosplit
diff --git a/httpd/httpd.h b/httpd/httpd.h
index 595cce9..05cbb8e 100644
--- a/httpd/httpd.h
+++ b/httpd/httpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.h,v 1.106 2016/08/15 16:12:34 jsing Exp $ */
+/* $OpenBSD: httpd.h,v 1.134 2017/08/11 18:48:56 jsing Exp $ */
/*
* Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -28,6 +28,7 @@
#include <sys/time.h>
#include <net/if.h>
+#include <netinet/in.h>
#include <stdarg.h>
#include <limits.h>
@@ -38,6 +39,10 @@
#include "patterns.h"
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
#define CONF_FILE "/etc/httpd.conf"
#define HTTPD_SOCKET "/var/run/httpd.sock"
#define HTTPD_USER "www"
@@ -54,14 +59,14 @@
#define HTTPD_TLS_KEY "/etc/ssl/private/server.key"
#define HTTPD_TLS_CIPHERS "compat"
#define HTTPD_TLS_DHE_PARAMS "none"
-#define HTTPD_TLS_ECDHE_CURVE "auto"
+#define HTTPD_TLS_ECDHE_CURVES "default"
#define FD_RESERVE 5
#define SERVER_MAX_CLIENTS 1024
#define SERVER_TIMEOUT 600
+#define SERVER_REQUESTTIMEOUT 60
#define SERVER_CACHESIZE -1 /* use default size */
#define SERVER_NUMPROC 3
-#define SERVER_MAXPROC 32
#define SERVER_MAXHEADERLENGTH 8192
#define SERVER_MAXREQUESTS 100 /* max requests per connection */
#define SERVER_MAXREQUESTBODY 1048576 /* 1M */
@@ -70,6 +75,10 @@
#define SERVER_MAX_PREFETCH 256
#define SERVER_MIN_PREFETCHED 32
#define SERVER_HSTS_DEFAULT_AGE 31536000
+#define SERVER_MAX_RANGES 4
+#define SERVER_DEF_TLS_LIFETIME (2 * 3600)
+#define SERVER_MIN_TLS_LIFETIME (60)
+#define SERVER_MAX_TLS_LIFETIME (24 * 3600)
#define MEDIATYPE_NAMEMAX 128 /* file name extension */
#define MEDIATYPE_TYPEMAX 64 /* length of type/subtype */
@@ -82,12 +91,16 @@
#define FCGI_CONTENT_SIZE 65535
+#define PROC_PARENT_SOCK_FILENO 3
+#define PROC_MAX_INSTANCES 32
+
enum httpchunk {
TOREAD_UNLIMITED = -1,
TOREAD_HTTP_HEADER = -2,
TOREAD_HTTP_CHUNK_LENGTH = -3,
TOREAD_HTTP_CHUNK_TRAILER = -4,
- TOREAD_HTTP_NONE = -5
+ TOREAD_HTTP_NONE = -5,
+ TOREAD_HTTP_RANGE = TOREAD_HTTP_CHUNK_LENGTH
};
#if DEBUG
@@ -99,6 +112,7 @@ enum httpchunk {
struct ctl_flags {
uint8_t cf_opts;
uint32_t cf_flags;
+ uint8_t cf_tls_sid[TLS_MAX_SESSION_ID_LENGTH];
};
enum key_type {
@@ -178,6 +192,7 @@ struct imsgev {
fatalx("bad length imsg received"); \
} while (0)
#define IMSG_DATA_SIZE(imsg) ((imsg)->hdr.len - IMSG_HEADER_SIZE)
+#define MAX_IMSG_DATA_SIZE (MAX_IMSGSIZE - IMSG_HEADER_SIZE)
struct ctl_conn {
TAILQ_ENTRY(ctl_conn) entry;
@@ -194,6 +209,7 @@ enum imsg_type {
IMSG_CTL_OK,
IMSG_CTL_FAIL,
IMSG_CTL_VERBOSE,
+ IMSG_CTL_PROCFD,
IMSG_CTL_RESET,
IMSG_CTL_SHUTDOWN,
IMSG_CTL_RELOAD,
@@ -208,7 +224,8 @@ enum imsg_type {
IMSG_CFG_DONE,
IMSG_LOG_ACCESS,
IMSG_LOG_ERROR,
- IMSG_LOG_OPEN
+ IMSG_LOG_OPEN,
+ IMSG_TLSTICKET_REKEY
};
enum privsep_procid {
@@ -232,11 +249,9 @@ struct privsep {
struct imsgev *ps_ievs[PROC_MAX];
const char *ps_title[PROC_MAX];
- pid_t ps_pid[PROC_MAX];
uint8_t ps_what[PROC_MAX];
unsigned int ps_instances[PROC_MAX];
- unsigned int ps_ninstances;
unsigned int ps_instance;
struct control_sock ps_csock;
@@ -260,13 +275,17 @@ struct privsep_proc {
enum privsep_procid p_id;
int (*p_cb)(int, struct privsep_proc *,
struct imsg *);
- pid_t (*p_init)(struct privsep *,
+ void (*p_init)(struct privsep *,
struct privsep_proc *);
- void (*p_shutdown)(void);
- unsigned int p_instance;
const char *p_chroot;
struct privsep *p_ps;
- struct httpd *p_env;
+ void (*p_shutdown)(void);
+ struct passwd *p_pw;
+};
+
+struct privsep_fd {
+ enum privsep_procid pf_procid;
+ unsigned int pf_instance;
};
enum fcgistate {
@@ -275,6 +294,33 @@ enum fcgistate {
FCGI_READ_PADDING
};
+struct fcgi_data {
+ enum fcgistate state;
+ int toread;
+ int padding_len;
+ int type;
+ int chunked;
+ int end;
+ int status;
+ int headersdone;
+};
+
+struct range {
+ off_t start;
+ off_t end;
+};
+
+struct range_data {
+ struct range range[SERVER_MAX_RANGES];
+ int range_count;
+ int range_index;
+ off_t range_toread;
+
+ /* For the Content headers in each part */
+ struct media_type *range_media;
+ size_t range_total;
+};
+
struct client {
uint32_t clt_id;
pid_t clt_pid;
@@ -293,6 +339,7 @@ struct client {
void *clt_descreq;
void *clt_descresp;
int clt_sndbufsiz;
+ uint64_t clt_boundary;
int clt_fd;
struct tls *clt_tls_ctx;
@@ -301,17 +348,15 @@ struct client {
off_t clt_toread;
size_t clt_headerlen;
+ int clt_headersdone;
unsigned int clt_persist;
+ unsigned int clt_pipelining;
int clt_line;
int clt_done;
int clt_chunk;
int clt_inflight;
- enum fcgistate clt_fcgi_state;
- int clt_fcgi_toread;
- int clt_fcgi_padding_len;
- int clt_fcgi_type;
- int clt_fcgi_chunked;
- int clt_fcgi_end;
+ struct range_data clt_ranges;
+ struct fcgi_data clt_fcgi;
char *clt_remote_user;
struct evbuffer *clt_srvevb;
@@ -405,6 +450,12 @@ struct auth {
};
TAILQ_HEAD(serverauth, auth);
+struct server_tls_ticket {
+ uint32_t tt_id;
+ uint32_t tt_keyrev;
+ unsigned char tt_key[TLS_TICKET_KEY_SIZE];
+};
+
struct server_config {
uint32_t id;
uint32_t parent_id;
@@ -421,6 +472,7 @@ struct server_config {
struct sockaddr_storage ss;
int prefixlen;
struct timeval timeout;
+ struct timeval requesttimeout;
uint32_t maxrequests;
size_t maxrequestbody;
@@ -429,11 +481,16 @@ struct server_config {
char *tls_cert_file;
char tls_ciphers[NAME_MAX];
char tls_dhe_params[NAME_MAX];
- char tls_ecdhe_curve[NAME_MAX];
+ char tls_ecdhe_curves[NAME_MAX];
uint8_t *tls_key;
size_t tls_key_len;
char *tls_key_file;
uint32_t tls_protocols;
+ uint8_t *tls_ocsp_staple;
+ size_t tls_ocsp_staple_len;
+ char *tls_ocsp_staple_file;
+ struct server_tls_ticket tls_ticket_key;
+ int tls_ticket_lifetime;
uint32_t flags;
int strip;
@@ -462,11 +519,19 @@ struct server_config {
};
TAILQ_HEAD(serverhosts, server_config);
+enum tls_config_type {
+ TLS_CFG_CERT,
+ TLS_CFG_KEY,
+ TLS_CFG_OCSP_STAPLE,
+};
+
struct tls_config {
uint32_t id;
- size_t tls_cert_len;
- size_t tls_key_len;
+ enum tls_config_type tls_type;
+ size_t tls_len;
+ size_t tls_chunk_len;
+ size_t tls_chunk_offset;
};
struct server {
@@ -496,6 +561,8 @@ struct httpd {
char *sc_chroot;
char *sc_logdir;
+ uint8_t sc_tls_sid[TLS_MAX_SESSION_ID_LENGTH];
+
struct serverlist *sc_servers;
struct mediatypes *sc_mediatypes;
struct media_type sc_default_type;
@@ -513,7 +580,7 @@ int control_init(struct privsep *, struct control_sock *);
int control_listen(struct control_sock *);
void control_cleanup(struct control_sock *);
void control_dispatch_imsg(int, short, void *);
-void control_imsg_forward(struct imsg *);
+void control_imsg_forward(struct privsep *, struct imsg *);
struct ctl_conn *
control_connbyfd(int);
@@ -525,9 +592,11 @@ int load_config(const char *, struct httpd *);
int cmdline_symset(char *);
/* server.c */
-pid_t server(struct privsep *, struct privsep_proc *);
-int server_tls_cmp(struct server *, struct server *);
+void server(struct privsep *, struct privsep_proc *);
+int server_tls_cmp(struct server *, struct server *, int);
int server_tls_load_keypair(struct server *);
+int server_tls_load_ocsp(struct server *);
+void server_generate_ticket_key(struct server_config *);
int server_privinit(struct server *);
void server_purge(struct server *);
void serverconfig_free(struct server_config *);
@@ -568,7 +637,7 @@ SPLAY_PROTOTYPE(client_tree, client, clt_nodes, server_client_cmp);
/* server_http.c */
void server_http_init(struct server *);
-void server_http(struct httpd *);
+void server_http(void);
int server_httpdesc_init(struct client *);
void server_read_http(struct bufferevent *, void *);
void server_abort_http(struct client *, unsigned int, const char *);
@@ -580,12 +649,13 @@ const char
*server_httperror_byid(unsigned int);
void server_read_httpcontent(struct bufferevent *, void *);
void server_read_httpchunks(struct bufferevent *, void *);
+void server_read_httprange(struct bufferevent *, void *);
int server_writeheader_http(struct client *clt, struct kv *, void *);
int server_headers(struct client *, void *,
int (*)(struct client *, struct kv *, void *), void *);
int server_writeresponse_http(struct client *);
-int server_response_http(struct client *, unsigned int, struct media_type *,
- off_t, time_t);
+int server_response_http(struct client *, unsigned int,
+ struct media_type *, off_t, time_t);
void server_reset_http(struct client *);
void server_close_http(struct client *);
int server_response(struct httpd *, struct client *);
@@ -618,9 +688,6 @@ const char *canonicalize_host(const char *, char *, size_t);
const char *canonicalize_path(const char *, char *, size_t);
size_t path_info(char *);
char *escape_html(const char *);
-void imsg_event_add(struct imsgev *);
-int imsg_compose_event(struct imsgev *, uint16_t, uint32_t,
- pid_t, int, void *, uint16_t);
void socket_rlimit(int);
char *evbuffer_getline(struct evbuffer *);
char *get_string(uint8_t *, size_t);
@@ -663,10 +730,13 @@ const char *print_time(struct timeval *, struct timeval *, char *, size_t);
const char *printb_flags(const uint32_t, const char *);
void getmonotime(struct timeval *);
+extern struct httpd *httpd_env;
+
/* log.c */
void log_init(int, int);
void log_procinit(const char *);
-void log_verbose(int);
+void log_setverbose(int);
+int log_getverbose(void);
void log_warn(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void log_warnx(const char *, ...)
@@ -685,11 +755,14 @@ __dead void fatalx(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
/* proc.c */
-void proc_init(struct privsep *, struct privsep_proc *, unsigned int);
+enum privsep_procid
+ proc_getid(struct privsep_proc *, unsigned int, const char *);
+void proc_init(struct privsep *, struct privsep_proc *, unsigned int,
+ int, char **, enum privsep_procid);
void proc_kill(struct privsep *);
-void proc_listen(struct privsep *, struct privsep_proc *, size_t);
+void proc_connect(struct privsep *);
void proc_dispatch(int, short event, void *);
-pid_t proc_run(struct privsep *, struct privsep_proc *,
+void proc_run(struct privsep *, struct privsep_proc *,
struct privsep_proc *, unsigned int,
void (*)(struct privsep *, struct privsep_proc *, void *), void *);
void proc_range(struct privsep *, enum privsep_procid, int *, int *);
@@ -707,6 +780,7 @@ struct imsgbuf *
proc_ibuf(struct privsep *, enum privsep_procid, int);
struct imsgev *
proc_iev(struct privsep *, enum privsep_procid, int);
+int proc_flush_imsg(struct privsep *, enum privsep_procid, int);
void imsg_event_add(struct imsgev *);
int imsg_compose_event(struct imsgev *, uint16_t, uint32_t,
pid_t, int, void *, uint16_t);
@@ -720,16 +794,16 @@ int config_setreset(struct httpd *, unsigned int);
int config_getreset(struct httpd *, struct imsg *);
int config_getcfg(struct httpd *, struct imsg *);
int config_setserver(struct httpd *, struct server *);
-int config_settls(struct httpd *, struct server *);
+int config_setserver_tls(struct httpd *, struct server *);
int config_getserver(struct httpd *, struct imsg *);
-int config_gettls(struct httpd *, struct imsg *);
+int config_getserver_tls(struct httpd *, struct imsg *);
int config_setmedia(struct httpd *, struct media_type *);
int config_getmedia(struct httpd *, struct imsg *);
int config_setauth(struct httpd *, struct auth *);
int config_getauth(struct httpd *, struct imsg *);
/* logger.c */
-pid_t logger(struct privsep *, struct privsep_proc *);
+void logger(struct privsep *, struct privsep_proc *);
int logger_open_priv(struct imsg *);
#endif /* _HTTPD_H */
diff --git a/httpd/log.c b/httpd/log.c
index 1f6ff49..b7e25a6 100644
--- a/httpd/log.c
+++ b/httpd/log.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: log.c,v 1.10 2015/12/07 12:13:51 reyk Exp $ */
+/* $OpenBSD: log.c,v 1.14 2017/03/21 12:06:55 bluhm Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -24,13 +24,14 @@
#include <errno.h>
#include <time.h>
-int debug;
-int verbose;
+static int debug;
+static int verbose;
const char *log_procname;
void log_init(int, int);
void log_procinit(const char *);
-void log_verbose(int);
+void log_setverbose(int);
+int log_getverbose(void);
void log_warn(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void log_warnx(const char *, ...)
@@ -71,11 +72,17 @@ log_procinit(const char *procname)
}
void
-log_verbose(int v)
+log_setverbose(int v)
{
verbose = v;
}
+int
+log_getverbose(void)
+{
+ return (verbose);
+}
+
void
logit(int pri, const char *fmt, ...)
{
@@ -90,6 +97,7 @@ void
vlog(int pri, const char *fmt, va_list ap)
{
char *nfmt;
+ int saved_errno = errno;
if (debug) {
/* best effort in out of mem situations */
@@ -103,31 +111,36 @@ vlog(int pri, const char *fmt, va_list ap)
fflush(stderr);
} else
vsyslog(pri, fmt, ap);
-}
+ errno = saved_errno;
+}
void
log_warn(const char *emsg, ...)
{
- char *nfmt;
- va_list ap;
+ char *nfmt;
+ va_list ap;
+ int saved_errno = errno;
/* best effort to even work in out of memory situations */
if (emsg == NULL)
- logit(LOG_CRIT, "%s", strerror(errno));
+ logit(LOG_ERR, "%s", strerror(saved_errno));
else {
va_start(ap, emsg);
- if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ if (asprintf(&nfmt, "%s: %s", emsg,
+ strerror(saved_errno)) == -1) {
/* we tried it... */
- vlog(LOG_CRIT, emsg, ap);
- logit(LOG_CRIT, "%s", strerror(errno));
+ vlog(LOG_ERR, emsg, ap);
+ logit(LOG_ERR, "%s", strerror(saved_errno));
} else {
- vlog(LOG_CRIT, nfmt, ap);
+ vlog(LOG_ERR, nfmt, ap);
free(nfmt);
}
va_end(ap);
}
+
+ errno = saved_errno;
}
void
@@ -136,7 +149,7 @@ log_warnx(const char *emsg, ...)
va_list ap;
va_start(ap, emsg);
- vlog(LOG_CRIT, emsg, ap);
+ vlog(LOG_ERR, emsg, ap);
va_end(ap);
}
@@ -163,7 +176,7 @@ log_debug(const char *emsg, ...)
}
static void
-vfatal(const char *emsg, va_list ap)
+vfatalc(int code, const char *emsg, va_list ap)
{
static char s[BUFSIZ];
const char *sep;
@@ -175,9 +188,9 @@ vfatal(const char *emsg, va_list ap)
s[0] = '\0';
sep = "";
}
- if (errno)
+ if (code)
logit(LOG_CRIT, "%s: %s%s%s",
- log_procname, s, sep, strerror(errno));
+ log_procname, s, sep, strerror(code));
else
logit(LOG_CRIT, "%s%s%s", log_procname, sep, s);
}
@@ -188,7 +201,7 @@ fatal(const char *emsg, ...)
va_list ap;
va_start(ap, emsg);
- vfatal(emsg, ap);
+ vfatalc(errno, emsg, ap);
va_end(ap);
exit(1);
}
@@ -198,9 +211,8 @@ fatalx(const char *emsg, ...)
{
va_list ap;
- errno = 0;
va_start(ap, emsg);
- vfatal(emsg, ap);
+ vfatalc(0, emsg, ap);
va_end(ap);
exit(1);
}
diff --git a/httpd/logger.c b/httpd/logger.c
index a212fb1..6d469b2 100644
--- a/httpd/logger.c
+++ b/httpd/logger.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: logger.c,v 1.15 2015/12/02 15:13:00 reyk Exp $ */
+/* $OpenBSD: logger.c,v 1.20 2016/09/01 10:59:38 reyk Exp $ */
/*
* Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
@@ -16,7 +16,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <sys/param.h> /* nitems */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/uio.h>
@@ -44,8 +43,6 @@ void logger_init(struct privsep *, struct privsep_proc *p, void *);
int logger_start(void);
int logger_log(struct imsg *);
-static struct httpd *env = NULL;
-int proc_id;
static uint32_t last_log_id = 0;
static struct privsep_proc procs[] = {
@@ -53,18 +50,17 @@ static struct privsep_proc procs[] = {
{ "server", PROC_SERVER, logger_dispatch_server }
};
-pid_t
+void
logger(struct privsep *ps, struct privsep_proc *p)
{
- env = ps->ps_env;
- return (proc_run(ps, p, procs, nitems(procs), logger_init, NULL));
+ proc_run(ps, p, procs, nitems(procs), logger_init, NULL);
}
void
logger_shutdown(void)
{
logger_close();
- config_purge(env, CONFIG_ALL);
+ config_purge(httpd_env, CONFIG_ALL);
}
void
@@ -76,9 +72,6 @@ logger_init(struct privsep *ps, struct privsep_proc *p, void *arg)
if (config_init(ps->ps_env) == -1)
fatal("failed to initialize configuration");
- /* Set to current prefork id */
- proc_id = p->p_instance;
-
/* We use a custom shutdown callback */
p->p_shutdown = logger_shutdown;
@@ -121,7 +114,7 @@ logger_open_file(const char *name)
iov[1].iov_base = log->log_name;
iov[1].iov_len = strlen(log->log_name) + 1;
- if (proc_composev(env->sc_ps, PROC_PARENT, IMSG_LOG_OPEN,
+ if (proc_composev(httpd_env->sc_ps, PROC_PARENT, IMSG_LOG_OPEN,
iov, 2) != 0) {
log_warn("%s: failed to compose IMSG_LOG_OPEN imsg", __func__);
goto err;
@@ -174,7 +167,7 @@ logger_open_priv(struct imsg *imsg)
if ((size_t)snprintf(name, sizeof(name), "/%s", p) >= sizeof(name))
return (-1);
- if ((len = strlcpy(path, env->sc_logdir, sizeof(path)))
+ if ((len = strlcpy(path, httpd_env->sc_logdir, sizeof(path)))
>= sizeof(path))
return (-1);
@@ -191,8 +184,8 @@ logger_open_priv(struct imsg *imsg)
return (-1);
}
- proc_compose_imsg(env->sc_ps, PROC_LOGGER, -1, IMSG_LOG_OPEN, -1, fd,
- &id, sizeof(id));
+ proc_compose_imsg(httpd_env->sc_ps, PROC_LOGGER, -1,
+ IMSG_LOG_OPEN, -1, fd, &id, sizeof(id));
DPRINTF("%s: opened log file %s, fd %d", __func__, path, fd);
@@ -286,17 +279,17 @@ logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{
switch (imsg->hdr.type) {
case IMSG_CFG_SERVER:
- config_getserver(env, imsg);
+ config_getserver(httpd_env, imsg);
break;
case IMSG_CFG_DONE:
- config_getcfg(env, imsg);
+ config_getcfg(httpd_env, imsg);
break;
case IMSG_CTL_START:
case IMSG_CTL_REOPEN:
logger_start();
break;
case IMSG_CTL_RESET:
- config_getreset(env, imsg);
+ config_getreset(httpd_env, imsg);
break;
case IMSG_LOG_OPEN:
return (logger_open_fd(imsg));
diff --git a/httpd/parse.y b/httpd/parse.y
index 6900bc6..203ddd1 100644
--- a/httpd/parse.y
+++ b/httpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.80 2016/08/15 16:12:34 jsing Exp $ */
+/* $OpenBSD: parse.y,v 1.91 2017/08/11 18:48:56 jsing Exp $ */
/*
* Copyright (c) 2007 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -130,10 +130,10 @@ typedef struct {
%}
%token ACCESS ALIAS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT CIPHERS COMMON
-%token COMBINED CONNECTION DHE DIRECTORY ECDHE ERR FCGI INDEX IP KEY LISTEN
-%token LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY ON PORT PREFORK PROTOCOLS
-%token REQUEST REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP TIMEOUT
-%token TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD
+%token COMBINED CONNECTION DHE DIRECTORY ECDHE ERR FCGI INDEX IP KEY LIFETIME
+%token LISTEN LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY OCSP ON PORT PREFORK
+%token PROTOCOLS REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP TICKET
+%token TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD REQUEST
%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS
%token <v.string> STRING
%token <v.number> NUMBER
@@ -193,7 +193,7 @@ opttls : /*empty*/ { $$ = 0; }
main : PREFORK NUMBER {
if (loadcfg)
break;
- if ($2 <= 0 || $2 > SERVER_MAXPROC) {
+ if ($2 <= 0 || $2 > PROC_MAX_INSTANCES) {
yyerror("invalid number of preforked "
"servers: %lld", $2);
YYERROR;
@@ -245,6 +245,8 @@ server : SERVER optmatch STRING {
s->srv_conf.parent_id = s->srv_conf.id;
s->srv_s = -1;
s->srv_conf.timeout.tv_sec = SERVER_TIMEOUT;
+ s->srv_conf.requesttimeout.tv_sec =
+ SERVER_REQUESTTIMEOUT;
s->srv_conf.maxrequests = SERVER_MAXREQUESTS;
s->srv_conf.maxrequestbody = SERVER_MAXREQUESTBODY;
s->srv_conf.flags = SRVFLAG_LOG;
@@ -264,9 +266,9 @@ server : SERVER optmatch STRING {
strlcpy(s->srv_conf.tls_dhe_params,
HTTPD_TLS_DHE_PARAMS,
sizeof(s->srv_conf.tls_dhe_params));
- strlcpy(s->srv_conf.tls_ecdhe_curve,
- HTTPD_TLS_ECDHE_CURVE,
- sizeof(s->srv_conf.tls_ecdhe_curve));
+ strlcpy(s->srv_conf.tls_ecdhe_curves,
+ HTTPD_TLS_ECDHE_CURVES,
+ sizeof(s->srv_conf.tls_ecdhe_curves));
s->srv_conf.hsts_max_age = SERVER_HSTS_DEFAULT_AGE;
@@ -314,7 +316,7 @@ server : SERVER optmatch STRING {
free(srv);
YYERROR;
}
- if (server_tls_cmp(s, srv) != 0) {
+ if (server_tls_cmp(s, srv, 0) != 0) {
yyerror("server \"%s\": tls "
"configuration mismatch on same "
"address/port",
@@ -342,6 +344,14 @@ server : SERVER optmatch STRING {
YYERROR;
}
+ if (server_tls_load_ocsp(srv) == -1) {
+ yyerror("server \"%s\": failed to load "
+ "ocsp staple", srv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ free(srv);
+ YYERROR;
+ }
+
DPRINTF("adding server \"%s[%u]\"",
srv->srv_conf.name, srv->srv_conf.id);
@@ -678,6 +688,10 @@ conflags : TIMEOUT timeout {
memcpy(&srv_conf->timeout, &$2,
sizeof(struct timeval));
}
+ | REQUEST TIMEOUT timeout {
+ memcpy(&srv_conf->requesttimeout, &$3,
+ sizeof(struct timeval));
+ }
| MAXIMUM REQUESTS NUMBER {
srv_conf->maxrequests = $3;
}
@@ -706,6 +720,13 @@ tlsopts : CERTIFICATE STRING {
fatal("out of memory");
free($2);
}
+ | OCSP STRING {
+ free(srv_conf->tls_ocsp_staple_file);
+ if ((srv_conf->tls_ocsp_staple_file = strdup($2))
+ == NULL)
+ fatal("out of memory");
+ free($2);
+ }
| CIPHERS STRING {
if (strlcpy(srv_conf->tls_ciphers, $2,
sizeof(srv_conf->tls_ciphers)) >=
@@ -727,9 +748,9 @@ tlsopts : CERTIFICATE STRING {
free($2);
}
| ECDHE STRING {
- if (strlcpy(srv_conf->tls_ecdhe_curve, $2,
- sizeof(srv_conf->tls_ecdhe_curve)) >=
- sizeof(srv_conf->tls_ecdhe_curve)) {
+ if (strlcpy(srv_conf->tls_ecdhe_curves, $2,
+ sizeof(srv_conf->tls_ecdhe_curves)) >=
+ sizeof(srv_conf->tls_ecdhe_curves)) {
yyerror("ecdhe too long");
free($2);
YYERROR;
@@ -745,6 +766,23 @@ tlsopts : CERTIFICATE STRING {
}
free($2);
}
+ | TICKET LIFETIME DEFAULT {
+ srv_conf->tls_ticket_lifetime = SERVER_DEF_TLS_LIFETIME;
+ }
+ | TICKET LIFETIME NUMBER {
+ if ($3 != 0 && $3 < SERVER_MIN_TLS_LIFETIME) {
+ yyerror("ticket lifetime too small");
+ YYERROR;
+ }
+ if ($3 > SERVER_MAX_TLS_LIFETIME) {
+ yyerror("ticket lifetime too large");
+ YYERROR;
+ }
+ srv_conf->tls_ticket_lifetime = $3;
+ }
+ | NO TICKET {
+ srv_conf->tls_ticket_lifetime = 0;
+ }
;
root : ROOT rootflags
@@ -1197,6 +1235,7 @@ lookup(char *s)
{ "index", INDEX },
{ "ip", IP },
{ "key", KEY },
+ { "lifetime", LIFETIME },
{ "listen", LISTEN },
{ "location", LOCATION },
{ "log", LOG },
@@ -1206,6 +1245,7 @@ lookup(char *s)
{ "max-age", MAXAGE },
{ "no", NO },
{ "nodelay", NODELAY },
+ { "ocsp", OCSP },
{ "on", ON },
{ "pass", PASS },
{ "port", PORT },
@@ -1224,6 +1264,7 @@ lookup(char *s)
{ "subdomains", SUBDOMAINS },
{ "syslog", SYSLOG },
{ "tcp", TCP },
+ { "ticket", TICKET },
{ "timeout", TIMEOUT },
{ "tls", TLS },
{ "type", TYPE },
@@ -1582,8 +1623,7 @@ parse_config(const char *filename, struct httpd *x_conf)
endprotoent();
/* Free macros */
- for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
- next = TAILQ_NEXT(sym, entry);
+ TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
if (!sym->persist) {
free(sym->nam);
free(sym->val);
@@ -1673,9 +1713,10 @@ symset(const char *nam, const char *val, int persist)
{
struct sym *sym;
- for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
- sym = TAILQ_NEXT(sym, entry))
- ; /* nothing */
+ TAILQ_FOREACH(sym, &symhead, entry) {
+ if (strcmp(nam, sym->nam) == 0)
+ break;
+ }
if (sym != NULL) {
if (sym->persist == 1)
@@ -1734,11 +1775,12 @@ symget(const char *nam)
{
struct sym *sym;
- TAILQ_FOREACH(sym, &symhead, entry)
+ TAILQ_FOREACH(sym, &symhead, entry) {
if (strcmp(nam, sym->nam) == 0) {
sym->used = 1;
return (sym->val);
}
+ }
return (NULL);
}
@@ -2007,10 +2049,11 @@ server_inherit(struct server *src, struct server_config *alias,
if ((dst->srv_conf.tls_key_file =
strdup(src->srv_conf.tls_key_file)) == NULL)
fatal("out of memory");
- dst->srv_conf.tls_cert = NULL;
- dst->srv_conf.tls_key = NULL;
- dst->srv_conf.tls_cert_len = 0;
- dst->srv_conf.tls_key_len = 0;
+ if (src->srv_conf.tls_ocsp_staple_file != NULL) {
+ if ((dst->srv_conf.tls_ocsp_staple_file =
+ strdup(src->srv_conf.tls_ocsp_staple_file)) == NULL)
+ fatal("out of memory");
+ }
if (src->srv_conf.return_uri != NULL &&
(dst->srv_conf.return_uri =
@@ -2050,6 +2093,14 @@ server_inherit(struct server *src, struct server_config *alias,
return (NULL);
}
+ if (server_tls_load_ocsp(dst) == -1) {
+ yyerror("failed to load ocsp staple "
+ "for server %s", dst->srv_conf.name);
+ serverconfig_free(&dst->srv_conf);
+ free(dst);
+ return (NULL);
+ }
+
/* Check if the new server already exists */
if (server_match(dst, 1) != NULL) {
yyerror("server \"%s\" defined twice",
diff --git a/httpd/patterns.7 b/httpd/patterns.7
index a01ede5..1ea3e41 100644
--- a/httpd/patterns.7
+++ b/httpd/patterns.7
@@ -1,4 +1,4 @@
-.\" $OpenBSD: patterns.7,v 1.5 2015/06/30 19:01:05 jmc Exp $
+.\" $OpenBSD: patterns.7,v 1.6 2017/06/10 13:31:45 schwarze Exp $
.\"
.\" Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
.\" Copyright (C) 1994-2015 Lua.org, PUC-Rio.
@@ -23,9 +23,9 @@
.\" SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
.\"
.\" Derived from section 6.4.1 in manual.html of Lua 5.3.1:
-.\" $Id: patterns.7,v 1.5 2015/06/30 19:01:05 jmc Exp $
+.\" $Id: patterns.7,v 1.6 2017/06/10 13:31:45 schwarze Exp $
.\"
-.Dd $Mdocdate: June 30 2015 $
+.Dd $Mdocdate: June 10 2017 $
.Dt PATTERNS 7
.Os
.Sh NAME
@@ -255,7 +255,7 @@ the part of the string matching
.Qq a*(.)%w(%s*)
is stored as the first capture (and therefore has number 1);
the character matching
-.So \. Sc
+.Qq \&.
is captured with number 2,
and the part matching
.Qq %s*
diff --git a/httpd/proc.c b/httpd/proc.c
index 7a6124a..cd387a1 100644
--- a/httpd/proc.c
+++ b/httpd/proc.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: proc.c,v 1.15 2015/12/07 16:05:56 reyk Exp $ */
+/* $OpenBSD: proc.c,v 1.37 2017/05/28 10:37:26 benno Exp $ */
/*
- * Copyright (c) 2010 - 2014 Reyk Floeter <reyk@openbsd.org>
+ * 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
@@ -22,6 +22,7 @@
#include <sys/socket.h>
#include <sys/wait.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -34,8 +35,12 @@
#include "httpd.h"
-void proc_open(struct privsep *, struct privsep_proc *,
- struct privsep_proc *, size_t);
+void proc_exec(struct privsep *, struct privsep_proc *, unsigned 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 *);
@@ -55,204 +60,383 @@ proc_ispeer(struct privsep_proc *procs, unsigned int nproc,
return (0);
}
-void
-proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
+enum privsep_procid
+proc_getid(struct privsep_proc *procs, unsigned int nproc,
+ const char *proc_name)
{
- unsigned int i, j, src, dst;
- struct privsep_pipes *pp;
+ struct privsep_proc *p;
+ unsigned int proc;
- /*
- * 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_ninstances,
- sizeof(struct privsep_pipes))) == NULL)
- fatal("proc_init: calloc");
+ for (proc = 0; proc < nproc; proc++) {
+ p = &procs[proc];
+ if (strcmp(p->p_title, proc_name))
+ continue;
- for (i = 0; i < ps->ps_ninstances; i++) {
- pp = &ps->ps_pipes[src][i];
+ return (p->p_id);
+ }
- for (dst = 0; dst < PROC_MAX; dst++) {
- /* Allocate maximum fd integers */
- if ((pp->pp_pipes[dst] =
- calloc(ps->ps_ninstances,
- sizeof(int))) == NULL)
- fatal("proc_init: calloc");
+ return (PROC_MAX);
+}
- /* Mark fd as unused */
- for (j = 0; j < ps->ps_ninstances; j++)
- pp->pp_pipes[dst][j] = -1;
+void
+proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
+ int argc, char **argv)
+{
+ unsigned int proc, nargc, i, proc_i;
+ 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++] = "-P";
+ 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");
+
+ execvp(argv[0], nargv);
+ fatal("%s: execvp", __func__);
+ break;
+ default:
+ /* Close child end. */
+ close(fd);
+ break;
}
}
}
-
- /*
- * Setup and run the parent and its children
- */
- privsep_process = PROC_PARENT;
- ps->ps_instances[PROC_PARENT] = 1;
- ps->ps_title[PROC_PARENT] = "parent";
- ps->ps_pid[PROC_PARENT] = getpid();
- ps->ps_pp = &ps->ps_pipes[privsep_process][0];
-
- for (i = 0; i < nproc; i++) {
- /* Default to 1 process instance */
- if (ps->ps_instances[procs[i].p_id] < 1)
- ps->ps_instances[procs[i].p_id] = 1;
- ps->ps_title[procs[i].p_id] = procs[i].p_title;
- }
-
- proc_open(ps, NULL, procs, nproc);
-
- /* Engage! */
- for (i = 0; i < nproc; i++)
- ps->ps_pid[procs[i].p_id] = (*procs[i].p_init)(ps, &procs[i]);
+ free(nargv);
}
void
-proc_kill(struct privsep *ps)
+proc_connect(struct privsep *ps)
{
- pid_t pid;
- unsigned int i;
+ struct imsgev *iev;
+ unsigned int src, dst, inst;
- if (privsep_process != PROC_PARENT)
+ /* Don't distribute any sockets if we are not really going to run. */
+ if (ps->ps_noaction)
return;
- for (i = 0; i < PROC_MAX; i++) {
- if (ps->ps_pid[i] == 0)
+ for (dst = 0; dst < PROC_MAX; dst++) {
+ /* We don't communicate with ourselves. */
+ if (dst == PROC_PARENT)
continue;
- killpg(ps->ps_pid[i], SIGTERM);
+
+ 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);
+ }
}
- do {
- pid = waitpid(WAIT_ANY, NULL, 0);
- } while (pid != -1 || (pid == -1 && errno == EINTR));
+ /* 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_close(ps);
+ proc_open(ps, src, dst);
+ }
}
void
-proc_open(struct privsep *ps, struct privsep_proc *p,
- struct privsep_proc *procs, size_t nproc)
+proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
+ 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];
- unsigned int i, j, src, proc;
-
- if (p == NULL)
- src = privsep_process; /* parent */
- else
- src = p->p_id;
- /*
- * Open socket pairs for our peers
- */
- for (proc = 0; proc < nproc; proc++) {
- procs[proc].p_ps = ps;
- procs[proc].p_env = ps->ps_env;
- if (procs[proc].p_cb == NULL)
- procs[proc].p_cb = proc_dispatch_null;
+ /* Don't initiate anything if we are not really going to run. */
+ if (ps->ps_noaction)
+ return;
- for (i = 0; i < ps->ps_instances[src]; i++) {
- for (j = 0; j < ps->ps_instances[procs[proc].p_id];
- j++) {
- pa = &ps->ps_pipes[src][i];
- pb = &ps->ps_pipes[procs[proc].p_id][j];
+ if (proc_id == PROC_PARENT) {
+ privsep_process = PROC_PARENT;
+ proc_setup(ps, procs, nproc);
- /* Check if fds are already set by peer */
- if (pa->pp_pipes[procs[proc].p_id][j] != -1)
- continue;
+ /*
+ * 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_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
PF_UNSPEC, fds) == -1)
- fatal("socketpair");
+ fatal("%s: socketpair", __func__);
- pa->pp_pipes[procs[proc].p_id][j] = fds[0];
- pb->pp_pipes[src][i] = fds[1];
+ pa->pp_pipes[dst][proc] = fds[0];
+ pb->pp_pipes[PROC_PARENT][0] = fds[1];
}
}
+
+ /* Engage! */
+ proc_exec(ps, procs, nproc, 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_listen(struct privsep *ps, struct privsep_proc *procs, size_t nproc)
+proc_accept(struct privsep *ps, int fd, enum privsep_procid dst,
+ unsigned int n)
{
- unsigned int i, dst, src, n, m;
+ 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];
+ }
+ }
+
/*
- * Close unused pipes
+ * 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++) {
- for (n = 0; n < ps->ps_instances[src]; n++) {
- /* Ingore current process */
- if (src == (unsigned int)privsep_process &&
- n == ps->ps_instance)
- continue;
+ /* 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__);
- pp = &ps->ps_pipes[src][n];
+ for (i = 0; i < ps->ps_instances[src]; i++) {
+ pp = &ps->ps_pipes[src][i];
for (dst = 0; dst < PROC_MAX; dst++) {
- if (src == dst)
- continue;
- for (m = 0; m < ps->ps_instances[dst]; m++) {
- if (pp->pp_pipes[dst][m] == -1)
- continue;
-
- /* Close and invalidate fd */
- close(pp->pp_pipes[dst][m]);
- pp->pp_pipes[dst][m] = -1;
- }
+ /* 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;
}
}
}
- src = privsep_process;
- ps->ps_pp = pp = &ps->ps_pipes[src][ps->ps_instance];
+ ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance];
+}
- /*
- * Listen on appropriate pipes
- */
- for (i = 0; i < nproc; i++) {
- dst = procs[i].p_id;
+void
+proc_kill(struct privsep *ps)
+{
+ char *cause;
+ pid_t pid;
+ int len, status;
- if (src == dst)
- fatal("proc_listen: cannot peer with oneself");
+ if (privsep_process != PROC_PARENT)
+ return;
- if ((ps->ps_ievs[dst] = calloc(ps->ps_instances[dst],
- sizeof(struct imsgev))) == NULL)
- fatal("proc_open");
+ proc_close(ps);
- for (n = 0; n < ps->ps_instances[dst]; n++) {
- if (pp->pp_pipes[dst][n] == -1)
+ 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 || (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;
- imsg_init(&(ps->ps_ievs[dst][n].ibuf),
- pp->pp_pipes[dst][n]);
- ps->ps_ievs[dst][n].handler = proc_dispatch;
- ps->ps_ievs[dst][n].events = EV_READ;
- ps->ps_ievs[dst][n].proc = &procs[i];
- ps->ps_ievs[dst][n].data = &ps->ps_ievs[dst][n];
- procs[i].p_instance = n;
-
- event_set(&(ps->ps_ievs[dst][n].ev),
- ps->ps_ievs[dst][n].ibuf.fd,
- ps->ps_ievs[dst][n].events,
- ps->ps_ievs[dst][n].handler,
- ps->ps_ievs[dst][n].data);
- event_add(&(ps->ps_ievs[dst][n].ev), NULL);
+ 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__);
}
}
}
@@ -301,7 +485,7 @@ proc_shutdown(struct privsep_proc *p)
log_info("%s exiting, pid %d", p->p_title, getpid());
- _exit(0);
+ exit(0);
}
void
@@ -321,51 +505,39 @@ proc_sig_handler(int sig, short event, void *arg)
/* ignore */
break;
default:
- fatalx("proc_sig_handler: unexpected signal");
+ fatalx("%s: unexpected signal", __func__);
/* NOTREACHED */
}
}
-pid_t
+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)
{
- pid_t pid;
struct passwd *pw;
const char *root;
struct control_sock *rcs;
- unsigned int n;
- if (ps->ps_noaction)
- return (0);
-
- proc_open(ps, p, procs, nproc);
-
- /* Fork child handlers */
- switch (pid = fork()) {
- case -1:
- fatal("proc_run: cannot fork");
- case 0:
- log_procinit(p->p_title);
+ log_procinit(p->p_title);
- /* Set the process group of the current process */
- setpgid(0, 0);
- break;
- default:
- return (pid);
- }
-
- pw = ps->ps_pw;
+ /* Set the process group of the current process */
+ setpgid(0, 0);
if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
if (control_init(ps, &ps->ps_csock) == -1)
- fatalx(__func__);
+ fatalx("%s: control_init", __func__);
TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry)
if (control_init(ps, rcs) == -1)
- fatalx(__func__);
+ fatalx("%s: control_init", __func__);
}
+ /* 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;
@@ -373,9 +545,9 @@ proc_run(struct privsep *ps, struct privsep_proc *p,
root = pw->pw_dir;
if (chroot(root) == -1)
- fatal("proc_run: chroot");
+ fatal("%s: chroot", __func__);
if (chdir("/") == -1)
- fatal("proc_run: chdir(\"/\")");
+ fatal("%s: chdir(\"/\")", __func__);
privsep_process = p->p_id;
@@ -384,20 +556,7 @@ proc_run(struct privsep *ps, struct privsep_proc *p,
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("proc_run: cannot drop privileges");
-
- /* Fork child handlers */
- for (n = 1; n < ps->ps_instances[p->p_id]; n++) {
- if (fork() == 0) {
- ps->ps_instance = p->p_instance = n;
- break;
- }
- }
-
-#ifdef DEBUG
- log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
- ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
-#endif
+ fatal("%s: cannot drop privileges", __func__);
event_init();
@@ -415,25 +574,26 @@ proc_run(struct privsep *ps, struct privsep_proc *p,
signal_add(&ps->ps_evsigpipe, NULL);
signal_add(&ps->ps_evsigusr1, NULL);
- proc_listen(ps, procs, nproc);
-
+ proc_setup(ps, procs, nproc);
+ proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0);
if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
TAILQ_INIT(&ctl_conns);
if (control_listen(&ps->ps_csock) == -1)
- fatalx(__func__);
+ fatalx("%s: control_listen", __func__);
TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry)
if (control_listen(rcs) == -1)
- fatalx(__func__);
+ fatalx("%s: control_listen", __func__);
}
+ DPRINTF("%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);
-
- return (0);
}
void
@@ -447,13 +607,14 @@ proc_dispatch(int fd, short event, void *arg)
ssize_t n;
int verbose;
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(__func__);
+ fatal("%s: imsg_read", __func__);
if (n == 0) {
/* this pipe is dead, so remove the event handler */
event_del(&iev->ev);
@@ -463,20 +624,26 @@ proc_dispatch(int fd, short event, void *arg)
}
if (event & EV_WRITE) {
- if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
- fatal(__func__);
+ 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(__func__);
+ 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, p->p_instance);
+ imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid);
#endif
/*
@@ -495,15 +662,20 @@ proc_dispatch(int fd, short event, void *arg)
case IMSG_CTL_VERBOSE:
IMSG_SIZE_CHECK(&imsg, &verbose);
memcpy(&verbose, imsg.data, sizeof(verbose));
- log_verbose(verbose);
+ log_setverbose(verbose);
+ break;
+ 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:
- log_warnx("%s: %s %d got invalid imsg %d peerid %d "
+ 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, p->p_instance);
- fatalx(__func__);
+ p->p_title, imsg.hdr.pid);
}
imsg_free(&imsg);
}
@@ -585,7 +757,7 @@ proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
proc_range(ps, id, &n, &m);
for (; n < m; n++) {
if (imsg_compose_event(&ps->ps_ievs[id][n],
- type, peerid, 0, fd, data, datalen) == -1)
+ type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1)
return (-1);
}
@@ -608,7 +780,7 @@ proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
proc_range(ps, id, &n, &m);
for (; n < m; n++)
if (imsg_composev_event(&ps->ps_ievs[id][n],
- type, peerid, 0, fd, iov, iovcnt) == -1)
+ type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1)
return (-1);
return (0);
@@ -646,3 +818,25 @@ proc_iev(struct privsep *ps, enum privsep_procid id, int n)
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);
+}
diff --git a/httpd/server.c b/httpd/server.c
index 849b92a..1a57d0b 100644
--- a/httpd/server.c
+++ b/httpd/server.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server.c,v 1.88 2016/08/15 16:12:34 jsing Exp $ */
+/* $OpenBSD: server.c,v 1.111 2017/08/11 18:48:56 jsing Exp $ */
/*
* Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -16,7 +16,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <sys/param.h> /* nitems */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
@@ -59,13 +58,14 @@ int server_socket(struct sockaddr_storage *, in_port_t,
struct server_config *, int, int);
int server_socket_listen(struct sockaddr_storage *, in_port_t,
struct server_config *);
+struct server *server_byid(uint32_t);
int server_tls_init(struct server *);
void server_tls_readcb(int, short, void *);
void server_tls_writecb(int, short, void *);
+void server_tls_handshake(int, short, void *);
void server_accept(int, short, void *);
-void server_handshake_tls(int, short, void *);
void server_input(struct client *);
void server_inflight_dec(struct client *, const char *);
@@ -76,28 +76,22 @@ volatile int server_clients;
volatile int server_inflight = 0;
uint32_t server_cltid;
-static struct httpd *env = NULL;
-int proc_id;
-
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT, server_dispatch_parent },
{ "logger", PROC_LOGGER, server_dispatch_logger }
};
-pid_t
+void
server(struct privsep *ps, struct privsep_proc *p)
{
- pid_t pid;
- env = ps->ps_env;
- pid = proc_run(ps, p, procs, nitems(procs), server_init, NULL);
- server_http(env);
- return (pid);
+ proc_run(ps, p, procs, nitems(procs), server_init, NULL);
+ server_http();
}
void
server_shutdown(void)
{
- config_purge(env, CONFIG_ALL);
+ config_purge(httpd_env, CONFIG_ALL);
usleep(200); /* XXX server needs to shutdown last */
}
@@ -115,7 +109,7 @@ server_privinit(struct server *srv)
* There's no need to open a new socket if a server with the
* same address already exists.
*/
- TAILQ_FOREACH(s, env->sc_servers, srv_entry) {
+ TAILQ_FOREACH(s, httpd_env->sc_servers, srv_entry) {
if (s != srv && s->srv_s != -1 &&
s->srv_conf.port == srv->srv_conf.port &&
sockaddr_cmp((struct sockaddr *)&s->srv_conf.ss,
@@ -133,7 +127,7 @@ server_privinit(struct server *srv)
}
int
-server_tls_cmp(struct server *s1, struct server *s2)
+server_tls_cmp(struct server *s1, struct server *s2, int match_keypair)
{
struct server_config *sc1, *sc2;
@@ -142,17 +136,22 @@ server_tls_cmp(struct server *s1, struct server *s2)
if (sc1->tls_protocols != sc2->tls_protocols)
return (-1);
- if (strcmp(sc1->tls_cert_file, sc2->tls_cert_file) != 0)
- return (-1);
- if (strcmp(sc1->tls_key_file, sc2->tls_key_file) != 0)
+ if (sc1->tls_ticket_lifetime != sc2->tls_ticket_lifetime)
return (-1);
if (strcmp(sc1->tls_ciphers, sc2->tls_ciphers) != 0)
return (-1);
if (strcmp(sc1->tls_dhe_params, sc2->tls_dhe_params) != 0)
return (-1);
- if (strcmp(sc1->tls_ecdhe_curve, sc2->tls_ecdhe_curve) != 0)
+ if (strcmp(sc1->tls_ecdhe_curves, sc2->tls_ecdhe_curves) != 0)
return (-1);
+ if (match_keypair) {
+ if (strcmp(sc1->tls_cert_file, sc2->tls_cert_file) != 0)
+ return (-1);
+ if (strcmp(sc1->tls_key_file, sc2->tls_key_file) != 0)
+ return (-1);
+ }
+
return (0);
}
@@ -162,17 +161,15 @@ server_tls_load_keypair(struct server *srv)
if ((srv->srv_conf.flags & SRVFLAG_TLS) == 0)
return (0);
- if ((srv->srv_conf.tls_cert = tls_load_file(
- srv->srv_conf.tls_cert_file, &srv->srv_conf.tls_cert_len,
- NULL)) == NULL)
+ if ((srv->srv_conf.tls_cert = tls_load_file(srv->srv_conf.tls_cert_file,
+ &srv->srv_conf.tls_cert_len, NULL)) == NULL)
return (-1);
log_debug("%s: using certificate %s", __func__,
srv->srv_conf.tls_cert_file);
/* XXX allow to specify password for encrypted key */
- if ((srv->srv_conf.tls_key = tls_load_file(
- srv->srv_conf.tls_key_file, &srv->srv_conf.tls_key_len,
- NULL)) == NULL)
+ if ((srv->srv_conf.tls_key = tls_load_file(srv->srv_conf.tls_key_file,
+ &srv->srv_conf.tls_key_len, NULL)) == NULL)
return (-1);
log_debug("%s: using private key %s", __func__,
srv->srv_conf.tls_key_file);
@@ -181,8 +178,29 @@ server_tls_load_keypair(struct server *srv)
}
int
+server_tls_load_ocsp(struct server *srv)
+{
+ if ((srv->srv_conf.flags & SRVFLAG_TLS) == 0)
+ return (0);
+
+ if (srv->srv_conf.tls_ocsp_staple_file == NULL)
+ return (0);
+
+ if ((srv->srv_conf.tls_ocsp_staple = tls_load_file(
+ srv->srv_conf.tls_ocsp_staple_file,
+ &srv->srv_conf.tls_ocsp_staple_len, NULL)) == NULL)
+ return (-1);
+ log_debug("%s: using ocsp staple from %s", __func__,
+ srv->srv_conf.tls_ocsp_staple_file);
+
+ return (0);
+}
+
+int
server_tls_init(struct server *srv)
{
+ struct server_config *srv_conf;
+
if ((srv->srv_conf.flags & SRVFLAG_TLS) == 0)
return (0);
@@ -201,9 +219,12 @@ server_tls_init(struct server *srv)
return (-1);
}
- tls_config_set_protocols(srv->srv_tls_config,
- srv->srv_conf.tls_protocols);
-
+ if (tls_config_set_protocols(srv->srv_tls_config,
+ srv->srv_conf.tls_protocols) != 0) {
+ log_warnx("%s: failed to set tls protocols: %s",
+ __func__, tls_config_error(srv->srv_tls_config));
+ return (-1);
+ }
if (tls_config_set_ciphers(srv->srv_tls_config,
srv->srv_conf.tls_ciphers) != 0) {
log_warnx("%s: failed to set tls ciphers: %s",
@@ -216,21 +237,63 @@ server_tls_init(struct server *srv)
__func__, tls_config_error(srv->srv_tls_config));
return (-1);
}
- if (tls_config_set_ecdhecurve(srv->srv_tls_config,
- srv->srv_conf.tls_ecdhe_curve) != 0) {
- log_warnx("%s: failed to set tls ecdhe curve: %s",
+ if (tls_config_set_ecdhecurves(srv->srv_tls_config,
+ srv->srv_conf.tls_ecdhe_curves) != 0) {
+ log_warnx("%s: failed to set tls ecdhe curves: %s",
__func__, tls_config_error(srv->srv_tls_config));
return (-1);
}
- if (tls_config_set_keypair_mem(srv->srv_tls_config,
+ if (tls_config_set_keypair_ocsp_mem(srv->srv_tls_config,
srv->srv_conf.tls_cert, srv->srv_conf.tls_cert_len,
- srv->srv_conf.tls_key, srv->srv_conf.tls_key_len) != 0) {
+ srv->srv_conf.tls_key, srv->srv_conf.tls_key_len,
+ srv->srv_conf.tls_ocsp_staple,
+ srv->srv_conf.tls_ocsp_staple_len) != 0) {
log_warnx("%s: failed to set tls certificate/key: %s",
__func__, tls_config_error(srv->srv_tls_config));
return (-1);
}
+ TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
+ if (srv_conf->tls_cert == NULL || srv_conf->tls_key == NULL)
+ continue;
+ log_debug("%s: adding keypair for server %s", __func__,
+ srv->srv_conf.name);
+ if (tls_config_add_keypair_ocsp_mem(srv->srv_tls_config,
+ srv_conf->tls_cert, srv_conf->tls_cert_len,
+ srv_conf->tls_key, srv_conf->tls_key_len,
+ srv_conf->tls_ocsp_staple,
+ srv_conf->tls_ocsp_staple_len) != 0) {
+ log_warnx("%s: failed to add tls keypair", __func__);
+ return (-1);
+ }
+ }
+
+ /* set common session ID among all processes */
+ if (tls_config_set_session_id(srv->srv_tls_config,
+ httpd_env->sc_tls_sid, sizeof(httpd_env->sc_tls_sid)) == -1) {
+ log_warnx("%s: could not set the TLS session ID: %s",
+ __func__, tls_config_error(srv->srv_tls_config));
+ return (-1);
+ }
+
+ /* ticket support */
+ if (srv->srv_conf.tls_ticket_lifetime) {
+ if (tls_config_set_session_lifetime(srv->srv_tls_config,
+ srv->srv_conf.tls_ticket_lifetime) == -1) {
+ log_warnx("%s: could not set the TLS session lifetime: "
+ "%s", __func__,
+ tls_config_error(srv->srv_tls_config));
+ return (-1);
+ }
+ tls_config_add_ticket_key(srv->srv_tls_config,
+ srv->srv_conf.tls_ticket_key.tt_keyrev,
+ srv->srv_conf.tls_ticket_key.tt_key,
+ sizeof(srv->srv_conf.tls_ticket_key.tt_key));
+ explicit_bzero(&srv->srv_conf.tls_ticket_key,
+ sizeof(srv->srv_conf.tls_ticket_key));
+ }
+
if (tls_configure(srv->srv_tls_ctx, srv->srv_tls_config) != 0) {
log_warnx("%s: failed to configure tls - %s", __func__,
tls_error(srv->srv_tls_ctx));
@@ -239,10 +302,8 @@ server_tls_init(struct server *srv)
/* We're now done with the public/private key... */
tls_config_clear_keys(srv->srv_tls_config);
- explicit_bzero(srv->srv_conf.tls_cert, srv->srv_conf.tls_cert_len);
- explicit_bzero(srv->srv_conf.tls_key, srv->srv_conf.tls_key_len);
- free(srv->srv_conf.tls_cert);
- free(srv->srv_conf.tls_key);
+ freezero(srv->srv_conf.tls_cert, srv->srv_conf.tls_cert_len);
+ freezero(srv->srv_conf.tls_key, srv->srv_conf.tls_key_len);
srv->srv_conf.tls_cert = NULL;
srv->srv_conf.tls_key = NULL;
srv->srv_conf.tls_cert_len = 0;
@@ -252,16 +313,23 @@ server_tls_init(struct server *srv)
}
void
+server_generate_ticket_key(struct server_config *srv_conf)
+{
+ struct server_tls_ticket *key = &srv_conf->tls_ticket_key;
+
+ key->tt_id = srv_conf->id;
+ key->tt_keyrev = arc4random();
+ arc4random_buf(key->tt_key, sizeof(key->tt_key));
+}
+
+void
server_init(struct privsep *ps, struct privsep_proc *p, void *arg)
{
- server_http(ps->ps_env);
+ server_http();
if (config_init(ps->ps_env) == -1)
fatal("failed to initialize configuration");
- /* Set to current prefork id */
- proc_id = p->p_instance;
-
/* We use a custom shutdown callback */
p->p_shutdown = server_shutdown;
@@ -273,9 +341,9 @@ server_init(struct privsep *ps, struct privsep_proc *p, void *arg)
#if 0
/* Schedule statistics timer */
- evtimer_set(&env->sc_statev, server_statistics, NULL);
- memcpy(&tv, &env->sc_statinterval, sizeof(tv));
- evtimer_add(&env->sc_statev, &tv);
+ evtimer_set(&ps->ps_env->sc_statev, server_statistics, NULL);
+ memcpy(&tv, &ps->ps_env->sc_statinterval, sizeof(tv));
+ evtimer_add(&ps->ps_env->sc_statev, &tv);
#endif
}
@@ -284,7 +352,10 @@ server_launch(void)
{
struct server *srv;
- TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+ TAILQ_FOREACH(srv, httpd_env->sc_servers, srv_entry) {
+ log_debug("%s: configuring server %s", __func__,
+ srv->srv_conf.name);
+
server_tls_init(srv);
server_http_init(srv);
@@ -312,7 +383,7 @@ server_purge(struct server *srv)
if (srv->srv_s != -1)
close(srv->srv_s);
- TAILQ_REMOVE(env->sc_servers, srv, srv_entry);
+ TAILQ_REMOVE(httpd_env->sc_servers, srv, srv_entry);
/* cleanup sessions */
while ((clt =
@@ -343,16 +414,10 @@ serverconfig_free(struct server_config *srv_conf)
free(srv_conf->return_uri);
free(srv_conf->tls_cert_file);
free(srv_conf->tls_key_file);
-
- if (srv_conf->tls_cert != NULL) {
- explicit_bzero(srv_conf->tls_cert, srv_conf->tls_cert_len);
- free(srv_conf->tls_cert);
- }
-
- if (srv_conf->tls_key != NULL) {
- explicit_bzero(srv_conf->tls_key, srv_conf->tls_key_len);
- free(srv_conf->tls_key);
- }
+ free(srv_conf->tls_ocsp_staple_file);
+ free(srv_conf->tls_ocsp_staple);
+ freezero(srv_conf->tls_cert, srv_conf->tls_cert_len);
+ freezero(srv_conf->tls_key, srv_conf->tls_key_len);
}
void
@@ -364,6 +429,8 @@ serverconfig_reset(struct server_config *srv_conf)
srv_conf->tls_cert_file = NULL;
srv_conf->tls_key = NULL;
srv_conf->tls_key_file = NULL;
+ srv_conf->tls_ocsp_staple = NULL;
+ srv_conf->tls_ocsp_staple_file = NULL;
}
struct server *
@@ -371,7 +438,7 @@ server_byaddr(struct sockaddr *addr, in_port_t port)
{
struct server *srv;
- TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+ TAILQ_FOREACH(srv, httpd_env->sc_servers, srv_entry) {
if (port == srv->srv_conf.port &&
sockaddr_cmp((struct sockaddr *)&srv->srv_conf.ss,
addr, srv->srv_conf.prefixlen) == 0)
@@ -387,7 +454,7 @@ serverconfig_byid(uint32_t id)
struct server *srv;
struct server_config *srv_conf;
- TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+ TAILQ_FOREACH(srv, httpd_env->sc_servers, srv_entry) {
if (srv->srv_conf.id == id)
return (&srv->srv_conf);
TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
@@ -399,6 +466,18 @@ serverconfig_byid(uint32_t id)
return (NULL);
}
+struct server *
+server_byid(uint32_t id)
+{
+ struct server *srv;
+
+ TAILQ_FOREACH(srv, httpd_env->sc_servers, srv_entry) {
+ if (srv->srv_conf.id == id)
+ return (srv);
+ }
+ return (NULL);
+}
+
int
server_foreach(int (*srv_cb)(struct server *,
struct server_config *, void *), void *arg)
@@ -406,7 +485,7 @@ server_foreach(int (*srv_cb)(struct server *,
struct server *srv;
struct server_config *srv_conf;
- TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+ TAILQ_FOREACH(srv, httpd_env->sc_servers, srv_entry) {
if ((srv_cb)(srv, &srv->srv_conf, arg) == -1)
return (-1);
TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
@@ -424,7 +503,7 @@ server_match(struct server *s2, int match_name)
struct server *s1;
/* Attempt to find matching server. */
- TAILQ_FOREACH(s1, env->sc_servers, srv_entry) {
+ TAILQ_FOREACH(s1, httpd_env->sc_servers, srv_entry) {
if ((s1->srv_conf.flags & SRVFLAG_LOCATION) != 0)
continue;
if (match_name) {
@@ -525,15 +604,33 @@ server_socket(struct sockaddr_storage *ss, in_port_t port,
*/
if (srv_conf->tcpflags & TCPFLAG_IPTTL) {
val = (int)srv_conf->tcpipttl;
- if (setsockopt(s, IPPROTO_IP, IP_TTL,
- &val, sizeof(val)) == -1)
- goto bad;
+ switch (ss->ss_family) {
+ case AF_INET:
+ if (setsockopt(s, IPPROTO_IP, IP_TTL,
+ &val, sizeof(val)) == -1)
+ goto bad;
+ break;
+ case AF_INET6:
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ &val, sizeof(val)) == -1)
+ goto bad;
+ break;
+ }
}
if (srv_conf->tcpflags & TCPFLAG_IPMINTTL) {
val = (int)srv_conf->tcpipminttl;
- if (setsockopt(s, IPPROTO_IP, IP_MINTTL,
- &val, sizeof(val)) == -1)
- goto bad;
+ switch (ss->ss_family) {
+ case AF_INET:
+ if (setsockopt(s, IPPROTO_IP, IP_MINTTL,
+ &val, sizeof(val)) == -1)
+ goto bad;
+ break;
+ case AF_INET6:
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MINHOPCOUNT,
+ &val, sizeof(val)) == -1)
+ goto bad;
+ break;
+ }
}
/*
@@ -761,7 +858,7 @@ server_input(struct client *clt)
bufferevent_setwatermark(clt->clt_bev, EV_READ, 0, FCGI_CONTENT_SIZE);
bufferevent_settimeout(clt->clt_bev,
- srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
+ srv_conf->requesttimeout.tv_sec, srv_conf->requesttimeout.tv_sec);
bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
}
@@ -780,8 +877,6 @@ server_write(struct bufferevent *bev, void *arg)
if (clt->clt_done)
goto done;
- bufferevent_enable(bev, EV_READ);
-
if (clt->clt_srvbev && clt->clt_srvbev_throttled) {
bufferevent_enable(clt->clt_srvbev, EV_READ);
clt->clt_srvbev_throttled = 0;
@@ -789,7 +884,7 @@ server_write(struct bufferevent *bev, void *arg)
return;
done:
- (*bev->errorcb)(bev, EVBUFFER_WRITE|EVBUFFER_EOF, bev->cbarg);
+ (*bev->errorcb)(bev, EVBUFFER_WRITE, bev->cbarg);
return;
}
@@ -834,7 +929,7 @@ server_read(struct bufferevent *bev, void *arg)
return;
done:
- (*bev->errorcb)(bev, EVBUFFER_READ|EVBUFFER_EOF, bev->cbarg);
+ (*bev->errorcb)(bev, EVBUFFER_READ, bev->cbarg);
return;
fail:
server_close(clt, strerror(errno));
@@ -847,7 +942,7 @@ server_error(struct bufferevent *bev, short error, void *arg)
struct evbuffer *dst;
if (error & EVBUFFER_TIMEOUT) {
- server_close(clt, "buffer event timeout");
+ server_abort_http(clt, 408, "timeout");
return;
}
if (error & EVBUFFER_ERROR) {
@@ -858,7 +953,11 @@ server_error(struct bufferevent *bev, short error, void *arg)
server_close(clt, "buffer event error");
return;
}
- if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
+ if (error & EVBUFFER_EOF) {
+ server_close(clt, "closed");
+ return;
+ }
+ if (error & (EVBUFFER_READ|EVBUFFER_WRITE)) {
bufferevent_disable(bev, EV_READ|EV_WRITE);
clt->clt_done = 1;
@@ -954,9 +1053,6 @@ server_accept(int fd, short event, void *arg)
server_clients++;
SPLAY_INSERT(client_tree, &srv->srv_clients, clt);
- /* Increment the per-relay client counter */
- //srv->srv_stats[proc_id].last++;
-
/* Pre-allocate output buffer */
clt->clt_output = evbuffer_new();
if (clt->clt_output == NULL) {
@@ -971,7 +1067,7 @@ server_accept(int fd, short event, void *arg)
return;
}
event_again(&clt->clt_ev, clt->clt_s, EV_TIMEOUT|EV_READ,
- server_handshake_tls, &clt->clt_tv_start,
+ server_tls_handshake, &clt->clt_tv_start,
&srv->srv_conf.timeout, clt);
return;
}
@@ -992,7 +1088,7 @@ server_accept(int fd, short event, void *arg)
}
void
-server_handshake_tls(int fd, short event, void *arg)
+server_tls_handshake(int fd, short event, void *arg)
{
struct client *clt = (struct client *)arg;
struct server *srv = (struct server *)clt->clt_srv;
@@ -1011,14 +1107,14 @@ server_handshake_tls(int fd, short event, void *arg)
server_input(clt);
} else if (ret == TLS_WANT_POLLIN) {
event_again(&clt->clt_ev, clt->clt_s, EV_TIMEOUT|EV_READ,
- server_handshake_tls, &clt->clt_tv_start,
+ server_tls_handshake, &clt->clt_tv_start,
&srv->srv_conf.timeout, clt);
} else if (ret == TLS_WANT_POLLOUT) {
event_again(&clt->clt_ev, clt->clt_s, EV_TIMEOUT|EV_WRITE,
- server_handshake_tls, &clt->clt_tv_start,
+ server_tls_handshake, &clt->clt_tv_start,
&srv->srv_conf.timeout, clt);
} else {
- log_warnx("%s: tls handshake failed - %s", __func__,
+ log_debug("%s: tls handshake failed - %s", __func__,
tls_error(clt->clt_tls_ctx));
server_close(clt, "tls handshake failed");
}
@@ -1071,7 +1167,7 @@ server_sendlog(struct server_config *srv_conf, int cmd, const char *emsg, ...)
iov[1].iov_base = msg;
iov[1].iov_len = strlen(msg) + 1;
- if (proc_composev(env->sc_ps, PROC_LOGGER, cmd, iov, 2) != 0) {
+ if (proc_composev(httpd_env->sc_ps, PROC_LOGGER, cmd, iov, 2) != 0) {
log_warn("%s: failed to compose imsg", __func__);
return;
}
@@ -1084,14 +1180,13 @@ server_log(struct client *clt, const char *msg)
struct server_config *srv_conf = clt->clt_srv_conf;
char *ptr = NULL, *vmsg = NULL;
int debug_cmd = -1;
- extern int verbose;
switch (srv_conf->logformat) {
case LOG_FORMAT_CONNECTION:
debug_cmd = IMSG_LOG_ACCESS;
break;
default:
- if (verbose > 1)
+ if (log_getverbose() > 1)
debug_cmd = IMSG_LOG_ERROR;
if (EVBUFFER_LENGTH(clt->clt_log)) {
while ((ptr =
@@ -1171,27 +1266,40 @@ server_close(struct client *clt, const char *msg)
int
server_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{
+ struct server *srv;
+ struct server_tls_ticket key;
+
switch (imsg->hdr.type) {
case IMSG_CFG_MEDIA:
- config_getmedia(env, imsg);
+ config_getmedia(httpd_env, imsg);
break;
case IMSG_CFG_AUTH:
- config_getauth(env, imsg);
+ config_getauth(httpd_env, imsg);
break;
case IMSG_CFG_SERVER:
- config_getserver(env, imsg);
+ config_getserver(httpd_env, imsg);
break;
case IMSG_CFG_TLS:
- config_gettls(env, imsg);
+ config_getserver_tls(httpd_env, imsg);
break;
case IMSG_CFG_DONE:
- config_getcfg(env, imsg);
+ config_getcfg(httpd_env, imsg);
break;
case IMSG_CTL_START:
server_launch();
break;
case IMSG_CTL_RESET:
- config_getreset(env, imsg);
+ config_getreset(httpd_env, imsg);
+ break;
+ case IMSG_TLSTICKET_REKEY:
+ IMSG_SIZE_CHECK(imsg, (&key));
+ memcpy(&key, imsg->data, sizeof(key));
+ /* apply to the right server */
+ srv = server_byid(key.tt_id);
+ if (srv) {
+ tls_config_add_ticket_key(srv->srv_tls_config,
+ key.tt_keyrev, key.tt_key, sizeof(key.tt_key));
+ }
break;
default:
return (-1);
diff --git a/httpd/server_fcgi.c b/httpd/server_fcgi.c
index 21ebeed..920b2cf 100644
--- a/httpd/server_fcgi.c
+++ b/httpd/server_fcgi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server_fcgi.c,v 1.68 2016/04/24 20:09:45 chrisz Exp $ */
+/* $OpenBSD: server_fcgi.c,v 1.75 2017/07/31 08:02:49 ians Exp $ */
/*
* Copyright (c) 2014 Florian Obser <florian@openbsd.org>
@@ -120,7 +120,6 @@ server_fcgi(struct httpd *env, struct client *clt)
goto fail;
} else {
struct sockaddr_un sun;
- size_t len;
if ((fd = socket(AF_UNIX,
SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
@@ -128,21 +127,21 @@ server_fcgi(struct httpd *env, struct client *clt)
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
- len = strlcpy(sun.sun_path,
- srv_conf->socket, sizeof(sun.sun_path));
- if (len >= sizeof(sun.sun_path)) {
- errstr = "socket path too long";
+ if (strlcpy(sun.sun_path, srv_conf->socket,
+ sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
+ errstr = "socket path to long";
goto fail;
}
- sun.sun_len = len;
if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
goto fail;
}
memset(hbuf, 0, sizeof(hbuf));
- clt->clt_fcgi_state = FCGI_READ_HEADER;
- clt->clt_fcgi_toread = sizeof(struct fcgi_record_header);
+ clt->clt_fcgi.state = FCGI_READ_HEADER;
+ clt->clt_fcgi.toread = sizeof(struct fcgi_record_header);
+ clt->clt_fcgi.status = 200;
+ clt->clt_fcgi.headersdone = 0;
if (clt->clt_srvevb != NULL)
evbuffer_free(clt->clt_srvevb);
@@ -387,13 +386,13 @@ server_fcgi(struct httpd *env, struct client *clt)
}
if (strcmp(desc->http_version, "HTTP/1.1") == 0) {
- clt->clt_fcgi_chunked = 1;
+ clt->clt_fcgi.chunked = 1;
} else {
/* HTTP/1.0 does not support chunked encoding */
- clt->clt_fcgi_chunked = 0;
+ clt->clt_fcgi.chunked = 0;
clt->clt_persist = 0;
}
- clt->clt_fcgi_end = 0;
+ clt->clt_fcgi.end = 0;
clt->clt_done = 0;
free(script);
@@ -499,36 +498,36 @@ server_fcgi_read(struct bufferevent *bev, void *arg)
char *ptr;
do {
- len = bufferevent_read(bev, buf, clt->clt_fcgi_toread);
+ len = bufferevent_read(bev, buf, clt->clt_fcgi.toread);
if (evbuffer_add(clt->clt_srvevb, buf, len) == -1) {
server_abort_http(clt, 500, "short write");
return;
}
- clt->clt_fcgi_toread -= len;
+ clt->clt_fcgi.toread -= len;
DPRINTF("%s: len: %lu toread: %d state: %d type: %d",
- __func__, len, clt->clt_fcgi_toread,
- clt->clt_fcgi_state, clt->clt_fcgi_type);
+ __func__, len, clt->clt_fcgi.toread,
+ clt->clt_fcgi.state, clt->clt_fcgi.type);
- if (clt->clt_fcgi_toread != 0)
+ if (clt->clt_fcgi.toread != 0)
return;
- switch (clt->clt_fcgi_state) {
+ switch (clt->clt_fcgi.state) {
case FCGI_READ_HEADER:
- clt->clt_fcgi_state = FCGI_READ_CONTENT;
+ clt->clt_fcgi.state = FCGI_READ_CONTENT;
h = (struct fcgi_record_header *)
EVBUFFER_DATA(clt->clt_srvevb);
DPRINTF("%s: record header: version %d type %d id %d "
"content len %d padding %d", __func__,
h->version, h->type, ntohs(h->id),
ntohs(h->content_len), h->padding_len);
- clt->clt_fcgi_type = h->type;
- clt->clt_fcgi_toread = ntohs(h->content_len);
- clt->clt_fcgi_padding_len = h->padding_len;
+ clt->clt_fcgi.type = h->type;
+ clt->clt_fcgi.toread = ntohs(h->content_len);
+ clt->clt_fcgi.padding_len = h->padding_len;
evbuffer_drain(clt->clt_srvevb,
EVBUFFER_LENGTH(clt->clt_srvevb));
- if (clt->clt_fcgi_toread != 0)
+ if (clt->clt_fcgi.toread != 0)
break;
- else if (clt->clt_fcgi_type == FCGI_STDOUT &&
+ else if (clt->clt_fcgi.type == FCGI_STDOUT &&
!clt->clt_chunk) {
server_abort_http(clt, 500, "empty stdout");
return;
@@ -536,7 +535,7 @@ server_fcgi_read(struct bufferevent *bev, void *arg)
/* fallthrough if content_len == 0 */
case FCGI_READ_CONTENT:
- switch (clt->clt_fcgi_type) {
+ switch (clt->clt_fcgi.type) {
case FCGI_STDERR:
if (EVBUFFER_LENGTH(clt->clt_srvevb) > 0 &&
(ptr = get_string(
@@ -549,13 +548,20 @@ server_fcgi_read(struct bufferevent *bev, void *arg)
}
break;
case FCGI_STDOUT:
- if (++clt->clt_chunk == 1) {
- if (server_fcgi_header(clt,
- server_fcgi_getheaders(clt))
- == -1) {
- server_abort_http(clt, 500,
- "malformed fcgi headers");
- return;
+ ++clt->clt_chunk;
+ if (!clt->clt_fcgi.headersdone) {
+ clt->clt_fcgi.headersdone =
+ server_fcgi_getheaders(clt);
+ if (clt->clt_fcgi.headersdone) {
+ if (server_fcgi_header(clt,
+ clt->clt_fcgi.status)
+ == -1) {
+ server_abort_http(clt,
+ 500,
+ "malformed fcgi "
+ "headers");
+ return;
+ }
}
if (!EVBUFFER_LENGTH(clt->clt_srvevb))
break;
@@ -571,21 +577,21 @@ server_fcgi_read(struct bufferevent *bev, void *arg)
}
evbuffer_drain(clt->clt_srvevb,
EVBUFFER_LENGTH(clt->clt_srvevb));
- if (!clt->clt_fcgi_padding_len) {
- clt->clt_fcgi_state = FCGI_READ_HEADER;
- clt->clt_fcgi_toread =
+ if (!clt->clt_fcgi.padding_len) {
+ clt->clt_fcgi.state = FCGI_READ_HEADER;
+ clt->clt_fcgi.toread =
sizeof(struct fcgi_record_header);
} else {
- clt->clt_fcgi_state = FCGI_READ_PADDING;
- clt->clt_fcgi_toread =
- clt->clt_fcgi_padding_len;
+ clt->clt_fcgi.state = FCGI_READ_PADDING;
+ clt->clt_fcgi.toread =
+ clt->clt_fcgi.padding_len;
}
break;
case FCGI_READ_PADDING:
evbuffer_drain(clt->clt_srvevb,
EVBUFFER_LENGTH(clt->clt_srvevb));
- clt->clt_fcgi_state = FCGI_READ_HEADER;
- clt->clt_fcgi_toread =
+ clt->clt_fcgi.state = FCGI_READ_HEADER;
+ clt->clt_fcgi.toread =
sizeof(struct fcgi_record_header);
break;
}
@@ -618,7 +624,7 @@ server_fcgi_header(struct client *clt, unsigned int code)
return (-1);
/* Set chunked encoding */
- if (clt->clt_fcgi_chunked) {
+ if (clt->clt_fcgi.chunked) {
/* XXX Should we keep and handle Content-Length instead? */
key.kv_key = "Content-Length";
if ((kv = kv_find(&resp->http_headers, &key)) != NULL)
@@ -655,8 +661,10 @@ server_fcgi_header(struct client *clt, unsigned int code)
}
/* Date header is mandatory and should be added as late as possible */
- if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 ||
- kv_add(&resp->http_headers, "Date", tmbuf) == NULL)
+ key.kv_key = "Date";
+ if ((kv = kv_find(&resp->http_headers, &key)) == NULL &&
+ (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 ||
+ kv_add(&resp->http_headers, "Date", tmbuf) == NULL))
return (-1);
/* Write initial header (fcgi might append more) */
@@ -727,14 +735,14 @@ server_fcgi_writechunk(struct client *clt)
struct evbuffer *evb = clt->clt_srvevb;
size_t len;
- if (clt->clt_fcgi_type == FCGI_END_REQUEST) {
+ if (clt->clt_fcgi.type == FCGI_END_REQUEST) {
len = 0;
} else
len = EVBUFFER_LENGTH(evb);
- if (clt->clt_fcgi_chunked) {
+ if (clt->clt_fcgi.chunked) {
/* If len is 0, make sure to write the end marker only once */
- if (len == 0 && clt->clt_fcgi_end++)
+ if (len == 0 && clt->clt_fcgi.end++)
return (0);
if (server_bufferevent_printf(clt, "%zx\r\n", len) == -1 ||
server_bufferevent_write_chunk(clt, evb, len) == -1 ||
@@ -751,7 +759,7 @@ server_fcgi_getheaders(struct client *clt)
{
struct http_descriptor *resp = clt->clt_descresp;
struct evbuffer *evb = clt->clt_srvevb;
- int code = 200;
+ int code, ret;
char *line, *key, *value;
const char *errstr;
@@ -760,12 +768,9 @@ server_fcgi_getheaders(struct client *clt)
if ((value = strchr(key, ':')) == NULL)
break;
- if (*value == ':') {
- *value++ = '\0';
- value += strspn(value, " \t");
- } else {
- *value++ = '\0';
- }
+
+ *value++ = '\0';
+ value += strspn(value, " \t");
DPRINTF("%s: %s: %s", __func__, key, value);
@@ -775,11 +780,15 @@ server_fcgi_getheaders(struct client *clt)
if (errstr != NULL || server_httperror_byid(
code) == NULL)
code = 200;
+ clt->clt_fcgi.status = code;
} else {
(void)kv_add(&resp->http_headers, key, value);
}
free(line);
}
- return (code);
+ ret = (line != NULL && *line == '\0');
+
+ free(line);
+ return ret;
}
diff --git a/httpd/server_file.c b/httpd/server_file.c
index 48ecbb5..4255119 100644
--- a/httpd/server_file.c
+++ b/httpd/server_file.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: server_file.c,v 1.62 2016/05/17 03:12:39 deraadt Exp $ */
+/* $OpenBSD: server_file.c,v 1.65 2017/02/02 22:19:59 reyk Exp $ */
/*
- * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006 - 2017 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
@@ -36,12 +36,6 @@
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
-#define MAX_RANGES 4
-
-struct range {
- off_t start;
- off_t end;
-};
int server_file_access(struct httpd *, struct client *,
char *, size_t);
@@ -55,8 +49,7 @@ int server_file_modified_since(struct http_descriptor *,
struct stat *);
int server_file_method(struct client *);
int parse_range_spec(char *, size_t, struct range *);
-struct range *parse_range(char *, size_t, int *);
-int buffer_add_range(int, struct evbuffer *, struct range *);
+int parse_ranges(struct client *, char *, size_t);
int
server_file_access(struct httpd *env, struct client *clt,
@@ -303,11 +296,10 @@ server_partial_file_request(struct httpd *env, struct client *clt, char *path,
struct http_descriptor *resp = clt->clt_descresp;
struct http_descriptor *desc = clt->clt_descreq;
struct media_type *media, multipart_media;
+ struct range_data *r = &clt->clt_ranges;
struct range *range;
- struct evbuffer *evb = NULL;
- size_t content_length;
+ size_t content_length = 0;
int code = 500, fd = -1, i, nranges, ret;
- uint32_t boundary;
char content_range[64];
const char *errstr = NULL;
@@ -315,7 +307,7 @@ server_partial_file_request(struct httpd *env, struct client *clt, char *path,
if (desc->http_method != HTTP_METHOD_GET)
return server_file_request(env, clt, path, st);
- if ((range = parse_range(range_str, st->st_size, &nranges)) == NULL) {
+ if ((nranges = parse_ranges(clt, range_str, st->st_size)) < 1) {
code = 416;
(void)snprintf(content_range, sizeof(content_range),
"bytes */%lld", st->st_size);
@@ -328,12 +320,10 @@ server_partial_file_request(struct httpd *env, struct client *clt, char *path,
goto abort;
media = media_find_config(env, srv_conf, path);
- if ((evb = evbuffer_new()) == NULL) {
- errstr = "failed to allocate file buffer";
- goto abort;
- }
+ r->range_media = media;
if (nranges == 1) {
+ range = &r->range[0];
(void)snprintf(content_range, sizeof(content_range),
"bytes %lld-%lld/%lld", range->start, range->end,
st->st_size);
@@ -341,56 +331,46 @@ server_partial_file_request(struct httpd *env, struct client *clt, char *path,
content_range) == NULL)
goto abort;
- content_length = range->end - range->start + 1;
- if (buffer_add_range(fd, evb, range) == 0)
- goto abort;
-
+ range = &r->range[0];
+ content_length += range->end - range->start + 1;
} else {
- content_length = 0;
- boundary = arc4random();
- /* Generate a multipart payload of byteranges */
- while (nranges--) {
- if ((i = evbuffer_add_printf(evb, "\r\n--%ud\r\n",
- boundary)) == -1)
- goto abort;
+ /* Add boundary, all parts will be handled by the callback */
+ arc4random_buf(&clt->clt_boundary, sizeof(clt->clt_boundary));
- content_length += i;
- if ((i = evbuffer_add_printf(evb,
- "Content-Type: %s/%s\r\n",
- media->media_type, media->media_subtype)) == -1)
- goto abort;
+ /* Calculate Content-Length of the complete multipart body */
+ for (i = 0; i < nranges; i++) {
+ range = &r->range[i];
- content_length += i;
- if ((i = evbuffer_add_printf(evb,
+ /* calculate Content-Length of the complete body */
+ if ((ret = snprintf(NULL, 0,
+ "\r\n--%llu\r\n"
+ "Content-Type: %s/%s\r\n"
"Content-Range: bytes %lld-%lld/%lld\r\n\r\n",
- range->start, range->end, st->st_size)) == -1)
+ clt->clt_boundary,
+ media->media_type, media->media_subtype,
+ range->start, range->end, st->st_size)) < 0)
goto abort;
- content_length += i;
- if (buffer_add_range(fd, evb, range) == 0)
- goto abort;
+ /* Add data length */
+ content_length += ret + range->end - range->start + 1;
- content_length += range->end - range->start + 1;
- range++;
}
-
- if ((i = evbuffer_add_printf(evb, "\r\n--%ud--\r\n",
- boundary)) == -1)
+ if ((ret = snprintf(NULL, 0, "\r\n--%llu--\r\n",
+ clt->clt_boundary)) < 0)
goto abort;
-
- content_length += i;
+ content_length += ret;
/* prepare multipart/byteranges media type */
(void)strlcpy(multipart_media.media_type, "multipart",
sizeof(multipart_media.media_type));
(void)snprintf(multipart_media.media_subtype,
sizeof(multipart_media.media_subtype),
- "byteranges; boundary=%ud", boundary);
+ "byteranges; boundary=%llu", clt->clt_boundary);
media = &multipart_media;
}
- close(fd);
- fd = -1;
+ /* Start with first range */
+ r->range_toread = TOREAD_HTTP_RANGE;
ret = server_response_http(clt, 206, media, content_length,
MINIMUM(time(NULL), st->st_mtim.tv_sec));
@@ -399,25 +379,32 @@ server_partial_file_request(struct httpd *env, struct client *clt, char *path,
goto fail;
case 0:
/* Connection is already finished */
- evbuffer_free(evb);
- evb = NULL;
+ close(fd);
goto done;
default:
break;
}
- if (server_bufferevent_write_buffer(clt, evb) == -1)
+ clt->clt_fd = fd;
+ if (clt->clt_srvbev != NULL)
+ bufferevent_free(clt->clt_srvbev);
+
+ clt->clt_srvbev_throttled = 0;
+ clt->clt_srvbev = bufferevent_new(clt->clt_fd, server_read_httprange,
+ server_write, server_file_error, clt);
+ if (clt->clt_srvbev == NULL) {
+ errstr = "failed to allocate file buffer event";
goto fail;
+ }
- evbuffer_free(evb);
- evb = NULL;
+ /* Adjust read watermark to the socket output buffer size */
+ bufferevent_setwatermark(clt->clt_srvbev, EV_READ, 0,
+ clt->clt_sndbufsiz);
- bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
- if (clt->clt_persist)
- clt->clt_toread = TOREAD_HTTP_HEADER;
- else
- clt->clt_toread = TOREAD_HTTP_NONE;
- clt->clt_done = 0;
+ bufferevent_settimeout(clt->clt_srvbev,
+ srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
+ bufferevent_enable(clt->clt_srvbev, EV_READ);
+ bufferevent_disable(clt->clt_bev, EV_READ);
done:
server_reset_http(clt);
@@ -602,7 +589,7 @@ void
server_file_error(struct bufferevent *bev, short error, void *arg)
{
struct client *clt = arg;
- struct evbuffer *dst;
+ struct evbuffer *src, *dst;
if (error & EVBUFFER_TIMEOUT) {
server_close(clt, "buffer event timeout");
@@ -621,6 +608,12 @@ server_file_error(struct bufferevent *bev, short error, void *arg)
clt->clt_done = 1;
+ src = EVBUFFER_INPUT(clt->clt_bev);
+
+ /* Close the connection if a previous pipeline is empty */
+ if (clt->clt_pipelining && EVBUFFER_LENGTH(src) == 0)
+ clt->clt_persist = 0;
+
if (clt->clt_persist) {
/* Close input file and wait for next HTTP request */
if (clt->clt_fd != -1)
@@ -629,6 +622,12 @@ server_file_error(struct bufferevent *bev, short error, void *arg)
clt->clt_toread = TOREAD_HTTP_HEADER;
server_reset_http(clt);
bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
+
+ /* Start pipelining if the buffer is not empty */
+ if (EVBUFFER_LENGTH(src)) {
+ clt->clt_pipelining++;
+ server_read_http(clt->clt_bev, arg);
+ }
return;
}
@@ -670,41 +669,44 @@ server_file_modified_since(struct http_descriptor *desc, struct stat *st)
return (-1);
}
-struct range *
-parse_range(char *str, size_t file_sz, int *nranges)
+int
+parse_ranges(struct client *clt, char *str, size_t file_sz)
{
- static struct range ranges[MAX_RANGES];
int i = 0;
char *p, *q;
+ struct range_data *r = &clt->clt_ranges;
+
+ memset(r, 0, sizeof(*r));
/* Extract range unit */
if ((p = strchr(str, '=')) == NULL)
- return (NULL);
+ return (-1);
*p++ = '\0';
/* Check if it's a bytes range spec */
if (strcmp(str, "bytes") != 0)
- return (NULL);
+ return (-1);
while ((q = strchr(p, ',')) != NULL) {
*q++ = '\0';
/* Extract start and end positions */
- if (parse_range_spec(p, file_sz, &ranges[i]) == 0)
+ if (parse_range_spec(p, file_sz, &r->range[i]) == 0)
continue;
i++;
- if (i == MAX_RANGES)
- return (NULL);
+ if (i == SERVER_MAX_RANGES)
+ return (-1);
p = q;
}
- if (parse_range_spec(p, file_sz, &ranges[i]) != 0)
+ if (parse_range_spec(p, file_sz, &r->range[i]) != 0)
i++;
- *nranges = i;
- return (i ? ranges : NULL);
+ r->range_total = file_sz;
+ r->range_count = i;
+ return (i);
}
int
@@ -754,26 +756,3 @@ parse_range_spec(char *str, size_t size, struct range *r)
return (1);
}
-
-int
-buffer_add_range(int fd, struct evbuffer *evb, struct range *range)
-{
- char buf[BUFSIZ];
- size_t n, range_sz;
- ssize_t nread;
-
- if (lseek(fd, range->start, SEEK_SET) == -1)
- return (0);
-
- range_sz = range->end - range->start + 1;
- while (range_sz) {
- n = MINIMUM(range_sz, sizeof(buf));
- if ((nread = read(fd, buf, n)) == -1)
- return (0);
-
- evbuffer_add(evb, buf, nread);
- range_sz -= nread;
- }
-
- return (1);
-}
diff --git a/httpd/server_http.c b/httpd/server_http.c
index b69805a..e64de0d 100644
--- a/httpd/server_http.c
+++ b/httpd/server_http.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: server_http.c,v 1.109 2016/07/27 11:02:41 reyk Exp $ */
+/* $OpenBSD: server_http.c,v 1.117 2017/05/15 10:40:47 jsg Exp $ */
/*
- * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006 - 2017 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
@@ -49,17 +49,12 @@ int server_http_authenticate(struct server_config *,
char *server_expand_http(struct client *, const char *,
char *, size_t);
-static struct httpd *env = NULL;
-
static struct http_method http_methods[] = HTTP_METHODS;
static struct http_error http_errors[] = HTTP_ERRORS;
void
-server_http(struct httpd *x_env)
+server_http(void)
{
- if (x_env != NULL)
- env = x_env;
-
DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid());
/* Sort the HTTP lookup arrays */
@@ -221,16 +216,34 @@ server_read_http(struct bufferevent *bev, void *arg)
goto done;
}
- while (!clt->clt_done && (line =
- evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT)) != NULL) {
- linelen = strlen(line);
+ while (!clt->clt_headersdone) {
+ if (!clt->clt_line) {
+ /* Peek into the buffer to see if it looks like HTTP */
+ key = EVBUFFER_DATA(src);
+ if (!isalpha(*key)) {
+ server_abort_http(clt, 400,
+ "invalid request line");
+ goto abort;
+ }
+ }
+
+ if ((line = evbuffer_readln(src,
+ &linelen, EVBUFFER_EOL_CRLF_STRICT)) == NULL) {
+ /* No newline found after too many bytes */
+ if (size > SERVER_MAXHEADERLENGTH) {
+ server_abort_http(clt, 413,
+ "request line too long");
+ goto abort;
+ }
+ break;
+ }
/*
* An empty line indicates the end of the request.
* libevent already stripped the \r\n for us.
*/
if (!linelen) {
- clt->clt_done = 1;
+ clt->clt_headersdone = 1;
free(line);
break;
}
@@ -365,7 +378,7 @@ server_read_http(struct bufferevent *bev, void *arg)
free(line);
}
- if (clt->clt_done) {
+ if (clt->clt_headersdone) {
if (desc->http_method == HTTP_METHOD_NONE) {
server_abort_http(clt, 406, "no method");
return;
@@ -377,7 +390,6 @@ server_read_http(struct bufferevent *bev, void *arg)
clt->clt_toread = TOREAD_UNLIMITED;
bev->readcb = server_read;
break;
- case HTTP_METHOD_DELETE:
case HTTP_METHOD_GET:
case HTTP_METHOD_HEAD:
/* WebDAV methods */
@@ -385,6 +397,7 @@ server_read_http(struct bufferevent *bev, void *arg)
case HTTP_METHOD_MOVE:
clt->clt_toread = 0;
break;
+ case HTTP_METHOD_DELETE:
case HTTP_METHOD_OPTIONS:
case HTTP_METHOD_POST:
case HTTP_METHOD_PUT:
@@ -435,7 +448,7 @@ server_read_http(struct bufferevent *bev, void *arg)
done:
if (clt->clt_toread != 0)
bufferevent_disable(bev, EV_READ);
- server_response(env, clt);
+ server_response(httpd_env, clt);
return;
}
if (clt->clt_done) {
@@ -615,6 +628,101 @@ server_read_httpchunks(struct bufferevent *bev, void *arg)
}
void
+server_read_httprange(struct bufferevent *bev, void *arg)
+{
+ struct client *clt = arg;
+ struct evbuffer *src = EVBUFFER_INPUT(bev);
+ size_t size;
+ struct media_type *media;
+ struct range_data *r = &clt->clt_ranges;
+ struct range *range;
+
+ getmonotime(&clt->clt_tv_last);
+
+ if (r->range_toread > 0) {
+ size = EVBUFFER_LENGTH(src);
+ if (!size)
+ return;
+
+ /* Read chunk data */
+ if ((off_t)size > r->range_toread) {
+ size = r->range_toread;
+ if (server_bufferevent_write_chunk(clt, src, size)
+ == -1)
+ goto fail;
+ r->range_toread = 0;
+ } else {
+ if (server_bufferevent_write_buffer(clt, src) == -1)
+ goto fail;
+ r->range_toread -= size;
+ }
+ if (r->range_toread < 1)
+ r->range_toread = TOREAD_HTTP_RANGE;
+ DPRINTF("%s: done, size %lu, to read %lld", __func__,
+ size, r->range_toread);
+ }
+
+ switch (r->range_toread) {
+ case TOREAD_HTTP_RANGE:
+ if (r->range_index >= r->range_count) {
+ if (r->range_count > 1) {
+ /* Add end marker */
+ if (server_bufferevent_printf(clt,
+ "\r\n--%llu--\r\n",
+ clt->clt_boundary) == -1)
+ goto fail;
+ }
+ r->range_toread = TOREAD_HTTP_NONE;
+ break;
+ }
+
+ range = &r->range[r->range_index];
+
+ if (r->range_count > 1) {
+ media = r->range_media;
+ if (server_bufferevent_printf(clt,
+ "\r\n--%llu\r\n"
+ "Content-Type: %s/%s\r\n"
+ "Content-Range: bytes %lld-%lld/%zu\r\n\r\n",
+ clt->clt_boundary,
+ media->media_type, media->media_subtype,
+ range->start, range->end, r->range_total) == -1)
+ goto fail;
+ }
+ r->range_toread = range->end - range->start + 1;
+
+ if (lseek(clt->clt_fd, range->start, SEEK_SET) == -1)
+ goto fail;
+
+ /* Throw away bytes that are already in the input buffer */
+ evbuffer_drain(src, EVBUFFER_LENGTH(src));
+
+ /* Increment for the next part */
+ r->range_index++;
+ break;
+ case TOREAD_HTTP_NONE:
+ case 0:
+ break;
+ }
+
+ if (clt->clt_done)
+ goto done;
+
+ if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(clt->clt_bev)) > (size_t)
+ SERVER_MAX_PREFETCH * clt->clt_sndbufsiz) {
+ bufferevent_disable(clt->clt_srvbev, EV_READ);
+ clt->clt_srvbev_throttled = 1;
+ }
+
+ return;
+ done:
+ (*bev->errorcb)(bev, EVBUFFER_READ, bev->cbarg);
+ return;
+ fail:
+ server_close(clt, strerror(errno));
+}
+
+void
server_reset_http(struct client *clt)
{
struct server *srv = clt->clt_srv;
@@ -624,8 +732,9 @@ server_reset_http(struct client *clt)
server_httpdesc_free(clt->clt_descreq);
server_httpdesc_free(clt->clt_descresp);
clt->clt_headerlen = 0;
- clt->clt_line = 0;
+ clt->clt_headersdone = 0;
clt->clt_done = 0;
+ clt->clt_line = 0;
clt->clt_chunk = 0;
free(clt->clt_remote_user);
clt->clt_remote_user = NULL;
@@ -778,6 +887,8 @@ server_abort_http(struct client *clt, unsigned int code, const char *msg)
msg = buf;
break;
case 401:
+ if (msg == NULL)
+ break;
if (stravis(&escapedmsg, msg, VIS_DQ) == -1) {
code = 500;
extraheader = NULL;
@@ -789,6 +900,8 @@ server_abort_http(struct client *clt, unsigned int code, const char *msg)
}
break;
case 416:
+ if (msg == NULL)
+ break;
if (asprintf(&extraheader,
"Content-Range: %s\r\n", msg) == -1) {
code = 500;
@@ -959,6 +1072,14 @@ server_expand_http(struct client *clt, const char *val, char *buf,
if (ret != 0)
return (NULL);
}
+ if (strstr(val, "$HTTP_HOST") != NULL) {
+ if (desc->http_host == NULL)
+ return (NULL);
+ if ((str = url_encode(desc->http_host)) == NULL)
+ return (NULL);
+ expand_string(buf, len, "$HTTP_HOST", str);
+ free(str);
+ }
if (strstr(val, "$REMOTE_") != NULL) {
if (strstr(val, "$REMOTE_ADDR") != NULL) {
if (print_host(&clt->clt_ss,
@@ -1095,6 +1216,10 @@ server_response(struct httpd *httpd, struct client *clt)
if (clt->clt_persist >= srv_conf->maxrequests)
clt->clt_persist = 0;
+ /* pipelining should end after the first "idempotent" method */
+ if (clt->clt_pipelining && clt->clt_toread > 0)
+ clt->clt_persist = 0;
+
/*
* Do we have a Host header and matching configuration?
* XXX the Host can also appear in the URL path.
diff --git a/regress/tests/Httpd.pm b/regress/tests/Httpd.pm
index d5b9df0..485ff68 100644
--- a/regress/tests/Httpd.pm
+++ b/regress/tests/Httpd.pm
@@ -1,4 +1,4 @@
-# $OpenBSD: Httpd.pm,v 1.1 2015/07/16 16:35:57 reyk Exp $
+# $OpenBSD: Httpd.pm,v 1.2 2017/01/30 21:18:24 reyk Exp $
# Copyright (c) 2010-2015 Alexander Bluhm <bluhm@openbsd.org>
# Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -27,6 +27,7 @@ sub new {
my $class = shift;
my %args = @_;
$args{chroot} ||= ".";
+ $args{docroot} ||= "htdocs";
$args{logfile} ||= $args{chroot}."/httpd.log";
$args{up} ||= $args{dryrun} || "server_launch: ";
$args{down} ||= $args{dryrun} ? "httpd.conf:" : "parent terminating";
@@ -54,7 +55,7 @@ sub new {
my $listenport = $self->{listenport};
print $fh "prefork 1\n"; # only crashes of first child are observed
- print $fh "chroot \"".$args{chroot}."\"\n";
+ print $fh "chroot \"".$args{docroot}."\"\n";
print $fh "logdir \"".$args{chroot}."\"\n";
my @http = @{$self->{http}};
diff --git a/regress/tests/LICENSE b/regress/tests/LICENSE
index 8f60827..a5303a4 100644
--- a/regress/tests/LICENSE
+++ b/regress/tests/LICENSE
@@ -1,4 +1,4 @@
-# Copyright (c) 2010-2015 Alexander Bluhm <bluhm@openbsd.org>
+# Copyright (c) 2010-2017 Alexander Bluhm <bluhm@openbsd.org>
# Copyright (c) 2014,2015 Reyk Floeter <reyk@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
diff --git a/regress/tests/Makefile b/regress/tests/Makefile
index 5298c7c..1c5c1ec 100644
--- a/regress/tests/Makefile
+++ b/regress/tests/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.2 2015/07/16 17:00:41 reyk Exp $
+# $OpenBSD: Makefile,v 1.8 2017/07/14 13:31:44 bluhm Exp $
# The following ports must be installed for the regression tests:
# p5-IO-Socket-INET6 object interface for AF_INET and AF_INET6 domain sockets
@@ -17,18 +17,9 @@ PERL_REQUIRE != perl -Mstrict -Mwarnings -e ' \
regress:
@echo "${PERL_REQUIRE}"
@echo install these perl packages for additional tests
+ @echo SKIPPED
.endif
-# Fill out these variables if you want to test httpd with
-# the httpd process running on a remote machine. You have to specify
-# a local and remote ip address for the tcp connections. To control
-# the remote machine you need a hostname for ssh to log in. All the
-# test files must be in the same directory local and remote.
-
-LOCAL_ADDR ?=
-REMOTE_ADDR ?=
-REMOTE_SSH ?=
-
# Automatically generate regress targets from test cases in directory.
ARGS != cd ${.CURDIR} && ls args-*.pl
@@ -37,10 +28,10 @@ REGRESS_TARGETS = ${TARGETS:S/^/run-regress-/}
CLEANFILES += *.log httpd.conf ktrace.out stamp-*
CLEANFILES += *.pem *.req *.crt *.key *.srl md5-*
-HTDOCS = 512 1048576 1073741824
-HTDOCS_MD5 = ${HTDOCS:S/^/${.OBJDIR}\/md5-/}
+HTDOCS_FILES = 512 1048576 1073741824
+HTDOCS_MD5 = ${HTDOCS_FILES:S,^,md5-,}
HTDOCS_SPARSE = yes
-CLEANFILES += ${HTDOCS}
+CLEANFILES += htdocs/*
# Set variables so that make runs with and without obj directory.
# Only do that if necessary to keep visible output short.
@@ -62,19 +53,20 @@ run-regress-$a: $a ${HTDOCS_MD5}
time SUDO=${SUDO} KTRACE=${KTRACE} HTTPD=${HTTPD} perl ${PERLINC} ${PERLPATH}httpd.pl ${.OBJDIR} ${PERLPATH}$a
.endfor
-# htdocs
+# populate htdocs
-.for d in ${HTDOCS}
-${.OBJDIR}/$d:
+.for d in ${HTDOCS_FILES}
+htdocs/$d:
@echo '\n======== file: $d ========'
+ mkdir -m 0755 -p ${@:H}
.if (${HTDOCS_SPARSE} != "yes")
- @dd if=/dev/arandom of=$@ count=$$(($d / 512)) bs=512
+ dd if=/dev/arandom of=$@ count=$$(($d / 512)) bs=512
.else
- @dd of=$@ seek=$$(($d / 512)) bs=512 count=0 status=none
+ dd of=$@ seek=$$(($d / 512)) bs=512 count=0 status=none
.endif
-${.OBJDIR}/md5-$d: ${.OBJDIR}/$d
- @md5 -q ${.OBJDIR}/$d > $@
+md5-$d: htdocs/$d
+ md5 -q htdocs/$d >$@
.endfor
# create certificates for TLS
diff --git a/regress/tests/README b/regress/tests/README
index c9c7836..0d2c8c0 100644
--- a/regress/tests/README
+++ b/regress/tests/README
@@ -1,16 +1,16 @@
-Run httpd regressions tests. The framework runs a client and a httpd.
+Run httpd regression tests. The framework runs a client and an httpd.
Each test creates a special httpd.conf and starts those two processes.
All processes write log files that are checked for certain messages.
The test arguments are kept in the args-*.pl files.
-SUDO=sudo
-As httpd needs root privileges either run the tests as root or set
+SUDO=doas
+As httpd needs root privileges, either run the tests as root or set
this variable and run make as a regular user. Only the code that
-requires it, is run as root.
+requires it is run as root.
KTRACE=ktrace
Set this variable if you want a ktrace output from httpd. Note that
-ktrace is invoked after sudo as sudo would disable it.
+ktrace is invoked after SUDO as SUDO would disable it.
HTTPD=/usr/src/usr.sbin/httpd/obj/httpd
Start an alternative httpd program that is not in the path.
diff --git a/regress/tests/args-get-1048576.pl b/regress/tests/args-get-1048576.pl
index 9253aec..fa06bba 100644
--- a/regress/tests/args-get-1048576.pl
+++ b/regress/tests/args-get-1048576.pl
@@ -6,7 +6,7 @@ our %args = (
client => {
path => "$len",
len => $len,
- http_vers => [ "1.0" ],
+ http_vers => [ "1.0" ],
},
len => 1048576,
md5 => path_md5("$len")
diff --git a/regress/tests/args-get-1073741824.pl b/regress/tests/args-get-1073741824.pl
index 2b4c5f4..c27e00d 100644
--- a/regress/tests/args-get-1073741824.pl
+++ b/regress/tests/args-get-1073741824.pl
@@ -6,7 +6,7 @@ my @lengths = ($len, $len);
our %args = (
client => {
path => "$len",
- http_vers => [ "1.0" ],
+ http_vers => [ "1.0" ],
lengths => \@lengths,
},
md5 => path_md5("$len"),
diff --git a/regress/tests/args-get-512.pl b/regress/tests/args-get-512.pl
index 20e92c4..6a0b79a 100644
--- a/regress/tests/args-get-512.pl
+++ b/regress/tests/args-get-512.pl
@@ -6,7 +6,7 @@ my @lengths = ($len, $len, $len);
our %args = (
client => {
path => "$len",
- http_vers => [ "1.0" ],
+ http_vers => [ "1.0" ],
lengths => \@lengths,
},
md5 => path_md5("$len"),
diff --git a/regress/tests/args-get-slash.pl b/regress/tests/args-get-slash.pl
index 9406f4b..e3e7a3b 100644
--- a/regress/tests/args-get-slash.pl
+++ b/regress/tests/args-get-slash.pl
@@ -7,11 +7,11 @@ our %args = (
my $self = shift;
print "GET /\r\n\r\n";
},
- nocheck => 1
+ nocheck => 1
},
httpd => {
loggrep => {
- qr/"GET \/" 500 0/ => 1,
+ qr/"GET \/" 400 0/ => 1,
},
},
);
diff --git a/regress/tests/funcs.pl b/regress/tests/funcs.pl
index beda09a..fde3807 100644
--- a/regress/tests/funcs.pl
+++ b/regress/tests/funcs.pl
@@ -1,6 +1,6 @@
-# $OpenBSD: funcs.pl,v 1.6 2016/05/03 19:13:04 bluhm Exp $
+# $OpenBSD: funcs.pl,v 1.8 2017/07/14 13:31:44 bluhm Exp $
-# Copyright (c) 2010-2015 Alexander Bluhm <bluhm@openbsd.org>
+# Copyright (c) 2010-2017 Alexander Bluhm <bluhm@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
@@ -188,9 +188,12 @@ sub http_request {
sub http_response {
my ($self, $len) = @_;
my $method = $self->{method} || "GET";
+ my $code = $self->{code} || "200 OK";
my $vers;
my $chunked = 0;
+ my $multipart = 0;
+ my $boundary;
{
local $/ = "\r\n";
local $_ = <STDIN>;
@@ -198,8 +201,8 @@ sub http_response {
or die ref($self), " missing http $len response";
chomp;
print STDERR "<<< $_\n";
- m{^HTTP/(\d\.\d) 200 OK$}
- or die ref($self), " http response not ok"
+ m{^HTTP/(\d\.\d) $code$}
+ or die ref($self), " http response not $code"
unless $self->{httpnok};
$vers = $1;
while (<STDIN>) {
@@ -207,7 +210,7 @@ sub http_response {
print STDERR "<<< $_\n";
last if /^$/;
if (/^Content-Length: (.*)/) {
- if ($self->{httpnok}) {
+ if ($self->{httpnok} or $self->{multipart}) {
$len = $1;
} else {
$1 == $len or die ref($self),
@@ -217,12 +220,20 @@ sub http_response {
if (/^Transfer-Encoding: chunked$/) {
$chunked = 1;
}
+ if (/^Content-Type: multipart\/byteranges; boundary=(.*)$/) {
+ $multipart = 1;
+ $boundary = $1;
+ }
}
}
- if ($chunked) {
+ die ref($self), " no multipart response"
+ if ($self->{multipart} && $multipart == 0);
+
+ if ($multipart) {
+ read_multipart($self, $boundary);
+ } elsif ($chunked) {
read_chunked($self);
} else {
- #$len = $vers eq "1.1" ? $len : undef;
read_char($self, $len)
if $method eq "GET";
}
@@ -265,6 +276,47 @@ sub read_chunked {
}
}
+sub read_multipart {
+ my $self = shift;
+ my $boundary = shift;
+ my $ctx = Digest::MD5->new();
+ my $len = 0;
+
+ for (;;) {
+ my $part = 0;
+ {
+ local $/ = "\r\n";
+ local $_ = <STDIN>;
+ local $_ = <STDIN>;
+ defined or die ref($self), " missing boundary";
+ chomp;
+ print STDERR "<<< $_\n";
+ /^--$boundary(--)?$/
+ or die ref($self), " boundary not found: $_";
+ if (not $1) {
+ while (<STDIN>) {
+ chomp;
+ if (/^Content-Length: (.*)/) {
+ $part = $1;
+ }
+ if (/^Content-Range: bytes (\d+)-(\d+)\/(\d+)$/) {
+ $part = $2 - $1 + 1;
+ }
+ print STDERR "<<< $_\n";
+ last if /^$/;
+ }
+ }
+ }
+ last unless $part > 0;
+
+ $len += read_part($self, $ctx, $part);
+ }
+
+ print STDERR "LEN: ", $len, "\n";
+ print STDERR "MD5: ", $ctx->hexdigest, "\n";
+
+}
+
sub errignore {
$SIG{PIPE} = 'IGNORE';
$SIG{__DIE__} = sub {
@@ -277,7 +329,7 @@ sub errignore {
}
########################################################################
-# Server funcs
+# Common funcs
########################################################################
sub read_char {
@@ -285,107 +337,39 @@ sub read_char {
my $max = shift // $self->{max};
my $ctx = Digest::MD5->new();
- my $len = 0;
- if (defined($max) && $max == 0) {
- print STDERR "Max\n";
- } else {
- while ((my $r = sysread(STDIN, my $buf, POSIX::BUFSIZ))) {
- my $pct;
- $_ = $buf;
- $len += $r;
- $ctx->add($_);
- $pct = ($len / $max) * 100.0;
- printf(STDERR "%.2f%%\n", $pct);
- if (defined($max) && $len >= $max) {
- print STDERR "\nMax";
- last;
- }
- }
- print STDERR "\n";
- }
+ my $len = read_part($self, $ctx, $max);
print STDERR "LEN: ", $len, "\n";
print STDERR "MD5: ", $ctx->hexdigest, "\n";
}
-sub http_server {
+sub read_part {
my $self = shift;
- my %header = %{$self->{header} || { Server => "Perl/".$^V }};
- my $cookie = $self->{cookie} || "";
+ my ($ctx, $max) = @_;
- my($method, $url, $vers);
- do {
- my $len;
- {
- local $/ = "\r\n";
- local $_ = <STDIN>;
- return unless defined $_;
- chomp;
- print STDERR "<<< $_\n";
- ($method, $url, $vers) = m{^(\w+) (.*) HTTP/(1\.[01])$}
- or die ref($self), " http request not ok";
- $method =~ /^(GET|PUT)$/
- or die ref($self), " unknown method: $method";
- ($len, my @chunks) = $url =~ /(\d+)/g;
- $len = [ $len, @chunks ] if @chunks;
- while (<STDIN>) {
- chomp;
- print STDERR "<<< $_\n";
- last if /^$/;
- if ($method eq "PUT" &&
- /^Content-Length: (.*)/) {
- $1 == $len or die ref($self),
- " bad content length $1";
- }
- $cookie ||= $1 if /^Cookie: (.*)/;
- }
- }
- if ($method eq "PUT" ) {
- if (ref($len) eq 'ARRAY') {
- read_chunked($self);
- } else {
- read_char($self, $len);
- }
- }
-
- my @response = ("HTTP/$vers 200 OK");
- $len = defined($len) ? $len : scalar(split /|/,$url);
- if ($vers eq "1.1" && $method eq "GET") {
- if (ref($len) eq 'ARRAY') {
- push @response, "Transfer-Encoding: chunked";
- } else {
- push @response, "Content-Length: $len";
- }
+ my $opct = 0;
+ my $len = 0;
+ for (;;) {
+ if (defined($max) && $len >= $max) {
+ print STDERR "Max\n";
+ last;
}
- foreach my $key (sort keys %header) {
- my $val = $header{$key};
- if (ref($val) eq 'ARRAY') {
- push @response, "$key: $_"
- foreach @{$val};
- } else {
- push @response, "$key: $val";
- }
+ my $rlen = POSIX::BUFSIZ;
+ if (defined($max) && $rlen > $max - $len) {
+ $rlen = $max - $len;
}
- push @response, "Set-Cookie: $cookie" if $cookie;
- push @response, "";
-
- print STDERR map { ">>> $_\n" } @response;
- print map { "$_\r\n" } @response;
-
- if ($method eq "GET") {
- if (ref($len) eq 'ARRAY') {
- if ($vers eq "1.1") {
- write_chunked($self, @$len);
- } else {
- write_char($self, $_) foreach (@$len);
- }
- } else {
- write_char($self, $len);
- }
+ defined(my $n = read(STDIN, my $buf, $rlen))
+ or die ref($self), " read failed: $!";
+ $n or last;
+ $len += $n;
+ $ctx->add($buf);
+ my $pct = ($len / $max) * 100.0;
+ if ($pct >= $opct + 1) {
+ printf(STDERR "%.2f%% $len/$max\n", $pct);
+ $opct = $pct;
}
- IO::Handle::flush(\*STDOUT);
- } while ($vers eq "1.1");
- $self->{redo}-- if $self->{redo};
+ }
+ return $len;
}
sub write_chunked {