libmicrohttpd2

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

tls_gnu_funcs.c (48902B)


      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 #include "mhd_predict.h"
     61 #include "mhd_unreachable.h"
     62 
     63 #include "mhd_socket_type.h"
     64 #include "mhd_str_types.h"
     65 
     66 #include "mhd_str_macros.h"
     67 #include "mhd_arr_num_elems.h"
     68 
     69 #include "mhd_str.h"
     70 #include "mhd_conn_socket.h"
     71 
     72 #include "mhd_tls_internal.h"
     73 
     74 #include "tls_gnu_tls_lib.h"
     75 
     76 #include "mhd_tls_ver_stct.h"
     77 
     78 #include "tls_gnu_daemon_data.h"
     79 #include "tls_gnu_conn_data.h"
     80 #include "tls_gnu_funcs.h"
     81 
     82 #include "daemon_options.h"
     83 #include "daemon_logger.h"
     84 
     85 #ifdef mhd_HAVE_GNUTLS_ACME
     86 #  include "mhd_tls_acme_func.h"
     87 #endif /* mhd_HAVE_GNUTLS_ACME */
     88 
     89 #ifdef  mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3
     90 #  include "tls_dh_params.h"
     91 #endif
     92 
     93 #include "mhd_public_api.h"
     94 
     95 #ifdef mhd_USE_TLS_DEBUG_MESSAGES
     96 static void
     97 mhd_tls_gnu_debug_print (int level, const char *msg)
     98 {
     99   (void)fprintf (stderr, "## GnuTLS %02i: %s",
    100                  level,
    101                  msg);
    102   (void)fflush (stderr);
    103 }
    104 
    105 
    106 #  define mhd_DBG_PRINT_TLS_INFO_MSG(message) \
    107           do { (void) fprintf (stderr, "## GnuTLS info: %s\n", (message)); \
    108                (void) fflush (stderr);} while (0)
    109 #  define mhd_DBG_PRINT_TLS_INFO_PARAM1(message, param) \
    110      do { (void) fprintf (stderr, "## GnuTLS info: " message "\n", (param)); \
    111           (void) fflush (stderr);} while (0)
    112 #else  /* ! mhd_USE_TLS_DEBUG_MESSAGES */
    113 #  define mhd_DBG_PRINT_TLS_ERRS()      ERR_clear_error ()
    114 #  define mhd_DBG_PRINT_TLS_INFO_MSG(message)       ((void) 0)
    115 #  define mhd_DBG_PRINT_TLS_INFO_PARAM1(message, param)  ((void) 0)
    116 #endif /* ! mhd_USE_TLS_DEBUG_MESSAGES */
    117 
    118 #ifdef mhd_TLS_GNU_HAS_ALPN
    119 static const char mhd_alpn_str_http1_0[] = mhd_ALPN_H1_0;
    120 static const char mhd_alpn_str_http1_1[] = mhd_ALPN_H1_1;
    121 #  ifdef MHD_SUPPORT_HTTP2
    122 static const char mhd_alpn_str_http2[] = mhd_ALPN_H2;
    123 #  endif
    124 #  ifdef mhd_HAVE_GNUTLS_ACME
    125 static const char mhd_alpn_str_acme[] = mhd_ALPN_ACME;
    126 #  endif
    127 #  if 0 /* Disabled code */
    128 static const char alpn_http_3[] = mhd_ALPN_H3;
    129 #  endif
    130 static const gnutls_datum_t mhd_alpn_dat_http1_0 = {
    131   (unsigned char *)mhd_DROP_CONST (mhd_alpn_str_http1_0),
    132   mhd_SSTR_LEN (mhd_alpn_str_http1_0)
    133 };
    134 static const gnutls_datum_t mhd_alpn_dat_http1_1 = {
    135   (unsigned char *)mhd_DROP_CONST (mhd_alpn_str_http1_1),
    136   mhd_SSTR_LEN (mhd_alpn_str_http1_1)
    137 };
    138 #  ifdef MHD_SUPPORT_HTTP2
    139 static const gnutls_datum_t mhd_alpn_dat_http2 = {
    140   (unsigned char *)mhd_DROP_CONST (mhd_alpn_str_http2),
    141   mhd_SSTR_LEN (mhd_alpn_str_http2)
    142 };
    143 #  endif
    144 #  ifdef mhd_HAVE_GNUTLS_ACME
    145 static const gnutls_datum_t mhd_alpn_dat_acme = {
    146   (unsigned char *)mhd_DROP_CONST (mhd_alpn_str_acme),
    147   mhd_SSTR_LEN (mhd_alpn_str_acme)
    148 };
    149 #  endif
    150 #endif /* mhd_TLS_GNU_HAS_ALPN */
    151 
    152 
    153 /* ** Global initialisation / de-initialisation ** */
    154 
    155 static bool gnutls_lib_inited = false;
    156 
    157 MHD_INTERNAL void
    158 mhd_tls_gnu_global_init (void)
    159 {
    160 #ifdef GNUTLS_VERSION
    161   /* Make sure that used shared GnuTLS library has least the same version as
    162      MHD was configured for. Fail if the version is earlier. */
    163   if (NULL != gnutls_check_version (GNUTLS_VERSION))
    164 #endif
    165   gnutls_lib_inited = (GNUTLS_E_SUCCESS == gnutls_global_init ());
    166 
    167 #ifdef mhd_USE_TLS_DEBUG_MESSAGES
    168   gnutls_global_set_log_function (&mhd_tls_gnu_debug_print);
    169   gnutls_global_set_log_level (2);
    170 #endif
    171 }
    172 
    173 
    174 MHD_INTERNAL void
    175 mhd_tls_gnu_global_deinit (void)
    176 {
    177 #ifdef mhd_USE_TLS_DEBUG_MESSAGES
    178   gnutls_global_set_log_level (0);
    179 #endif
    180 
    181   if (gnutls_lib_inited)
    182     gnutls_global_deinit ();
    183   gnutls_lib_inited = false;
    184 }
    185 
    186 
    187 MHD_INTERNAL MHD_FN_PURE_ bool
    188 mhd_tls_gnu_is_inited_fine (void)
    189 {
    190   return gnutls_lib_inited;
    191 }
    192 
    193 
    194 /* ** Daemon initialisation / de-initialisation ** */
    195 
    196 /**
    197  * Check application-provided daemon TLS settings
    198  * @param d the daemon handle
    199  * @param s the application-provided settings
    200  * @return #MHD_SC_OK on success,
    201  *         error code otherwise
    202  */
    203 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
    204 check_app_tls_settings (struct MHD_Daemon *restrict d,
    205                         struct DaemonOptions *restrict s)
    206 {
    207   mhd_assert (MHD_TLS_BACKEND_NONE != s->tls);
    208   mhd_assert ((MHD_TLS_BACKEND_GNUTLS == s->tls) \
    209               || (MHD_TLS_BACKEND_ANY == s->tls));
    210   if (NULL == s->tls_cert_key.v_mem_cert)
    211   {
    212     mhd_LOG_MSG (d, MHD_SC_TLS_CONF_BAD_CERT, \
    213                  "No valid TLS certificate is provided");
    214     return MHD_SC_TLS_CONF_BAD_CERT;
    215   }
    216   mhd_assert (NULL != s->tls_cert_key.v_mem_key);
    217 #ifdef mhd_HAVE_TLS_ACME
    218   if (!mhd_tls_gnu_is_acme_alpn_supported (s)
    219       && s->acme_alpn_required)
    220   {
    221     mhd_LOG_MSG (d, MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS,
    222                  "This build of GnuTLS backend does not support "
    223                  "ACME ALPN challenge protocol, but daemon settings "
    224                  "require it");
    225     return MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS;
    226   }
    227 #endif
    228 
    229   return MHD_SC_OK;
    230 }
    231 
    232 
    233 /* Sanity check: both macros cannot be defined at the same time */
    234 #if defined(mhd_TLS_GNU_DH_PARAMS_USE_KNOWN) && \
    235   defined(mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3)
    236 #  error Invalid macro combination: both mhd_TLS_GNU_DH_PARAMS_USE_KNOWN and \
    237   mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3 are defined
    238 #endif
    239 
    240 /**
    241  * Initialise daemon TLS Diffie-Hellman parameters.
    242  *
    243  * This function initialises Diffie-Hellman parameters for the daemon based
    244  * on GnuTLS recommended defaults.
    245  * With modern GnuTLS versions this function is a no-op and always succeeds.
    246  *
    247  * This function does not put any messages to the log.
    248  * @param d_tls the daemon TLS data
    249  * @return 'true' if succeeded,
    250  *         'false' if failed
    251  */
    252 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ bool
    253 daemon_init_dh_data (struct mhd_TlsGnuDaemonData *restrict d_tls)
    254 {
    255 #if defined(mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3)
    256   gnutls_datum_t dh_data;
    257   if (GNUTLS_E_SUCCESS  !=
    258       gnutls_dh_params_init (&(d_tls->dh_params)))
    259     return false;
    260 
    261   dh_data.data = mhd_DROP_CONST (mhd_tls_dh_params_pkcs3);
    262   dh_data.size = sizeof (mhd_tls_dh_params_pkcs3);
    263   if (GNUTLS_E_SUCCESS ==
    264       gnutls_dh_params_import_pkcs3 (d_tls->dh_params,
    265                                      &dh_data,
    266                                      GNUTLS_X509_FMT_PEM))
    267     return true; /* success exit point */
    268 
    269   /* Below is a clean-up code path */
    270   gnutls_dh_params_deinit (d_tls->dh_params);
    271   return false;
    272 #else
    273   (void)d_tls;  /* Mute compiler warning */
    274   return true;
    275 #endif
    276 }
    277 
    278 
    279 /**
    280  * De-initialise daemon TLS Diffie-Hellman parameters.
    281  * @param d_tls the daemon TLS data
    282  */
    283 static MHD_FN_PAR_NONNULL_ALL_ void
    284 daemon_deinit_dh_data (struct mhd_TlsGnuDaemonData *restrict d_tls)
    285 {
    286 #if defined(mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3)
    287   mhd_assert (NULL != d_tls->dh_params);
    288   gnutls_dh_params_deinit (d_tls->dh_params);
    289 #else
    290   (void)d_tls;  /* Mute compiler warning */
    291 #endif
    292 }
    293 
    294 
    295 /**
    296  * Set credentials TLS Diffie-Hellman parameters.
    297  *
    298  * This function initialises Diffie-Hellman parameters based on GnuTLS
    299  * recommended defaults.
    300  * With modern GnuTLS versions this function is a no-op and always succeeds.
    301  *
    302  * This function does not put any messages to the log.
    303  * @param d_tls the daemon TLS data
    304  * @param cred the credentials TLS data
    305  * @return 'true' if succeeded,
    306  *         'false' if failed
    307  */
    308 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ bool
    309 tld_gnu_cred_set_dh_data (struct mhd_TlsGnuDaemonData *restrict d_tls,
    310                           struct mhd_TlsGnuCredData *restrict cred)
    311 {
    312 #if defined(mhd_TLS_GNU_DH_PARAMS_USE_KNOWN)
    313   (void)d_tls;  /* Mute compiler warning */
    314   /* Rely on reasonable TLS defaults set in the TLS library.
    315      Modern GnuTLS versions rely completely on RFC 7919 and do not need
    316      this function; therefore do not bother implementing special
    317      application-defined settings for security level just for a limited
    318      number of GnuTLS versions (>= 3.5.6 && < 3.6.0). */
    319   return (GNUTLS_E_SUCCESS ==
    320           gnutls_certificate_set_known_dh_params (cred->gnu_cred,
    321                                                   GNUTLS_SEC_PARAM_MEDIUM));
    322 #elif defined(mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3)
    323   gnutls_certificate_set_dh_params (cred->gnu_cred,
    324                                     d_tls->dh_params);
    325   return true; /* success exit point */
    326 #else
    327   (void)cred;   /* Mute compiler warning */
    328   (void)d_tls;  /* Mute compiler warning */
    329   return true;
    330 #endif
    331 }
    332 
    333 
    334 /**
    335  * Create TLS credentials from the provided certificate and key data
    336  *
    337  * @param d_tls the daemon TLS data
    338  * @param[out] cred the credentials data structure to be filled
    339  * @param with_ref_cnt whether to use the reference counter
    340  * @param cert_len the length of the @p cert buffer, not including the
    341  *                 zero-termination byte,
    342  *                 must not be zero
    343  * @param cert the certificate data in PEM format, zero-terminated
    344  * @param key_len the length of the @p key buffer, not including the
    345  *                zero-termination byte,
    346  *                must not be zero
    347  * @param key the private key data in PEM format, zero-terminated
    348  * @param pass the password for the private key, zero-terminated,
    349  *             may be NULL if the private key is not password-protected
    350  * @return #mhd_TLS_CRED_CREATE_OK on success,
    351  *         other enum mhd_TlsCredCreateResult values otherwise
    352  */
    353 static MHD_FN_MUST_CHECK_RESULT_
    354 MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
    355 MHD_FN_PAR_NONNULL_ (5) MHD_FN_PAR_NONNULL_ (7)
    356 MHD_FN_PAR_OUT_ (2)
    357 MHD_FN_PAR_IN_SIZE_ (5, 4) MHD_FN_PAR_IN_SIZE_ (7, 6)
    358 MHD_FN_PAR_CSTR_ (5) MHD_FN_PAR_CSTR_ (7) MHD_FN_PAR_CSTR_ (8)
    359 enum mhd_TlsCredCreateResult
    360 tls_gnu_cred_init (struct mhd_TlsGnuDaemonData *restrict d_tls,
    361                    struct mhd_TlsGnuCredData *restrict cred,
    362                    bool with_ref_cnt,
    363                    size_t cert_len,
    364                    const char *restrict cert,
    365                    size_t key_len,
    366                    const char *restrict key,
    367                    const char *restrict pass)
    368 {
    369   enum mhd_TlsCredCreateResult ret;
    370   int res;
    371   gnutls_datum_t cert_data;
    372   gnutls_datum_t key_data;
    373 
    374   mhd_assert (0 != cert_len);
    375   mhd_assert (0 != key_len);
    376 
    377   if (mhd_COND_HARDLY_EVER (cert_len != (unsigned int)cert_len)
    378       || mhd_COND_HARDLY_EVER (key_len != (unsigned int)key_len))
    379     return mhd_TLS_CRED_CREATE_BAD_CRED_DATA;
    380 
    381   if (GNUTLS_E_SUCCESS !=
    382       gnutls_certificate_allocate_credentials (&(cred->gnu_cred)))
    383     return mhd_TLS_CRED_CREATE_ALLOC_FAILED;
    384 
    385   cert_data.data = (unsigned char *)mhd_DROP_CONST (cert);
    386   cert_data.size = (unsigned int)cert_len;
    387   key_data.data = (unsigned char *)mhd_DROP_CONST (key);
    388   key_data.size = (unsigned int)key_len;
    389   res = gnutls_certificate_set_x509_key_mem2 (cred->gnu_cred,
    390                                               &cert_data,
    391                                               &key_data,
    392                                               GNUTLS_X509_FMT_PEM,
    393                                               pass,
    394                                               0);
    395 
    396   switch (res)
    397   {
    398   case GNUTLS_E_SUCCESS:
    399     ret = mhd_TLS_CRED_CREATE_OK;
    400     break;
    401   case GNUTLS_E_MEMORY_ERROR:
    402     ret = mhd_TLS_CRED_CREATE_ALLOC_FAILED;
    403     break;
    404   default:
    405     ret = mhd_TLS_CRED_CREATE_BAD_CRED_DATA;
    406     break;
    407   }
    408 
    409   if (mhd_TLS_CRED_CREATE_OK == ret)
    410   {
    411     if (!tld_gnu_cred_set_dh_data (d_tls,
    412                                    cred))
    413       ret = mhd_TLS_CRED_CREATE_FAILED;
    414 
    415     if (mhd_TLS_CRED_CREATE_OK == ret)
    416     {
    417       if (with_ref_cnt)
    418       {
    419         if (!mhd_atomic_counter_init (&(cred->ref_cnt), 1u))
    420           ret = mhd_TLS_CRED_CREATE_ALLOC_FAILED; /* Not fully correct code path, but it is very unlikely to happen */
    421       }
    422 #ifndef NDEBUG
    423       else
    424       {
    425         /* Initialise the reference counter to 0 for debug builds to catch
    426            any accidental use of the reference counter when it is not used. */
    427         if (!mhd_atomic_counter_init (&(cred->ref_cnt), 0u))
    428           ret = mhd_TLS_CRED_CREATE_ALLOC_FAILED; /* Not fully correct code path, but it is very unlikely to happen */
    429       }
    430 #endif /* ! NDEBUG */
    431 
    432       if (mhd_TLS_CRED_CREATE_OK == ret)
    433         return ret; /* Success exit point */
    434 
    435       /* Below is a clean-up code path */
    436     }
    437   }
    438 
    439   gnutls_certificate_free_credentials (cred->gnu_cred);
    440   mhd_assert (mhd_TLS_CRED_CREATE_OK != ret);
    441   return ret; /* Failure exit point */
    442 }
    443 
    444 
    445 /**
    446  * Set certificate credentials for a GnuTLS session without acquiring a
    447  * reference.
    448  *
    449  * The GnuTLS session borrows the credentials.  The caller must keep @p cred
    450  * alive until the session is de-initialised or different certificate
    451  * credentials are set for the session.
    452  *
    453  * @param[in,out] c_tls the connection TLS data containing the session
    454  * @param cred the credentials to set
    455  * @return 'true' if the credentials were set successfully,
    456  *         'false' otherwise
    457  */
    458 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) bool
    459 tls_gnu_cred_use_norefcnt (struct mhd_TlsGnuConnData *restrict c_tls,
    460                            const struct mhd_TlsGnuCredData *restrict cred)
    461 {
    462   return (GNUTLS_E_SUCCESS ==
    463           gnutls_credentials_set (c_tls->sess,
    464                                   GNUTLS_CRD_CERTIFICATE,
    465                                   cred->gnu_cred));
    466 }
    467 
    468 
    469 #ifdef mhd_HAVE_GNUTLS_ACME
    470 /**
    471  * Set certificate credentials for a GnuTLS session and acquire a reference.
    472  *
    473  * The reference counter is incremented only after GnuTLS accepts the
    474  * credentials.  The caller must release the acquired reference after the
    475  * session is de-initialised or the credentials are replaced in the session.
    476  *
    477  * @param[in,out] c_tls the connection TLS data containing the session
    478  * @param[in,out] cred the credentials to set and reference
    479  * @return 'true' if the credentials were set and referenced successfully,
    480  *         'false' otherwise
    481  */
    482 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) bool
    483 tls_gnu_cred_use_refcnt (struct mhd_TlsGnuConnData *restrict c_tls,
    484                          struct mhd_TlsGnuCredData *restrict cred)
    485 {
    486   bool res;
    487 
    488   res = tls_gnu_cred_use_norefcnt (c_tls,
    489                                    cred);
    490   if (res)
    491     mhd_atomic_counter_inc (&(cred->ref_cnt));
    492 
    493   return res;
    494 }
    495 
    496 
    497 #endif /* mhd_HAVE_GNUTLS_ACME */
    498 
    499 /**
    500  * Decrement reference count and de-initialise TLS credentials if no
    501  * references left.
    502  * @param cred the credentials data structure to be deinitialised
    503  * @param with_ref_cnt whether to use the reference counter
    504  * @return 'true' if no references left and credentials de-initialised,
    505  *         'false' if there are still references left (never returned with
    506  *         @p with_ref_cnt set to 'false')
    507  */
    508 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) bool
    509 tls_gnu_cred_deinit (struct mhd_TlsGnuCredData *restrict cred,
    510                      bool with_ref_cnt)
    511 {
    512   mhd_assert (NULL != cred->gnu_cred);
    513 
    514   if (with_ref_cnt)
    515   {
    516     if (1u != mhd_atomic_counter_get_dec (&(cred->ref_cnt)))
    517       return false; /* Not the last reference, do not free credentials */
    518     mhd_atomic_counter_deinit (&(cred->ref_cnt));
    519   }
    520 #ifndef NDEBUG
    521   else
    522   {
    523     mhd_assert (0u == mhd_atomic_counter_get (&(cred->ref_cnt)));
    524     mhd_atomic_counter_deinit (&(cred->ref_cnt));
    525   }
    526 #endif /* ! NDEBUG */
    527 
    528   gnutls_certificate_free_credentials (cred->gnu_cred);
    529 #ifndef NDEBUG
    530   cred->gnu_cred = NULL;
    531 #endif /* ! NDEBUG */
    532   return true;
    533 }
    534 
    535 
    536 /**
    537  * Set daemon TLS credentials.
    538  * This function puts error messages to the log if needed.
    539  * @param d the daemon handle
    540  * @param d_tls the daemon TLS settings
    541  * @param s the application-provided settings
    542  * @return #MHD_SC_OK on success,
    543  *         error code otherwise
    544  */
    545 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
    546 daemon_init_credentials (struct MHD_Daemon *restrict d,
    547                          struct mhd_TlsGnuDaemonData *restrict d_tls,
    548                          struct DaemonOptions *restrict s)
    549 {
    550   size_t cert_len;
    551   size_t key_len;
    552 
    553   // TODO: Support multiple certificates
    554   cert_len = strlen (s->tls_cert_key.v_mem_cert); // TODO: Reuse calculated length
    555   key_len = strlen (s->tls_cert_key.v_mem_key);   // TODO: Reuse calculated length
    556 
    557   mhd_assert (0 != cert_len);
    558   mhd_assert (0 != key_len);
    559 
    560   switch (tls_gnu_cred_init (d_tls,
    561                              &(d_tls->cred),
    562                              false,
    563                              cert_len,
    564                              s->tls_cert_key.v_mem_cert,
    565                              key_len,
    566                              s->tls_cert_key.v_mem_key,
    567                              s->tls_cert_key.v_mem_pass))
    568   {
    569   case mhd_TLS_CRED_CREATE_OK:
    570     break;
    571 
    572   case mhd_TLS_CRED_CREATE_BAD_CRED_DATA:
    573     mhd_LOG_MSG (d, MHD_SC_TLS_CONF_BAD_CERT,
    574                  "Failed to set the provided TLS certificate");
    575     return MHD_SC_TLS_CONF_BAD_CERT;
    576 
    577   case mhd_TLS_CRED_CREATE_ALLOC_FAILED:
    578     mhd_LOG_MSG (d, MHD_SC_DAEMON_MEM_ALLOC_FAILURE,
    579                  "Failed to allocate memory or other resources "
    580                  "for TLS credentials");
    581     return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
    582 
    583   case mhd_TLS_CRED_CREATE_FAILED:
    584   case mhd_TLS_CRED_CREATE_UNSUPPORTED:
    585     mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED,
    586                  "Failed to initialise TLS credentials for the daemon");
    587     return MHD_SC_TLS_DAEMON_INIT_FAILED;
    588   default:
    589     mhd_UNREACHABLE ();
    590     return MHD_SC_INTERNAL_ERROR;
    591   }
    592 
    593   return MHD_SC_OK;
    594 }
    595 
    596 
    597 /**
    598  * Free daemon fully allocated credentials.
    599  * @param d_tls the daemon TLS settings
    600  */
    601 static MHD_FN_PAR_NONNULL_ALL_ void
    602 daemon_deinit_credentials (struct mhd_TlsGnuDaemonData *restrict d_tls)
    603 {
    604   mhd_assert (NULL != d_tls->cred.gnu_cred);
    605   /* To avoid dangling pointer to DH data in the credentials,
    606      free credentials first and then free DH data. */
    607   (void)tls_gnu_cred_deinit (&(d_tls->cred),
    608                              false);
    609 }
    610 
    611 
    612 /**
    613  * Try to set specified GnuTLS priorities string
    614  * @param prio_string the priorities string
    615  * @param d_tls the daemon TLS settings
    616  * @return GNUTLS_E_SUCCESS on success,
    617  *         GnuTLS error code otherwise
    618  */
    619 static MHD_FN_PAR_CSTR_ (1) MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_INOUT_ (2) int
    620 try_prio_string (const char *restrict prio_string,
    621                  struct mhd_TlsGnuDaemonData *restrict d_tls)
    622 {
    623   int res;
    624   mhd_DBG_PRINT_TLS_INFO_PARAM1 ("Trying '%s' priorities",
    625                                  prio_string ? prio_string : "[NULL]");
    626 
    627   res = gnutls_priority_init (&(d_tls->pri_cache),
    628                               prio_string,
    629                               NULL);
    630 
    631   if (GNUTLS_E_SUCCESS == res)
    632   {
    633     mhd_DBG_PRINT_TLS_INFO_PARAM1 ("Initialised with '%s' priorities",
    634                                    prio_string ? prio_string : "[NULL]");
    635     return res;
    636   }
    637 
    638   mhd_DBG_PRINT_TLS_INFO_PARAM1 ("Failed initialise priorities: %s",
    639                                  gnutls_strerror (res));
    640 
    641   return res;
    642 }
    643 
    644 
    645 static const struct MHD_StringNullable tlsgnulib_base_priorities[] = {
    646   /* Do not use "multi-keyword": if the first configuration is found, but has
    647      some error, the next configuration is not tried. */
    648 #if 0 /* ifdef mhd_TLS_GNU_SUPPORTS_MULTI_KEYWORDS_PRIORITY */
    649   mhd_MSTR_INIT ("@LIBMICROHTTPD,SYSTEM")
    650 #else
    651   mhd_MSTR_INIT ("@LIBMICROHTTPD")
    652   ,
    653   mhd_MSTR_INIT ("@SYSTEM")
    654 #endif
    655   ,
    656   {0, NULL}
    657   ,
    658   mhd_MSTR_INIT ("NORMAL")
    659 };
    660 
    661 /**
    662  * Initialise GnuTLS priorities cache
    663  * @param d the daemon handle
    664  * @param d_tls the daemon TLS settings
    665  * @param s the application-provided settings
    666  * @return #MHD_SC_OK on success,
    667  *         error code otherwise
    668  */
    669 static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
    670 daemon_init_priorities_cache (struct MHD_Daemon *restrict d,
    671                               struct mhd_TlsGnuDaemonData *restrict d_tls,
    672                               struct DaemonOptions *restrict s)
    673 {
    674   size_t i;
    675 
    676   if (NULL != s->tls_app_name.v_app_name)
    677   {
    678     char app_name_uc[128u + 1u] = { '@' };
    679     const size_t app_name_len = strlen (s->tls_app_name.v_app_name);
    680     int res;
    681 
    682     mhd_ASSUME (128u > app_name_len);
    683 
    684     mhd_str_to_uppercase_bin_n (app_name_len + 1u, /* '+1' for zero-termination */
    685                                 s->tls_app_name.v_app_name,
    686                                 app_name_uc + 1u);
    687 
    688     res = try_prio_string (app_name_uc,
    689                            d_tls);
    690     if (GNUTLS_E_SUCCESS == res)
    691       return MHD_SC_OK;
    692     else if (GNUTLS_E_MEMORY_ERROR == res)
    693       return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
    694 
    695     mhd_LOG_PRINT (d, \
    696                    s->tls_app_name.v_disable_fallback ?
    697                    MHD_SC_TLS_DAEMON_INIT_FAILED :
    698                    MHD_SC_TLS_LIB_CONF_WARNING, \
    699                    mhd_LOG_FMT ("Failed to initialise '%s' priorities"),
    700                    gnutls_strerror (res));
    701 
    702     if (s->tls_app_name.v_disable_fallback)
    703       return MHD_SC_TLS_DAEMON_INIT_FAILED;
    704   }
    705 
    706   for (i = 0; i < mhd_ARR_NUM_ELEMS (tlsgnulib_base_priorities); ++i)
    707   {
    708     int res;
    709 #if !defined(mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY)
    710     if (NULL == tlsgnulib_base_priorities[i].cstr)
    711     {
    712       /* GnuTLS default priorities */
    713 #  if defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY)
    714       d_tls->pri_cache = NULL;
    715       mhd_DBG_PRINT_TLS_INFO_MSG ("Using NULL pointer as default "
    716                                   "GnuTLS priorities");
    717       return MHD_SC_OK;
    718 #  else
    719       continue; /* "default" priorities cannot be used */
    720 #  endif
    721     }
    722 #endif /* ! mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY */
    723     res = try_prio_string (tlsgnulib_base_priorities[i].cstr,
    724                            d_tls);
    725 
    726     if (GNUTLS_E_SUCCESS == res)
    727       return MHD_SC_OK;
    728     else if (GNUTLS_E_MEMORY_ERROR == res)
    729       return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
    730   }
    731 
    732   mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \
    733                "Failed to initialise TLS priorities cache");
    734   return MHD_SC_TLS_DAEMON_INIT_FAILED;
    735 }
    736 
    737 
    738 /**
    739  * De-initialise priorities cache
    740  * @param d_tls the daemon TLS settings
    741  */
    742 static MHD_FN_PAR_NONNULL_ALL_ void
    743 daemon_deinit_priorities_cache (struct mhd_TlsGnuDaemonData *restrict d_tls)
    744 {
    745 #if !defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY)
    746   mhd_assert (NULL != d_tls->pri_cache);
    747 #else
    748   if (NULL != d_tls->pri_cache)
    749 #endif
    750   gnutls_priority_deinit (d_tls->pri_cache);
    751 }
    752 
    753 
    754 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
    755 MHD_FN_PAR_OUT_ (3) mhd_StatusCodeInt
    756 mhd_tls_gnu_daemon_init3 (struct MHD_Daemon *restrict d,
    757                           struct DaemonOptions *restrict s,
    758                           struct mhd_TlsGnuDaemonData **restrict p_d_tls)
    759 {
    760   mhd_StatusCodeInt res;
    761   struct mhd_TlsGnuDaemonData *restrict d_tls;
    762 
    763   /* Successful initialisation must be checked earlier */
    764   mhd_assert (gnutls_lib_inited);
    765 
    766   res = check_app_tls_settings (d, s);
    767   if (MHD_SC_OK != res)
    768     return res;
    769 
    770   d_tls = (struct mhd_TlsGnuDaemonData *)
    771           mhd_calloc (1, sizeof (struct mhd_TlsGnuDaemonData));
    772   *p_d_tls = d_tls;
    773   if (NULL == d_tls)
    774     return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
    775 
    776 #ifndef HAVE_NULL_PTR_ALL_ZEROS
    777 #  ifdef mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3
    778   d_tls->dh_params = NULL;
    779 #  endif /* mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3 */
    780 #  ifdef mhd_HAVE_GNUTLS_ACME
    781   d_tls->acme_certs = NULL;
    782 #  endif /* mhd_HAVE_GNUTLS_ACME */
    783   d_tls->cred.gnu_cred = NULL;
    784   d_tls->pri_cache = NULL;
    785 #  ifdef mhd_TLS_GNU_HAS_ALPN
    786   d_tls->alpn_prots[0].data = NULL;
    787   d_tls->alpn_prots[1].data = NULL;
    788   d_tls->alpn_prots[2].data = NULL;
    789 #  endif /* mhd_TLS_GNU_HAS_ALPN */
    790 #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
    791 
    792 #ifdef mhd_HAVE_GNUTLS_ACME
    793   d_tls->acme_certs = mhd_daemon_get_acme_certs (d);
    794 #endif /* mhd_HAVE_GNUTLS_ACME */
    795 
    796 #ifdef mhd_TLS_GNU_HAS_ALPN
    797   // TODO: use daemon option to disable ALPN
    798   // TODO: use daemon option to select protocols for ALPN
    799   d_tls->num_alpn_prots = 0;
    800 
    801 #  ifdef MHD_SUPPORT_HTTP2
    802   if (1 /* enabled HTTP/2 ? */)
    803     d_tls->alpn_prots[d_tls->num_alpn_prots++] = mhd_alpn_dat_http2;
    804 #  endif /* MHD_SUPPORT_HTTP2 */
    805 
    806   if (1 /* enabled HTTP/1.x ? */)
    807   {
    808     d_tls->alpn_prots[d_tls->num_alpn_prots++] = mhd_alpn_dat_http1_1;
    809     d_tls->alpn_prots[d_tls->num_alpn_prots++] = mhd_alpn_dat_http1_0;
    810   }
    811 
    812   mhd_assert (mhd_ARR_NUM_ELEMS (d_tls->alpn_prots) >= d_tls->num_alpn_prots);
    813 #endif /* mhd_TLS_GNU_HAS_ALPN */
    814 
    815   if (!daemon_init_dh_data (d_tls))
    816   {
    817     mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \
    818                  "Failed to initialise Diffie-Hellman parameters " \
    819                  "for the daemon");
    820     res = MHD_SC_TLS_DAEMON_INIT_FAILED;
    821   }
    822   else
    823   {
    824     res = daemon_init_credentials (d,
    825                                    d_tls,
    826                                    s);
    827     if (MHD_SC_OK == res)
    828     {
    829       res = daemon_init_priorities_cache (d,
    830                                           d_tls,
    831                                           s);
    832       if (MHD_SC_OK == res)
    833         return MHD_SC_OK; /* Success exit point */
    834 
    835       /* Below is a clean-up code path */
    836       daemon_deinit_credentials (d_tls);
    837     }
    838     daemon_deinit_dh_data (d_tls);
    839   }
    840 
    841   free (d_tls);
    842   *p_d_tls = NULL;
    843   mhd_assert (MHD_SC_OK != res);
    844   return res;
    845 }
    846 
    847 
    848 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    849 MHD_FN_PAR_INOUT_ (1) void
    850 mhd_tls_gnu_daemon_deinit (struct mhd_TlsGnuDaemonData *restrict d_tls)
    851 {
    852   mhd_assert (NULL != d_tls);
    853   daemon_deinit_priorities_cache (d_tls);
    854   daemon_deinit_credentials (d_tls);
    855   daemon_deinit_dh_data (d_tls);
    856   free (d_tls);
    857 }
    858 
    859 
    860 /* ** Credentials creation / destruction ** */
    861 
    862 #ifdef mhd_HAVE_GNUTLS_ACME
    863 
    864 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_
    865 MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2)
    866 MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_NONNULL_ (6)
    867 MHD_FN_PAR_OUT_ (2)
    868 MHD_FN_PAR_IN_SIZE_ (4, 3) MHD_FN_PAR_IN_SIZE_ (6, 5)
    869 MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_CSTR_ (6) MHD_FN_PAR_CSTR_ (7)
    870 enum mhd_TlsCredCreateResult
    871 mhd_tls_gnu_cred_create_nopl (struct mhd_TlsGnuDaemonData *restrict d_tls,
    872                               union mhd_TlsCredDataPtr *restrict pp_cred,
    873                               size_t cert_len,
    874                               const char *restrict cert,
    875                               size_t key_len,
    876                               const char *restrict key,
    877                               const char *restrict pass)
    878 {
    879   enum mhd_TlsCredCreateResult res;
    880 
    881   pp_cred->gnu = (struct mhd_TlsGnuCredData *)
    882                  mhd_calloc (1,
    883                              sizeof(struct mhd_TlsGnuCredData));
    884   if (NULL == pp_cred->gnu)
    885     return mhd_TLS_CRED_CREATE_ALLOC_FAILED;
    886 
    887   res = tls_gnu_cred_init (d_tls,
    888                            pp_cred->gnu,
    889                            true,
    890                            cert_len,
    891                            cert,
    892                            key_len,
    893                            key,
    894                            pass);
    895   if (mhd_TLS_CRED_CREATE_OK == res)
    896     return res; /* Success exit point */
    897 
    898   free (pp_cred->gnu);
    899   pp_cred->gnu = NULL;
    900   mhd_assert (mhd_TLS_CRED_CREATE_OK != res);
    901   return res; /* Failure exit point */
    902 }
    903 
    904 
    905 MHD_INTERNAL void
    906 mhd_tls_gnu_cred_destroy_nodmn (union mhd_TlsCredDataPtr cred)
    907 {
    908   mhd_ASSUME (NULL != cred.gnu);
    909   if (tls_gnu_cred_deinit (cred.gnu,
    910                            true))
    911     free (cred.gnu);
    912 }
    913 
    914 
    915 #endif /* mhd_HAVE_GNUTLS_ACME */
    916 
    917 
    918 /* ** Connection initialisation / de-initialisation ** */
    919 
    920 MHD_INTERNAL size_t
    921 mhd_tls_gnu_conn_get_tls_size_v (void)
    922 {
    923   return sizeof (struct mhd_TlsGnuConnData);
    924 }
    925 
    926 
    927 #ifdef mhd_TLS_GNU_HAS_ALPN
    928 /**
    929  * Set the configured ALPN protocols for a GnuTLS session.
    930  *
    931  * @param d_tls the daemon TLS settings containing the configured protocols
    932  * @param[in,out] c_tls the connection TLS data containing the session
    933  * @return 'true' if no protocols are configured or the protocols were set
    934  *         successfully,
    935  *         'false' otherwise
    936  */
    937 static MHD_FN_PAR_NONNULL_ALL_ bool
    938 conn_set_configured_alpn (const struct mhd_TlsGnuDaemonData *restrict d_tls,
    939                           struct mhd_TlsGnuConnData *restrict c_tls)
    940 {
    941   unsigned int alpn_flags;
    942 
    943   if (0u == d_tls->num_alpn_prots)
    944     return true; /* Nothing to set */
    945 
    946 
    947   alpn_flags = 0;
    948 #  if 0
    949   alpn_flags |= GNUTLS_ALPN_SERVER_PRECEDENCE;
    950 #  endif
    951 
    952   return (GNUTLS_E_SUCCESS ==
    953           gnutls_alpn_set_protocols (c_tls->sess,
    954                                      d_tls->alpn_prots,
    955                                      d_tls->num_alpn_prots,
    956                                      alpn_flags));
    957 }
    958 
    959 
    960 #endif /* mhd_TLS_GNU_HAS_ALPN */
    961 
    962 
    963 #ifdef mhd_HAVE_GNUTLS_ACME
    964 
    965 #  ifdef mhd_USE_GNUTLS_EXT_RAW_PARSE
    966 /**
    967  * Check one ClientHello extension reported by the GnuTLS raw extension parser.
    968  *
    969  * @param[in,out] cls the incremental ACME ClientHello check state
    970  * @param tls_id the TLS extension identifier
    971  * @param data the complete extension data
    972  * @param data_size the size of @p data in bytes
    973  * @return #GNUTLS_E_SUCCESS if checking should continue,
    974  *         #GNUTLS_E_ILLEGAL_PARAMETER if checking should stop
    975  */
    976 static int
    977 mhd_tls_gnu_acme_check_cb (void *cls,
    978                            unsigned int tls_id,
    979                            const unsigned char *data,
    980                            unsigned int data_size)
    981 {
    982   struct mhd_TlsClientHelloAcmeCheckData *const restrict check
    983     = (struct mhd_TlsClientHelloAcmeCheckData *)cls;
    984   size_t data_size_t = (size_t)data_size;
    985 
    986   if (data_size != data_size_t)
    987     return GNUTLS_E_ILLEGAL_PARAMETER; /* The extension is too large, reject */
    988 
    989   if (!mhd_tls_acme_check_ext (tls_id,
    990                                data_size_t,
    991                                data,
    992                                check))
    993     return GNUTLS_E_ILLEGAL_PARAMETER; /* Any negative number stops processing */
    994 
    995   return GNUTLS_E_SUCCESS;
    996 }
    997 
    998 
    999 #  endif /* mhd_USE_GNUTLS_EXT_RAW_PARSE */
   1000 
   1001 /**
   1002  * GnuTLS handshake hook for checking a ClientHello message for ACME ALPN.
   1003  *
   1004  * @param[in,out] sess the GnuTLS session processing the ClientHello message
   1005  * @param htype the type of the handshake message being processed,
   1006  *              one of the @a gnutls_handshake_description_t values
   1007  * @param when set to non-zero called during post-processing or generation,
   1008  *             and to zero otherwise
   1009  * @param incoming set to non-zero if processing incoming message and
   1010  *                 to zero if processing an outgoing message
   1011  * @param msg the content of the handshake message without the handshake
   1012  *            headers
   1013  * @return #GNUTLS_E_SUCCESS for a normal HTTPS ClientHello or after successful
   1014  *         ACME setup,
   1015  *         a negative GnuTLS error code to abort the handshake otherwise
   1016  */
   1017 static int
   1018 mhd_tls_gnu_hello_hook_acme (gnutls_session_t sess,
   1019                              unsigned int htype,
   1020                              unsigned when,
   1021                              unsigned int incoming,
   1022                              const gnutls_datum_t *msg)
   1023 {
   1024   struct mhd_TlsGnuConnData *const restrict c_tls =
   1025     (struct mhd_TlsGnuConnData *)gnutls_session_get_ptr (sess);
   1026   const union mhd_TlsCredDataPtr *acme_cred; /* Set to non-NULL if request is recognised ACME challenge */
   1027   union mhd_TlsCredDataPtr replaced_cred; /* Set to non-NULL if the ACME credentials are replaced */
   1028   bool acme_ok;  /* Used special response for ACME challenge  */
   1029 
   1030   mhd_assert (GNUTLS_HANDSHAKE_CLIENT_HELLO == htype);
   1031   mhd_assert (GNUTLS_HOOK_PRE == when);
   1032   mhd_assert (incoming);
   1033   mhd_assert (NULL != c_tls);
   1034   mhd_assert (NULL != c_tls->d_tls);
   1035   mhd_assert (c_tls->sess == sess);
   1036 
   1037 #  ifdef NDEBUG
   1038   (void)htype;
   1039   (void)when;
   1040   (void)incoming;
   1041 #  endif /* NDEBUG */
   1042 
   1043 #  ifdef mhd_USE_GNUTLS_EXT_RAW_PARSE
   1044   if (1)
   1045   {
   1046     bool parse_ok;
   1047     struct mhd_TlsClientHelloAcmeCheckData acme_check;
   1048 
   1049     mhd_tls_acme_check_ext_init (&acme_check,
   1050                                  c_tls->d_tls->acme_certs);
   1051 
   1052     parse_ok = (GNUTLS_E_SUCCESS ==
   1053                 gnutls_ext_raw_parse (&acme_check,
   1054                                       &mhd_tls_gnu_acme_check_cb,
   1055                                       msg,
   1056                                       GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO));
   1057 
   1058     acme_cred = mhd_tls_acme_check_ext_finish (&acme_check,
   1059                                                parse_ok);
   1060   }
   1061 #  else  /* ! mhd_USE_GNUTLS_EXT_RAW_PARSE */
   1062   if (1)
   1063   {
   1064     size_t msg_size = (size_t)msg->size;
   1065 
   1066     if (msg_size != msg->size)
   1067       return GNUTLS_E_ILLEGAL_PARAMETER; /* The message is too large, reject */
   1068 
   1069     acme_cred =
   1070       mhd_tls_acme_check_clienthello_body (msg_size,
   1071                                            msg->data,
   1072                                            c_tls->d_tls->acme_certs);
   1073   }
   1074 #  endif /* ! mhd_USE_GNUTLS_EXT_RAW_PARSE */
   1075 
   1076   if (NULL == acme_cred)
   1077   {
   1078     /* Normal (not ACME ALPN challenge) connection */
   1079     if (NULL != c_tls->acme_cred.gnu) /* Second ClientHello after TLS 1.3 HRR with another result */
   1080     {
   1081       mhd_assert (c_tls->clienthello_processed);
   1082       return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; /* Unmatched ClientHello, abort handshake */
   1083     }
   1084 
   1085     /* Process the connection as a normal HTTPS connection */
   1086     if (!c_tls->clienthello_processed)
   1087       (void)conn_set_configured_alpn (c_tls->d_tls,   /* Function cannot be called twice for the same connection */
   1088                                       c_tls);
   1089 
   1090     c_tls->clienthello_processed = true;
   1091     return GNUTLS_E_SUCCESS; /* Non-ACME connection exit point */
   1092   }
   1093 
   1094   /* Process the connection as an ACME ALPN challenge connection */
   1095 
   1096   mhd_assert (NULL != acme_cred->gnu);
   1097 
   1098   acme_ok = true; /* Assume success unless something fails below */
   1099   if (c_tls->clienthello_processed)
   1100     acme_ok = (NULL != c_tls->acme_cred.gnu);  /* The first ClientHello was processed as an ACME connection too */
   1101 
   1102   replaced_cred.gnu = NULL;
   1103   if (acme_ok)
   1104   {
   1105     if ((NULL != c_tls->acme_cred.gnu)
   1106         && (acme_cred->gnu != c_tls->acme_cred.gnu))
   1107     { /* The list of ACME credentials has changed since the last ClientHello, release the old credentials */
   1108       replaced_cred = c_tls->acme_cred;
   1109       c_tls->acme_cred.gnu = NULL;
   1110     }
   1111 
   1112     if (NULL == c_tls->acme_cred.gnu)
   1113     {
   1114       acme_ok = tls_gnu_cred_use_refcnt (c_tls,
   1115                                          acme_cred->gnu);
   1116       if (acme_ok)
   1117         c_tls->acme_cred = *acme_cred;
   1118     }
   1119   }
   1120   /* If non-NULL was returned, the daemon's certificates list must be
   1121      unlocked */
   1122   mhd_daemon_acme_cert_r_unlock (c_tls->d_tls->acme_certs);
   1123 
   1124   /* Release old credentials if any */
   1125   if (NULL != replaced_cred.gnu)
   1126     mhd_tls_gnu_cred_destroy_nodmn (replaced_cred);
   1127 
   1128   if (acme_ok)
   1129   {
   1130     /* Handle ALPN */
   1131     if (!c_tls->clienthello_processed)
   1132       acme_ok = (GNUTLS_E_SUCCESS ==
   1133                  gnutls_alpn_set_protocols (c_tls->sess,   /* Function cannot be called twice for the same connection */
   1134                                             &mhd_alpn_dat_acme,
   1135                                             1u,
   1136                                             GNUTLS_ALPN_MANDATORY));
   1137   }
   1138 
   1139   c_tls->clienthello_processed = true;
   1140   return acme_ok ?
   1141          GNUTLS_E_SUCCESS
   1142          : GNUTLS_E_INTERNAL_ERROR; /* Any negative number stops processing */
   1143 }
   1144 
   1145 
   1146 #endif /* mhd_HAVE_GNUTLS_ACME */
   1147 
   1148 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   1149 MHD_FN_PAR_OUT_ (3) bool
   1150 mhd_tls_gnu_conn_init (const struct mhd_TlsGnuDaemonData *restrict d_tls,
   1151                        const struct mhd_ConnSocket *sk,
   1152                        struct mhd_TlsGnuConnData *restrict c_tls)
   1153 {
   1154   unsigned int c_flags;
   1155   int res;
   1156 
   1157   c_flags = GNUTLS_SERVER;
   1158 #if GNUTLS_VERSION_NUMBER >= 0x030000
   1159   /* Note: the proper support for the blocking sockets may require use of
   1160      gnutls_handshake_set_timeout() and
   1161      gnutls_transport_set_pull_timeout_function() (the latter is not actually
   1162      required for the modern GnuTLS versions at least) */
   1163   if (sk->props.is_nonblck)
   1164     c_flags |= GNUTLS_NONBLOCK;
   1165 #endif
   1166 #ifdef mhd_TLS_GNU_HAS_NO_SIGNAL
   1167   c_flags |= GNUTLS_NO_SIGNAL;
   1168 #endif
   1169 
   1170   if (GNUTLS_E_SUCCESS !=
   1171       gnutls_init (&(c_tls->sess),
   1172                    c_flags))
   1173     return false;
   1174 
   1175 #if GNUTLS_VERSION_NUMBER >= 0x030100
   1176   if (!sk->props.is_nonblck)
   1177     gnutls_handshake_set_timeout (c_tls->sess,
   1178                                   GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
   1179 #endif
   1180 #if !defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY)
   1181   mhd_assert (NULL != d_tls->pri_cache);
   1182 #else
   1183   if (NULL == d_tls->pri_cache)
   1184     res = gnutls_set_default_priority (c_tls->sess);
   1185   else
   1186 #endif
   1187   res = gnutls_priority_set (c_tls->sess,
   1188                              d_tls->pri_cache);
   1189 
   1190   if (GNUTLS_E_SUCCESS == res)
   1191   {
   1192     if (tls_gnu_cred_use_norefcnt (c_tls,
   1193                                    &(d_tls->cred)))
   1194     {
   1195 #if defined(mhd_TLS_GNU_HAS_TRANSP_SET_INT) && defined(MHD_SOCKETS_KIND_POSIX)
   1196       gnutls_transport_set_int (c_tls->sess,
   1197                                 sk->fd);
   1198 #elif defined(MHD_SOCKETS_KIND_POSIX)
   1199       gnutls_transport_set_ptr (c_tls->sess,
   1200                                 mhd_INT_TO_PTR (sk->fd));
   1201 #else  /* MHD_SOCKETS_KIND_WINSOCK */
   1202       gnutls_transport_set_ptr (c_tls->sess,
   1203                                 mhd_UINT_TO_PTR (sk->fd));
   1204 #endif /* MHD_SOCKETS_KIND_WINSOCK */
   1205 
   1206       /* The basic TLS session properties has been set.
   1207          The rest is optional settings. */
   1208 
   1209 #if defined(mhd_HAVE_GNUTLS_ACME)
   1210 #  ifndef HAVE_NULL_PTR_ALL_ZEROS
   1211       c_tls->acme_cred.gnu = NULL;
   1212       c_tls->d_tls = NULL;
   1213 #  endif /* HAVE_NULL_PTR_ALL_ZEROS */
   1214       if (mhd_daemon_has_acme_certs (d_tls->acme_certs))
   1215       {
   1216         c_tls->d_tls = d_tls;
   1217         gnutls_session_set_ptr (c_tls->sess,
   1218                                 c_tls);
   1219         gnutls_handshake_set_hook_function (c_tls->sess,
   1220                                             GNUTLS_HANDSHAKE_CLIENT_HELLO,
   1221                                             GNUTLS_HOOK_PRE,
   1222                                             &mhd_tls_gnu_hello_hook_acme);
   1223         /* Do not set ALPN here as ALPN selection depends on whether
   1224            connection is processed as HTTPS connection or as ACME ALPN
   1225            challenge connection. */
   1226       }
   1227       else
   1228 #endif /* mhd_HAVE_GNUTLS_ACME */
   1229 #ifdef mhd_TLS_GNU_HAS_ALPN
   1230       (void)conn_set_configured_alpn (d_tls,
   1231                                       c_tls);  /* Ignore result */
   1232 #endif /* mhd_TLS_GNU_HAS_ALPN */
   1233 
   1234 #ifndef NDEBUG
   1235       c_tls->dbg.is_inited = true;
   1236 #endif /* ! NDEBUG */
   1237 
   1238       return true; /* Success exit point */
   1239     }
   1240     /* Below is a clean-up code path */
   1241   }
   1242 
   1243   gnutls_deinit (c_tls->sess);
   1244   return false; /* Failure exit point */
   1245 }
   1246 
   1247 
   1248 /**
   1249  * De-initialise connection TLS settings.
   1250  * The provided pointer is not freed/deallocated.
   1251  * @param c_tls the initialised connection TLS settings
   1252  */
   1253 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
   1254 mhd_tls_gnu_conn_deinit (struct mhd_TlsGnuConnData *restrict c_tls)
   1255 {
   1256   mhd_assert (NULL != c_tls->sess);
   1257   mhd_assert (c_tls->dbg.is_inited);
   1258   gnutls_deinit (c_tls->sess);
   1259 #ifdef mhd_HAVE_GNUTLS_ACME
   1260   if (NULL != c_tls->acme_cred.gnu)
   1261     mhd_tls_gnu_cred_destroy_nodmn (c_tls->acme_cred);
   1262 #endif /* mhd_HAVE_GNUTLS_ACME */
   1263 }
   1264 
   1265 
   1266 /* ** TLS connection establishing ** */
   1267 
   1268 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   1269 enum mhd_TlsProcedureResult
   1270 mhd_tls_gnu_conn_handshake (struct mhd_TlsGnuConnData *restrict c_tls)
   1271 {
   1272   int res;
   1273 
   1274   mhd_assert (c_tls->dbg.is_inited);
   1275   mhd_assert (!c_tls->dbg.is_tls_handshake_completed);
   1276   mhd_assert (!c_tls->dbg.is_finished);
   1277   mhd_assert (!c_tls->dbg.is_failed);
   1278 
   1279   res = gnutls_handshake (c_tls->sess);
   1280   switch (res)
   1281   {
   1282   case GNUTLS_E_SUCCESS:
   1283 #ifndef NDEBUG
   1284     c_tls->dbg.is_tls_handshake_completed = true;
   1285 #endif /* ! NDEBUG */
   1286     return mhd_TLS_PROCED_SUCCESS;
   1287   case GNUTLS_E_INTERRUPTED:
   1288   case GNUTLS_E_AGAIN:
   1289   case GNUTLS_E_WARNING_ALERT_RECEIVED: /* Ignore any warning for now */
   1290     if (1)
   1291     {
   1292       int is_sending;
   1293 
   1294       is_sending = gnutls_record_get_direction (c_tls->sess);
   1295       if (GNUTLS_E_INTERRUPTED == res)
   1296         return is_sending ?
   1297                mhd_TLS_PROCED_SEND_INTERRUPTED :
   1298                mhd_TLS_PROCED_RECV_INTERRUPTED;
   1299       return is_sending ?
   1300              mhd_TLS_PROCED_SEND_MORE_NEEDED :
   1301              mhd_TLS_PROCED_RECV_MORE_NEEDED;
   1302     }
   1303     break;
   1304   default:
   1305     break;
   1306   }
   1307 #ifndef NDEBUG
   1308   c_tls->dbg.is_failed = true;
   1309 #endif /* ! NDEBUG */
   1310   return mhd_TLS_PROCED_FAILED;
   1311 }
   1312 
   1313 
   1314 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   1315 enum mhd_TlsProcedureResult
   1316 mhd_tls_gnu_conn_shutdown (struct mhd_TlsGnuConnData *restrict c_tls)
   1317 {
   1318   int res;
   1319 
   1320   mhd_assert (c_tls->dbg.is_inited);
   1321   mhd_assert (c_tls->dbg.is_tls_handshake_completed);
   1322   mhd_assert (!c_tls->dbg.is_finished);
   1323   mhd_assert (!c_tls->dbg.is_failed);
   1324 
   1325   res = gnutls_bye (c_tls->sess,
   1326                     c_tls->rmt_shut_tls_wr ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR);
   1327   switch (res)
   1328   {
   1329   case GNUTLS_E_SUCCESS:
   1330 #ifndef NDEBUG
   1331     c_tls->dbg.is_finished = true;
   1332 #endif /* ! NDEBUG */
   1333     return mhd_TLS_PROCED_SUCCESS;
   1334   case GNUTLS_E_INTERRUPTED:
   1335   case GNUTLS_E_AGAIN:
   1336   case GNUTLS_E_WARNING_ALERT_RECEIVED: /* Ignore any warning for now */
   1337     if (1)
   1338     {
   1339       int is_sending;
   1340 
   1341       is_sending = gnutls_record_get_direction (c_tls->sess);
   1342       if (GNUTLS_E_INTERRUPTED == res)
   1343         return is_sending ?
   1344                mhd_TLS_PROCED_SEND_INTERRUPTED :
   1345                mhd_TLS_PROCED_RECV_INTERRUPTED;
   1346       return is_sending ?
   1347              mhd_TLS_PROCED_SEND_MORE_NEEDED :
   1348              mhd_TLS_PROCED_RECV_MORE_NEEDED;
   1349     }
   1350     break;
   1351   default:
   1352     break;
   1353   }
   1354 #ifndef NDEBUG
   1355   c_tls->dbg.is_failed = true;
   1356 #endif /* ! NDEBUG */
   1357   return mhd_TLS_PROCED_FAILED;
   1358 }
   1359 
   1360 
   1361 /* ** Data receiving and sending ** */
   1362 
   1363 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   1364 MHD_FN_PAR_OUT_SIZE_ (3, 2)
   1365 MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
   1366 mhd_tls_gnu_conn_recv (struct mhd_TlsGnuConnData *restrict c_tls,
   1367                        size_t buf_size,
   1368                        char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
   1369                        size_t *restrict received)
   1370 {
   1371   ssize_t res;
   1372 
   1373   mhd_assert (c_tls->dbg.is_inited);
   1374   mhd_assert (c_tls->dbg.is_tls_handshake_completed);
   1375   mhd_assert (!c_tls->dbg.is_finished);
   1376   mhd_assert (!c_tls->dbg.is_failed);
   1377 #ifdef mhd_HAVE_GNUTLS_ACME
   1378   mhd_assert (NULL == c_tls->acme_cred.gnu);
   1379 #endif /* mhd_HAVE_GNUTLS_ACME */
   1380 
   1381   /* Check for GnuTLS return value limitation */
   1382   if (0 > (ssize_t)buf_size)
   1383     buf_size = (ssize_t)((~((size_t)0u)) >> 1);   /* SSIZE_MAX */
   1384 
   1385   res = gnutls_record_recv (c_tls->sess,
   1386                             buf,
   1387                             buf_size);
   1388   if (0 >= res)
   1389   {
   1390     *received = 0;
   1391     switch (res)
   1392     {
   1393     case 0: /* Not an error */
   1394       c_tls->rmt_shut_tls_wr = true;
   1395       return mhd_SOCKET_ERR_NO_ERROR;
   1396     case GNUTLS_E_AGAIN:
   1397       return mhd_SOCKET_ERR_AGAIN;
   1398     case GNUTLS_E_INTERRUPTED:
   1399       return mhd_SOCKET_ERR_INTR;
   1400     case GNUTLS_E_PREMATURE_TERMINATION:
   1401       return mhd_SOCKET_ERR_CONNRESET;
   1402     default:
   1403       break;
   1404     }
   1405     /* Treat all other kinds of errors as hard errors */
   1406 #ifndef NDEBUG
   1407     c_tls->dbg.is_failed = true;
   1408 #endif /* ! NDEBUG */
   1409     return mhd_SOCKET_ERR_TLS;
   1410   }
   1411 
   1412   *received = (size_t)res;
   1413   return mhd_SOCKET_ERR_NO_ERROR;
   1414 }
   1415 
   1416 
   1417 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   1418 mhd_tls_gnu_conn_has_data_in (struct mhd_TlsGnuConnData *restrict c_tls)
   1419 {
   1420   return 0 != gnutls_record_check_pending (c_tls->sess);
   1421 }
   1422 
   1423 
   1424 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   1425 MHD_FN_PAR_IN_SIZE_ (3, 2)
   1426 MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
   1427 mhd_tls_gnu_conn_send4 (struct mhd_TlsGnuConnData *restrict c_tls,
   1428                         size_t buf_size,
   1429                         const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
   1430                         size_t *restrict sent)
   1431 {
   1432   ssize_t res;
   1433 
   1434   mhd_assert (c_tls->dbg.is_inited);
   1435   mhd_assert (c_tls->dbg.is_tls_handshake_completed);
   1436   mhd_assert (!c_tls->dbg.is_finished);
   1437   mhd_assert (!c_tls->dbg.is_failed);
   1438 #ifdef mhd_HAVE_GNUTLS_ACME
   1439   mhd_assert (NULL == c_tls->acme_cred.gnu);
   1440 #endif /* mhd_HAVE_GNUTLS_ACME */
   1441 
   1442   /* Check for GnuTLS return value limitation */
   1443   if (0 > (ssize_t)buf_size)
   1444     buf_size = (ssize_t)((~((size_t)0u)) >> 1);   /* SSIZE_MAX */
   1445 
   1446   res = gnutls_record_send (c_tls->sess,
   1447                             buf,
   1448                             buf_size);
   1449 
   1450   mhd_assert (0 != res);
   1451 
   1452   if (0 > res)
   1453   {
   1454     *sent = 0;
   1455     switch (res)
   1456     {
   1457     case GNUTLS_E_AGAIN:
   1458       return mhd_SOCKET_ERR_AGAIN;
   1459     case GNUTLS_E_INTERRUPTED:
   1460       return mhd_SOCKET_ERR_INTR;
   1461     case GNUTLS_E_PREMATURE_TERMINATION:
   1462       return mhd_SOCKET_ERR_CONNRESET;
   1463     default:
   1464       break;
   1465     }
   1466     /* Treat all other kinds of errors as hard errors */
   1467 #ifndef NDEBUG
   1468     c_tls->dbg.is_failed = true;
   1469 #endif /* ! NDEBUG */
   1470     return mhd_SOCKET_ERR_TLS;
   1471   }
   1472 
   1473   *sent = (size_t)res;
   1474   return mhd_SOCKET_ERR_NO_ERROR;
   1475 }
   1476 
   1477 
   1478 /* ** TLS connection information ** */
   1479 
   1480 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   1481 MHD_FN_PAR_OUT_ (2) void
   1482 mhd_tls_gnu_conn_get_tls_sess (
   1483   struct mhd_TlsGnuConnData *restrict c_tls,
   1484   union MHD_ConnInfoDynamicTlsSess *restrict tls_sess_out)
   1485 {
   1486   tls_sess_out->v_gnutls_session = c_tls->sess;
   1487 }
   1488 
   1489 
   1490 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   1491 MHD_FN_PAR_OUT_ (2) bool
   1492 mhd_tls_gnu_conn_get_tls_ver (struct mhd_TlsGnuConnData *restrict c_tls,
   1493                               struct mhd_StctTlsVersion *restrict tls_ver_out)
   1494 {
   1495   gnutls_protocol_t gtls_tls_ver;
   1496 
   1497   mhd_assert (c_tls->dbg.is_tls_handshake_completed);
   1498 
   1499   gtls_tls_ver = gnutls_protocol_get_version (c_tls->sess);
   1500 #if GNUTLS_VERSION_NUMBER >= 0x030603
   1501   if (GNUTLS_TLS1_3 == gtls_tls_ver)
   1502     tls_ver_out->tls_ver = MHD_TLS_VERSION_1_3;
   1503   else
   1504 #endif
   1505   if (GNUTLS_TLS1_2 == gtls_tls_ver)
   1506     tls_ver_out->tls_ver = MHD_TLS_VERSION_1_2;
   1507   else if (GNUTLS_TLS1_1 == gtls_tls_ver)
   1508     tls_ver_out->tls_ver = MHD_TLS_VERSION_1_1;
   1509   else if (GNUTLS_TLS1_0 == gtls_tls_ver)
   1510     tls_ver_out->tls_ver = MHD_TLS_VERSION_1_0;
   1511   else if (GNUTLS_VERSION_UNKNOWN == gtls_tls_ver)
   1512     return false; /* The TLS version cannot be detected */
   1513   else
   1514     /* The TLS version is known for GnuTLS, but cannot be mapped */
   1515     tls_ver_out->tls_ver = MHD_TLS_VERSION_UNKNOWN;
   1516 
   1517   return true;
   1518 }
   1519 
   1520 
   1521 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ enum mhd_TlsAlpnProt
   1522 mhd_tls_gnu_conn_get_alpn_prot (struct mhd_TlsGnuConnData *restrict c_tls)
   1523 {
   1524 #ifdef mhd_TLS_GNU_HAS_ALPN
   1525   gnutls_datum_t sel_prot;
   1526 #  ifdef mhd_HAVE_GNUTLS_ACME
   1527   mhd_assert (NULL == c_tls->acme_cred.gnu);
   1528 #  endif /* mhd_HAVE_GNUTLS_ACME */
   1529 
   1530   if (GNUTLS_E_SUCCESS ==
   1531       gnutls_alpn_get_selected_protocol (c_tls->sess, &sel_prot))
   1532     return mhd_tls_alpn_decode_n (sel_prot.size,
   1533                                   sel_prot.data);
   1534 #endif /* mhd_TLS_GNU_HAS_ALPN */
   1535 
   1536   return mhd_TLS_ALPN_PROT_NOT_SELECTED;
   1537 }
   1538 
   1539 
   1540 #ifdef mhd_HAVE_TLS_ACME
   1541 #  ifdef mhd_HAVE_GNUTLS_ACME
   1542 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   1543 mhd_tls_gnu_conn_is_acme (struct mhd_TlsGnuConnData *restrict c_tls)
   1544 {
   1545   return (NULL != c_tls->acme_cred.gnu);
   1546 }
   1547 
   1548 
   1549 #  endif /* mhd_HAVE_GNUTLS_ACME */
   1550 #endif /* mhd_HAVE_TLS_ACME */