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