Allow to use wolfSSL for https inspection
authorFabian Keil <fk@fabiankeil.de>
Tue, 12 Jan 2021 07:12:38 +0000 (08:12 +0100)
committerFabian Keil <fk@fabiankeil.de>
Sat, 9 Mar 2024 11:11:46 +0000 (12:11 +0100)
It's licensed under GPlv2 or later and unlike mbedTLS
there don't seem to be plans to change the license.

As a bonus, wolfSSL supports TLS 1.3 and can be significantly
faster than mbedTLS. Mainly tested on ElectroBSD amd64 where
it can compete with OpenSSL and LibreSSL:
https://www.fabiankeil.de/gehacktes/privoxy-tls-benchmarks/

To enable the support, install wolfSSL and run ./configure
with the --with-wolfssl option.

Privoxy users and packagers that currently build Privoxy
binaries with mbedTLS may want to consider using wolfSSL
in the future once it has been properly tested.

Sponsored by: Privoxy project funds collected at SPI

GNUmakefile.in
configure.in
project.h
wolfssl.c [new file with mode: 0644]

index feec584..04cceb1 100644 (file)
@@ -243,6 +243,10 @@ OPENSSL_SRC      = @FEATURE_HTTPS_INSPECTION_ONLY_OPENSSL@openssl.c
 OPENSSL_OBJS     = @FEATURE_HTTPS_INSPECTION_ONLY_OPENSSL@$(OPENSSL_SRC:.c=.o)
 OPENSSL_HDRS     = @FEATURE_HTTPS_INSPECTION_ONLY_OPENSSL@$(OPENSSL_SRC:.c=.h)
 
+WOLFSSL_SRC      = @FEATURE_HTTPS_INSPECTION_ONLY_WOLFSSL@wolfssl.c
+WOLFSSL_OBJS     = @FEATURE_HTTPS_INSPECTION_ONLY_WOLFSSL@$(WOLFSSL_SRC:.c=.o)
+WOLFSSL_HDRS     = @FEATURE_HTTPS_INSPECTION_ONLY_WOLFSSL@$(WOLFSSL_SRC:.c=.h)
+
 PCRS_SRC     = @STATIC_PCRS_ONLY@pcrs.c
 PCRS_OBJS    = @STATIC_PCRS_ONLY@$(PCRS_SRC:.c=.@OBJEXT@)
 PCRS_HDRS    = @STATIC_PCRS_ONLY@$(PCRS_SRC:.c=.h)
@@ -262,9 +266,9 @@ SOCKET_LIB   = @SOCKET_LIB@
 # PThreads library, if needed.
 PTHREAD_LIB  = @PTHREAD_ONLY@@PTHREAD_LIB@
 
-SRCS         = $(C_SRC) $(CLIENT_TAG_SRC) $(FUZZ_SRC) $(W32_SRC) $(PCRS_SRC) $(REGEX_SRC) $(SSL_SRC) $(MBEDTLS_SRC) $(OPENSSL_SRC)
-OBJS         = $(C_OBJS) $(CLIENT_TAG_OBJS) $(FUZZ_OBJS) $(W32_OBJS) $(PCRS_OBJS) $(REGEX_OBJS) $(SSL_OBJS) $(MBEDTLS_OBJS) $(OPENSSL_OBJS)
-HDRS         = $(C_HDRS) $(W32_HDRS) $(PCRS_HDRS) $(REGEX_HDRS) $(SSL_HDRS) $(MBEDTLS_HDRS) $(OPENSSL_HDRS)
+SRCS         = $(C_SRC) $(CLIENT_TAG_SRC) $(FUZZ_SRC) $(W32_SRC) $(PCRS_SRC) $(REGEX_SRC) $(SSL_SRC) $(MBEDTLS_SRC) $(OPENSSL_SRC) $(WOLFSSL_SRC)
+OBJS         = $(C_OBJS) $(CLIENT_TAG_OBJS) $(FUZZ_OBJS) $(W32_OBJS) $(PCRS_OBJS) $(REGEX_OBJS) $(SSL_OBJS) $(MBEDTLS_OBJS) $(OPENSSL_OBJS) $(WOLFSSL_OBJS)
+HDRS         = $(C_HDRS) $(W32_HDRS) $(PCRS_HDRS) $(REGEX_HDRS) $(SSL_HDRS) $(MBEDTLS_HDRS) $(OPENSSL_HDRS) $(WOLFSSL_HDRS)
 LIBS         = @LIBS@ $(W32_LIB) $(SOCKET_LIB) $(PTHREAD_LIB)
 
 
index 95141ec..1f7256d 100644 (file)
@@ -1295,6 +1295,45 @@ if test X"$OPT_OPENSSL" != Xno; then
 fi
 AC_SUBST(FEATURE_HTTPS_INSPECTION_ONLY_OPENSSL)
 
+dnl ========================================================
+dnl check for wolfSSL which can be used for https inspection
+dnl ========================================================
+FEATURE_HTTPS_INSPECTION_ONLY_WOLFSSL=#
+OPT_WOLFSSL=no
+AC_ARG_WITH(wolfssl,dnl
+AC_HELP_STRING([--with-wolfssl], [Enable Wolfssl/LibreSSL detection for https inspection.])
+AC_HELP_STRING([--without-wolfssl], [Disable Wolfssl/LibreSSL detection]),
+  OPT_WOLFSSL=$withval)
+
+if test X"$OPT_WOLFSSL" != Xno; then
+    if test X"$OPT_MBEDTLS" != Xno; then
+        AC_MSG_ERROR([wolfSSL and mbedTLS support can't be enabled at the same time])
+    fi
+    if test X"$OPT_OPENSSL" != Xno; then
+        AC_MSG_ERROR([wolfSSL and OpenSSL support can't be enabled at the same time])
+    fi
+
+    AC_CHECK_LIB(wolfssl, wolfSSL_Init, [HAVE_WOLFSSL_INIT="yes"])
+
+    if test "x$HAVE_WOLFSSL_INIT" = "xyes"; then
+        AC_DEFINE(FEATURE_HTTPS_INSPECTION, 1, [if SSL/TLS is enabled])
+        AC_DEFINE(FEATURE_HTTPS_INSPECTION_WOLFSSL, 1, [if Wolfssl is enabled])
+        AC_SUBST(FEATURE_HTTPS_INSPECTION_WOLFSSL, [1])
+        FEATURE_HTTPS_INSPECTION="yes"
+        FEATURE_HTTPS_INSPECTION_WOLFSSL="yes"
+    fi
+
+    if test "x$FEATURE_HTTPS_INSPECTION_WOLFSSL" = "xyes"; then
+    AC_MSG_NOTICE([Detected wolfSSL. Enabling https inspection.])
+
+    LIBS="$LIBS -lwolfssl"
+
+    FEATURE_HTTPS_INSPECTION_ONLY=
+    FEATURE_HTTPS_INSPECTION_ONLY_WOLFSSL=
+  fi
+fi
+AC_SUBST(FEATURE_HTTPS_INSPECTION_ONLY_WOLFSSL)
+
 AC_SUBST(FEATURE_HTTPS_INSPECTION_ONLY)
 
 dnl ========================================================
index baf7cf6..6aaefd7 100644 (file)
--- a/project.h
+++ b/project.h
 #include <openssl/err.h>
 #endif /* FEATURE_HTTPS_INSPECTION_OPENSSL */
 
+#ifdef FEATURE_HTTPS_INSPECTION_WOLFSSL
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#endif /* FEATURE_HTTPS_INSPECTION_WOLFSSL */
+
 /* Need for struct sockaddr_storage */
 #ifdef HAVE_RFC2553
 #  ifndef _WIN32
@@ -337,6 +342,17 @@ typedef struct {
    BIO *bio;
 } openssl_connection_attr;
 #endif /* FEATURE_HTTPS_INSPECTION_OPENSSL */
+
+#ifdef FEATURE_HTTPS_INSPECTION_WOLFSSL
+/*
+ * Struct of attributes necessary for TLS/SSL connection
+ */
+typedef struct {
+   WOLFSSL_CTX *ctx;
+   WOLFSSL *ssl;
+} wolfssl_connection_attr;
+#endif /* def FEATURE_HTTPS_INSPECTION_WOLFSSL */
+
 /**
  * A HTTP request.  This includes the method (GET, POST) and
  * the parsed URL.
@@ -1008,6 +1024,9 @@ struct ssl_attr {
 #ifdef FEATURE_HTTPS_INSPECTION_OPENSSL
    openssl_connection_attr  openssl_attr; /* OpenSSL atrrs */
 #endif /* FEATURE_HTTPS_INSPECTION_OPENSSL */
+#ifdef FEATURE_HTTPS_INSPECTION_WOLFSSL
+   wolfssl_connection_attr wolfssl_attr; /* wolfSSL atrrs */
+#endif /* FEATURE_HTTPS_INSPECTION_WOLFSSL */
 };
 /**
  * The state of a Privoxy processing thread.
@@ -1149,7 +1168,7 @@ struct client_state
 #define SSL_CERT_NOT_VERIFIED   0xFFFFFFFF
    uint32_t server_cert_verification_result;
 #endif /* FEATURE_HTTPS_INSPECTION_MBEDTLS */
-#ifdef FEATURE_HTTPS_INSPECTION_OPENSSL
+#if defined(FEATURE_HTTPS_INSPECTION_OPENSSL) || defined(FEATURE_HTTPS_INSPECTION_WOLFSSL)
 #define SSL_CERT_NOT_VERIFIED    ~0L
    long server_cert_verification_result;
 #endif /* FEATURE_HTTPS_INSPECTION_OPENSSL */
diff --git a/wolfssl.c b/wolfssl.c
new file mode 100644 (file)
index 0000000..bdbf8e9
--- /dev/null
+++ b/wolfssl.c
@@ -0,0 +1,1996 @@
+/*********************************************************************
+ *
+ * File        :  $Source: /cvsroot/ijbswa/current/wolfssl.c,v $
+ *
+ * Purpose     :  File with TLS/SSL extension. Contains methods for
+ *                creating, using and closing TLS/SSL connections
+ *                using wolfSSL.
+ *
+ * Copyright   :  Copyright (C) 2018-2021 by Fabian Keil <fk@fabiankeil.de>
+ *                Copyright (C) 2020 Maxim Antonov <mantonov@gmail.com>
+ *                Copyright (C) 2017 Vaclav Svec. FIT CVUT.
+ *
+ *                This program is free software; you can redistribute it
+ *                and/or modify it under the terms of the GNU General
+ *                Public License as published by the Free Software
+ *                Foundation; either version 2 of the License, or (at
+ *                your option) any later version.
+ *
+ *                This program is distributed in the hope that it will
+ *                be useful, but WITHOUT ANY WARRANTY; without even the
+ *                implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *                PARTICULAR PURPOSE.  See the GNU General Public
+ *                License for more details.
+ *
+ *                The GNU General Public License should be included with
+ *                this file.  If not, you can view it at
+ *                http://www.gnu.org/copyleft/gpl.html
+ *                or write to the Free Software Foundation, Inc., 59
+ *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *********************************************************************/
+
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include "config.h"
+
+#include <wolfssl/options.h>
+#include <wolfssl/openssl/x509v3.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/wolfcrypt/coding.h>
+#include <wolfssl/wolfcrypt/rsa.h>
+
+#include "project.h"
+#include "miscutil.h"
+#include "errlog.h"
+#include "encode.h"
+#include "jcc.h"
+#include "jbsockets.h"
+#include "ssl.h"
+#include "ssl_common.h"
+
+static int ssl_certificate_is_invalid(const char *cert_file);
+static int generate_host_certificate(struct client_state *csp,
+   const char *certificate_path, const char *key_path);
+static void free_client_ssl_structures(struct client_state *csp);
+static void free_server_ssl_structures(struct client_state *csp);
+static int ssl_store_cert(struct client_state *csp, X509 *crt);
+static void log_ssl_errors(int debuglevel, const char* fmt, ...) __attribute__((format(printf, 2, 3)));
+
+static int wolfssl_initialized = 0;
+
+/*
+ * Whether or not sharing the RNG is thread-safe
+ * doesn't matter because we only use it with
+ * the certificate_mutex locked.
+ */
+static RNG wolfssl_rng;
+
+#ifndef WOLFSSL_ALT_CERT_CHAINS
+/*
+ * Without WOLFSSL_ALT_CERT_CHAINS wolfSSL will reject valid
+ * certificates if the certificate chain contains CA certificates
+ * that are "only" signed by trusted CA certificates but aren't
+ * trusted CAs themselves.
+ */
+#warning wolfSSL has been compiled without WOLFSSL_ALT_CERT_CHAINS
+#endif
+
+/*********************************************************************
+ *
+ * Function    :  wolfssl_init
+ *
+ * Description :  Initializes wolfSSL library once
+ *
+ * Parameters  :  N/A
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+static void wolfssl_init(void)
+{
+   if (wolfssl_initialized == 0)
+   {
+      privoxy_mutex_lock(&ssl_init_mutex);
+      if (wolfssl_initialized == 0)
+      {
+         if (wolfSSL_Init() != WOLFSSL_SUCCESS)
+         {
+            log_error(LOG_LEVEL_FATAL, "Failed to initialize wolfSSL");
+         }
+         wc_InitRng(&wolfssl_rng);
+         wolfssl_initialized = 1;
+      }
+      privoxy_mutex_unlock(&ssl_init_mutex);
+   }
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  is_ssl_pending
+ *
+ * Description :  Tests if there are some waiting data on ssl connection.
+ *                Only considers data that has actually been received
+ *                locally and ignores data that is still on the fly
+ *                or has not yet been sent by the remote end.
+ *
+ * Parameters  :
+ *          1  :  ssl_attr = SSL context to test
+ *
+ * Returns     :   0 => No data are pending
+ *                >0 => Pending data length. XXX: really?
+ *
+ *********************************************************************/
+extern size_t is_ssl_pending(struct ssl_attr *ssl_attr)
+{
+   return (size_t)wolfSSL_pending(ssl_attr->wolfssl_attr.ssl);
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  ssl_send_data
+ *
+ * Description :  Sends the content of buf (for n bytes) to given SSL
+ *                connection context.
+ *
+ * Parameters  :
+ *          1  :  ssl_attr = SSL context to send data to
+ *          2  :  buf = Pointer to data to be sent
+ *          3  :  len = Length of data to be sent to the SSL context
+ *
+ * Returns     :  Length of sent data or negative value on error.
+ *
+ *********************************************************************/
+extern int ssl_send_data(struct ssl_attr *ssl_attr, const unsigned char *buf, size_t len)
+{
+   WOLFSSL *ssl;
+   int ret = 0;
+   int pos = 0; /* Position of unsent part in buffer */
+   int fd = -1;
+
+   if (len == 0)
+   {
+      return 0;
+   }
+
+   ssl = ssl_attr->wolfssl_attr.ssl;
+   fd = wolfSSL_get_fd(ssl);
+
+   while (pos < len)
+   {
+      int send_len = (int)len - pos;
+
+      log_error(LOG_LEVEL_WRITING, "TLS on socket %d: %N",
+         fd, send_len, buf+pos);
+
+      ret = wolfSSL_write(ssl, buf+pos, send_len);
+      if (ret <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "Sending data on socket %d over TLS failed", fd);
+         return -1;
+      }
+
+      /* Adding count of sent bytes to position in buffer */
+      pos = pos + ret;
+   }
+
+   return (int)len;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  ssl_recv_data
+ *
+ * Description :  Receives data from given SSL context and puts
+ *                it into buffer.
+ *
+ * Parameters  :
+ *          1  :  ssl_attr = SSL context to receive data from
+ *          2  :  buf = Pointer to buffer where data will be written
+ *          3  :  max_length = Maximum number of bytes to read
+ *
+ * Returns     :  Number of bytes read, 0 for EOF, or -1
+ *                on error.
+ *
+ *********************************************************************/
+extern int ssl_recv_data(struct ssl_attr *ssl_attr, unsigned char *buf, size_t max_length)
+{
+   WOLFSSL *ssl;
+   int ret = 0;
+   int fd = -1;
+
+   memset(buf, 0, max_length);
+
+   /*
+    * Receiving data from SSL context into buffer
+    */
+   ssl = ssl_attr->wolfssl_attr.ssl;
+   ret = wolfSSL_read(ssl, buf, (int)max_length);
+   fd = wolfSSL_get_fd(ssl);
+
+   if (ret < 0)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "Receiving data on socket %d over TLS failed", fd);
+
+      return -1;
+   }
+
+   log_error(LOG_LEVEL_RECEIVED, "TLS from socket %d: %N",
+      fd, ret, buf);
+
+   return ret;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  get_public_key_size_string
+ *
+ * Description : Translates a public key type to a string.
+ *
+ * Parameters  :
+ *          1  :  key_type = The public key type.
+ *
+ * Returns     :  String containing the translated key size.
+ *
+ *********************************************************************/
+static const char *get_public_key_size_string(int key_type)
+{
+   switch (key_type)
+   {
+      case EVP_PKEY_RSA:
+         return "RSA key size";
+      case EVP_PKEY_DSA:
+         return "DSA key size";
+      case EVP_PKEY_EC:
+         return "EC key size";
+      default:
+         return "non-RSA/DSA/EC key size";
+   }
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  ssl_store_cert
+ *
+ * Description : This function is called once for each certificate in the
+ *               server's certificate trusted chain and prepares
+ *               information about the certificate. The information can
+ *               be used to inform the user about invalid certificates.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  cert = certificate from trusted chain
+ *
+ * Returns     :  0 on success and negative value on error
+ *
+ *********************************************************************/
+static int ssl_store_cert(struct client_state *csp, X509 *cert)
+{
+   long len;
+   struct certs_chain *last = &(csp->server_certs_chain);
+   int ret = 0;
+   WOLFSSL_BIO *bio = BIO_new(BIO_s_mem());
+   WOLFSSL_EVP_PKEY *pkey = NULL;
+   char *bio_mem_data = NULL;
+   char *encoded_text;
+   long l;
+   unsigned char serial_number[32];
+   int  serial_number_size = sizeof(serial_number);
+   WOLFSSL_X509_NAME *issuer_name;
+   WOLFSSL_X509_NAME *subject_name;
+   char *subject_alternative_name;
+   int loc;
+   int san_prefix_printed = 0;
+
+   if (!bio)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR, "BIO_new() failed");
+      return -1;
+   }
+
+   /*
+    * Searching for last item in certificates linked list
+    */
+   while (last->next != NULL)
+   {
+      last = last->next;
+   }
+
+   /*
+    * Preparing next item in linked list for next certificate
+    */
+   last->next = zalloc_or_die(sizeof(struct certs_chain));
+
+   /*
+    * Saving certificate file into buffer
+    */
+   if (wolfSSL_PEM_write_bio_X509(bio, cert) != WOLFSSL_SUCCESS)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR, "wolfSSL_PEM_write_bio_X509() failed");
+      ret = -1;
+      goto exit;
+   }
+
+   len = wolfSSL_BIO_get_mem_data(bio, &bio_mem_data);
+   last->file_buf = malloc((size_t)len + 1);
+   if (last->file_buf == NULL)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to allocate %lu bytes to store the X509 PEM certificate",
+         len + 1);
+      ret = -1;
+      goto exit;
+   }
+
+   strncpy(last->file_buf, bio_mem_data, (size_t)len);
+   last->file_buf[len] = '\0';
+   wolfSSL_BIO_free(bio);
+   bio = wolfSSL_BIO_new(wolfSSL_BIO_s_mem());
+   if (!bio)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR, "wolfSSL_BIO_new() failed");
+      ret = -1;
+      goto exit;
+   }
+
+   /*
+    * Saving certificate information into buffer
+    */
+   l = wolfSSL_X509_get_version(cert);
+   if (l >= 0 && l <= 2)
+   {
+      if (wolfSSL_BIO_printf(bio, "cert. version     : %ld\n", l + 1) <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "wolfSSL_BIO_printf() for version failed");
+         ret = -1;
+         goto exit;
+      }
+   }
+   else
+   {
+      if (wolfSSL_BIO_printf(bio, "cert. version     : Unknown (%ld)\n", l) <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "wolfSSL_BIO_printf() for version failed");
+         ret = -1;
+         goto exit;
+      }
+   }
+
+   if (wolfSSL_BIO_puts(bio, "serial number     : ") <= 0)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_BIO_puts() for serial number failed");
+      ret = -1;
+      goto exit;
+   }
+   if (wolfSSL_X509_get_serial_number(cert, serial_number, &serial_number_size)
+      != WOLFSSL_SUCCESS)
+   {
+      log_error(LOG_LEVEL_ERROR, "wolfSSL_X509_get_serial_number() failed");
+      ret = -1;
+      goto exit;
+   }
+
+   if (serial_number_size <= (int)sizeof(char))
+   {
+      if (wolfSSL_BIO_printf(bio, "%lu (0x%lx)\n", serial_number[0],
+            serial_number[0]) <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "wolfSSL_BIO_printf() for serial number as single byte failed");
+         ret = -1;
+         goto exit;
+      }
+   }
+   else
+   {
+      int i;
+      for (i = 0; i < serial_number_size; i++)
+      {
+         if (wolfSSL_BIO_printf(bio, "%02x%c", serial_number[i],
+               ((i + 1 == serial_number_size) ? '\n' : ':')) <= 0)
+         {
+            log_ssl_errors(LOG_LEVEL_ERROR,
+               "wolfSSL_BIO_printf() for serial number bytes failed");
+            ret = -1;
+            goto exit;
+         }
+      }
+   }
+
+   if (wolfSSL_BIO_puts(bio, "issuer name       : ") <= 0)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR, "wolfSSL_BIO_puts() for issuer failed");
+      ret = -1;
+      goto exit;
+   }
+   issuer_name = wolfSSL_X509_get_issuer_name(cert);
+   if (wolfSSL_X509_NAME_get_sz(issuer_name) <= 0)
+   {
+      if (wolfSSL_BIO_puts(bio, "none") <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "wolfSSL_BIO_puts() for issuer name failed");
+         ret = -1;
+         goto exit;
+      }
+   }
+   else if (wolfSSL_X509_NAME_print_ex(bio, issuer_name, 0, 0) < 0)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_X509_NAME_print_ex() for issuer failed");
+      ret = -1;
+      goto exit;
+   }
+
+   if (wolfSSL_BIO_puts(bio, "\nsubject name      : ") <= 0)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_BIO_puts() for subject name failed");
+      ret = -1;
+      goto exit;
+   }
+   subject_name = wolfSSL_X509_get_subject_name(cert);
+   if (wolfSSL_X509_NAME_get_sz(subject_name) <= 0)
+   {
+      if (wolfSSL_BIO_puts(bio, "none") <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "wolfSSL_BIO_puts() for subject name failed");
+         ret = -1;
+         goto exit;
+      }
+   }
+   else if (wolfSSL_X509_NAME_print_ex(bio, subject_name, 0, 0) < 0)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_X509_NAME_print_ex() for subject name failed");
+      ret = -1;
+      goto exit;
+   }
+
+   if (wolfSSL_BIO_puts(bio, "\nissued  on        : ") <= 0)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_BIO_puts() for issued on failed");
+      ret = -1;
+      goto exit;
+   }
+   if (!wolfSSL_ASN1_TIME_print(bio, wolfSSL_X509_get_notBefore(cert)))
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_ASN1_TIME_print() for issued on failed");
+      ret = -1;
+      goto exit;
+   }
+
+   if (wolfSSL_BIO_puts(bio, "\nexpires on        : ") <= 0)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_BIO_puts() for expires on failed");
+      ret = -1;
+      goto exit;
+   }
+   if (!wolfSSL_ASN1_TIME_print(bio, wolfSSL_X509_get_notAfter(cert)))
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_ASN1_TIME_print() for expires on failed");
+      ret = -1;
+      goto exit;
+   }
+
+   /* XXX: Show signature algorithm */
+
+   pkey = wolfSSL_X509_get_pubkey(cert);
+   if (!pkey)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR, "wolfSSL_X509_get_pubkey() failed");
+      ret = -1;
+      goto exit;
+   }
+   ret = wolfSSL_BIO_printf(bio, "\n%-18s: %d bits",
+      get_public_key_size_string(wolfSSL_EVP_PKEY_base_id(pkey)),
+      wolfSSL_EVP_PKEY_bits(pkey));
+   if (ret <= 0)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_BIO_printf() for key size failed");
+      ret = -1;
+      goto exit;
+   }
+
+   /*
+    * XXX: Show cert usage, etc.
+    */
+   loc = wolfSSL_X509_get_ext_by_NID(cert, NID_basic_constraints, -1);
+   if (loc != -1)
+   {
+      WOLFSSL_X509_EXTENSION *ex = wolfSSL_X509_get_ext(cert, loc);
+      if (BIO_puts(bio, "\nbasic constraints : ") <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "BIO_printf() for basic constraints failed");
+         ret = -1;
+         goto exit;
+      }
+      if (!wolfSSL_X509V3_EXT_print(bio, ex, 0, 0))
+      {
+         if (!wolfSSL_ASN1_STRING_print_ex(bio,
+               wolfSSL_X509_EXTENSION_get_data(ex),
+               ASN1_STRFLGS_RFC2253))
+         {
+            log_ssl_errors(LOG_LEVEL_ERROR,
+               "wolfSSL_ASN1_STRING_print_ex() for basic constraints failed");
+            ret = -1;
+            goto exit;
+         }
+      }
+   }
+
+   while ((subject_alternative_name = wolfSSL_X509_get_next_altname(cert))
+      != NULL)
+   {
+      if (san_prefix_printed == 0)
+      {
+         ret = wolfSSL_BIO_printf(bio, "\nsubject alt name  : ");
+         san_prefix_printed = 1;
+      }
+      if (ret > 0)
+      {
+         ret = wolfSSL_BIO_printf(bio, "%s ", subject_alternative_name);
+      }
+      if (ret <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "wolfSSL_BIO_printf() for Subject Alternative Name failed");
+         ret = -1;
+         goto exit;
+      }
+   }
+
+#if 0
+   /*
+    * This code compiles but does not work because wolfSSL
+    * sets NID_netscape_cert_type to NID_undef.
+    */
+   loc = wolfSSL_X509_get_ext_by_NID(cert, NID_netscape_cert_type, -1);
+   if (loc != -1)
+   {
+      WOLFSSL_X509_EXTENSION *ex = wolfSSL_X509_get_ext(cert, loc);
+      if (wolfSSL_BIO_puts(bio, "\ncert. type        : ") <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "wolfSSL_BIO_printf() for cert type failed");
+         ret = -1;
+         goto exit;
+      }
+      if (!wolfSSL_X509V3_EXT_print(bio, ex, 0, 0))
+      {
+         if (!wolfSSL_ASN1_STRING_print_ex(bio,
+               wolfSSL_X509_EXTENSION_get_data(ex),
+               ASN1_STRFLGS_RFC2253))
+         {
+            log_ssl_errors(LOG_LEVEL_ERROR,
+               "wolfSSL_ASN1_STRING_print_ex() for cert type failed");
+            ret = -1;
+            goto exit;
+         }
+      }
+   }
+#endif
+
+#if 0
+   /*
+    * This code compiles but does not work. wolfSSL_OBJ_obj2nid()
+    * triggers a 'X509V3_EXT_print not yet implemented for ext type' message.
+     */
+   loc = wolfSSL_X509_get_ext_by_NID(cert, NID_key_usage, -1);
+   if (loc != -1)
+   {
+      WOLFSSL_X509_EXTENSION *extension = wolfSSL_X509_get_ext(cert, loc);
+      if (BIO_puts(bio, "\nkey usage         : ") <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "wolfSSL_BIO_printf() for key usage failed");
+         ret = -1;
+         goto exit;
+      }
+      if (!wolfSSL_X509V3_EXT_print(bio, extension, 0, 0))
+      {
+         if (!wolfSSL_ASN1_STRING_print_ex(bio,
+               wolfSSL_X509_EXTENSION_get_data(extension),
+               ASN1_STRFLGS_RFC2253))
+         {
+            log_ssl_errors(LOG_LEVEL_ERROR,
+               "wolfSSL_ASN1_STRING_print_ex() for key usage failed");
+            ret = -1;
+            goto exit;
+         }
+      }
+   }
+#endif
+
+#if 0
+   /*
+    * This compiles but doesn't work. wolfSSL_X509_ext_isSet_by_NID()
+    * complains about "NID not in table".
+    */
+   loc = wolfSSL_X509_get_ext_by_NID(cert, NID_ext_key_usage, -1);
+   if (loc != -1) {
+      WOLFSSL_X509_EXTENSION *ex = wolfSSL_X509_get_ext(cert, loc);
+      if (wolfSSL_BIO_puts(bio, "\next key usage     : ") <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "wolfSSL_BIO_printf() for ext key usage failed");
+         ret = -1;
+         goto exit;
+      }
+      if (!wolfSSL_X509V3_EXT_print(bio, ex, 0, 0))
+      {
+         if (!wolfSSL_ASN1_STRING_print_ex(bio,
+               wolfSSL_X509_EXTENSION_get_data(ex),
+               ASN1_STRFLGS_RFC2253))
+         {
+            log_ssl_errors(LOG_LEVEL_ERROR,
+               "wolfSSL_ASN1_STRING_print_ex() for ext key usage failed");
+            ret = -1;
+            goto exit;
+         }
+      }
+   }
+#endif
+
+#if 0
+   /*
+    * This compiles but doesn't work. wolfSSL_X509_ext_isSet_by_NID()
+    * complains about "NID not in table". XXX: again?
+    */
+   loc = wolfSSL_X509_get_ext_by_NID(cert, NID_certificate_policies, -1);
+   if (loc != -1)
+   {
+      WOLFSSL_X509_EXTENSION *ex = wolfSSL_X509_get_ext(cert, loc);
+      if (wolfSSL_BIO_puts(bio, "\ncertificate policies : ") <= 0)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "wolfSSL_BIO_printf() for certificate policies failed");
+         ret = -1;
+         goto exit;
+      }
+      if (!wolfSSL_X509V3_EXT_print(bio, ex, 0, 0))
+      {
+         if (!wolfSSL_ASN1_STRING_print_ex(bio,
+               wolfSSL_X509_EXTENSION_get_data(ex),
+               ASN1_STRFLGS_RFC2253))
+         {
+            log_ssl_errors(LOG_LEVEL_ERROR,
+               "wolfSSL_ASN1_STRING_print_ex() for certificate policies failed");
+            ret = -1;
+            goto exit;
+         }
+      }
+   }
+#endif
+
+   /* make valgrind happy */
+   static const char zero = 0;
+   wolfSSL_BIO_write(bio, &zero, 1);
+
+   len = wolfSSL_BIO_get_mem_data(bio, &bio_mem_data);
+   if (len <= 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "BIO_get_mem_data() returned %ld "
+         "while gathering certificate information", len);
+      ret = -1;
+      goto exit;
+   }
+   encoded_text = html_encode(bio_mem_data);
+   if (encoded_text == NULL)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to HTML-encode the certificate information");
+      ret = -1;
+      goto exit;
+   }
+
+   strlcpy(last->info_buf, encoded_text, sizeof(last->info_buf));
+   freez(encoded_text);
+   ret = 0;
+
+exit:
+   if (bio)
+   {
+      wolfSSL_BIO_free(bio);
+   }
+   if (pkey)
+   {
+      wolfSSL_EVP_PKEY_free(pkey);
+   }
+   return ret;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  host_to_hash
+ *
+ * Description :  Creates MD5 hash from host name. Host name is loaded
+ *                from structure csp and saved again into it.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     : -1 => Error while creating hash
+ *                0 => Hash created successfully
+ *
+ *********************************************************************/
+static int host_to_hash(struct client_state *csp)
+{
+   Md5 md5;
+   int ret;
+   size_t i;
+
+   ret = wc_InitMd5(&md5);
+   if (ret != 0)
+   {
+      return -1;
+   }
+
+   ret = wc_Md5Update(&md5, (const byte *)csp->http->host,
+      (word32)strlen(csp->http->host));
+   if (ret != 0)
+   {
+      return -1;
+   }
+
+   ret = wc_Md5Final(&md5, csp->http->hash_of_host);
+   if (ret != 0)
+   {
+      return -1;
+   }
+
+   wc_Md5Free(&md5);
+
+   /* Converting hash into string with hex */
+   for (i = 0; i < 16; i++)
+   {
+      ret = snprintf((char *)csp->http->hash_of_host_hex + 2 * i,
+         sizeof(csp->http->hash_of_host_hex) - 2 * i,
+         "%02x", csp->http->hash_of_host[i]);
+      if (ret < 0)
+      {
+         log_error(LOG_LEVEL_ERROR, "sprintf() failed. Return value: %d", ret);
+         return -1;
+      }
+   }
+
+   return 0;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  create_client_ssl_connection
+ *
+ * Description :  Creates a TLS-secured connection with the client.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  0 on success, negative value if connection wasn't created
+ *                successfully.
+ *
+ *********************************************************************/
+extern int create_client_ssl_connection(struct client_state *csp)
+{
+   struct ssl_attr *ssl_attr = &csp->ssl_client_attr;
+   /* Paths to certificates file and key file */
+   char *key_file  = NULL;
+   char *cert_file = NULL;
+   int ret = 0;
+   WOLFSSL *ssl;
+
+   /* Should probably be called from somewhere else. */
+   wolfssl_init();
+
+   /*
+    * Preparing hash of host for creating certificates
+    */
+   ret = host_to_hash(csp);
+   if (ret != 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "Generating hash of host failed: %d", ret);
+      ret = -1;
+      goto exit;
+   }
+
+   /*
+    * Preparing paths to certificates files and key file
+    */
+   cert_file = make_certs_path(csp->config->certificate_directory,
+      (const char *)csp->http->hash_of_host_hex, CERT_FILE_TYPE);
+   key_file  = make_certs_path(csp->config->certificate_directory,
+      (const char *)csp->http->hash_of_host_hex, KEY_FILE_TYPE);
+
+   if (cert_file == NULL || key_file == NULL)
+   {
+      ret = -1;
+      goto exit;
+   }
+
+   /* Do we need to generate a new host certificate and key? */
+   if (!file_exists(cert_file) || !file_exists(key_file) ||
+       ssl_certificate_is_invalid(cert_file))
+   {
+      /*
+       * Yes we do. Lock mutex to prevent certificate and
+       * key inconsistencies.
+       */
+      privoxy_mutex_lock(&certificate_mutex);
+      ret = generate_host_certificate(csp, cert_file, key_file);
+      privoxy_mutex_unlock(&certificate_mutex);
+      if (ret < 0)
+      {
+         /*
+          * No need to log something, generate_host_certificate()
+          * took care of it.
+          */
+         ret = -1;
+         goto exit;
+      }
+   }
+   ssl_attr->wolfssl_attr.ctx = wolfSSL_CTX_new(wolfSSLv23_method());
+   if (ssl_attr->wolfssl_attr.ctx == NULL)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR, "Unable to create TLS context");
+      ret = -1;
+      goto exit;
+   }
+
+   /* Set the key and cert */
+   if (wolfSSL_CTX_use_certificate_file(ssl_attr->wolfssl_attr.ctx,
+         cert_file, SSL_FILETYPE_PEM) != WOLFSSL_SUCCESS)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "Loading host certificate %s failed", cert_file);
+      ret = -1;
+      goto exit;
+   }
+
+   if (wolfSSL_CTX_use_PrivateKey_file(ssl_attr->wolfssl_attr.ctx,
+         key_file, SSL_FILETYPE_PEM) != WOLFSSL_SUCCESS)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "Loading host certificate private key %s failed", key_file);
+      ret = -1;
+      goto exit;
+   }
+
+   wolfSSL_CTX_set_options(ssl_attr->wolfssl_attr.ctx, WOLFSSL_OP_NO_SSLv3);
+
+   ssl = ssl_attr->wolfssl_attr.ssl = wolfSSL_new(ssl_attr->wolfssl_attr.ctx);
+
+   if (wolfSSL_set_fd(ssl, csp->cfd) != WOLFSSL_SUCCESS)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_set_fd() failed to set the client socket");
+      ret = -1;
+      goto exit;
+   }
+
+   if (csp->config->cipher_list != NULL)
+   {
+      if (!wolfSSL_set_cipher_list(ssl, csp->config->cipher_list))
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "Setting the cipher list '%s' for the client connection failed",
+            csp->config->cipher_list);
+         ret = -1;
+         goto exit;
+      }
+   }
+
+   /*
+    *  Handshake with client
+    */
+   log_error(LOG_LEVEL_CONNECT,
+      "Performing the TLS/SSL handshake with client. Hash of host: %s",
+      csp->http->hash_of_host_hex);
+
+   ret = wolfSSL_accept(ssl);
+   if (ret == WOLFSSL_SUCCESS)
+   {
+      log_error(LOG_LEVEL_CONNECT,
+         "Client successfully connected over %s (%s).",
+         wolfSSL_get_version(ssl), wolfSSL_get_cipher_name(ssl));
+      csp->ssl_with_client_is_opened = 1;
+      ret = 0;
+   }
+   else
+   {
+      char buffer[80];
+      int error = wolfSSL_get_error(ssl, ret);
+      log_error(LOG_LEVEL_ERROR,
+         "The TLS handshake with the client failed. error = %d, %s",
+         error, wolfSSL_ERR_error_string((unsigned long)error, buffer));
+      ret = -1;
+   }
+
+exit:
+   /*
+    * Freeing allocated paths to files
+    */
+   freez(cert_file);
+   freez(key_file);
+
+   /* Freeing structures if connection wasn't created successfully */
+   if (ret < 0)
+   {
+      free_client_ssl_structures(csp);
+   }
+   return ret;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  shutdown_connection
+ *
+ * Description :  Shuts down a TLS connection if the socket is still
+ *                alive.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+static void shutdown_connection(WOLFSSL *ssl, const char *type)
+{
+   int shutdown_attempts = 0;
+   int ret;
+   int fd;
+   enum { MAX_SHUTDOWN_ATTEMPTS = 2 };
+
+   fd = wolfSSL_get_fd(ssl);
+
+   do
+   {
+      if (!socket_is_still_alive(fd))
+      {
+         log_error(LOG_LEVEL_CONNECT, "Not shutting down %s connection "
+            "on socket %d. The socket is no longer alive.", type, fd);
+         return;
+      }
+      ret = wolfSSL_shutdown(ssl);
+      if (WOLFSSL_SUCCESS != ret)
+      {
+         shutdown_attempts++;
+         log_error(LOG_LEVEL_CONNECT, "Failed to shutdown %s connection "
+            "on socket %d. Attempts so far: %d, ret: %d", type, fd,
+            shutdown_attempts, ret);
+      }
+   } while (ret == WOLFSSL_SHUTDOWN_NOT_DONE &&
+      shutdown_attempts < MAX_SHUTDOWN_ATTEMPTS);
+   if (WOLFSSL_SUCCESS != ret)
+   {
+      char buffer[80];
+      int error = wolfSSL_get_error(ssl, ret);
+      log_error(LOG_LEVEL_ERROR, "Failed to shutdown %s connection "
+         "on socket %d after %d attempts. ret: %d, error: %d, %s",
+         type, fd, shutdown_attempts, ret, error,
+         wolfSSL_ERR_error_string((unsigned long)error, buffer));
+   }
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  close_client_ssl_connection
+ *
+ * Description :  Closes TLS connection with client. This function
+ *                checks if this connection is already created.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+extern void close_client_ssl_connection(struct client_state *csp)
+{
+   struct ssl_attr *ssl_attr = &csp->ssl_client_attr;
+
+   if (csp->ssl_with_client_is_opened == 0)
+   {
+      return;
+   }
+
+   /*
+    * Notify the peer that the connection is being closed.
+    */
+   shutdown_connection(ssl_attr->wolfssl_attr.ssl, "client");
+
+   free_client_ssl_structures(csp);
+   csp->ssl_with_client_is_opened = 0;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  free_client_ssl_structures
+ *
+ * Description :  Frees structures used for SSL communication with
+ *                client.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+static void free_client_ssl_structures(struct client_state *csp)
+{
+   struct ssl_attr *ssl_attr = &csp->ssl_client_attr;
+
+   if (ssl_attr->wolfssl_attr.ssl)
+   {
+      wolfSSL_free(ssl_attr->wolfssl_attr.ssl);
+   }
+   if (ssl_attr->wolfssl_attr.ctx)
+   {
+      wolfSSL_CTX_free(ssl_attr->wolfssl_attr.ctx);
+   }
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  close_server_ssl_connection
+ *
+ * Description :  Closes TLS connection with server. This function
+ *                checks if this connection is already opened.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+extern void close_server_ssl_connection(struct client_state *csp)
+{
+   struct ssl_attr *ssl_attr = &csp->ssl_server_attr;
+
+   if (csp->ssl_with_server_is_opened == 0)
+   {
+      return;
+   }
+
+   /*
+   * Notify the peer that the connection is being closed.
+   */
+   shutdown_connection(ssl_attr->wolfssl_attr.ssl, "server");
+
+   free_server_ssl_structures(csp);
+   csp->ssl_with_server_is_opened = 0;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  create_server_ssl_connection
+ *
+ * Description :  Creates TLS-secured connection with the server.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  0 on success, negative value if connection wasn't created
+ *                successfully.
+ *
+ *********************************************************************/
+extern int create_server_ssl_connection(struct client_state *csp)
+{
+   wolfssl_connection_attr *ssl_attrs = &csp->ssl_server_attr.wolfssl_attr;
+   int ret = 0;
+   WOLFSSL *ssl;
+   int connect_ret = 0;
+
+   csp->server_cert_verification_result = SSL_CERT_NOT_VERIFIED;
+   csp->server_certs_chain.next = NULL;
+
+   ssl_attrs->ctx = wolfSSL_CTX_new(wolfSSLv23_method());
+   if (ssl_attrs->ctx == NULL)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR, "TLS context creation failed");
+      ret = -1;
+      goto exit;
+   }
+
+   if (csp->dont_verify_certificate)
+   {
+      wolfSSL_CTX_set_verify(ssl_attrs->ctx, WOLFSSL_VERIFY_NONE, NULL);
+   }
+   else if (wolfSSL_CTX_load_verify_locations(ssl_attrs->ctx,
+      csp->config->trusted_cas_file, NULL) != WOLFSSL_SUCCESS)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR, "Loading trusted CAs file %s failed",
+         csp->config->trusted_cas_file);
+      ret = -1;
+      goto exit;
+   }
+
+   wolfSSL_CTX_set_options(ssl_attrs->ctx, WOLFSSL_OP_NO_SSLv3);
+
+   ssl = ssl_attrs->ssl = wolfSSL_new(ssl_attrs->ctx);
+
+   if (wolfSSL_set_fd(ssl, csp->server_connection.sfd) != WOLFSSL_SUCCESS)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "wolfSSL_set_fd() failed to set the server socket");
+      ret = -1;
+      goto exit;
+   }
+
+   if (csp->config->cipher_list != NULL)
+   {
+      if (wolfSSL_set_cipher_list(ssl, csp->config->cipher_list) != WOLFSSL_SUCCESS)
+      {
+         log_ssl_errors(LOG_LEVEL_ERROR,
+            "Setting the cipher list '%s' for the server connection failed",
+            csp->config->cipher_list);
+         ret = -1;
+         goto exit;
+      }
+   }
+
+   ret = wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME,
+      csp->http->host, (unsigned short)strlen(csp->http->host));
+   if (ret != WOLFSSL_SUCCESS)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR, "Failed to set use of SNI");
+      ret = -1;
+      goto exit;
+   }
+
+   ret = wolfSSL_check_domain_name(ssl, csp->http->host);
+   if (ret != WOLFSSL_SUCCESS)
+   {
+      char buffer[80];
+      int error = wolfSSL_get_error(ssl, ret);
+      log_error(LOG_LEVEL_FATAL,
+         "Failed to set check domain name. error = %d, %s",
+         error, wolfSSL_ERR_error_string((unsigned long)error, buffer));
+      ret = -1;
+      goto exit;
+   }
+
+#ifdef HAVE_SECURE_RENEGOTIATION
+#warning wolfssl has been compiled with HAVE_SECURE_RENEGOTIATION while you probably want HAVE_RENEGOTIATION_INDICATION
+   if(wolfSSL_UseSecureRenegotiation(ssl) != WOLFSSL_SUCCESS)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "Failed to enable 'Secure' Renegotiation. Continuing anyway.");
+   }
+#endif
+#ifndef HAVE_RENEGOTIATION_INDICATION
+#warning Looks like wolfssl has been compiled without HAVE_RENEGOTIATION_INDICATION
+#endif
+
+   log_error(LOG_LEVEL_CONNECT,
+      "Performing the TLS/SSL handshake with the server");
+
+   /* wolfSSL_Debugging_ON(); */
+   connect_ret = wolfSSL_connect(ssl);
+   /* wolfSSL_Debugging_OFF(); */
+
+   /*
+   wolfSSL_Debugging_ON();
+   */
+   if (!csp->dont_verify_certificate)
+   {
+      long verify_result = wolfSSL_get_error(ssl, connect_ret);
+
+      if (verify_result == X509_V_OK)
+      {
+         ret = 0;
+         csp->server_cert_verification_result = SSL_CERT_VALID;
+      }
+      else
+      {
+         WOLF_STACK_OF(WOLFSSL_X509) *chain;
+
+         csp->server_cert_verification_result = verify_result;
+         log_error(LOG_LEVEL_ERROR,
+            "X509 certificate verification for %s failed with error %ld: %s",
+            csp->http->hostport, verify_result,
+            wolfSSL_X509_verify_cert_error_string(verify_result));
+
+         chain = wolfSSL_get_peer_cert_chain(ssl);
+         if (chain != NULL)
+         {
+            int i;
+            for (i = 0; i < wolfSSL_sk_X509_num(chain); i++)
+            {
+               if (ssl_store_cert(csp, wolfSSL_sk_X509_value(chain, i)) != 0)
+               {
+                  log_error(LOG_LEVEL_ERROR,
+                     "ssl_store_cert() failed for cert %d", i);
+                  /*
+                   * ssl_send_certificate_error() wil not be able to show
+                   * the certificate but the user will stil get the error
+                   * description.
+                   */
+               }
+            }
+         }
+
+         ret = -1;
+         goto exit;
+      }
+   }
+   /*
+   wolfSSL_Debugging_OFF();
+   */
+   if (connect_ret == WOLFSSL_SUCCESS)
+   {
+      log_error(LOG_LEVEL_CONNECT,
+         "Server successfully connected over %s (%s).",
+         wolfSSL_get_version(ssl), wolfSSL_get_cipher_name(ssl));
+      csp->ssl_with_server_is_opened = 1;
+      ret = 0;
+   }
+   else
+   {
+      char buffer[80];
+      int error = wolfSSL_get_error(ssl, ret);
+      log_error(LOG_LEVEL_ERROR,
+         "The TLS handshake with the server %s failed. error = %d, %s",
+         csp->http->hostport,
+         error, wolfSSL_ERR_error_string((unsigned long)error, buffer));
+      ret = -1;
+   }
+
+exit:
+   /* Freeing structures if connection wasn't created successfully */
+   if (ret < 0)
+   {
+      free_server_ssl_structures(csp);
+   }
+
+   return ret;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  free_server_ssl_structures
+ *
+ * Description :  Frees structures used for SSL communication with server
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+static void free_server_ssl_structures(struct client_state *csp)
+{
+   struct ssl_attr *ssl_attr = &csp->ssl_server_attr;
+
+   if (ssl_attr->wolfssl_attr.ssl)
+   {
+      wolfSSL_free(ssl_attr->wolfssl_attr.ssl);
+   }
+   if (ssl_attr->wolfssl_attr.ctx)
+   {
+      wolfSSL_CTX_free(ssl_attr->wolfssl_attr.ctx);
+   }
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  log_ssl_errors
+ *
+ * Description :  Log SSL errors
+ *
+ * Parameters  :
+ *          1  :  debuglevel = Debug level
+ *          2  :  desc = Error description
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+static void log_ssl_errors(int debuglevel, const char* fmt, ...)
+{
+   unsigned long err_code;
+   char prefix[ERROR_BUF_SIZE];
+   va_list args;
+   va_start(args, fmt);
+   vsnprintf(prefix, sizeof(prefix), fmt, args);
+   int reported = 0;
+
+   while ((err_code = wolfSSL_ERR_get_error()))
+   {
+      char err_buf[ERROR_BUF_SIZE];
+      reported = 1;
+      wolfSSL_ERR_error_string_n(err_code, err_buf, sizeof(err_buf));
+      log_error(debuglevel, "%s: %s", prefix, err_buf);
+   }
+   va_end(args);
+   /*
+    * In case if called by mistake and there were
+    * no TLS errors let's report it to the log.
+    */
+   if (!reported)
+   {
+      log_error(debuglevel, "%s: no TLS errors detected", prefix);
+   }
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  ssl_base64_encode
+ *
+ * Description :  Encode a buffer into base64 format.
+ *
+ * Parameters  :
+ *          1  :  dst = Destination buffer
+ *          2  :  dlen = Destination buffer length
+ *          3  :  olen = Number of bytes written
+ *          4  :  src = Source buffer
+ *          5  :  slen = Amount of data to be encoded
+ *
+ * Returns     :  0 on success, error code othervise
+ *
+ *********************************************************************/
+extern int ssl_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
+                             const unsigned char *src, size_t slen)
+{
+   word32 output_length;
+   int ret;
+
+   *olen = 4 * ((slen/3) + ((slen%3) ? 1 : 0)) + 1;
+   if (*olen > dlen)
+   {
+      return ENOBUFS;
+   }
+
+   output_length = (word32)dlen;
+   ret = Base64_Encode_NoNl(src, (word32)slen, dst, &output_length);
+   if (ret != 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "base64 encoding failed with %d", ret);
+      return ret;
+   }
+   *olen = output_length;
+
+   return 0;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  close_file_stream
+ *
+ * Description :  Close file stream, report error on close error
+ *
+ * Parameters  :
+ *          1  :  f = file stream to close
+ *          2  :  path = path for error report
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+static void close_file_stream(FILE *f, const char *path)
+{
+   if (fclose(f) != 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Error closing file %s: %s", path, strerror(errno));
+   }
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  write_certificate
+ *
+ * Description :  Writes a PEM-encoded certificate to a file.
+ *
+ * Parameters  :
+ *          1  :  certificate_path = Path to the file to create
+ *          2  :  certificate = PEM-encoded certificate to write.
+ *
+ * Returns     :  NULL => Error. Otherwise a key;
+ *
+ *********************************************************************/
+static int write_certificate(const char *certificate_path, const char *certificate)
+{
+   FILE *fp;
+
+   assert(certificate_path != NULL);
+   assert(certificate != NULL);
+
+   fp = fopen(certificate_path, "wb");
+   if (NULL == fp)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to open %s to write the certificate: %E",
+         certificate_path);
+      return -1;
+   }
+   if (fputs(certificate, fp) < 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "Failed to write certificate to %s: %E",
+         certificate_path);
+      fclose(fp);
+      return -1;
+   }
+   fclose(fp);
+
+   return 0;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  generate_rsa_key
+ *
+ * Description : Generates a new RSA key and saves it in a file.
+ *
+ * Parameters  :
+ *          1  :  rsa_key_path = Path to the key that should be written.
+ *
+ * Returns     :  -1 => Error while generating private key
+ *                 0 => Success.
+ *
+ *********************************************************************/
+static int generate_rsa_key(const char *rsa_key_path)
+{
+   RsaKey rsa_key;
+   byte rsa_key_der[4096];
+   int ret;
+   byte key_pem[4096];
+   int der_key_size;
+   int pem_key_size;
+   FILE *f = NULL;
+
+   assert(file_exists(rsa_key_path) != 1);
+
+   wc_InitRsaKey(&rsa_key, NULL);
+
+   log_error(LOG_LEVEL_CONNECT, "Making RSA key %s ...", rsa_key_path);
+   ret = wc_MakeRsaKey(&rsa_key, RSA_KEYSIZE, RSA_KEY_PUBLIC_EXPONENT,
+      &wolfssl_rng);
+   if (ret != 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "RSA key generation failed");
+      ret = -1;
+      goto exit;
+   }
+   log_error(LOG_LEVEL_CONNECT, "Done making RSA key %s", rsa_key_path);
+
+   der_key_size = wc_RsaKeyToDer(&rsa_key, rsa_key_der, sizeof(rsa_key_der));
+   wc_FreeRsaKey(&rsa_key);
+   if (der_key_size < 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "RSA key conversion to DER format failed");
+      ret = -1;
+      goto exit;
+   }
+   pem_key_size = wc_DerToPem(rsa_key_der, (word32)der_key_size,
+      key_pem, sizeof(key_pem), PRIVATEKEY_TYPE);
+   if (pem_key_size < 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "RSA key conversion to PEM format failed");
+      ret = -1;
+      goto exit;
+   }
+
+   /*
+    * Saving key into file
+    */
+   if ((f = fopen(rsa_key_path, "wb")) == NULL)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Opening file %s to save private key failed: %E",
+         rsa_key_path);
+      ret = -1;
+      goto exit;
+   }
+
+   if (fwrite(key_pem, 1, (size_t)pem_key_size, f) != pem_key_size)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Writing private key into file %s failed",
+         rsa_key_path);
+      close_file_stream(f, rsa_key_path);
+      ret = -1;
+      goto exit;
+   }
+
+   close_file_stream(f, rsa_key_path);
+
+exit:
+
+   return ret;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  ssl_certificate_load
+ *
+ * Description :  Loads certificate from file.
+ *
+ * Parameters  :
+ *          1  :  cert_path = The certificate path to load
+ *
+ * Returns     :   NULL => error loading certificate,
+ *                   pointer to certificate instance otherwise
+ *
+ *********************************************************************/
+static X509 *ssl_certificate_load(const char *cert_path)
+{
+   X509 *cert = NULL;
+   FILE *cert_f = NULL;
+
+   if (!(cert_f = fopen(cert_path, "r")))
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Error opening certificate file %s: %s", cert_path, strerror(errno));
+      return NULL;
+   }
+
+   if (!(cert = PEM_read_X509(cert_f, NULL, NULL, NULL)))
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "Error reading certificate file %s", cert_path);
+   }
+
+   close_file_stream(cert_f, cert_path);
+   return cert;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  ssl_certificate_is_invalid
+ *
+ * Description :  Checks whether or not a certificate is valid.
+ *                Currently only checks that the certificate can be
+ *                parsed and that the "valid to" date is in the future.
+ *
+ * Parameters  :
+ *          1  :  cert_file = The certificate to check
+ *
+ * Returns     :   0 => The certificate is valid.
+ *                 1 => The certificate is invalid
+ *
+ *********************************************************************/
+static int ssl_certificate_is_invalid(const char *cert_file)
+{
+   int ret;
+
+   X509 *cert = NULL;
+
+   if (!(cert = ssl_certificate_load(cert_file)))
+   {
+      return 1;
+   }
+
+   ret = wolfSSL_X509_cmp_current_time(wolfSSL_X509_get_notAfter(cert));
+   if (ret == 0)
+   {
+      log_ssl_errors(LOG_LEVEL_ERROR,
+         "Error checking certificate %s validity", cert_file);
+      ret = -1;
+   }
+
+   wolfSSL_X509_free(cert);
+
+   return ret == -1 ? 1 : 0;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  load_rsa_key
+ *
+ * Description :  Load a PEM-encoded RSA file into memory.
+ *
+ * Parameters  :
+ *          1  :  rsa_key_path = Path to the file that holds the key.
+ *          2  :  password = Password to unlock the key. NULL if no
+ *                           password is required.
+ *          3  :  rsa_key = Initialized RSA key storage.
+ *
+ * Returns     :   0 => Error while creating the key.
+ *                 1 => It worked
+ *
+ *********************************************************************/
+static int load_rsa_key(const char *rsa_key_path, const char *password, RsaKey *rsa_key)
+{
+   FILE *fp;
+   size_t length;
+   long ret;
+   unsigned char *key_pem;
+   DerBuffer *der_buffer;
+   word32 der_index = 0;
+   DerBuffer decrypted_der_buffer;
+   unsigned char der_data[4096];
+
+   fp = fopen(rsa_key_path, "rb");
+   if (NULL == fp)
+   {
+      log_error(LOG_LEVEL_ERROR, "Failed to open %s: %E", rsa_key_path);
+      return 0;
+   }
+
+   /* Get file length */
+   if (fseek(fp, 0, SEEK_END))
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Unexpected error while fseek()ing to the end of %s: %E",
+         rsa_key_path);
+      fclose(fp);
+      return 0;
+   }
+   ret = ftell(fp);
+   if (-1 == ret)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Unexpected ftell() error while loading %s: %E",
+         rsa_key_path);
+      fclose(fp);
+      return 0;
+   }
+   length = (size_t)ret;
+
+   /* Go back to the beginning. */
+   if (fseek(fp, 0, SEEK_SET))
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Unexpected error while fseek()ing to the beginning of %s: %E",
+         rsa_key_path);
+      fclose(fp);
+      return 0;
+   }
+
+   key_pem = malloc_or_die(length);
+
+   if (1 != fread(key_pem, length, 1, fp))
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Couldn't completely read file %s.", rsa_key_path);
+      fclose(fp);
+      freez(key_pem);
+      return 0;
+   }
+
+   fclose(fp);
+
+   if (password == NULL)
+   {
+      ret = wc_PemToDer(key_pem, (long)length, PRIVATEKEY_TYPE,
+         &der_buffer, NULL, NULL, NULL);
+   }
+   else
+   {
+      der_buffer = &decrypted_der_buffer;
+      der_buffer->buffer = der_data;
+      ret = wc_KeyPemToDer(key_pem, (int)length, der_buffer->buffer,
+         sizeof(der_data), password);
+      if (ret < 0)
+      {
+         log_error(LOG_LEVEL_ERROR,
+            "Failed to convert PEM key %s into DER format. Error: %ld",
+            rsa_key_path, ret);
+         freez(key_pem);
+         return 0;
+      }
+      der_buffer->length = (word32)ret;
+   }
+
+   freez(key_pem);
+
+   if (ret < 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to convert buffer into DER format for file %s. Error = %ld",
+         rsa_key_path, ret);
+      return 0;
+   }
+
+   ret = wc_RsaPrivateKeyDecode(der_buffer->buffer, &der_index, rsa_key,
+      der_buffer->length);
+   if (password == NULL)
+   {
+      freez(der_buffer);
+   }
+   if (ret < 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to decode DER buffer into RSA key structure for %s",
+         rsa_key_path);
+      return 0;
+   }
+
+   return 1;
+}
+
+#ifndef WOLFSSL_ALT_NAMES
+#error wolfSSL lacks Subject Alternative Name support
+#endif
+/*********************************************************************
+ *
+ * Function    :  set_subject_alternative_name
+ *
+ * Description :  Sets the Subject Alternative Name extension to
+ *                a cert using the awesome "API" provided by wolfSSL.
+ *
+ * Parameters  :
+ *          1  :  cert = The certificate to modify
+ *          2  :  hostname = The hostname to add
+ *
+ * Returns     :  <0 => Error.
+ *                 0 => It worked
+ *
+ *********************************************************************/
+static int set_subject_alternative_name(struct Cert *certificate, const char *hostname)
+{
+   const size_t hostname_length = strlen(hostname);
+
+   if (hostname_length >= 253)
+   {
+      /*
+       * We apparently only have a byte to represent the length
+       * of the sequence.
+       */
+      log_error(LOG_LEVEL_ERROR,
+         "Hostname '%s' is too long to set Subject Alternative Name",
+         hostname);
+      return -1;
+   }
+   certificate->altNames[0] = 0x30; /* Sequence */
+   certificate->altNames[1] = (unsigned char)hostname_length + 2;
+
+   certificate->altNames[2] = 0x82; /* DNS name */
+   certificate->altNames[3] = (unsigned char)hostname_length;
+   memcpy(&certificate->altNames[4], hostname, hostname_length);
+
+   certificate->altNamesSz = (int)hostname_length + 4;
+
+   return 0;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  generate_host_certificate
+ *
+ * Description :  Creates certificate file in presetted directory.
+ *                If certificate already exists, no other certificate
+ *                will be created. Subject of certificate is named
+ *                by csp->http->host from parameter. This function also
+ *                triggers generating of private key for new certificate.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  certificate_path = Path to the certficate to generate.
+ *          3  :  rsa_key_path = Path to the key to generate for the
+ *                               certificate.
+ *
+ * Returns     :  -1 => Error while creating certificate.
+ *                 0 => Certificate already exists.
+ *                 1 => Certificate created
+ *
+ *********************************************************************/
+static int generate_host_certificate(struct client_state *csp,
+   const char *certificate_path, const char *rsa_key_path)
+{
+   struct Cert certificate;
+   RsaKey ca_key;
+   RsaKey rsa_key;
+   int ret;
+   byte certificate_der[4096];
+   int der_certificate_length;
+   byte certificate_pem[4096];
+   int pem_certificate_length;
+
+   if (file_exists(certificate_path) == 1)
+   {
+      /* The file exists, but is it valid? */
+      if (ssl_certificate_is_invalid(certificate_path))
+      {
+         log_error(LOG_LEVEL_CONNECT,
+            "Certificate %s is no longer valid. Removing it.",
+            certificate_path);
+         if (unlink(certificate_path))
+         {
+            log_error(LOG_LEVEL_ERROR, "Failed to unlink %s: %E",
+               certificate_path);
+            return -1;
+         }
+         if (unlink(rsa_key_path))
+         {
+            log_error(LOG_LEVEL_ERROR, "Failed to unlink %s: %E",
+               rsa_key_path);
+            return -1;
+         }
+      }
+      else
+      {
+         return 0;
+      }
+   }
+   else
+   {
+      log_error(LOG_LEVEL_CONNECT, "Creating new certificate %s",
+         certificate_path);
+   }
+   if (enforce_sane_certificate_state(certificate_path, rsa_key_path))
+   {
+      return -1;
+   }
+
+   wc_InitRsaKey(&rsa_key, NULL);
+   wc_InitRsaKey(&ca_key, NULL);
+
+   if (generate_rsa_key(rsa_key_path) == -1)
+   {
+      return -1;
+   }
+
+   wc_InitCert(&certificate);
+
+   strncpy(certificate.subject.country, CERT_PARAM_COUNTRY_CODE, CTC_NAME_SIZE);
+   strncpy(certificate.subject.org, "Privoxy", CTC_NAME_SIZE);
+   strncpy(certificate.subject.unit, "Development", CTC_NAME_SIZE);
+   strncpy(certificate.subject.commonName, csp->http->host, CTC_NAME_SIZE);
+   certificate.daysValid = 90;
+   certificate.selfSigned = 0;
+   certificate.sigType = CTC_SHA256wRSA;
+   if (!host_is_ip_address(csp->http->host) &&
+       set_subject_alternative_name(&certificate, csp->http->host))
+   {
+      ret = -1;
+      goto exit;
+   }
+
+   ret = wc_SetIssuer(&certificate, csp->config->ca_cert_file);
+   if (ret < 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to set Issuer file %s", csp->config->ca_cert_file);
+      ret = -1;
+      goto exit;
+   }
+
+   if (load_rsa_key(rsa_key_path, NULL, &rsa_key) != 1)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to load RSA key %s", rsa_key_path);
+      ret = -1;
+      goto exit;
+   }
+
+   /* wolfSSL_Debugging_ON(); */
+   der_certificate_length = wc_MakeCert(&certificate, certificate_der,
+      sizeof(certificate_der), &rsa_key, NULL, &wolfssl_rng);
+   /* wolfSSL_Debugging_OFF(); */
+
+   if (der_certificate_length < 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "Failed to make certificate");
+      ret = -1;
+      goto exit;
+   }
+
+   if (load_rsa_key(csp->config->ca_key_file, csp->config->ca_password,
+      &ca_key) != 1)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to load CA key %s", csp->config->ca_key_file);
+      ret = -1;
+      goto exit;
+   }
+
+   der_certificate_length = wc_SignCert(certificate.bodySz, certificate.sigType,
+      certificate_der, sizeof(certificate_der), &ca_key, NULL, &wolfssl_rng);
+   wc_FreeRsaKey(&ca_key);
+   if (der_certificate_length < 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "Failed to sign certificate");
+      ret = -1;
+      goto exit;
+   }
+
+   pem_certificate_length = wc_DerToPem(certificate_der,
+      (word32)der_certificate_length, certificate_pem,
+      sizeof(certificate_pem), CERT_TYPE);
+   if (pem_certificate_length < 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to convert certificate from DER to PEM");
+      ret = -1;
+      goto exit;
+   }
+   certificate_pem[pem_certificate_length] = '\0';
+
+   if (write_certificate(certificate_path, (const char*)certificate_pem))
+   {
+      ret = -1;
+      goto exit;
+   }
+
+   ret = 1;
+
+exit:
+   wc_FreeRsaKey(&rsa_key);
+   wc_FreeRsaKey(&ca_key);
+
+   return 1;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  ssl_crt_verify_info
+ *
+ * Description :  Returns an informational string about the verification
+ *                status of a certificate.
+ *
+ * Parameters  :
+ *          1  :  buf = Buffer to write to
+ *          2  :  size = Maximum size of buffer
+ *          3  :  csp = client state
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+extern void ssl_crt_verify_info(char *buf, size_t size, struct client_state *csp)
+{
+   strncpy(buf, wolfSSL_X509_verify_cert_error_string(
+      csp->server_cert_verification_result), size);
+   buf[size - 1] = 0;
+}
+
+
+#ifdef FEATURE_GRACEFUL_TERMINATION
+/*********************************************************************
+ *
+ * Function    :  ssl_release
+ *
+ * Description :  Release all SSL resources
+ *
+ * Parameters  :
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+extern void ssl_release(void)
+{
+   if (wolfssl_initialized == 1)
+   {
+      wc_FreeRng(&wolfssl_rng);
+      wolfSSL_Cleanup();
+   }
+}
+#endif /* def FEATURE_GRACEFUL_TERMINATION */