diff --git a/configure.ac b/configure.ac index 17320db6..b94ab155 100644 --- a/configure.ac +++ b/configure.ac @@ -4867,6 +4867,13 @@ AM_CONDITIONAL([HAVE_POST_PARSER], [test "x$enable_postparser" != "xno"]) AC_MSG_RESULT([[$enable_postparser]]) have_gnutls=no +have_gnutls_pkgcfg=no +have_openssl=no +have_openssl_pkgcfg=no +AS_UNSET([MHD_TLS_LIB_CPPFLAGS]) +AS_UNSET([MHD_TLS_LIB_LDFLAGS]) +AS_UNSET([MHD_TLS_LIBDEPS]) +MSG_TLS_BACKENDS="none" # optional: HTTPS support. Enabled if GnuTLS is available. AC_ARG_ENABLE([https], @@ -4885,11 +4892,12 @@ AS_IF([test "x$enable_https" != "xno"], [ AS_CASE([$with_gnutls], [no],[ - AC_MSG_RESULT([[GnuTLS disabled]]) + have_gnutls="no" AS_UNSET([GNUTLS_CPPFLAGS]) AS_UNSET([GNUTLS_CFLAGS]) AS_UNSET([GNUTLS_LDFLAGS]) AS_UNSET([GNUTLS_LIBS]) + AC_MSG_RESULT([[GnuTLS disabled]]) ], [yes],[ AC_MSG_RESULT([[automatically, forced]]) @@ -4916,7 +4924,7 @@ AS_IF([test "x$enable_https" != "xno"], GNUTLS_LDFLAGS="-L$with_gnutls/lib" GNUTLS_LIBS="-lgnutls" ], - [AC_MSG_ERROR([can't find usable libgnutls at specified prefix $with_gnutls])], + [AC_MSG_ERROR([cannot find usable libgnutls at specified prefix $with_gnutls])], [-lgnutls] ) CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" @@ -4931,7 +4939,6 @@ AS_IF([test "x$enable_https" != "xno"], [ PKG_CHECK_MODULES([GNUTLS], [[gnutls]], [ - have_gnutls_pkgcfg='yes' CPPFLAGS="${CPPFLAGS_ac} $GNUTLS_CFLAGS ${user_CPPFLAGS}" # A simple check for the working header and the library MHD_CHECK_FUNC([gnutls_check_version], @@ -4944,7 +4951,8 @@ AS_IF([test "x$enable_https" != "xno"], return 3; ], [ - have_gnutls=yes + have_gnutls="yes" + have_gnutls_pkgcfg="yes" # GNUTLS_CFLAGS is actually CPPFLAGS GNUTLS_CPPFLAGS="$GNUTLS_CFLAGS" AS_UNSET([GNUTLS_CFLAGS]) @@ -4953,9 +4961,9 @@ AS_IF([test "x$enable_https" != "xno"], ], [ AS_VAR_IF([with_gnutls],["yes"], - [AC_MSG_ERROR([can't find usable libgnutls at specified prefix $with_gnutls])] + [AC_MSG_ERROR([cannot find usable libgnutls])] ) - AC_MSG_WARN([pkg-config reports that GnuTLS is present, but GnuTLS can't be used]) + AC_MSG_WARN([pkg-config reports that GnuTLS is present, but GnuTLS cannot be used]) AS_UNSET([GNUTLS_CPPFLAGS]) AS_UNSET([GNUTLS_CFLAGS]) AS_UNSET([GNUTLS_LDFLAGS]) @@ -4966,7 +4974,7 @@ AS_IF([test "x$enable_https" != "xno"], ], [ # check for GnuTLS at default paths - have_gnutls_pkgcfg='no' + have_gnutls_pkgcfg="no" # A simple check for the working header and the library MHD_CHECK_FUNC([gnutls_check_version], [[ @@ -4986,7 +4994,7 @@ AS_IF([test "x$enable_https" != "xno"], ], [ AS_VAR_IF([with_gnutls],["yes"], - [AC_MSG_ERROR([can't find usable libgnutls])] + [AC_MSG_ERROR([cannot find usable libgnutls])] ) AS_UNSET([GNUTLS_CPPFLAGS]) AS_UNSET([GNUTLS_CFLAGS]) @@ -5003,10 +5011,7 @@ AS_IF([test "x$enable_https" != "xno"], [ AC_CACHE_CHECK([[whether GnuTLS is modern enough]], [mhd_cv_gnutls_ver_ok], [ - SAVE_LIBS="$LIBS" CPPFLAGS="${CPPFLAGS_ac} ${GNUTLS_CPPFLAGS} ${user_CPPFLAGS}" - LDFLAGS="${LDFLAGS_ac} ${GNUTLS_LDFLAGS} ${user_LDFLAGS}" - LIBS="$GNUTLS_LIBS $LIBS" AC_COMPILE_IFELSE( [ AC_LANG_PROGRAM( @@ -5022,20 +5027,27 @@ choke me now ]] ) ], - [[mhd_cv_gnutls_ver_ok='yes']], [[mhd_cv_gnutls_ver_ok='no']] + [[mhd_cv_gnutls_ver_ok="yes"]], [[mhd_cv_gnutls_ver_ok="no"]] ) - LIBS="$SAVE_LIBS" - LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}" CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" ] ) - AS_VAR_IF([mhd_cv_gnutls_ver_ok],["yes"],[:],[have_gnutls="no"]) + AS_VAR_IF([mhd_cv_gnutls_ver_ok],["yes"],[:], + [ + have_gnutls="no" + AS_VAR_IF([with_gnutls],["yes"], + [AC_MSG_ERROR([cannot find usable libgnutls])] + ) + ] + ) ] ) AS_VAR_IF([have_gnutls],["yes"], [:], [ + have_gnutls="no" + have_gnutls_pkgcfg="no" AS_UNSET([GNUTLS_CPPFLAGS]) AS_UNSET([GNUTLS_CFLAGS]) AS_UNSET([GNUTLS_LDFLAGS]) @@ -5044,23 +5056,222 @@ choke me now ) - AS_VAR_IF([have_gnutls],["yes"], + AC_MSG_CHECKING([[how to find OpenSSL library]]) + AC_ARG_WITH([[openssl]], + [ + AS_HELP_STRING([[--with-openssl[=PRFX]]], + [use OpenSSL for HTTPS support, optional PRFX overrides pkg-config data for OpenSSL headers (PRFX/include) and libs (PRFX/lib)]) + ], + [ + AS_CASE([$with_openssl], + [no],[ + have_openssl="no" + AC_MSG_RESULT([[OpenSSL disabled]]) + AS_UNSET([OPENSSL_CPPFLAGS]) + AS_UNSET([OPENSSL_CFLAGS]) + AS_UNSET([OPENSSL_LDFLAGS]) + AS_UNSET([OPENSSL_LIBS]) + ], + [yes],[ + AC_MSG_RESULT([[automatically, forced]]) + ], + [ + AC_MSG_RESULT([[-I$with_openssl/include -L$with_openssl/lib -lssl -lcrypto]]) + LDFLAGS="${LDFLAGS_ac} -L$with_openssl/lib ${user_LDFLAGS}" + CPPFLAGS="${CPPFLAGS_ac} -I$with_openssl/include ${user_CPPFLAGS}" + have_openssl_pkgcfg="no" + # A simple check for the working header and the library + MHD_CHECK_FUNC([SSL_CTX_new], + [[ +#include + ]], + [[ + SSL_CTX_free(SSL_CTX_new(TLS_server_method())); + ]], + [ + have_openssl="yes" + OPENSSL_CPPFLAGS="-I$with_openssl/include" + AS_UNSET([OPENSSL_CFLAGS]) + OPENSSL_LDFLAGS="-L$with_openssl/lib" + OPENSSL_LIBS="-lssl -lcrypto" + ], + [AC_MSG_ERROR([cannot find usable OpenSSL library at specified prefix $with_openssl])], + [-lssl -lcrypto] + ) + CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" + CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" + LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}" + ]) + ], + [AC_MSG_RESULT([[automatically]])] + ) + + AS_IF([test "x$with_openssl" != "xno" && test "x$have_openssl" != "xyes"], + [ + PKG_CHECK_MODULES([OPENSSL], [[openssl >= 3.0]], + [ + CPPFLAGS="${CPPFLAGS_ac} $OPENSSL_CFLAGS ${user_CPPFLAGS}" + # A simple check for the working header and the library + MHD_CHECK_FUNC([SSL_CTX_new], + [[ +#include + ]], + [[ + SSL_CTX_free(SSL_CTX_new(TLS_server_method())); + ]], + [ + have_openssl=yes + have_openssl_pkgcfg='yes' + # OPENSSL_CFLAGS is actually CPPFLAGS + OPENSSL_CPPFLAGS="$OPENSSL_CFLAGS" + AS_UNSET([OPENSSL_CFLAGS]) + # OPENSSL_LIBS is a combination of LDFLAGS and LIBS + AS_UNSET([OPENSSL_LDFLAGS]) + ], + [ + AS_VAR_IF([with_openssl],["yes"], + [AC_MSG_ERROR([cannot find usable OpenSSL library])] + ) + AC_MSG_WARN([pkg-config reports that OpenSSL is present, but OpenSSL cannot be used]) + AS_UNSET([OPENSSL_CPPFLAGS]) + AS_UNSET([OPENSSL_CFLAGS]) + AS_UNSET([OPENSSL_LDFLAGS]) + AS_UNSET([OPENSSL_LIBS]) + ], + [$OPENSSL_LIBS] + ) + ], + [ + # check for OpenSSL at default paths + have_openssl_pkgcfg="no" + # A simple check for the working header and the library + MHD_CHECK_FUNC([SSL_CTX_new], + [[ +#include + ]], + [[ + SSL_CTX_free(SSL_CTX_new(TLS_server_method())); + ]], + [ + have_openssl="yes" + AS_UNSET([OPENSSL_CPPFLAGS]) + AS_UNSET([OPENSSL_CFLAGS]) + AS_UNSET([OPENSSL_LDFLAGS]) + OPENSSL_LIBS="-lssl -lcrypto" + ], + [ + AS_VAR_IF([with_openssl],["yes"], + [AC_MSG_ERROR([cannot find usable OpenSSL library])] + ) + AS_UNSET([OPENSSL_CPPFLAGS]) + AS_UNSET([OPENSSL_CFLAGS]) + AS_UNSET([OPENSSL_LDFLAGS]) + AS_UNSET([OPENSSL_LIBS]) + ], + [-lssl -lcrypto] + ) + ] + ) + ] + ) + AS_VAR_IF([have_openssl],["yes"], + [ + AC_CACHE_CHECK([[whether OpenSSL is modern enough]], [mhd_cv_openssl_ver_ok], + [ + CPPFLAGS="${CPPFLAGS_ac} ${OPENSSL_CPPFLAGS} ${user_CPPFLAGS}" + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM( + [[ +#include + ]], + [[ +#ifndef OPENSSL_VERSION_PREREQ +#error OPENSSL_VERSION_PREREQ is not defined. OpenSSL is too old. +choke me now +#endif +#if !OPENSSL_VERSION_PREREQ(3,0) +#error OpenSSL version is too old and not suitable. +choke me now +#endif + ]] + ) + ], + [[mhd_cv_openssl_ver_ok="yes"]], [[mhd_cv_openssl_ver_ok="no"]] + ) + CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" + ] + ) + AS_VAR_IF([mhd_cv_openssl_ver_ok],["yes"],[:], + [ + have_openssl="no" + AS_VAR_IF([with_openssl],["yes"], + [AC_MSG_ERROR([cannot find usable OpenSSL library])] + ) + ] + ) + ] + ) + + AS_VAR_IF([have_openssl],["yes"], + [:], + [ + have_openssl="no" + have_openssl_pkgcfg="no" + AS_UNSET([OPENSSL_CPPFLAGS]) + AS_UNSET([OPENSSL_CFLAGS]) + AS_UNSET([OPENSSL_LDFLAGS]) + AS_UNSET([OPENSSL_LIBS]) + ] + ) + + + AS_IF([test "x$have_gnutls" = "xyes" || test "x$have_openssl" = "xyes"], [ enable_https="yes" - MSG_HTTPS="yes (using libgnutls)" + MSG_HTTPS="yes (single backend)" + MSG_TLS_BACKENDS="" + AS_VAR_IF([have_gnutls],["yes"], + [ + AS_IF([test -n "${MSG_TLS_BACKENDS}"], + [ + MSG_TLS_BACKENDS="${MSG_TLS_BACKENDS}, " + MSG_HTTPS="yes (multiple backends)" + ] + ) + AC_DEFINE([MHD_USE_GNUTLS],[1],[Define to '1' i][f GnuTLS library should be used]) + MSG_TLS_BACKENDS="${MSG_TLS_BACKENDS}GnuTLS" + MHD_APPEND_FLAG_TO_VAR([MHD_TLS_LIB_CPPFLAGS],[$GNUTLS_CPPFLAGS]) + MHD_APPEND_FLAG_TO_VAR([MHD_TLS_LIB_LDFLAGS],[$GNUTLS_LDFLAGS]) + MHD_PREPEND_FLAG_TO_VAR([MHD_TLS_LIBDEPS],[$GNUTLS_LIBS]) + ] + ) + AS_VAR_IF([have_openssl],["yes"], + [ + AS_IF([test -n "${MSG_TLS_BACKENDS}"], + [ + MSG_TLS_BACKENDS="${MSG_TLS_BACKENDS}, " + MSG_HTTPS="yes (multiple backends)" + ] + ) + AC_DEFINE([MHD_USE_OPENSSL],[1],[Define to '1' i][f OpenSSL library should be used]) + MSG_TLS_BACKENDS="${MSG_TLS_BACKENDS}OpenSSL" + MHD_APPEND_FLAG_TO_VAR([MHD_TLS_LIB_CPPFLAGS],[$OPENSSL_CPPFLAGS]) + MHD_APPEND_FLAG_TO_VAR([MHD_TLS_LIB_LDFLAGS],[$OPENSSL_LDFLAGS]) + MHD_PREPEND_FLAG_TO_VAR([MHD_TLS_LIBDEPS],[$OPENSSL_LIBS]) + ] + ) + AS_IF([test -z "$MSG_TLS_BACKENDS"],[AC_MSG_FAILURE([configure internal error: no TLS backends])]) AC_DEFINE([MHD_ENABLE_HTTPS],[1],[Define to '1' i][f HTTPS protocol should be enabled]) - AC_DEFINE([MHD_USE_GNUTLS],[1],[Define to '1' i][f GnuTLS library should be used]) - MHD_TLS_LIB_CPPFLAGS="$GNUTLS_CPPFLAGS" - MHD_TLS_LIB_LDFLAGS="$GNUTLS_LDFLAGS" - MHD_TLS_LIBDEPS="$GNUTLS_LIBS" ], [ AS_VAR_IF([enable_https],["yes"], - [AC_MSG_ERROR([GniTLS library not found, HTTPS cannot be enabled])] + [AC_MSG_ERROR([no suitable TLS lib found, HTTPS cannot be enabled])] ) enable_https="no" - MSG_HTTPS="no (lacking libgnutls)" + MSG_HTTPS="no (no suitable TLS lib found)" + MSG_TLS_BACKENDS="none" ] ) ], @@ -5070,15 +5281,35 @@ choke me now ] ) - AC_MSG_CHECKING(whether to support HTTPS) +AS_VAR_IF([enable_https],["yes"], + [ + AS_CASE([$MSG_HTTPS],[yes*],[:], + [AC_MSG_FAILURE([configure internal error: wrong MSG_HTTPS])] + ) + ], + [ + enable_https="no" + MSG_TLS_BACKENDS="none" + AS_UNSET([MHD_TLS_LIB_CPPFLAGS]) + AS_UNSET([MHD_TLS_LIB_LDFLAGS]) + AS_UNSET([MHD_TLS_LIBDEPS]) + AS_CASE([$MSG_HTTPS],[no*],[:], + [AC_MSG_FAILURE([configure internal error: wrong MSG_HTTPS])] + ) + ] +) AC_MSG_RESULT([$MSG_HTTPS]) -AM_CONDITIONAL([MHD_ENABLE_HTTPS], [test "x$enable_https" = "xyes"]) AM_CONDITIONAL([MHD_USE_GNUTLS], [[test "x$have_gnutls" = "xyes"]]) AC_SUBST([GNUTLS_CPPFLAGS]) AC_SUBST([GNUTLS_LDFLAGS]) AC_SUBST([GNUTLS_LIBS]) +AM_CONDITIONAL([MHD_USE_OPENSSL], [[test "x$have_openssl" = "xyes"]]) +AC_SUBST([OPENSSL_CPPFLAGS]) +AC_SUBST([OPENSSL_LDFLAGS]) +AC_SUBST([OPENSSL_LIBS]) +AM_CONDITIONAL([MHD_ENABLE_HTTPS], [test "x$enable_https" = "xyes"]) AS_VAR_IF([have_gnutls], ["yes"], [ @@ -7182,6 +7413,7 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} Configuration Summary: epoll support: ${enable_epoll=no} sendfile used: ${found_sendfile} HTTPS support: ${MSG_HTTPS} + TLS backends: ${MSG_TLS_BACKENDS} Logging support: ${enable_log_messages} Verbose auto replies: ${enable_http_messages} Cookie parser: ${enable_cookie} @@ -7207,13 +7439,23 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} Configuration Summary: Fuzzing tests: ${run_zzuf_tests_MSG=no} ]) -AS_IF([test "x$enable_https" = "xyes"], - [AC_MSG_NOTICE([HTTPS subsystem configuration: - License : LGPL version 2.1 or any later version - ])], - [AC_MSG_NOTICE([ - License : LGPLv2.1+ or eCos - ])]) +licence_num="0" +AS_VAR_IF([have_gnutls],["yes"], + [AS_IF([test "2" -gt "$licence_num"],[licence_num="2"])] +) +AS_VAR_IF([have_openssl],["yes"], + [AS_IF([test "3" -gt "$licence_num"],[licence_num="3"])] +) +AS_CASE([$licence_num], + [0],[licence_descr="LGPLv2.1+ or eCos"], + [2],[licence_descr="LGPL version 2.1 or any later version"], + [3],[licence_descr="LGPL version 3.0 or any later version"], + [AC_MSG_ERROR(internal error: unexpected licence version)] +) + +AC_MSG_NOTICE([ + Licence: ${licence_descr} +]) AS_IF([test "x$enable_bauth" != "xyes" || \ test "x$enable_dauth" != "xyes" || \ diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h index 0935c18b..a8b2beb5 100644 --- a/src/include/microhttpd2.h +++ b/src/include/microhttpd2.h @@ -1296,6 +1296,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_TLS_CONNECTION_INIT_FAILED = 51201 , + /** + * Warning about TLS backend configuration + */ + MHD_SC_TLS_LIB_CONF_WARNING = 51202 + , /** * Failed to perform TLS handshake */ @@ -3345,14 +3350,12 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_UseStictLevel enum MHD_FIXED_ENUM_APP_SET_ MHD_TlsBackend { /** - * Disable TLS, use plain TCP connections + * Disable TLS, use plain TCP connections (default) */ MHD_TLS_BACKEND_NONE = 0 , /** * Use best available TLS backend. - * Currently this is equivalent to GnuTLS (if TLS is enabled - * for MHD build). */ MHD_TLS_BACKEND_ANY = 1 , @@ -3360,6 +3363,11 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_TlsBackend * Use GnuTLS as TLS backend. */ MHD_TLS_BACKEND_GNUTLS = 2 + , + /** + * Use OpenSSL as TLS backend. + */ + MHD_TLS_BACKEND_OPENSSL = 3 }; /** diff --git a/src/include/microhttpd2_preamble.h.in b/src/include/microhttpd2_preamble.h.in index cc4c295c..cf51d0f3 100644 --- a/src/include/microhttpd2_preamble.h.in +++ b/src/include/microhttpd2_preamble.h.in @@ -1296,6 +1296,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_TLS_CONNECTION_INIT_FAILED = 51201 , + /** + * Warning about TLS backend configuration + */ + MHD_SC_TLS_LIB_CONF_WARNING = 51202 + , /** * Failed to perform TLS handshake */ @@ -3345,14 +3350,12 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_UseStictLevel enum MHD_FIXED_ENUM_APP_SET_ MHD_TlsBackend { /** - * Disable TLS, use plain TCP connections + * Disable TLS, use plain TCP connections (default) */ MHD_TLS_BACKEND_NONE = 0 , /** * Use best available TLS backend. - * Currently this is equivalent to GnuTLS (if TLS is enabled - * for MHD build). */ MHD_TLS_BACKEND_ANY = 1 , @@ -3360,6 +3363,11 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_TlsBackend * Use GnuTLS as TLS backend. */ MHD_TLS_BACKEND_GNUTLS = 2 + , + /** + * Use OpenSSL as TLS backend. + */ + MHD_TLS_BACKEND_OPENSSL = 3 }; /** diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am index ec88862d..2643bafd 100644 --- a/src/mhd2/Makefile.am +++ b/src/mhd2/Makefile.am @@ -117,6 +117,10 @@ 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 +tls_open_files = \ + tls_open_tls_lib.h tls_open_daemon_data.h tls_open_conn_data.h \ + tls_open_funcs.c tls_open_funcs.h + if HAVE_POST_PARSER libmicrohttpd2_la_SOURCES += $(post_parser_files) endif @@ -131,6 +135,10 @@ if MHD_ENABLE_HTTPS if MHD_USE_GNUTLS libmicrohttpd2_la_SOURCES += $(tls_gnu_files) endif + +if MHD_USE_OPENSSL + libmicrohttpd2_la_SOURCES += $(tls_open_files) +endif endif libmicrohttpd2_la_CPPFLAGS = \ diff --git a/src/mhd2/mhd_tls_choice.h b/src/mhd2/mhd_tls_choice.h index 4512b5af..d6ec6ddf 100644 --- a/src/mhd2/mhd_tls_choice.h +++ b/src/mhd2/mhd_tls_choice.h @@ -66,11 +66,30 @@ */ #define mhd_TLS_GNU_IS_SUPPORTED() (! ! mhd_TLS_GNU_ENABLED) +/* * OpenSSL * */ + +#ifdef MHD_USE_OPENSSL +/** + * Defined to one if OpenSSL is enabled at build time or to zero if not enabled + */ +# define mhd_TLS_OPEN_ENABLED (1) +#else +/** + * Defined to one if GnuTLS is enabled at build time or to zero if not enabled + */ +# define mhd_TLS_OPEN_ENABLED (0) +#endif + +/** + * Return non-zero if OpenSSL is supported + */ +#define mhd_TLS_OPEN_IS_SUPPORTED() (! ! mhd_TLS_GNU_ENABLED) + /** * Defined to the number of enabled TLS backends */ #define mhd_TLS_NUM_BACKENDS \ - (mhd_TLS_GNU_ENABLED /* + mhd_TLS_SOME_ENABLED */) + (mhd_TLS_GNU_ENABLED + mhd_TLS_OPEN_ENABLED) #if mhd_TLS_NUM_BACKENDS == 0 #error At least one TLS backend must be enabled is this header is included @@ -117,6 +136,19 @@ * The TLS back-end identifier for macro names */ # define mhd_TLS_MACRO_NAME_ID GNU +#elif defined(MHD_USE_OPENSSL) +/** + * The TLS back-end identifier for function names + */ +# define mhd_TLS_FUNC_NAME_ID open +/** + * The TLS back-end identifier for data names + */ +# define mhd_TLS_DATA_NAME_ID Open +/** + * The TLS back-end identifier for macro names + */ +# define mhd_TLS_MACRO_NAME_ID OPEN #endif diff --git a/src/mhd2/mhd_tls_funcs.c b/src/mhd2/mhd_tls_funcs.c index 8038b869..53f17010 100644 --- a/src/mhd2/mhd_tls_funcs.c +++ b/src/mhd2/mhd_tls_funcs.c @@ -39,8 +39,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_other_is_inited_fine() */ ? + return (mhd_tls_gnu_is_inited_fine () + || mhd_tls_open_is_inited_fine ()) ? mhd_TLS_BACKEND_AVAIL_OK : mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE; #ifdef MHD_USE_GNUTLS @@ -48,6 +48,12 @@ mhd_tls_is_backend_available (struct DaemonOptions *s) return mhd_tls_gnu_is_inited_fine () ? mhd_TLS_BACKEND_AVAIL_OK : mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE; +#endif +#ifdef MHD_USE_OPENSSL + if (MHD_TLS_BACKEND_OPENSSL == s->tls) + return mhd_tls_open_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 index 8326bd6e..a2d0c7b5 100644 --- a/src/mhd2/mhd_tls_funcs.h +++ b/src/mhd2/mhd_tls_funcs.h @@ -41,6 +41,8 @@ // TODO: Multi-TLS implementation #elif defined(MHD_USE_GNUTLS) # include "tls_gnu_funcs.h" +#elif defined(MHD_USE_OPENSSL) +# include "tls_open_funcs.h" #endif #ifndef MHD_USE_GNUTLS @@ -50,6 +52,13 @@ # define mhd_tls_gnu_is_inited_fine() (0) #endif +#ifndef MHD_USE_OPENSSL +/** + * Check whether OpenSSL backend was successfully initialised globally + */ +# define mhd_tls_open_is_inited_fine() (0) +#endif + /* ** Global initialisation / de-initialisation ** */ /** diff --git a/src/mhd2/tls_open_conn_data.h b/src/mhd2/tls_open_conn_data.h new file mode 100644 index 00000000..162cd88c --- /dev/null +++ b/src/mhd2/tls_open_conn_data.h @@ -0,0 +1,76 @@ +/* + 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_open_conn_data.h + * @brief The definition of OpenSSL daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_OPEN_CONN_DATA_H +#define MHD_TLS_OPEN_CONN_DATA_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_OPENSSL +#error This header can be used only if GnuTLS is enabled +#endif + +#include "tls_open_tls_lib.h" + +#include "sys_bool_type.h" + +#ifndef NDEBUG +struct mhd_TlsOpenConnDebug +{ + unsigned int is_inited; + unsigned int is_tls_handshake_completed; + unsigned int is_failed; +}; +#endif /* ! NDEBUG */ + +/** + * The structure with connection-specific GnuTLS data + */ +struct mhd_TlsOpenConnData +{ + /** + * OpenSSL session data + */ + SSL *sess; + + /** + * 'true' if sent TLS shutdown "alert" + */ + bool shut_tls_wr_sent; + + /** + * 'true' if received EOF (the remote side initiated shutting down) + */ + bool shut_tls_wr_received; +#ifndef NDEBUG + /** + * Debugging data + */ + struct mhd_TlsOpenConnDebug dbg; +#endif /* ! NDEBUG */ +}; + +#endif /* ! MHD_TLS_OPEN_CONN_DATA_H */ diff --git a/src/mhd2/tls_open_daemon_data.h b/src/mhd2/tls_open_daemon_data.h new file mode 100644 index 00000000..112462f1 --- /dev/null +++ b/src/mhd2/tls_open_daemon_data.h @@ -0,0 +1,56 @@ +/* + 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_open_daemon_data.h + * @brief The definition of OpenSSL daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_OPEN_DAEMON_DATA_H +#define MHD_TLS_OPEN_DAEMON_DATA_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_OPENSSL +#error This header can be used only if GnuTLS is enabled +#endif + +#include "tls_open_tls_lib.h" + +/** + * The structure with daemon-specific GnuTLS data + */ +struct mhd_TlsOpenDaemonData +{ + /** + * The library context. + * Thanks to the library context, daemons are isolated from each other and + * from any application context. + */ + OSSL_LIB_CTX *libctx; + + /** + * The server context + */ + SSL_CTX *ctx; +}; + +#endif /* ! MHD_TLS_OPEN_DAEMON_DATA_H */ diff --git a/src/mhd2/tls_open_funcs.c b/src/mhd2/tls_open_funcs.c new file mode 100644 index 00000000..cfe1af2f --- /dev/null +++ b/src/mhd2/tls_open_funcs.c @@ -0,0 +1,1134 @@ +/* + 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_open_funcs.c + * @brief The implementation of GnuTLS wrapper functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include + +#include "compat_calloc.h" +#include "sys_malloc.h" +#include "mhd_assert.h" + +#include "mhd_conn_socket.h" + +#include "tls_open_tls_lib.h" + +#include "tls_open_daemon_data.h" +#include "tls_open_conn_data.h" +#include "tls_open_funcs.h" + +#include "daemon_options.h" + +#include "daemon_logger.h" + +#include "mhd_public_api.h" + +#ifdef mhd_TLS_OPEN_DH_PARAMS_NEEDS_PKCS3 +# include "tls_dh_params.h" +#endif + +#ifdef mhd_USE_TLS_DEBUG_MESSAGES +# include /* For TLS debug printing */ +#endif + +#ifdef mhd_USE_TLS_DEBUG_MESSAGES + +static MHD_FN_PAR_NONNULL_ (1) int +mhd_tls_open_dbg_print_errs (const char *msg, + size_t msg_len, + void *cls) +{ + int ret; + int print_size = (int) msg_len; + + (void) cls; /* Not used */ + + if ((print_size < 0) || + (msg_len != (unsigned int) print_size)) + print_size = (int) ((~((unsigned int) 0u)) >> 1); + + ret = fprintf (stderr, + "## OpenSSL error: %.*s\n", + print_size, msg); + (void) fflush (stderr); + return ret; +} + + +# define mhd_DBG_PRINT_TLS_ERRS() \ + ERR_print_errors_cb (&mhd_tls_open_dbg_print_errs, NULL) +#else +# define mhd_DBG_PRINT_TLS_ERRS() ERR_clear_error () +#endif + +/* ** Global initialisation / de-initialisation ** */ + +static bool openssl_lib_inited = false; + +MHD_INTERNAL void +mhd_tls_open_global_init_once (void) +{ + const unsigned long ver_num = OpenSSL_version_num (); + /* Make sure that used shared OpenSSL library has least the same version as + MHD was configured for. Fail if the version is earlier. */ + openssl_lib_inited = ((0x900000UL < ver_num) /* Versions before 3.0 */ + && (OPENSSL_VERSION_NUMBER <= ver_num)); + + /* The call of OPENSSL_init_ssl() typically not needed, but it won't hurt + if library was initialised automatically. + In some exotic situations automatic initialisation could fail, and + this call would make sure that the library is initialised before used. */ + openssl_lib_inited = openssl_lib_inited + && (0 < OPENSSL_init_ssl (0, NULL)); +} + + +MHD_INTERNAL bool +mhd_tls_open_is_inited_fine (void) +{ + return openssl_lib_inited; +} + + +/* ** Daemon initialisation / de-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; +} + + +/* Helper to prevent password prompts in terminal */ +static int +null_passwd_cb (char *buf, + int size, + int rwflag, + void *cls) +{ + (void) buf; (void) size; (void) rwflag; (void) cls; /* Unused */ +#ifdef mhd_USE_TLS_DEBUG_MESSAGES + fprintf (stderr, "## OpenSSL: the NULL passphrase callback is called\n"); + fflush (stderr); +#endif + return 0; +} + + +/** + * Initialise OpenSSL library context + * @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_lib_ctx (struct MHD_Daemon *restrict d, + struct mhd_TlsOpenDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + bool fallback_config; + bool prevent_fallbacks; + char *conf_filename; + d_tls->libctx = OSSL_LIB_CTX_new (); + + (void) s; // TODO: support app-defined name for TLS backend profile + + if (NULL == d_tls->libctx) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to create TLS library context"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + prevent_fallbacks = (0 != OSSL_LIB_CTX_get_conf_diagnostics (d_tls->libctx)); + + fallback_config = false; + ERR_clear_error (); + + conf_filename = CONF_get1_default_config_file (); + if (NULL == conf_filename) + mhd_DBG_PRINT_TLS_ERRS (); + else + { + bool libctx_inited; + CONF *conf; + + libctx_inited = false; + conf = NCONF_new_ex (d_tls->libctx, + NULL); + if (NULL == conf) + mhd_DBG_PRINT_TLS_ERRS (); + else + { + if (0 >= NCONF_load (conf, + conf_filename, + NULL)) + { + unsigned long err; + + err = ERR_peek_last_error (); + mhd_DBG_PRINT_TLS_ERRS (); + libctx_inited = true; /* Nothing to initialise */ + + if ((ERR_LIB_CONF != ERR_GET_LIB (err)) || + (CONF_R_NO_SUCH_FILE != ERR_GET_REASON (err))) + { + fallback_config = true; + mhd_LOG_PRINT (d, MHD_SC_TLS_LIB_CONF_WARNING, \ + mhd_LOG_FMT ("Error in TLS library configuration " + "file '%s'"), \ + conf_filename); + } + } + else /* NCONF_load() succeed */ + { + (void) s; // TODO: support app-defined name for TLS backend profile + + if (! libctx_inited) + { + if (NULL != NCONF_get_section (conf, + "libmicrohttpd")) + { + if (0 < + CONF_modules_load (conf, + "libmicrohttpd", + 0)) + libctx_inited = true; + else + { + mhd_DBG_PRINT_TLS_ERRS (); + fallback_config = true; + mhd_LOG_PRINT (d, MHD_SC_TLS_LIB_CONF_WARNING, \ + mhd_LOG_FMT ("Failed to load configuration file " \ + "section [%s]"), \ + "libmicrohttpd"); + + libctx_inited = + (0 < CONF_modules_load (conf, + "libmicrohttpd", + CONF_MFLAGS_IGNORE_ERRORS)); + if (! libctx_inited) + mhd_DBG_PRINT_TLS_ERRS (); + } + } + } + if (! libctx_inited) + { + if (0 < + CONF_modules_load (conf, + NULL, + 0)) + libctx_inited = true; + else + { + mhd_DBG_PRINT_TLS_ERRS (); + fallback_config = true; + mhd_LOG_PRINT (d, MHD_SC_TLS_LIB_CONF_WARNING, \ + mhd_LOG_FMT ("Failed to load configuration file " \ + "default section")); + + libctx_inited = + (0 < CONF_modules_load (conf, + NULL, + CONF_MFLAGS_IGNORE_ERRORS)); + if (! libctx_inited) + mhd_DBG_PRINT_TLS_ERRS (); + } + } + if (fallback_config && libctx_inited && ! prevent_fallbacks) + prevent_fallbacks = + (0 != OSSL_LIB_CTX_get_conf_diagnostics (d_tls->libctx)); + } + NCONF_free (conf); + } + OPENSSL_free (conf_filename); + + if (fallback_config && prevent_fallbacks) + libctx_inited = true; + + if (libctx_inited) + { + return MHD_SC_OK; /* Success exit point */ + } + } + + OSSL_LIB_CTX_free (d_tls->libctx); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to initialise TLS library context"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; +} + + +/** + * De-initialise OpenSSL library context + * @param d_tls the daemon TLS settings + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_lib_ctx (struct mhd_TlsOpenDaemonData *restrict d_tls) +{ + mhd_assert (NULL != d_tls->libctx); + OSSL_LIB_CTX_free (d_tls->libctx); +} + + +static const unsigned char alpn_codes_list[] = { +#if 0 /* Disabled code */ + 2u, 'h', '3' /* Registered value for HTTP/3 */ + , + 2u, 'h', '2' /* Registered value for HTTP/2 over TLS */ + , +#endif /* Disabled code */ + 8u, 'h', 't', 't', 'p', '/', '1', '.', '1' /* Registered value for HTTP/1.1 */ + , + 8u, 'h', 't', 't', 'p', '/', '1', '.', '0' /* Registered value for HTTP/1.0 */ +}; + +/** + * Provide the list of supported protocols for NPN extension + * @param sess the TLS session (ignored) + * @param[out] out the pointer to get the location of the data + * @param[out] outlen the size of the data provided + * @param cls the closure (ignored) + * @return always SSL_TLSEXT_ERR_OK + */ +static int +get_npn_list (SSL *sess, + const unsigned char **out, + unsigned int *outlen, + void *cls) +{ + (void) sess; (void) cls; /* Unused */ + *out = alpn_codes_list; + *outlen = sizeof(alpn_codes_list); + return SSL_TLSEXT_ERR_OK; +} + + +/** + * Select protocol from the provided list for ALPN extension + * @param sess the TLS session (ignored) + * @param[out] out the pointer to get the location of selected protocol value + * @param[out] outlen the size of the selected protocol value + * @param in the list of protocols values provided by the client + * @param inlen the size of the list of protocols values provided by the client + * @param cls the closure (ignored) + * @return SSL_TLSEXT_ERR_OK if matching protocol found and selected, + * SSL_TLSEXT_ERR_ALERT_FATAL otherwise + */ +static int +select_alpn_prot (SSL *sess, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *cls) +{ + (void) sess; (void) cls; /* Unused */ + if (OPENSSL_NPN_NEGOTIATED == + SSL_select_next_proto (mhd_DROP_CONST (out), + outlen, + in, + inlen, + alpn_codes_list, + sizeof(alpn_codes_list))) + return SSL_TLSEXT_ERR_OK; /* Success */ + + return SSL_TLSEXT_ERR_ALERT_FATAL; /* Failure */ +} + + +/** + * Initialise TLS server context + * @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_ctx (struct MHD_Daemon *restrict d, + struct mhd_TlsOpenDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + uint64_t ctx_opts; + +#ifndef HAVE_LOG_FUNCTIONALITY + (void) d; /* Mute compiler warning */ +#endif + (void) s; // TODO: support configuration options + + mhd_assert (NULL != d_tls->libctx); + + ERR_clear_error (); + + d_tls->ctx = SSL_CTX_new_ex (d_tls->libctx, + NULL, + TLS_server_method ()); + if (NULL == d_tls->ctx) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to initialise TLS server context"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + /* Enable some safe and useful workarounds */ + ctx_opts = SSL_OP_SAFARI_ECDHE_ECDSA_BUG | SSL_OP_TLSEXT_PADDING; + + // TODO: add configuration option + // ctx_opts |= SSL_OP_CIPHER_SERVER_PREFERENCE; + + /* Enable kernel TLS */ // TODO: add configuration option + ctx_opts |= SSL_OP_ENABLE_KTLS; +#ifdef SSL_OP_ENABLE_KTLS_TX_ZEROCOPY_SENDFILE + ctx_opts |= SSL_OP_ENABLE_KTLS_TX_ZEROCOPY_SENDFILE; +#endif + + /* HTTP defines strict framing for the client-side data, + no risk of attack on server on unexpected connection interruption */ + /* ctx_opts |= SSL_OP_IGNORE_UNEXPECTED_EOF; */ // TODO: recheck + + /* There is no reason to use re-negotiation with HTTP */ + ctx_opts |= SSL_OP_NO_RENEGOTIATION; + + /* Do not use session resumption for now */ + ctx_opts |= SSL_OP_NO_TICKET; + + (void) SSL_CTX_set_options (d_tls->ctx, + ctx_opts); + + /* Prevent interactive password prompts */ + SSL_CTX_set_default_passwd_cb (d_tls->ctx, + &null_passwd_cb); + + // TODO: regenerate certificates + // TODO: make the setting configurable + // FIXME: this is a bad workaround! + SSL_CTX_set_security_level (d_tls->ctx, 0); /* Required to accept current test CA */ + + /* recv()- and send()-related options */ + (void) SSL_CTX_set_mode (d_tls->ctx, + SSL_MODE_ENABLE_PARTIAL_WRITE + | SSL_MODE_AUTO_RETRY); + (void) SSL_CTX_clear_mode (d_tls->ctx, + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER + | SSL_MODE_ASYNC); + + SSL_CTX_set_read_ahead (d_tls->ctx, + ! 0); + + /* ALPN and NPN */ + // TODO: use daemon option to disable them + SSL_CTX_set_alpn_select_cb (d_tls->ctx, + &select_alpn_prot, + NULL); + SSL_CTX_set_next_protos_advertised_cb (d_tls->ctx, + &get_npn_list, + NULL); + + return MHD_SC_OK; +} + + +/** + * De-initialise TLS server context + * @param d_tls the daemon TLS settings + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_ctx (struct mhd_TlsOpenDaemonData *restrict d_tls) +{ + mhd_assert (NULL != d_tls->ctx); + SSL_CTX_free (d_tls->ctx); +} + + +/** + * Load provided certificates chain + * @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_load_certs_chain (struct MHD_Daemon *restrict d, + struct mhd_TlsOpenDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode ret; + BIO *m_bio; + X509 *cert; + + mhd_assert (NULL != d_tls->libctx); + mhd_assert (NULL != d_tls->ctx); + + ERR_clear_error (); + + m_bio = BIO_new_mem_buf (s->tls_cert_key.v_mem_cert, + -1); + if (NULL == m_bio) + { + mhd_DBG_PRINT_TLS_ERRS (); + return MHD_SC_DAEMON_MALLOC_FAILURE; + } + + ret = MHD_SC_OK; + + /* The certificate object must be pre-allocated to associate it with + * the lib context */ + cert = X509_new_ex (d_tls->libctx, + NULL); + if (NULL != cert) + { + if (NULL != PEM_read_bio_X509_AUX (m_bio, + &cert, + &null_passwd_cb, + NULL)) + { + if (0 < SSL_CTX_use_certificate (d_tls->ctx, + cert)) + { + if (0 != ERR_peek_error ()) + mhd_DBG_PRINT_TLS_ERRS (); + + /* The object successfully "copied" to CTX, + * the original object is not needed anymore. */ + X509_free (cert); + cert = NULL; + + do + { + X509 *c_cert; /* Certifying certificate */ + c_cert = X509_new_ex (d_tls->libctx, + NULL); + if (NULL == c_cert) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to create new chain certificate object"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + else + { + if (NULL == PEM_read_bio_X509 (m_bio, + &cert, + &null_passwd_cb, + NULL)) + { + unsigned long err; + err = ERR_peek_last_error (); + if ((ERR_LIB_PEM == ERR_GET_LIB (err)) && + (PEM_R_NO_START_LINE == ERR_GET_REASON (err))) + { + /* End of data */ + ERR_clear_error (); + X509_free (c_cert); /* Empty, not needed */ + + mhd_assert (MHD_SC_OK == ret); + return MHD_SC_OK; /* Success exit point */ + } + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to load next object in the certificates " \ + "chain"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + else + { + if (SSL_CTX_add0_chain_cert (d_tls->ctx, + c_cert)) + { + /* Success, do not free the certificate as + * function '_add0_' was used to add it. */ + /* Read the next certificate in the chain. */ + continue; + } + else + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to add the new certificate object " + "to the chain"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + } + X509_free (c_cert); /* Failed, the object is not needed */ + mhd_assert (MHD_SC_OK != ret); + } + } while (MHD_SC_OK == ret); + } + else + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to set the certificate"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + } + else + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to process the certificate"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + if (NULL != cert) + X509_free (cert); + } + else + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to create new certificate object"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + BIO_free (m_bio); + mhd_assert (MHD_SC_OK != ret); + return ret; +} + + +/** + * Initialise TLS certificate + * The function loads the certificate chain and the private key. + * @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_cert (struct MHD_Daemon *restrict d, + struct mhd_TlsOpenDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode ret; + BIO *m_bio; + EVP_PKEY *pr_key; + int res; + + mhd_assert (NULL != d_tls->libctx); + mhd_assert (NULL != d_tls->ctx); + + ERR_clear_error (); + + ret = daemon_load_certs_chain (d, + d_tls, + s); + if (MHD_SC_OK != ret) + return ret; + + /* Check and cache the certificates chain. + This also prevents automatic chain re-building for each session. */ + res = + SSL_CTX_build_cert_chain ( + d_tls->ctx, + SSL_BUILD_CHAIN_FLAG_CHECK /* Use only certificates in the chain */ + | SSL_BUILD_CHAIN_FLAG_UNTRUSTED /* Intermediate certs does not need to be trusted */ + | SSL_BUILD_CHAIN_FLAG_NO_ROOT /* The root should not be sent */ + | SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR /* Allow the root CA to be not trusted */ + ); + if (0 >= res) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed rebuild certificate chain"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + if (2 == res) + mhd_DBG_PRINT_TLS_ERRS (); + + m_bio = BIO_new_mem_buf (s->tls_cert_key.v_mem_key, + -1); + if (NULL == m_bio) + { + mhd_DBG_PRINT_TLS_ERRS (); + return MHD_SC_DAEMON_MALLOC_FAILURE; + } + pr_key = + PEM_read_bio_PrivateKey_ex (m_bio, + NULL, + NULL == s->tls_cert_key.v_mem_pass ? + &null_passwd_cb : NULL, + mhd_DROP_CONST (s->tls_cert_key.v_mem_pass), + d_tls->libctx, + NULL); + BIO_free (m_bio); + if (NULL == pr_key) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to read the private key"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + res = SSL_CTX_use_PrivateKey (d_tls->ctx, + pr_key); + EVP_PKEY_free (pr_key); /* The key has been "copied" or failed */ + if (1 != res) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to set the private key"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + /* This actually RE-checks the key. + The key should be already checked automatically when it was set after + setting the certificate. */ + if (1 != SSL_CTX_check_private_key (d_tls->ctx)) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "The private key does not match the certificate"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + return MHD_SC_OK; +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (3) mhd_StatusCodeInt +mhd_tls_open_daemon_init (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s, + struct mhd_TlsOpenDaemonData **restrict p_d_tls) +{ + mhd_StatusCodeInt res; + struct mhd_TlsOpenDaemonData *restrict d_tls; + + res = check_app_tls_sessings (d, s); + if (MHD_SC_OK != res) + return res; + + d_tls = (struct mhd_TlsOpenDaemonData *) + mhd_calloc (1, sizeof (struct mhd_TlsOpenDaemonData)); + *p_d_tls = d_tls; + if (NULL == d_tls) + return MHD_SC_DAEMON_MALLOC_FAILURE; + + res = daemon_init_lib_ctx (d, + d_tls, + s); + if (MHD_SC_OK == res) + { + res = daemon_init_ctx (d, + d_tls, + s); + if (MHD_SC_OK == res) + { + res = daemon_init_cert (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_ctx (d_tls); + } + daemon_deinit_lib_ctx (d_tls); + } + 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_open_daemon_deinit (struct mhd_TlsOpenDaemonData *restrict d_tls) +{ + mhd_assert (NULL != d_tls); + daemon_deinit_ctx (d_tls); + daemon_deinit_lib_ctx (d_tls); + free (d_tls); +} + + +/* ** Connection initialisation / de-initialisation ** */ + +MHD_INTERNAL size_t +mhd_tls_open_conn_get_tls_size (void) +{ + return sizeof (struct mhd_TlsOpenConnData); +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (3) bool +mhd_tls_open_conn_init (const struct mhd_TlsOpenDaemonData *restrict d_tls, + const struct mhd_ConnSocket *sk, + struct mhd_TlsOpenConnData *restrict c_tls) +{ + int fd; + + ERR_clear_error (); + + fd = (int) sk->fd; + if (sk->fd != (MHD_Socket) fd) + return false; /* OpenSSL docs clam that it should not be possible */ + + c_tls->sess = SSL_new (d_tls->ctx); + + if (NULL == c_tls->sess) + { + mhd_DBG_PRINT_TLS_ERRS (); + return false; + } + + if (0 < SSL_set_fd (c_tls->sess, fd)) + { + SSL_set_accept_state (c_tls->sess); /* Force server mode */ + +#ifndef NDEBUG + c_tls->dbg.is_inited = true; +#endif + return true; /* Success exit point */ + } + + SSL_free (c_tls->sess); + c_tls->sess = NULL; + return false; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void +mhd_tls_open_conn_deinit (struct mhd_TlsOpenConnData *restrict c_tls) +{ + mhd_assert (NULL != c_tls->sess); + mhd_assert (c_tls->dbg.is_inited); + SSL_free (c_tls->sess); +} + + +/* ** TLS connection establishing ** */ + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +enum mhd_TlsProcedureResult +mhd_tls_open_conn_handshake (struct mhd_TlsOpenConnData *restrict 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); + + ERR_clear_error (); + + res = SSL_do_handshake (c_tls->sess); + + if (1 == res) + { +#ifndef NDEBUG + c_tls->dbg.is_tls_handshake_completed = true; +#endif /* ! NDEBUG */ + return mhd_TLS_PROCED_SUCCESS; /* Success exit point */ + } + + switch (SSL_get_error (c_tls->sess, res)) + { + case SSL_ERROR_WANT_READ: + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + This is very bad when edge triggered polling is used as is is not clear + whether the "recv-ready" flag should be cleared. + If the flag is cleared, but it should not (because the process has been + "interrupted") then already pending data could be never processed. + If the flag is not cleared, but it should be cleared (because all + received data has been processed) then it would create busy-waiting loop. + Use clear of "ready" flag as safer, but not ideal solution. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_TLS_PROCED_SEND_MORE_NEEDED; + case SSL_ERROR_WANT_WRITE: + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + This is very bad when edge triggered polling is used as is is not clear + whether the "send-ready" flag should be cleared. + If the flag is cleared, but it should not (because the process has been + "interrupted") then already pending data could be never sent. + If the flag is not cleared, but it should be cleared (because the network + is busy) then it would create busy-waiting loop. + Use clear of "ready" flag as safer, but not ideal solution. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_TLS_PROCED_RECV_MORE_NEEDED; + case SSL_ERROR_NONE: + mhd_assert (0 && "This should not be possible"); + MHD_UNREACHABLE_; + break; + default: /* Handled with all other errors below */ + break; + } + mhd_DBG_PRINT_TLS_ERRS (); +#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_open_conn_shutdown (struct mhd_TlsOpenConnData *restrict 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); + + ERR_clear_error (); + + res = SSL_shutdown (c_tls->sess); + + if (1 == res) + { + c_tls->shut_tls_wr_sent = true; + c_tls->shut_tls_wr_received = true; + return mhd_TLS_PROCED_SUCCESS; /* Success exit point */ + } + + /* The OpenSSL documentation contradicts itself: there are two mutually + exclusive statements on a single page. + * https://docs.openssl.org/master/man3/SSL_shutdown/#shutdown-lifecycle + indicates that for nonblocking socket ZERO could be returned when + "close_notify" is GOING to be sent, but NOT sent yet. + It also suggests to CALL SSL_get_error(3) when ZERO is returned. + * https://docs.openssl.org/master/man3/SSL_shutdown/#return-values + indicates ZERO is returned ONLY when "close_notify" HAS BEEN sent. + It also suggests to NOT CALL SSL_get_error(3) when ZERO is returned. + */ + switch (SSL_get_error (c_tls->sess, res)) + { + case SSL_ERROR_WANT_READ: + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + This is very bad when edge triggered polling is used as is is not clear + whether the "recv-ready" flag should be cleared. + If the flag is cleared, but it should not (because the process has been + "interrupted") then already pending data could be never processed. + If the flag is not cleared, but it should be cleared (because all + received data has been processed) then it would create busy-waiting loop. + Use clear of "ready" flag as safer, but not ideal solution. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_TLS_PROCED_SEND_MORE_NEEDED; + case SSL_ERROR_WANT_WRITE: + c_tls->shut_tls_wr_sent = true; + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + This is very bad when edge triggered polling is used as is is not clear + whether the "send-ready" flag should be cleared. + If the flag is cleared, but it should not (because the process has been + "interrupted") then already pending data could be never sent. + If the flag is not cleared, but it should be cleared (because the network + is busy) then it would create busy-waiting loop. + Use clear of "ready" flag as safer, but not ideal solution. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_TLS_PROCED_RECV_MORE_NEEDED; + case SSL_ERROR_NONE: + mhd_assert (res != 0 && "Should not be possible"); + c_tls->shut_tls_wr_sent = true; + return mhd_TLS_PROCED_RECV_INTERRUPTED; + default: /* Handled with all other errors below */ + break; + } + mhd_DBG_PRINT_TLS_ERRS (); +#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_open_conn_recv (struct mhd_TlsOpenConnData *restrict c_tls, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict received) +{ + 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->dbg.is_failed); + + ERR_clear_error (); + + res = SSL_read_ex (c_tls->sess, + buf, + buf_size, + received); + if (1 == res) + { + mhd_assert (0 != *received); + return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ + } + + mhd_assert (0 == res); + *received = 0; + switch (SSL_get_error (c_tls->sess, res)) + { + case SSL_ERROR_ZERO_RETURN: /* Not an error */ + c_tls->shut_tls_wr_received = true; + return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ + case SSL_ERROR_WANT_READ: + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + This is very bad when edge triggered polling is used as is is not clear + whether the "recv-ready" flag should be cleared. + If the flag is cleared, but it should not (because the process has been + "interrupted") then already pending data could be never processed. + If the flag is not cleared, but it should be cleared (because all + received data has been processed) then it would create busy-waiting loop. + Use clear of "ready" flag as safer, but not ideal solution. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_SOCKET_ERR_AGAIN; + case SSL_ERROR_NONE: + mhd_assert (0 && "Should not be possible"); + break; + case SSL_ERROR_WANT_WRITE: + mhd_assert (0 && "Should not be possible as re-handshakes are disallowed"); + break; + case SSL_ERROR_SYSCALL: + mhd_DBG_PRINT_TLS_ERRS (); +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_CONN_BROKEN; + case SSL_ERROR_SSL: + default: + break; + } + /* Treat all other kinds of errors as hard errors */ + mhd_DBG_PRINT_TLS_ERRS (); +#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_open_conn_has_data_in (struct mhd_TlsOpenConnData *restrict c_tls) +{ + return 0 != SSL_pending (c_tls->sess); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_IN_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (4) enum mhd_SocketError +mhd_tls_open_conn_send (struct mhd_TlsOpenConnData *restrict c_tls, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict sent) +{ + 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->dbg.is_failed); + + ERR_clear_error (); + + res = SSL_write_ex (c_tls->sess, + buf, + buf_size, + sent); + if (1 == res) + { + mhd_assert (0 != *sent); + return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ + } + + mhd_assert (0 == res); + *sent = 0; + switch (SSL_get_error (c_tls->sess, res)) + { + case SSL_ERROR_WANT_WRITE: + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + This is very bad when edge triggered polling is used as is is not clear + whether the "send-ready" flag should be cleared. + If the flag is cleared, but it should not (because the process has been + "interrupted") then already pending data could be never sent. + If the flag is not cleared, but it should be cleared (because the network + is busy) then it would create busy-waiting loop. + Use clear of "ready" flag as safer, but not ideal solution. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_SOCKET_ERR_AGAIN; + case SSL_ERROR_NONE: + mhd_assert (0 && "Should not be possible"); + break; + case SSL_ERROR_WANT_READ: + mhd_assert (0 && "Should not be possible as re-handshakes are disallowed"); + break; + case SSL_ERROR_ZERO_RETURN: + mhd_assert (0 && "Should not be possible when sending"); + break; + case SSL_ERROR_SYSCALL: + mhd_DBG_PRINT_TLS_ERRS (); +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_CONN_BROKEN; + case SSL_ERROR_SSL: + default: + break; + } + /* Treat all other kinds of errors as hard errors */ + mhd_DBG_PRINT_TLS_ERRS (); +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_TLS; +} diff --git a/src/mhd2/tls_open_funcs.h b/src/mhd2/tls_open_funcs.h new file mode 100644 index 00000000..12d29a5f --- /dev/null +++ b/src/mhd2/tls_open_funcs.h @@ -0,0 +1,214 @@ +/* + 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_open_funcs.h + * @brief The declarations of OpenSSL interface wrapper functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_OPEN_FUNCS_H +#define MHD_TLS_OPEN_FUNCS_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_OPENSSL +#error This header can be used only if OpenSSL 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 OpenSSL data + */ +struct mhd_TlsOpenDaemonData; /* Forward declaration */ + +/** + * The structure with connection-specific OpenSSL data + */ +struct mhd_TlsOpenConnData; /* Forward declaration */ + + +/* ** Global initialisation / de-initialisation ** */ + +/** + * Globally initialise OpenSSL backend. + * Once initialised, this backend cannot be de-initialised. + */ +MHD_INTERNAL void +mhd_tls_open_global_init_once (void); + +/* No-op for OpenSSL backend */ +#define mhd_tls_open_global_re_init() ((void) 0) + +/* No-op for OpenSSL backend */ +#define mhd_tls_open_global_deinit() ((void) 0) + +/** + * Check whether OpenSSL backend was successfully initialised globally + */ +MHD_INTERNAL bool +mhd_tls_open_is_inited_fine (void); + + +/* ** Daemon initialisation / de-initialisation ** */ + +struct MHD_Daemon; /* Forward declaration */ +struct DaemonOptions; /* Forward declaration */ + +/** + * Allocate and initialise daemon TLS parameters + * @param d the daemon handle + * @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_open_daemon_init (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s, + struct mhd_TlsOpenDaemonData **restrict p_d_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (3); + +/** + * 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_open_daemon_deinit (struct mhd_TlsOpenDaemonData *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_open_conn_get_tls_size (void); + +/** + * 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_open_conn_init (const struct mhd_TlsOpenDaemonData *restrict d_tls, + const struct mhd_ConnSocket *sk, + struct mhd_TlsOpenConnData *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_open_conn_deinit (struct mhd_TlsOpenConnData *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_open_conn_handshake (struct mhd_TlsOpenConnData *restrict 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_open_conn_shutdown (struct mhd_TlsOpenConnData *restrict 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_open_conn_recv (struct mhd_TlsOpenConnData *restrict 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_open_conn_has_data_in (struct mhd_TlsOpenConnData *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 buffer_size the size of the @a buffer (in bytes) + * @param buffer content of the buffer to send + * @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_open_conn_send (struct mhd_TlsOpenConnData *restrict c_tls, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict sent) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4); + +#endif /* ! MHD_TLS_OPEN_FUNCS_H */ diff --git a/src/mhd2/tls_open_tls_lib.h b/src/mhd2/tls_open_tls_lib.h new file mode 100644 index 00000000..4e2bf85c --- /dev/null +++ b/src/mhd2/tls_open_tls_lib.h @@ -0,0 +1,57 @@ +/* + 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_open_tls_lib.h + * @brief The MHD wrapper for OpenSSL headers + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_OPEN_TLS_LIB_H +#define MHD_TLS_OPEN_TLS_LIB_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_OPENSSL +#error This header can be used only if GnuTLS is enabled +#endif + +/* Declare compatibility with OpenSSL versions >= 3.0 */ +#define OPENSSL_API_COMPAT 30000 +/* Request to hide all symbols deprecated before OPENSSL_API_COMPAT version */ +#define OPENSSL_NO_DEPRECATED 1 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef OPENSSL_VERSION_NUMBER +#error OPENSSL_VERSION_NUMBER is not defined +#endif + +#ifndef OPENSSL_VERSION_PREREQ +#error OPENSSL_VERSION_PREREQ is not defined +#endif + +#endif /* ! MHD_TLS_OPEN_TLS_LIB_H */