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