Proxy commit for jsing@:
authorreyk <reyk@openbsd.org>
Mon, 4 Aug 2014 17:38:12 +0000 (17:38 +0000)
committerreyk <reyk@openbsd.org>
Mon, 4 Aug 2014 17:38:12 +0000 (17:38 +0000)
"Add TLS/SSL support to httpd, based on the recent ressl commits."

From jsing@
ok reyk@

usr.sbin/httpd/Makefile
usr.sbin/httpd/config.c
usr.sbin/httpd/httpd.conf.5
usr.sbin/httpd/httpd.h
usr.sbin/httpd/parse.y
usr.sbin/httpd/server.c

index 13f6e60..63d50f4 100644 (file)
@@ -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
index c8e8af4..35405b8 100644 (file)
@@ -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 <reyk@openbsd.org>
@@ -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)
index c04ddd1..f25136e 100644 (file)
@@ -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 <reyk@openbsd.org>
 .\"
@@ -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.
index afd759a..9a7ef8b 100644 (file)
@@ -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 <reyk@openbsd.org>
@@ -26,6 +26,7 @@
 #include <sys/param.h>         /* MAXHOSTNAMELEN */
 #include <limits.h>
 #include <imsg.h>
+#include <ressl.h>
 
 #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);
index 7c42e47..649bd97 100644 (file)
@@ -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 <reyk@openbsd.org>
@@ -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 <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.port>        port
+%type  <v.number>      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 }
index 84f586f..5748a85 100644 (file)
@@ -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 <reyk@openbsd.org>
@@ -42,6 +42,7 @@
 #include <pwd.h>
 #include <event.h>
 #include <fnmatch.h>
+#include <ressl.h>
 
 #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)