Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

new module: mod_gnutls

  • Loading branch information...
commit bef05e0b2ea11b0afd8137613b394396edf93cb4 1 parent d173c7d
@stbuehler stbuehler authored
View
17 configure.ac
@@ -113,6 +113,23 @@ if test x$lfs = xtrue; then
CFLAGS="${CFLAGS} -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES"
fi
+dnl Check for gnutls
+AC_ARG_WITH([gnutls], [AS_HELP_STRING([--with-gnutls],[gnutls library for ssl/tls])],
+[WITH_GNUTLS=$withval],[WITH_GNUTLS=no])
+
+if test "$WITH_GNUTLS" != "no"; then
+ PKG_CHECK_MODULES([GNUTLS], [gnutls],[],[
+ AC_MSG_ERROR([gnutls not found])
+ ])
+
+ AC_SUBST([GNUTLS_CFLAGS])
+ AC_SUBST([GNUTLS_LIBS])
+ USE_GNUTLS=true
+fi
+
+AM_CONDITIONAL([USE_GNUTLS], [test "$USE_GNUTLS" = "true"])
+
+
dnl Check for lua
AC_MSG_CHECKING([for lua])
AC_ARG_WITH([lua], [AS_HELP_STRING([--with-lua],[lua engine (recommended)])],
View
18 src/CMakeLists.txt
@@ -17,14 +17,14 @@ cmake_policy(VERSION 2.6.4)
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES)
-# OPTION(WITH_OPENSSL "with openssl-support [default: on]" ON)
OPTION(WITH_LUA "with lua 5.1 for lua-configfile [default: on]" ON)
OPTION(WITHOUT_CONFIG_PARSER "without standard config parser [default: off]" OFF)
-OPTION(WITH_OPENSSL "with openssl-support [default: off]")
+OPTION(WITH_OPENSSL "with openssl support [default: off]")
+OPTION(WITH_GNUTLS "with gnutls support [default: off]")
OPTION(BUILD_STATIC "build a static lighttpd with all modules added")
OPTION(BUILD_EXTRA_WARNINGS "extra warnings")
-OPTION(WITH_BZIP "with bzip2-support for mod_deflate")
-OPTION(WITH_ZLIB "with deflate-support for mod_deflate")
+OPTION(WITH_BZIP "with bzip2 support for mod_deflate")
+OPTION(WITH_ZLIB "with deflate support for mod_deflate")
OPTION(WITH_PROFILER "with memory profiler")
OPTION(BUILD_UNIT_TESTS "build unit tests for testing")
@@ -104,6 +104,10 @@ IF(WITH_LUA)
SET(HAVE_LUA_H 1 "Have liblua header")
ENDIF(WITH_LUA)
+IF(WITH_GNUTLS)
+ pkg_search_module(GNUTLS REQUIRED gnutls)
+ENDIF(WITH_GNUTLS)
+
IF(WITH_OPENSSL)
CHECK_INCLUDE_FILES(openssl/ssl.h HAVE_OPENSSL_SSL_H)
IF(HAVE_OPENSSL_SSL_H)
@@ -357,6 +361,12 @@ IF(WITH_LUA)
ADD_AND_INSTALL_LIBRARY(mod_lua "modules/mod_lua.c")
ENDIF(WITH_LUA)
+IF(WITH_GNUTLS)
+ ADD_AND_INSTALL_LIBRARY(mod_gnutls "modules/mod_gnutls.c")
+ TARGET_LINK_LIBRARIES(mod_gnutls ${GNUTLS_LDFLAGS})
+ ADD_TARGET_PROPERTIES(mod_gnutls COMPILE_FLAGS ${GNUTLS_CFLAGS})
+ENDIF(WITH_GNUTLS)
+
IF(HAVE_LIBSSL AND HAVE_LIBCRYPTO)
ADD_AND_INSTALL_LIBRARY(mod_openssl "modules/mod_openssl.c")
TARGET_LINK_LIBRARIES(mod_openssl ssl)
View
19 src/modules/Makefile.am
@@ -1,7 +1,7 @@
install_libs =
common_cflags = -I$(top_srcdir)/include -I$(top_builddir)/include
-common_cflags += $(GTHREAD_CFLAGS) $(LIBEV_CFLAGS) $(LUA_CFLAGS) $(OPENSSL_CFLAGS)
+common_cflags += $(GTHREAD_CFLAGS) $(LIBEV_CFLAGS) $(LUA_CFLAGS)
common_libs = $(GTHREAD_LIBS) $(LIBEV_LIBS) $(LUA_LIBS)
common_ldflags = -module -export-dynamic -avoid-version -no-undefined $(common_libs)
common_libadd = ../common/liblighttpd2-common.la ../main/liblighttpd2-shared.la
@@ -43,8 +43,8 @@ libmod_debug_la_LIBADD = $(common_libadd)
if USE_MOD_DEFLATE
install_libs += libmod_deflate.la
libmod_deflate_la_SOURCES = mod_deflate.c
-libmod_deflate_la_LDFLAGS = $(common_ldflags) $(Z_LIB) $(BZ_LIB)
-libmod_deflate_la_LIBADD = $(common_libadd)
+libmod_deflate_la_LDFLAGS = $(common_ldflags)
+libmod_deflate_la_LIBADD = $(common_libadd) $(Z_LIB) $(BZ_LIB)
endif
install_libs += libmod_dirlist.la
@@ -72,6 +72,14 @@ libmod_fortune_la_SOURCES = mod_fortune.c
libmod_fortune_la_LDFLAGS = $(common_ldflags)
libmod_fortune_la_LIBADD = $(common_libadd)
+if USE_GNUTLS
+install_libs += libmod_gnutls.la
+libmod_gnutls_la_CPPFLAGS = $(AM_CPPFLAGS) $(GNUTLS_CFLAGS)
+libmod_gnutls_la_SOURCES = mod_gnutls.c
+libmod_gnutls_la_LDFLAGS = $(common_ldflags)
+libmod_gnutls_la_LIBADD = $(common_libadd) $(GNUTLS__LIB)
+endif
+
install_libs += libmod_limit.la
libmod_limit_la_SOURCES = mod_limit.c
libmod_limit_la_LDFLAGS = $(common_ldflags)
@@ -92,9 +100,10 @@ libmod_memcached_la_LIBADD = $(common_libadd)
if USE_OPENSSL
install_libs += libmod_openssl.la
+libmod_openssl_la_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_CFLAGS)
libmod_openssl_la_SOURCES = mod_openssl.c
-libmod_openssl_la_LDFLAGS = $(common_ldflags) $(OPENSSL_LIBS)
-libmod_openssl_la_LIBADD = $(common_libadd)
+libmod_openssl_la_LDFLAGS = $(common_ldflags)
+libmod_openssl_la_LIBADD = $(common_libadd) $(OPENSSL_LIBS)
endif
install_libs += libmod_progress.la
View
591 src/modules/mod_gnutls.c
@@ -0,0 +1,591 @@
+
+#include <lighttpd/base.h>
+
+#include <gnutls/gnutls.h>
+#include <glib-2.0/glib/galloca.h>
+
+
+LI_API gboolean mod_gnutls_init(liModules *mods, liModule *mod);
+LI_API gboolean mod_gnutls_free(liModules *mods, liModule *mod);
+
+
+typedef struct mod_connection_ctx mod_connection_ctx;
+typedef struct mod_context mod_context;
+
+struct mod_connection_ctx {
+ gnutls_session_t session;
+ liConnection *con;
+ mod_context *ctx;
+
+ int con_events;
+ liJob con_handle_events_job;
+
+ unsigned int gnutls_again_in_progress:1;
+
+ unsigned int initial_handshaked_finished:1;
+ unsigned int client_initiated_renegotiation:1;
+};
+
+struct mod_context {
+ gint refcount;
+
+ gnutls_certificate_credentials_t server_cert;
+ gnutls_priority_t server_priority;
+ gnutls_priority_t server_priority_beast;
+
+ unsigned int protect_against_beast:1;
+};
+
+static void mod_gnutls_context_release(mod_context *ctx) {
+ if (!ctx) return;
+ assert(g_atomic_int_get(&ctx->refcount) > 0);
+ if (g_atomic_int_dec_and_test(&ctx->refcount)) {
+ gnutls_priority_deinit(ctx->server_priority_beast);
+ gnutls_priority_deinit(ctx->server_priority);
+ gnutls_certificate_free_credentials(ctx->server_cert);
+
+ g_slice_free(mod_context, ctx);
+ }
+}
+
+static void mod_gnutls_context_acquire(mod_context *ctx) {
+ assert(g_atomic_int_get(&ctx->refcount) > 0);
+ g_atomic_int_inc(&ctx->refcount);
+}
+
+
+static mod_context *mod_gnutls_context_new(liServer *srv) {
+ mod_context *ctx = g_slice_new0(mod_context);
+ int r;
+
+ if (GNUTLS_E_SUCCESS != (r = gnutls_certificate_allocate_credentials(&ctx->server_cert))) {
+ ERROR(srv, "gnutls_certificate_allocate_credentials failed(%s): %s\n",
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ goto error0;
+ }
+
+ if (GNUTLS_E_SUCCESS != (r = gnutls_priority_init(&ctx->server_priority, "NORMAL", NULL))) {
+ ERROR(srv, "gnutls_priority_init failed(%s): %s\n",
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ goto error1;
+ }
+
+ if (GNUTLS_E_SUCCESS != (r = gnutls_priority_init(&ctx->server_priority_beast, "NORMAL:-CIPHER-ALL:+ARCFOUR-128", NULL))) {
+ ERROR(srv, "gnutls_priority_init failed(%s): %s\n",
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ goto error2;
+ }
+
+ ctx->refcount = 1;
+ ctx->protect_against_beast = 1;
+
+ return ctx;
+
+error2:
+ gnutls_priority_deinit(ctx->server_priority);
+
+error1:
+ gnutls_certificate_free_credentials(ctx->server_cert);
+
+error0:
+ g_slice_free(mod_context, ctx);
+ return NULL;
+}
+
+
+
+
+static void mod_gnutls_con_handle_events_cb(liJob *job) {
+ mod_connection_ctx *conctx = LI_CONTAINER_OF(job, mod_connection_ctx, con_handle_events_job);
+ liConnection *con = conctx->con;
+
+ conctx->gnutls_again_in_progress = 0;
+ connection_handle_io(con);
+}
+
+static void mod_gnutls_io_cb(struct ev_loop *loop, ev_io *w, int revents) {
+ liConnection *con = (liConnection*) w->data;
+ mod_connection_ctx *conctx = con->srv_sock_data;
+
+ if (revents & EV_ERROR) {
+ /* if this happens, we have a serious bug in the event handling */
+ VR_ERROR(con->mainvr, "%s", "EV_ERROR encountered, dropping connection!");
+ li_connection_error(con);
+ return;
+ }
+
+ con->can_read = TRUE;
+ con->can_write = TRUE;
+
+ /* disable all events; they will get reactivated later */
+ li_ev_io_set_events(loop, w, 0);
+
+ li_job_now(&con->wrk->jobqueue, &conctx->con_handle_events_job);
+}
+
+static int mod_gnutls_post_client_hello_cb(gnutls_session_t session) {
+ gnutls_protocol_t p = gnutls_protocol_get_version(session);
+ mod_connection_ctx *conctx = gnutls_session_get_ptr(session);
+
+ if (conctx->ctx->protect_against_beast) {
+ if (GNUTLS_SSL3 == p || GNUTLS_TLS1_0 == p) {
+ gnutls_priority_set(session, conctx->ctx->server_priority_beast);
+ }
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+static gboolean mod_gnutls_con_new(liConnection *con) {
+ liServer *srv = con->srv;
+ mod_context *ctx = con->srv_sock->data;
+ mod_connection_ctx *conctx = g_slice_new0(mod_connection_ctx);
+ int r;
+
+ con->srv_sock_data = NULL;
+
+ if (GNUTLS_E_SUCCESS != (r = gnutls_init(&conctx->session, GNUTLS_SERVER))) {
+ ERROR(srv, "gnutls_init (%s): %s",
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ g_slice_free(mod_connection_ctx, conctx);
+ return FALSE;
+ }
+
+ mod_gnutls_context_acquire(ctx);
+
+ if (GNUTLS_E_SUCCESS != (r = gnutls_priority_set(conctx->session, ctx->server_priority))) {
+ ERROR(srv, "gnutls_priority_set (%s): %s",
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ goto fail;
+ }
+ if (GNUTLS_E_SUCCESS != (r = gnutls_credentials_set(conctx->session, GNUTLS_CRD_CERTIFICATE, ctx->server_cert))) {
+ ERROR(srv, "gnutls_credentials_set (%s): %s",
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ goto fail;
+ }
+
+ gnutls_transport_set_ptr(conctx->session, (gnutls_transport_ptr_t)(intptr_t) con->sock_watcher.fd);
+ gnutls_session_set_ptr(conctx->session, conctx);
+
+ gnutls_handshake_set_post_client_hello_function(conctx->session, mod_gnutls_post_client_hello_cb);
+
+ conctx->con = con;
+ conctx->ctx = ctx;
+ li_job_init(&conctx->con_handle_events_job, mod_gnutls_con_handle_events_cb);
+ conctx->con_events = 0;
+ conctx->gnutls_again_in_progress = 0;
+ conctx->initial_handshaked_finished = 0;
+ conctx->client_initiated_renegotiation = 0;
+
+ con->srv_sock_data = conctx;
+ con->info.is_ssl = TRUE;
+
+ ev_set_cb(&con->sock_watcher, mod_gnutls_io_cb);
+
+ return TRUE;
+
+fail:
+ gnutls_deinit(conctx->session);
+ mod_gnutls_context_release(ctx);
+
+ g_slice_free(mod_connection_ctx, conctx);
+
+ return FALSE;
+}
+
+static void mod_gnutls_con_close(liConnection *con) {
+ mod_connection_ctx *conctx = con->srv_sock_data;
+
+ if (!conctx) return;
+
+ gnutls_bye(conctx->session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(conctx->session);
+ mod_gnutls_context_release(conctx->ctx);
+
+ con->srv_sock_data = NULL;
+ con->info.is_ssl = FALSE;
+ li_job_clear(&conctx->con_handle_events_job);
+
+ g_slice_free(mod_connection_ctx, conctx);
+}
+
+static void mod_gnutls_update_events(liConnection *con, int events) {
+ mod_connection_ctx *conctx = con->srv_sock_data;
+
+ /* new events -> add them to socket watcher too */
+ if (!conctx->gnutls_again_in_progress && 0 != (events & ~conctx->con_events)) {
+ li_ev_io_add_events(con->wrk->loop, &con->sock_watcher, events);
+ }
+
+ conctx->con_events = events;
+}
+
+static liNetworkStatus mod_gnutls_handle_error(liConnection *con, mod_connection_ctx *conctx, const char *gnutlsfunc, off_t len, int r) {
+ switch (r) {
+ case GNUTLS_E_AGAIN:
+ conctx->gnutls_again_in_progress = TRUE;
+ li_ev_io_set_events(con->wrk->loop, &con->sock_watcher, gnutls_record_get_direction(conctx->session) ? EV_WRITE : EV_READ);
+ return (len > 0) ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_WAIT_FOR_EVENT;
+ case GNUTLS_E_REHANDSHAKE:
+ if (conctx->initial_handshaked_finished) {
+ VR_ERROR(con->mainvr, "%s", "gnutls: client initiated renegotitation, closing connection");
+ return LI_NETWORK_STATUS_FATAL_ERROR;
+ }
+ break;
+ case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
+ /* connection abort */
+ return LI_NETWORK_STATUS_CONNECTION_CLOSE;
+ case GNUTLS_E_UNKNOWN_CIPHER_SUITE:
+ case GNUTLS_E_UNSUPPORTED_VERSION_PACKET:
+ VR_DEBUG(con->mainvr, "%s (%s): %s", gnutlsfunc,
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ return LI_NETWORK_STATUS_CONNECTION_CLOSE;
+ default:
+ if (gnutls_error_is_fatal(r)) {
+ VR_ERROR(con->mainvr, "%s (%s): %s", gnutlsfunc,
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ return LI_NETWORK_STATUS_FATAL_ERROR;
+ } else {
+ VR_ERROR(con->mainvr, "%s non fatal (%s): %s", gnutlsfunc,
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ }
+ }
+ return (len > 0) ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_WAIT_FOR_EVENT;
+}
+
+static liNetworkStatus mod_gnutls_do_handshake(liConnection *con, mod_connection_ctx *conctx) {
+ int r;
+
+ if (GNUTLS_E_SUCCESS != (r = gnutls_handshake(conctx->session))) {
+ return mod_gnutls_handle_error(con, conctx, "gnutls_handshake", 0, r);
+ } else {
+ conctx->initial_handshaked_finished = 1;
+ return LI_NETWORK_STATUS_SUCCESS;
+ }
+}
+
+static liNetworkStatus mod_gnutls_con_write(liConnection *con, goffset write_max) {
+ const ssize_t blocksize = 16*1024; /* 16k */
+ char *block_data;
+ off_t block_len;
+ ssize_t r;
+ liChunkIter ci;
+ liChunkQueue *cq = con->raw_out;
+ mod_connection_ctx *conctx = con->srv_sock_data;
+
+ if (!conctx->initial_handshaked_finished) {
+ liNetworkStatus res = mod_gnutls_do_handshake(con, conctx);
+ if (res != LI_NETWORK_STATUS_SUCCESS) return res;
+ }
+
+ do {
+ if (0 == cq->length)
+ return LI_NETWORK_STATUS_SUCCESS;
+
+ ci = li_chunkqueue_iter(cq);
+ switch (li_chunkiter_read(con->mainvr, ci, 0, blocksize, &block_data, &block_len)) {
+ case LI_HANDLER_GO_ON:
+ break;
+ case LI_HANDLER_ERROR:
+ default:
+ return LI_NETWORK_STATUS_FATAL_ERROR;
+ }
+
+ if (0 >= (r = gnutls_record_send(conctx->session, block_data, block_len))) {
+ return mod_gnutls_handle_error(con, conctx, "gnutls_record_send", 0, r);
+ }
+
+ /* VR_BACKEND_LINES(con->mainvr, block_data, "written %i from %i bytes: ", (int) r, (int) block_len); */
+
+ li_chunkqueue_skip(cq, r);
+ write_max -= r;
+ } while (r == block_len && write_max > 0);
+
+ if (0 != cq->length) {
+ li_ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_WRITE);
+ }
+ return LI_NETWORK_STATUS_SUCCESS;
+}
+
+static liNetworkStatus mod_gnutls_con_read(liConnection *con) {
+ liChunkQueue *cq = con->raw_in;
+ mod_connection_ctx *conctx = con->srv_sock_data;
+
+ const ssize_t blocksize = 16*1024; /* 16k */
+ off_t max_read = 16 * blocksize; /* 256k */
+ ssize_t r;
+ off_t len = 0;
+
+ if (!conctx->initial_handshaked_finished) {
+ liNetworkStatus res = mod_gnutls_do_handshake(con, conctx);
+ if (res != LI_NETWORK_STATUS_SUCCESS) return res;
+ }
+
+ if (cq->limit && cq->limit->limit > 0) {
+ if (max_read > cq->limit->limit - cq->limit->current) {
+ max_read = cq->limit->limit - cq->limit->current;
+ if (max_read <= 0) {
+ max_read = 0; /* we still have to read something */
+ VR_ERROR(con->mainvr, "li_network_read: fd %i should be disabled as chunkqueue is already full",
+ con->sock_watcher.fd);
+ }
+ }
+ }
+
+ do {
+ liBuffer *buf;
+ gboolean cq_buf_append;
+
+ buf = li_chunkqueue_get_last_buffer(cq, 1024);
+ cq_buf_append = (buf != NULL);
+
+ if (buf != NULL) {
+ /* use last buffer as raw_in_buffer; they should be the same anyway */
+ if (G_UNLIKELY(buf != con->raw_in_buffer)) {
+ li_buffer_acquire(buf);
+ li_buffer_release(con->raw_in_buffer);
+ con->raw_in_buffer = buf;
+ }
+ } else {
+ buf = con->raw_in_buffer;
+ if (buf != NULL && buf->alloc_size - buf->used < 1024) {
+ /* release *buffer */
+ li_buffer_release(buf);
+ con->raw_in_buffer = buf = NULL;
+ }
+ if (buf == NULL) {
+ con->raw_in_buffer = buf = li_buffer_new(blocksize);
+ }
+ }
+ assert(con->raw_in_buffer == buf);
+
+ if (0 > (r = gnutls_record_recv(conctx->session, buf->addr + buf->used, buf->alloc_size - buf->used))) {
+ return mod_gnutls_handle_error(con, conctx, "gnutls_record_recv", len, r);
+ } else if (r == 0) {
+ return LI_NETWORK_STATUS_CONNECTION_CLOSE;
+ }
+
+ if (cq_buf_append) {
+ li_chunkqueue_update_last_buffer_size(cq, r);
+ } else {
+ gsize offset;
+
+ li_buffer_acquire(buf);
+
+ offset = buf->used;
+ buf->used += r;
+ li_chunkqueue_append_buffer2(cq, buf, offset, r);
+ }
+ if (buf->alloc_size - buf->used < 1024) {
+ /* release *buffer */
+ li_buffer_release(buf);
+ con->raw_in_buffer = buf = NULL;
+ }
+ len += r;
+ } while (len < max_read);
+
+ return LI_NETWORK_STATUS_SUCCESS;
+}
+
+static void mod_gnutls_sock_release(liServerSocket *srv_sock) {
+ mod_context *ctx = srv_sock->data;
+
+ if (!ctx) return;
+
+ mod_gnutls_context_release(ctx);
+}
+
+
+static void gnutls_setup_listen_cb(liServer *srv, int fd, gpointer data) {
+ mod_context *ctx = data;
+ liServerSocket *srv_sock;
+ UNUSED(data);
+
+ if (-1 == fd) {
+ mod_gnutls_context_release(ctx);
+ return;
+ }
+
+ srv_sock = li_server_listen(srv, fd);
+
+ srv_sock->data = ctx; /* transfer ownership, no refcount change */
+
+ srv_sock->write_cb = mod_gnutls_con_write;
+ srv_sock->read_cb = mod_gnutls_con_read;
+ srv_sock->new_cb = mod_gnutls_con_new;
+ srv_sock->close_cb = mod_gnutls_con_close;
+ srv_sock->release_cb = mod_gnutls_sock_release;
+ srv_sock->update_events_cb = mod_gnutls_update_events;
+}
+
+static gboolean gnutls_setup(liServer *srv, liPlugin* p, liValue *val, gpointer userdata) {
+ mod_context *ctx;
+ GHashTableIter hti;
+ gpointer hkey, hvalue;
+ GString *htkey;
+ liValue *htval;
+ int r;
+
+ /* setup defaults */
+ GString *ipstr = NULL;
+ const char
+ *priority = NULL,
+ *pemfile = NULL, *ca_file = NULL;
+ gboolean
+ protect_against_beast = TRUE;
+
+ UNUSED(p); UNUSED(userdata);
+
+ if (val->type != LI_VALUE_HASH) {
+ ERROR(srv, "%s", "gnutls expects a hash as parameter");
+ return FALSE;
+ }
+
+ g_hash_table_iter_init(&hti, val->data.hash);
+ while (g_hash_table_iter_next(&hti, &hkey, &hvalue)) {
+ htkey = hkey; htval = hvalue;
+
+ if (g_str_equal(htkey->str, "listen")) {
+ if (htval->type != LI_VALUE_STRING) {
+ ERROR(srv, "%s", "gnutls listen expects a string as parameter");
+ return FALSE;
+ }
+ ipstr = htval->data.string;
+ } else if (g_str_equal(htkey->str, "pemfile")) {
+ if (htval->type != LI_VALUE_STRING) {
+ ERROR(srv, "%s", "gnutls pemfile expects a string as parameter");
+ return FALSE;
+ }
+ pemfile = htval->data.string->str;
+ } else if (g_str_equal(htkey->str, "ca-file")) {
+ if (htval->type != LI_VALUE_STRING) {
+ ERROR(srv, "%s", "gnutls ca-file expects a string as parameter");
+ return FALSE;
+ }
+ ca_file = htval->data.string->str;
+ } else if (g_str_equal(htkey->str, "priority")) {
+ if (htval->type != LI_VALUE_STRING) {
+ ERROR(srv, "%s", "gnutls priority expects a string as parameter");
+ return FALSE;
+ }
+ priority = htval->data.string->str;
+ } else if (g_str_equal(htkey->str, "protect-against-beast")) {
+ if (htval->type != LI_VALUE_BOOLEAN) {
+ ERROR(srv, "%s", "gnutls protect-against-beast expects a boolean as parameter");
+ return FALSE;
+ }
+ protect_against_beast = htval->data.boolean;
+ }
+ }
+
+ if (!ipstr) {
+ ERROR(srv, "%s", "gnutls needs a listen parameter");
+ return FALSE;
+ }
+
+ if (!pemfile) {
+ ERROR(srv, "%s", "gnutls needs a pemfile");
+ return FALSE;
+ }
+
+ if (!(ctx = mod_gnutls_context_new(srv))) return FALSE;
+
+ ctx->protect_against_beast = protect_against_beast;
+
+ if (GNUTLS_E_SUCCESS != (r = gnutls_certificate_set_x509_key_file(ctx->server_cert, pemfile, pemfile, GNUTLS_X509_FMT_PEM))) {
+ ERROR(srv, "gnutls_certificate_set_x509_key_file failed(certfile '%s', keyfile '%s', PEM) (%s): %s\n",
+ pemfile, pemfile,
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ goto error_free_ctx;
+ }
+
+ if ((NULL != ca_file) && GNUTLS_E_SUCCESS != (r = gnutls_certificate_set_x509_trust_file(ctx->server_cert, ca_file, GNUTLS_X509_FMT_PEM))) {
+ ERROR(srv, "gnutls_certificate_set_x509_trust_file failed(cafile '%s', PEM) (%s): %s\n",
+ ca_file,
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ goto error_free_ctx;
+ }
+
+ if (priority) {
+ const char *errpos = NULL;
+ gnutls_priority_t prio;
+ GString *s = srv->main_worker->tmp_str;
+
+ if (GNUTLS_E_SUCCESS != (r = gnutls_priority_init(&prio, priority, &errpos))) {
+ ERROR(srv, "gnutls_priority_init failed(priority '%s', error at '%s') (%s): %s\n",
+ priority, errpos,
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ goto error_free_ctx;
+ }
+
+ gnutls_priority_deinit(ctx->server_priority);
+ ctx->server_priority = prio;
+
+ if (protect_against_beast) {
+ g_string_assign(s, priority);
+ g_string_append_len(s, CONST_STR_LEN(":-CIPHER-ALL:+ARCFOUR-128"));
+ if (GNUTLS_E_SUCCESS != (r = gnutls_priority_init(&prio, priority, &errpos))) {
+ ERROR(srv, "gnutls_priority_init failed(priority '%s', error at '%s') (%s): %s\n",
+ priority, errpos,
+ gnutls_strerror_name(r), gnutls_strerror(r));
+ goto error_free_ctx;
+ }
+
+ gnutls_priority_deinit(ctx->server_priority_beast);
+ ctx->server_priority_beast = prio;
+ }
+ }
+
+ li_angel_listen(srv, ipstr, gnutls_setup_listen_cb, ctx);
+
+ return TRUE;
+
+error_free_ctx:
+ if (ctx) {
+ mod_gnutls_context_release(ctx);
+ }
+
+ return FALSE;
+}
+
+static const liPluginOption options[] = {
+ { NULL, 0, 0, NULL }
+};
+
+static const liPluginAction actions[] = {
+ { NULL, NULL, NULL }
+};
+
+static const liPluginSetup setups[] = {
+ { "gnutls", gnutls_setup, NULL },
+
+ { NULL, NULL, NULL }
+};
+
+
+static void plugin_init(liServer *srv, liPlugin *p, gpointer userdata) {
+ UNUSED(srv); UNUSED(userdata);
+
+ p->options = options;
+ p->actions = actions;
+ p->setups = setups;
+}
+
+gboolean mod_gnutls_init(liModules *mods, liModule *mod) {
+ MODULE_VERSION_CHECK(mods);
+
+ gnutls_global_init();
+
+ mod->config = li_plugin_register(mods->main, "mod_gnutls", plugin_init, NULL);
+
+ return mod->config != NULL;
+}
+
+gboolean mod_gnutls_free(liModules *mods, liModule *mod) {
+ if (mod->config)
+ li_plugin_free(mods->main, mod->config);
+
+ gnutls_global_deinit();
+
+ return TRUE;
+}
Please sign in to comment.
Something went wrong with that request. Please try again.