libmicrohttpd2

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

commit ed7ff26f38bf305e450f0b1dc7e9f72dc9bf174c
parent 2dbd077a69236f77bf4cc0067dea469cefbd5797
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date:   Thu, 11 Dec 2025 17:09:17 +0100

Added MbedTLS backend

Diffstat:
Msrc/include/microhttpd2.h | 19+++++++++++++++++++
Msrc/include/microhttpd2_main.h.in | 14++++++++++++++
Msrc/include/microhttpd2_preamble.h.in | 5+++++
Msrc/mhd2/Makefile.am | 8++++++++
Msrc/mhd2/daemon_get_info.c | 7+++++++
Msrc/mhd2/lib_get_info.c | 8++++++++
Msrc/mhd2/mhd_tls_choice.h | 41++++++++++++++++++++++++++++++++++++++++-
Msrc/mhd2/mhd_tls_common.c | 12+++++++++++-
Msrc/mhd2/mhd_tls_funcs.h | 2++
Asrc/mhd2/tls_mbed_conn_data.h | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_mbed_daemon_data.h | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_mbed_funcs.c | 1541+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_mbed_funcs.h | 318+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_mbed_tls_lib.h | 197+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/tls_multi_conn_data.h | 9+++++++++
Msrc/mhd2/tls_multi_daemon_data.h | 9+++++++++
Msrc/mhd2/tls_multi_funcs.c | 130++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/mhd2/tls_multi_funcs.h | 2+-
Msrc/mhd2/tls_multi_tls_lib.h | 7+++++++
19 files changed, 2567 insertions(+), 4 deletions(-)

diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h @@ -3651,6 +3651,11 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_TlsBackend * Use OpenSSL as TLS backend. */ MHD_TLS_BACKEND_OPENSSL = 3 + , + /** + * Use MbedTLS as TLS backend. + */ + MHD_TLS_BACKEND_MBEDTLS = 4 }; /** @@ -9058,6 +9063,10 @@ struct MHD_LibInfoTLSType * The OpenSSL backend is supported/available/enabled. */ enum MHD_Bool backend_openssl; + /** + * The MbedTLS backend is supported/available/enabled. + */ + enum MHD_Bool backend_mbedtls; }; /** @@ -10102,6 +10111,16 @@ union MHD_ConnInfoDynamicTlsSess #else void /* SSL */ *v_openssl_session; #endif + + /* Include <mbedtls/ssl.h> before this header to get a better type safety */ + /** + * MbedTLS session handle, of type "mbedtls_ssl_context*". + */ +#if defined(MBEDTLS_SSL_H) + mbedtls_ssl_context *v_mbedtls_session; +#else + void /* mbedtls_ssl_context */ *v_mbedtls_session; +#endif }; /** diff --git a/src/include/microhttpd2_main.h.in b/src/include/microhttpd2_main.h.in @@ -4241,6 +4241,10 @@ struct MHD_LibInfoTLSType * The OpenSSL backend is supported/available/enabled. */ enum MHD_Bool backend_openssl; + /** + * The MbedTLS backend is supported/available/enabled. + */ + enum MHD_Bool backend_mbedtls; }; /** @@ -5285,6 +5289,16 @@ union MHD_ConnInfoDynamicTlsSess #else void /* SSL */ *v_openssl_session; #endif + + /* Include <mbedtls/ssl.h> before this header to get a better type safety */ + /** + * MbedTLS session handle, of type "mbedtls_ssl_context*". + */ +#if defined(MBEDTLS_SSL_H) + mbedtls_ssl_context *v_mbedtls_session; +#else + void /* mbedtls_ssl_context */ *v_mbedtls_session; +#endif }; /** diff --git a/src/include/microhttpd2_preamble.h.in b/src/include/microhttpd2_preamble.h.in @@ -3651,6 +3651,11 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_TlsBackend * Use OpenSSL as TLS backend. */ MHD_TLS_BACKEND_OPENSSL = 3 + , + /** + * Use MbedTLS as TLS backend. + */ + MHD_TLS_BACKEND_MBEDTLS = 4 }; /** diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am @@ -268,6 +268,10 @@ tls_open_OPTSOURCES = \ tls_open_tls_lib.h tls_open_daemon_data.h tls_open_conn_data.h \ tls_open_funcs.c tls_open_funcs.h +tls_mbed_OPTSOURCES = \ + tls_mbed_tls_lib.h tls_mbed_daemon_data.h tls_mbed_conn_data.h \ + tls_mbed_funcs.c tls_mbed_funcs.h + if MHD_SUPPORT_HTTP2 libmicrohttpd2_la_SOURCES += $(httptwo_OPTSOURCES) endif @@ -310,6 +314,10 @@ endif if MHD_SUPPORT_OPENSSL libmicrohttpd2_la_SOURCES += $(tls_open_OPTSOURCES) endif + +if MHD_SUPPORT_MBEDTLS + libmicrohttpd2_la_SOURCES += $(tls_mbed_OPTSOURCES) +endif endif libmicrohttpd2_la_CPPFLAGS = \ diff --git a/src/mhd2/daemon_get_info.c b/src/mhd2/daemon_get_info.c @@ -170,6 +170,11 @@ MHD_daemon_get_info_fixed_sz ( output_buf->v_tls_backend = MHD_TLS_BACKEND_OPENSSL; break; # endif +# ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + output_buf->v_tls_backend = MHD_TLS_BACKEND_MBEDTLS; + break; +# endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -179,6 +184,8 @@ MHD_daemon_get_info_fixed_sz ( output_buf->v_tls_backend = MHD_TLS_BACKEND_GNUTLS; #elif defined(MHD_SUPPORT_OPENSSL) output_buf->v_tls_backend = MHD_TLS_BACKEND_OPENSSL; +#elif defined(MHD_SUPPORT_MBEDTLS) + output_buf->v_tls_backend = MHD_TLS_BACKEND_MBEDTLS; #else #error No TLS backends enabled, while TLS support is enabled #endif diff --git a/src/mhd2/lib_get_info.c b/src/mhd2/lib_get_info.c @@ -76,6 +76,9 @@ # if defined(MHD_SUPPORT_OPENSSL) # include "tls_open_funcs.h" # endif +# if defined(MHD_SUPPORT_MBEDTLS) +# include "tls_mbed_funcs.h" +# endif #endif #ifdef MHD_SUPPORT_MD5 @@ -528,15 +531,18 @@ MHD_lib_get_info_dynamic_sz ( output_buf->v_tls_backends.tls_supported = MHD_NO; output_buf->v_tls_backends.backend_gnutls = MHD_NO; output_buf->v_tls_backends.backend_openssl = MHD_NO; + output_buf->v_tls_backends.backend_mbedtls = MHD_NO; #else bool gnutls_avail; bool openssl_avail; + bool mdedtls_avail; if (! mhd_lib_init_global_if_needed ()) return MHD_SC_INFO_GET_TYPE_UNOBTAINABLE; gnutls_avail = mhd_tls_gnu_is_inited_fine (); openssl_avail = mhd_tls_open_is_inited_fine (); + mdedtls_avail = mhd_tls_mbed_is_inited_fine (); output_buf->v_tls_backends.tls_supported = (gnutls_avail || openssl_avail) ? MHD_YES : MHD_NO; @@ -544,6 +550,8 @@ MHD_lib_get_info_dynamic_sz ( gnutls_avail ? MHD_YES : MHD_NO; output_buf->v_tls_backends.backend_openssl = openssl_avail ? MHD_YES : MHD_NO; + output_buf->v_tls_backends.backend_mbedtls = + mdedtls_avail ? MHD_YES : MHD_NO; mhd_lib_deinit_global_if_needed (); #endif diff --git a/src/mhd2/mhd_tls_choice.h b/src/mhd2/mhd_tls_choice.h @@ -103,11 +103,30 @@ */ #define mhd_TLS_OPEN_IS_SUPPORTED() (! ! mhd_TLS_OPEN_ENABLED) +/* * MbedTLS * */ + +#ifdef MHD_SUPPORT_MBEDTLS +/** + * Defined to one if MbedTLS is enabled at build time or to zero if not enabled + */ +# define mhd_TLS_MBED_ENABLED (1) +#else +/** + * Defined to one if MbedTLS is enabled at build time or to zero if not enabled + */ +# define mhd_TLS_MBED_ENABLED (0) +#endif + +/** + * Return non-zero if OpenSSL is supported + */ +#define mhd_TLS_MBED_IS_SUPPORTED() (! ! mhd_TLS_MBED_ENABLED) + /** * Defined to the number of enabled TLS backends */ #define mhd_TLS_NUM_BACKENDS \ - (mhd_TLS_GNU_ENABLED + mhd_TLS_OPEN_ENABLED) + (mhd_TLS_GNU_ENABLED + mhd_TLS_OPEN_ENABLED + mhd_TLS_MBED_ENABLED) #if mhd_TLS_NUM_BACKENDS == 0 #error At least one TLS backend must be enabled if this header is included @@ -186,6 +205,19 @@ * The TLS back-end identifier for macro names */ # define mhd_TLS_MACRO_NAME_ID OPEN +#elif defined(MHD_SUPPORT_MBEDTLS) +/** + * The TLS back-end identifier for function names + */ +# define mhd_TLS_FUNC_NAME_ID mbed +/** + * The TLS back-end identifier for data names + */ +# define mhd_TLS_DATA_NAME_ID Mbed +/** + * The TLS back-end identifier for macro names + */ +# define mhd_TLS_MACRO_NAME_ID MBED #endif @@ -205,6 +237,13 @@ # define mhd_tls_open_is_inited_fine() (! ! 0) #endif +#ifndef MHD_SUPPORT_MBEDTLS +/** + * Check whether MbedTLS backend was successfully initialised globally + */ +# define mhd_tls_mbed_is_inited_fine() (! ! 0) +#endif + /* ** Functions names and structures names macros ** */ diff --git a/src/mhd2/mhd_tls_common.c b/src/mhd2/mhd_tls_common.c @@ -55,6 +55,9 @@ #if defined(MHD_SUPPORT_OPENSSL) # include "tls_open_funcs.h" #endif +#if defined(MHD_SUPPORT_MBEDTLS) +# include "tls_mbed_funcs.h" +#endif #include "daemon_options.h" @@ -67,7 +70,8 @@ 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_open_is_inited_fine ()) ? + || mhd_tls_open_is_inited_fine () + || mhd_tls_mbed_is_inited_fine ()) ? mhd_TLS_BACKEND_AVAIL_OK : mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE; #ifdef MHD_SUPPORT_GNUTLS @@ -82,5 +86,11 @@ mhd_tls_is_backend_available (struct DaemonOptions *s) mhd_TLS_BACKEND_AVAIL_OK : mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE; #endif +#ifdef MHD_SUPPORT_MBEDTLS + if (MHD_TLS_BACKEND_MBEDTLS == s->tls) + return mhd_tls_mbed_is_inited_fine () ? + mhd_TLS_BACKEND_AVAIL_OK : + mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE; +#endif return mhd_TLS_BACKEND_AVAIL_NOT_SUPPORTED; } diff --git a/src/mhd2/mhd_tls_funcs.h b/src/mhd2/mhd_tls_funcs.h @@ -61,6 +61,8 @@ # include "tls_gnu_funcs.h" #elif defined(MHD_SUPPORT_OPENSSL) # include "tls_open_funcs.h" +#elif defined(MHD_SUPPORT_MBEDTLS) +# include "tls_mbed_funcs.h" #endif /* ** Global initialisation / de-initialisation ** */ diff --git a/src/mhd2/tls_mbed_conn_data.h b/src/mhd2/tls_mbed_conn_data.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ +/* + This file is part of GNU libmicrohttpd. + Copyright (C) 2025 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. + + Alternatively, you can redistribute GNU libmicrohttpd and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version, together + with the eCos exception, as follows: + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile this + file and link it with other works to produce a work based on this + file, this file does not by itself cause the resulting work to be + covered by the GNU General Public License. However the source code + for this file must still be made available in accordance with + section (3) of the GNU General Public License v2. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + + You should have received copies of the GNU Lesser General Public + License and the GNU General Public License along with this library; + if not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file src/mhd2/tls_mbed_conn_data.h + * @brief The definition of MbedTLS connection-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_MBED_CONN_DATA_H +#define MHD_TLS_MBED_CONN_DATA_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_SUPPORT_MBEDTLS +#error This header can be used only if MbedTLS is enabled +#endif + +#include "sys_bool_type.h" +#include "sys_sizet_type.h" + +#include "tls_mbed_tls_lib.h" + +#include "mhd_socket_error.h" + +struct mhd_ConnSocket; /* Forward declaration */ + +#ifndef NDEBUG +struct mhd_TlsMbedConnDebug +{ + bool is_inited; + bool is_tls_handshake_completed; + bool is_failed; +}; +#endif /* ! NDEBUG */ + +/** + * The state for the current "custom transport" operation + */ +struct mhd_TlsMbedConnCstmTrtState +{ + /** + * 'true' if recv() callback has been called + */ + bool recv_called; + /** + * The result of last call of recv(). + * #mhd_SOCKET_ERR_NO_ERROR if recv() has not been called. + */ + enum mhd_SocketError recv_res; + /** + * 'true' if send() callback has been called + */ + bool send_called; + /** + * The result of last call of send(). + * #mhd_SOCKET_ERR_NO_ERROR if send() has not been called. + */ + enum mhd_SocketError send_res; + /** + * The size of the send() data before TLS encryption. + * Zero if no application data is being sent. + */ + size_t send_unenc_size; +}; + +/** + * Data for connection's "custom transport" + */ +struct mhd_TlsMbedConnCstmTrtData +{ + /** + * The pointer to the socket information data + */ + struct mhd_ConnSocket *sk; + /** + * The state for the current "custom transport" operation + */ + struct mhd_TlsMbedConnCstmTrtState state; +}; + +/** + * The structure with connection-specific MbedTLS data + * + * @note Unlike other TLS backends this struct contains MbedTLS data itself, + * not just pointers. + */ +struct mhd_TlsMbedConnData +{ + /** + * MbedTLS session data + */ + mbedtls_ssl_context sess; + + /** + * Data for connection's "custom transport" + */ + struct mhd_TlsMbedConnCstmTrtData tr; + /** + * 'true' is already received data in waiting in TLS buffers + */ + bool recv_data_in_buff; + /** + * 'true' if sent TLS shutdown "alert" + */ + bool shut_tls_wr_sent; + + /** + * 'true' if received EOF (the peer initiated TLS shut down) + */ + bool shut_tls_wr_received; +#ifndef NDEBUG + /** + * Debugging data + */ + struct mhd_TlsMbedConnDebug dbg; +#endif /* ! NDEBUG */ +}; + +#endif /* ! MHD_TLS_MBED_CONN_DATA_H */ diff --git a/src/mhd2/tls_mbed_daemon_data.h b/src/mhd2/tls_mbed_daemon_data.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ +/* + This file is part of GNU libmicrohttpd. + Copyright (C) 2025 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. + + Alternatively, you can redistribute GNU libmicrohttpd and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version, together + with the eCos exception, as follows: + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile this + file and link it with other works to produce a work based on this + file, this file does not by itself cause the resulting work to be + covered by the GNU General Public License. However the source code + for this file must still be made available in accordance with + section (3) of the GNU General Public License v2. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + + You should have received copies of the GNU Lesser General Public + License and the GNU General Public License along with this library; + if not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file src/mhd2/tls_mbed_daemon_data.h + * @brief The definition of MbedTLS daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_MBED_DAEMON_DATA_H +#define MHD_TLS_MBED_DAEMON_DATA_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_SUPPORT_MBEDTLS +#error This header can be used only if MbedTLS is enabled +#endif + +#include "tls_mbed_tls_lib.h" + +/** + * The structure with daemon-specific MbedTLS data. + * + * @note Unlike other TLS backends this struct contains MbedTLS data itself, + * not just pointers. + */ +struct mhd_TlsMbedDaemonData +{ + /** + * The daemon TLS configuration. + */ + mbedtls_ssl_config tls_conf; + + /** + * The certificates chain + */ + mbedtls_x509_crt cert_chain; + + /** + * The private key + */ + mbedtls_pk_context prv_key; + +#ifdef MBEDTLS_SSL_ALPN + /** + * Enabled protocols for ALPN + */ + const char *alpn_prots[4]; +#endif /* MBEDTLS_SSL_ALPN */ +}; + +#endif /* ! MHD_TLS_MBED_DAEMON_DATA_H */ diff --git a/src/mhd2/tls_mbed_funcs.c b/src/mhd2/tls_mbed_funcs.c @@ -0,0 +1,1541 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ +/* + This file is part of GNU libmicrohttpd. + Copyright (C) 2025 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. + + Alternatively, you can redistribute GNU libmicrohttpd and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version, together + with the eCos exception, as follows: + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile this + file and link it with other works to produce a work based on this + file, this file does not by itself cause the resulting work to be + covered by the GNU General Public License. However the source code + for this file must still be made available in accordance with + section (3) of the GNU General Public License v2. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + + You should have received copies of the GNU Lesser General Public + License and the GNU General Public License along with this library; + if not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file src/mhd2/tls_mbed_funcs.c + * @brief The implementation of MbedTLS wrapper functions + * @author Karlson2k (Evgeny Grin) + */ + +/* A few macros can be defined at MHD build-time to adjust code interfacing + with MbedTLS library: + - MHD_TLS_MBED_USE_PSA_FREE + - MHD_TLS_MBED_PREF_RNG_PSA + - MHD_TLS_MBED_PREF_RNG_HMAC + - MHD_TLS_MBED_PREF_RNG_CTR + - MHD_TLS_MBED_SKIP_PLATFORM_SETUP + - MHD_TLS_MBED_USE_PLATFORM_TEARDOWN + - MHD_TLS_MBED_SKIP_CERT_KEY_MATCH_CHECK + - MHD_TLS_MBED_DBG_PRINT_LEVEL + See macros use in this file and in tls_mbed_tls_lib.h. + Macros can be defined, for example, by CPPFLAGS before run of the configure. + */ + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "compat_calloc.h" +#include "sys_malloc.h" +#include <string.h> + +#ifdef mhd_USE_TLS_DEBUG_MESSAGES +# include <stdio.h> /* For TLS debug printing */ +#endif + +#include "mhd_assert.h" +#include "mhd_unreachable.h" +#include "mhd_assume.h" + +#include "mhd_constexpr.h" +#include "mhd_arr_num_elems.h" + +#include "mhd_conn_socket.h" + +#include "mhd_tls_internal.h" + +#include "tls_mbed_tls_lib.h" + +#include "mhd_public_api.h" + +#include "mhd_tls_ver_stct.h" + +#include "daemon_logger.h" + +#include "daemon_options.h" + +#include "sckt_recv.h" +#include "sckt_send.h" + +#include "tls_mbed_daemon_data.h" +#include "tls_mbed_conn_data.h" +#include "tls_mbed_funcs.h" + +#if defined(mhd_USE_TLS_DEBUG_MESSAGES) && defined(MBEDTLS_DEBUG_C) +# define mhd_TLS_MBED_HAS_DEBUG_PRINT 1 + +/* MHD_TLS_MBED_DBG_PRINT_LEVEL can be defined to number in range 0..5, + where 5 is the most detailed log */ +#ifdef MHD_TLS_MBED_DBG_PRINT_LEVEL +# define mhd_DBG_PRINT_LEVEL (MHD_TLS_MBED_DBG_PRINT_LEVEL + 0) +#else +# define mhd_DBG_PRINT_LEVEL (2) +#endif + +static void +mhd_tls_mbed_debug_print (void *ctx, + int level, + const char *filename, + int line_num, + const char *msg) +{ + (void) ctx; /* Unused */ + /* The level should be pre-filtred by MbedTLS, but it is filtered again + here in case if something else changed it. */ + if (mhd_DBG_PRINT_LEVEL < level) + return; + (void) fprintf (stderr, "## MbedTLS %02i [%s:%d]: %s\n", + level, + filename, + line_num, + msg); + (void) fflush (stderr); +} + + +#endif /* mhd_USE_TLS_DEBUG_MESSAGES && MBEDTLS_DEBUG_C */ + + +/* ** Global initialisation / de-initialisation ** */ + +#ifdef MHD_TLS_MBED_PREF_RNG_HMAC +static const mbedtls_md_info_t * +mbed_get_md_for_drbg (void) +{ + mhd_constexpr mbedtls_md_type_t mds[] = { +#ifdef mhd_TLS_MBED_HAS_SHA3_IDS + MBEDTLS_MD_SHA3_256 + , +#endif /* mhd_TLS_MBED_HAS_SHA3_IDS */ + MBEDTLS_MD_SHA256 +#ifdef mhd_TLS_MBED_HAS_SHA3_IDS + , + MBEDTLS_MD_SHA3_512 +#endif /* mhd_TLS_MBED_HAS_SHA3_IDS */ + , + MBEDTLS_MD_SHA512 +#ifdef mhd_TLS_MBED_HAS_SHA3_IDS + , + MBEDTLS_MD_SHA3_384 +#endif /* mhd_TLS_MBED_HAS_SHA3_IDS */ + , + MBEDTLS_MD_SHA384 +#ifdef mhd_TLS_MBED_HAS_SHA3_IDS + , + MBEDTLS_MD_SHA3_224 +#endif /* mhd_TLS_MBED_HAS_SHA3_IDS */ + , + MBEDTLS_MD_SHA224 + }; + size_t i; + + for (i = 0; i < mhd_ARR_NUM_ELEMS (mds); ++i) + { + const mbedtls_md_info_t *const ret = + mbedtls_md_info_from_type (mds[i]); + if (NULL != ret) + return ret; + } + + return (const mbedtls_md_info_t *) NULL; +} + + +#endif /* MHD_TLS_MBED_PREF_RNG_HMAC */ + +static bool mbedtls_lib_inited_now = false; +/* Must be checked when MHD-internal random generator is used */ +static bool mbedtls_rng_inited_now = false; +static bool mbedtls_lib_inited_once = false; + +#ifdef mhd_TLS_MBED_HAS_PLATFORM_SETUP +static mbedtls_platform_context mhd_mbed_plat_ctx; +#endif /* mhd_TLS_MBED_HAS_PLATFORM_SETUP */ + +#if defined(mhd_TLS_MBED_USE_LIB_ENTROPY) +static mbedtls_entropy_context mhd_mbed_entr_ctx; +#endif /* mhd_TLS_MBED_USE_LIB_ENTROPY */ + +#if defined(MHD_TLS_MBED_PREF_RNG_CTR) +static mbedtls_ctr_drbg_context mhd_mbed_ctr_drbg_ctx; +#elif defined(MHD_TLS_MBED_PREF_RNG_HMAC) +static mbedtls_hmac_drbg_context mhd_mbed_hmac_drbg_ctx; +#endif /* MHD_TLS_MBED_PREF_RNG_HMAC */ + +static bool +mbed_rng_init (void) +{ +#ifdef mhd_TLS_MBED_RNG_PREF_NEEDS_ENTROPY + int (*entropy_cb)(void *ctx, unsigned char *out, size_t out_size); + void *entropy_cb_ctx; + +# ifdef mhd_TLS_MBED_USE_LIB_ENTROPY + mbedtls_entropy_init (&mhd_mbed_entr_ctx); + entropy_cb = &mbedtls_entropy_func; + entropy_cb_ctx = &mhd_mbed_entr_ctx; +# else /* ! mhd_TLS_MBED_USE_LIB_ENTROPY */ + /* Seeding with system's entropy sources could be implemented here */ +#error MbedTLS random generator needs entropy sources +# endif /* ! mhd_TLS_MBED_USE_LIB_ENTROPY */ +#endif /* mhd_TLS_MBED_RNG_PREF_NEEDS_ENTROPY */ + + mhd_assert (! mbedtls_rng_inited_now); + + if (1) /* For local scope only */ + { +#ifdef mhd_TLS_MBED_RNG_PREF_NEEDS_ENTROPY + static const char id_str[] = "libmicrohttpd2"; + static uint_fast64_t init_cntr = 0u; + static void *uniq_ptr1 = &init_cntr; /* Any address should be unique in the same address space */ + void *uniq_ptr2 = &uniq_ptr2; /* Any address should be unique in the same address space */ + unsigned char pers[sizeof(id_str) + + sizeof(uniq_ptr1) + + sizeof(uniq_ptr2) + + sizeof(init_cntr)]; + + memcpy (pers, + id_str, + sizeof(id_str)); + memcpy (pers + sizeof(id_str), + &uniq_ptr1, + sizeof(uniq_ptr1)); + memcpy (pers + sizeof(id_str) + sizeof(uniq_ptr1), + &uniq_ptr2, + sizeof(uniq_ptr2)); + memcpy (pers + sizeof(id_str) + sizeof(uniq_ptr1) + sizeof(uniq_ptr2), + &init_cntr, + sizeof(init_cntr)); + ++init_cntr; +#endif /* mhd_TLS_MBED_RNG_PREF_NEEDS_ENTROPY */ + +#if defined(MHD_TLS_MBED_PREF_RNG_CTR) + mbedtls_ctr_drbg_init (&mhd_mbed_ctr_drbg_ctx); + mbedtls_rng_inited_now = + (0 == mbedtls_ctr_drbg_seed (&mhd_mbed_ctr_drbg_ctx, + entropy_cb, + entropy_cb_ctx, + pers, + sizeof(pers))); + if (! mbedtls_rng_inited_now) + mbedtls_ctr_drbg_free (&mhd_mbed_ctr_drbg_ctx); +#elif defined(MHD_TLS_MBED_PREF_RNG_HMAC) + mbedtls_hmac_drbg_init (&mhd_mbed_hmac_drbg_ctx); + mbedtls_rng_inited_now = + (0 == mbedtls_hmac_drbg_seed (&mhd_mbed_hmac_drbg_ctx, + mbed_get_md_for_drbg (), /* NULL is handled by mbedtls_hmac_drbg_seed() */ + entropy_cb, + entropy_cb_ctx, + pers, + sizeof(pers))); + if (! mbedtls_rng_inited_now) + mbedtls_hmac_drbg_free (&mhd_mbed_hmac_drbg_ctx); +#elif defined(MHD_TLS_MBED_PREF_RNG_PSA) + mbedtls_rng_inited_now = true; /* No additional initialisation needed */ +#elif ! defined(mhd_TLS_MBED_INIT_TLS_REQ_RNG) + mbedtls_rng_inited_now = false; +#else /* mhd_TLS_MBED_INIT_TLS_REQ_RNG */ +#error MbedTLS backend requires random generator + /* Support for external strong random generator could be added */ + mbedtls_rng_inited_now = false; +#endif + if (mbedtls_rng_inited_now) + return true; /* Success exit point */ + } + +#ifdef mhd_TLS_MBED_USE_LIB_ENTROPY + mbedtls_entropy_free (&mhd_mbed_entr_ctx); +#endif /* mhd_TLS_MBED_USE_LIB_ENTROPY */ + + return false; /* Failure exit point */ +} + + +static void +mbed_rng_deinit (void) +{ + if (! mbedtls_rng_inited_now) + return; + +#if defined(MHD_TLS_MBED_PREF_RNG_CTR) + mbedtls_ctr_drbg_free (&mhd_mbed_ctr_drbg_ctx); +#elif defined(MHD_TLS_MBED_PREF_RNG_HMAC) + mbedtls_hmac_drbg_free (&mhd_mbed_hmac_drbg_ctx); +#endif + +#ifdef mhd_TLS_MBED_USE_LIB_ENTROPY + mbedtls_entropy_free (&mhd_mbed_entr_ctx); +#endif /* mhd_TLS_MBED_USE_LIB_ENTROPY */ + + mbedtls_rng_inited_now = false; +} + + +MHD_INTERNAL void +mhd_tls_mbed_global_init (void) +{ +#ifdef MBEDTLS_VERSION_C + if (1) + { + const unsigned int ver = mbedtls_version_get_number (); + if (MBEDTLS_VERSION_NUMBER > ver) + return; /* Run-time version is lower than build-time version */ + if (((MBEDTLS_VERSION_NUMBER) >> 24u) != (ver >> 24u)) + return; /* Run-time major version does not match build-time major version */ + } +#endif /* MBEDTLS_VERSION_C */ + +#ifdef mhd_TLS_MBED_HAS_PLATFORM_SETUP +# ifdef mhd_TLS_MBED_USE_PLATFORM_TEARDOWN + /* 'setup' platform repeatedly only only if 'teardown' is called */ + if (mbedtls_lib_inited_once) + (void) 0; /* Do not repeat 'setup' */ + else /* combined with tne next 'if()' */ +# endif /* mhd_TLS_MBED_USE_PLATFORM_TEARDOWN */ + if (0 != mbedtls_platform_setup (&mhd_mbed_plat_ctx)) + return; /* Error platform initialising */ +#endif /* mhd_TLS_MBED_HAS_PLATFORM_SETUP */ + +#ifdef mhd_TLS_MBED_USE_PSA + /* It is safe to call psa_crypto_init() several times */ + if (PSA_SUCCESS != psa_crypto_init ()) + { +#ifdef mhd_TLS_MBED_USE_PLATFORM_TEARDOWN + mbedtls_platform_teardown (&mhd_mbed_plat_ctx); +#endif /* mhd_TLS_MBED_USE_PLATFORM_TEARDOWN */ + return; + } +#endif /* mhd_TLS_MBED_USE_PSA */ + mbedtls_lib_inited_once = true; + +#if mhd_TLS_MBED_INIT_TLS_REQ_RNG + mbedtls_lib_inited_now = mbed_rng_init (); +#else /* ! mhd_TLS_MBED_INIT_TLS_REQ_RNG */ + (void) mbed_rng_init (); + mbedtls_lib_inited_now = true; /* MbedTLS could be used even without random generator */ +#endif /* ! mhd_TLS_MBED_INIT_TLS_REQ_RNG */ + + if (! mbedtls_lib_inited_now) + { +#ifdef mhd_TLS_MBED_USE_PLATFORM_TEARDOWN + mbedtls_platform_teardown (&mhd_mbed_plat_ctx); +#endif /* mhd_TLS_MBED_USE_PLATFORM_TEARDOWN */ + +#ifdef mhd_TLS_MBED_USE_PSA_FREE + mbedtls_psa_crypto_free (); +#endif /* mhd_TLS_MBED_USE_PLATFORM_TEARDOWN */ + (void) 0; + } +} + + +MHD_INTERNAL void +mhd_tls_mbed_global_deinit (void) +{ + if (! mbedtls_lib_inited_now) + return; + + mbed_rng_deinit (); + +#ifdef mhd_TLS_MBED_USE_PSA_FREE + /* Not used by default as it will break all calls to PSA performed + directly by the application after closing all active MHD daemons */ + mbedtls_psa_crypto_free (); +#endif /* mhd_TLS_MBED_USE_PLATFORM_TEARDOWN */ + +#ifdef mhd_TLS_MBED_USE_PLATFORM_TEARDOWN + /* Not used by default as it will break all calls to MbedTLS performed + directly by the application after closing all active MHD daemons */ + mbedtls_platform_teardown (&mhd_mbed_plat_ctx); +#endif /* mhd_TLS_MBED_USE_PLATFORM_TEARDOWN */ + + mbedtls_lib_inited_now = false; +} + + +MHD_INTERNAL MHD_FN_PURE_ bool +mhd_tls_mbed_is_inited_fine (void) +{ + mhd_assert (! mbedtls_lib_inited_now || mbedtls_lib_inited_once); + return mbedtls_lib_inited_now; +} + + +/* ** Daemon initialisation / de-initialisation ** */ + +/** + * Check application-provided daemon TLS settings + * @param d the daemon handle + * @param sk_edge_trigg the sockets polling uses edge-triggering + * @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_settings (struct MHD_Daemon *restrict d, + bool sk_edge_trigg, + struct DaemonOptions *restrict s) +{ + mhd_assert (MHD_TLS_BACKEND_NONE != s->tls); + mhd_assert ((MHD_TLS_BACKEND_MBEDTLS == 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); + + if ((MHD_WM_THREAD_PER_CONNECTION == s->work_mode.mode) || + ((MHD_WM_WORKER_THREADS == s->work_mode.mode) + && (1u < s->work_mode.params.num_worker_threads))) + { + bool threads_supported; +#if ! defined(MBEDTLS_THREADING_C) + threads_supported = false; +#else /* MBEDTLS_THREADING_C */ +# if defined(MBEDTLS_VERSION_FEATURES) + threads_supported = + (0 == mbedtls_version_check_feature ("MBEDTLS_THREADING_C")); +# else /* ! MBEDTLS_VERSION_FEATURES */ + threads_supported = true; +# endif /* ! MBEDTLS_VERSION_FEATURES */ +#endif /* MBEDTLS_THREADING_C */ + if (! threads_supported) + { + mhd_LOG_MSG (d, MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS, \ + "MbedTLS built without threading support and cannot " + "be used in multi-threaded modes"); + return MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS; + } + } + + return MHD_SC_OK; +} + + +/** + * Set daemon TLS credentials. + * 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 + * @param rng_func the random generator function + * @param rng_ctx the random generator function context + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) +MHD_FN_PAR_NONNULL_ (3) MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_init_credentials ( + struct MHD_Daemon *restrict d, + struct mhd_TlsMbedDaemonData *restrict d_tls, + struct DaemonOptions *restrict s +#if defined(mhd_TLS_MBED_INIT_TLS_REQ_RNG) + , + int (*rng_func)(void *ctx, unsigned char *out, size_t out_size), + void *rng_ctx +#endif /* mhd_TLS_MBED_INIT_TLS_REQ_RNG */ + ) +{ + enum MHD_StatusCode ret; + size_t cert_len; + size_t key_len; + size_t pwd_len; + int res; + + ret = MHD_SC_OK; + + // TODO: Support multiple certificates + cert_len = strlen (s->tls_cert_key.v_mem_cert); // TODO: Reuse calculated length + key_len = strlen (s->tls_cert_key.v_mem_key); // TODO: Reuse calculated length + pwd_len = (NULL == s->tls_cert_key.v_mem_pass) ? + 0u : strlen (s->tls_cert_key.v_mem_pass); // TODO: Reuse calculated length + + mhd_assert (0 != cert_len); + mhd_assert (0 != key_len); + + mbedtls_x509_crt_init (&(d_tls->cert_chain)); + + res = mbedtls_x509_crt_parse (&(d_tls->cert_chain), + (const unsigned char *) + s->tls_cert_key.v_mem_cert, + cert_len + 1u /* Include terminating zero */); + if (0 == res) + { + mbedtls_pk_init (&(d_tls->prv_key)); +#if defined(mhd_TLS_MBED_INIT_TLS_REQ_RNG) + if (0 != mbedtls_pk_parse_key (&(d_tls->prv_key), + (const unsigned char *) + s->tls_cert_key.v_mem_key, + key_len + 1u, + (const unsigned char *) + s->tls_cert_key.v_mem_pass, + pwd_len, + rng_func, + rng_ctx)) + ret = MHD_SC_TLS_CONF_BAD_CERT; +#else /* ! mhd_TLS_MBED_INIT_TLS_REQ_RNG */ + if (0 != mbedtls_pk_parse_key (&(d_tls->prv_key), + (const unsigned char *) + s->tls_cert_key.v_mem_key, + key_len + 1u, + (const unsigned char *) + s->tls_cert_key.v_mem_pass, + pwd_len)) + ret = MHD_SC_TLS_CONF_BAD_CERT; +#endif /* ! mhd_TLS_MBED_INIT_TLS_REQ_RNG */ + + if (MHD_SC_OK == ret) + { + /* The next macro can be defined at MHD build-time to skip potentially + expensive check */ +#ifdef MHD_TLS_MBED_SKIP_CERT_KEY_MATCH_CHECK + return MHD_SC_OK; /* Success exit point */ +#else /* ! MHD_TLS_MBED_SKIP_CERT_KEY_MATCH_CHECK */ +# if defined(mhd_TLS_MBED_INIT_TLS_REQ_RNG) + res = mbedtls_pk_check_pair (&(d_tls->cert_chain.pk), + &(d_tls->prv_key), + rng_func, + rng_ctx); +# else /* ! mhd_TLS_MBED_INIT_TLS_REQ_RNG */ + res = mbedtls_pk_check_pair (&(d_tls->cert_chain.pk), + &(d_tls->prv_key)); +# endif /* ! mhd_TLS_MBED_INIT_TLS_REQ_RNG */ + if ((0 == res) || + (MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE == res)) + return MHD_SC_OK; /* Success exit point */ + + mhd_LOG_MSG (d, MHD_SC_TLS_CONF_BAD_CERT, \ + "The private key data does not match the certificate"); + ret = MHD_SC_TLS_CONF_BAD_CERT; +#endif /* ! MHD_TLS_MBED_SKIP_CERT_KEY_MATCH_CHECK */ + } + else + { + mhd_LOG_MSG (d, MHD_SC_TLS_CONF_BAD_CERT, \ + "The private key data cannot be decoded"); + ret = MHD_SC_TLS_CONF_BAD_CERT; + } + + mbedtls_pk_free (&(d_tls->prv_key)); + } + else + { + mhd_LOG_PRINT (d, + MHD_SC_TLS_CONF_BAD_CERT, + mhd_LOG_FMT ("Failed to parse certificates chain. " + "Number of failed certificates: %i"), + res); + ret = MHD_SC_TLS_CONF_BAD_CERT; + } + mbedtls_x509_crt_free (&(d_tls->cert_chain)); + + mhd_assert (MHD_SC_OK != ret); + return ret; /* Failure exit point */ +} + + +/** + * De-initialise daemon TLS credentials. + * @param d_tls the daemon TLS settings + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_credentials (struct mhd_TlsMbedDaemonData *restrict d_tls) +{ + mbedtls_pk_free (&(d_tls->prv_key)); + + mbedtls_x509_crt_free (&(d_tls->cert_chain)); +} + + +#ifdef MBEDTLS_SSL_ALPN +/** + * Initialise daemon ALPN data + * 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_set_alpn (struct MHD_Daemon *restrict d, + struct mhd_TlsMbedDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + static const char alpn_str_http1_0[] = mhd_ALPN_H1_0; + static const char alpn_str_http1_1[] = mhd_ALPN_H1_1; +# ifdef MHD_SUPPORT_HTTP2 + static const char alpn_str_http2[] = mhd_ALPN_H2; +# endif + size_t i; + + (void) s; /* Unused currently. Implement reading allowed HTTP versions */ + + i = 0u; + // TODO: implement reading protocol versions from settings */ +#ifdef MHD_SUPPORT_HTTP2 + if (1 /* enabled HTTP/2 ? */) + d_tls->alpn_prots[i++] = alpn_str_http2; +#endif /* MHD_SUPPORT_HTTP2 */ + + if (1 /* enabled HTTP/1.x ? */) + { + d_tls->alpn_prots[i++] = alpn_str_http1_1; + d_tls->alpn_prots[i++] = alpn_str_http1_0; + } + + d_tls->alpn_prots[i] = NULL; /* NULL termination */ + mhd_assert (mhd_ARR_NUM_ELEMS (d_tls->alpn_prots) > i); + mhd_assert (0u != i); + + if (0 == mbedtls_ssl_conf_alpn_protocols (&(d_tls->tls_conf), + d_tls->alpn_prots)) + return MHD_SC_OK; /* Success exit point */ + + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to set ALPN data"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; +} + + +#else /* ! MBEDTLS_SSL_ALPN */ +# define daemon_set_alpn(d,d_tls,s) (MHD_SC_OK) +#endif /* ! MBEDTLS_SSL_ALPN */ + +/** + * Set daemon TLS configuration. + * 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 + * @param rng_func the random generator function + * @param rng_ctx the random generator function context + * @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_config (struct MHD_Daemon *restrict d, + struct mhd_TlsMbedDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode ret; +#if defined(mhd_TLS_MBED_INIT_TLS_REQ_RNG) + int (*rng_func)(void *ctx, unsigned char *out, size_t out_size); + void *rng_ctx; + +# if defined(MHD_TLS_MBED_PREF_RNG_CTR) + rng_func = &mbedtls_ctr_drbg_random; + rng_ctx = &mhd_mbed_ctr_drbg_ctx; +# elif defined(MHD_TLS_MBED_PREF_RNG_HMAC) + rng_func = &mbedtls_hmac_drbg_random; + rng_ctx = &mhd_mbed_hmac_drbg_ctx; +# elif defined(MHD_TLS_MBED_PREF_RNG_PSA) + rng_func = &mbedtls_psa_get_random; + rng_ctx = MBEDTLS_PSA_RANDOM_STATE; +# else /* MHD_TLS_MBED_PREF_RNG_PSA */ + /* Support for external strong random generator could be added here */ +#error No random generator is enabled in MbedTLS + return MHD_SC_INTERNAL_ERROR; +# endif /* MHD_TLS_MBED_PREF_RNG_PSA */ + + ret = daemon_init_credentials (d, + d_tls, + s, + rng_func, + rng_ctx); +#else /* mhd_TLS_MBED_INIT_TLS_REQ_RNG */ + ret = daemon_init_credentials (d, + d_tls, + s); +#endif /* mhd_TLS_MBED_INIT_TLS_REQ_RNG */ + + if (MHD_SC_OK != ret) + return ret; + + mbedtls_ssl_config_init (&(d_tls->tls_conf)); + +#ifdef mhd_TLS_MBED_HAS_DEBUG_PRINT + mbedtls_ssl_conf_dbg (&(d_tls->tls_conf), + mhd_tls_mbed_debug_print, + NULL); + mbedtls_debug_set_threshold (mhd_DBG_PRINT_LEVEL); +#endif /* mhd_TLS_MBED_HAS_DEBUG_PRINT */ + + if (0 == + mbedtls_ssl_config_defaults (&(d_tls->tls_conf), + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) + { +#if defined(mhd_TLS_MBED_INIT_TLS_REQ_RNG) + mbedtls_ssl_conf_rng (&(d_tls->tls_conf), + rng_func, + rng_ctx); +#endif /* mhd_TLS_MBED_INIT_TLS_REQ_RNG */ + + /* Client certificates are not implemented yet */ + mbedtls_ssl_conf_authmode (&(d_tls->tls_conf), + MBEDTLS_SSL_VERIFY_NONE); + + if (0 == + mbedtls_ssl_conf_own_cert (&(d_tls->tls_conf), + &(d_tls->cert_chain), + &(d_tls->prv_key))) + { + ret = daemon_set_alpn (d, + d_tls, + s); + if (MHD_SC_OK == ret) + return MHD_SC_OK; /* Success exit point */ + + /* Below is a cleanup path */ + } + else + ret = MHD_SC_DAEMON_MEM_ALLOC_FAILURE; /* Do not waste binary space on the additional message */ + } + else + ret = MHD_SC_DAEMON_MEM_ALLOC_FAILURE; /* Do not waste binary space on the additional message */ + + mbedtls_ssl_config_free (&(d_tls->tls_conf)); + + daemon_deinit_credentials (d_tls); + + mhd_assert (MHD_SC_OK != ret); + return ret; /* Failure exit point */ +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (4) mhd_StatusCodeInt +mhd_tls_mbed_daemon_init (struct MHD_Daemon *restrict d, + bool sk_edge_trigg, + struct DaemonOptions *restrict s, + struct mhd_TlsMbedDaemonData **restrict p_d_tls) +{ + mhd_StatusCodeInt res; + struct mhd_TlsMbedDaemonData *restrict d_tls; + + /* Successful initialisation must be checked earlier */ + mhd_assert (mbedtls_lib_inited_once); + mhd_assert (mbedtls_lib_inited_now); + + res = check_app_tls_settings (d, + sk_edge_trigg, + s); + if (MHD_SC_OK != res) + return res; + + d_tls = (struct mhd_TlsMbedDaemonData *) + mhd_calloc (1, sizeof (struct mhd_TlsMbedDaemonData)); + *p_d_tls = d_tls; + if (NULL == d_tls) + return MHD_SC_DAEMON_MEM_ALLOC_FAILURE; + + res = daemon_init_config (d, + d_tls, + s); + if (MHD_SC_OK == res) + { + return MHD_SC_OK; /* Success exit point */ + } + /* Below is a clean-up code path */ + free (d_tls); + *p_d_tls = NULL; + mhd_assert (MHD_SC_OK != res); + return res; /* Failure exit point */ +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_INOUT_ (1) void +mhd_tls_mbed_daemon_deinit (struct mhd_TlsMbedDaemonData *restrict d_tls) +{ + mhd_assert (NULL != d_tls); + + mbedtls_ssl_config_free (&(d_tls->tls_conf)); + + daemon_deinit_credentials (d_tls); + + free (d_tls); +} + + +/* ** Connection initialisation / de-initialisation ** */ + +MHD_INTERNAL size_t +mhd_tls_mbed_conn_get_tls_size_v (void) +{ + return sizeof (struct mhd_TlsMbedConnData); +} + + +/* Forward declarations of custom transport callbacks */ +static int +mhd_mbed_cb_recv (void *ctx, + unsigned char *buf, + size_t size); + +static int +mhd_mbed_cb_send (void *ctx, + const unsigned char *buf, + size_t size); + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (3) bool +mhd_tls_mbed_conn_init (const struct mhd_TlsMbedDaemonData *restrict d_tls, + struct mhd_ConnSocket *sk, + struct mhd_TlsMbedConnData *restrict c_tls) +{ + c_tls->tr.sk = sk; + + mbedtls_ssl_init (&(c_tls->sess)); + + if (0 == mbedtls_ssl_setup (&(c_tls->sess), + &(d_tls->tls_conf))) + { + mbedtls_ssl_set_bio (&(c_tls->sess), + c_tls, + &mhd_mbed_cb_send, + &mhd_mbed_cb_recv, + NULL /* no recv_timeout callback */); + +#ifndef NDEBUG + c_tls->dbg.is_inited = true; +#endif + return true; /* Success exit point */ + } + + mbedtls_ssl_free (&(c_tls->sess)); + return false; /* Failure exit point */ +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void +mhd_tls_mbed_conn_deinit (struct mhd_TlsMbedConnData *restrict c_tls) +{ + mhd_assert (c_tls->dbg.is_inited); + mbedtls_ssl_free (&(c_tls->sess)); +#ifndef NDEBUG + c_tls->dbg.is_inited = false; +#endif +} + + +/* ** Custom transport functions ** */ + +/** + * Prepare for network operation + * @param c_tls the connection TLS data + */ +mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ void +mhd_tls_mbed_sckt_comm_prep (struct mhd_TlsMbedConnData *restrict c_tls) +{ + memset (&(c_tls->tr.state), + 0, + sizeof(c_tls->tr.state)); +} + + +/** + * Prepare for send() network operation + * @param c_tls the connection TLS data + * @param unencr_size the size of the data to send before encryption + * @param push_data set to 'false' if it is know that the data to be sent + * is incomplete (message or chunk), + * set to 'true' if the data is complete or the final part + */ +mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ void +mhd_tls_mbed_sckt_comm_prep_send (struct mhd_TlsMbedConnData *restrict c_tls, + size_t unencr_size, + bool push_data) +{ + mhd_tls_mbed_sckt_comm_prep (c_tls); + + if (push_data) + c_tls->tr.state.send_unenc_size = unencr_size; + else + c_tls->tr.state.send_unenc_size = (size_t) (~((size_t) 0)); +} + + +/** + * The callback which called by MbedTLS to receive the data + * @param ctx the context for the send callback + * @param buf the buffer to put received data + * @param size the size of the @a buf + * @return the positive number of bytes received on success, + * 0 if EOF received (peer closed write/send), + * #MBEDTLS_ERR_SSL_WANT_READ if receiving would block OR + * receiving was interrupted, + * #MBEDTLS_ERR_NET_CONN_RESET if connection was broken + * or #MBEDTLS_ERR_NET_RECV_FAILED in case of other errors + */ +static int +mhd_mbed_cb_recv (void *ctx, + unsigned char *buf, + size_t size) +{ + struct mhd_TlsMbedConnData *const c_tls = (struct mhd_TlsMbedConnData *) ctx; + struct mhd_TlsMbedConnCstmTrtState *const state = &(c_tls->tr.state); + size_t received; + + /* MbedTLS may call recv() several times. + This may result in unwanted extra syscalls, unfair connections + processing or even blocking if socket is blocking. + MHD limits to single send() syscall per operation to evenly distribute + workload to all connections. */ + if (state->recv_called) + return MBEDTLS_ERR_SSL_WANT_READ; + + /* MbedTLS may call blindly recv() after calling send() first. + If send() was the first socket operation then the socket has been + checked by MHD for 'send-ready' as receiving operation was expected. + Do not use recv() if 'recv-ready' is not known and the socket is blocking. + */ + if (state->send_called && ! c_tls->tr.sk->props.is_nonblck) + return MBEDTLS_ERR_SSL_WANT_READ; + + if (1) + { + const int size_i = (int) size; + + if ((0 > size_i) || + (size != (size_t) size_i)) + { + /* Return value limitation */ + size = (size_t) (((unsigned int) ~((unsigned int) 0)) >> 1u); + } + } + + state->recv_res = mhd_sckt_recv (c_tls->tr.sk, + size, + (char *) buf, + &received); + state->recv_called = true; + + if (mhd_SOCKET_ERR_NO_ERROR == state->recv_res) + { + mhd_ASSUME (size >= received); + mhd_assert (0 <= (int) received); + return (int) received; + } + + if (mhd_SOCKET_ERR_INTR >= state->recv_res) + { + mhd_assert ((mhd_SOCKET_ERR_INTR == state->recv_res) || + (mhd_SOCKET_ERR_AGAIN == state->recv_res)); + return MBEDTLS_ERR_SSL_WANT_READ; + } + + if (mhd_SOCKET_ERR_IS_HARD (state->recv_res)) + { + c_tls->tr.sk->state.discnt_err = state->recv_res; + return MBEDTLS_ERR_NET_CONN_RESET; + } + + return MBEDTLS_ERR_NET_RECV_FAILED; +} + + +/** + * The callback called by MbedTLS to send the data + * @param ctx the context for the send callback + * @param buf the buffer with the data to send + * @param size the size of the data in the @a buf + * @return the positive number of bytes sent on success, + * #MBEDTLS_ERR_SSL_WANT_WRITE if sending would block OR + * sending was interrupted, + * #MBEDTLS_ERR_NET_CONN_RESET if connection was broken + * or #MBEDTLS_ERR_NET_SEND_FAILED in case of other errors + */ +static int +mhd_mbed_cb_send (void *ctx, + const unsigned char *buf, + size_t size) +{ + struct mhd_TlsMbedConnData *const c_tls = (struct mhd_TlsMbedConnData *) ctx; + struct mhd_TlsMbedConnCstmTrtState *const state = &(c_tls->tr.state); + /* Check whether the complete data is sending. + The compression is not used so the data after the encryption must not + be smaller than before the encryption. + The check may result in false-positive (unlikely in practice), but + this should not hurt the performance. */ + bool push_data = (size >= state->send_unenc_size); + size_t sent; + + /* MbedTLS may call send() several times in a loop, until all data is sent. + This may result in unwanted extra syscalls, unfair connections processing + or even blocking if socket is blocking. + MHD limits to single send() syscall per operation to evenly distribute + workload to all connections. */ + if (state->send_called) + return MBEDTLS_ERR_SSL_WANT_WRITE; + + /* MbedTLS may call blindly send() after calling recv() first. + If recv() was the first socket operation then the socket has been + checked by MHD for 'recv-ready' as receiving operation was expected. + Do not use send() if 'send-ready' is not known and the socket is blocking. + */ + if (state->recv_called && ! c_tls->tr.sk->props.is_nonblck) + return MBEDTLS_ERR_SSL_WANT_WRITE; + + if (1) + { + const int size_i = (int) size; + + if ((0 > size_i) || + (size != (size_t) size_i)) + { + /* Return value limitation */ + size = (size_t) (((unsigned int) ~((unsigned int) 0)) >> 1u); + push_data = false; + } + } + + state->send_res = mhd_sckt_send (c_tls->tr.sk, + size, + (const char *) buf, + push_data, + &sent); + state->send_called = true; + + if (mhd_SOCKET_ERR_NO_ERROR == state->send_res) + { + mhd_ASSUME (size >= sent); + mhd_assert (0 < (int) sent); + return (int) sent; + } + + if (mhd_SOCKET_ERR_INTR >= state->send_res) + { + mhd_assert ((mhd_SOCKET_ERR_INTR == state->send_res) || + (mhd_SOCKET_ERR_AGAIN == state->send_res)); + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + + if (mhd_SOCKET_ERR_IS_HARD (state->send_res)) + { + c_tls->tr.sk->state.discnt_err = state->send_res; + return MBEDTLS_ERR_NET_CONN_RESET; + } + + return MBEDTLS_ERR_NET_SEND_FAILED; +} + + +/* ** TLS connection establishing ** */ + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +enum mhd_TlsProcedureResult +mhd_tls_mbed_conn_handshake (struct mhd_TlsMbedConnData *c_tls) +{ + int res; + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (! c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->shut_tls_wr_sent); + mhd_assert (! c_tls->shut_tls_wr_received); + mhd_assert (! c_tls->dbg.is_failed); + + mhd_tls_mbed_sckt_comm_prep (c_tls); + + res = mbedtls_ssl_handshake (&(c_tls->sess)); + + mhd_assert ((c_tls->tr.state.recv_called) || + (mhd_SOCKET_ERR_NO_ERROR == c_tls->tr.state.recv_res)); + mhd_assert ((c_tls->tr.state.send_called) || + (mhd_SOCKET_ERR_NO_ERROR == c_tls->tr.state.send_res)); + + switch (res) + { + case 0: +#ifndef NDEBUG + c_tls->dbg.is_tls_handshake_completed = true; +#endif /* ! NDEBUG */ + return mhd_TLS_PROCED_SUCCESS; /* Success exit point */ + + case MBEDTLS_ERR_SSL_WANT_READ: + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.recv_res)); + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.send_res)); + + if (! c_tls->tr.state.recv_called) + return mhd_TLS_PROCED_RECV_INTERRUPTED; /* Do not clear 'recv-ready' flag */ + + if (mhd_SOCKET_ERR_AGAIN == c_tls->tr.state.recv_res) + return mhd_TLS_PROCED_RECV_MORE_NEEDED; /* Clear 'recv-ready' flag */ + + return mhd_TLS_PROCED_RECV_INTERRUPTED; /* Do not clear 'recv-ready' flag */ + + case MBEDTLS_ERR_SSL_WANT_WRITE: + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.recv_res)); + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.send_res)); + + if (! c_tls->tr.state.send_called) + return mhd_TLS_PROCED_SEND_INTERRUPTED; /* Do not clear 'send-ready' flag */ + + if (mhd_SOCKET_ERR_AGAIN == c_tls->tr.state.send_res) + return mhd_TLS_PROCED_SEND_MORE_NEEDED; /* Clear 'send-ready' flag */ + + return mhd_TLS_PROCED_SEND_INTERRUPTED; /* Do not clear 'send-ready' flag */ + + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + mhd_assert (0 && "MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS must not be returned"); + break; + + case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: + /* The result means that mbedtls_ssl_handshake() must be called again + later. + As this result does not map directly to any of available flags, + so map it to "waiting for send-ready" as the socket should be already + 'send-ready'. */ + + return (c_tls->tr.state.send_called && + (mhd_SOCKET_ERR_AGAIN == c_tls->tr.state.send_res)) ? + mhd_TLS_PROCED_SEND_MORE_NEEDED : mhd_TLS_PROCED_SEND_INTERRUPTED; + + case MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA: +#ifdef MBEDTLS_SSL_EARLY_DATA + /* Could be replaced with early data support is implemented */ +#endif /* MBEDTLS_SSL_EARLY_DATA */ + mhd_assert (0 && + "MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA must not be returned"); + break; + + default: + break; /* Handle other values below */ + } + + /* All other result codes must be interpreted as a hard error */ +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + + return mhd_TLS_PROCED_FAILED; +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +enum mhd_TlsProcedureResult +mhd_tls_mbed_conn_shutdown (struct mhd_TlsMbedConnData *c_tls) +{ + int res; + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->dbg.is_failed); + + mhd_tls_mbed_sckt_comm_prep (c_tls); + + res = mbedtls_ssl_close_notify (&(c_tls->sess)); + + switch (res) + { + case 0: + c_tls->shut_tls_wr_sent = true; + c_tls->shut_tls_wr_received = true; + return mhd_TLS_PROCED_SUCCESS; /* Success exit point */ + + case MBEDTLS_ERR_SSL_WANT_READ: + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.recv_res)); + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.send_res)); + + if (! c_tls->tr.state.recv_called) + return mhd_TLS_PROCED_RECV_INTERRUPTED; /* Do not clear 'recv-ready' flag */ + + if (mhd_SOCKET_ERR_AGAIN == c_tls->tr.state.recv_res) + return mhd_TLS_PROCED_RECV_MORE_NEEDED; /* Clear 'recv-ready' flag */ + + return mhd_TLS_PROCED_RECV_INTERRUPTED; /* Do not clear 'recv-ready' flag */ + + case MBEDTLS_ERR_SSL_WANT_WRITE: + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.recv_res)); + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.send_res)); + + if (! c_tls->tr.state.send_called) + return mhd_TLS_PROCED_SEND_INTERRUPTED; /* Do not clear 'send-ready' flag */ + + if (mhd_SOCKET_ERR_AGAIN == c_tls->tr.state.send_res) + return mhd_TLS_PROCED_SEND_MORE_NEEDED; /* Clear 'send-ready' flag */ + + return mhd_TLS_PROCED_SEND_INTERRUPTED; /* Do not clear 'send-ready' flag */ + + default: + break; /* Handle other values below */ + } + + /* All other result codes must be interpreted as a hard error */ +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + + return mhd_TLS_PROCED_FAILED; +} + + +/* ** Data receiving and sending ** */ + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (4) enum mhd_SocketError +mhd_tls_mbed_conn_recv (struct mhd_TlsMbedConnData *c_tls, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict received) +{ + int res; + + mhd_assert (0 != buf_size); + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->shut_tls_wr_sent); + mhd_assert (! c_tls->dbg.is_failed); + + if (1) + { + const int buf_size_i = (int) buf_size; + if ((0 > buf_size_i) || + (buf_size != (size_t) buf_size_i)) + { + /* Called function return value limitation */ + buf_size = (size_t) (((unsigned int) ~((unsigned int) 0)) >> 1u); + } + } + + c_tls->recv_data_in_buff = false; + mhd_tls_mbed_sckt_comm_prep (c_tls); + + res = mbedtls_ssl_read (&(c_tls->sess), + (unsigned char *) buf, + buf_size); + + if (0 <= res) + { + mhd_ASSUME (buf_size >= (size_t) res); + *received = (size_t) res; + + return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ + } + + switch (res) + { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + c_tls->shut_tls_wr_received = true; + *received = 0u; + + return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ + + case MBEDTLS_ERR_SSL_WANT_READ: + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.recv_res)); + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.send_res)); + + if (! c_tls->tr.state.recv_called) + return mhd_SOCKET_ERR_INTR; /* Do not clear 'recv-ready' flag */ + + if (mhd_SOCKET_ERR_NO_ERROR == c_tls->tr.state.recv_res) + { + /* recv() succeed for the first time and then called again */ + return c_tls->tr.sk->props.is_nonblck ? + mhd_SOCKET_ERR_INTR : mhd_SOCKET_ERR_AGAIN; + } + + return c_tls->tr.state.recv_res; + + case MBEDTLS_ERR_SSL_WANT_WRITE: + mhd_assert (0 && + "The handshake must be fully completed earlier"); + break; + + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + mhd_assert (0 && "MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS must not be returned"); + break; + + case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: + /* MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS means that recv() should be called + again later. Pretend that data is already pending to not block on + waiting for the new incoming data. */ + c_tls->recv_data_in_buff = true; + return mhd_SOCKET_ERR_INTR; /* Do not clear 'recv-ready' flag */ + + case MBEDTLS_ERR_SSL_CLIENT_RECONNECT: + mhd_assert (0 && + "MBEDTLS_ERR_SSL_CLIENT_RECONNECT must not be " + "returned for non-DTLS"); + break; + + case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: + mhd_assert (0 && + "MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET must not be " + "returned on the server side"); + break; + + case MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA: +#ifdef MBEDTLS_SSL_EARLY_DATA + /* Could be replaced with early data support is implemented */ +#endif /* MBEDTLS_SSL_EARLY_DATA */ + mhd_assert (0 && + "MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA must not be returned"); + break; + + default: + break; /* Handle other values below */ + } + + /* Treat all other kinds of errors as hard errors */ +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_TLS; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool +mhd_tls_mbed_conn_has_data_in (struct mhd_TlsMbedConnData *restrict c_tls) +{ + return c_tls->recv_data_in_buff || + (0 != mbedtls_ssl_check_pending (&(c_tls->sess))); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_IN_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (5) enum mhd_SocketError +mhd_tls_mbed_conn_send (struct mhd_TlsMbedConnData *c_tls, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + bool push_data, + size_t *restrict sent) +{ + int res; + + mhd_assert (0 != buf_size); + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->shut_tls_wr_sent); + mhd_assert (! c_tls->dbg.is_failed); + + if (1) + { + const int buf_size_i = (int) buf_size; + if ((0 > buf_size_i) || + (buf_size != (size_t) buf_size_i)) + { + /* Called function return value limitation */ + buf_size = (size_t) (((unsigned int) ~((unsigned int) 0)) >> 1u); + push_data = false; + } + } + + mhd_tls_mbed_sckt_comm_prep_send (c_tls, + buf_size, + push_data); + + res = mbedtls_ssl_write (&(c_tls->sess), + (const unsigned char *) buf, + buf_size); + + if (0 < res) + { + mhd_ASSUME (buf_size >= (size_t) res); + *sent = (size_t) res; + + return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ + } + + switch (res) + { + case 0: + mhd_assert (0 && + "Zero must not be returned when sending non-zero size"); + break; + + case MBEDTLS_ERR_SSL_WANT_WRITE: + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.send_res)); + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c_tls->tr.state.recv_res)); + + if (! c_tls->tr.state.send_called) + return mhd_SOCKET_ERR_INTR; /* Do not clear 'recv-ready' flag */ + + if (mhd_SOCKET_ERR_NO_ERROR == c_tls->tr.state.send_res) + { + /* send() succeed for the first time and then called again */ + return c_tls->tr.sk->props.is_nonblck ? + mhd_SOCKET_ERR_INTR : mhd_SOCKET_ERR_AGAIN; + } + + return c_tls->tr.state.send_res; + + case MBEDTLS_ERR_SSL_WANT_READ: + mhd_assert (0 && + "The handshake must be fully completed earlier"); + break; + + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + mhd_assert (0 && "MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS must not be returned"); + break; + + case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: + /* MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS means that send() should be called + again later. Wait for 'send-ready' which should be already set or + will be set later, when OS pushed the data to the network. */ + return mhd_SOCKET_ERR_INTR; /* Do not clear 'recv-ready' flag */ + + case MBEDTLS_ERR_SSL_CLIENT_RECONNECT: + mhd_assert (0 && + "MBEDTLS_ERR_SSL_CLIENT_RECONNECT must not be " + "returned for non-DTLS"); + break; + + case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: + mhd_assert (0 && + "MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET must not be " + "returned on the server side"); + break; + + case MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA: +#ifdef MBEDTLS_SSL_EARLY_DATA + /* Could be replaced with early data support is implemented */ +#endif /* MBEDTLS_SSL_EARLY_DATA */ + mhd_assert (0 && + "MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA must not be returned"); + break; + + default: + break; /* Handle other values below */ + } + + /* Treat all other kinds of errors as hard errors */ +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_TLS; +} + + +/* ** TLS connection information ** */ + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (2) void +mhd_tls_mbed_conn_get_tls_sess ( + struct mhd_TlsMbedConnData *restrict c_tls, + union MHD_ConnInfoDynamicTlsSess *restrict tls_sess_out) +{ + tls_sess_out->v_mbedtls_session = &(c_tls->sess); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (2) bool +mhd_tls_mbed_conn_get_tls_ver (struct mhd_TlsMbedConnData *restrict c_tls, + struct mhd_StctTlsVersion *restrict tls_ver_out) +{ + mhd_assert (c_tls->dbg.is_tls_handshake_completed); + +#ifndef MBEDTLS_VERSION_NUMBER + if (1) + return false; /* Need MbedTLS version number to implement */ +#else /* MBEDTLS_VERSION_NUMBER */ + if (1) + { + uint_fast16_t tls_ver_num; +#if ((MBEDTLS_VERSION_NUMBER + 0) >= 0x03020000) + mbedtls_ssl_protocol_version mbedtls_tls_ver; + + mbedtls_tls_ver = mbedtls_ssl_get_version_number (&(c_tls->sess)); + + tls_ver_num = (uint_fast16_t) mbedtls_tls_ver; +#else /* MBEDTLS_VERSION_NUMBER < 0x03020000 */ + tls_ver_num = (uint_fast16_t) c_tls->sess.MBEDTLS_PRIVATE (major_ver); + tls_ver_num <<= 8u; + tls_ver_num |= (uint_fast16_t) c_tls->sess.MBEDTLS_PRIVATE (minor_ver); +#endif /* MBEDTLS_VERSION_NUMBER < 0x03020000 */ + /* Avoid MbedTLS helper macros and enum values in switch() as they are + unstable in MbedTLS. */ + switch (tls_ver_num) + { + case 0u: + return false; + + case 0x0301u: /* Not really supported by MbedTLS >=3.0 */ + tls_ver_out->tls_ver = MHD_TLS_VERSION_1_0; + break; + + case 0x0302u: /* Not really supported by MbedTLS >=3.0 */ + tls_ver_out->tls_ver = MHD_TLS_VERSION_1_1; + break; + + case 0x0303u: + tls_ver_out->tls_ver = MHD_TLS_VERSION_1_2; + break; + + case 0x0304u: + tls_ver_out->tls_ver = MHD_TLS_VERSION_1_3; + break; + + default: + tls_ver_out->tls_ver = MHD_TLS_VERSION_UNKNOWN; + break; + } + } +#endif /* MBEDTLS_VERSION_NUMBER */ + + return true; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ enum mhd_TlsAlpnProt +mhd_tls_mbed_conn_get_alpn_prot (struct mhd_TlsMbedConnData *restrict c_tls) +{ +#ifdef MBEDTLS_SSL_ALPN + const char *alpn_str; + + alpn_str = mbedtls_ssl_get_alpn_protocol (&(c_tls->sess)); + if (NULL == alpn_str) + return mhd_TLS_ALPN_PROT_NOT_SELECTED; + + return mhd_tls_alpn_decode_n (strlen (alpn_str), + (const unsigned char *) alpn_str); +#else /* ! MBEDTLS_SSL_ALPN */ + return mhd_TLS_ALPN_PROT_NOT_SELECTED; +#endif /* ! MBEDTLS_SSL_ALPN */ +} diff --git a/src/mhd2/tls_mbed_funcs.h b/src/mhd2/tls_mbed_funcs.h @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ +/* + This file is part of GNU libmicrohttpd. + Copyright (C) 2025 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. + + Alternatively, you can redistribute GNU libmicrohttpd and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version, together + with the eCos exception, as follows: + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile this + file and link it with other works to produce a work based on this + file, this file does not by itself cause the resulting work to be + covered by the GNU General Public License. However the source code + for this file must still be made available in accordance with + section (3) of the GNU General Public License v2. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + + You should have received copies of the GNU Lesser General Public + License and the GNU General Public License along with this library; + if not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file src/mhd2/tls_mbed_funcs.h + * @brief The declarations of MbedTLS wrapper functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_MBED_FUNCS_H +#define MHD_TLS_MBED_FUNCS_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_SUPPORT_MBEDTLS +#error This header can be used only if MbedTLS is enabled +#endif + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "mhd_status_code_int.h" + +#include "mhd_tls_enums.h" +#include "mhd_socket_error.h" + +/** + * The structure with daemon-specific MbedTLS data + */ +struct mhd_TlsMbedDaemonData; /* Forward declaration */ + +/** + * The structure with connection-specific MbedTLS data + */ +struct mhd_TlsMbedConnData; /* Forward declaration */ + +union MHD_ConnInfoDynamicTlsSess; /* Forward declaration */ + +struct mhd_StctTlsVersion; /* Forward declaration */ + +/* ** Global initialisation / de-initialisation ** */ + +/** + * Globally initialise MbedTLS backend + */ +MHD_INTERNAL void +mhd_tls_mbed_global_init (void); + +/* An alias for mhd_tls_mbed_global_init() */ +#define mhd_tls_mbed_global_init_once() mhd_tls_mbed_global_init () + +/* An alias for mhd_tls_mbed_global_init() */ +#define mhd_tls_mbed_global_re_init() mhd_tls_mbed_global_init () + +/** + * Globally de-initialise MbedTLS backend + */ +MHD_INTERNAL void +mhd_tls_mbed_global_deinit (void); + +/** + * Check whether MbedTLS backend was successfully initialised globally + * @return 'true' if backend has been successfully initialised, + * 'false' if backend cannot be used + */ +MHD_INTERNAL bool +mhd_tls_mbed_is_inited_fine (void) +MHD_FN_PURE_; + + +/* ** Daemon initialisation / de-initialisation ** */ + +struct MHD_Daemon; /* Forward declaration */ +struct DaemonOptions; /* Forward declaration */ + +/** + * Check whether MbedTLS backend supports edge-triggered sockets polling + * @param s the daemon settings + * @return 'true' if the backend supports edge-triggered sockets polling, + * 'false' if edge-triggered sockets polling cannot be used + */ +#define mhd_tls_mbed_is_edge_trigg_supported(s) (! 0) + + +/** + * Allocate and initialise daemon TLS parameters + * @param d the daemon handle + * @param et if 'true' then sockets polling uses edge-triggering + * @param s the daemon settings + * @param p_d_tls the pointer to variable to set the pointer to + * the daemon's TLS settings (allocated by this function) + * @return #MHD_SC_OK on success (p_d_tls set to the allocated settings), + * error code otherwise + */ +MHD_INTERNAL mhd_StatusCodeInt +mhd_tls_mbed_daemon_init (struct MHD_Daemon *restrict d, + bool sk_edge_trigg, + struct DaemonOptions *restrict s, + struct mhd_TlsMbedDaemonData **restrict p_d_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (4); + +/** + * 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_mbed_daemon_deinit (struct mhd_TlsMbedDaemonData *restrict d_tls) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); + + +/* ** Connection initialisation / de-initialisation ** */ + +struct mhd_ConnSocket; /* Forward declaration */ + +/** + * Get size size of the connection's TLS settings + */ +MHD_INTERNAL size_t +mhd_tls_mbed_conn_get_tls_size_v (void); + +/** + * Get size size of the connection's TLS settings + * @param d_tls the pointer to the daemon's TLS settings + */ +#define mhd_tls_mbed_conn_get_tls_size(d_tls) \ + mhd_tls_mbed_conn_get_tls_size_v () + +/** + * Initialise connection TLS settings + * @param d_tls the daemon TLS settings + * @param sk data about the socket for the connection + * @param[out] c_tls the pointer to the allocated space for + * the connection TLS settings + * @return 'true' on success, + * 'false' otherwise + */ +MHD_INTERNAL bool +mhd_tls_mbed_conn_init (const struct mhd_TlsMbedDaemonData *restrict d_tls, + struct mhd_ConnSocket *sk, + struct mhd_TlsMbedConnData *restrict c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (3); + +/** + * De-initialise connection TLS settings. + * The provided pointer is not freed/deallocated. + * @param c_tls the initialised connection TLS settings + */ +MHD_INTERNAL void +mhd_tls_mbed_conn_deinit (struct mhd_TlsMbedConnData *restrict c_tls) +MHD_FN_PAR_NONNULL_ALL_; + + +/* ** TLS connection establishing ** */ + +/** + * Perform TLS handshake + * @param c_tls the connection TLS handle + * @return #mhd_TLS_PROCED_SUCCESS if completed successfully + * or other enum mhd_TlsProcedureResult values + */ +MHD_INTERNAL enum mhd_TlsProcedureResult +mhd_tls_mbed_conn_handshake (struct mhd_TlsMbedConnData *c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_; + +/** + * Perform shutdown of TLS layer + * @param c_tls the connection TLS handle + * @return #mhd_TLS_PROCED_SUCCESS if completed successfully + * or other enum mhd_TlsProcedureResult values + */ +MHD_INTERNAL enum mhd_TlsProcedureResult +mhd_tls_mbed_conn_shutdown (struct mhd_TlsMbedConnData *c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_; + + +/* ** Data sending and receiving over TLS connection ** */ + +/** + * Receive the data from the remote side over TLS connection + * + * @param c_tls the connection TLS handle + * @param buf_size the size of the @a buf buffer + * @param[out] buf the buffer to fill with the received data + * @param[out] received the pointer to variable to get the size of the data + * actually put to the @a buffer + * @return mhd_SOCKET_ERR_NO_ERROR if receive succeed (the @a received gets + * the received size) or socket error + */ +MHD_INTERNAL enum mhd_SocketError +mhd_tls_mbed_conn_recv (struct mhd_TlsMbedConnData *c_tls, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict received) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4); + +/** + * Check whether any incoming data is pending in the TLS buffers + * + * @param c_tls the connection TLS handle + * @return 'true' if any incoming remote data is already pending (the TLS recv() + * call can be performed), + * 'false' otherwise + */ +MHD_INTERNAL bool +mhd_tls_mbed_conn_has_data_in (struct mhd_TlsMbedConnData *restrict c_tls) +MHD_FN_PAR_NONNULL_ALL_; + +/** + * Send data to the remote side over TLS connection + * + * @param c_tls the connection TLS handle + * @param buf_size the size of the @a buf (in bytes) + * @param buf content of the buffer to send + * @param push_data set to 'false' if it is know that the data in the @a buf + * is incomplete (message or chunk), + * set to 'true' if the data is complete or the final part + * @param[out] sent the pointer to get amount of actually sent bytes + * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets + * the sent size) or socket error + */ +MHD_INTERNAL enum mhd_SocketError +mhd_tls_mbed_conn_send (struct mhd_TlsMbedConnData *c_tls, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + bool push_data, + size_t *restrict sent) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_OUT_ (5); + + +/* ** TLS connection information ** */ + +/** + * Check whether the connection is using "custom transport" functions. + * "Custom transport" means that data sending and receiving over system + * sockets is performed by MHD callbacks. + * When "custom transport" is used, backend TLS send/recv functions are: + * * perform additional syscalls (socket options) for data pushing/buffering, + * * change socket states like corked, NO_DELAY, both by syscalls and in + * MHD socket metadata, + * * set disconnect error from the system reported socket error. + * + * @param c_tls the connection TLS handle + * @return boolean 'true' if custom transport is used, + * boolean 'false' otherwise + */ +#define mhd_tls_mbed_conn_has_cstm_tr(c_tls) (! 0) + +/** + * Get the TLS session used in connection + * @param c_tls the connection TLS handle + * @param tls_sess_out the pointer to variable to be set to the TLS session + * handle + */ +MHD_INTERNAL void +mhd_tls_mbed_conn_get_tls_sess ( + struct mhd_TlsMbedConnData *restrict c_tls, + union MHD_ConnInfoDynamicTlsSess *restrict tls_sess_out) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2); + +/** + * Get the TLS version used in connection + * @param c_tls the connection TLS handle + * @param tls_ver_out the pointer to variable to be set to the TLS version + * @return 'true' is TLS version information set successfully, + * 'false' if TLS version information cannot be obtained or mapped + */ +MHD_INTERNAL bool +mhd_tls_mbed_conn_get_tls_ver (struct mhd_TlsMbedConnData *restrict c_tls, + struct mhd_StctTlsVersion *restrict tls_ver_out) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2); + +/** + * Get a protocol selected by ALPN + * @param c_tls the connection TLS handle + * @return the selected protocol code + */ +MHD_INTERNAL enum mhd_TlsAlpnProt +mhd_tls_mbed_conn_get_alpn_prot (struct mhd_TlsMbedConnData *restrict c_tls) +MHD_FN_PAR_NONNULL_ALL_; + + +#endif /* ! MHD_TLS_MBED_FUNCS_H */ diff --git a/src/mhd2/tls_mbed_tls_lib.h b/src/mhd2/tls_mbed_tls_lib.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ +/* + This file is part of GNU libmicrohttpd. + Copyright (C) 2025 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. + + Alternatively, you can redistribute GNU libmicrohttpd and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version, together + with the eCos exception, as follows: + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile this + file and link it with other works to produce a work based on this + file, this file does not by itself cause the resulting work to be + covered by the GNU General Public License. However the source code + for this file must still be made available in accordance with + section (3) of the GNU General Public License v2. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + + You should have received copies of the GNU Lesser General Public + License and the GNU General Public License along with this library; + if not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file src/mhd2/tls_mbed_tls_lib.h + * @brief The wrapper for MbedTLS headers + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_MBED_TLS_LIB_H +#define MHD_TLS_MBED_TLS_LIB_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_SUPPORT_MBEDTLS +#error This header can be used only if MbedTLS is enabled +#endif + +#include <mbedtls/build_info.h> +#include <mbedtls/platform.h> +#ifdef MBEDTLS_VERSION_C +# include <mbedtls/version.h> +#endif /* MBEDTLS_VERSION_C */ + +#if ((MBEDTLS_VERSION_MAJOR + 0) < 3) +#error MbedTLS version 3.0 or later is required +#endif +#if ((MBEDTLS_VERSION_NUMBER + 0) < 0x03000000) +#error MbedTLS version 3.0 or later is required +#endif + +/* #mhd_TLS_MBED_USE_PSA_FREE is MHD build-time user-definable macro */ +#if defined(MBEDTLS_PSA_CRYPTO_CLIENT) +# define mhd_TLS_MBED_USE_PSA 1 +# ifdef MHD_TLS_MBED_USE_PSA_FREE +/* The application must not use MbedTLS directly */ +# define mhd_TLS_MBED_USE_PSA_FREE 1 +# endif +#endif + +#ifdef mhd_TLS_MBED_USE_PSA +# include <psa/crypto.h> +#endif /* mhd_TLS_MBED_USE_PSA */ + +#ifdef MBEDTLS_MD_C +/* Actually MD must be available if TLS is enabled */ +# include <mbedtls/md.h> +#endif + +#if ((MBEDTLS_VERSION_NUMBER + 0) >= 0x03050000) +# define mhd_TLS_MBED_HAS_SHA3_IDS 1 +#endif + +#ifdef MBEDTLS_ENTROPY_C +# include <mbedtls/entropy.h> +#endif /* MBEDTLS_ENTROPY_C */ + +#ifdef mhd_TLS_MBED_USE_PSA +# include <mbedtls/psa_util.h> +# define mhd_TLS_MBED_HAS_RNG_PSA 1 +#elif defined(MHD_TLS_MBED_PREF_RNG_PSA) +# undef MHD_TLS_MBED_PREF_RNG_PSA +#endif + +#ifdef MBEDTLS_HMAC_DRBG_C +# include <mbedtls/hmac_drbg.h> +# define mhd_TLS_MBED_HAS_RNG_HMAC 1 +#elif defined(MHD_TLS_MBED_PREF_RNG_HMAC) +# undef MHD_TLS_MBED_PREF_RNG_HMAC +#endif /* MBEDTLS_HMAC_DRBG_C */ + +#ifdef MBEDTLS_CTR_DRBG_C +# include <mbedtls/ctr_drbg.h> +# define mhd_TLS_MBED_HAS_RNG_CTR 1 +#elif defined(MHD_TLS_MBED_PREF_RNG_CTR) +# undef MHD_TLS_MBED_PREF_RNG_CTR +#endif /* MBEDTLS_CTR_DRBG_C */ + +#if ! defined(MHD_TLS_MBED_PREF_RNG_PSA) && \ + ! defined(MHD_TLS_MBED_PREF_RNG_HMAC) && \ + ! defined(MHD_TLS_MBED_PREF_RNG_CTR) +# if defined(mhd_TLS_MBED_HAS_RNG_PSA) +# define MHD_TLS_MBED_PREF_RNG_PSA 1 +# elif defined(mhd_TLS_MBED_HAS_RNG_HMAC) && \ + defined(MBEDTLS_MD_C) +# define MHD_TLS_MBED_PREF_RNG_HMAC 1 +# define mhd_TLS_MBED_RNG_PREF_NEEDS_ENTROPY 1 +# elif defined(mhd_TLS_MBED_HAS_RNG_CTR) +# define MHD_TLS_MBED_PREF_RNG_CTR 1 +# define mhd_TLS_MBED_RNG_PREF_NEEDS_ENTROPY 1 +# endif +#endif + +#if defined(mhd_TLS_MBED_RNG_PREF_NEEDS_ENTROPY) && \ + defined(MBEDTLS_ENTROPY_C) +# define mhd_TLS_MBED_USE_LIB_ENTROPY 1 +#endif + +#if ((MBEDTLS_VERSION_NUMBER + 0) < 0x04000000) +/** + * TLS initialisation requires random generator + */ +# define mhd_TLS_MBED_INIT_TLS_REQ_RNG 1 +#endif + +#include <mbedtls/x509_crt.h> + +#if ! defined(MBEDTLS_X509_CRT_PARSE_C) +#error X.509 certificate parsing functions are required +#endif /* ! MBEDTLS_X509_CRT_PARSE_C */ + +#include <mbedtls/pk.h> + +#if ! defined(MBEDTLS_PK_PARSE_C) +#error Public key parser is required +#endif /* ! MBEDTLS_PK_PARSE_C */ + +#if ! defined(MBEDTLS_PEM_PARSE_C) +#error PEM parser is required +#endif /* ! MBEDTLS_PEM_PARSE_C */ + +/* Required header, checked in 'configure' */ +#include <mbedtls/ssl.h> + +/* #MHD_TLS_MBED_SKIP_PLATFORM_SETUP and #MHD_TLS_MBED_USE_PLATFORM_TEARDOWN + are MHD build-time user-definable macros */ +/* User may set #MHD_TLS_MBED_SKIP_PLATFORM_SETUP and/or + #MHD_TLS_MBED_USE_PLATFORM_TEARDOWN when building MHD to control + automatic platform setup / teardown */ +#if defined(MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT) && \ + ! defined(MHD_TLS_MBED_SKIP_PLATFORM_SETUP) +# define mhd_TLS_MBED_HAS_PLATFORM_SETUP 1 +# ifdef MHD_TLS_MBED_USE_PLATFORM_TEARDOWN +/* The application must not use MbedTLS directly */ +# define mhd_TLS_MBED_USE_PLATFORM_TEARDOWN 1 +# endif +#endif + +#ifdef MBEDTLS_NET_C +/* Actually, the header should be available unconditionally, but could be + accidently excluded if module is disabled. */ +# include <mbedtls/net_sockets.h> +#endif + +#ifndef MBEDTLS_ERR_NET_RECV_FAILED +/* Unknown error when receiving the data */ +# define MBEDTLS_ERR_NET_RECV_FAILED (-0x004C) +#endif +#ifndef MBEDTLS_ERR_NET_SEND_FAILED +/* Unknown error when sending the data */ +# define MBEDTLS_ERR_NET_SEND_FAILED (-0x004E) +#endif +#ifndef MBEDTLS_ERR_NET_CONN_RESET +/* The network connection is broken */ +# define MBEDTLS_ERR_NET_CONN_RESET (-0x0050) +#endif + +#ifdef MBEDTLS_DEBUG_C +# include <mbedtls/debug.h> +#endif + +#endif /* ! MHD_TLS_MBED_TLS_LIB_H */ diff --git a/src/mhd2/tls_multi_conn_data.h b/src/mhd2/tls_multi_conn_data.h @@ -59,6 +59,9 @@ struct mhd_TlsGnuConnData; /* forward declaration */ #ifdef MHD_SUPPORT_OPENSSL struct mhd_TlsOpenConnData; /* forward declaration */ #endif +#ifdef MHD_SUPPORT_MBEDTLS +struct mhd_TlsMbedConnData; /* forward declaration */ +#endif /** * The pointer to the underlying TLS backend connection data @@ -77,6 +80,12 @@ struct mhd_TlsMultiConnRoutePtr */ struct mhd_TlsOpenConnData *openssl; #endif +#ifdef MHD_SUPPORT_MBEDTLS + /** + * Pointer to OpenSSL connection-specific data + */ + struct mhd_TlsMbedConnData *mbedtls; +#endif }; /** diff --git a/src/mhd2/tls_multi_daemon_data.h b/src/mhd2/tls_multi_daemon_data.h @@ -59,6 +59,9 @@ struct mhd_TlsGnuDaemonData; /* forward declaration */ #ifdef MHD_SUPPORT_OPENSSL struct mhd_TlsOpenDaemonData; /* forward declaration */ #endif +#ifdef MHD_SUPPORT_MBEDTLS +struct mhd_TlsMbedDaemonData; /* forward declaration */ +#endif /** * The pointer to the underlying TLS backend daemon data @@ -77,6 +80,12 @@ struct mhd_TlsMultiDaemonRoutePtr */ struct mhd_TlsOpenDaemonData *openssl; #endif +#ifdef MHD_SUPPORT_MBEDTLS + /** + * Pointer to OpenSSL daemon-specific data + */ + struct mhd_TlsMbedDaemonData *mbedtls; +#endif }; /** diff --git a/src/mhd2/tls_multi_funcs.c b/src/mhd2/tls_multi_funcs.c @@ -67,6 +67,9 @@ #if defined(MHD_SUPPORT_OPENSSL) # include "tls_open_funcs.h" #endif +#if defined(MHD_SUPPORT_MBEDTLS) +# include "tls_mbed_funcs.h" +#endif #include "daemon_options.h" #include "daemon_logger.h" @@ -101,6 +104,9 @@ mhd_tls_multi_global_init_once (void) #if defined(MHD_SUPPORT_OPENSSL) mhd_tls_open_global_init_once (); #endif +#if defined(MHD_SUPPORT_MBEDTLS) + mhd_tls_mbed_global_init_once (); +#endif } @@ -114,6 +120,9 @@ mhd_tls_multi_global_deinit (void) #if defined(MHD_SUPPORT_GNUTLS) mhd_tls_gnu_global_deinit (); #endif +#if defined(MHD_SUPPORT_MBEDTLS) + mhd_tls_mbed_global_deinit (); +#endif } @@ -126,6 +135,9 @@ mhd_tls_multi_global_re_init (void) #if defined(MHD_SUPPORT_OPENSSL) mhd_tls_open_global_re_init (); #endif +#if defined(MHD_SUPPORT_MBEDTLS) + mhd_tls_mbed_global_re_init (); +#endif } @@ -150,6 +162,11 @@ mhd_tls_multi_is_edge_trigg_supported (struct DaemonOptions *s) && mhd_tls_open_is_inited_fine ()) return true; #endif +#ifdef MHD_SUPPORT_MBEDTLS + if (mhd_tls_mbed_is_edge_trigg_supported (s) + && mhd_tls_mbed_is_inited_fine ()) + return true; +#endif return false; case MHD_TLS_BACKEND_GNUTLS: #ifdef MHD_SUPPORT_GNUTLS @@ -165,6 +182,13 @@ mhd_tls_multi_is_edge_trigg_supported (struct DaemonOptions *s) return mhd_tls_open_is_edge_trigg_supported (s); #endif break; + case MHD_TLS_BACKEND_MBEDTLS: +#ifdef MHD_SUPPORT_MBEDTLS + /* Ignore "backend inited" status here, + it will be checked on daemon TLS init */ + return mhd_tls_mbed_is_edge_trigg_supported (s); +#endif + break; default: mhd_UNREACHABLE (); break; @@ -237,6 +261,25 @@ tls_daemon_init_try (enum mhd_TlsMultiRoute route, "the daemon, error code: %u", (unsigned) res); return res; #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + if (! mhd_tls_mbed_is_inited_fine ()) + return MHD_SC_TLS_BACKEND_UNAVAILABLE; + res = mhd_tls_mbed_daemon_init (d, + sk_edge_trigg, + s, + &(d_tls->data.mbedtls)); + if (MHD_SC_OK == res) + { + mhd_M_DEBUG_PRINT ("MbedTLS backend initialised successfully " \ + "for the daemon"); + d_tls->choice = route; + return MHD_SC_OK; + } + mhd_M_DEBUG_PRINT1 ("Failed to initialise MbedTLS backend for " \ + "the daemon, error code: %u", (unsigned) res); + return res; +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: break; @@ -276,6 +319,9 @@ mhd_tls_multi_daemon_init (struct MHD_Daemon *restrict d, #ifdef MHD_SUPPORT_OPENSSL mhd_TLS_MULTI_ROUTE_OPEN, #endif +#ifdef MHD_SUPPORT_MBEDTLS + mhd_TLS_MULTI_ROUTE_MBED, +#endif mhd_TLS_MULTI_ROUTE_NONE /* Not used */ }; /* Try backends one-by-one */ @@ -311,6 +357,16 @@ mhd_tls_multi_daemon_init (struct MHD_Daemon *restrict d, d_tls); #endif /* MHD_SUPPORT_OPENSSL */ break; +#ifdef MHD_SUPPORT_MBEDTLS + case MHD_TLS_BACKEND_MBEDTLS: + mhd_assert (mhd_tls_mbed_is_inited_fine ()); /* Must be checked earlier */ + res = tls_daemon_init_try (mhd_TLS_MULTI_ROUTE_MBED, + d, + sk_edge_trigg, + s, + d_tls); +#endif /* MHD_SUPPORT_MBEDTLS */ + break; #ifndef MHD_SUPPORT_GNUTLS case MHD_TLS_BACKEND_GNUTLS: @@ -318,6 +374,9 @@ mhd_tls_multi_daemon_init (struct MHD_Daemon *restrict d, #ifndef MHD_SUPPORT_OPENSSL case MHD_TLS_BACKEND_OPENSSL: #endif /* ! MHD_SUPPORT_OPENSSL */ +#ifndef MHD_SUPPORT_MBEDTLS + case MHD_TLS_BACKEND_MBEDTLS: +#endif /* ! MHD_SUPPORT_MBEDTLS */ case MHD_TLS_BACKEND_NONE: default: mhd_UNREACHABLE (); @@ -351,6 +410,11 @@ mhd_tls_multi_daemon_deinit (struct mhd_TlsMultiDaemonData *restrict d_tls) mhd_tls_open_daemon_deinit (d_tls->data.openssl); break; #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + mhd_tls_mbed_daemon_deinit (d_tls->data.mbedtls); + break; +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -380,6 +444,11 @@ mhd_tls_multi_conn_get_tls_size (struct mhd_TlsMultiDaemonData *restrict d_tls) data_size += mhd_tls_open_conn_get_tls_size (d_tls->data.openssl); break; #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + data_size += mhd_tls_mbed_conn_get_tls_size (d_tls->data.mbedtls); + break; +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -392,7 +461,7 @@ mhd_tls_multi_conn_get_tls_size (struct mhd_TlsMultiDaemonData *restrict d_tls) MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (3) bool mhd_tls_multi_conn_init (const struct mhd_TlsMultiDaemonData *restrict d_tls, - const struct mhd_ConnSocket *sk, + struct mhd_ConnSocket *sk, struct mhd_TlsMultiConnData *restrict c_tls) { c_tls->choice = d_tls->choice; @@ -414,6 +483,14 @@ mhd_tls_multi_conn_init (const struct mhd_TlsMultiDaemonData *restrict d_tls, sk, c_tls->data.openssl); #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + /* Assume the same alignment requirements for both structures */ + c_tls->data.mbedtls = (struct mhd_TlsMbedConnData *) (c_tls + 1); + return mhd_tls_mbed_conn_init (d_tls->data.mbedtls, + sk, + c_tls->data.mbedtls); +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -444,6 +521,11 @@ mhd_tls_multi_conn_deinit (struct mhd_TlsMultiConnData *restrict c_tls) mhd_tls_open_conn_deinit (c_tls->data.openssl); break; #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + mhd_tls_mbed_conn_deinit (c_tls->data.mbedtls); + break; +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -467,6 +549,10 @@ mhd_tls_multi_conn_handshake (struct mhd_TlsMultiConnData *restrict c_tls) case mhd_TLS_MULTI_ROUTE_OPEN: return mhd_tls_open_conn_handshake (c_tls->data.openssl); #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + return mhd_tls_mbed_conn_handshake (c_tls->data.mbedtls); +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -490,6 +576,10 @@ mhd_tls_multi_conn_shutdown (struct mhd_TlsMultiConnData *restrict c_tls) case mhd_TLS_MULTI_ROUTE_OPEN: return mhd_tls_open_conn_shutdown (c_tls->data.openssl); #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + return mhd_tls_mbed_conn_shutdown (c_tls->data.mbedtls); +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -525,6 +615,13 @@ mhd_tls_multi_conn_recv (struct mhd_TlsMultiConnData *restrict c_tls, buf, received); #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + return mhd_tls_mbed_conn_recv (c_tls->data.mbedtls, + buf_size, + buf, + received); +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -548,6 +645,10 @@ mhd_tls_multi_conn_has_cstm_tr (struct mhd_TlsMultiConnData *restrict c_tls) case mhd_TLS_MULTI_ROUTE_OPEN: return mhd_tls_open_conn_has_cstm_tr (c_tls->data.openssl); #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + return mhd_tls_mbed_conn_has_cstm_tr (c_tls->data.mbedtls); +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -570,6 +671,10 @@ mhd_tls_multi_conn_has_data_in (struct mhd_TlsMultiConnData *restrict c_tls) case mhd_TLS_MULTI_ROUTE_OPEN: return mhd_tls_open_conn_has_data_in (c_tls->data.openssl); #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + return mhd_tls_mbed_conn_has_data_in (c_tls->data.mbedtls); +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -607,6 +712,14 @@ mhd_tls_multi_conn_send (struct mhd_TlsMultiConnData *restrict c_tls, push_data, sent); #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + return mhd_tls_mbed_conn_send (c_tls->data.mbedtls, + buf_size, + buf, + push_data, + sent); +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -638,6 +751,12 @@ mhd_tls_multi_conn_get_tls_sess ( tls_sess_out); break; #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + mhd_tls_mbed_conn_get_tls_sess (c_tls->data.mbedtls, + tls_sess_out); + break; +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -663,6 +782,11 @@ mhd_tls_multi_conn_get_tls_ver (struct mhd_TlsMultiConnData *restrict c_tls, return mhd_tls_open_conn_get_tls_ver (c_tls->data.openssl, tls_ver_out); #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + return mhd_tls_mbed_conn_get_tls_ver (c_tls->data.mbedtls, + tls_ver_out); +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); @@ -685,6 +809,10 @@ mhd_tls_multi_conn_get_alpn_prot (struct mhd_TlsMultiConnData *restrict c_tls) case mhd_TLS_MULTI_ROUTE_OPEN: return mhd_tls_open_conn_get_alpn_prot (c_tls->data.openssl); #endif +#ifdef MHD_SUPPORT_MBEDTLS + case mhd_TLS_MULTI_ROUTE_MBED: + return mhd_tls_mbed_conn_get_alpn_prot (c_tls->data.mbedtls); +#endif case mhd_TLS_MULTI_ROUTE_NONE: default: mhd_UNREACHABLE (); diff --git a/src/mhd2/tls_multi_funcs.h b/src/mhd2/tls_multi_funcs.h @@ -163,7 +163,7 @@ mhd_tls_multi_conn_get_tls_size (struct mhd_TlsMultiDaemonData *restrict d_tls); */ MHD_INTERNAL bool mhd_tls_multi_conn_init (const struct mhd_TlsMultiDaemonData *restrict d_tls, - const struct mhd_ConnSocket *sk, + struct mhd_ConnSocket *sk, struct mhd_TlsMultiConnData *restrict c_tls) MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (3); diff --git a/src/mhd2/tls_multi_tls_lib.h b/src/mhd2/tls_multi_tls_lib.h @@ -77,6 +77,13 @@ enum mhd_TlsMultiRoute */ mhd_TLS_MULTI_ROUTE_OPEN #endif +#ifdef MHD_SUPPORT_MBEDTLS + , + /** + * Use OpenSSL backend + */ + mhd_TLS_MULTI_ROUTE_MBED +#endif }; #endif /* ! MHD_TLS_MULTI_TLS_LIB_H */