From cd6fda427cc703174cf744b2424efc9e4239e81b Mon Sep 17 00:00:00 2001 From: "Evgeny Grin (Karlson2k)" Date: Mon, 18 Nov 2024 17:16:53 +0300 Subject: [PATCH] Implemented initialisation of daemon's GnuTLS data --- src/include/d_options.rec | 59 ++- src/include/microhttpd2.h | 61 +-- .../microhttpd2_generated_daemon_options.h | 56 +-- src/include/microhttpd2_preamble.h.in | 51 ++- src/mhd2/Makefile.am | 3 +- src/mhd2/daemon_create.c | 9 +- src/mhd2/daemon_options.h | 8 +- src/mhd2/daemon_set_options.c | 51 ++- src/mhd2/daemon_start.c | 123 ++++++- src/mhd2/mhd_daemon.h | 21 ++ src/mhd2/mhd_tls_funcs.c | 49 +++ src/mhd2/mhd_tls_funcs.h | 63 ++++ src/mhd2/tls_dh_params.h | 53 +++ src/mhd2/tls_gnu_conn_data.h | 49 +++ src/mhd2/tls_gnu_daemon_data.h | 61 +++ src/mhd2/tls_gnu_funcs.c | 346 +++++++++++++++++- src/mhd2/tls_gnu_funcs.h | 38 ++ src/mhd2/tls_gnu_tls_lib.h | 103 ++++++ 18 files changed, 1110 insertions(+), 94 deletions(-) create mode 100644 src/mhd2/mhd_tls_funcs.c create mode 100644 src/mhd2/tls_dh_params.h create mode 100644 src/mhd2/tls_gnu_conn_data.h create mode 100644 src/mhd2/tls_gnu_daemon_data.h create mode 100644 src/mhd2/tls_gnu_tls_lib.h diff --git a/src/include/d_options.rec b/src/include/d_options.rec index 4d4b089b..345082ce 100644 --- a/src/include/d_options.rec +++ b/src/include/d_options.rec @@ -149,18 +149,67 @@ Argument1: enum MHD_TlsBackend backend Description1: the TLS backend to use, + #MHD_TLS_BACKEND_NONE for non-TLS (plain TCP) connections -Name: tls_key_cert +Name: tls_cert_key Value: 121 Comment: Provide TLS key and certificate data in-memory. + Works only if TLS mode is enabled. Type: struct MHD_DaemonOptionValueTlsCert -Argument1: const char *mem_key -Description1: the private key loaded into memory (not a filename) -Argument2: const char *mem_cert -Description2: the certificate loaded into memory (not a filename) +Argument1: /* const */ char *mem_cert +Description1: The X.509 certificates chain in PEM format loaded into memory (not a filename). ++ The first certificate must be the server certificate, following by the chain of signing ++ certificates up to (but not including) CA root certificate. +Argument2: const char *mem_key +Description2: the private key in PEM format loaded into memory (not a filename) Argument3: const char *mem_pass Description3: the option passphrase phrase to decrypt the private key, + could be NULL is private does not need a password +CustomSetter: /* custom setter */ ++ if ((NULL == option->val.tls_cert_key.v_mem_cert) ++ || (NULL == option->val.tls_cert_key.v_mem_key)) ++ return MHD_SC_TLS_CONF_BAD_CERT; ++ else ++ { ++ size_t cert_size; ++ size_t key_size; ++ size_t pass_size; ++ cert_size = strlen (option->val.tls_cert_key.v_mem_cert); ++ key_size = strlen (option->val.tls_cert_key.v_mem_key); ++ if ((0 == cert_size) ++ || (0 == key_size)) ++ return MHD_SC_TLS_CONF_BAD_CERT; ++ ++cert_size; /* Space for zero-termination */ ++ ++key_size; /* Space for zero-termination */ ++ if (NULL != option->val.tls_cert_key.v_mem_pass) ++ pass_size = strlen (option->val.tls_cert_key.v_mem_cert); ++ else ++ pass_size = 0; ++ if (0 != pass_size) ++ ++pass_size; /* Space for zero-termination */ ++ if (NULL != settings->tls_cert_key.v_mem_cert) ++ free (settings->tls_cert_key.v_mem_cert); // TODO: Support multiple certificates!! ++ settings->tls_cert_key.v_mem_cert = malloc (cert_size + key_size + pass_size); ++ if (NULL == settings->tls_cert_key.v_mem_cert) ++ return MHD_SC_DAEMON_MALLOC_FAILURE; ++ memcpy (settings->tls_cert_key.v_mem_cert, ++ option->val.tls_cert_key.v_mem_cert, ++ cert_size); ++ memcpy (settings->tls_cert_key.v_mem_cert + cert_size, ++ option->val.tls_cert_key.v_mem_key, ++ key_size); ++ settings->tls_cert_key.v_mem_key = ++ settings->tls_cert_key.v_mem_cert + cert_size; ++ if (0 != pass_size) ++ { ++ memcpy (settings->tls_cert_key.v_mem_cert + cert_size + key_size, ++ option->val.tls_cert_key.v_mem_pass, ++ pass_size); ++ settings->tls_cert_key.v_mem_pass = ++ settings->tls_cert_key.v_mem_cert + cert_size + key_size; ++ } ++ else ++ settings->tls_cert_key.v_mem_pass = NULL; ++ } + Name: tls_client_ca Value: 122 diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h index ef9e6ba3..d2666225 100644 --- a/src/include/microhttpd2.h +++ b/src/include/microhttpd2.h @@ -459,6 +459,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode , /** * Failed to allocate memory for the daemon resources. + * TODO: combine similar error codes for daemon */ MHD_SC_DAEMON_MALLOC_FAILURE = 30081 , @@ -713,12 +714,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_TLS_DISABLED = 50000 , - /** - * The application attempted to setup TLS parameters before - * enabling TLS. - */ - MHD_SC_TLS_BACKEND_UNINITIALIZED = 50003 - , /** * The selected TLS backend does not yet support this operation. */ @@ -1286,6 +1281,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_LIB_INIT_GLOBAL_FAILED = 51000 , + /** + * Failed to initialise TLS context for the daemon + */ + MHD_SC_DAEMON_TLS_INIT_FAILED = 51200 + , /** * Something wrong in the internal MHD logic. * This error should be never returned if MHD works as expected. @@ -1309,17 +1309,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60001 , - /** - * The application requested an unsupported TLS backend to be used. - */ - MHD_SC_TLS_BACKEND_UNSUPPORTED = 60003 - , - /** - * The application requested a TLS cipher suite which is not - * supported by the selected backend. - */ - MHD_SC_TLS_CIPHERS_INVALID = 60004 - , /** * MHD is closing a connection because the application * logic to generate the response data failed. @@ -1390,6 +1379,34 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL = 60016 , + /** + * The application requested an unsupported TLS backend to be used. + */ + MHD_SC_TLS_BACKEND_UNSUPPORTED = 60020 + , + /** + * The application attempted to setup TLS parameters before + * enabling TLS. + */ + MHD_SC_TLS_BACKEND_UNINITIALIZED = 60021 + , + /** + * The application requested a TLS backend which cannot be used due + * to missing TLS dynamic library or backend initialisation problem. + */ + MHD_SC_TLS_BACKEND_UNAVAILABLE = 60022 + , + /** + * Provided TLS certificate and/or private key are incorrect + */ + MHD_SC_TLS_CONF_BAD_CERT = 60023 + , + /** + * The application requested a TLS cipher suite which is not + * supported by the selected backend. + */ + MHD_SC_TLS_CIPHERS_INVALID = 60024 + , /** * The response header name has forbidden characters or token */ @@ -3954,16 +3971,18 @@ MHD_D_OPTION_TLS ( /** * Provide TLS key and certificate data in-memory. * Works only if TLS mode is enabled. - * @param mem_key the private key loaded into memory (not a filename) - * @param mem_cert the certificate loaded into memory (not a filename) + * @param mem_cert The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. + * @param mem_key the private key in PEM format loaded into memory (not a filename) * @param mem_pass the option passphrase phrase to decrypt the private key, * could be NULL is private does not need a password * @return structure with the requested setting */ struct MHD_DaemonOptionAndValue -MHD_D_OPTION_TLS_KEY_CERT ( +MHD_D_OPTION_TLS_CERT_KEY ( + /* const */ char *mem_cert, const char *mem_key, - const char *mem_cert, const char *mem_pass ); diff --git a/src/include/microhttpd2_generated_daemon_options.h b/src/include/microhttpd2_generated_daemon_options.h index db6d2d73..28c14efd 100644 --- a/src/include/microhttpd2_generated_daemon_options.h +++ b/src/include/microhttpd2_generated_daemon_options.h @@ -108,7 +108,7 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used. * Provide TLS key and certificate data in-memory. * Works only if TLS mode is enabled. */ - MHD_D_O_TLS_KEY_CERT = 121 + MHD_D_O_TLS_CERT_KEY = 121 , /** @@ -424,19 +424,21 @@ struct MHD_DaemonOptionValueTFO }; /** - * Data for #MHD_D_O_TLS_KEY_CERT + * Data for #MHD_D_O_TLS_CERT_KEY */ struct MHD_DaemonOptionValueTlsCert { /** - * the private key loaded into memory (not a filename) + * The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. */ - const char *v_mem_key; + /* const */ char *v_mem_cert; /** - * the certificate loaded into memory (not a filename) + * the private key in PEM format loaded into memory (not a filename) */ - const char *v_mem_cert; + const char *v_mem_key; /** * the option passphrase phrase to decrypt the private key, @@ -656,10 +658,12 @@ union MHD_DaemonOptionValue enum MHD_TlsBackend tls; /** - * Value for #MHD_D_O_TLS_KEY_CERT. - * the private key loaded into memory (not a filename) + * Value for #MHD_D_O_TLS_CERT_KEY. + * The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. */ - struct MHD_DaemonOptionValueTlsCert tls_key_cert; + struct MHD_DaemonOptionValueTlsCert tls_cert_key; /** * Value for #MHD_D_O_TLS_CLIENT_CA. @@ -1023,20 +1027,22 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used. /** * Provide TLS key and certificate data in-memory. * Works only if TLS mode is enabled. - * @param mem_key the private key loaded into memory (not a filename) - * @param mem_cert the certificate loaded into memory (not a filename) + * @param mem_cert The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. + * @param mem_key the private key in PEM format loaded into memory (not a filename) * @param mem_pass the option passphrase phrase to decrypt the private key, * could be NULL is private does not need a password * @return structure with the requested setting */ -# define MHD_D_OPTION_TLS_KEY_CERT(mem_key,mem_cert,mem_pass) \ +# define MHD_D_OPTION_TLS_CERT_KEY(mem_cert,mem_key,mem_pass) \ MHD_NOWARN_COMPOUND_LITERALS_ \ (const struct MHD_DaemonOptionAndValue) \ { \ - .opt = MHD_D_O_TLS_KEY_CERT, \ - .val.tls_key_cert.v_mem_key = (mem_key), \ - .val.tls_key_cert.v_mem_cert = (mem_cert), \ - .val.tls_key_cert.v_mem_pass = (mem_pass) \ + .opt = MHD_D_O_TLS_CERT_KEY, \ + .val.tls_cert_key.v_mem_cert = (mem_cert), \ + .val.tls_cert_key.v_mem_key = (mem_key), \ + .val.tls_cert_key.v_mem_pass = (mem_pass) \ } \ MHD_RESTORE_WARN_COMPOUND_LITERALS_ /** @@ -1740,25 +1746,27 @@ MHD_D_OPTION_TLS ( /** * Provide TLS key and certificate data in-memory. * Works only if TLS mode is enabled. - * @param mem_key the private key loaded into memory (not a filename) - * @param mem_cert the certificate loaded into memory (not a filename) + * @param mem_cert The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. + * @param mem_key the private key in PEM format loaded into memory (not a filename) * @param mem_pass the option passphrase phrase to decrypt the private key, * could be NULL is private does not need a password * @return structure with the requested setting */ static MHD_INLINE struct MHD_DaemonOptionAndValue -MHD_D_OPTION_TLS_KEY_CERT ( +MHD_D_OPTION_TLS_CERT_KEY ( + /* const */ char *mem_cert, const char *mem_key, - const char *mem_cert, const char *mem_pass ) { struct MHD_DaemonOptionAndValue opt_val; - opt_val.opt = MHD_D_O_TLS_KEY_CERT; - opt_val.val.tls_key_cert.v_mem_key = mem_key; - opt_val.val.tls_key_cert.v_mem_cert = mem_cert; - opt_val.val.tls_key_cert.v_mem_pass = mem_pass; + opt_val.opt = MHD_D_O_TLS_CERT_KEY; + opt_val.val.tls_cert_key.v_mem_cert = mem_cert; + opt_val.val.tls_cert_key.v_mem_key = mem_key; + opt_val.val.tls_cert_key.v_mem_pass = mem_pass; return opt_val; } diff --git a/src/include/microhttpd2_preamble.h.in b/src/include/microhttpd2_preamble.h.in index 51e71bc8..bf55ca48 100644 --- a/src/include/microhttpd2_preamble.h.in +++ b/src/include/microhttpd2_preamble.h.in @@ -459,6 +459,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode , /** * Failed to allocate memory for the daemon resources. + * TODO: combine similar error codes for daemon */ MHD_SC_DAEMON_MALLOC_FAILURE = 30081 , @@ -713,12 +714,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_TLS_DISABLED = 50000 , - /** - * The application attempted to setup TLS parameters before - * enabling TLS. - */ - MHD_SC_TLS_BACKEND_UNINITIALIZED = 50003 - , /** * The selected TLS backend does not yet support this operation. */ @@ -1286,6 +1281,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_LIB_INIT_GLOBAL_FAILED = 51000 , + /** + * Failed to initialise TLS context for the daemon + */ + MHD_SC_DAEMON_TLS_INIT_FAILED = 51200 + , /** * Something wrong in the internal MHD logic. * This error should be never returned if MHD works as expected. @@ -1309,17 +1309,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60001 , - /** - * The application requested an unsupported TLS backend to be used. - */ - MHD_SC_TLS_BACKEND_UNSUPPORTED = 60003 - , - /** - * The application requested a TLS cipher suite which is not - * supported by the selected backend. - */ - MHD_SC_TLS_CIPHERS_INVALID = 60004 - , /** * MHD is closing a connection because the application * logic to generate the response data failed. @@ -1390,6 +1379,34 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL = 60016 , + /** + * The application requested an unsupported TLS backend to be used. + */ + MHD_SC_TLS_BACKEND_UNSUPPORTED = 60020 + , + /** + * The application attempted to setup TLS parameters before + * enabling TLS. + */ + MHD_SC_TLS_BACKEND_UNINITIALIZED = 60021 + , + /** + * The application requested a TLS backend which cannot be used due + * to missing TLS dynamic library or backend initialisation problem. + */ + MHD_SC_TLS_BACKEND_UNAVAILABLE = 60022 + , + /** + * Provided TLS certificate and/or private key are incorrect + */ + MHD_SC_TLS_CONF_BAD_CERT = 60023 + , + /** + * The application requested a TLS cipher suite which is not + * supported by the selected backend. + */ + MHD_SC_TLS_CIPHERS_INVALID = 60024 + , /** * The response header name has forbidden characters or token */ diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am index 15afc210..1c1caab5 100644 --- a/src/mhd2/Makefile.am +++ b/src/mhd2/Makefile.am @@ -108,10 +108,11 @@ upgrade_files = \ tls_common_files = \ mhd_tls_choice.h \ - mhd_tls_funcs.h \ + mhd_tls_funcs.c mhd_tls_funcs.h \ tls_dh_params.h tls_gnu_files = \ + tls_gnu_tls_lib.h tls_gnu_daemon_data.h tls_gnu_conn_data.h \ tls_gnu_funcs.c tls_gnu_funcs.h if HAVE_POST_PARSER diff --git a/src/mhd2/daemon_create.c b/src/mhd2/daemon_create.c index ad12f57e..4f77345f 100644 --- a/src/mhd2/daemon_create.c +++ b/src/mhd2/daemon_create.c @@ -81,9 +81,9 @@ MHD_daemon_create (MHD_RequestCallback req_cb, /* Any floating point and pointer members must be initialised manually here */ #ifndef HAVE_NULL_PTR_ALL_ZEROS s->bind_sa.v_sa = NULL; - s->tls_key_cert.v_mem_key = NULL; - s->tls_key_cert.v_mem_cert = NULL; - s->tls_key_cert.v_mem_pass = NULL; + s->tls_cert_key.v_mem_key = NULL; + s->tls_cert_key.v_mem_cert = NULL; + s->tls_cert_key.v_mem_pass = NULL; s->tls_client_ca = NULL; s->tls_psk_callback.v_psk_cb = NULL; s->tls_psk_callback.v_psk_cb_cls = NULL; @@ -98,6 +98,9 @@ MHD_daemon_create (MHD_RequestCallback req_cb, s->notify_stream.v_nsc = NULL; s->notify_stream.v_cls = NULL; s->random_entropy.v_buf = NULL; + s->tls_cert_key.v_mem_cert = NULL; + s->tls_cert_key.v_mem_key = NULL; + s->tls_cert_key.v_mem_pass = NULL; d->log_params.v_log_cb = NULL; /* optional */ #endif /* !HAVE_NULL_PTR_ALL_ZEROS */ diff --git a/src/mhd2/daemon_options.h b/src/mhd2/daemon_options.h index aef89fa4..41ed1ffe 100644 --- a/src/mhd2/daemon_options.h +++ b/src/mhd2/daemon_options.h @@ -94,10 +94,12 @@ struct DaemonOptions /** - * Value for #MHD_D_O_TLS_KEY_CERT. - * the private key loaded into memory (not a filename) + * Value for #MHD_D_O_TLS_CERT_KEY. + * The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. */ - struct MHD_DaemonOptionValueTlsCert tls_key_cert; + struct MHD_DaemonOptionValueTlsCert tls_cert_key; /** diff --git a/src/mhd2/daemon_set_options.c b/src/mhd2/daemon_set_options.c index bfcc137a..d32da022 100644 --- a/src/mhd2/daemon_set_options.c +++ b/src/mhd2/daemon_set_options.c @@ -86,10 +86,53 @@ MHD_daemon_set_options ( case MHD_D_O_TLS: settings->tls = option->val.tls; continue; - case MHD_D_O_TLS_KEY_CERT: - settings->tls_key_cert.v_mem_key = option->val.tls_key_cert.v_mem_key; - settings->tls_key_cert.v_mem_cert = option->val.tls_key_cert.v_mem_cert; - settings->tls_key_cert.v_mem_pass = option->val.tls_key_cert.v_mem_pass; + case MHD_D_O_TLS_CERT_KEY: + /* custom setter */ + if ((NULL == option->val.tls_cert_key.v_mem_cert) + || (NULL == option->val.tls_cert_key.v_mem_key)) + return MHD_SC_TLS_CONF_BAD_CERT; + else + { + size_t cert_size; + size_t key_size; + size_t pass_size; + cert_size = strlen (option->val.tls_cert_key.v_mem_cert); + key_size = strlen (option->val.tls_cert_key.v_mem_key); + if ((0 == cert_size) + || (0 == key_size)) + return MHD_SC_TLS_CONF_BAD_CERT; + ++cert_size; /* Space for zero-termination */ + ++key_size; /* Space for zero-termination */ + if (NULL != option->val.tls_cert_key.v_mem_pass) + pass_size = strlen (option->val.tls_cert_key.v_mem_cert); + else + pass_size = 0; + if (0 != pass_size) + ++pass_size; /* Space for zero-termination */ + if (NULL != settings->tls_cert_key.v_mem_cert) + free (settings->tls_cert_key.v_mem_cert); // TODO: Support multiple certificates!! + settings->tls_cert_key.v_mem_cert = malloc (cert_size + key_size + pass_size); + if (NULL == settings->tls_cert_key.v_mem_cert) + return MHD_SC_DAEMON_MALLOC_FAILURE; + memcpy (settings->tls_cert_key.v_mem_cert, + option->val.tls_cert_key.v_mem_cert, + cert_size); + memcpy (settings->tls_cert_key.v_mem_cert + cert_size, + option->val.tls_cert_key.v_mem_key, + key_size); + settings->tls_cert_key.v_mem_key = + settings->tls_cert_key.v_mem_cert + cert_size; + if (0 != pass_size) + { + memcpy (settings->tls_cert_key.v_mem_cert + cert_size + key_size, + option->val.tls_cert_key.v_mem_pass, + pass_size); + settings->tls_cert_key.v_mem_pass = + settings->tls_cert_key.v_mem_cert + cert_size + key_size; + } + else + settings->tls_cert_key.v_mem_pass = NULL; + } continue; case MHD_D_O_TLS_CLIENT_CA: settings->tls_client_ca = option->val.tls_client_ca; diff --git a/src/mhd2/daemon_start.c b/src/mhd2/daemon_start.c index 7904ab57..bf4c1f5d 100644 --- a/src/mhd2/daemon_start.c +++ b/src/mhd2/daemon_start.c @@ -72,6 +72,10 @@ #include "mhd_lib_init.h" #include "daemon_logger.h" +#ifdef MHD_ENABLE_HTTPS +# include "mhd_tls_funcs.h" +#endif + #ifdef MHD_USE_THREADS # include "mhd_itc.h" # include "mhd_threads.h" @@ -101,6 +105,8 @@ dsettings_release (struct DaemonOptions *s) mhd_socket_close (s->listen_socket); if (NULL != s->bind_sa.v_sa) free (s->bind_sa.v_sa); + if (NULL != s->tls_cert_key.v_mem_cert) + free (s->tls_cert_key.v_mem_cert); free (s); } @@ -1437,6 +1443,88 @@ dauth_init (struct MHD_Daemon *restrict d, #endif + +/** + * Initialise daemon TLS data + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \ + MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_init_tls (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + mhd_StatusCodeInt ret; + + mhd_assert (! d->dbg.tls_inited); +#ifdef MHD_ENABLE_HTTPS + d->tls = NULL; +#endif + + if (MHD_TLS_BACKEND_NONE == s->tls) + { +#ifndef NDEBUG + d->dbg.tls_inited = true; +#endif + return MHD_SC_OK; + } +#ifndef MHD_ENABLE_HTTPS + mhd_LOG_MSG (d, \ + MHD_SC_TLS_DISABLED, \ + "HTTPS is not supported by this MHD build"); + return MHD_SC_TLS_DISABLED; +#else /* MHD_ENABLE_HTTPS */ + if (1) + { + enum mhd_TlsBackendAvailable tls_avail; + + tls_avail = mhd_tls_is_backend_available (s); + if (mhd_TLS_BACKEND_AVAIL_NOT_SUPPORTED == tls_avail) + { + mhd_LOG_MSG (d, \ + MHD_SC_TLS_BACKEND_UNSUPPORTED, \ + "The requested TLS backend is not supported " \ + "by this MHD build"); + return MHD_SC_TLS_BACKEND_UNSUPPORTED; + } + else if (mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE == tls_avail) + { + mhd_LOG_MSG (d, \ + MHD_SC_TLS_BACKEND_UNAVAILABLE, \ + "The requested TLS backend is not available"); + return MHD_SC_TLS_BACKEND_UNAVAILABLE; + } + } + ret = mhd_tls_daemon_init (d, + &(d->tls), + s); + mhd_assert ((MHD_SC_OK == ret) || (NULL == d->tls)); + mhd_assert ((MHD_SC_OK != ret) || (NULL != d->tls)); +#ifndef NDEBUG + d->dbg.tls_inited = (MHD_SC_OK == ret); +#endif + return (enum MHD_StatusCode) ret; +#endif /* MHD_ENABLE_HTTPS */ +} + + +/** + * Deinitialise daemon TLS data + * @param d the daemon object + */ +MHD_FN_PAR_NONNULL_ (1) static void +daemon_deinit_tls (struct MHD_Daemon *restrict d) +{ + mhd_assert (d->dbg.tls_inited); +#ifdef MHD_ENABLE_HTTPS + if (NULL != d->tls) + mhd_tls_daemon_deinit (d->tls); +#endif +} + + /** * Initialise large buffer tracking. * @param d the daemon object @@ -2828,32 +2916,35 @@ daemon_start_internal (struct MHD_Daemon *restrict d, if (MHD_SC_OK != res) return res; + mhd_assert (d->dbg.net_inited); + res = daemon_init_tls (d, s); - // TODO: Other init - - res = daemon_init_threading_and_conn (d, s); if (MHD_SC_OK == res) { - mhd_assert (d->dbg.net_inited); - mhd_assert (d->dbg.threading_inited); - mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type)); - - res = daemon_init_large_buf (d, s); + mhd_assert (d->dbg.tls_inited); + res = daemon_init_threading_and_conn (d, s); if (MHD_SC_OK == res) { - res = daemon_start_threads (d); + mhd_assert (d->dbg.threading_inited); + mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type)); + + res = daemon_init_large_buf (d, s); if (MHD_SC_OK == res) { - return MHD_SC_OK; + res = daemon_start_threads (d); + if (MHD_SC_OK == res) + { + return MHD_SC_OK; + } + + /* Below is a clean-up path */ + daemon_deinit_large_buf (d); } - - /* Below is a clean-up path */ - daemon_deinit_large_buf (d); + daemon_deinit_threading_and_conn (d); } - daemon_deinit_threading_and_conn (d); + daemon_deinit_tls (d); } - daemon_deinit_net (d); mhd_assert (MHD_SC_OK != res); return res; @@ -2911,6 +3002,8 @@ MHD_daemon_destroy (struct MHD_Daemon *daemon) daemon_deinit_large_buf (daemon); + daemon_deinit_tls (daemon); + daemon_deinit_net (daemon); } daemon->state = mhd_DAEMON_STATE_STOPPED; /* Useful only for debugging */ diff --git a/src/mhd2/mhd_daemon.h b/src/mhd2/mhd_daemon.h index 26e72d09..028f100d 100644 --- a/src/mhd2/mhd_daemon.h +++ b/src/mhd2/mhd_daemon.h @@ -35,6 +35,10 @@ #include "mhd_public_api.h" +#ifdef MHD_ENABLE_HTTPS +# include "mhd_tls_choice.h" +#endif + #ifdef MHD_USE_THREADS # include "mhd_threads.h" # include "mhd_itc_types.h" @@ -873,6 +877,7 @@ struct mhd_daemon_debug { bool net_inited; bool net_deinited; + bool tls_inited; bool events_allocated; unsigned int num_events_elements; bool events_fully_inited; @@ -913,6 +918,15 @@ struct MHD_Daemon */ struct mhd_DaemonNetwork net; +#ifdef MHD_ENABLE_HTTPS + /** + * The pointer to the daemon TLS data. + * If set to non-NULL then HTTPS protocol is used, if set to NULL then + * plain HTTP protocol used. + */ + struct mhd_DaemonTlsData *tls; +#endif + #ifdef MHD_USE_THREADS /* Threading data */ @@ -1002,4 +1016,11 @@ struct MHD_Daemon (mhd_D_IS_USING_EPOLL (d) || \ (mhd_WM_INT_EXTERNAL_EVENTS_EDGE ==((d)->wmode_int))) +#ifdef MHD_ENABLE_HTTPS +# define mhd_D_HAS_TLS(d) ((d->tls) ? (! 0) : (0)) +#else +# define mhd_D_HAS_TLS(d) (0) +#endif + + #endif /* ! MHD_DAEMON_H */ diff --git a/src/mhd2/mhd_tls_funcs.c b/src/mhd2/mhd_tls_funcs.c new file mode 100644 index 00000000..b9221250 --- /dev/null +++ b/src/mhd2/mhd_tls_funcs.c @@ -0,0 +1,49 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/mhd_tls_funcs.c + * @brief The TLS backend generic functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "mhd_tls_funcs.h" + +#include "mhd_assert.h" + +#include "mhd_public_api.h" +#include "daemon_options.h" + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ +enum mhd_TlsBackendAvailable +mhd_tls_is_backend_available (struct DaemonOptions *s) +{ + mhd_assert (MHD_TLS_BACKEND_NONE != s->tls); + if (MHD_TLS_BACKEND_ANY == s->tls) + return mhd_tls_gnu_is_inited_fine () + /* || mhd_tls_other_is_inited_fine() */; +#ifdef MHD_USE_GNUTLS + if (MHD_TLS_BACKEND_GNUTLS == s->tls) + return mhd_tls_gnu_is_inited_fine (); +#endif + return false; +} diff --git a/src/mhd2/mhd_tls_funcs.h b/src/mhd2/mhd_tls_funcs.h index a3ef55ed..2bf30733 100644 --- a/src/mhd2/mhd_tls_funcs.h +++ b/src/mhd2/mhd_tls_funcs.h @@ -30,6 +30,8 @@ #include "mhd_sys_options.h" +#include "sys_bool_type.h" + #include "mhd_tls_choice.h" #ifndef MHD_ENABLE_HTTPS #error This header should be used only if HTTPS is enabled @@ -39,6 +41,12 @@ # include "tls_gnu_funcs.h" #endif +#ifndef MHD_USE_GNUTLS +# define mhd_tls_gnu_is_inited_fine() (0) +#endif + +/* ** Global initialisation ** */ + /** * Perform one-time global initialisation of TLS backend */ @@ -57,5 +65,60 @@ #define mhd_tls_global_re_init() \ mhd_MACRO_CONCAT3 (mhd_tls_,mhd_TLS_FUNC_NAME_ID,_global_re_init)() +/* ** Daemon initialisation ** */ + +/** + * Set daemon TLS parameters + * @param d the daemon handle + * @param p_d_tls the pointer to variable to set the pointer to + * the daemon's TLS settings (allocated by this function) + * @param s the daemon settings + * @return #MHD_SC_OK on success (p_d_tls set to the allocated settings), + * error code otherwise + */ +#define mhd_tls_daemon_init(d,p_d_tls,s) \ + mhd_MACRO_CONCAT3 (mhd_tls_,mhd_TLS_FUNC_NAME_ID,_daemon_init)( \ + (d),(p_d_tls),(s)) + +/** + * De-initialise daemon TLS parameters (and free memory allocated for TLS + * settings) + * @param d_tls the pointer to the daemon's TLS settings + */ +#define mhd_tls_daemon_deinit(d_tls) \ + mhd_MACRO_CONCAT3 (mhd_tls_,mhd_TLS_FUNC_NAME_ID,_daemon_deinit)( \ + (d_tls)) + + +/** + * Result of TLS backend availablility check + */ +enum mhd_TlsBackendAvailable +{ + /** + * The TLS backend is available and can be used + */ + mhd_TLS_BACKEND_AVAIL_OK = 0 + , + /** + * The TLS backend support is not enabled in this MHD build + */ + mhd_TLS_BACKEND_AVAIL_NOT_SUPPORTED + , + /** + * The TLS backend supported, but not available + */ + mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE +}; + +/** + * Check whether the requested TLS backend is available + * @param s the daemon settings + * @return 'mhd_TLS_BACKEND_AVAIL_OK' if requested backend is available, + * error code otherwise + */ +MHD_INTERNAL enum mhd_TlsBackendAvailable +mhd_tls_is_backend_available (struct DaemonOptions *s) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_; #endif /* ! MHD_TLS_FUNCS_H */ diff --git a/src/mhd2/tls_dh_params.h b/src/mhd2/tls_dh_params.h new file mode 100644 index 00000000..4ab779f5 --- /dev/null +++ b/src/mhd2/tls_dh_params.h @@ -0,0 +1,53 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/tls_dh_params.h + * @brief The Diffie-Hellman parameters for TLS backends without RFC7919 + * support + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_DH_PARAMS_H +#define MHD_TLS_DH_PARAMS_H 1 + +#include "mhd_sys_options.h" + +/** + * The PEM encoded PKCS#3 Diffie-Hellman parameters. + * + * To be used only if TLS backend does not support RFC7919. + * Generated by GnuTLS "certtool --get-dh-params" command. + */ +static const char mhd_tls_dh_params_pkcs3[] = + "-----BEGIN DH PARAMETERS-----\n" + "MIIBjAKCAYEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n" + "+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n" + "87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\n" + "YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n" + "7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD\n" + "ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3\n" + "7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32\n" + "nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZsYu\n" + "N///////////AgECAgIBFA==\n" + "-----END DH PARAMETERS-----\n"; + + +#endif /* ! MHD_TLS_DH_PARAMS_H */ diff --git a/src/mhd2/tls_gnu_conn_data.h b/src/mhd2/tls_gnu_conn_data.h new file mode 100644 index 00000000..a2db40c7 --- /dev/null +++ b/src/mhd2/tls_gnu_conn_data.h @@ -0,0 +1,49 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/tls_gnu_conn_data.h + * @brief The definition of GnuTLS daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_GNU_CONN_DATA_H +#define MHD_TLS_GNU_CONN_DATA_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_GNUTLS +#error This header can be used only if GnuTLS is enabled +#endif + +#include "tls_gnu_tls_lib.h" + +/** + * The structure with connection-specific GnuTLS data + */ +struct mhd_ConnTlsGnuData +{ + /** + * GnuTLS session data + */ + gnutls_session_t sess; +}; + +#endif /* ! MHD_TLS_GNU_CONN_DATA_H */ diff --git a/src/mhd2/tls_gnu_daemon_data.h b/src/mhd2/tls_gnu_daemon_data.h new file mode 100644 index 00000000..2db67d55 --- /dev/null +++ b/src/mhd2/tls_gnu_daemon_data.h @@ -0,0 +1,61 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/tls_gnu_daemon_data.h + * @brief The definition of GnuTLS daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_GNU_DAEMON_DATA_H +#define MHD_TLS_GNU_DAEMON_DATA_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_GNUTLS +#error This header can be used only if GnuTLS is enabled +#endif + +#include "tls_gnu_tls_lib.h" + +/** + * The structure with daemon-specific GnuTLS data + */ +struct mhd_DaemonTlsGnuData +{ + /** + * The credentials + */ + gnutls_certificate_credentials_t cred; + +#ifdef mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3 + /** + * Diffie-Hellman parameters + */ + gnutls_dh_params_t dh_params; +#endif + + /** + * TLS priorities cache + */ + gnutls_priority_t pri_cache; +}; + +#endif /* ! MHD_TLS_GNU_DAEMON_DATA_H */ diff --git a/src/mhd2/tls_gnu_funcs.c b/src/mhd2/tls_gnu_funcs.c index f9cf3773..d641342a 100644 --- a/src/mhd2/tls_gnu_funcs.c +++ b/src/mhd2/tls_gnu_funcs.c @@ -29,10 +29,30 @@ #include "sys_bool_type.h" #include "sys_base_types.h" -#include +#include +#include "mhd_str_types.h" +#include "mhd_str_macros.h" +#include "mhd_arr_num_elems.h" + +#include "compat_calloc.h" +#include "sys_malloc.h" +#include "mhd_assert.h" + +#include "tls_gnu_tls_lib.h" + +#include "tls_gnu_daemon_data.h" #include "tls_gnu_funcs.h" +#include "daemon_options.h" + +#include "mhd_public_api.h" +#include "daemon_logger.h" + +#ifdef mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3 +# include "tls_dh_params.h" +#endif + struct mhd_DaemonTlsGnuData; /* Forward declaration */ struct mhd_ConnTlsGnuData; /* Forward declaration */ @@ -62,3 +82,327 @@ mhd_tls_gnu_global_deinit (void) gnutls_global_deinit (); gnutls_lib_inited = false; } + + +MHD_INTERNAL bool +mhd_tls_gnu_is_inited_fine (void) +{ + return gnutls_lib_inited; +} + + +/* ** Daemon initialisation ** */ + +/** + * Check application-provided daemon TLS settings + * @param d the daemon handle + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +check_app_tls_sessings (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + mhd_assert (MHD_TLS_BACKEND_NONE == s->tls); + mhd_assert ((MHD_TLS_BACKEND_GNUTLS == s->tls) || \ + (MHD_TLS_BACKEND_ANY == s->tls)); + if (NULL == s->tls_cert_key.v_mem_cert) + { + mhd_LOG_MSG (d, MHD_SC_TLS_CONF_BAD_CERT, \ + "No valid TLS certificate is provided"); + return MHD_SC_TLS_CONF_BAD_CERT; + } + mhd_assert (NULL != s->tls_cert_key.v_mem_key); + + return MHD_SC_OK; +} + + +/** + * Initialise daemon TLS Diffie-Hellman parameters. + * + * This function initialise Diffie-Hellman parameters for the daemon based + * on GnuTLS recommended defaults. + * With modern GnuTLS versions this function is no-op and always succeed. + * + * This function does not put any messages to the log. + * @param d_tls the daemon TLS data + * @return 'true' if succeed, + * 'false' if failed + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ bool +daemon_init_dh_data (struct mhd_DaemonTlsGnuData *restrict d_tls) +{ +#if defined(mhd_TLS_GNU_DH_PARAMS_USE_KNOWN) + /* Rely on reasonable TLS defaults set in the TLS library. + Modern GnuTLS versions relies completely on RFC 7919 and do not need + this function therefore do not bother implementing special + application-defined settings just for limited number of GnuTLS + versions (>= 3.5.6 && < 3.6.0). */ + return (GNUTLS_E_SUCCESS == + gnutls_certificate_set_known_dh_params (d_tls->cred, + GNUTLS_SEC_PARAM_MEDIUM)); +#elif defined(mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3) + gnutls_datum_t dh_data; + if (GNUTLS_E_SUCCESS != + gnutls_dh_params_init (&(d_tls->dh_params))) + return false; + + dh_data.data = mhd_DROP_CONST (mhd_tls_dh_params_pkcs3); + dh_data.size = sizeof (mhd_tls_dh_params_pkcs3); + if (GNUTLS_E_SUCCESS == + gnutls_dh_params_import_pkcs3 (d_tls->dh_params, + &dh_data, + GNUTLS_X509_FMT_PEM)) + { + gnutls_certificate_set_dh_params (d_tls->cred, + d_tls->dh_params); + return true; /* success exit point */ + } + /* Below is a clean-up code path */ + gnutls_dh_params_deinit (d_tls->dh_params); + return false; +#else + (void) d_tls; /* Mute compiler warning */ + return true; +#endif +} + + +/** + * De-initialise daemon TLS Diffie-Hellman parameters. + * @param d_tls the daemon TLS data + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_dh_data (struct mhd_DaemonTlsGnuData *restrict d_tls) +{ +#if defined(mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3) + mhd_assert (NULL != d_tls->dh_params); + gnutls_dh_params_deinit (d_tls->dh_params); +#else + (void) d_tls; /* Mute compiler warning */ +#endif +} + + +/** + * Set daemon TLS credentials (and Diffie-Hellman parameters). + * This function puts error messages to the log if needed. + * @param d the daemon handle + * @param d_tls the daemon TLS settings + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_init_credentials (struct MHD_Daemon *restrict d, + struct mhd_DaemonTlsGnuData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode res; + size_t cert_len; + size_t key_len; + + if (GNUTLS_E_SUCCESS != + gnutls_certificate_allocate_credentials (&(d_tls->cred))) + { + mhd_LOG_MSG (d, MHD_SC_DAEMON_TLS_INIT_FAILED, \ + "Failed to initialise TLS credentials for the daemon"); + return MHD_SC_DAEMON_TLS_INIT_FAILED; + } + + // TODO: Support multiple certificates + cert_len = strlen (s->tls_cert_key.v_mem_cert) + 1; // TODO: Reuse calculated length + key_len = strlen (s->tls_cert_key.v_mem_key) + 1; // TODO: Reuse calculated length + + if (((unsigned int) cert_len != cert_len) + || ((unsigned int) key_len != key_len)) + res = MHD_SC_TLS_CONF_BAD_CERT; /* Very unlikely, do not waste space on special message */ + else + { + gnutls_datum_t cert_data; + gnutls_datum_t key_data; + + cert_data.data = mhd_DROP_CONST (s->tls_cert_key.v_mem_cert); + cert_data.size = (unsigned int) cert_len; + key_data.data = mhd_DROP_CONST (s->tls_cert_key.v_mem_key); + key_data.size = (unsigned int) key_len; + if (0 > + gnutls_certificate_set_x509_key_mem2 (d_tls->cred, + &cert_data, + &key_data, + GNUTLS_X509_FMT_PEM, + s->tls_cert_key.v_mem_pass, + 0)) + { + mhd_LOG_MSG (d, MHD_SC_TLS_CONF_BAD_CERT, \ + "Failed to set the provided TLS certificate"); + res = MHD_SC_TLS_CONF_BAD_CERT; + } + else + { + if (! daemon_init_dh_data (d_tls)) + { + mhd_LOG_MSG (d, MHD_SC_DAEMON_TLS_INIT_FAILED, \ + "Failed to initialise Diffie-Hellman parameters " \ + "for the daemon"); + res = MHD_SC_DAEMON_TLS_INIT_FAILED; + } + else + return MHD_SC_OK; + } + } + + gnutls_certificate_free_credentials (d_tls->cred); + mhd_assert (MHD_SC_OK != res); + return res; /* Failure exit point */ +} + + +/** + * Free daemon fully allocated credentials (and Diffie-Hellman parameters). + * @param d_tls the daemon TLS settings + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_credentials (struct mhd_DaemonTlsGnuData *restrict d_tls) +{ + mhd_assert (NULL != d_tls->cred); + /* To avoid dangling pointer to DH data in the credentials, + free credentials first and then free DH data. */ + gnutls_certificate_free_credentials (d_tls->cred); + daemon_deinit_dh_data (d_tls); +} + + +static const struct MHD_StringNullable tlsgnulib_base_priorities[] = { + {0, NULL} /* Replaced with app-defined name */ + , +#ifdef mhd_TLS_GNU_SUPPORTS_MULTI_KEYWORDS_PRIORITY + mhd_MSTR_INIT ("@LIBMICROHTTPD,@SYSTEM") +#else + mhd_MSTR_INIT ("@LIBMICROHTTPD") + , + mhd_MSTR_INIT ("@SYSTEM") +#endif + , + {0, NULL} + , + mhd_MSTR_INIT ("NORMAL") +}; + +/** + * Initialise GnuTLS priorities cache + * @param d the daemon handle + * @param d_tls the daemon TLS settings + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_init_priorities_cache (struct MHD_Daemon *restrict d, + struct mhd_DaemonTlsGnuData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + size_t i; + + (void) s; // TODO: support app-defined name for TLS backend profile + + for (i = 0; i < mhd_ARR_NUM_ELEMS (tlsgnulib_base_priorities); ++i) + { + if (0 == i) + continue; // TODO: support app-defined name for TLS backend profile +#if ! defined(mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY) + if (NULL == tlsgnulib_base_priorities[i].cstr) + { + /* GnuTLS default priorities */ +# if defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY) + d_tls->pri_cache = NULL; + break; +# else + continue; /* "default" priorities cannot be used */ +# endif + } +#endif /* ! mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY */ + if (GNUTLS_E_SUCCESS == + gnutls_priority_init (&(d_tls->pri_cache), + tlsgnulib_base_priorities[i].cstr, + NULL)) + break; + } + + if (i < mhd_ARR_NUM_ELEMS (tlsgnulib_base_priorities)) + return MHD_SC_OK; /* Success exit point */ + + mhd_LOG_MSG (d, MHD_SC_DAEMON_TLS_INIT_FAILED, \ + "Failed to initialise TLS priorities cache"); + return MHD_SC_DAEMON_TLS_INIT_FAILED; +} + + +/** + * De-initialise priorities cache + * @param d_tls the daemon TLS settings + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_priorities_cache (struct mhd_DaemonTlsGnuData *restrict d_tls) +{ +#if ! defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY) + mhd_assert (NULL != d_tls->pri_cache); +#else + if (NULL != d_tls->pri_cache) +#endif + gnutls_priority_deinit (d_tls->pri_cache); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (2) mhd_StatusCodeInt +mhd_tls_gnu_daemon_init (struct MHD_Daemon *restrict d, + struct mhd_DaemonTlsGnuData **restrict p_d_tls, + struct DaemonOptions *restrict s) +{ + mhd_StatusCodeInt res; + struct mhd_DaemonTlsGnuData *restrict d_tls; + + res = check_app_tls_sessings (d, s); + if (MHD_SC_OK != res) + return res; + + d_tls = (struct mhd_DaemonTlsGnuData *) + mhd_calloc (1, sizeof (struct mhd_DaemonTlsGnuData)); + *p_d_tls = d_tls; + if (NULL == d_tls) + return MHD_SC_DAEMON_MALLOC_FAILURE; + + res = daemon_init_credentials (d, + d_tls, + s); + if (MHD_SC_OK == res) + { + res = daemon_init_priorities_cache (d, + d_tls, + s); + if (MHD_SC_OK == res) + return MHD_SC_OK; /* Success exit point */ + + /* Below is a clean-up code path */ + daemon_deinit_credentials (d_tls); + } + + free (d_tls); + *p_d_tls = NULL; + mhd_assert (MHD_SC_OK != res); + return res; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_INOUT_ (1) void +mhd_tls_gnu_daemon_deinit (struct mhd_DaemonTlsGnuData *restrict d_tls) +{ + mhd_assert (NULL != d_tls); + daemon_deinit_priorities_cache (d_tls); + daemon_deinit_credentials (d_tls); + free (d_tls); +} diff --git a/src/mhd2/tls_gnu_funcs.h b/src/mhd2/tls_gnu_funcs.h index 59a84649..49c5b00e 100644 --- a/src/mhd2/tls_gnu_funcs.h +++ b/src/mhd2/tls_gnu_funcs.h @@ -33,6 +33,8 @@ #error This header can be used only if GnuTLS is enabled #endif +#include "mhd_status_code_int.h" + /** * The structure with daemon-specific GnuTLS data */ @@ -64,4 +66,40 @@ mhd_tls_gnu_global_init (void); MHD_INTERNAL void mhd_tls_gnu_global_deinit (void); +/** + * Check whether GnuTLS backend was successfully initialised globally + */ +MHD_INTERNAL bool +mhd_tls_gnu_is_inited_fine (void); + + +/* ** Daemon initialisation ** */ + +struct MHD_Daemon; /* Forward declaration */ +struct DaemonOptions; /* Forward declaration */ + +/** + * Set daemon TLS parameters + * @param d the daemon handle + * @param p_d_tls the pointer to variable to set the pointer to + * the daemon's TLS settings (allocated by this function) + * @param s the daemon settings + * @return #MHD_SC_OK on success (p_d_tls set to the allocated settings), + * error code otherwise + */ +MHD_INTERNAL mhd_StatusCodeInt +mhd_tls_gnu_daemon_init (struct MHD_Daemon *restrict d, + struct mhd_DaemonTlsGnuData **restrict p_d_tls, + struct DaemonOptions *restrict s) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2); + +/** + * De-initialise daemon TLS parameters (and free memory allocated for TLS + * settings) + * @param d_tls the pointer to the daemon's TLS settings + */ +MHD_INTERNAL void +mhd_tls_gnu_daemon_deinit (struct mhd_DaemonTlsGnuData *restrict d_tls) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); + #endif /* ! MHD_TLS_GNU_FUNCS_H */ diff --git a/src/mhd2/tls_gnu_tls_lib.h b/src/mhd2/tls_gnu_tls_lib.h new file mode 100644 index 00000000..b10148db --- /dev/null +++ b/src/mhd2/tls_gnu_tls_lib.h @@ -0,0 +1,103 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/tls_gnu_tls_lib.h + * @brief The wrapper for GnuTLS header + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_GNU_TLS_LIB_H +#define MHD_TLS_GNU_TLS_LIB_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_GNUTLS +#error This header can be used only if GnuTLS is enabled +#endif + +#include + +#ifndef GNUTLS_VERSION_NUMBER +#error GNUTLS_VERSION_NUMBER is not defined +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030600 +/** + * Indicate that RC7919 defaults are used + */ +# define mhd_TLS_GNU_HAS_RFC7919_DEFS 1 +#endif + +#ifndef mhd_TLS_GNU_HAS_RFC7919_DEFS +# if GNUTLS_VERSION_NUMBER >= 0x030506 +/** + * Use gnutls_certificate_set_known_dh_params() function to set DH parameters + */ +# define mhd_TLS_GNU_DH_PARAMS_USE_KNOWN 1 +# else +/** + * TLS backend needs encoded Diffie-Hellman parameters + */ +# define mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3 1 +# endif +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x020104 +/** + * Defined if gnutls_set_default_priority() function is available + */ +# define mhd_TLS_GNU_HAS_SET_DEF_PRIORITY 1 +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030300 +/** + * Defined if NULL is treated as "default priorities" when used as "priorities" + * argument for gnutls_priority_init() and gnutls_priority_init2() + */ +# define mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY 1 +#endif + +#if ! defined(mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY) \ + && defined(mhd_TLS_GNU_HAS_SET_DEF_PRIORITY) +/** + * Defined if NULL in priorities cache is treated as indication that default + * priorities must used for connection / session + */ +# define mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY 1 +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030603 +/** + * Defined if gnutls_priority_init2() function and the flag + * GNUTLS_PRIORITY_INIT_DEF_APPEND are available + */ +# define mhd_TLS_GNU_HAS_PRIORITY_INIT2 1 +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030501 +/** + * Defined if gnutls_priority_init{,2}() functions support "@KEYWORD1,@KEYWORD2" + * to as fallback values. + */ +# define mhd_TLS_GNU_SUPPORTS_MULTI_KEYWORDS_PRIORITY 1 +#endif + +#endif /* ! MHD_TLS_GNU_TLS_LIB_H */