diff --git a/configure.ac b/configure.ac index 16e66d6..08026cb 100644 --- a/configure.ac +++ b/configure.ac @@ -323,6 +323,33 @@ AC_ARG_WITH(openssl-libs, [ use_openssl=yes LDFLAGS="$LDFLAGS -L$withval" ] ) +dnl Check for tlssep +AC_MSG_CHECKING(for tlssep) +AC_ARG_WITH(tlssep, + AC_HELP_STRING([--with-tlssep@<:@=DIR@:>@],[Include tlssep support (default no)]), + [WITH_TLSSEP=$withval],[WITH_TLSSEP=no]) + +if test "$WITH_TLSSEP" != "no"; then + use_tlssep=yes + if test "$WITH_TLSSEP" != "yes"; then + CPPFLAGS="$CPPFLAGS -I$WITH_TLSSEP/include" + LDFLAGS="$LDFLAGS -L$WITH_TLSSEP/lib" + fi +else + use_tlssep=no +fi +AC_MSG_RESULT([$use_tlssep]) + +AC_ARG_WITH(tlssep-includes, + AC_HELP_STRING([--with-tlssep-includes=DIR],[tlssep includes]), + [ use_tlssep=yes CPPFLAGS="$CPPFLAGS -I$withval" ] +) + +AC_ARG_WITH(tlssep-libs, + AC_HELP_STRING([--with-tlssep-libs=DIR],[tlssep libraries]), + [ use_tlssep=yes LDFLAGS="$LDFLAGS -L$withval" ] +) + AC_ARG_WITH(kerberos5, AC_HELP_STRING([--with-kerberos5],[use Kerberos5 support with OpenSSL]), [ use_kerberos=yes ], [use_kerberos=no] @@ -343,6 +370,14 @@ if test "x$use_openssl" = "xyes"; then AC_SUBST(SSL_LIB) fi +if test "x$use_tlssep" = "xyes"; then + AC_CHECK_HEADERS([tlssep.h]) + OLDLIBS="$LIBS" + AC_CHECK_LIB(tlssep-0.1, tlssep_accept, [ TLSSEP_LIB="-ltlssep-0.1 -lrt -lssl -lcrypto" AC_DEFINE(HAVE_LIBTLSSEP, [], [Have tlssep]) ], [], [ -ltlssep-0.1 -lrt "$DL_LIB" ]) + LIBS="$OLDLIBS" + AC_SUBST(TLSSEP_LIB) +fi + AC_MSG_CHECKING(for perl regular expressions support) AC_ARG_WITH(pcre, AC_HELP_STRING([--with-pcre],[Enable pcre support (default yes)]), [WITH_PCRE=$withval],[WITH_PCRE=yes]) @@ -770,6 +805,13 @@ else disable_feature="$disable_feature $features" fi +features="network-tlssep" +if test ! "x$TLSSEP_LIB" = x; then + enable_feature="$enable_feature $features" +else + disable_feature="$disable_feature $features" +fi + dnl no crypt call features="auth-crypt" if test "$ac_cv_search_crypt" = no; then diff --git a/src/Makefile.am b/src/Makefile.am index a4ada19..5c71062 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -74,7 +74,7 @@ common_src=buffer.c log.c \ network_write.c network_linux_sendfile.c \ network_freebsd_sendfile.c network_writev.c \ network_solaris_sendfilev.c network_openssl.c \ - splaytree.c status_counter.c + network_tlssep.c splaytree.c status_counter.c src = server.c response.c connections.c network.c \ configfile.c configparser.c request.c proc_open.c @@ -284,7 +284,7 @@ hdr = server.h buffer.h network.h log.h keyvalue.h \ DEFS= @DEFS@ -DHAVE_VERSION_H -DLIBRARY_DIR="\"$(libdir)\"" -DSBIN_DIR="\"$(sbindir)\"" lighttpd_SOURCES = $(src) -lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_libadd) $(SSL_LIB) $(FAM_LIBS) $(LIBEV_LIBS) $(LIBUNWIND_LIBS) +lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_libadd) $(SSL_LIB) $(TLSSEP_LIB) $(FAM_LIBS) $(LIBEV_LIBS) $(LIBUNWIND_LIBS) lighttpd_LDFLAGS = -export-dynamic lighttpd_CCPFLAGS = $(FAM_CFLAGS) $(LIBEV_CFLAGS) diff --git a/src/base.h b/src/base.h index 87bacfc..81dda1e 100644 --- a/src/base.h +++ b/src/base.h @@ -36,6 +36,11 @@ # if ! defined OPENSSL_NO_TLSEXT && ! defined SSL_CTRL_SET_TLSEXT_HOSTNAME # define OPENSSL_NO_TLSEXT # endif +#else +#if defined HAVE_LIBTLSSEP && defined HAVE_TLSSEP_H +# define USE_TLSSEP +# include +#endif #endif #ifdef HAVE_FAM_H @@ -324,6 +329,10 @@ typedef struct { EVP_PKEY *ssl_pemfile_pkey; X509 *ssl_pemfile_x509; STACK_OF(X509_NAME) *ssl_ca_file_cert_names; +#else +# ifdef USE_TLSSEP + tlssep_context_t *ssl_ctx; +# endif #endif } specific_config; @@ -443,6 +452,10 @@ typedef struct { buffer *tlsext_server_name; # endif unsigned int renegotiations; /* count of SSL_CB_HANDSHAKE_START */ +#else +#ifdef USE_TLSSEP + tlssep_desc_t *ssl; +#endif #endif /* etag handling */ etag_flags_t etag_flags; @@ -539,6 +552,10 @@ typedef struct server_socket { #ifdef USE_OPENSSL SSL_CTX *ssl_ctx; +#else +# ifdef USE_TLSSEP + tlssep_context_t *ssl_ctx; +# endif #endif } server_socket; @@ -645,8 +662,8 @@ typedef struct server { fdevent_handler_t event_handler; int (* network_backend_write)(struct server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); -#ifdef USE_OPENSSL - int (* network_ssl_backend_write)(struct server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes); +#if defined (USE_OPENSSL) || defined (USE_TLSSEP) + int (* network_ssl_backend_write)(struct server *srv, connection *con, void *ssl_context, chunkqueue *cq, off_t max_bytes); #endif uid_t uid; diff --git a/src/connections.c b/src/connections.c index 8f26a30..7bc9725 100644 --- a/src/connections.c +++ b/src/connections.c @@ -28,6 +28,10 @@ #ifdef USE_OPENSSL # include # include +#else +#ifdef USE_TLSSEP +# include +#endif #endif #ifdef HAVE_SYS_FILIO_H @@ -114,10 +118,13 @@ static int connection_del(server *srv, connection *con) { } int connection_close(server *srv, connection *con) { -#ifdef USE_OPENSSL +#if defined(USE_OPENSSL) || defined(USE_TLSSEP) server_socket *srv_sock = con->srv_socket; #endif + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); + fdevent_unregister(srv->ev, con->fd); + #ifdef USE_OPENSSL if (srv_sock->is_ssl) { if (con->ssl) SSL_free(con->ssl); @@ -125,18 +132,22 @@ int connection_close(server *srv, connection *con) { } #endif - fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); - fdevent_unregister(srv->ev, con->fd); -#ifdef __WIN32 +#ifndef USE_TLSSEP +# ifdef __WIN32 if (closesocket(con->fd)) { log_error_write(srv, __FILE__, __LINE__, "sds", "(warning) close:", con->fd, strerror(errno)); } -#else +# else if (close(con->fd)) { log_error_write(srv, __FILE__, __LINE__, "sds", "(warning) close:", con->fd, strerror(errno)); } +# endif +#else + if (srv_sock->is_ssl) { + tlssep_close(con->ssl); + } #endif srv->cur_fds--; @@ -308,10 +319,47 @@ static int connection_handle_read_ssl(server *srv, connection *con) { return 0; #else +#ifdef USE_TLSSEP + int fnval = 0; + char *mem = NULL; + tlssep_status_t status; + size_t mem_len = 0; + int len = 0; + + if (!con->srv_socket->is_ssl) return -1; + + chunkqueue_get_memory(con->read_queue, &mem, &mem_len, 0, BUFSIZ); + + status = tlssep_read(con->ssl, mem, mem_len, &len); + if (TLSSEP_STATUS_OK != status && TLSSEP_STATUS_AGAIN != status) { + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + len, tlssep_strerror(status)); + connection_set_state(srv, con, CON_STATE_ERROR); + fnval = -1; + goto done; + } + + chunkqueue_use_memory(con->read_queue, len > 0 ? len : 0); + + if (len > 0 || TLSSEP_STATUS_AGAIN == status) { + con->bytes_read += len; + joblist_append(srv, con); + goto done; + } else { + con->is_readable = 0; + fnval = -2; + goto done; + } + +done: + return fnval; +#else + UNUSED(srv); UNUSED(con); return -1; #endif +#endif } /* 0: everything ok, -1: error, -2: con closed */ @@ -1128,14 +1176,9 @@ connection *connection_accept(server *srv, server_socket *srv_socket) { srv->con_opened++; con = connections_get_new_connection(srv); - - con->fd = cnt; - con->fde_ndx = -1; #if 0 gettimeofday(&(con->start_tv), NULL); #endif - fdevent_register(srv->ev, con->fd, connection_handle_fdevent, con); - connection_set_state(srv, con, CON_STATE_REQUEST_START); con->connection_start = srv->cur_ts; @@ -1143,10 +1186,6 @@ connection *connection_accept(server *srv, server_socket *srv_socket) { buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr))); con->srv_socket = srv_socket; - if (-1 == (fdevent_fcntl_set(srv->ev, con->fd))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); - return NULL; - } #ifdef USE_OPENSSL /* connect FD to SSL */ if (srv_socket->is_ssl) { @@ -1167,7 +1206,44 @@ connection *connection_accept(server *srv, server_socket *srv_socket) { return NULL; } } +#else +#ifdef USE_TLSSEP + if (srv_socket->is_ssl) { + assert(NULL != srv_socket->ssl_ctx); + + con->ssl = malloc (sizeof(tlssep_desc_t)); + tlssep_status_t status = tlssep_accept(srv_socket->ssl_ctx, cnt, NULL, NULL, con->ssl); + if (TLSSEP_STATUS_OK != status) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + tlssep_strerror(status)); + return NULL; + } + + status = tlssep_setnonblock(con->ssl); + if (TLSSEP_STATUS_OK != status) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + tlssep_strerror(status)); + return NULL; + } + + /* Reset file descriptor to tlssep's notification + * socket, since the network socket now belongs to the + * decorator. + */ + cnt = con->ssl->notificationfd; + } +#endif #endif + con->fd = cnt; + con->fde_ndx = -1; + fdevent_register(srv->ev, con->fd, connection_handle_fdevent, con); + + /* Do not go non-blocking until after tlssep_accept. */ + if (-1 == (fdevent_fcntl_set(srv->ev, con->fd))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); + return NULL; + } + return con; } } @@ -1175,7 +1251,7 @@ connection *connection_accept(server *srv, server_socket *srv_socket) { int connection_state_machine(server *srv, connection *con) { int done = 0, r; -#ifdef USE_OPENSSL +#if defined(USE_OPENSSL) || defined(USE_TLSSEP) server_socket *srv_sock = con->srv_socket; #endif @@ -1380,12 +1456,18 @@ int connection_state_machine(server *srv, connection *con) { } } #endif + +/* FIXME: Cannot shutdown, as con->fd is NOTIFICATION fd, not NETWORK fd. */ +#ifndef USE_TLSSEP if ((0 == shutdown(con->fd, SHUT_WR))) { con->close_timeout_ts = srv->cur_ts; connection_set_state(srv, con, CON_STATE_CLOSE); } else { connection_close(srv, con); } +#else + connection_close(srv, con); +#endif srv->con_closed++; } diff --git a/src/network.c b/src/network.c index f1c9489..0777359 100644 --- a/src/network.c +++ b/src/network.c @@ -35,6 +35,10 @@ # include # endif # endif +#else +# ifdef USE_TLSSEP +# include +# endif #endif #ifdef USE_OPENSSL @@ -427,12 +431,16 @@ static int network_server_init(server *srv, buffer *host_token, specific_config goto error_free_socket; } #else +#ifdef USE_TLSSEP + srv_socket->ssl_ctx = s->ssl_ctx; +#else log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - "ssl requested but openssl support is not compiled in"); + "ssl requested but neither openssl nor tlssep support is not compiled in"); goto error_free_socket; #endif +#endif #ifdef TCP_DEFER_ACCEPT } else if (s->defer_accept) { int v = s->defer_accept; @@ -507,6 +515,15 @@ int network_close(server *srv) { buffer_free(srv_socket->srv_token); free(srv_socket); +#ifdef USE_TLSSEP + /* + if (srv_sock->is_ssl) { + tlssep_terminate(con->ssl); + free(con->ssl); + con->ssl = NULL; + } + */ +#endif } free(srv->srv_sockets.ptr); @@ -922,6 +939,25 @@ int network_init(server *srv) { } # endif } +#else +# ifdef USE_TLSSEP + for (i = 0; i < srv->config_context->used; i++) { + specific_config *s = srv->config_storage[i]; + s->ssl_ctx = malloc(sizeof(tlssep_context_t)); + if (NULL == s->ssl_ctx) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "unable to allocate memory"); + return -1; + } + + tlssep_status_t status = tlssep_init(s->ssl_ctx); + if(TLSSEP_STATUS_OK != status) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "failed to initialize TLS"); + return -1; + } + } +# endif #endif b = buffer_init(); @@ -938,6 +974,10 @@ int network_init(server *srv) { #ifdef USE_OPENSSL srv->network_ssl_backend_write = network_write_chunkqueue_openssl; +#else +#ifdef USE_TLSSEP + srv->network_ssl_backend_write = network_write_chunkqueue_tlssep; +#endif #endif /* get a usefull default */ @@ -1083,7 +1123,7 @@ int network_write_chunkqueue(server *srv, connection *con, chunkqueue *cq, off_t #endif if (srv_socket->is_ssl) { -#ifdef USE_OPENSSL +#if defined (USE_OPENSSL) || defined (USE_TLSSEP) ret = srv->network_ssl_backend_write(srv, con, con->ssl, cq, max_bytes); #endif } else { diff --git a/src/network_backends.h b/src/network_backends.h index 5813253..78ea8a4 100644 --- a/src/network_backends.h +++ b/src/network_backends.h @@ -58,7 +58,11 @@ int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); #ifdef USE_OPENSSL -int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes); +int network_write_chunkqueue_openssl(server *srv, connection *con, void *ssl, chunkqueue *cq, off_t max_bytes); +#else +#ifdef USE_TLSSEP +int network_write_chunkqueue_tlssep(server *srv, connection *con, void *desc, chunkqueue *cq, off_t max_bytes); +#endif #endif #endif diff --git a/src/network_openssl.c b/src/network_openssl.c index d9ae33c..b259011 100644 --- a/src/network_openssl.c +++ b/src/network_openssl.c @@ -27,9 +27,10 @@ # include # include -int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes) { +int network_write_chunkqueue_openssl(server *srv, connection *con, void *_ssl, chunkqueue *cq, off_t max_bytes) { int ssl_r; chunk *c; + SSL *ssl = _ssl; /* this is a 64k sendbuffer * diff --git a/src/network_tlssep.c b/src/network_tlssep.c new file mode 100644 index 0000000..f01326c --- /dev/null +++ b/src/network_tlssep.c @@ -0,0 +1,190 @@ +#include "network_backends.h" + +#ifdef USE_TLSSEP + +#include "network.h" +#include "fdevent.h" +#include "log.h" +#include "stat_cache.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +# include + +int network_write_chunkqueue_tlssep(server *srv, connection *con, void *_desc, chunkqueue *cq, off_t max_bytes) { + chunk *c; + tlssep_desc_t *desc = _desc; + + /* this is a 64k sendbuffer + * + * it has to stay at the same location all the time to satisfy the needs + * of SSL_write to pass the SAME parameter in case of a _WANT_WRITE + * + * the buffer is allocated once, is NOT realloced and is NOT freed at shutdown + * -> we expect a 64k block to 'leak' in valgrind + * + * + * In reality we would like to use mmap() but we don't have a guarantee that + * we get the same mmap() address for each call. On openbsd the mmap() address + * even randomized. + * That means either we keep the mmap() open or we do a read() into a + * constant buffer + * */ +#define LOCAL_SEND_BUFSIZE (64 * 1024) + static char *local_send_buffer = NULL; + + /* the remote side closed the connection before without shutdown request + * - IE + * - wget + * if keep-alive is disabled */ + + if (con->keep_alive == 0) { + /* FIXME: ??? */ + } + + for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { + int chunk_finished = 0; + + switch(c->type) { + case MEM_CHUNK: { + char * offset; + off_t toSend; + int r; + tlssep_status_t status; + + if (buffer_string_is_empty(c->mem)) { + chunk_finished = 1; + break; + } + + offset = c->mem->ptr + c->offset; + toSend = buffer_string_length(c->mem) - c->offset; + if (toSend > max_bytes) toSend = max_bytes; + + + status = tlssep_write(desc, offset, toSend, &r); + if (TLSSEP_STATUS_OK != status) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + tlssep_strerror(status)); + } + + /* FIXME: + if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { + log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection"); + return -1; + } + */ + + c->offset += r; + cq->bytes_out += r; + max_bytes -= r; + + if (c->offset == (off_t)buffer_string_length(c->mem)) { + chunk_finished = 1; + } + + break; + } + case FILE_CHUNK: { + char *s; + int r; + stat_cache_entry *sce = NULL; + int ifd; + int write_wait = 0; + tlssep_status_t status; + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + strerror(errno), c->file.name); + return -1; + } + + if (NULL == local_send_buffer) { + local_send_buffer = malloc(LOCAL_SEND_BUFSIZE); + force_assert(local_send_buffer); + } + + do { + off_t offset = c->file.start + c->offset; + off_t toSend = c->file.length - c->offset; + if (toSend > max_bytes) toSend = max_bytes; + + if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE; + + if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "open failed:", strerror(errno)); + + return -1; + } + + + if (-1 == lseek(ifd, offset, SEEK_SET)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "lseek failed:", strerror(errno)); + close(ifd); + return -1; + } + if (-1 == (toSend = read(ifd, local_send_buffer, toSend))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "read failed:", strerror(errno)); + close(ifd); + return -1; + } + + s = local_send_buffer; + + close(ifd); + + status = tlssep_write(desc, s, toSend, &r); + if (TLSSEP_STATUS_OK != status) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + tlssep_strerror(status)); + } + + /* FIXME: + if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { + log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection"); + return -1; + } + */ + + c->offset += r; + cq->bytes_out += r; + max_bytes -= r; + + if (c->offset == c->file.length) { + chunk_finished = 1; + } + } while (!chunk_finished && !write_wait && max_bytes > 0); + + break; + } + default: + log_error_write(srv, __FILE__, __LINE__, "s", "type not known"); + + return -1; + } + + if (!chunk_finished) { + /* not finished yet */ + + break; + } + } + + return 0; +} +#endif diff --git a/src/server.c b/src/server.c index 5089375..2dc6aa5 100644 --- a/src/server.c +++ b/src/server.c @@ -459,7 +459,7 @@ static void show_features (void) { #else "\t- crypt support\n" #endif -#ifdef USE_OPENSSL +#if defined(USE_OPENSSL) || defined(USE_GRAYLINE) "\t+ SSL Support\n" #else "\t- SSL Support\n" @@ -515,7 +515,7 @@ static void show_features (void) { } static void show_help (void) { -#ifdef USE_OPENSSL +#if defined(USE_OPENSSL) || defined(USE_GRAYLINE) # define TEXT_SSL " (ssl)" #else # define TEXT_SSL