From ce95017bf16f7937013134fc5188900d3e2cd9c7 Mon Sep 17 00:00:00 2001 From: Reyk Floeter Date: Wed, 16 Aug 2017 09:27:36 +0200 Subject: sync --- httpd/server.c | 288 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 198 insertions(+), 90 deletions(-) (limited to 'httpd/server.c') 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 @@ -16,7 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include /* nitems */ #include #include #include @@ -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); @@ -180,9 +177,30 @@ server_tls_load_keypair(struct server *srv) return (0); } +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; @@ -251,17 +312,24 @@ server_tls_init(struct server *srv) return (0); } +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); -- cgit v1.2.3-54-g00ecf