/* ========================================================================== */
/*! \file
 * \brief Transport layer security
 *
 * Copyright (c) 2012-2016 by the developers. See the LICENSE file for details.
 *
 * If nothing else is specified, function return zero to indicate success
 * and a negative value to indicate an error.
 */


/* ========================================================================== */
/* Include headers */

#include "posix.h"  /* Include this first because of feature test macros */

#include "config.h"

#if CFG_USE_TLS  /* This requires data from "config.h" */
#  include <openssl/crypto.h>
#  include <openssl/asn1.h>
#  include <openssl/bio.h>
#  include <openssl/err.h>
#  include <openssl/evp.h>
#  include <openssl/objects.h>
#  include <openssl/opensslv.h>
#  include <openssl/rand.h>
#  include <openssl/rsa.h>
#  include <openssl/ssl.h>
#  include <openssl/x509.h>
#  include <openssl/x509v3.h>
#  include <openssl/x509_vfy.h>
#endif  /* CFG_USE_TLS */
#include <string.h>

#include "conf.h"      /* For CRL update configuration */
#include "digest.h"    /* For public key randomart images */
#include "encoding.h"  /* For CRL update timestamp conversion */
#include "extutils.h"  /* For CRL download */
#include "fileutils.h"
#include "main.h"
#include "tls.h"


/* ========================================================================== */
/*! \addtogroup TLS
 *
 * This module provides optional support for an encrypted connection to the
 * article source. It is intended to follow RFC2246, RFC4346 and RFC5246
 * (depending on the underlying OpenSSL library). The server to client
 * authentication based on X.509 certificates should be RFC1422 and RFC5280
 * compliant.
 *
 * \attention
 * It is required that 'SSIZE_MAX' is at least 'INT_MAX' (must be checked by
 * build system).
 *
 * \attention
 * We have no own source for random numbers. OpenSSL must be configured to be
 * able to fetch random numbers itself. If the operating system provides no
 * random number generator (RNG), there must be a separate one installed that
 * OpenSSL can use (consider egd or prngd if your OS provide no such option).
 * If OpenSSL reports there is no RNG available, the program fails to start.
 *
 * By default only cipher suites using ephemeral Diffie-Hellman-Merkle key
 * exchange (to provide forward secrecy) and strong symmetric data encryption
 * algorithms are offered to the server.
 * Because RFC4642 define it as mandatory, the weak cipher suite RSA-RSA-RC4-MD5
 * can be added to the list together with some other cipher suites that are
 * considered weak (without forward secrecy or based on elliptic curves). This
 * option is selected with a parameter of the \c tls_open() function.
 * This will also add the cipher suite RSA-RSA-AES128-SHA that is defined
 * as mandatory by RFC5246.
 *
 * According to RFC6176 and RFC7568 the SSL protocol is never negotiated and the
 * TLS protocol used instead.
 *
 * We never offer anonymous cipher suites to the server. Therefore while the
 * encrypted connection is established, the server must present a certificate.
 * This certificate can optionally be verified for server to client
 * authentication with the function \c tls_cert_verify() . This will ensure
 * that a trusted CA is the root of the certificate chain and that the subject
 * of the last certificate contains the hostname we have resolved to connect the
 * server.
 *
 * If the compile time option CFG_USE_TLS_OWNCERTS is defined to a nonzero
 * value, the certificate presented by the server is not verified using the
 * default locations of the OpenSSL installation. The root certificate of the
 * certificate chain must be installed in our local certificate verification
 * path instead.
 * <br>
 * Local certificate verification path: \c ~/.$CFG_NAME/certs
 *
 * If the compile time option CFG_USE_TLS_CRLS is defined to a nonzero value,
 * additionally the CRLs of the certificate issuers are checked for the whole
 * certificate chain to ensure that no certificate was revoked.
 * The required CRLs are downloaded automatically (the distribution points must
 * be listed in the certificates and available via HTTP protocol).
 * <br>
 * CRL verification path: \c ~/.$CFG_NAME/crls
 */
/* @{ */


#if CFG_USE_TLS


/* ========================================================================== */
/* Experimental features */

/*! \brief Set this to 1 to check CRLs for the whole chain of X.509 certificates
 * (EXPERIMENTAL)
 *
 * \note
 * This feature requires an operating system with X/Open System Interface (XSI).
 *
 * \attention
 * With this option disabled, certificates that were revoked by the issuing CAs
 * are nevertheless accepted!
 *
 * CRLs are downloaded automatically and must be available via HTTP protocol.
 *
 * \note
 * If the CRLs are large, this may consume significant network bandwith and
 * time. The update interval can be configured with \c CONF_CRL_UPD_IV .
 */
#  define CFG_USE_TLS_CRLS  0

/*! \brief Set this to 1 to check DH parameter size used for key exchange
 * (EXPERIMENTAL)
 *
 * This is not possible with the OpenSSL 1.0 API, but since OpenSSL 1.0.2 some
 * extensions are implemented that can be used for this purpose.
 *
 * \attention
 * With this option enabled, the program will no longer be able to use a shared
 * OpenSSL library that provide the current 1.0 ABI.
 *
 * \note
 * This option is a temporary workaround until OpenSSL 1.1 is available
 * (will be removed after the support period of OpenSSL 1.0 ends).
 */
#  define CFG_USE_TLS_DHPARAM_CHECK  0

/*! \brief Set this to 1 to offer only configured signature algorithms for
 * TLSv1.2 protocol (EXPERIMENTAL)
 *
 * This is not possible with the OpenSSL 1.0 API, but since OpenSSL 1.0.2 some
 * extensions are implemented that can be used for this purpose.
 *
 * \attention
 * With this option enabled, the program will no longer be able to use a shared
 * OpenSSL library that provide the current 1.0 ABI.
 *
 * \note
 * This option is a temporary workaround until OpenSSL 1.1 is available
 * (will be removed after the support period of OpenSSL 1.0 ends).
 */
#  define CFG_USE_TLS_SETSIGALG  0


/* ========================================================================== */
/* Constants */

/*! \brief Set this to 1 to use locally stored X.509 root certificates only
 *
 * The root certificate is searched in the local location only. The system wide
 * certificate store of the OpenSSL installation is ignored.
 */
#  define CFG_USE_TLS_OWNCERTS  0

/*! \brief Set this to 1 to accept wildcard for left-most component of X.509
 * certificate subjects
 *
 * \note
 * RFC4642 specify this as valid with regard to the NNTP protocol, therefore
 * the value 1 is the default.
 */
#  define CFG_USE_TLS_WILDCARD_SUBJECT  1

/*! \brief Default (strong) cipher suite list
 *
 * This list of cipher suites is the default. It is possible to use weaker
 * cipher suites when a connection is opened. See \ref tls_open() for details.
 *
 * This list contains only cipher suites that provide forward secrecy by using
 * Diffie-Hellman-Merkle key exchange. We use the conventional, slower algorithm
 * based on discrete logarithms. Variants based on elliptic curves are excluded
 * by intent because the documents published by Edward Snowden suggest that some
 * of them should be considered weak (in the sense that the standards on which
 * they are based were specifically written/influenced to weaken them).
 *
 * TLSv1.2 protocol is preferred (if the OpenSSL library supports it).
 *
 * Cipher suites that use DSA or ECDSA based authentication are excluded
 * because both are very dependent on a high quality RNG for its nonce and the
 * key length of DSA for TLS is effectively limited to 1024 bit.
 *
 * \note
 * The size of the DH parameters provided by the server is relevant for the
 * strength of the encryption and the forward secrecy. Unfortunately it can't be
 * checked with OpenSSL 1.0 (consider to enable the experimental workaround via
 * \ref CFG_USE_TLS_DHPARAM_CHECK if OpenSSL 1.1 can't be used).
 *
 * \note
 * The tag \c !SSLv2 in the list means the corresponding cipher suites not the
 * SSLv2 protocol.
 *
 * \attention
 * Ensure that this list never contains anonymous cipher suites. Otherwise the
 * server doesn't present a certificate and the authenticity check will fail.
 */
#  define TLS_CIPHERS_DEFAULT \
   "!SSLv2:!RC2:!RC4:!DES:!3DES:!MD5:!kEECDH:!aNULL:!aDSS:!aECDSA" \
   ":kEDH+HIGH:-SSLv3:kEDH+HIGH"

/*! \brief Optional (weak) cipher suite list
 *
 * This list contains cipher suites that are considered weak for better
 * compatibility to existing servers. Some standards like RFC4642 also defines
 * some of the weak algorithms as mandatory and we must support them to be
 * compliant.
 *
 * Cipher suites that use DSA or ECDSA based authentication are excluded because
 * we reject certificates with DSA or ECDSA keys.
 *
 * \note
 * The tag \c !SSLv2 in the list means the corresponding cipher suites not the
 * SSLv2 protocol.
 *
 * \attention
 * Ensure that this list never contains anonymous cipher suites. Otherwise the
 * server doesn't present a certificate and the authenticity check will fail.
 */
#  define TLS_CIPHERS_WEAK \
   "!SSLv2:!RC2:!DES:!aNULL:!aDSS:!aECDSA" \
   /* Strong symmetric algorithms and forward secrecy first */ \
   ":kEDH+HIGH:kEECDH+HIGH:-SSLv3:kEDH+HIGH:kEECDH+HIGH" \
   ":-3DES:kEDH+HIGH:kEECDH+HIGH" \
   /* Strong symmetric algorithms and no forward secrecy next */ \
   ":AES256-GCM-SHA384:CAMELLIA256-SHA:AES128-SHA" \
   /* Weak symmetric algorithms and no forward secrecy last */ \
   ":RC4-MD5"

/*! \brief Signature algorithms for TLSv1.2 protocol
 *
 * This list contains the signature algorithms offered for TLSv1.2 protocol in
 * order of decreasing preference.
 * We only offer hash algorithms from the SHA-2 family (this means MD5 and SHA-1
 * are not used for negotiation).
 *
 * \attention
 * The asymmetric cipher must always be RSA because we reject certificates with
 * DSA or ECDSA keys.
 *
 * \note
 * For older protocol versions the algorithm is fixed and this list is ignored.
 */
#  define TLS_SIGALGS  "RSA+SHA512:RSA+SHA384:RSA+SHA256"


/* ========================================================================== */
/* Variables */

static posix_locale_t  tls_loc_ctype_posix = 0;
static SSL_CTX*  tls_ctx = NULL;  /* Global context for all connections */
#  if CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG
static SSL_CONF_CTX*  cctx;  /* Global configuration context */
#  endif  /* CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG */
static const char*  tls_certpath = NULL;
#  if CFG_USE_TLS_CRLS
static const char*  tls_crlpath = NULL;
static int  tls_crl_update_skip = 0;  /* Don't update CRLs if nonzero */
#  endif  /* CFG_USE_TLS_CRLS */


/* ========================================================================== */
/* Check for root certificate
 *
 * \param[in]  cc       Pointer to certificate
 * \param[out] is_root  Result
 * \param[in]  bufsize  Size of name buffers \e buf_i and \e buf_s
 * \param[out] buf_i    Pointer to issuer name buffer
 * \param[out] buf_s    Pointer to subject name buffer
 *
 * \note
 * If the caller is not interested in the issuer or subject name, \e b_i and/or
 * \e b_s are allowed to be \c NULL . If both are \c NULL , \e bufsize is
 * ignored.
 */

static int  tls_cert_check_root(X509*  cc, int*  is_root, size_t  bufsize,
                                char*  buf_i, char*  buf_s)
{
   int  res = 0;
   BIO*  biop = BIO_new(BIO_s_mem());
   X509_NAME*  issuer;
   X509_NAME*  subject;
   char*  b_i = NULL;
   char*  b_s = NULL;
   char*  p = NULL;
   size_t  len = 0;

   /* Check BIO */
   if(NULL == biop)
   {
      print_error("TLS: Creating BIO failed");
      res = -1;
   }

   /* Get Issuer of certificate */
   if(!res)
   {
      issuer = X509_get_issuer_name(cc);
      if(NULL == issuer)
      {
         print_error("TLS: Extraction of issuer from X.509 certificate failed");
         res = -1;
      }
      else
      {
         X509_NAME_print_ex(biop, issuer, 0, XN_FLAG_ONELINE);
         len = (size_t) BIO_get_mem_data(biop, &p);
         if(!len)  { res = -1; }
         else
         {
            b_i = (char*) posix_malloc(len + (size_t) 1);
            if(NULL == b_i)  { res = -1; }
            else
            {
               memcpy((void*) b_i, (void*) p, len);
               b_i[len] = 0;
            }
         }
      }
   }

   /* Get Subject of certificate */
   if(!res)
   {
      if(1 != BIO_reset(biop))  { res = -1; }
      else
      {
         subject = X509_get_subject_name(cc);
         if(NULL == subject)
         {
            print_error("TLS: Extraction of subject from X.509 certificate "
                        "failed");
            res = -1;
         }
         else
         {
            X509_NAME_print_ex(biop, subject, 0, XN_FLAG_ONELINE);
            len = (size_t) BIO_get_mem_data(biop, &p);
            if(!len)  { res = -1; }
            else
            {
               b_s = (char*) posix_malloc(len + (size_t) 1);
               if(NULL == b_s)  { res = -1; }
               else
               {
                  memcpy((void*) b_s, (void*) p, len);
                  b_s[len] = 0;
               }
            }
         }
      }
   }

   /* Compare subject with issuer */
   if(!res)
   {
      *is_root = 0;
      if(!strcmp(b_i, b_s))
      {
         /* Self signed (root) certificate */
         *is_root = 1;
      }
   }

   /* Return strings (truncate them if necessary) */
   if(!res && bufsize)
   {
      if(NULL != buf_i)
      {
         len = strlen(b_i);
         if(bufsize - (size_t) 1 < len)  { len = bufsize - (size_t) 1; }
         memcpy((void*) buf_i, (void*) b_i, len);
         buf_i[len] = 0;
      }
      if(NULL != buf_s)
      {
         len = strlen(b_s);
         if(bufsize - (size_t) 1 < len)  { len = bufsize - (size_t) 1; }
         memcpy((void*) buf_s, (void*) b_s, len);
         buf_s[len] = 0;
      }
   }

   /* Release memory */
   posix_free((void*) b_i);
   posix_free((void*) b_s);
   if(NULL != biop)  { BIO_free(biop); }

   return(res);
}


/* ========================================================================== */
/* Get certificate directory path
 *
 * \param[out] certpath  Pointer to certificate path
 *
 * The caller is responsible to free the memory for the buffer on success.
 */

#  if CFG_USE_TLS_OWNCERTS
static int  tls_get_certpath(char**  certpath)
{
   int  res = -1;
   const char*  homedir = NULL;
   const char  confdir[] = "/." CFG_NAME "/";
   const char  certdir[] = "certs";
   int  rv;

   /* Get home directory from environment */
   rv = ts_getenv("HOME", &homedir);
   /* Prepare configuration directory */
   if(!rv)
   {
      /* Check home directory path */
      rv = fu_check_homedir(homedir);
      /* Create config file pathname */
      if(!rv)
      {
         /* The additional byte is for the terminating NUL */
         *certpath = (char*) posix_malloc(strlen(homedir) + strlen(confdir)
                                          + strlen(certdir) + (size_t) 1);
         if(NULL == *certpath)
         {
            print_error("TLS: Cannot allocate memory for certificate path");
         }
         else
         {
            /* Create config directory if it doesn't exist */
            strcpy(*certpath, homedir);
            strcat(*certpath, confdir);
            rv = posix_mkdir(*certpath, (posix_mode_t) POSIX_S_IRWXU);
            if(!rv || (-1 == rv && POSIX_EEXIST == posix_errno))
            {
               /* Configuration directory is now available */
               res = 0;
            }
            else  { print_error("TLS: Cannot create configuration directory"); }
            if(!res)
            {
               strcat(*certpath, certdir);
               rv = posix_mkdir(*certpath, (posix_mode_t) POSIX_S_IRWXU);
               if(!(!rv || (-1 == rv && POSIX_EEXIST == posix_errno)))
               {
                  print_error("TLS: Cannot create certificate directory");
                  res = -1;
               }
            }
         }
      }
      posix_free((void*) homedir);
   }
   else  { print_error("TLS: Environment variable 'HOME' is not defined"); }

   return(res);
}
#  endif  /* CFG_USE_TLS_OWNCERTS */


#  if CFG_USE_TLS_CRLS


/* ========================================================================== */
/* Check for elapsed CRL update interval
 *
 * \return
 * - 0 if CRLs are usable
 * - 1 for elapsed update interval
 * - -1 on error (if calculation failed)
 */

static int  tls_check_crl_age(void)
{
   int  res;
   posix_time_t  ts;
   core_time_t  pts_last;
   core_time_t  pts_current;
   core_time_t  age;
   core_time_t  interval = 0;

   res = enc_convert_iso8601_to_posix(&pts_last, config[CONF_CRL_UPD_TS].val.s);
   if(!res)
   {
      posix_time(&ts);
      if((posix_time_t) 0 > ts)  { res = -1; }
      else
      {
         pts_current = (core_time_t) ts;
         if(pts_last > pts_current)
         {
            print_error("TLS: Last CRL update timestamp is in the future");
            res = -1;
         }
         else
         {
            age = pts_current - pts_last;
            age /= (core_time_t) 3600;
            /* Clamp negative intervals to zero */
            if(0 > config[CONF_CRL_UPD_IV].val.i)
            {
               print_error("TLS: Negative CRL update interval was set to zero");
               config[CONF_CRL_UPD_IV].val.i = 0;
            }
            /* Clamp to 1 year upper limit */
            if(8760 < config[CONF_CRL_UPD_IV].val.i)
            {
               print_error("TLS: Too large CRL update interval was clamped");
               config[CONF_CRL_UPD_IV].val.i = 8760;
            }
            interval = (core_time_t) config[CONF_CRL_UPD_IV].val.i;
            if(interval < age)  { res = 1; }
         }
      }
   }

   /* Check for error */
   if(0 > res)  { print_error("TLS: CRL update interval check failed"); }

   return(res);
}


/* ========================================================================== */
/* Update CRL update timestamp */

static int  tls_set_crl_age(void)
{
   int  res;
   char  isodate[21];

   res = enc_get_iso8601_utc(isodate);
   if(!res)  { res = conf_string_replace(&config[CONF_CRL_UPD_TS], isodate); }

   return(res);
}


/* ========================================================================== */
/* Reset CRL update timestamp to force update */

static int  tls_reset_crl_age(void)
{
   int  res;

   res = conf_string_replace(&config[CONF_CRL_UPD_TS], "1970-01-01T00:00:00Z");

   return(res);
}


/* ========================================================================== */
/* Get CRL directory path
 *
 * \param[out] certpath  Pointer to CRL path
 *
 * The caller is responsible to free the memory for the buffer on success.
 */

static int  tls_get_crlpath(char**  crlpath)
{
   int  res = -1;
   const char*  homedir = NULL;
   const char  confdir[] = "/." CFG_NAME "/";
   const char  crldir[] = "crls";
   int  rv;

   /* Get home directory from environment */
   rv = ts_getenv("HOME", &homedir);
   /* Prepare configuration directory */
   if(!rv)
   {
      /* Check home directory path */
      rv = fu_check_homedir(homedir);
      /* Create config file pathname */
      if(!rv)
      {
         /* The additional byte is for the terminating NUL */
         *crlpath = (char*) posix_malloc(strlen(homedir) + strlen(confdir)
                                          + strlen(crldir) + (size_t) 1);
         if(NULL == *crlpath)
         {
            print_error("TLS: Cannot allocate memory for CRL path");
         }
         else
         {
            /* Create config directory if it doesn't exist */
            strcpy(*crlpath, homedir);
            strcat(*crlpath, confdir);
            rv = posix_mkdir(*crlpath, (posix_mode_t) POSIX_S_IRWXU);
            if(!rv || (-1 == rv && POSIX_EEXIST == posix_errno))
            {
               /* Configuration directory is now available */
               res = 0;
            }
            else  { print_error("TLS: Cannot create configuration directory"); }
            if(!res)
            {
               strcat(*crlpath, crldir);
               rv = posix_mkdir(*crlpath, (posix_mode_t) POSIX_S_IRWXU);
               if(!(!rv || (-1 == rv && POSIX_EEXIST == posix_errno)))
               {
                  print_error("TLS: Cannot create CRL directory");
                  res = -1;
               }
            }
         }
      }
      posix_free((void*) homedir);
   }
   else  { print_error("TLS: Environment variable 'HOME' is not defined"); }

   return(res);
}


/* ========================================================================== */
/* Fetch CRL from distribution point
 *
 * \param[in]  cci  Index in certificate chain
 * \param[in]  uri  URI or certificate CRL to download
 * \param[out] pn   Pathname of downloaded file
 *
 * Using the filenames from the URIs of the certificates may lead to filename
 * conflicts. Generic filenames are used (based on the index in the certificate
 * chain) instead.
 *
 * On success the pathname of the file is written to \e pn and the caller is
 * responsible to free the memory allocated for the buffer.
 */

static int  tls_download_crl(int  cci, const char*  uri, char**  pn)
{
   int  res = -1;
   int  rv;
   char  filename[8] = { '/', 'C', 'R', 'L', '_', '_', '_', 0 };
   char*  pathname = NULL;
   size_t  len;

   if(NULL == tls_crlpath)
   {
      print_error("TLS: Download of CRL failed, directory not available");
   }
   else
   {
      if(0 > cci || 999 < cci)
      {
         print_error("TLS: Download of CRL failed, invalid chain index");
      }
      else
      {
         rv = posix_snprintf(&filename[4], 4, "%d", cci);
         if(1 > rv || 3 < rv)
         {
            print_error("TLS: Download of CRL failed, filename invalid");
         }
         else
         {
            len = strlen(tls_crlpath);
            len += strlen(filename);
            pathname = (char*) posix_malloc(++len);
            if(NULL != pathname)
            {
               strcpy(pathname, tls_crlpath);
               strcat(pathname, filename);
               res = ext_download_file(pathname, uri);
            }
         }
      }
   }
   *pn = pathname;

   /* Release memory for buffer on error */
   if(res)
   {
      posix_free((void*) *pn);
      *pn = NULL;
   }

   return(res);
}


/* ========================================================================== */
/* Convert CRL from DER to PEM format
 *
 * \param[in]  pn_der  Pathname of CRL in DER format
 * \param[out] pn_pem  Pathname of CRL in PEM format
 *
 * On success the pathname of the PEM file is written to \e pn_pem and the
 * caller is responsible to free the memory allocated for the buffer.
 */

static int  tls_convert_crl_to_pem(const char*  pn_der, char**  pn_pem)
{
   int  res = 0;
   int  rv;
   BIO*  biop;
   X509_CRL*  crl = NULL;
   size_t  len;
   int  fd;
   char  pem_cmp[] = "-----BEGIN X509 CRL-----";  /* ASCII armor of PEM */
   char  pem_buf[] = "                        ";

   /* Prepare target filename */
   len = strlen(pn_der);
   len += 4;  /* for ".pem" Extension */
   *pn_pem = (char*) posix_malloc(++len);
   if(NULL == *pn_pem)
   {
      print_error("TLS: Memory allocation failed for filename");
      res = -1;
   }
   else
   {
      strcpy(*pn_pem, pn_der);
      strcat(*pn_pem, ".pem");
   }

   /* Load CRL */
   if(!res)
   {
      res = -1;
      biop = BIO_new(BIO_s_file());
      if(NULL != biop)
      {
         rv = BIO_read_filename(biop, pn_der);
         if(1 != rv)
         {
            print_error("TLS: BIO error while configuring DER filename of CRL");
         }
         else
         {
            /* Check whether CRL already is in PEM format */
            len = strlen(pem_cmp);
            rv = BIO_read(biop, pem_buf, len);
            if(len == rv)
            {
               if(!memcmp((void*) pem_cmp, (void*) pem_buf, len))
               {
                  /* CRL already in PEM format */
                  res = 1;
               }
               else
               {
                  /* Assume CRL is in DER format */
                  rv = BIO_seek(biop, 0);
                  if(0 != rv)
                  {
                     print_error("TLS: I/O error while loading CRL");
                  }
                  else
                  {
                     crl = d2i_X509_CRL_bio(biop, NULL);
                     if(NULL == crl)
                     {
                        print_error("TLS: Loading CRL to memory failed");
                     }
                     else
                     {
                        /* CRL loaded to memory */
                        res = 0;
                     }
                  }
               }
            }
         }
         BIO_free(biop);
      }
   }

   /* Convert and store CRL */
   if(!res)
   {
      res = -1;
      /* Create CRL file with desired permissions, then reopen it via BIO */
      /* Attention: Calling 'umask()' would affect other threads! */
      rv = fu_open_file(*pn_pem, &fd, POSIX_O_WRONLY | POSIX_O_CREAT,
                        POSIX_S_IRUSR | POSIX_S_IWUSR);
      if(!rv)
      {
         fu_close_file(&fd, NULL);
         biop = BIO_new(BIO_s_file());
         if(NULL != biop)
         {
            rv = BIO_write_filename(biop, *pn_pem);
            if(1 != rv)
            {
               print_error("TLS: "
                           "BIO error while configuring PEM filename of CRL");
            }
            else
            {
               rv = PEM_write_bio_X509_CRL(biop, crl);
               if(1 != rv)
               {
                  print_error("TLS: Converting and storing CRL failed");
               }
               else
               {
                  /* CRL stored in PEM format */
                  rv = BIO_get_fd(biop, NULL);
                  if(-1 != rv)  { fu_sync(rv, NULL); }
                  res = 0;
               }
            }
            BIO_free(biop);
         }
      }
   }

   /* Create symlink if CRL is already in PEM format */
   if(1 == res)
   {
      res = posix_symlink(pn_der, *pn_pem);
   }

   /* Release memory for buffer on error */
   if(res)
   {
      print_error("TLS: Conversion of CRL to PEM format failed");
      posix_free((void*) *pn_pem);
      *pn_pem = NULL;
   }

   return(res);
}


/* ========================================================================== */
/* Create hash symlink to CRL
 *
 * \param[in] pn_pem  Pathname of CRL in PEM format
 * \param[in] cert    Pointer to certificate
 *
 * The symlinks to CRLs need to have the following format for OpenSSL 1.0:
 * \c HHHHHHHH.rD
 *
 * The field \c H is the hash that is searched for. The field \c D is used to
 * distinguish duplicates (identical hash values for different files).
 *
 * \attention
 * The caller is responsible to delete old symlinks in the directory where
 * \e pn_pem resides before calling this function.
 */

static int  tls_create_hash_symlink(const char*  pn_pem, X509*  cert)
{
   int  res = -1;
   int  rv;
   unsigned long int  issuer_hash;
   size_t  len;
   char  name_symlink[13];
   char*  pn_symlink = NULL;
   unsigned int  i = 0;
   int  abort = 0;

   if(NULL == tls_crlpath)
   {
      print_error("TLS: Creating of symlink failed, directory not available");
   }
   else
   {
      /* Limit to 9 duplicate names */
      while(!abort && 9U >= i)
      {
         issuer_hash = X509_issuer_name_hash(cert);
         /* See 'c_rehash' manual for the name format */
         rv = posix_snprintf(name_symlink, 13, "/%08lx.r%u", issuer_hash, i);
         if(12 != rv)
         {
            print_error("TLS: Creating of symlink failed, name not valid");
            abort = 1;
         }
         else
         {
            len = strlen(tls_crlpath);
            len += strlen(name_symlink);
            pn_symlink = (char*) posix_malloc(++len);
            if(NULL == pn_symlink)  { abort = 1; }
            else
            {
               strcpy(pn_symlink, tls_crlpath);
               strcat(pn_symlink, name_symlink);
               res = posix_symlink(pn_pem, pn_symlink);
               if(!res)
               {
                  /* Success */
                  abort = 1;
               }
               else
               {
                  /* Check whether symlink already exist */
                  if(!(-1 == res && POSIX_EEXIST == posix_errno))
                  {
                     /* No => Creation failed => Abort */
                     print_error("TLS: Creating of symlink failed");
                     abort = 1;
                  }
               }
               posix_free((void*) pn_symlink);
            }
         }
         ++i;
      }
      /* Check whether duplicate numbers gone out */
      if(9 < i)
      {
         print_error("TLS: Creating of symlink failed, too many duplicates");
      }
   }

   return(res);
}


/* ========================================================================== */
/* Get mutex for CRL update
 *
 * \param[out] fd  Pointer to filedescriptor of lockfile
 */

static int  tls_crl_get_lock(int*  fd)
{
   int  res = -1;
   const char*  homedir = NULL;
   const char  confdir[] = "/." CFG_NAME "/";
   const char  crl_lockfile[] = ".crl_lock";
   char*  lockpathname = NULL;
   int  rv;

   /* Get home directory from environment */
   rv = ts_getenv("HOME", &homedir);
   /* Prepare configuration directory */
   if(!rv)
   {
      /* Check home directory path */
      rv = fu_check_homedir(homedir);
      /* Create config file pathname */
      if(!rv)
      {
         /* The additional byte is for the terminating NUL */
         lockpathname = (char*) posix_malloc(strlen(homedir) + strlen(confdir)
                                             + strlen(crl_lockfile)
                                             + (size_t) 1);
         if(NULL == lockpathname)
         {
            print_error("TLS: Cannot allocate memory "
                        "for CRL lockfile pathname");
         }
         else
         {
            /* Create config directory if it doesn't exist */
            strcpy(lockpathname, homedir);
            strcat(lockpathname, confdir);
            rv = posix_mkdir(lockpathname, (posix_mode_t) POSIX_S_IRWXU);
            if(!rv || (-1 == rv && POSIX_EEXIST == posix_errno))
            {
               /* Configuration directory is now available */
               strcat(lockpathname, crl_lockfile);
               rv = fu_open_file(lockpathname, fd,
                                 POSIX_O_WRONLY | POSIX_O_CREAT,
                                 POSIX_S_IRUSR | POSIX_S_IWUSR);
               if(rv)
               {
                  print_error("TLS: Cannot open mutex file for CRL directory");
               }
               else
               {
                  if(fu_lock_file(*fd))
                  {
                     print_error("TLS: Cannot lock mutex file "
                                 "for CRL directory");
                  }
                  else  { res = 0; }
               }
            }
            else  { print_error("TLS: Cannot create configuration directory"); }
         }
      }
   }
   posix_free((void*) lockpathname);

   return(res);
}


/* ========================================================================== */
/* Release mutex for CRL update
 *
 * \param[in,out] fd  Pointer to filedescriptor of lockfile
 */

static void  tls_crl_release_lock(int*  fd)
{
   fu_close_file(fd, NULL);
}


/* ========================================================================== */
/* Update CRLs for certificate chain
 *
 * \param[in] certificate_chain  Pointer to certificate chain
 *
 * \return
 * - 0 on success
 * - Positive value if no CRLs are required (self signed server certificate)
 * - -1 on unknown error
 * - -2 if the lock for the mutex file can't be acquired
 */

static int  tls_update_crls(STACK_OF(X509)*  certificate_chain)
{
   int  res = -1;
   int  rv;
   int  crl_valid = 0;
   int  crls_required = 0;
   int  ccl;  /* Length of certificate chain */
   int  cci;  /* Index in certificate chain */
   X509*  cc;  /* Current certificate */
   int  num, num2;
   int  i, i2;
   STACK_OF(DIST_POINT)*  dp = NULL;
   DIST_POINT*  dpi;
   GENERAL_NAME*  ani;
   const size_t  buflen = 256;
   char  b_i[buflen];
   int  is_root = 0;
   char*  p = NULL;
   char*  crl_pn_der;  /* Pathname to raw CRL (expected in DER format) */
   char*  crl_pn_pem;  /* Pathname of cooked CRL (in PEM format) */
   int  crlpath_ok = 0;
   int  lockfd = -1;

   /* Prepare and lock mutex file for CRL update */
   if(tls_crl_get_lock(&lockfd))  { res = -2; }
   else
   {
      /* Got lock => Automatic CRL update */
      printf("%s: TLS: -----------------------------------------------------\n",
             CFG_NAME);
      printf("%s: TLS: Automatic CRL maintenance (EXPERIMENTAL)\n", CFG_NAME);
      /* Delete CRL directory */
      if(NULL != tls_crlpath)
      {
         if(!fu_delete_tree(tls_crlpath))
         {
            if(!posix_mkdir(tls_crlpath, (posix_mode_t) POSIX_S_IRWXU))
            {
               /* Empty CRL directory is now available */
               crlpath_ok = 1;
            }
         }
      }
      if(!crlpath_ok)
      {
         print_error("TLS: CRL update failed, directory not available");
      }
      else
      {
         /* Get length of certificate chain */
         ccl = sk_X509_num(certificate_chain);
         for(cci = 0; cci < ccl; ++cci)
         {
            /* Get next certificate */
            cc = sk_X509_value(certificate_chain, cci);
            if(NULL == cc)
            {
               print_error("TLS: Error while parsing certificate chain");
               break;
            }
            rv = tls_cert_check_root(cc, &is_root, buflen, b_i, NULL);
            if(rv)  { break; }
            /* Determine type of certificate */
            if(!cci)
            {
               if(is_root)
               {
                  printf("%s: TLS: Self signed server certificate:\n",
                         CFG_NAME);
               }
               else  { printf("%s: TLS: Server certificate:\n", CFG_NAME); }
            }
            else
            {
               if(is_root)
               {
                  printf("%s: TLS: Root certificate:\n", CFG_NAME);
               }
               else
               {
                  printf("%s: TLS: Intermediate certificate:\n", CFG_NAME);
               }
            }
            /* Print issuer */
            printf("%s: TLS:    Issuer: %s\n", CFG_NAME, b_i);
            /* Get CRL of certificate */
            crl_valid = 0;
            if(is_root)
            {
               printf("%s: TLS:    Always trusted if installed"
                      " (Must be uninstalled for revocation)\n", CFG_NAME);
               res = 0;
            }
            else
            {
               dp = X509_get_ext_d2i(cc, NID_crl_distribution_points, NULL, 
                                     NULL);
               if(NULL == dp)
               {
                  print_error("TLS:    No CRL distribution points found"
                              " in certificate");
               }
               else
               {
                  num2 = sk_DIST_POINT_num(dp);
                  for(i2 = 0; i2 < num2; ++i2)
                  {
                     dpi = sk_DIST_POINT_value(dp, i2);
                     if(NULL != dpi->distpoint)
                     {
                        num = sk_GENERAL_NAME_num(
                                 dpi->distpoint->name.fullname);
                        for(i = 0; i < num; ++i)
                        {
                           ani = sk_GENERAL_NAME_value(
                                    dpi->distpoint->name.fullname, i);
                           if(GEN_URI == ani->type)
                           {
                              p = (char*)
                                  ani->d.uniformResourceIdentifier->data;
                              printf("%s: TLS:    "
                                     "CRL distribution point %d: %s\n",
                                      CFG_NAME, i2, p);
                              /* Download CRL */
                              crl_pn_der = NULL;
                              crl_pn_pem = NULL;
                              res = tls_download_crl(cci, p, &crl_pn_der);
                              if(!res)
                              {
                                 /* Convert CRL to PEM format */
                                 res = tls_convert_crl_to_pem(crl_pn_der,
                                                              &crl_pn_pem);
                                 if(!res)
                                 {
                                    /* Create symlink to CRL in PEM format */
                                    res = tls_create_hash_symlink(crl_pn_pem,
                                                                  cc);
                                 }
                                 /* Check for success */
                                 if(!res)
                                 {
                                    crl_valid = 1;
                                    crls_required = 1;
                                 }
                              }
                              /* Release memory allocated for pathnames */
                              posix_free((void*) crl_pn_pem);
                              posix_free((void*) crl_pn_der);
                              break;
                           }
                        }
                     }
                     if(crl_valid)  { break; }
                  }
               }
            }
            /* Check for error */
            if(res)  { break; }
         }
      }
      printf("%s: TLS: -----------------------------------------------------\n",
             CFG_NAME);
      /* Release lock of mutex file */
      tls_crl_release_lock(&lockfd);
      /* Return positive value if no CRLs are required */
      if(!res && !crls_required)  { res = 1; }
   }

   /* Release memory */
   if(NULL != dp)  { CRL_DIST_POINTS_free(dp); }

   return(res);
}


#  endif  /* CFG_USE_TLS_CRLS */


/* ========================================================================== */
/* Check certificate chain for signing key length and signature hash algorithm
 *
 * \param[in] certificate_chain  Pointer to certificate chain
 * \param[in] proto              Flag indicating TLSv1.2 or higher protocol
 *
 * \attention
 * Any non-RSA key is rejected. This should never happen for TLSv1.2 because of
 * the signature algorithm negotiation.
 *
 * If the modulus size of a RSA key is less than 1024 bits, the key is rejected.
 * If the modulus size of a RSA key is less than 3072 bits, a warning message
 * is generated.
 *
 * The signature hash algorithms MD2, MD4 and MD5 are rejected except for the
 * self signature of the root certificate (signature is not used in this case).
 * If the signature hash algorithm is SHA-1 or SHA-224, a warning message is
 * generated. This should never happen for TLSv1.2 because of the signature
 * algorithm negotiation.
 *
 * \return
 * - 0 on success
 * - -1 if one or more keys are rejected
 */

static int  tls_chain_check_sigalgs(STACK_OF(X509)*  certificate_chain,
                                    int  proto)
{
   int  res = -1;
   int  ccl;  /* Length of certificate chain */
   int  cci;  /* Index in certificate chain */
   X509*  cc;  /* Current certificate */
   int  rv;
   posix_ssize_t  rv2;
   int  is_root = 0;
   const char*  s;
   EVP_PKEY*  pkey = NULL;
   RSA*  rsa_key = NULL;
   int  rsa_mod_size;
   int  alg;
   const char*  md_s;
   int  md_ok;
   unsigned char*  buf;
   int  len;
   char  prefix[80];

   /* Get length of certificate chain */
   ccl = sk_X509_num(certificate_chain);
   for(cci = 0; cci < ccl; ++cci)
   {
      /* Get next certificate */
      cc = sk_X509_value(certificate_chain, cci);
      if(NULL == cc)
      {
         print_error("TLS: Error while parsing certificate chain");
         break;
      }
      /* Check for root certificate */
      rv = tls_cert_check_root(cc, &is_root, 0, NULL, NULL);
      if(rv)  { break; }
      s = "";
      if(!cci)  { s = "(Server) "; }
      if(is_root)  { s = "(Root) "; }
      printf("%s: TLS: Certificate level %d %sin chain:\n", CFG_NAME, cci, s);
      /* Extract public key */
      pkey = X509_extract_key(cc);
      if(NULL == pkey)
      {
         print_error("TLS: Extraction of public key from X.509 certificate"
                     " failed");
         break;
      }
#  if  CFG_USE_OPENSSL_API_1_1
      if(EVP_PKEY_RSA != EVP_PKEY_base_id(pkey))
#  else   /* CFG_USE_OPENSSL_API_1_1 */
      if(EVP_PKEY_RSA != pkey->type)
#  endif   /* CFG_USE_OPENSSL_API_1_1 */
      {
         print_error("TLS: Certificate don't use RSA key (not supported)");
         break;
      }
      rsa_key = EVP_PKEY_get1_RSA(pkey);
      if(NULL == rsa_key)
      {
         print_error("TLS: Extraction of key from certificate failed");
         break;
      }
      rsa_mod_size = RSA_size(rsa_key);
      if(0 >= rsa_mod_size || 8192 < rsa_mod_size)
      {
         print_error("TLS: Calculation of key size failed");
         break;
      }
      rsa_mod_size *= 8;
      printf("%s: TLS:    RSA key modulus size: %d bit\n", CFG_NAME,
             rsa_mod_size);
      if(1024 > rsa_mod_size)
      {
         print_error("TLS: RSA key modulus size of certificate rejected");
         break;
      }
      else if(3072 > rsa_mod_size)
      {
         /* 3072 bit would match a 128 bit symmetric cipher */
         printf("%s: TLS:    Warning: RSA key modulus should be "
                "at least 3072 bit\n", CFG_NAME);
      }
      if(!cci)
      {
         /* Print randomart image for key */
         buf = NULL;
         len = i2d_RSA_PUBKEY(rsa_key, &buf);
         if(0 > len)
         {
            print_error("TLS: Extraction of key fingerprint failed");
            break;
         }
         else
         {
            /* 'SubjectPublicKeyInfo' structure now stored in buf */
            rv2 = posix_snprintf(prefix, 80, "%s: TLS:    ", CFG_NAME);
            if(0 > rv2)
            {
               print_error("TLS: Creating message prefix failed");
               break;
            }
            rv = digest_randomart(prefix, (const char*) buf, (size_t) len);
            OPENSSL_free(buf);
            if(rv)
            {
               print_error("TLS: Creating randomart for key failed");
               break;
            }
         }
      }
      RSA_free(rsa_key);
      /* Extract hash algorithm for signatures */
#  if CFG_USE_OPENSSL_API_1_1
      alg = X509_get_signature_nid(cc);
#  else   /* CFG_USE_OPENSSL_API_1_1 */
      alg = OBJ_obj2nid(cc->sig_alg->algorithm);
#  endif   /* CFG_USE_OPENSSL_API_1_1 */
      if(NID_undef == alg)
      {
         print_error("TLS: Extraction of hash algorithm failed");
         break;
      }
      md_s = OBJ_nid2sn(alg);
      if(is_root)
      {
         printf("%s: TLS:    Self signature is not used\n", CFG_NAME);
      }
      else
      {
         printf("%s: TLS:    Signature algorithm: %s\n", CFG_NAME, md_s);
         md_ok = 0;
         switch(alg)
         {
            case NID_md2WithRSAEncryption:
            case NID_md4WithRSAEncryption:
            case NID_md5WithRSAEncryption:
            {
               /* Reject weak algorithms */
               print_error("TLS: Hash algorithm of signature rejected");
               break;
            }
            case NID_sha1WithRSAEncryption:
            case NID_sha224WithRSAEncryption:
            {
               /* SHA-256 would match a 128 bit symmetric cipher */
               printf("%s: TLS:    Warning: At least SHA-256 should be "
                     "used for signatures\n", CFG_NAME);
#  if CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG
               if(proto)
               {
                  /*
                   * RFC5246 mandates that the certificate chain must use one
                   * of the signature algorithms offered in negotiation.
                   * But we have not offered this algorithm!
                   */
                  print_error("TLS:    Server has violated RFC5246");
               }
#  endif  /* CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG */
               md_ok = 1;
               break;
            }
            case NID_sha256WithRSAEncryption:
            case NID_sha384WithRSAEncryption:
            case NID_sha512WithRSAEncryption:
            {
               /* This is what we have offered for negotiation with TLSv1.2 */
               md_ok = 1;
               break;
            }
            default:
            {
               print_error("TLS:    Unexpected hash algorithm rejected");
               if(proto)
               {
                  print_error("TLS:    Server has violated RFC5246");
               }
               break;
            }
         }
         if(!md_ok)  { break; }
      }
   }
   if(cci == ccl)  { res = 0; }

   return(res);
}


/* ========================================================================== */
/* Verify that subject of certificate matches hostname
 *
 * \param[in] cn       Common name from certificate
 * \param[in] subject  Subject to match
 *
 * The comparison is done locale independent and case insensitive.
 *
 * \note
 * A wildcard for the first part of the subject is accepted if
 * \ref CFG_USE_TLS_WILDCARD_SUBJECT is defined to a nonzero value.
 */

static int  tls_subject_check(const char*  cn, const char*  subject)
{
   int  res = 0;
#  if CFG_USE_TLS_WILDCARD_SUBJECT
   int  wildcard = 0;
   const char*  p;

   /* Check for wildcard as first part of subject */
   if('*' == subject[0])
   {
      /* Yes => Compare only parts after first dot */
      wildcard = 1;
      res = -1;
      if('.' == subject[1])
      {
         subject = &subject[2];
         p = strchr(cn, (int) '.');
         if(NULL != p)
         {
            cn = ++p;
            res = 0;
         }
      }
   }
#  endif  /* CFG_USE_TLS_WILDCARD_SUBJECT */
   if(!res)
   {
      /* Compare using POSIX locale */
      res = posix_strcasecmp_l(cn, subject, tls_loc_ctype_posix);
   }
   if(0 < res)  { res = -res; }

   /* Print message for result */
   if(res)  { printf(" (doesn't match hostname)\n"); }
#  if CFG_USE_TLS_WILDCARD_SUBJECT
   else if(wildcard)  { printf(" (matches hostname using wildcard)\n"); }
#  endif  /* CFG_USE_TLS_WILDCARD_SUBJECT */
   else  { printf(" (matches hostname)\n"); }

   return(res);
}


/* ========================================================================== */
/* Create context for TLS connection
 *
 * We negotiate for TLSv1 or higher connections (SSL up to 3.0 is rejected).
 * To provide forward secrecy, only cipher suites with ephemeral
 * Diffie-Hellmann-Merkle key exchange are offered by default.
 * Session tickets and compression are disabled.
 *
 * \attention
 * With the initial configuration the server certificate is not automatically
 * checked. The caller must explicitly check the certificate for authentication
 * using the \ref tls_cert_verify() function.
 */

static int  tls_create_context(void)
{
   int  res = 0;
   long int  mode = SSL_MODE_ENABLE_PARTIAL_WRITE
                    | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
   long int  options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TICKET
                       | SSL_OP_NO_COMPRESSION;
   long int  parameters =  X509_V_FLAG_X509_STRICT
#  if CFG_USE_TLS_CRLS
                          | X509_V_FLAG_CRL_CHECK
                          | X509_V_FLAG_CRL_CHECK_ALL
                          | X509_V_FLAG_EXTENDED_CRL_SUPPORT
                          | X509_V_FLAG_USE_DELTAS
#  endif  /* CFG_USE_TLS_CRLS */
                          ;
   X509_VERIFY_PARAM*  para = NULL;
   long  f;
   int  rv;
#  if CFG_USE_TLS_OWNCERTS || CFG_USE_TLS_CRLS
   char*  cpp = NULL;
#  endif  /* CFG_USE_TLS_OWNCERTS || CFG_USE_TLS_CRLS */
#  if CFG_USE_TLS_CRLS
   X509_LOOKUP*  look = NULL;
#  endif  /* CFG_USE_TLS_CRLS */

   /* Create TLS context for client side */
   tls_ctx = SSL_CTX_new(SSLv23_client_method());
   if(NULL == tls_ctx)
   {
      print_error("TLS: Cannot allocate context");
      res = -1;
   }
   if(!res)
   {
#  if CFG_USE_OPENSSL_API_1_1
      /* Disable all internal checks */
      SSL_CTX_set_security_level(tls_ctx, 0);
#  endif   /* CFG_USE_OPENSSL_API_1_1 */
      f = SSL_CTX_set_mode(tls_ctx, mode);
      if((f & mode) != mode)
      {
         print_error("TLS: Cannot configure context");
         res = -1;
      }
   }
   if(!res)
   {
      f = SSL_CTX_set_options(tls_ctx, options);
      if((f & options) != options)
      {
         print_error("TLS: Cannot set context options");
         res = -1;
      }
   }
   if(!res)
   {
      rv = SSL_CTX_set_cipher_list(tls_ctx, TLS_CIPHERS_DEFAULT);
      if(1 != rv)
      {
         print_error("TLS: No usable ciphers with EDH key agreement available, "
                     "check your OpenSSL configuration");
         res = -1;
      }
   }
   if(!res)
   {
      SSL_CTX_set_verify(tls_ctx, SSL_VERIFY_NONE, NULL);  /* We do it manually */
      para = X509_VERIFY_PARAM_new();
      if(NULL == para)
      {
         print_error("TLS: Memory allocation for X.509 parameters failed ");
         res = -1;
      }
      else
      {
         rv = X509_VERIFY_PARAM_set_flags(para, parameters);
         if(1 != rv)
         {
            print_error("TLS: Cannot set X.509 parameters");
            res = -1;
         }
         if(!res)
         {
            rv = X509_VERIFY_PARAM_set_purpose(para, X509_PURPOSE_SSL_SERVER);
            if(1 != rv)
            {
               print_error("TLS: Cannot set X.509 verification purpose");
               res = -1;
            }
         }
         if(!res)
         {
            rv = SSL_CTX_set1_param(tls_ctx, para);
            if(1 != rv)
            {
               print_error("TLS: Cannot store X.509 parameters in TLS context");
               res = -1;
            }
         }
         X509_VERIFY_PARAM_free(para);
      }
   }
#  if CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG
   if(!res)
   {
      /* Create configuration context */
      cctx = SSL_CONF_CTX_new();
      if(NULL == cctx)
      {
         print_error("TLS: Cannot create TLS configuration context");
         res = -1;
      }
      else
      {
         /* Init configuration context */
         SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE | SSL_CONF_FLAG_CLIENT
                                | SSL_CONF_FLAG_SHOW_ERRORS);
         /* Assign current TLS context to configuration context */
         SSL_CONF_CTX_set_ssl_ctx(cctx, tls_ctx);
         /* Set signature algorithms for TLSv1.2 */
         rv = SSL_CONF_cmd(cctx, "SignatureAlgorithms", TLS_SIGALGS);
         if(2 != rv)
         {
            print_error("TLS: Cannot store TLSv1.2 signature algorithms");
            res = -1;
         }
         else
         {
            /* Attention: The OpenSSL documentation is wrong! */
            rv = SSL_CONF_CTX_finish(cctx);
            if(1 != rv)
            {
               print_error("TLS: Cannot store configuration context");
               res = -1;
            }
         }
      }
   }
#  endif  /* CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG */

#  if CFG_USE_TLS_OWNCERTS
   /* Use local root certificate on request (must be installed manually) */
   if(!res)  { res = tls_get_certpath(&cpp); }
   if(!res)
   {
      tls_certpath = cpp;
      printf("%s: TLS: Location of X.509 root certificates: %s/\n",
             CFG_NAME, tls_certpath);
      rv = SSL_CTX_load_verify_locations(tls_ctx, NULL, tls_certpath);
      if(1 != rv)
      {
         print_error("TLS: Setup of certificate verification failed");
         res = -1;
      }
   }
#  else  /* CFG_USE_TLS_OWNCERTS */
   /* Use default certificate location of OpenSSL */
   if(!res)
   {
      rv = SSL_CTX_set_default_verify_paths(tls_ctx);
      if(1 != rv)
      {
         print_error("TLS: Setup of certificate verification failed");
         res = -1;
      }
   }
#  endif  /* CFG_USE_TLS_OWNCERTS */

#  if CFG_USE_TLS_CRLS
   /* Use CRLs vor certificate revocation checks */
   if(!res)  { res = tls_get_crlpath(&cpp); }
   if(!res)
   {
      tls_crlpath = cpp;
      printf("%s: TLS: Location of X.509 CRLs: %s/\n", CFG_NAME, tls_crlpath);
   }
   if(!res)
   {
      look = X509_STORE_add_lookup(SSL_CTX_get_cert_store(tls_ctx),
                                   X509_LOOKUP_hash_dir());
      if(NULL == look)
      {
         print_error("TLS: Cannot add CRL lookup method to TLS context");
         res = -1;
      }
   }
   if(!res)
   {
      rv = X509_LOOKUP_add_dir(look, tls_crlpath, X509_FILETYPE_PEM);
      if(1 != rv)
      {
         print_error("TLS: Cannot set CRL path");
         res = -1;
      }
   }
#  else  /* CFG_USE_TLS_CRLS */
   /* Do not check revocation of certificates */
   if(!res)
   {
      printf("%s: TLS: Warning: X.509 certificate revocation checks disabled\n",
             CFG_NAME);
   }
#  endif  /* CFG_USE_TLS_CRLS */

   /* For code review: The Cert/CRL paths are destroyed with the context! */
   return(res);
}


/* ========================================================================== */
/* Destroy context for TLS connection */

static void  tls_destroy_context(void)
{
#  if CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG
   /* Note: 'cctx' is allowed to be 'NULL' here */
   SSL_CONF_CTX_free(cctx);
#  endif  /* CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG */

   if(NULL != tls_ctx)
   {
      SSL_CTX_free(tls_ctx);
      /* Reference count is now zero */
      tls_ctx = NULL;
   }

   posix_free((void*) tls_certpath);
#  if CFG_USE_TLS_CRLS
   posix_free((void*) tls_crlpath);
#  endif  /* CFG_USE_TLS_CRLS */
}


/* ========================================================================== */
/*! \brief Init TLS subsystem
 *
 * \return
 * - 0 on success
 * - Negative value on error
 */

int  tls_init(void)
{
   int  res = 0;

   /* Create a locale object with LC_CTYPE == POSIX */
   tls_loc_ctype_posix = posix_newlocale(POSIX_LC_CTYPE_MASK, "POSIX",
                                         (posix_locale_t) 0);
   if((posix_locale_t) 0 == tls_loc_ctype_posix)
   {
      print_error("TLS: Cannot create locale object");
      res = -1;
   }

   if(!res)
   {
      /* Load strings for human readable error messsages */
      SSL_load_error_strings();

      /* Register available ciphers and digests */
      SSL_library_init();

      /* Ensure that Pseudo Random Number Generator (PRNG) is seeded */
      if(!RAND_status())
      {
         print_error("TLS: Seeding PRNG failed, "
                     "check your OpenSSL configuration");
         res = -1;
      }
   }

   return(res);
}


/* ========================================================================== */
/*! \brief Shutdown TLS subsystem */

void  tls_exit(void)
{
   if((posix_locale_t) 0 != tls_loc_ctype_posix)
   {
      posix_freelocale(tls_loc_ctype_posix);
   }
   EVP_cleanup();
}


/* ========================================================================== */
/*! \brief Check TLS subsystem for known vulnerabilities
 *
 * \attention
 * Because the TLS subsystem may use dynamically linked OpenSSL libraries, this
 * check can't be done at compile time!
 *
 * OpenSSL version numbers since 0.9.5 are composed as follows:
 *
 * 0xMNNFFPPS (Major miNor Fix Patch Status)
 * <br>
 * <hr>
 * 0x00906023 (0.9.6b beta 3)
 * <br>
 * 0x1000107F (1.0.1g release)
 * <br>
 * 0x20000000 (2.0.0 dev, reported by LibreSSL)
 *
 * \note
 * LibreSSL always reports the same OpenSSL version.
 *
 * \return
 * - 0 on success
 * - Negative value if TLS subsystem may use code with known vulnerabilities
 */

int  tls_vulnerability_check(void)
{
   int  res = 0;
   /* Using function instead of the constant is required for shared library */
#  if CFG_USE_OPENSSL_API_1_1
   unsigned long int  version = OpenSSL_version_num();
#  else   /* CFG_USE_OPENSSL_API_1_1 */
   unsigned long int  version = SSLeay();
#  endif   /* CFG_USE_OPENSSL_API_1_1 */
   unsigned int  major = (unsigned int) ((version & 0xF0000000UL) >> 28);
   unsigned int  minor = (unsigned int) ((version & 0x0FF00000UL) >> 20);
   unsigned int  fix   = (unsigned int) ((version & 0x000FF000UL) >> 12);
   unsigned int  patch = (unsigned int) ((version & 0x00000FF0UL) >> 4);
   char  patch_letter;
   char  patch_string[4] = { 0, 0, 0, 0 };

   /* Print OpenSSL version */
   if(patch)
   {
      if(25U >= patch)
      {
         /* Single character */
         patch_letter = (char) (0x60U + patch);
      }
      else if(50U >= patch)
      {
         /* Second character */
         patch_letter = (char) (0x60U + patch - 25U);
         patch_string[1] = patch_letter;
         /* First character */
         patch_letter = 'z';
      }
      else if(75U >= patch)
      {
         /* Third character */
         patch_letter = (char) (0x60U + patch - 50U);
         patch_string[2] = patch_letter;
         /* Second character */
         patch_string[1] = 'z';
         /* First character */
         patch_letter = 'z';
      }
      else
      {
         /* This will hopefully never be required */
         patch_letter = '?';
      }
      patch_string[0] = patch_letter;
   }
#  ifdef LIBRESSL_VERSION_TEXT
   printf("%s: TLS: Compiled for LibreSSL\n", CFG_NAME);
#  endif  /* LIBRESSL_VERSION_TEXT */
   printf("%s: TLS: OpenSSL library version: %X.%X.%X%s (0x%08lX)\n",
          CFG_NAME, major, minor, fix, patch_string, version);

   /*
    * Check for potential certificate check "#OprahSSL" bug (CVE-2015-1793)
    * in OpenSSL (Vulnerable versions: 1.0.1n, 1.0.1o, 1.0.2b and 1.0.2c)
    */
   if(1U == major && 0U == minor)
   {
      if( (1U == fix && 0xEU <= patch && 0xFU >= patch)
          || (2U == fix && 0x2U <= patch && 0x3U >= patch) )
      {
         printf("%s: TLS: OpenSSL library possibly vulnerable to #OprahSSL!\n",
                CFG_NAME);
         res = -1;
      }
   }

   /*
    * Check for potential heartbeat "Heartbleed" bug (CVE-2014-0160) in OpenSSL
    * (Vulnerable versions: 1.0.1 to 1.0.1f)
    */
   if(1U == major && 0U == minor && 1U == fix && 7U > patch)
   {
      printf("%s: TLS: OpenSSL library possibly vulnerable to Heartbleed!\n",
             CFG_NAME);
      res = -1;
   }

   /*
    * Check for potential MITM vulnerability (CVE-2014-0224) in OpenSSL
    * (Vulnerable versions: 1.0.0 to 1.0.0l and 1.0.1 to 1.0.1g)
    */
   if(1U == major && 0U == minor)
   {
      if( (0U == fix && 0x0DU > patch) || (1U == fix && 8U > patch) )
      {
         printf("%s: TLS: "
                "OpenSSL library possibly vulnerable to MITM attacks!\n",
                CFG_NAME);
         res = -1;
      }
   }

   return(res);
}


/* ========================================================================== */
/*! \brief Establish TLS encryption layer on top of open network connection
 *
 * \param[in]  sd    Socket descriptor
 * \param[out] co    Pointer to connection object pointer
 * \param[in]  weak  Offer weak cipher suite(s) to server
 *
 * The socket descriptor \e sd must be assigned to an open connection.
 *
 * On success, a pointer to the connection object is stored at the location
 * pointed by \e co .
 *
 * \attention
 * RFC5246 specify the cipher suite RSA-RSA-AES128-SHA (0x00, 0x2F) as
 * mandatory.
 * RFC4642 specify the cipher suite RSA-RSA-RC4-MD5 (0x00, 0x04) as mandatory.
 * Both will provide no forward secrecy and the latter one in addition use the
 * weak RC4 symmetric encryption algorithm.
 * Therefore they are not included in the default list of ciphers we offer to
 * the server.
 *
 * \note
 * RFC4642 recommends to offer the cipher suite EDH-DSS-3DES-SHA (0x00, 0x13).
 * It was removed from the default list of ciphers we offer to the server
 * because is uses the DSA algorithm for authentication.
 *
 * To comply with RFC5246/RFC4642 and nevertheless offer the disabled cipher
 * suites, or if the server don't support DHE key exchange, \e weak must be set
 * to a nonzero value.
 *
 * If OpenSSL API 1.1 is available, the parameters from the server are verified
 * to be at least 1024 bits in size for DHE key exchange (if \e weak is set to
 * zero).
 *
 * If OpenSSL API 1.1 is available and TLSv1.2 protocol is used, the signature
 * algorithm to use for the DHE key exchange is negotiated with the server. The
 * algorithms we offer are defined with \ref TLS_SIGALGS .
 *
 * \return
 * - 0 on success
 * - Negative value on error
 */

int  tls_open(int  sd, void**  co, int  weak)
{
   int  res = -1;
   int  rv;
   SSL**  ssl;
   unsigned long int  error;
   const char*  sp = NULL;
   const char*  cp = NULL;
   char  b[200];
#  if CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_DHPARAM_CHECK
   EVP_PKEY*  key;
   int  length;
#  endif  /* CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_DHPARAM_CHECK */
#  if CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG
   int  id;
#  endif  /* CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG */

   if(NULL != co)
   {
      ssl = (SSL**) co;

      /* Create context */
      res = tls_create_context();

      /* Create connection object */
      if(!res)
      {
         *ssl = SSL_new(tls_ctx);
         if(NULL == *ssl)
         {
            print_error("TLS: Cannot create connection object");
            res = -1;
         }
      }

      /* RFC4642 specify this weak cipher suite as mandatory */
      if(!res && weak)
      {
         rv = SSL_set_cipher_list(*ssl, TLS_CIPHERS_WEAK);
         if(1 != rv)
         {
            print_error("TLS: Adding weak cipher suite from RFC4642 failed");
            res = -1;
         }
         else
         {
            printf("%s: TLS: Warning: Offering weak cipher suites to server\n",
                   CFG_NAME);
         }
      }

      /* Attach connection object to socket */
      if(!res)
      {
         rv = SSL_set_fd(*ssl, sd);
         if(1 != rv)
         {
            print_error("TLS: Cannot assign connection object to socket");
            res = -1;
         }
      }

      /* Initiate TLS handshake */
      if(!res)
      {
         rv = SSL_connect(*ssl);
         if(1 != rv)
         {
            /* Assignment in truth expression is intended */
            while((error = ERR_get_error()))
            {
               sp = ERR_reason_error_string(error);
               /* printf("Errorcode: %d\n", ERR_GET_REASON(error)); */
               posix_snprintf(b, 200,
                              "TLS: Failed to establish TLS connection (%s)",
                              sp);
               print_error(b);
            }
            res = -1;
         }
         else
         {
            /* Print cipher suite */
            res = tls_get_ciphersuite((void**) ssl, &sp, &cp);
            if(res)  { print_error("TLS: Reading negotiation result failed"); }
            else
            {
               printf("%s: TLS: Using protocol: %s with cipher suite %s\n",
                      CFG_NAME, sp, cp);
#  if CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG
               if(SSL_get_peer_signature_nid(*ssl, &id))
               {
                  printf("%s: TLS: Signature algorithm for key exchange: "
                         "RSA-%s\n", CFG_NAME, OBJ_nid2sn(id));
               }
               else
               {
                  /* '!weak' indicates DHE key exchange here */
                  /* Up to TLSv1.1 the signature algorithm is not selectable */
                  if(!weak && strcmp("TLSv1", sp) && strcmp("TLSv1.1", sp))
                  {
                     print_error("TLS: Key exchange signature algorithm "
                                 "negotiation failed");
                     res = -1;
                  }
               }
#  else  /* CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG */
               if(!weak && strcmp("TLSv1", sp) && strcmp("TLSv1.1", sp))
               {
                  printf("%s: TLS: Warning: Key exchange signature algorithm "
                         "negotiation not possible with OpenSSL API 1.0\n",
                         CFG_NAME);
               }
#  endif  /* CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_SETSIGALG */
            }
#  if CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_DHPARAM_CHECK
            /* '!weak' indicates DHE key exchange here */
            if(!weak)
            {
               /* Check DH parameters */
               rv = SSL_get_server_tmp_key(*ssl, &key);
               if(!rv)  { res = -1; }
               if(!res)
               {
                  if(EVP_PKEY_DH != EVP_PKEY_id(key))  { res = -1; }
                  else
                  {
                     length = EVP_PKEY_bits(key);
                     if(0 >= length)  { res = -1; }
                  }
               }
               if(!res)
               {
                  printf("%s: TLS: DH parameter size: %d bit\n",
                         CFG_NAME, length);
                  if(1024 > length)
                  {
                     print_error("TLS: DH parameter size rejected");
                     res = -1;
                  }
                  else if(3072 > length)
                  {
                     /* 3072 bit would match a 128 bit symmetric cipher */
                     printf("%s: TLS: Warning: DH parameters should be"
                            " at least 3072 bit\n", CFG_NAME);
                  }
               }
               /* Close connection on error */
               if(res)
               {
                  print_error("TLS: DH parameter check failed");
                  tls_close((void*) ssl);
               }
            }
#  else  /* CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_DHPARAM_CHECK */
            printf("%s: TLS: Warning: DH parameter check not possible with "
                   "OpenSSL API 1.0\n", CFG_NAME);
#  endif  /* CFG_USE_OPENSSL_API_1_1 || CFG_USE_TLS_DHPARAM_CHECK */
         }
      }
   }

   return(res);
}


/* ========================================================================== */
/*! \brief Terminate TLS encryption layer on top of network connection
 *
 * \param[in,out] co  Pointer to connection object pointer
 *
 * \note
 * This function also destroys the TLS context that contains the certificate
 * CRLs. After CRLs are updated, it is not required to shutdown the whole module
 * via \ref tls_exit() . Calling this function is sufficient to invalidate any
 * CRLs in memory.
 *
 * \return
 * - 1 on success
 * - Negative value on error
 */

int  tls_close(void**  co)
{
   int  res;

   /* Close connection */
   res = SSL_shutdown(*((SSL**) co));
   if(!res)  { res = SSL_shutdown(*((SSL**) co)); }

   /* Destroy context (including buffered CRLs) */
   tls_destroy_context();

   /* Check for error */
   if(0 <= res && 1 != res)
   {
      print_error("TLS: Error while closing TLS connection");
      res = -1;
   }

   return(res);
}


/* ========================================================================== */
/*! \brief Get protocol and cipher suite name that was negotiated for connection
 *
 * \param[in]  co  Pointer to connection object pointer
 * \param[out] pv  Pointer to protocol version string
 * \param[out] cs  Pointer to cipher suite string
 *
 * \note
 * If the caller is not interested in the information, the parameters \e pv and
 * \e cs it is allowed to pass \c NULL .
 *
 * \return
 * - 0 on success
 * - -1 on error
 */

int  tls_get_ciphersuite(void**  co, const char**  pv, const char**  cs)
{
   int  res = -1;
   SSL**  ssl;
   const SSL_CIPHER*  cp;

   ssl = (SSL**) co;
   cp = SSL_get_current_cipher(*ssl);
   if(NULL == cp)
   {
      print_error("TLS: Reading negotiated protocol and cipher suite failed");
   }
   else
   {
      /*
       * Note:
       * Calling 'SSL_CIPHER_get_version()' is wrong, because this function
       * reports the TLS version in which the used cipher suite was defined.
       * But this function should return information about the protocol used for
       * the current connection corresponding to parameter 'co'.
       */
      if(NULL != pv)  { *pv = SSL_get_version(*ssl); }
      if(NULL != cs)  { *cs = SSL_CIPHER_get_name(cp); }
      res = 0;
   }

   return(res);
}


/* ========================================================================== */
/*! \brief Check whether server has presented a certificate and verify it
 *
 * \param[in]  co    Pointer to connection object pointer
 * \param[out] cert  Pointer to cerificate pointer
 * \param[in]  cn    Common name string the certificate should contain
 * \param[in]  weak  Disable some checks for better compatibility
 *
 * \attention
 * The parameter \e weak must be set to the same value that was used for the
 * call to \ref tls_open() .
 *
 * The verification executes the following steps:
 * - Ensure that the server has presented a certificate (chain)
 * - Optional: Check the signature algorithms and key lengths used in the
 *   certificate chain (Setting \e weak to a nonzero value disables this step)
 * - Follow certificate chain back to the anchor and verify that it is trusted
 * - Optional: Verify that none of the certificates in the chain was revoked
 *   (The CRLs for the revocation checks are downloaded automatically)
 * - Verify that the subject of the certificate contain \e cn
 *
 * According to RFC4642 the following rules are applied:
 * - The matching of hostname \e cn must be case-insensitive => We do so.
 * - Wildcard \c * is allowed as left-most name component => We accept this.
 * - Hostnames listed via \c subjectAltName extension should be checked first
 *   => We do so.
 * - If the certificate contains multiple names (e.g., more than one \c dNSName
 *   field), then a match with any one of the fields is considered acceptable
 *   => We accept any match.
 *
 * If the return value is not negative, the server has presented a certificate
 * and a pointer to it was written to \e cert .
 *
 * \note
 * The returned value for \e cert stay in memory until the connection object
 * \e co is destroyed.
 *
 * The return value \c -2 should be interpreted as a request to close the
 * connection and re-establish it again (and then using the new CRLs).
 *
 * \return
 * - 0 on success (Certificate was presented, is valid and trusted)
 * - 1 if the certificate is not trusted
 * - -1 on error
 * - -2 if the verification was not executed because CRLs have been updated
 */

int  tls_cert_verify(void**  co, void**  cert, const char*  cn, int  weak)
{
   int  res = 0;
   SSL**  ssl = (SSL**) co;
   STACK_OF(X509)*  certificate_chain = NULL;
   X509*  certificate = NULL;
   X509_NAME*  subject;
   X509_NAME_ENTRY*  common_name;
   ASN1_STRING*  common_name_string;
   BIO*  biop = BIO_new(BIO_s_mem());
   long int  rv;
   const char*  sp = NULL;
   char  b[256];
   const char*  p = NULL;
   int  i = -1;
   GENERAL_NAMES*  an = NULL;
   GENERAL_NAME*  ani;
   int  num;
   int  match = 0;
   const char*  pv;
   int  proto = 0;

   /* Extract whole certificate chain from connection object */
   certificate_chain = SSL_get_peer_cert_chain(*ssl);
   if(NULL == certificate_chain)
   {
      print_error("TLS: Extracting certificate chain failed");
      res = -1;
   }
   if(!res && weak)
   {
      printf("%s: TLS: Warning: Using weak certificate chain checks\n",
             CFG_NAME);
   }

   /* Update CRLs for certificate chain */
#  if CFG_USE_TLS_CRLS
   if(!res && !tls_crl_update_skip && tls_check_crl_age())
   {
      res = tls_update_crls(certificate_chain);
      if(0 > res)  { print_error("TLS: Updating of CRLs failed"); }
      else
      {
         /* CRLs successfully updated */
         tls_set_crl_age();
         if(0 < res)
         {
            /* Self signed root certificate */
            print_error("TLS: No CRLs required");
            res = 0;
         }
         else
         {
            /* Request to close and reopen connection */
            print_error("TLS: Verification of certificate aborted because of "
                        "CRL update");
            SSL_get_verify_result(*ssl);
            res = -2;
         }
      }
   }
#  endif  /* CFG_USE_TLS_CRLS */

   /* Check algorithms and key length of signatures in certificate chain */
   if(!res && !weak)
   {
      res = tls_get_ciphersuite((void**) ssl, &pv, NULL);
      if(res)  { print_error("TLS: Reading protocol version failed"); }
      else
      {
         /* Check for TLSv1.2 or higher */
         if(strcmp("TLSv1", pv) && strcmp("TLSv1.1", pv))  { proto = 1; }
      }
      if(!res)  { res = tls_chain_check_sigalgs(certificate_chain, proto); }
   }

   /* Destroy certificate chain */
   if(NULL != certificate_chain)
   {
      sk_X509_pop_free(certificate_chain, X509_free);
   }

   /* Verify server certificate */
   if(!res)
   {
      certificate = SSL_get_peer_certificate(*ssl);
      if(NULL == certificate)
      {
         print_error("TLS: Server has presented no certificate");
         res = -1;
      }
      else
      {
         /* Certificate present */
         res = 1;
         *cert = (void*) certificate;
         /* Let OpenSSL check whether the certificate is trusted */
         rv = SSL_get_verify_result(*ssl);
         if(X509_V_OK != rv)
         {
            sp = X509_verify_cert_error_string(rv);
            /* printf("Errorcode: %d\n", ERR_GET_REASON(rv)); */
            posix_snprintf(b, 256,
                           "TLS: Alert: Server X.509 certificate verification "
                           "failed (%s)", sp);
            print_error(b);
#  if CFG_USE_TLS_CRLS
            if(X509_V_ERR_UNABLE_TO_GET_CRL == rv
               || X509_V_ERR_CRL_HAS_EXPIRED == rv)
            {
               /* Reset CRL update timestamp to force new CRL update */
               print_error("TLS: Resetting CRL update timestamp");
               tls_reset_crl_age();
            }
#  endif  /* CFG_USE_TLS_CRLS */
         }
         else
         {
            /* Verify subject */
            subject = X509_get_subject_name(certificate);
            if(NULL == subject)
            {
               print_error("TLS: Extraction of subject from X.509 certificate "
                           "failed");
            }
            else
            {
               printf("%s: TLS: Server certificate:\n", CFG_NAME);
               /* Extract alternative names first and compare with cn */
               an = X509_get_ext_d2i(certificate, NID_subject_alt_name,
                                     NULL, NULL);
               if(NULL != an)
               {
                  num = sk_GENERAL_NAME_num(an);
                  for(i = 0; i < num; ++i)
                  {
                     ani = sk_GENERAL_NAME_value(an, i);
                     if(GEN_DNS == ani->type)
                     {
#  if CFG_USE_OPENSSL_API_1_1
                        p = (const char*) ASN1_STRING_get0_data(ani->d.dNSName);
#  else   /* CFG_USE_OPENSSL_API_1_1 */
                        /* This function is deprecated since API 1.1 */
                        p = (const char*) ASN1_STRING_data(ani->d.dNSName);
#  endif   /* CFG_USE_OPENSSL_API_1_1 */
                        printf("%s: TLS:    Subject Alternative Name: %s",
                               CFG_NAME, p);
                        if(!tls_subject_check(cn, p))  { match = 1;  break; }
                     }
                  }
                  GENERAL_NAMES_free(an);
               }
               /* Extract common names from subject and compare with cn */
               if(!match)
               {
                  while(1)
                  {
                     i = X509_NAME_get_index_by_NID(subject, NID_commonName, i);
                     if (-1 == i)  { break; }
                     common_name = X509_NAME_get_entry(subject, i);
                     common_name_string = X509_NAME_ENTRY_get_data(common_name);
#  if CFG_USE_OPENSSL_API_1_1
                     p = (const char*) ASN1_STRING_get0_data(common_name_string);
#  else   /* CFG_USE_OPENSSL_API_1_1 */
                     /* This function is deprecated since API 1.1 */
                     p = (const char*) ASN1_STRING_data(common_name_string);
#  endif   /* CFG_USE_OPENSSL_API_1_1 */
                     printf("%s: TLS:    Subject Name: %s", CFG_NAME, p);
                     if(!tls_subject_check(cn, p))  { match = 1;  break; }
                  }
               }
               if(!match)
               {
                  posix_snprintf(b, 256,
                                 "TLS: Alert: Server X.509 certificate "
                                 "verification failed "
                                 "(Subject doesn't match hostname %s)", cn);
                  print_error(b);
               }
               else
               {
                  /* We trust the certificate and certificate match hostname */
#  if CFG_USE_TLS_CRLS
                  printf("%s: TLS: Server certificate verification "
                         "successful\n", CFG_NAME);
#  else  /* CFG_USE_TLS_CRLS */
                  printf("%s: TLS: Server certificate verification successful "
                         "(revocation checks were skipped)\n", CFG_NAME);
#  endif  /* CFG_USE_TLS_CRLS */
                  res = 0;
               }
            }
         }
      }
   }

   /* Clean up */
   if(NULL != certificate)  { X509_free(certificate); }
   if(NULL != biop)  { BIO_free(biop); }

   return(res);
}


/* ========================================================================== */
/*! \brief Print certificate
 *
 * \param[in]  cert  Pointer to cerificate
 * \param[out] cbuf  Pointer buffer pointer
 *
 * On success, a memory block is allocated for the certificate and a pointer to
 * it is written to \e cbuf. The caller is responsible to free the memory block
 * allocated for the certificate.
 *
 * \return
 * - 0 on success
 * - -1 on error
 */

int  tls_cert_get_string(void*  cert, const char**  cbuf)
{
   int  res = -1;
   X509*  certificate;
   BIO*  biop = BIO_new(BIO_s_mem());
   int  rv;
   char*  p = NULL;
   size_t  len;
   char*  buf = NULL;

   certificate = (X509*) cert;
   rv = X509_print(biop, certificate);
   if(1 == rv)
   {
      len = (size_t) BIO_get_mem_data(biop, &p);
      if(NULL != p && len)
      {
         buf = (char*) posix_malloc(len + (size_t) 1);
         if(NULL != buf)
         {
            memcpy((void*) buf, (void*) p, len);
            buf[len] = 0;
            *cbuf = buf;
            res = 0;
         }
      }
   }

   /* Clean up */
   if(NULL != biop)  { BIO_free(biop); }
   if(res)  { posix_free((void*) buf); }

   return(res);
}


/* ========================================================================== */
/*! \brief Send data
 *
 * \param[in] co   Pointer to connection object pointer
 * \param[in] buf  Pointer to data buffer
 * \param[in] len  Number of octets to send
 *
 * \return
 * - Number of bytes sent
 * - -1 on error
 */

posix_ssize_t  tls_send(void*  co, const void*  buf, size_t  len)
{
   posix_ssize_t  res = -1;
   int  num;

   /* Check size */
   if(INT_MAX < len)  { print_error("TLS: tls_send(): Data length too large"); }
   num = (int) len;

   res = (posix_ssize_t) SSL_write((SSL*) co, buf, num);
   if(!res)  { res = -1; }

   return(res);
}


/* ========================================================================== */
/*! \brief Receive data
 *
 * \param[in]  co    Pointer to connection object pointer
 * \param[out] buf   Pointer to data buffer
 * \param[in]  len   Number of octets to receive
 * \param[in]  peek  Data should stay available for reading if nonzero
 *
 * \attention
 * The \e peek flag doesn't work anymore since OpenSSL 1.1.0 and must be zero
 * in this case (otherwise a deadlock will occur).
 *
 * \return
 * - Number of bytes received
 * - -1 on error
 */

posix_ssize_t  tls_recv(void*  co, void*  buf, size_t  len, int  peek)
{
   posix_ssize_t  res = -1;
   int  num;

   /* Check size */
   if(INT_MAX < len)  { print_error("TLS: tls_recv(): Data length too large"); }
   num = (int) len;

   /* Check for peek flag */
   if(peek)
   {
#  if CFG_USE_OPENSSL_API_1_1
      print_error("TLS: tls_recv(): "
                  "Flag 'peek' not supported with OpenSSL 1.1.x");
#  else   /* CFG_USE_OPENSSL_API_1_1 */
      res = (posix_ssize_t) SSL_peek((SSL*) co, buf, num);
#  endif   /* CFG_USE_OPENSSL_API_1_1 */
   }
   else  { res = (posix_ssize_t) SSL_read((SSL*) co, buf, num); }
   if(!res)  { res = -1; }

   return(res);
}


/* ========================================================================== */
/*! \brief Check whether CRL update interval has elapsed
 *
 * This function is exported for the UI module to check the CRL update interval
 * and give the user a choice to delay the CRL updates.
 * The function \ref tls_crl_update_control() is intended to enable and disable
 * the automatic CRL updates.
 *
 * \note
 * The update choice can be disabled with configuration option \c CONF_CRL_UPD_C
 * (if configured to zero, this function will alays return zero).
 *
 * \return
 * - 0 Do nothing
 * - 1 CRL update interval elapsed. Give user a choice to suppress update
 * - -1 on error
 */

int  tls_crl_update_check(void)
{
   int  res = 0;

#  if CFG_USE_TLS_CRLS
   if(config[CONF_CRL_UPD_C].val.i)  { res = tls_check_crl_age(); }
#  endif  /* CFG_USE_TLS_CRLS */

   return(res);
}


/* ========================================================================== */
/*! \brief Enable or disable automatic CRL updates
 *
 * \param[in] crl_upd_disable  Disable automatic CRL updates if nonzero
 *
 * \attention
 * If CRLs are used, they need to be present and valid! With this function the
 * update of CRLs can only be suppressed until they expire. After expiration of
 * the CRLs a TLS connection can no longer be established (if a certificate
 * chain is used for authentication).
 *
 * \note
 * When disabled, the CRL updates can be reenabled on the fly at any time by
 * calling this function again with \e crl_upd_disable set to zero.
 */

void  tls_crl_update_control(int crl_upd_disable)
{
#  if CFG_USE_TLS_CRLS
   /* Store CRL update flag */
   tls_crl_update_skip = crl_upd_disable;
#  endif  /* CFG_USE_TLS_CRLS */
}


/* ========================================================================== */
/*! \brief Free an object allocated by TLS module
 *
 * Use this function to release dynamic memory that was allocated by the
 * TLS module.
 *
 * \param[in] p  Pointer to object
 *
 * Release the memory for the object pointed to by \e p.
 *
 * \note
 * The pointer \e p is allowed to be \c NULL and no operation is performed in
 * this case.
 */

void  tls_free(void*  p)
{
   posix_free(p);
}


#endif  /* CFG_USE_TLS */


/* @} */

/* EOF */
