From 3fe67476d6c260ccd3a1056e3115306519c068c0 Mon Sep 17 00:00:00 2001 From: reyk Date: Mon, 4 Aug 2014 17:38:12 +0000 Subject: [PATCH] Proxy commit for jsing@: "Add TLS/SSL support to httpd, based on the recent ressl commits." From jsing@ ok reyk@ --- usr.sbin/httpd/Makefile | 6 +- usr.sbin/httpd/config.c | 5 +- usr.sbin/httpd/httpd.conf.5 | 4 +- usr.sbin/httpd/httpd.h | 10 +- usr.sbin/httpd/parse.y | 17 ++- usr.sbin/httpd/server.c | 228 ++++++++++++++++++++++++++++++++++-- 6 files changed, 251 insertions(+), 19 deletions(-) diff --git a/usr.sbin/httpd/Makefile b/usr.sbin/httpd/Makefile index 13f6e60c4e0..63d50f420b3 100644 --- a/usr.sbin/httpd/Makefile +++ b/usr.sbin/httpd/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.24 2014/08/04 15:49:28 reyk Exp $ +# $OpenBSD: Makefile,v 1.25 2014/08/04 17:38:12 reyk Exp $ PROG= httpd SRCS= parse.y @@ -6,8 +6,8 @@ SRCS+= config.c control.c httpd.c log.c logger.c proc.c SRCS+= server.c server_http.c server_file.c server_fcgi.c MAN= httpd.8 httpd.conf.5 -LDADD= -levent -lutil -DPADD= ${LIBEVENT} ${LIBUTIL} +LDADD= -levent -lressl -lssl -lcrypto -lutil +DPADD= ${LIBEVENT} ${LIBRESSL} ${LIBSSL} ${LIBCRYPTO} ${LIBUTIL} #DEBUG= -g -DDEBUG=3 CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes diff --git a/usr.sbin/httpd/config.c b/usr.sbin/httpd/config.c index c8e8af48c52..35405b8499b 100644 --- a/usr.sbin/httpd/config.c +++ b/usr.sbin/httpd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.14 2014/08/04 15:49:28 reyk Exp $ */ +/* $OpenBSD: config.c,v 1.15 2014/08/04 17:38:12 reyk Exp $ */ /* * Copyright (c) 2011 - 2014 Reyk Floeter @@ -305,8 +305,7 @@ config_getserver(struct httpd *env, struct imsg *imsg) /* Add "host" to existing listening server */ if (imsg->fd != -1) close(imsg->fd); - return (config_getserver_config(env, - srv, imsg)); + return (config_getserver_config(env, srv, imsg)); } if (srv_conf.flags & SRVFLAG_LOCATION) diff --git a/usr.sbin/httpd/httpd.conf.5 b/usr.sbin/httpd/httpd.conf.5 index c04ddd1ed78..f25136e1332 100644 --- a/usr.sbin/httpd/httpd.conf.5 +++ b/usr.sbin/httpd/httpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: httpd.conf.5,v 1.20 2014/08/04 17:12:44 reyk Exp $ +.\" $OpenBSD: httpd.conf.5,v 1.21 2014/08/04 17:38:12 reyk Exp $ .\" .\" Copyright (c) 2014 Reyk Floeter .\" @@ -133,7 +133,7 @@ root directory of .Nm httpd and defaults to .Pa /run/slowcgi.sock . -.It Ic listen on Ar address Ic port Ar number +.It Ic listen on Ar address Ic port Ar number Op Ic ssl Set the listen address and port. .It Ic location Ar path { ... } Specify server configuration rules for a specific location. diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h index afd759a8317..9a7ef8b823f 100644 --- a/usr.sbin/httpd/httpd.h +++ b/usr.sbin/httpd/httpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.h,v 1.40 2014/08/04 15:49:28 reyk Exp $ */ +/* $OpenBSD: httpd.h,v 1.41 2014/08/04 17:38:12 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -26,6 +26,7 @@ #include /* MAXHOSTNAMELEN */ #include #include +#include #define CONF_FILE "/etc/httpd.conf" #define HTTPD_SOCKET "/var/run/httpd.sock" @@ -262,11 +263,14 @@ struct client { in_port_t clt_port; struct sockaddr_storage clt_ss; struct bufferevent *clt_bev; + char *clt_buf; + size_t clt_buflen; struct evbuffer *clt_output; struct event clt_ev; void *clt_desc; int clt_fd; + struct ressl *clt_ressl_ctx; struct bufferevent *clt_srvbev; off_t clt_toread; @@ -305,6 +309,7 @@ SPLAY_HEAD(client_tree, client); #define SRVFLAG_SOCKET 0x0400 #define SRVFLAG_SYSLOG 0x0800 #define SRVFLAG_NO_SYSLOG 0x1000 +#define SRVFLAG_SSL 0x2000 #define SRVFLAG_BITS \ "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX" \ @@ -366,6 +371,9 @@ struct server { struct event srv_ev; struct event srv_evt; + struct ressl *srv_ressl_ctx; + struct ressl_config *srv_ressl_config; + struct client_tree srv_clients; }; TAILQ_HEAD(serverlist, server); diff --git a/usr.sbin/httpd/parse.y b/usr.sbin/httpd/parse.y index 7c42e47a421..649bd977981 100644 --- a/usr.sbin/httpd/parse.y +++ b/usr.sbin/httpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.22 2014/08/04 16:07:59 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.23 2014/08/04 17:38:12 reyk Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter @@ -125,11 +125,13 @@ typedef struct { %} %token AUTO COMMON COMBINED CONNECTION DIRECTORY FCGI FILE INDEX LISTEN -%token LOCATION LOG NO ON PORT PREFORK ROOT SERVER SOCKET STYLE SYSLOG TYPES +%token LOCATION LOG NO ON PORT PREFORK ROOT SERVER SOCKET SSL STYLE SYSLOG +%token TYPES %token ERROR INCLUDE %token STRING %token NUMBER %type port +%type optssl %% @@ -166,6 +168,10 @@ varset : STRING '=' STRING { } ; +optssl : /*empty*/ { $$ = 0; } + | SSL { $$ = 1; } + ; + main : PREFORK NUMBER { if (loadcfg) break; @@ -240,7 +246,7 @@ serveropts_l : serveropts_l serveroptsl nl | serveroptsl optnl ; -serveroptsl : LISTEN ON STRING port { +serveroptsl : LISTEN ON STRING port optssl { struct addresslist al; struct address *h; struct server *s; @@ -276,6 +282,10 @@ serveroptsl : LISTEN ON STRING port { s->srv_conf.port = h->port.val[0]; s->srv_conf.prefixlen = h->prefixlen; host_free(&al); + + if ($5) { + s->srv_conf.flags |= SRVFLAG_SSL; + } } | ROOT STRING { if (strlcpy(srv->srv_conf.root, $2, @@ -620,6 +630,7 @@ lookup(char *s) { "root", ROOT }, { "server", SERVER }, { "socket", SOCKET }, + { "ssl", SSL }, { "style", STYLE }, { "syslog", SYSLOG }, { "types", TYPES } diff --git a/usr.sbin/httpd/server.c b/usr.sbin/httpd/server.c index 84f586fbfac..5748a85fefc 100644 --- a/usr.sbin/httpd/server.c +++ b/usr.sbin/httpd/server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server.c,v 1.26 2014/08/04 15:49:28 reyk Exp $ */ +/* $OpenBSD: server.c,v 1.27 2014/08/04 17:38:12 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -42,6 +42,7 @@ #include #include #include +#include #include "httpd.h" @@ -58,7 +59,12 @@ int server_socket(struct sockaddr_storage *, in_port_t, int server_socket_listen(struct sockaddr_storage *, in_port_t, struct server_config *); +int server_ssl_init(struct server *); +void server_ssl_readcb(int, short, void *); +void server_ssl_writecb(int, short, void *); + void server_accept(int, short, void *); +void server_accept_ssl(int, short, void *); void server_input(struct client *); extern void bufferevent_read_pressure_cb(struct evbuffer *, size_t, @@ -108,6 +114,40 @@ server_privinit(struct server *srv) return (0); } +int +server_ssl_init(struct server *srv) +{ + if ((srv->srv_conf.flags & SRVFLAG_SSL) == 0) + return (0); + + log_debug("%s: setting up SSL for %s", __func__, srv->srv_conf.name); + + if (ressl_init() != 0) { + log_warn("%s: failed to initialise ressl", __func__); + return (-1); + } + if ((srv->srv_ressl_config = ressl_config_new()) == NULL) { + log_warn("%s: failed to get ressl config", __func__); + return (-1); + } + if ((srv->srv_ressl_ctx = ressl_server()) == NULL) { + log_warn("%s: failed to get ressl server", __func__); + return (-1); + } + + /* XXX - make these configurable. */ + ressl_config_set_cert_file(srv->srv_ressl_config, "/server.crt"); + ressl_config_set_key_file(srv->srv_ressl_config, "/server.key"); + + if (ressl_configure(srv->srv_ressl_ctx, srv->srv_ressl_config) != 0) { + log_warn("%s: failed to configure SSL - %s", __func__, + ressl_error(srv->srv_ressl_ctx)); + return (-1); + } + + return (0); +} + void server_init(struct privsep *ps, struct privsep_proc *p, void *arg) { @@ -139,6 +179,7 @@ server_launch(void) struct server *srv; TAILQ_FOREACH(srv, env->sc_servers, srv_entry) { + server_ssl_init(srv); server_http_init(srv); log_debug("%s: running server %s", __func__, @@ -181,6 +222,9 @@ server_purge(struct server *srv) free(srv_conf); } + ressl_config_free(srv->srv_ressl_config); + ressl_free(srv->srv_ressl_ctx); + free(srv); } @@ -363,6 +407,124 @@ server_socket_connect(struct sockaddr_storage *ss, in_port_t port, return (-1); } +void +server_ssl_readcb(int fd, short event, void *arg) +{ + struct bufferevent *bufev = arg; + struct client *clt = bufev->cbarg; + char rbuf[IBUF_READ_SIZE]; + int what = EVBUFFER_READ; + int howmuch = IBUF_READ_SIZE; + int ret; + size_t len; + + if (event == EV_TIMEOUT) { + what |= EVBUFFER_TIMEOUT; + goto err; + } + + if (bufev->wm_read.high != 0) + howmuch = MIN(sizeof(rbuf), bufev->wm_read.high); + + ret = ressl_read(clt->clt_ressl_ctx, rbuf, howmuch, &len); + if (ret == RESSL_READ_AGAIN || ret == RESSL_WRITE_AGAIN) { + goto retry; + } else if (ret != 0) { + what |= EVBUFFER_ERROR; + goto err; + } + + if (evbuffer_add(bufev->input, rbuf, len) == -1) { + what |= EVBUFFER_ERROR; + goto err; + } + + server_bufferevent_add(&bufev->ev_read, bufev->timeout_read); + + len = EVBUFFER_LENGTH(bufev->input); + if (bufev->wm_read.low != 0 && len < bufev->wm_read.low) + return; + if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) { + struct evbuffer *buf = bufev->input; + event_del(&bufev->ev_read); + evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev); + return; + } + + if (bufev->readcb != NULL) + (*bufev->readcb)(bufev, bufev->cbarg); + return; + +retry: + server_bufferevent_add(&bufev->ev_read, bufev->timeout_read); + return; + +err: + (*bufev->errorcb)(bufev, what, bufev->cbarg); +} + +void +server_ssl_writecb(int fd, short event, void *arg) +{ + struct bufferevent *bufev = arg; + struct client *clt = bufev->cbarg; + int ret; + short what = EVBUFFER_WRITE; + size_t len; + + if (event == EV_TIMEOUT) { + what |= EVBUFFER_TIMEOUT; + goto err; + } + + if (EVBUFFER_LENGTH(bufev->output)) { + if (clt->clt_buf == NULL) { + clt->clt_buflen = EVBUFFER_LENGTH(bufev->output); + if ((clt->clt_buf = malloc(clt->clt_buflen)) == NULL) { + what |= EVBUFFER_ERROR; + goto err; + } + bcopy(EVBUFFER_DATA(bufev->output), + clt->clt_buf, clt->clt_buflen); + } + ret = ressl_write(clt->clt_ressl_ctx, clt->clt_buf, + clt->clt_buflen, &len); + if (ret == RESSL_READ_AGAIN || ret == RESSL_WRITE_AGAIN) { + goto retry; + } else if (ret != 0) { + what |= EVBUFFER_ERROR; + goto err; + } + evbuffer_drain(bufev->output, len); + } + if (clt->clt_buf != NULL) { + free(clt->clt_buf); + clt->clt_buf = NULL; + clt->clt_buflen = 0; + } + + if (EVBUFFER_LENGTH(bufev->output) != 0) + server_bufferevent_add(&bufev->ev_write, bufev->timeout_write); + + if (bufev->writecb != NULL && + EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low) + (*bufev->writecb)(bufev, bufev->cbarg); + return; + +retry: + if (clt->clt_buflen != 0) + server_bufferevent_add(&bufev->ev_write, bufev->timeout_write); + return; + +err: + if (clt->clt_buf != NULL) { + free(clt->clt_buf); + clt->clt_buf = NULL; + clt->clt_buflen = 0; + } + (*bufev->errorcb)(bufev, what, bufev->cbarg); +} + void server_input(struct client *clt) { @@ -371,8 +533,7 @@ server_input(struct client *clt) evbuffercb inwr = server_write; if (server_httpdesc_init(clt) == -1) { - server_close(clt, - "failed to allocate http descriptor"); + server_close(clt, "failed to allocate http descriptor"); return; } @@ -389,6 +550,13 @@ server_input(struct client *clt) return; } + if (srv_conf->flags & SRVFLAG_SSL) { + event_set(&clt->clt_bev->ev_read, clt->clt_s, EV_READ, + server_ssl_readcb, clt->clt_bev); + event_set(&clt->clt_bev->ev_write, clt->clt_s, EV_WRITE, + server_ssl_writecb, clt->clt_bev); + } + bufferevent_settimeout(clt->clt_bev, srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE); @@ -417,6 +585,8 @@ server_write(struct bufferevent *bev, void *arg) void server_dump(struct client *clt, const void *buf, size_t len) { + size_t outlen; + if (!len) return; @@ -426,11 +596,9 @@ server_dump(struct client *clt, const void *buf, size_t len) * of non-blocking events etc. This is useful to print an * error message before gracefully closing the client. */ -#if 0 - if (cre->ssl != NULL) - (void)SSL_write(cre->ssl, buf, len); + if (clt->clt_ressl_ctx != NULL) + (void)ressl_write(clt->clt_ressl_ctx, buf, len, &outlen); else -#endif (void)write(clt->clt_s, buf, len); } @@ -573,6 +741,13 @@ server_accept(int fd, short event, void *arg) return; } + if (srv->srv_conf.flags & SRVFLAG_SSL) { + event_again(&clt->clt_ev, clt->clt_s, EV_TIMEOUT|EV_READ, + server_accept_ssl, &clt->clt_tv_start, + &srv->srv_conf.timeout, clt); + return; + } + server_input(clt); return; @@ -589,6 +764,41 @@ server_accept(int fd, short event, void *arg) } } +void +server_accept_ssl(int fd, short event, void *arg) +{ + struct client *clt = (struct client *)arg; + struct server *srv = (struct server *)clt->clt_srv; + int ret; + + if (event == EV_TIMEOUT) { + server_close(clt, "SSL accept timeout"); + return; + } + + if (srv->srv_ressl_ctx == NULL) + fatalx("NULL ressl context"); + + ret = ressl_accept_socket(srv->srv_ressl_ctx, &clt->clt_ressl_ctx, + clt->clt_s); + if (ret == RESSL_READ_AGAIN) { + event_again(&clt->clt_ev, clt->clt_s, EV_TIMEOUT|EV_READ, + server_accept_ssl, &clt->clt_tv_start, + &srv->srv_conf.timeout, clt); + } else if (ret == RESSL_WRITE_AGAIN) { + event_again(&clt->clt_ev, clt->clt_s, EV_TIMEOUT|EV_WRITE, + server_accept_ssl, &clt->clt_tv_start, + &srv->srv_conf.timeout, clt); + } else if (ret != 0) { + log_warnx("%s: SSL accept failed - %s", __func__, + ressl_error(srv->srv_ressl_ctx)); + return; + } + + server_input(clt); + return; +} + void server_inflight_dec(struct client *clt, const char *why) { @@ -728,6 +938,10 @@ server_close(struct client *clt, const char *msg) if (clt->clt_s != -1) close(clt->clt_s); + if (clt->clt_ressl_ctx != NULL) + ressl_close(clt->clt_ressl_ctx); + ressl_free(clt->clt_ressl_ctx); + server_inflight_dec(clt, __func__); if (clt->clt_log != NULL) -- 2.20.1