libmicrohttpd2

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

tls_gnu_funcs.c (29226B)


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