libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

tls_gnu_funcs.c (29183B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2024-2025 Evgeny Grin (Karlson2k)
      5 
      6   GNU libmicrohttpd is free software; you can redistribute it and/or
      7   modify it under the terms of the GNU Lesser General Public
      8   License as published by the Free Software Foundation; either
      9   version 2.1 of the License, or (at your option) any later version.
     10 
     11   GNU libmicrohttpd is distributed in the hope that it will be useful,
     12   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14   Lesser General Public License for more details.
     15 
     16   Alternatively, you can redistribute GNU libmicrohttpd and/or
     17   modify it under the terms of the GNU General Public License as
     18   published by the Free Software Foundation; either version 2 of
     19   the License, or (at your option) any later version, together
     20   with the eCos exception, as follows:
     21 
     22     As a special exception, if other files instantiate templates or
     23     use macros or inline functions from this file, or you compile this
     24     file and link it with other works to produce a work based on this
     25     file, this file does not by itself cause the resulting work to be
     26     covered by the GNU General Public License. However the source code
     27     for this file must still be made available in accordance with
     28     section (3) of the GNU General Public License v2.
     29 
     30     This exception does not invalidate any other reasons why a work
     31     based on this file might be covered by the GNU General Public
     32     License.
     33 
     34   You should have received copies of the GNU Lesser General Public
     35   License and the GNU General Public License along with this library;
     36   if not, see <https://www.gnu.org/licenses/>.
     37 */
     38 
     39 /**
     40  * @file src/mhd2/tls_gnu_funcs.c
     41  * @brief  The implementation of GnuTLS wrapper functions
     42  * @author Karlson2k (Evgeny Grin)
     43  */
     44 
     45 #include "mhd_sys_options.h"
     46 
     47 #include "sys_bool_type.h"
     48 #include "sys_base_types.h"
     49 
     50 #include "compat_calloc.h"
     51 #include "sys_malloc.h"
     52 #include <string.h>
     53 
     54 #ifdef mhd_USE_TLS_DEBUG_MESSAGES
     55 #  include <stdio.h> /* For TLS debug printing */
     56 #endif
     57 
     58 #include "mhd_assert.h"
     59 #include "mhd_assume.h"
     60 
     61 #include "mhd_socket_type.h"
     62 #include "mhd_str_types.h"
     63 
     64 #include "mhd_str_macros.h"
     65 #include "mhd_arr_num_elems.h"
     66 
     67 #include "mhd_str.h"
     68 #include "mhd_conn_socket.h"
     69 
     70 #include "mhd_tls_internal.h"
     71 
     72 #include "tls_gnu_tls_lib.h"
     73 
     74 #include "mhd_tls_ver_stct.h"
     75 
     76 #include "tls_gnu_daemon_data.h"
     77 #include "tls_gnu_conn_data.h"
     78 #include "tls_gnu_funcs.h"
     79 
     80 #include "daemon_options.h"
     81 #include "daemon_logger.h"
     82 
     83 #ifdef  mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3
     84 #  include "tls_dh_params.h"
     85 #endif
     86 
     87 #include "mhd_public_api.h"
     88 
     89 #ifdef mhd_USE_TLS_DEBUG_MESSAGES
     90 static void
     91 mhd_tls_gnu_debug_print (int level, const char *msg)
     92 {
     93   (void) fprintf (stderr, "## GnuTLS %02i: %s",
     94                   level,
     95                   msg);
     96   (void) fflush (stderr);
     97 }
     98 
     99 
    100 #  define mhd_DBG_PRINT_TLS_INFO_MSG(message) \
    101         do { (void) fprintf (stderr, "## GnuTLS info: %s\n", (message)); \
    102              (void) fflush (stderr);} while (0)
    103 #  define mhd_DBG_PRINT_TLS_INFO_PARAM1(message,param) \
    104         do { (void) fprintf (stderr, "## GnuTLS info: " message "\n", (param)); \
    105              (void) fflush (stderr);} while (0)
    106 #else  /* ! mhd_USE_TLS_DEBUG_MESSAGES */
    107 #  define mhd_DBG_PRINT_TLS_ERRS()      ERR_clear_error ()
    108 #  define mhd_DBG_PRINT_TLS_INFO_MSG(message)       ((void) 0)
    109 #  define mhd_DBG_PRINT_TLS_INFO_PARAM1(message,param)  ((void) 0)
    110 #endif /* ! mhd_USE_TLS_DEBUG_MESSAGES */
    111 
    112 #ifdef mhd_TLS_GNU_HAS_ALPN
    113 static const char mhd_alpn_str_http1_0[] = mhd_ALPN_H1_0;
    114 static const char mhd_alpn_str_http1_1[] = mhd_ALPN_H1_1;
    115 #  ifdef MHD_SUPPORT_HTTP2
    116 static const char mhd_alpn_str_http2[] = mhd_ALPN_H2;
    117 #  endif
    118 #  if 0 /* Disabled code */
    119 static const char alpn_http_3[] = mhd_ALPN_H3;
    120 #  endif
    121 static const gnutls_datum_t mhd_alpn_dat_http1_0 = {
    122   (unsigned char *) mhd_DROP_CONST (mhd_alpn_str_http1_0),
    123   mhd_SSTR_LEN (mhd_alpn_str_http1_0)
    124 };
    125 static const gnutls_datum_t mhd_alpn_dat_http1_1 = {
    126   (unsigned char *) mhd_DROP_CONST (mhd_alpn_str_http1_1),
    127   mhd_SSTR_LEN (mhd_alpn_str_http1_1)
    128 };
    129 #  ifdef MHD_SUPPORT_HTTP2
    130 static const gnutls_datum_t mhd_alpn_dat_http2 = {
    131   (unsigned char *) mhd_DROP_CONST (mhd_alpn_str_http2),
    132   mhd_SSTR_LEN (mhd_alpn_str_http2)
    133 };
    134 #  endif
    135 #endif /* mhd_TLS_GNU_HAS_ALPN */
    136 
    137 
    138 /* ** Global initialisation / de-initialisation ** */
    139 
    140 static bool gnutls_lib_inited = false;
    141 
    142 MHD_INTERNAL void
    143 mhd_tls_gnu_global_init (void)
    144 {
    145 #ifdef GNUTLS_VERSION
    146   /* Make sure that used shared GnuTLS library has least the same version as
    147      MHD was configured for. Fail if the version is earlier. */
    148   if (NULL != gnutls_check_version (GNUTLS_VERSION))
    149 #endif
    150   gnutls_lib_inited = (GNUTLS_E_SUCCESS == gnutls_global_init ());
    151 
    152 #ifdef mhd_USE_TLS_DEBUG_MESSAGES
    153   gnutls_global_set_log_function (&mhd_tls_gnu_debug_print);
    154   gnutls_global_set_log_level (2);
    155 #endif
    156 }
    157 
    158 
    159 MHD_INTERNAL void
    160 mhd_tls_gnu_global_deinit (void)
    161 {
    162 #ifdef mhd_USE_TLS_DEBUG_MESSAGES
    163   gnutls_global_set_log_level (0);
    164 #endif
    165 
    166   if (gnutls_lib_inited)
    167     gnutls_global_deinit ();
    168   gnutls_lib_inited = false;
    169 }
    170 
    171 
    172 MHD_INTERNAL MHD_FN_PURE_ bool
    173 mhd_tls_gnu_is_inited_fine (void)
    174 {
    175   return gnutls_lib_inited;
    176 }
    177 
    178 
    179 /* ** Daemon initialisation / de-initialisation ** */
    180 
    181 /**
    182  * Check application-provided daemon TLS settings
    183  * @param d the daemon handle
    184  * @param s the application-provided settings
    185  * @return #MHD_SC_OK on success,
    186  *         error code otherwise
    187  */
    188 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
    189 check_app_tls_settings (struct MHD_Daemon *restrict d,
    190                         struct DaemonOptions *restrict s)
    191 {
    192   mhd_assert (MHD_TLS_BACKEND_NONE != s->tls);
    193   mhd_assert ((MHD_TLS_BACKEND_GNUTLS == s->tls) || \
    194               (MHD_TLS_BACKEND_ANY == s->tls));
    195   if (NULL == s->tls_cert_key.v_mem_cert)
    196   {
    197     mhd_LOG_MSG (d, MHD_SC_TLS_CONF_BAD_CERT, \
    198                  "No valid TLS certificate is provided");
    199     return MHD_SC_TLS_CONF_BAD_CERT;
    200   }
    201   mhd_assert (NULL != s->tls_cert_key.v_mem_key);
    202 
    203   return MHD_SC_OK;
    204 }
    205 
    206 
    207 /**
    208  * Initialise daemon TLS Diffie-Hellman parameters.
    209  *
    210  * This function initialise Diffie-Hellman parameters for the daemon based
    211  * on GnuTLS recommended defaults.
    212  * With modern GnuTLS versions this function is no-op and always succeed.
    213  *
    214  * This function does not put any messages to the log.
    215  * @param d_tls the daemon TLS data
    216  * @return 'true' if succeed,
    217  *         'false' if failed
    218  */
    219 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ bool
    220 daemon_init_dh_data (struct mhd_TlsGnuDaemonData *restrict d_tls)
    221 {
    222 #if defined(mhd_TLS_GNU_DH_PARAMS_USE_KNOWN)
    223   /* Rely on reasonable TLS defaults set in the TLS library.
    224      Modern GnuTLS versions relies completely on RFC 7919 and do not need
    225      this function therefore do not bother implementing special
    226      application-defined settings just for limited number of GnuTLS
    227      versions (>= 3.5.6 && < 3.6.0). */
    228   return (GNUTLS_E_SUCCESS ==
    229           gnutls_certificate_set_known_dh_params (d_tls->cred,
    230                                                   GNUTLS_SEC_PARAM_MEDIUM));
    231 #elif defined(mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3)
    232   gnutls_datum_t dh_data;
    233   if (GNUTLS_E_SUCCESS  !=
    234       gnutls_dh_params_init (&(d_tls->dh_params)))
    235     return false;
    236 
    237   dh_data.data = mhd_DROP_CONST (mhd_tls_dh_params_pkcs3);
    238   dh_data.size = sizeof (mhd_tls_dh_params_pkcs3);
    239   if (GNUTLS_E_SUCCESS ==
    240       gnutls_dh_params_import_pkcs3 (d_tls->dh_params,
    241                                      &dh_data,
    242                                      GNUTLS_X509_FMT_PEM))
    243   {
    244     gnutls_certificate_set_dh_params (d_tls->cred,
    245                                       d_tls->dh_params);
    246     return true; /* success exit point */
    247   }
    248   /* Below is a clean-up code path */
    249   gnutls_dh_params_deinit (d_tls->dh_params);
    250   return false;
    251 #else
    252   (void) d_tls; /* Mute compiler warning */
    253   return true;
    254 #endif
    255 }
    256 
    257 
    258 /**
    259  * De-initialise daemon TLS Diffie-Hellman parameters.
    260  * @param d_tls the daemon TLS data
    261  */
    262 static MHD_FN_PAR_NONNULL_ALL_ void
    263 daemon_deinit_dh_data (struct mhd_TlsGnuDaemonData *restrict d_tls)
    264 {
    265 #if defined(mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3)
    266   mhd_assert (NULL != d_tls->dh_params);
    267   gnutls_dh_params_deinit (d_tls->dh_params);
    268 #else
    269   (void) d_tls; /* Mute compiler warning */
    270 #endif
    271 }
    272 
    273 
    274 /**
    275  * Set daemon TLS credentials (and Diffie-Hellman parameters).
    276  * This function puts error messages to the log if needed.
    277  * @param d the daemon handle
    278  * @param d_tls the daemon TLS settings
    279  * @param s the application-provided settings
    280  * @return #MHD_SC_OK on success,
    281  *         error code otherwise
    282  */
    283 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
    284 daemon_init_credentials (struct MHD_Daemon *restrict d,
    285                          struct mhd_TlsGnuDaemonData *restrict d_tls,
    286                          struct DaemonOptions *restrict s)
    287 {
    288   enum MHD_StatusCode ret;
    289   size_t cert_len;
    290   size_t key_len;
    291 
    292   if (GNUTLS_E_SUCCESS !=
    293       gnutls_certificate_allocate_credentials (&(d_tls->cred)))
    294   {
    295     mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \
    296                  "Failed to initialise TLS credentials for the daemon");
    297     return MHD_SC_TLS_DAEMON_INIT_FAILED;
    298   }
    299 
    300   // TODO: Support multiple certificates
    301   cert_len = strlen (s->tls_cert_key.v_mem_cert); // TODO: Reuse calculated length
    302   key_len = strlen (s->tls_cert_key.v_mem_key);   // TODO: Reuse calculated length
    303 
    304   mhd_assert (0 != cert_len);
    305   mhd_assert (0 != key_len);
    306 
    307   if ((cert_len != (unsigned int) cert_len)
    308       || (key_len != (unsigned int) key_len))
    309     ret = MHD_SC_TLS_CONF_BAD_CERT; /* Very unlikely, do not waste space on special message */
    310   else
    311   {
    312     gnutls_datum_t cert_data;
    313     gnutls_datum_t key_data;
    314     int res;
    315 
    316     cert_data.data =
    317       (unsigned char *) mhd_DROP_CONST (s->tls_cert_key.v_mem_cert);
    318     cert_data.size = (unsigned int) cert_len;
    319     key_data.data =
    320       (unsigned char *) mhd_DROP_CONST (s->tls_cert_key.v_mem_key);
    321     key_data.size = (unsigned int) key_len;
    322     res = gnutls_certificate_set_x509_key_mem2 (d_tls->cred,
    323                                                 &cert_data,
    324                                                 &key_data,
    325                                                 GNUTLS_X509_FMT_PEM,
    326                                                 s->tls_cert_key.v_mem_pass,
    327                                                 0);
    328     if (0 > res)
    329     {
    330       mhd_LOG_PRINT (d, \
    331                      MHD_SC_TLS_CONF_BAD_CERT, \
    332                      mhd_LOG_FMT ("Failed to set the provided " \
    333                                   "TLS certificate: %s"),
    334                      gnutls_strerror (res));
    335       ret = MHD_SC_TLS_CONF_BAD_CERT;
    336     }
    337     else
    338     {
    339       if (! daemon_init_dh_data (d_tls))
    340       {
    341         mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \
    342                      "Failed to initialise Diffie-Hellman parameters " \
    343                      "for the daemon");
    344         ret = MHD_SC_TLS_DAEMON_INIT_FAILED;
    345       }
    346       else
    347         return MHD_SC_OK;
    348     }
    349   }
    350 
    351   gnutls_certificate_free_credentials (d_tls->cred);
    352   mhd_assert (MHD_SC_OK != ret);
    353   return ret; /* Failure exit point */
    354 }
    355 
    356 
    357 /**
    358  * Free daemon fully allocated credentials (and Diffie-Hellman parameters).
    359  * @param d_tls the daemon TLS settings
    360  */
    361 static MHD_FN_PAR_NONNULL_ALL_ void
    362 daemon_deinit_credentials (struct mhd_TlsGnuDaemonData *restrict d_tls)
    363 {
    364   mhd_assert (NULL != d_tls->cred);
    365   /* To avoid dangling pointer to DH data in the credentials,
    366      free credentials first and then free DH data. */
    367   gnutls_certificate_free_credentials (d_tls->cred);
    368   daemon_deinit_dh_data (d_tls);
    369 }
    370 
    371 
    372 /**
    373  * Try to set specified GnuTLS priorities string
    374  * @param prio_string the priorities string
    375  * @param d_tls the daemon TLS settings
    376  * @return GNUTLS_E_SUCCESS on success,
    377  *         GnuTLS error code otherwise
    378  */
    379 static MHD_FN_PAR_CSTR_ (1) MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_INOUT_ (2) int
    380 try_prio_string (const char *restrict prio_string,
    381                  struct mhd_TlsGnuDaemonData *restrict d_tls)
    382 {
    383   int res;
    384   mhd_DBG_PRINT_TLS_INFO_PARAM1 ("Trying '%s' priorities",
    385                                  prio_string ? prio_string : "[NULL]");
    386 
    387   res = gnutls_priority_init (&(d_tls->pri_cache),
    388                               prio_string,
    389                               NULL);
    390 
    391   if (GNUTLS_E_SUCCESS == res)
    392   {
    393     mhd_DBG_PRINT_TLS_INFO_PARAM1 ("Initialised with '%s' priorities",
    394                                    prio_string ? prio_string : "[NULL]");
    395     return res;
    396   }
    397 
    398   mhd_DBG_PRINT_TLS_INFO_PARAM1 ("Failed initialise priorities: %s",
    399                                  gnutls_strerror (res));
    400 
    401   return res;
    402 }
    403 
    404 
    405 static const struct MHD_StringNullable tlsgnulib_base_priorities[] = {
    406   /* Do not use "multi-keyword": if the first configuration is found, but has
    407      some error, the next configuration is not tried. */
    408 #if 0 /* ifdef mhd_TLS_GNU_SUPPORTS_MULTI_KEYWORDS_PRIORITY */
    409   mhd_MSTR_INIT ("@LIBMICROHTTPD,SYSTEM")
    410 #else
    411   mhd_MSTR_INIT ("@LIBMICROHTTPD")
    412   ,
    413   mhd_MSTR_INIT ("@SYSTEM")
    414 #endif
    415   ,
    416   {0, NULL}
    417   ,
    418   mhd_MSTR_INIT ("NORMAL")
    419 };
    420 
    421 /**
    422  * Initialise GnuTLS priorities cache
    423  * @param d the daemon handle
    424  * @param d_tls the daemon TLS settings
    425  * @param s the application-provided settings
    426  * @return #MHD_SC_OK on success,
    427  *         error code otherwise
    428  */
    429 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
    430 daemon_init_priorities_cache (struct MHD_Daemon *restrict d,
    431                               struct mhd_TlsGnuDaemonData *restrict d_tls,
    432                               struct DaemonOptions *restrict s)
    433 {
    434   size_t i;
    435 
    436   if (NULL != s->tls_app_name.v_app_name)
    437   {
    438     char app_name_uc[128u + 1u] = { '@' };
    439     const size_t app_name_len = strlen (s->tls_app_name.v_app_name);
    440     int res;
    441 
    442     mhd_ASSUME (128u > app_name_len);
    443 
    444     mhd_str_to_uppercase_bin_n (app_name_len + 1u, /* '+1' for zero-termination */
    445                                 s->tls_app_name.v_app_name,
    446                                 app_name_uc + 1u);
    447 
    448     res = try_prio_string (app_name_uc,
    449                            d_tls);
    450     if (GNUTLS_E_SUCCESS == res)
    451       return MHD_SC_OK;
    452     else if (GNUTLS_E_MEMORY_ERROR == res)
    453       return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
    454 
    455     mhd_LOG_PRINT (d, \
    456                    s->tls_app_name.v_disable_fallback ?
    457                    MHD_SC_TLS_DAEMON_INIT_FAILED :
    458                    MHD_SC_TLS_LIB_CONF_WARNING, \
    459                    mhd_LOG_FMT ("Failed to initialise '%s' priorities"),
    460                    gnutls_strerror (res));
    461 
    462     if (s->tls_app_name.v_disable_fallback)
    463       return MHD_SC_TLS_DAEMON_INIT_FAILED;
    464   }
    465 
    466   for (i = 0; i < mhd_ARR_NUM_ELEMS (tlsgnulib_base_priorities); ++i)
    467   {
    468     int res;
    469 #if ! defined(mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY)
    470     if (NULL == tlsgnulib_base_priorities[i].cstr)
    471     {
    472       /* GnuTLS default priorities */
    473 #  if defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY)
    474       d_tls->pri_cache = NULL;
    475       mhd_DBG_PRINT_TLS_INFO_MSG ("Using NULL pointer as default "
    476                                   "GnuTLS priorities");
    477       return MHD_SC_OK;
    478 #  else
    479       continue; /* "default" priorities cannot be used */
    480 #  endif
    481     }
    482 #endif /* ! mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY */
    483     res = try_prio_string (tlsgnulib_base_priorities[i].cstr,
    484                            d_tls);
    485 
    486     if (GNUTLS_E_SUCCESS == res)
    487       return MHD_SC_OK;
    488     else if (GNUTLS_E_MEMORY_ERROR == res)
    489       return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
    490   }
    491 
    492   mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \
    493                "Failed to initialise TLS priorities cache");
    494   return MHD_SC_TLS_DAEMON_INIT_FAILED;
    495 }
    496 
    497 
    498 /**
    499  * De-initialise priorities cache
    500  * @param d_tls the daemon TLS settings
    501  */
    502 static MHD_FN_PAR_NONNULL_ALL_ void
    503 daemon_deinit_priorities_cache (struct mhd_TlsGnuDaemonData *restrict d_tls)
    504 {
    505 #if ! defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY)
    506   mhd_assert (NULL != d_tls->pri_cache);
    507 #else
    508   if (NULL != d_tls->pri_cache)
    509 #endif
    510   gnutls_priority_deinit (d_tls->pri_cache);
    511 }
    512 
    513 
    514 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
    515 MHD_FN_PAR_OUT_ (3) mhd_StatusCodeInt
    516 mhd_tls_gnu_daemon_init3 (struct MHD_Daemon *restrict d,
    517                           struct DaemonOptions *restrict s,
    518                           struct mhd_TlsGnuDaemonData **restrict p_d_tls)
    519 {
    520   mhd_StatusCodeInt res;
    521   struct mhd_TlsGnuDaemonData *restrict d_tls;
    522 
    523   /* Successful initialisation must be checked earlier */
    524   mhd_assert (gnutls_lib_inited);
    525 
    526   res = check_app_tls_settings (d, s);
    527   if (MHD_SC_OK != res)
    528     return res;
    529 
    530   d_tls = (struct mhd_TlsGnuDaemonData *)
    531           mhd_calloc (1, sizeof (struct mhd_TlsGnuDaemonData));
    532   *p_d_tls = d_tls;
    533   if (NULL == d_tls)
    534     return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
    535 
    536 #ifdef mhd_TLS_GNU_HAS_ALPN
    537   // TODO: use daemon option to disable ALPN
    538   // TODO: use daemon option to select protocols for ALPN
    539   d_tls->num_alpn_prots = 0;
    540 
    541 #ifdef MHD_SUPPORT_HTTP2
    542   if (1 /* enabled HTTP/2 ? */)
    543     d_tls->alpn_prots[d_tls->num_alpn_prots++] = mhd_alpn_dat_http2;
    544 #endif /* MHD_SUPPORT_HTTP2 */
    545 
    546   if (1 /* enabled HTTP/1.x ? */)
    547   {
    548     d_tls->alpn_prots[d_tls->num_alpn_prots++] = mhd_alpn_dat_http1_1;
    549     d_tls->alpn_prots[d_tls->num_alpn_prots++] = mhd_alpn_dat_http1_0;
    550   }
    551 
    552   mhd_assert (mhd_ARR_NUM_ELEMS (d_tls->alpn_prots) >= d_tls->num_alpn_prots);
    553 #endif /* mhd_TLS_GNU_HAS_ALPN */
    554 
    555   res = daemon_init_credentials (d,
    556                                  d_tls,
    557                                  s);
    558   if (MHD_SC_OK == res)
    559   {
    560     res = daemon_init_priorities_cache (d,
    561                                         d_tls,
    562                                         s);
    563     if (MHD_SC_OK == res)
    564       return MHD_SC_OK; /* Success exit point */
    565 
    566     /* Below is a clean-up code path */
    567     daemon_deinit_credentials (d_tls);
    568   }
    569 
    570   free (d_tls);
    571   *p_d_tls = NULL;
    572   mhd_assert (MHD_SC_OK != res);
    573   return res;
    574 }
    575 
    576 
    577 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    578 MHD_FN_PAR_INOUT_ (1) void
    579 mhd_tls_gnu_daemon_deinit (struct mhd_TlsGnuDaemonData *restrict d_tls)
    580 {
    581   mhd_assert (NULL != d_tls);
    582   daemon_deinit_priorities_cache (d_tls);
    583   daemon_deinit_credentials (d_tls);
    584   free (d_tls);
    585 }
    586 
    587 
    588 /* ** Connection initialisation / de-initialisation ** */
    589 
    590 MHD_INTERNAL size_t
    591 mhd_tls_gnu_conn_get_tls_size_v (void)
    592 {
    593   return sizeof (struct mhd_TlsGnuConnData);
    594 }
    595 
    596 
    597 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
    598 MHD_FN_PAR_OUT_ (3) bool
    599 mhd_tls_gnu_conn_init (const struct mhd_TlsGnuDaemonData *restrict d_tls,
    600                        const struct mhd_ConnSocket *sk,
    601                        struct mhd_TlsGnuConnData *restrict c_tls)
    602 {
    603   unsigned int c_flags;
    604   int res;
    605 
    606   c_flags = GNUTLS_SERVER;
    607 #if GNUTLS_VERSION_NUMBER >= 0x030000
    608   /* Note: the proper support for the blocking sockets may require use of
    609      gnutls_handshake_set_timeout() and
    610      gnutls_transport_set_pull_timeout_function() (the latter is not actually
    611      required for the modern GnuTLS versions at least) */
    612   if (sk->props.is_nonblck)
    613     c_flags |= GNUTLS_NONBLOCK;
    614 #endif
    615 #ifdef mhd_TLS_GNU_HAS_NO_SIGNAL
    616   c_flags |= GNUTLS_NO_SIGNAL;
    617 #endif
    618 
    619   if (GNUTLS_E_SUCCESS !=
    620       gnutls_init (&(c_tls->sess),
    621                    c_flags))
    622     return false;
    623 
    624 #if GNUTLS_VERSION_NUMBER >= 0x030100
    625   if (! sk->props.is_nonblck)
    626     gnutls_handshake_set_timeout (c_tls->sess,
    627                                   GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
    628 #endif
    629 #if ! defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY)
    630   mhd_assert (NULL != d_tls->pri_cache);
    631 #else
    632   if (NULL == d_tls->pri_cache)
    633     res = gnutls_set_default_priority (c_tls->sess);
    634   else
    635 #endif
    636   res = gnutls_priority_set (c_tls->sess,
    637                              d_tls->pri_cache);
    638 
    639   if (GNUTLS_E_SUCCESS == res)
    640   {
    641     if (GNUTLS_E_SUCCESS ==
    642         gnutls_credentials_set (c_tls->sess,
    643                                 GNUTLS_CRD_CERTIFICATE,
    644                                 d_tls->cred))
    645     {
    646 #if defined(mhd_TLS_GNU_HAS_TRANSP_SET_INT) && defined(MHD_SOCKETS_KIND_POSIX)
    647       gnutls_transport_set_int (c_tls->sess,
    648                                 sk->fd);
    649 #elif defined(MHD_SOCKETS_KIND_POSIX)
    650       gnutls_transport_set_ptr (c_tls->sess,
    651                                 mhd_INT_TO_PTR (sk->fd));
    652 #else  /* MHD_SOCKETS_KIND_WINSOCK */
    653       gnutls_transport_set_ptr (c_tls->sess,
    654                                 mhd_UINT_TO_PTR (sk->fd));
    655 #endif /* MHD_SOCKETS_KIND_WINSOCK */
    656 
    657       /* The basic TLS session properties has been set.
    658          The rest is optional settings. */
    659 #ifdef mhd_TLS_GNU_HAS_ALPN
    660       if (0 != d_tls->num_alpn_prots)
    661       {
    662         int alpn_res;
    663         unsigned int alpn_flags;
    664 
    665         alpn_flags = 0;
    666 #  if 0
    667         alpn_flags |= GNUTLS_ALPN_SERVER_PRECEDENCE;
    668 #  endif
    669 
    670         alpn_res =
    671           gnutls_alpn_set_protocols (c_tls->sess,
    672                                      d_tls->alpn_prots,
    673                                      d_tls->num_alpn_prots,
    674                                      alpn_flags);
    675         (void) alpn_res; /* Ignore any possible ALPN set errors */
    676       }
    677 #endif /* mhd_TLS_GNU_HAS_ALPN */
    678 #ifndef NDEBUG
    679       c_tls->dbg.is_inited = true;
    680 #endif /* ! NDEBUG */
    681 
    682       return true; /* Success exit point */
    683     }
    684     /* Below is a clean-up code path */
    685   }
    686 
    687   gnutls_deinit (c_tls->sess);
    688   return false; /* Failure exit point */
    689 }
    690 
    691 
    692 /**
    693  * De-initialise connection TLS settings.
    694  * The provided pointer is not freed/deallocated.
    695  * @param c_tls the initialised connection TLS settings
    696  */
    697 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
    698 mhd_tls_gnu_conn_deinit (struct mhd_TlsGnuConnData *restrict c_tls)
    699 {
    700   mhd_assert (NULL != c_tls->sess);
    701   mhd_assert (c_tls->dbg.is_inited);
    702   gnutls_deinit (c_tls->sess);
    703 }
    704 
    705 
    706 /* ** TLS connection establishing ** */
    707 
    708 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
    709 enum mhd_TlsProcedureResult
    710 mhd_tls_gnu_conn_handshake (struct mhd_TlsGnuConnData *restrict c_tls)
    711 {
    712   int res;
    713 
    714   mhd_assert (c_tls->dbg.is_inited);
    715   mhd_assert (! c_tls->dbg.is_tls_handshake_completed);
    716   mhd_assert (! c_tls->dbg.is_finished);
    717   mhd_assert (! c_tls->dbg.is_failed);
    718 
    719   res = gnutls_handshake (c_tls->sess);
    720   switch (res)
    721   {
    722   case GNUTLS_E_SUCCESS:
    723 #ifndef NDEBUG
    724     c_tls->dbg.is_tls_handshake_completed = true;
    725 #endif /* ! NDEBUG */
    726     return mhd_TLS_PROCED_SUCCESS;
    727   case GNUTLS_E_INTERRUPTED:
    728   case GNUTLS_E_AGAIN:
    729   case GNUTLS_E_WARNING_ALERT_RECEIVED: /* Ignore any warning for now */
    730     if (1)
    731     {
    732       int is_sending;
    733 
    734       is_sending = gnutls_record_get_direction (c_tls->sess);
    735       if (GNUTLS_E_INTERRUPTED == res)
    736         return is_sending ?
    737                mhd_TLS_PROCED_SEND_INTERRUPTED :
    738                mhd_TLS_PROCED_RECV_INTERRUPTED;
    739       return is_sending ?
    740              mhd_TLS_PROCED_SEND_MORE_NEEDED :
    741              mhd_TLS_PROCED_RECV_MORE_NEEDED;
    742     }
    743     break;
    744   default:
    745     break;
    746   }
    747 #ifndef NDEBUG
    748   c_tls->dbg.is_failed = true;
    749 #endif /* ! NDEBUG */
    750   return mhd_TLS_PROCED_FAILED;
    751 }
    752 
    753 
    754 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
    755 enum mhd_TlsProcedureResult
    756 mhd_tls_gnu_conn_shutdown (struct mhd_TlsGnuConnData *restrict c_tls)
    757 {
    758   int res;
    759 
    760   mhd_assert (c_tls->dbg.is_inited);
    761   mhd_assert (c_tls->dbg.is_tls_handshake_completed);
    762   mhd_assert (! c_tls->dbg.is_finished);
    763   mhd_assert (! c_tls->dbg.is_failed);
    764 
    765   res = gnutls_bye (c_tls->sess,
    766                     c_tls->rmt_shut_tls_wr ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR);
    767   switch (res)
    768   {
    769   case GNUTLS_E_SUCCESS:
    770 #ifndef NDEBUG
    771     c_tls->dbg.is_finished = true;
    772 #endif /* ! NDEBUG */
    773     return mhd_TLS_PROCED_SUCCESS;
    774   case GNUTLS_E_INTERRUPTED:
    775   case GNUTLS_E_AGAIN:
    776   case GNUTLS_E_WARNING_ALERT_RECEIVED: /* Ignore any warning for now */
    777     if (1)
    778     {
    779       int is_sending;
    780 
    781       is_sending = gnutls_record_get_direction (c_tls->sess);
    782       if (GNUTLS_E_INTERRUPTED == res)
    783         return is_sending ?
    784                mhd_TLS_PROCED_SEND_INTERRUPTED :
    785                mhd_TLS_PROCED_RECV_INTERRUPTED;
    786       return is_sending ?
    787              mhd_TLS_PROCED_SEND_MORE_NEEDED :
    788              mhd_TLS_PROCED_RECV_MORE_NEEDED;
    789     }
    790     break;
    791   default:
    792     break;
    793   }
    794 #ifndef NDEBUG
    795   c_tls->dbg.is_failed = true;
    796 #endif /* ! NDEBUG */
    797   return mhd_TLS_PROCED_FAILED;
    798 }
    799 
    800 
    801 /* ** Data receiving and sending ** */
    802 
    803 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    804 MHD_FN_PAR_OUT_SIZE_ (3,2)
    805 MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
    806 mhd_tls_gnu_conn_recv (struct mhd_TlsGnuConnData *restrict c_tls,
    807                        size_t buf_size,
    808                        char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
    809                        size_t *restrict received)
    810 {
    811   ssize_t res;
    812 
    813   mhd_assert (c_tls->dbg.is_inited);
    814   mhd_assert (c_tls->dbg.is_tls_handshake_completed);
    815   mhd_assert (! c_tls->dbg.is_finished);
    816   mhd_assert (! c_tls->dbg.is_failed);
    817 
    818   /* Check for GnuTLS return value limitation */
    819   if (0 > (ssize_t) buf_size)
    820     buf_size = (ssize_t) ((~((size_t) 0u)) >> 1); /* SSIZE_MAX */
    821 
    822   res = gnutls_record_recv (c_tls->sess,
    823                             buf,
    824                             buf_size);
    825   if (0 >= res)
    826   {
    827     *received = 0;
    828     switch (res)
    829     {
    830     case 0: /* Not an error */
    831       c_tls->rmt_shut_tls_wr = true;
    832       return mhd_SOCKET_ERR_NO_ERROR;
    833     case GNUTLS_E_AGAIN:
    834       return mhd_SOCKET_ERR_AGAIN;
    835     case GNUTLS_E_INTERRUPTED:
    836       return mhd_SOCKET_ERR_INTR;
    837     case GNUTLS_E_PREMATURE_TERMINATION:
    838       return mhd_SOCKET_ERR_CONNRESET;
    839     default:
    840       break;
    841     }
    842     /* Treat all other kinds of errors as hard errors */
    843 #ifndef NDEBUG
    844     c_tls->dbg.is_failed = true;
    845 #endif /* ! NDEBUG */
    846     return mhd_SOCKET_ERR_TLS;
    847   }
    848 
    849   *received = (size_t) res;
    850   return mhd_SOCKET_ERR_NO_ERROR;
    851 }
    852 
    853 
    854 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
    855 mhd_tls_gnu_conn_has_data_in (struct mhd_TlsGnuConnData *restrict c_tls)
    856 {
    857   return 0 != gnutls_record_check_pending (c_tls->sess);
    858 }
    859 
    860 
    861 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    862 MHD_FN_PAR_IN_SIZE_ (3,2)
    863 MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
    864 mhd_tls_gnu_conn_send4 (struct mhd_TlsGnuConnData *restrict c_tls,
    865                         size_t buf_size,
    866                         const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
    867                         size_t *restrict sent)
    868 {
    869   ssize_t res;
    870 
    871   mhd_assert (c_tls->dbg.is_inited);
    872   mhd_assert (c_tls->dbg.is_tls_handshake_completed);
    873   mhd_assert (! c_tls->dbg.is_finished);
    874   mhd_assert (! c_tls->dbg.is_failed);
    875 
    876   /* Check for GnuTLS return value limitation */
    877   if (0 > (ssize_t) buf_size)
    878     buf_size = (ssize_t) ((~((size_t) 0u)) >> 1); /* SSIZE_MAX */
    879 
    880   res = gnutls_record_send (c_tls->sess,
    881                             buf,
    882                             buf_size);
    883 
    884   mhd_assert (0 != res);
    885 
    886   if (0 > res)
    887   {
    888     *sent = 0;
    889     switch (res)
    890     {
    891     case GNUTLS_E_AGAIN:
    892       return mhd_SOCKET_ERR_AGAIN;
    893     case GNUTLS_E_INTERRUPTED:
    894       return mhd_SOCKET_ERR_INTR;
    895     case GNUTLS_E_PREMATURE_TERMINATION:
    896       return mhd_SOCKET_ERR_CONNRESET;
    897     default:
    898       break;
    899     }
    900     /* Treat all other kinds of errors as hard errors */
    901 #ifndef NDEBUG
    902     c_tls->dbg.is_failed = true;
    903 #endif /* ! NDEBUG */
    904     return mhd_SOCKET_ERR_TLS;
    905   }
    906 
    907   *sent = (size_t) res;
    908   return mhd_SOCKET_ERR_NO_ERROR;
    909 }
    910 
    911 
    912 /* ** TLS connection information ** */
    913 
    914 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    915 MHD_FN_PAR_OUT_ (2) void
    916 mhd_tls_gnu_conn_get_tls_sess (
    917   struct mhd_TlsGnuConnData *restrict c_tls,
    918   union MHD_ConnInfoDynamicTlsSess *restrict tls_sess_out)
    919 {
    920   tls_sess_out->v_gnutls_session = c_tls->sess;
    921 }
    922 
    923 
    924 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    925 MHD_FN_PAR_OUT_ (2) bool
    926 mhd_tls_gnu_conn_get_tls_ver (struct mhd_TlsGnuConnData *restrict c_tls,
    927                               struct mhd_StctTlsVersion *restrict tls_ver_out)
    928 {
    929   gnutls_protocol_t gtls_tls_ver;
    930 
    931   mhd_assert (c_tls->dbg.is_tls_handshake_completed);
    932 
    933   gtls_tls_ver = gnutls_protocol_get_version (c_tls->sess);
    934 #if GNUTLS_VERSION_NUMBER >= 0x030603
    935   if (GNUTLS_TLS1_3 == gtls_tls_ver)
    936     tls_ver_out->tls_ver = MHD_TLS_VERSION_1_3;
    937   else
    938 #endif
    939   if (GNUTLS_TLS1_2 == gtls_tls_ver)
    940     tls_ver_out->tls_ver = MHD_TLS_VERSION_1_2;
    941   else if (GNUTLS_TLS1_1 == gtls_tls_ver)
    942     tls_ver_out->tls_ver = MHD_TLS_VERSION_1_1;
    943   else if (GNUTLS_TLS1_0 == gtls_tls_ver)
    944     tls_ver_out->tls_ver = MHD_TLS_VERSION_1_0;
    945   else if (GNUTLS_VERSION_UNKNOWN == gtls_tls_ver)
    946     return false; /* The TLS version cannot be detected */
    947   else
    948     /* The TLS version is known for GnuTLS, but cannot be mapped */
    949     tls_ver_out->tls_ver = MHD_TLS_VERSION_UNKNOWN;
    950 
    951   return true;
    952 }
    953 
    954 
    955 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ enum mhd_TlsAlpnProt
    956 mhd_tls_gnu_conn_get_alpn_prot (struct mhd_TlsGnuConnData *restrict c_tls)
    957 {
    958 #ifdef mhd_TLS_GNU_HAS_ALPN
    959   gnutls_datum_t sel_prot;
    960 
    961   if (GNUTLS_E_SUCCESS ==
    962       gnutls_alpn_get_selected_protocol (c_tls->sess, &sel_prot))
    963     return mhd_tls_alpn_decode_n (sel_prot.size,
    964                                   sel_prot.data);
    965 #endif /* mhd_TLS_GNU_HAS_ALPN */
    966 
    967   return mhd_TLS_ALPN_PROT_NOT_SELECTED;
    968 }