]> git.feebdaed.xyz Git - 0xmirror/civetweb.git/commitdiff
Add GnutTLS support.
authorcatch-error <mirko.kraft@gmx.ch>
Fri, 27 Dec 2024 18:56:19 +0000 (19:56 +0100)
committercatch-error <mirko.kraft@gmx.ch>
Fri, 27 Dec 2024 18:56:19 +0000 (19:56 +0100)
Signed-off-by: catch-error <mirko.kraft@gmx.ch>
CMakeLists.txt
Makefile
README.md
docs/Building.md
docs/UserManual.md
docs/gnutls.md [new file with mode: 0644]
format.bat
src/CMakeLists.txt
src/civetweb.c
src/mod_gnutls.inl [new file with mode: 0644]

index 293dabac5a12dae93e3f7e6bbc1ea34c2cad420a..602fbe410a2d99e02fb5de8662990cc09b6e30f4 100644 (file)
@@ -237,8 +237,11 @@ message(STATUS "Compile for OpenSSL 1.1 API - ${CIVETWEB_SSL_OPENSSL_API_1_1}")
 option(CIVETWEB_SSL_OPENSSL_API_3_0 "Use the OpenSSL 3.0 API" OFF)
 message(STATUS "Compile for OpenSSL 3.0 API - ${CIVETWEB_SSL_OPENSSL_API_3_0}")
 
+option(CIVETWEB_ENABLE_GNUTLS "Use the GnuTls" OFF)
+message(STATUS "SSL support (GnuTLS)  - ${CIVETWEB_ENABLE_GNUTLS}")
+
 option(CIVETWEB_ENABLE_MBEDTLS "Use the MbedTls" OFF)
-message(STATUS "SSL support - ${CIVETWEB_ENABLE_MBEDTLS}")
+message(STATUS "SSL support (MbedTLS) - ${CIVETWEB_ENABLE_MBEDTLS}")
 
 # Dynamically load or link the SSL libraries
 cmake_dependent_option(
@@ -538,6 +541,8 @@ if (CIVETWEB_ENABLE_MEMORY_DEBUGGING)
 endif()
 if (NOT CIVETWEB_ENABLE_SSL)
   add_definitions(-DNO_SSL)
+elseif (CIVETWEB_ENABLE_GNUTLS)
+  add_definitions(-DUSE_GNUTLS)
 elseif (CIVETWEB_ENABLE_MBEDTLS)
   add_definitions(-DUSE_MBEDTLS)
 elseif (NOT CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
index 3e39658157ffa4ab44fb6d5e4ac4e718876843cb..7831ce39a6884e91956de919de825cb8ada937c6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -97,6 +97,9 @@ endif
 
 ifdef NO_SSL
   CFLAGS += -DNO_SSL
+else ifdef WITH_GNUTLS
+  CFLAGS += -DUSE_GNUTLS
+  LIBS += -lgnutls -lhogweed -lgmp -lnettle
 else ifdef WITH_MBEDTLS
   CFLAGS += -DUSE_MBEDTLS
   LIBS += -lmbedcrypto -lmbedtls -lmbedx509
@@ -303,6 +306,7 @@ help:
        @echo "   WITH_CPP=1            build library with c++ classes"
        @echo "   WITH_EXPERIMENTAL=1   build with experimental features"
        @echo "   WITH_DAEMONIZE=1      build with daemonize."
+       @echo "   WITH_GNUTLS=1         build with GnuTLS support."
        @echo "   WITH_MBEDTLS=1        build with mbedTLS support."
        @echo "   WITH_OPENSSL_API_1_0=1  build with OpenSSL 1.0.x support."
        @echo "   WITH_OPENSSL_API_1_1=1  build with OpenSSL 1.1.x support."
index d0624ab52cff5628b62fbe06b86043803dcebc38..dae058791bd64e29498bd7ef963cf1b245dac4de 100644 (file)
--- a/README.md
+++ b/README.md
@@ -134,6 +134,8 @@ simplicity by a carefully selected list of features:
 
 [Mbed TLS](https://github.com/ARMmbed/mbedtls)
 
+[GNU TLS](https://gnutls.org)
+
 
 Support
 -------
index c6f92add745a21fe6ec676d7063421bc99c515ab..b618533380b36c2b785186bf86665a8bd4acab52 100644 (file)
@@ -179,8 +179,9 @@ make build COPT="-DNDEBUG -DNO_CGI"
 | `SSL_ALREADY_INITIALIZED`    | do not initialize libcrypto                                         |
 | `OPENSSL_API_1_0`            | Use OpenSSL V1.0.x interface                                        |
 | `OPENSSL_API_1_1`            | Use OpenSSL V1.1.x interface                                        |
-| `OPENSSL_API_3_0`            | Use OpenSSL V3.0.x interface                                          |
-| `USE_MBEDTLS`                | Use MbedTLS (cannot be combined with OPENSSL_API_*)                 |
+| `OPENSSL_API_3_0`            | Use OpenSSL V3.0.x interface                                        |
+| `USE_GNUTLS`                 | Use GnuTLS (cannot be combined with OPENSSL_API_* or USE_MBEDTLS)   |
+| `USE_MBEDTLS`                | Use MbedTLS (cannot be combined with OPENSSL_API_* or USE_GNUTLS)   |
 |                              |                                                                     |
 | `BUILD_DATE`                 | define as a string to be used as build id instead of __DATE__       |
 |                              |                                                                     |
index ba009feca230ce62141a75fa3dcc0efd054e4d1a..7dc48c38b75b7f39c6242f4ffb02bb988de2c7c4 100644 (file)
@@ -692,7 +692,8 @@ The OpenSSL cipher string uses different cipher names than IANA
 (see [this mapping](https://testssl.sh/openssl-iana.mapping.html)).
 
 In case CivetWeb is built with a TLS library other than OpenSSL 
-(e.g., [mbedTLS](https://tls.mbed.org/supported-ssl-ciphersuites)), 
+(e.g., [mbedTLS](https://tls.mbed.org/supported-ssl-ciphersuites)
+or [GnuTLS](https://www.gnutls.org/manual/html_node/Supported-ciphersuites.html)), 
 the cipher names may be different.
 
 ### ssl\_default\_verify\_paths `yes`
diff --git a/docs/gnutls.md b/docs/gnutls.md
new file mode 100644 (file)
index 0000000..c247850
--- /dev/null
@@ -0,0 +1,20 @@
+#### Use GnuTLS instead of OpenSSL
+=====
+
+1 [Build libgmp](https://gmplib.org)
+
+ - 1.1 [Download source](https://gmplib.org/#DOWNLOAD)
+ - 1.2 ./configure && make && make install
+
+2 [Build libhogweed and libnettle](https://www.lysator.liu.se/~nisse/nettle/)
+
+ - 2.1 [Download source](https://ftp.gnu.org/gnu/nettle/)
+ - 2.2 ./configure && make && make install
+
+3 Build civetweb
+
+ - make build WITH_GNUTLS=1
+
+4 Run civetweb
+ - export LD_LIBRARY_PATH=/usr/local/lib/:$LD_LIBRARY_PATH
+ - ./civetweb  -listening_ports 8443s  -ssl_certificate resources/cert/server.pem  -document_root ./test/htmldir/
index 203b0446bddf15b499e57248d1fb12bb2ba65dc8..3bb94e650208c0f1a0e87519d9be0097362a9bb2 100755 (executable)
@@ -17,6 +17,7 @@ clang-format -i src/handle_form.inl
 clang-format -i src/response.inl
 clang-format -i src/http2.inl
 clang-format -i src/mod_mbedtls.inl
+clang-format -i src/mod_gnutls.inl
 
 clang-format -i src/third_party/civetweb_lua.h
 
index 28c4e8cc3dc440f283344f07d1c0f02c9001fcff..8736e126a05fe137e422294c7c87ea64090da23e 100644 (file)
@@ -47,7 +47,12 @@ endif()
 
 # We need to link OpenSSL if not dynamically loading
 if (CIVETWEB_ENABLE_SSL)
-  if (CIVETWEB_ENABLE_MBEDTLS)
+  if (CIVETWEB_ENABLE_GNUTLS)
+    find_package(GnuTLS)
+    include_directories(${GNUTLS_INCLUDE_DIR})
+    message(STATUS "GnuTLS include directory: ${GNUTLS_INCLUDE_DIR}")
+    target_link_libraries(civetweb-c-library ${GNUTLS_LIBRARIES})
+  elseif (CIVETWEB_ENABLE_MBEDTLS)
     find_package(MbedTLS)
     include_directories(${MbedTLS_INCLUDE_DIR})
     message(STATUS "MbedTLS include directory: ${MbedTLS_INCLUDE_DIR}")
index c52c8e7abc041da7fff14ba23125be867d094ef9..814e1de0dae9a268e80f1c4183d6307524b60b20 100644 (file)
@@ -1594,8 +1594,9 @@ static int mg_init_library_called = 0;
 static int mg_openssl_initialized = 0;
 #endif
 #if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1)                     \
-    && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS)
-#error "Please define OPENSSL_API_#_# or USE_MBEDTLS"
+    && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS)                      \
+       && !defined(USE_GNUTLS)
+#error "Please define OPENSSL_API_#_# or USE_MBEDTLS or USE_GNUTLS"
 #endif
 #if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1)
 #error "Multiple OPENSSL_API versions defined"
@@ -1608,7 +1609,10 @@ static int mg_openssl_initialized = 0;
 #endif
 #if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)                      \
      || defined(OPENSSL_API_3_0))                                              \
-    && defined(USE_MBEDTLS)
+    && (defined(USE_MBEDTLS) || defined(USE_GNUTLS))
+#error "Multiple SSL libraries defined"
+#endif
+#if defined(USE_MBEDTLS) && defined(USE_GNUTLS)
 #error "Multiple SSL libraries defined"
 #endif
 #endif
@@ -1773,11 +1777,15 @@ typedef int socklen_t;
 #endif
 
 
-/* SSL: mbedTLS vs. no-ssl vs. OpenSSL */
+/* SSL: mbedTLS vs. GnuTLS vs. no-ssl vs. OpenSSL */
 #if defined(USE_MBEDTLS)
 /* mbedTLS */
 #include "mod_mbedtls.inl"
 
+#elif defined(USE_GNUTLS)
+/* GnuTLS */
+#include "mod_gnutls.inl"
+
 #elif defined(NO_SSL)
 /* no SSL */
 typedef struct SSL SSL; /* dummy for SSL argument to push/pull */
@@ -2564,7 +2572,6 @@ struct mg_connection {
                         * versions. For the current definition, see
                         * mg_get_connection_info_impl */
 #endif
-
        SSL *ssl;               /* SSL descriptor */
        struct socket client;   /* Connected client */
        time_t conn_birth_time; /* Time (wall clock) when connection was
@@ -6131,7 +6138,7 @@ push_inner(struct mg_context *ctx,
                return -2;
        }
 
-#if defined(NO_SSL) && !defined(USE_MBEDTLS)
+#if defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS)
        if (ssl) {
                return -2;
        }
@@ -6157,6 +6164,16 @@ push_inner(struct mg_context *ctx,
                                err = 0;
                        }
                } else
+#elif defined(USE_GNUTLS)
+               if (ssl != NULL) {
+                       n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t) len);
+                       if (n < 0) {
+                               fprintf(stderr, "SSL write failed (%d): %s", n, gnutls_strerror(n));
+                               return -2;
+                       } else {
+                               err = 0;
+                       }
+               } else
 #elif !defined(NO_SSL)
                if (ssl != NULL) {
                        ERR_clear_error();
@@ -6413,6 +6430,62 @@ pull_inner(FILE *fp,
                        nread = 0;
                }
 
+#elif defined(USE_GNUTLS)
+       } else if (conn->ssl != NULL) {
+               struct mg_pollfd pfd[2];
+               size_t to_read;
+               int pollres;
+               unsigned int num_sock = 1;
+
+               to_read = gnutls_record_check_pending(conn->ssl->sess);
+
+               if (to_read > 0) {
+                       /* We already know there is no more data buffered in conn->buf
+                        * but there is more available in the SSL layer. So don't poll
+                        * conn->client.sock yet. */
+
+                       pollres = 1;
+                       if (to_read > (size_t)len)
+                               to_read = (size_t)len;
+               } else {
+                       pfd[0].fd = conn->client.sock;
+                       pfd[0].events = POLLIN;
+
+                       if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
+                               pfd[num_sock].fd =
+                                   conn->phys_ctx->thread_shutdown_notification_socket;
+                               pfd[num_sock].events = POLLIN;
+                               num_sock++;
+                       }
+
+                       to_read = (size_t)len;
+
+                       pollres = mg_poll(pfd,
+                                         num_sock,
+                                         (int)(timeout * 1000.0),
+                                         &(conn->phys_ctx->stop_flag));
+
+                       if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
+                               return -2;
+                       }
+               }
+
+               if (pollres > 0) {
+                       nread = gtls_ssl_read(conn->ssl, (unsigned char *)buf, to_read);
+                       if (nread < 0) {
+                               fprintf(stderr, "SSL read failed (%d): %s", nread, gnutls_strerror(nread));
+                               return -2;
+                       } else {
+                               err = 0;
+                       }
+               } else if (pollres < 0) {
+                       /* Error */
+                       return -2;
+               } else {
+                       /* pollres = 0 means timeout */
+                       nread = 0;
+               }
+
 #elif !defined(NO_SSL)
        } else if (conn->ssl != NULL) {
                int ssl_pending;
@@ -9555,7 +9628,7 @@ connect_socket(
                return 0;
        }
 
-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(NO_SSL_DL)
+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) && !defined(NO_SSL_DL)
 #if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
        if (use_ssl && (TLS_client_method == NULL)) {
                if (error != NULL) {
@@ -16660,6 +16733,37 @@ mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
                   : 0;
 }
 
+#elif defined(USE_GNUTLS)
+/* Check if SSL is required.
+ * If so, set up ctx->ssl_ctx pointer. */
+static int
+mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
+{
+       if (!phys_ctx) {
+               return 0;
+       }
+
+       if (!dom_ctx) {
+               dom_ctx = &(phys_ctx->dd);
+       }
+
+       if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
+               /* No SSL port is set. No need to setup SSL. */
+               return 1;
+       }
+
+       dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx));
+       if (dom_ctx->ssl_ctx == NULL) {
+               fprintf(stderr, "ssl_ctx malloc failed\n");
+               return 0;
+       }
+
+       return gtls_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE])
+                      == 0
+                  ? 1
+                  : 0;
+}
+
 #elif !defined(NO_SSL)
 
 static int ssl_use_pem_file(struct mg_context *phys_ctx,
@@ -17935,7 +18039,7 @@ uninitialize_openssl(void)
 #endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
        }
 }
-#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) */
+#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) */
 
 
 #if !defined(NO_FILESYSTEMS)
@@ -18207,6 +18311,11 @@ close_connection(struct mg_connection *conn)
                mbed_ssl_close(conn->ssl);
                conn->ssl = NULL;
        }
+#elif defined(USE_GNUTLS)
+       if (conn->ssl != NULL) {
+               gtls_ssl_close(conn->ssl);
+               conn->ssl = NULL;
+       }
 #elif !defined(NO_SSL)
        if (conn->ssl != NULL) {
                /* Run SSL_shutdown twice to ensure completely close SSL connection
@@ -18278,7 +18387,7 @@ mg_close_connection(struct mg_connection *conn)
 
        close_connection(conn);
 
-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
        if (((conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT)
             || (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT))
            && (conn->phys_ctx->dd.ssl_ctx != NULL)) {
@@ -18380,7 +18489,7 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
                return NULL;
        }
 
-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
 #if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0))                     \
     && !defined(NO_SSL_DL)
 
@@ -18457,7 +18566,7 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
                                    error->text_buffer_size,
                                    "Can not create mutex");
                }
-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
                SSL_CTX_free(conn->dom_ctx->ssl_ctx);
 #endif
                closesocket(sock);
@@ -18465,7 +18574,7 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
                return NULL;
        }
 
-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
        if (use_ssl) {
                /* TODO: Check ssl_verify_peer and ssl_ca_path here.
                 * SSL_CTX_set_verify call is needed to switch off server
@@ -20186,6 +20295,24 @@ worker_thread_run(struct mg_connection *conn)
                                close_connection(conn);
                        }
 
+#elif defined(USE_GNUTLS)
+                       /* HTTPS connection */
+                       if (gtls_ssl_accept(&(conn->ssl),
+                                           conn->dom_ctx->ssl_ctx,
+                                           conn->client.sock,
+                                           conn->phys_ctx)
+                           == 0) {
+                               /* conn->dom_ctx is set in get_request */
+                               /* process HTTPS connection */
+                               init_connection(conn);
+                               conn->connection_type = CONNECTION_TYPE_REQUEST;
+                               conn->protocol_type = PROTOCOL_TYPE_HTTP1;
+                               process_new_connection(conn);
+                       } else {
+                               /* make sure the connection is cleaned up on SSL failure */
+                               close_connection(conn);
+                       }
+
 #elif !defined(NO_SSL)
                        /* HTTPS connection */
                        if (sslize(conn, SSL_accept, NULL)) {
@@ -20693,6 +20820,13 @@ free_context(struct mg_context *ctx)
                ctx->dd.ssl_ctx = NULL;
        }
 
+#elif defined(USE_GNUTLS)
+       if (ctx->dd.ssl_ctx != NULL) {
+               gtls_sslctx_uninit(ctx->dd.ssl_ctx);
+               mg_free(ctx->dd.ssl_ctx);
+               ctx->dd.ssl_ctx = NULL;
+       }
+
 #elif !defined(NO_SSL)
        /* Deallocate SSL context */
        if (ctx->dd.ssl_ctx != NULL) {
@@ -21383,7 +21517,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        }
 #endif
 
-#if defined(USE_MBEDTLS)
+#if defined(USE_MBEDTLS) || defined(USE_GNUTLS)
        if (!mg_sslctx_init(ctx, NULL)) {
                const char *err_msg = "Error initializing SSL context";
                /* Fatal error - abort start. */
@@ -21868,7 +22002,7 @@ mg_start_domain2(struct mg_context *ctx,
        new_dom->shared_lua_websockets = NULL;
 #endif
 
-#if !defined(NO_SSL) && !defined(USE_MBEDTLS)
+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS)
        if (!init_ssl_ctx(ctx, new_dom)) {
                /* Init SSL failed */
                if (error != NULL) {
@@ -21947,7 +22081,7 @@ mg_check_feature(unsigned feature)
 #if !defined(NO_FILES)
                                            | MG_FEATURES_FILES
 #endif
-#if !defined(NO_SSL) || defined(USE_MBEDTLS)
+#if !defined(NO_SSL) || defined(USE_MBEDTLS) || defined(USE_GNUTLS)
                                            | MG_FEATURES_SSL
 #endif
 #if !defined(NO_CGI)
diff --git a/src/mod_gnutls.inl b/src/mod_gnutls.inl
new file mode 100644 (file)
index 0000000..b4ca5d4
--- /dev/null
@@ -0,0 +1,240 @@
+#if defined(USE_GNUTLS) // USE_GNUTLS used with NO_SSL
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+typedef struct {
+       gnutls_session_t sess;
+} SSL;
+typedef struct {
+       gnutls_certificate_credentials_t cred;
+       gnutls_priority_t prio;
+} SSL_CTX;
+
+
+/* public api */
+CIVETWEB_API int gtls_sslctx_init(SSL_CTX *ctx, const char *crt);
+CIVETWEB_API void gtls_sslctx_uninit(SSL_CTX *ctx);
+CIVETWEB_API void gtls_ssl_close(SSL *ssl);
+CIVETWEB_API int gtls_ssl_accept(SSL **ssl,
+                    SSL_CTX *ssl_ctx,
+                    int sock,
+                    struct mg_context *phys_ctx);
+CIVETWEB_API int gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len);
+CIVETWEB_API int gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len);
+
+
+CIVETWEB_API int
+gtls_sslctx_init(SSL_CTX *ctx, const char *crt)
+{
+       int rc;
+
+       if (ctx == NULL || crt == NULL) {
+               return -1;
+       }
+
+       DEBUG_TRACE("%s", "Initializing GnuTLS SSL");
+
+       rc = gnutls_certificate_allocate_credentials(&ctx->cred);
+       if (rc != GNUTLS_E_SUCCESS) {
+               DEBUG_TRACE("Failed to allocate credentials (%d): %s",
+                           rc,
+                           gnutls_strerror(rc));
+               goto failed;
+       }
+
+       rc = gnutls_priority_init(&ctx->prio, NULL, NULL);
+       if (rc != GNUTLS_E_SUCCESS) {
+               DEBUG_TRACE("Failed to allocate priority cache (%d): %s",
+                           rc,
+                           gnutls_strerror(rc));
+               goto failed;
+       }
+
+       rc = gnutls_certificate_set_x509_key_file2(ctx->cred,
+                                                  crt,
+                                                  crt,
+                                                  GNUTLS_X509_FMT_PEM,
+                                                  NULL,
+                                                  GNUTLS_PKCS_PLAIN
+                                                      | GNUTLS_PKCS_NULL_PASSWORD);
+       if (rc != GNUTLS_E_SUCCESS) {
+               DEBUG_TRACE("TLS parse crt/key file failed (%d): %s",
+                           rc,
+                           gnutls_strerror(rc));
+               goto failed;
+       }
+
+       return 0;
+
+failed:
+       gtls_sslctx_uninit(ctx);
+
+       return -1;
+}
+
+
+CIVETWEB_API void
+gtls_sslctx_uninit(SSL_CTX *ctx)
+{
+       if (ctx != NULL) {
+               gnutls_certificate_free_credentials(ctx->cred);
+               gnutls_priority_deinit(ctx->prio);
+               ctx->cred = NULL;
+               ctx->prio = NULL;
+       }
+}
+
+
+CIVETWEB_API int
+gtls_ssl_accept(SSL **ssl,
+                SSL_CTX *ssl_ctx,
+                int sock,
+                struct mg_context *phys_ctx)
+{
+       int rc;
+
+       if (ssl == NULL || ssl_ctx == NULL) {
+               return -1;
+       }
+
+       DEBUG_TRACE("TLS accept processing %p", ssl);
+
+       *ssl = (SSL *)mg_calloc_ctx(1, sizeof(SSL), phys_ctx);
+       if (*ssl == NULL) {
+               DEBUG_TRACE("Failed to allocate memory for session %zu", sizeof(SSL));
+               return -1;
+       }
+
+       rc = gnutls_init(&(*ssl)->sess, GNUTLS_SERVER);
+       if (rc != GNUTLS_E_SUCCESS) {
+               DEBUG_TRACE("Failed to initialize session (%d): %s",
+                           rc,
+                           gnutls_strerror(rc));
+               goto failed;
+       }
+
+       rc = gnutls_priority_set((*ssl)->sess, ssl_ctx->prio);
+       if (rc != GNUTLS_E_SUCCESS) {
+               DEBUG_TRACE("TLS set priortities failed (%d): %s",
+                           rc,
+                           gnutls_strerror(rc));
+               goto failed;
+       }
+
+       rc = gnutls_credentials_set((*ssl)->sess,
+                                   GNUTLS_CRD_CERTIFICATE,
+                                   ssl_ctx->cred);
+       if (rc != GNUTLS_E_SUCCESS) {
+               DEBUG_TRACE("TLS set credentials failed (%d): %s",
+                           rc,
+                           gnutls_strerror(rc));
+               goto failed;
+       }
+
+       gnutls_certificate_send_x509_rdn_sequence((*ssl)->sess, 1);
+       gnutls_certificate_server_set_request((*ssl)->sess, GNUTLS_CERT_IGNORE);
+       gnutls_handshake_set_timeout((*ssl)->sess,
+                                    GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+       gnutls_transport_set_int((*ssl)->sess, sock);
+
+       while ((rc = gnutls_handshake((*ssl)->sess)) != GNUTLS_E_SUCCESS) {
+               if (gnutls_error_is_fatal(rc)) {
+                       if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+                               DEBUG_TRACE("TLS fatal alert received: %s",
+                                           gnutls_alert_get_name(
+                                               gnutls_alert_get((*ssl)->sess)));
+                       } else {
+                               DEBUG_TRACE("TLS handshake failed (%d): %s",
+                                           rc,
+                                           gnutls_strerror(rc));
+                       }
+
+                       goto failed;
+               }
+       }
+
+       DEBUG_TRACE("TLS connection %p accepted", *ssl);
+
+       return 0;
+
+failed:
+       gnutls_deinit((*ssl)->sess);
+       mg_free(*ssl);
+       *ssl = NULL;
+
+       return -1;
+}
+
+
+CIVETWEB_API void
+gtls_ssl_close(SSL *ssl)
+{
+       int rc;
+
+       if (ssl == NULL) {
+               return;
+       }
+
+       while ((rc = gnutls_bye(ssl->sess, GNUTLS_SHUT_RDWR)) != GNUTLS_E_SUCCESS) {
+               switch (rc) {
+               case GNUTLS_E_AGAIN: /* fall through */
+               case GNUTLS_E_INTERRUPTED:
+                       continue;
+               default: /* should actually never happen */
+                       break;
+               }
+       }
+
+       DEBUG_TRACE("TLS connection %p closed", ssl);
+       gnutls_deinit(ssl->sess);
+       mg_free(ssl);
+}
+
+
+CIVETWEB_API int
+gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len)
+{
+       ssize_t rc;
+
+       if (ssl == NULL) {
+               return GNUTLS_E_INVALID_SESSION;
+       }
+
+       while ((rc = gnutls_record_recv(ssl->sess, buf, len)) < 0) {
+               switch (rc) {
+               case GNUTLS_E_AGAIN: /* fall through */
+               case GNUTLS_E_INTERRUPTED:
+                       continue;
+               default:
+                       break;
+               }
+       }
+       /* DEBUG_TRACE("gnutls_record_recv: %d", rc); */
+       return (int)rc;
+}
+
+
+CIVETWEB_API int
+gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len)
+{
+       ssize_t rc;
+
+       if (ssl == NULL) {
+               return GNUTLS_E_INVALID_SESSION;
+       }
+
+       while ((rc = gnutls_record_send(ssl->sess, buf, len)) < 0) {
+               switch (rc) {
+               case GNUTLS_E_AGAIN: /* fall through */
+               case GNUTLS_E_INTERRUPTED:
+                       continue;
+               default:
+                       break;
+               }
+       }
+       /* DEBUG_TRACE("gnutls_record_send: %d", rc); */
+       return (int)rc;
+}
+
+#endif /* USE_GNUTLS */