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 */