libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 172b0eeb45e771ca0df56697bcfb581cc8a96a88
parent 281b22da0e60e642a5ad528e5a25ff7ae9b2d7ed
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Sat, 24 Sep 2022 20:13:16 +0300

Implemented support for hash calculation by GnuTLS lib functions

Diffstat:
Mconfigure.ac | 256++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/include/microhttpd.h | 39+++++++++++++++++++++++++++++++--------
Msrc/microhttpd/Makefile.am | 46++++++++++++++++++++++++++++++++++++++++++++--
Msrc/microhttpd/daemon.c | 6++++++
Msrc/microhttpd/digestauth.c | 659+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/microhttpd/md5.h | 5+++++
Asrc/microhttpd/md5_ext.c | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/microhttpd/md5_ext.h | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/microhttpd/mhd_md5_wrap.h | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/microhttpd/mhd_sha256_wrap.h | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/microhttpd/sha256.h | 5+++++
Asrc/microhttpd/sha256_ext.c | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/microhttpd/sha256_ext.h | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/microhttpd/test_dauth_userdigest.c | 14++++++++++++++
Msrc/microhttpd/test_dauth_userhash.c | 14++++++++++++++
Msrc/microhttpd/test_md5.c | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/microhttpd/test_sha256.c | 90++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/testcurl/test_digestauth.c | 19+++++++++----------
Msrc/testcurl/test_digestauth2.c | 14+++++++++++++-
Msrc/testcurl/test_digestauth_concurrent.c | 18++++++++----------
Msrc/testcurl/test_digestauth_sha256.c | 19+++++++++----------
Msrc/testcurl/test_digestauth_with_arguments.c | 11++++++-----
Mw32/common/MHD_config.h | 6++++++
Mw32/common/libmicrohttpd-files.vcxproj | 2++
Mw32/common/libmicrohttpd-filters.vcxproj | 6++++++
25 files changed, 1677 insertions(+), 265 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -3048,8 +3048,8 @@ AC_MSG_RESULT([[$enable_bauth]]) AC_MSG_CHECKING([[whether to support HTTP Digest authentication]]) AC_ARG_ENABLE([dauth], [AS_HELP_STRING([--disable-dauth], [disable HTTP Digest Authentication support])], - [enable_dauth=${enableval}], - [enable_dauth=yes]) + [enable_dauth="${enableval}"], + [enable_dauth="yes"]) AS_IF([[test "x$enable_dauth" != "xno"]], [ enable_dauth=yes AC_DEFINE([DAUTH_SUPPORT],[1],[Define to 1 if libmicrohttpd is compiled with Digest Auth support.]) ]) @@ -3084,57 +3084,249 @@ AC_MSG_RESULT([[$enable_cookie]]) # optional: MD5 support for Digest Auth. Enabled by default. AC_ARG_ENABLE([[md5]], - [AS_HELP_STRING([[--disable-md5]],[disable MD5 hashing support for Digest Authentication]) + [AS_HELP_STRING([[--enable-md5=TYPE]],[enable TYPE of MD5 hashing code (yes, no, builtin, tlslib) [yes if dauth enabled]]) ], [ - AS_VAR_IF([[enable_md5]],[["yes"]], + AS_VAR_IF([enable_md5],["internal"],[enable_md5='builtin']) + AS_VAR_IF([enable_md5],["built-in"],[enable_md5='builtin']) + AS_VAR_IF([enable_dauth],["yes"],[], [ - AS_VAR_IF([enable_dauth],["yes"],[], + AS_VAR_IF([enable_md5],["no"],[], [ - AC_MSG_WARN([The parameter --enable-md5 is ignored as Digest Authentication is disabled]) + AC_MSG_WARN([The parameter --enable-md5=${enable_md5} is ignored as Digest Authentication is disabled]) enable_md5='no' ] ) - ],[[enable_md5='no']] + ] ) ], [[enable_md5="${enable_dauth}"]] ) +AS_CASE([${enable_md5}],[yes|tlslib], + [ + AS_IF([test "x${enable_compact_code}" != "xno" || test "x$enable_md5" = "xtlslib"], + [ + AS_IF([test "x$enable_https" = "xyes"], + [ + AC_CACHE_CHECK([whether GnuTLS supports MD5 hashing],[mhd_cv_gnutls_md5], + [ + CFLAGS="${CFLAGS_ac} ${GNUTLS_CFLAGS} ${user_CFLAGS}" + CPPFLAGS="${CPPFLAGS_ac} ${MHD_TLS_LIB_CPPFLAGS} ${user_CPPFLAGS}" + CFLAGS="${CFLAGS_ac} ${MHD_TLS_LIB_CFLAGS} ${user_CFLAGS}" + LDFLAGS="${LDFLAGS_ac} ${MHD_TLS_LIB_LDFLAGS} ${user_LDFLAGS}" + save_LIBS="$LIBS" + LIBS="${MHD_TLS_LIBDEPS} ${LIBS}" + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM( + [[ +#include <gnutls/crypto.h> + ]], + [[ + gnutls_hash_hd_t hash_handle; + unsigned char digest[16]; + int exit_code; + + if (0 == gnutls_hash_init(&hash_handle, GNUTLS_DIG_MD5)) + { + if (0 == gnutls_hash(hash_handle, "", 1)) + { + gnutls_hash_output(hash_handle, digest); + if (0x93 == digest[0]) + exit_code = 0; + else + exit_code = 7; + } + else + exit_code = 5; + gnutls_hash_deinit(hash_handle, (void *)0); + } + else + exit_code = 2; + if (exit_code) + return exit_code; + ]] + ) + ], + [mhd_cv_gnutls_md5='yes'],[mhd_cv_gnutls_md5='no'] + ) + LIBS="${save_LIBS}" + CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" + CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" + CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" + LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}" + ] + ) + AS_VAR_IF([mhd_cv_gnutls_md5],["no"], + [ + AS_VAR_IF([enable_md5],["tlslib"], + [AC_MSG_FAILURE([TLS library MD5 implementation is not available])] + ) + enable_md5="builtin" + ], + [enable_md5="tlslib"] + ) + ], + [ + AS_VAR_IF([enable_md5],["tlslib"], + [AC_MSG_ERROR([HTTPS is not enabled, TLS library MD5 implementation cannot be used])] + ) + enable_md5="builtin" + ] + ) + ], + [ + enable_md5="builtin" + ] + ) + ] +) AC_MSG_CHECKING([[whether to support MD5]]) -AS_VAR_IF([[enable_md5]],[["yes"]], +AS_UNSET([enable_md5_MSG]) +AS_CASE([${enable_md5}], + [builtin],[enable_md5_MSG='yes, built-in'], + [tlslib],[enable_md5_MSG='yes, external (TLS library)'], + [no],[enable_md5_MSG='no'], + [yes],[AC_MSG_ERROR([configure internal error: unexpected variable value])], + [AC_MSG_ERROR([Unrecognized parameter --enable-md5=${enable_md5}])] +) +AS_IF([test "x${enable_md5}" = "xbuiltin" || test "x${enable_md5}" = "xtlslib" ], [ - AC_DEFINE([[MHD_MD5_SUPPORT]],[[1]], - [Define to 1 if libmicrohttpd is compiled with MD5 hashing support.]) + AC_DEFINE([[MHD_MD5_SUPPORT]],[[1]], + [Define to 1 if libmicrohttpd is compiled with MD5 hashing support.]) ] ) -AM_CONDITIONAL([ENABLE_MD5], [[test "x${enable_md5}" = "xyes"]]) -AC_MSG_RESULT([[${enable_md5}]]) +AS_IF([test "x${enable_md5}" = "xtlslib" ], + [ + AC_DEFINE([[MHD_MD5_TLSLIB]],[[1]], + [Define to 1 if libmicrohttpd is compiled with MD5 hashing by TLS library.]) + ] +) +AM_CONDITIONAL([ENABLE_MD5], [[test "x${enable_md5}" = "xbuiltin" || test "x${enable_md5}" = "xtlslib" ]]) +AM_CONDITIONAL([ENABLE_MD5_EXT], [[test "x${enable_md5}" = "xtlslib" ]]) +AC_MSG_RESULT([[${enable_md5_MSG}]]) # optional: SHA-256 support for Digest Auth. Enabled by default. AC_ARG_ENABLE([[sha256]], - [AS_HELP_STRING([[--disable-sha256]],[disable SHA-256 hashing support for Digest Authentication]) + [AS_HELP_STRING([[--enable-sha256=TYPE]],[enable TYPE of SHA-256 hashing code (yes, no, builtin, tlslib) [yes if dauth enabled]]) ], [ - AS_VAR_IF([[enable_sha256]],[["yes"]], + AS_VAR_IF([enable_sha256],["internal"],[enable_sha256='builtin']) + AS_VAR_IF([enable_sha256],["built-in"],[enable_sha256='builtin']) + AS_VAR_IF([enable_dauth],["yes"],[], [ - AS_VAR_IF([enable_dauth],["yes"],[], + AS_VAR_IF([enable_sha256],["no"],[], [ - AC_MSG_WARN([The parameter --enable-sha256 is ignored as Digest Authentication is disabled]) + AC_MSG_WARN([The parameter --enable-sha256=${enable_sha256} is ignored as Digest Authentication is disabled]) enable_sha256='no' ] ) - ],[[enable_sha256='no']] + ] ) ], [[enable_sha256="${enable_dauth}"]] ) +AS_CASE([${enable_sha256}],[yes|tlslib], + [ + AS_IF([test "x${enable_compact_code}" != "xno" || test "x$enable_sha256" = "xtlslib"], + [ + AS_IF([test "x$enable_https" = "xyes"], + [ + AC_CACHE_CHECK([whether GnuTLS supports sha256 hashing],[mhd_cv_gnutls_sha256], + [ + CFLAGS="${CFLAGS_ac} ${GNUTLS_CFLAGS} ${user_CFLAGS}" + CPPFLAGS="${CPPFLAGS_ac} ${MHD_TLS_LIB_CPPFLAGS} ${user_CPPFLAGS}" + CFLAGS="${CFLAGS_ac} ${MHD_TLS_LIB_CFLAGS} ${user_CFLAGS}" + LDFLAGS="${LDFLAGS_ac} ${MHD_TLS_LIB_LDFLAGS} ${user_LDFLAGS}" + save_LIBS="$LIBS" + LIBS="${MHD_TLS_LIBDEPS} ${LIBS}" + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM( + [[ +#include <gnutls/crypto.h> + ]], + [[ + gnutls_hash_hd_t hash_handle; + unsigned char digest[32]; + int exit_code; + + if (0 == gnutls_hash_init(&hash_handle, GNUTLS_DIG_SHA256)) + { + if (0 == gnutls_hash(hash_handle, "", 1)) + { + gnutls_hash_output(hash_handle, digest); + if (0x6e == digest[0]) + exit_code = 0; + else + exit_code = 7; + } + else + exit_code = 5; + gnutls_hash_deinit(hash_handle, (void *)0); + } + else + exit_code = 2; + if (exit_code) + return exit_code; + ]] + ) + ], + [mhd_cv_gnutls_sha256='yes'],[mhd_cv_gnutls_sha256='no'] + ) + LIBS="${save_LIBS}" + CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" + CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" + CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" + LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}" + ] + ) + AS_VAR_IF([mhd_cv_gnutls_sha256],["no"], + [ + AS_VAR_IF([enable_sha256],["tlslib"], + [AC_MSG_FAILURE([TLS library SHA-256 implementation is not available])] + ) + enable_sha256="builtin" + ], + [enable_sha256="tlslib"] + ) + ], + [ + AS_VAR_IF([enable_sha256],["tlslib"], + [AC_MSG_ERROR([HTTPS is not enabled, TLS library SHA-256 implementation cannot be used])] + ) + enable_sha256="builtin" + ] + ) + ], + [ + enable_sha256="builtin" + ] + ) + ] +) AC_MSG_CHECKING([[whether to support SHA-256]]) -AS_VAR_IF([[enable_sha256]],[["yes"]], +AS_UNSET([enable_sha256_MSG]) +AS_CASE([${enable_sha256}], + [builtin],[enable_sha256_MSG='yes, built-in'], + [tlslib],[enable_sha256_MSG='yes, external (TLS library)'], + [no],[enable_sha256_MSG='no'], + [yes],[AC_MSG_ERROR([configure internal error: unexpected variable value])], + [AC_MSG_ERROR([Unrecognized parameter --enable-sha256=${enable_sha256}])] +) +AS_IF([test "x${enable_sha256}" = "xbuiltin" || test "x${enable_sha256}" = "xtlslib" ], [ - AC_DEFINE([[MHD_SHA256_SUPPORT]],[[1]], - [Define to 1 if libmicrohttpd is compiled with SHA-256 hashing support.]) + AC_DEFINE([[MHD_SHA256_SUPPORT]],[[1]], + [Define to 1 if libmicrohttpd is compiled with SHA-256 hashing support.]) ] ) -AM_CONDITIONAL([ENABLE_SHA256], [[test "x${enable_sha256}" = "xyes"]]) -AC_MSG_RESULT([[${enable_sha256}]]) +AS_IF([test "x${enable_sha256}" = "xtlslib" ], + [ + AC_DEFINE([[MHD_SHA256_TLSLIB]],[[1]], + [Define to 1 if libmicrohttpd is compiled with SHA-256 hashing by TLS library.]) + ] +) +AM_CONDITIONAL([ENABLE_SHA256], [[test "x${enable_sha256}" = "xbuiltin" || test "x${enable_sha256}" = "xtlslib" ]]) +AM_CONDITIONAL([ENABLE_SHA256_EXT], [[test "x${enable_sha256}" = "xtlslib" ]]) +AC_MSG_RESULT([[${enable_sha256_MSG}]]) # optional: SHA-512/256 support for Digest Auth. Enabled by default. AC_ARG_ENABLE([[sha512-256]], @@ -3154,6 +3346,12 @@ AC_ARG_ENABLE([[sha512-256]], ], [[enable_sha512_256="${enable_dauth}"]] ) AC_MSG_CHECKING([[whether to support SHA-512/256]]) +AS_UNSET([enable_sha512_256_MSG]) +AS_CASE([${enable_sha512_256}], + [yes],[enable_sha512_256_MSG='yes, built-in'], + [no],[enable_sha512_256_MSG='no'], + [AC_MSG_ERROR([Unrecognized parameter --enable-sha512-256=${enable_sha512_256}])] +) AS_VAR_IF([[enable_sha512_256]],[["yes"]], [ AC_DEFINE([[MHD_SHA512_256_SUPPORT]],[[1]], @@ -3161,11 +3359,11 @@ AS_VAR_IF([[enable_sha512_256]],[["yes"]], ] ) AM_CONDITIONAL([ENABLE_SHA512_256], [[test "x${enable_sha512_256}" = "xyes"]]) -AC_MSG_RESULT([[${enable_sha512_256}]]) +AC_MSG_RESULT([[${enable_sha512_256_MSG}]]) AS_IF([test "x$enable_dauth" != "xno"], [ - AS_IF([test "x${enable_md5}" != "xyes" && test "x${enable_sha256}" != "xyes" && test "x${enable_sha512_256}" != "xyes"], + AS_IF([test "x${enable_md5}" = "xno" && test "x${enable_sha256}" = "xno" && test "x${enable_sha512_256}" != "xyes"], [AC_MSG_ERROR([At least one hashing algorithm must be enabled if Digest Auth is enabled])] ) ] @@ -4052,9 +4250,9 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} Configuration Summary: Messages: ${enable_messages} Basic auth.: ${enable_bauth} Digest auth.: ${enable_dauth} - MD5: ${enable_md5} - SHA-256: ${enable_sha256} - SHA-512/256: ${enable_sha512_256} + MD5: ${enable_md5_MSG} + SHA-256: ${enable_sha256_MSG} + SHA-512/256: ${enable_sha512_256_MSG} HTTP "Upgrade": ${enable_httpupgrade} Cookie parsing: ${enable_cookie} Postproc: ${enable_postprocessor} @@ -4075,8 +4273,8 @@ AS_IF([test "x$enable_https" = "xyes"], AS_IF([test "x$enable_bauth" != "xyes" || \ test "x$enable_dauth" != "xyes" || \ - test "x${enable_md5}" != "xyes" || \ - test "x${enable_sha256}" != "xyes" || \ + test "x${enable_md5}" = "xno" || \ + test "x${enable_sha256}" = "xno" || \ test "x${enable_sha512_256}" != "xyes" || \ test "x$enable_httpupgrade" != "xyes" || \ test "x$enable_cookie" != "xyes" || \ diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -96,7 +96,7 @@ extern "C" * they are parsed as decimal numbers. * Example: 0x01093001 = 1.9.30-1. */ -#define MHD_VERSION 0x00097539 +#define MHD_VERSION 0x00097540 /* If generic headers don't work on your platform, include headers which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t', @@ -4691,8 +4691,10 @@ enum MHD_DigestAuthMultiAlgo3 * upon return * @param bin_buf_size the size of the @a userhash_bin buffer, must be * at least #MHD_digest_get_hash_size(algo3) bytes long - * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or - * if @a algo3 algorithm is not supported. + * @return MHD_YES on success, + * MHD_NO if @a bin_buf_size is too small or if @a algo3 algorithm is + * not supported (or external error has occurred, + * see #MHD_FEATURE_EXTERN_HASH) * @note Available since #MHD_VERSION 0x00097535 * @ingroup authentication */ @@ -4736,8 +4738,10 @@ MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 algo3, * userhash string * @param bin_buf_size the size of the @a userhash_bin buffer, must be * at least #MHD_digest_get_hash_size(algo3)*2+1 chars long - * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or - * if @a algo3 algorithm is not supported. + * @return MHD_YES on success, + * MHD_NO if @a bin_buf_size is too small or if @a algo3 algorithm is + * not supported (or external error has occurred, + * see #MHD_FEATURE_EXTERN_HASH). * @note Available since #MHD_VERSION 0x00097535 * @ingroup authentication */ @@ -5287,8 +5291,10 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection, * userdigest upon return * @param userdigest_bin the size of the @a userdigest_bin buffer, must be * at least #MHD_digest_get_hash_size(algo3) bytes long - * @return MHD_YES on success, MHD_NO if @a userdigest_bin is too small or - * if @a algo3 algorithm is not supported. + * @return MHD_YES on success, + * MHD_NO if @a userdigest_bin is too small or if @a algo3 algorithm is + * not supported (or external error has occurred, + * see #MHD_FEATURE_EXTERN_HASH). * @sa #MHD_digest_auth_check_digest3() * @note Available since #MHD_VERSION 0x00097535 * @ingroup authentication @@ -5651,6 +5657,9 @@ MHD_queue_auth_fail_response (struct MHD_Connection *connection, int signal_stale); +/* ********************* Basic Authentication functions *************** */ + + /** * Information decoded from Basic Authentication client's header. * @@ -6138,7 +6147,21 @@ enum MHD_FEATURE * module is built. * @note Available since #MHD_VERSION 0x00097536 */ - MHD_FEATURE_DIGEST_AUTH_USERHASH = 31 + MHD_FEATURE_DIGEST_AUTH_USERHASH = 31, + + /** + * Get whether any of hashing algorithms is implemented by external + * function (like TLS library) and may fail due to external conditions, + * like "out-of-memory". + * + * If result is #MHD_YES then functions which use hash calculations + * like #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_check3() and others + * potentially may fail even with valid input because of out-of-memory error + * or crypto accelerator device failure, however in practice such fails are + * unlikely. + * @note Available since #MHD_VERSION 0x00097540 + */ + MHD_FEATURE_EXTERN_HASH = 32 }; diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am @@ -171,11 +171,25 @@ libmicrohttpd_la_SOURCES += \ mhd_bithelpers.h mhd_byteorder.h mhd_align.h if ENABLE_MD5 libmicrohttpd_la_SOURCES += \ + mhd_md5_wrap.h +if ! ENABLE_MD5_EXT +libmicrohttpd_la_SOURCES += \ md5.c md5.h +else +libmicrohttpd_la_SOURCES += \ + md5_ext.c md5_ext.h +endif endif if ENABLE_SHA256 libmicrohttpd_la_SOURCES += \ + mhd_sha256_wrap.h +if ! ENABLE_SHA256_EXT +libmicrohttpd_la_SOURCES += \ sha256.c sha256.h +else +libmicrohttpd_la_SOURCES += \ + sha256_ext.c sha256_ext.h +endif endif if ENABLE_SHA512_256 libmicrohttpd_la_SOURCES += \ @@ -462,12 +476,40 @@ test_http_reasons_SOURCES = \ reason_phrase.c mhd_str.c mhd_str.h test_md5_SOURCES = \ - test_md5.c test_helpers.h \ + test_md5.c test_helpers.h mhd_md5_wrap.h ../include/mhd_options.h +if ! ENABLE_MD5_EXT +test_md5_SOURCES += \ md5.c md5.h mhd_bithelpers.h mhd_byteorder.h mhd_align.h +test_md5_CPPFLAGS = $(AM_CPPFLAGS) +test_md5_CFLAGS = $(AM_CFLAGS) +test_md5_LDFLAGS = $(AM_LDFLAGS) +test_md5_LDADD = $(LDADD) +else +test_md5_SOURCES += \ + md5_ext.c md5_ext.h +test_md5_CPPFLAGS = $(AM_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) +test_md5_CFLAGS = $(AM_CFLAGS) $(MHD_TLS_LIB_CFLAGS) +test_md5_LDFLAGS = $(AM_LDFLAGS) $(MHD_TLS_LIB_LDFLAGS) +test_md5_LDADD = $(MHD_TLS_LIBDEPS) $(LDADD) +endif test_sha256_SOURCES = \ - test_sha256.c test_helpers.h \ + test_sha256.c test_helpers.h mhd_sha256_wrap.h ../include/mhd_options.h +if ! ENABLE_SHA256_EXT +test_sha256_SOURCES += \ sha256.c sha256.h mhd_bithelpers.h mhd_byteorder.h mhd_align.h +test_sha256_CPPFLAGS = $(AM_CPPFLAGS) +test_sha256_CFLAGS = $(AM_CFLAGS) +test_sha256_LDFLAGS = $(AM_LDFLAGS) +test_sha256_LDADD = $(LDADD) +else +test_sha256_SOURCES += \ + sha256_ext.c sha256_ext.h +test_sha256_CPPFLAGS = $(AM_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) +test_sha256_CFLAGS = $(AM_CFLAGS) $(MHD_TLS_LIB_CFLAGS) +test_sha256_LDFLAGS = $(AM_LDFLAGS) $(MHD_TLS_LIB_LDFLAGS) +test_sha256_LDADD = $(MHD_TLS_LIBDEPS) $(LDADD) +endif test_sha512_256_SOURCES = \ test_sha512_256.c test_helpers.h \ diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -8465,6 +8465,12 @@ MHD_is_feature_supported (enum MHD_FEATURE feature) #else return MHD_NO; #endif + case MHD_FEATURE_EXTERN_HASH: +#if defined(MHD_MD5_TLSLIB) || defined(MHD_SHA256_TLSLIB) + return MHD_YES; +#else + return MHD_NO; +#endif default: break; diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c @@ -34,10 +34,10 @@ #include "internal.h" #include "response.h" #ifdef MHD_MD5_SUPPORT -# include "md5.h" +# include "mhd_md5_wrap.h" #endif /* MHD_MD5_SUPPORT */ #ifdef MHD_SHA256_SUPPORT -# include "sha256.h" +# include "mhd_sha256_wrap.h" #endif /* MHD_SHA256_SUPPORT */ #ifdef MHD_SHA512_256_SUPPORT # include "sha512_256.h" @@ -304,10 +304,10 @@ MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3) union DigestCtx { #ifdef MHD_MD5_SUPPORT - struct Md5Ctx md5_ctx; + struct Md5CtxWr md5_ctx; #endif /* MHD_MD5_SUPPORT */ #ifdef MHD_SHA256_SUPPORT - struct Sha256Ctx sha256_ctx; + struct Sha256CtxWr sha256_ctx; #endif /* MHD_SHA256_SUPPORT */ #ifdef MHD_SHA512_256_SUPPORT struct Sha512_256Ctx sha512_256_ctx; @@ -334,9 +334,10 @@ struct DigestAlgorithm * Buffer for hex-print of the final digest. */ #if _DEBUG - bool setup; /**< The structure was set-up */ - bool inited; /**< The calculation was initialised */ - bool hashing; /**< Some data has been hashed, but digest is not yet finalised */ + bool uninitialised; /**< The structure has been not set-up */ + bool algo_selected; /**< The algorithm has been selected */ + bool ready_for_hashing; /**< The structure is ready to hash data */ + bool hashing; /**< Some data has been hashed, but the digest has not finalised yet */ #endif /* _DEBUG */ }; @@ -349,7 +350,8 @@ struct DigestAlgorithm _MHD_static_inline unsigned int digest_get_size (struct DigestAlgorithm *da) { - mhd_assert (da->setup); + mhd_assert (! da->uninitialised); + mhd_assert (da->algo_selected); #ifdef MHD_MD5_SUPPORT if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) return MD5_DIGEST_SIZE; @@ -367,90 +369,136 @@ digest_get_size (struct DigestAlgorithm *da) } +#if defined(MHD_MD5_HAS_DEINIT) || defined(MHD_SHA256_HAS_DEINIT) /** - * Set-up the digest calculation structure. - * @param da the structure to set-up - * @param algo the algorithm to use for digest calculation - * @return boolean 'true' if successfully set-up, - * false otherwise. + * Indicates presence of digest_deinit() function */ -_MHD_static_inline bool -digest_setup (struct DigestAlgorithm *da, - enum MHD_DigestBaseAlgo algo) +#define MHD_DIGEST_HAS_DEINIT 1 +#endif /* MHD_MD5_HAS_DEINIT || MHD_SHA256_HAS_DEINIT */ + +#ifdef MHD_DIGEST_HAS_DEINIT +/** + * Zero-initialise digest calculation structure. + * + * This initialisation is enough to safely call #digest_deinit() only. + * To make any real digest calculation, #digest_setup_and_init() must be called. + * @param da the digest calculation + */ +_MHD_static_inline void +digest_setup_zero (struct DigestAlgorithm *da) { #ifdef _DEBUG - da->setup = false; - da->inited = false; + da->uninitialised = false; + da->algo_selected = false; + da->ready_for_hashing = false; da->hashing = false; #endif /* _DEBUG */ - if (false -#ifdef MHD_MD5_SUPPORT - || (MHD_DIGEST_BASE_ALGO_MD5 == algo) -#endif /* MHD_MD5_SUPPORT */ -#ifdef MHD_SHA256_SUPPORT - || (MHD_DIGEST_BASE_ALGO_SHA256 == algo) -#endif /* MHD_SHA256_SUPPORT */ -#ifdef MHD_SHA512_256_SUPPORT - || (MHD_DIGEST_BASE_ALGO_SHA512_256 == algo) -#endif /* MHD_SHA512_256_SUPPORT */ - ) - { - da->algo = algo; -#ifdef _DEBUG - da->setup = true; -#endif /* _DEBUG */ - return true; - } - return false; /* Bad or unsupported algorithm */ + da->algo = MHD_DIGEST_BASE_ALGO_INVALID; } /** - * Initialise/reset the digest calculation structure. - * @param da the structure to initialise/reset + * De-initialise digest calculation structure. + * + * This function must be called if #digest_setup_and_init() was called for + * @a da. + * This function must not be called if @a da was not initialised by + * #digest_setup_and_init() or by #digest_setup_zero(). + * @param da the digest calculation */ _MHD_static_inline void -digest_init (struct DigestAlgorithm *da) +digest_deinit (struct DigestAlgorithm *da) { - mhd_assert (da->setup); - mhd_assert (! da->hashing); - mhd_assert (! da->inited); -#ifdef MHD_MD5_SUPPORT + mhd_assert (! da->uninitialised); +#ifdef MHD_MD5_HAS_DEINIT if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) + MHD_MD5_deinit (&da->ctx.md5_ctx); + else +#endif /* MHD_MD5_HAS_DEINIT */ +#ifdef MHD_SHA256_HAS_DEINIT + if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) + MHD_SHA256_deinit (&da->ctx.sha256_ctx); + else +#endif /* MHD_SHA256_HAS_DEINIT */ + (void) 0; + digest_setup_zero (da); +} + + +#else /* ! MHD_DIGEST_HAS_DEINIT */ +#define digest_setup_zero(da) (void)0 +#define digest_deinit(da) (void)0 +#endif /* ! MHD_DIGEST_HAS_DEINIT */ + + +/** + * Set-up the digest calculation structure and initialise with initial values. + * + * If @a da was successfully initialised, #digest_deinit() must be called + * after finishing using of the @a da. + * + * This function must not be called more than once for any @a da. + * + * @param da the structure to set-up + * @param algo the algorithm to use for digest calculation + * @return boolean 'true' if successfully set-up, + * false otherwise. + */ +_MHD_static_inline bool +digest_init_one_time (struct DigestAlgorithm *da, + enum MHD_DigestBaseAlgo algo) +{ +#ifdef _DEBUG + da->uninitialised = false; + da->algo_selected = false; + da->ready_for_hashing = false; + da->hashing = false; +#endif /* _DEBUG */ +#ifdef MHD_MD5_SUPPORT + if (MHD_DIGEST_BASE_ALGO_MD5 == algo) { - MHD_MD5_init (&da->ctx.md5_ctx); + da->algo = MHD_DIGEST_BASE_ALGO_MD5; #ifdef _DEBUG - da->inited = true; + da->algo_selected = true; #endif + MHD_MD5_init_one_time (&da->ctx.md5_ctx); +#ifdef _DEBUG + da->ready_for_hashing = true; +#endif + return true; } - else #endif /* MHD_MD5_SUPPORT */ #ifdef MHD_SHA256_SUPPORT - if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) + if (MHD_DIGEST_BASE_ALGO_SHA256 == algo) { - MHD_SHA256_init (&da->ctx.sha256_ctx); + da->algo = MHD_DIGEST_BASE_ALGO_SHA256; #ifdef _DEBUG - da->inited = true; + da->algo_selected = true; #endif + MHD_SHA256_init_one_time (&da->ctx.sha256_ctx); +#ifdef _DEBUG + da->ready_for_hashing = true; +#endif + return true; } - else #endif /* MHD_SHA256_SUPPORT */ #ifdef MHD_SHA512_256_SUPPORT - if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo) + if (MHD_DIGEST_BASE_ALGO_SHA512_256 == algo) { - MHD_SHA512_256_init (&da->ctx.sha512_256_ctx); + da->algo = MHD_DIGEST_BASE_ALGO_SHA512_256; #ifdef _DEBUG - da->inited = true; + da->algo_selected = true; #endif - } - else -#endif /* MHD_SHA512_256_SUPPORT */ - { + MHD_SHA512_256_init (&da->ctx.sha512_256_ctx); #ifdef _DEBUG - da->inited = false; + da->ready_for_hashing = true; #endif - mhd_assert (0); /* Bad algorithm */ + return true; } +#endif /* MHD_SHA512_256_SUPPORT */ + + da->algo = MHD_DIGEST_BASE_ALGO_INVALID; + return false; /* Unsupported or bad algorithm */ } @@ -465,7 +513,9 @@ digest_update (struct DigestAlgorithm *da, const void *data, size_t length) { - mhd_assert (da->inited); + mhd_assert (! da->uninitialised); + mhd_assert (da->algo_selected); + mhd_assert (da->ready_for_hashing); #ifdef MHD_MD5_SUPPORT if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) MHD_MD5_update (&da->ctx.md5_ctx, (const uint8_t *) data, length); @@ -525,30 +575,160 @@ digest_update_with_colon (struct DigestAlgorithm *da) _MHD_static_inline void digest_calc_hash (struct DigestAlgorithm *da, uint8_t *digest) { - mhd_assert (da->inited); + mhd_assert (! da->uninitialised); + mhd_assert (da->algo_selected); + mhd_assert (da->ready_for_hashing); #ifdef MHD_MD5_SUPPORT if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) + { +#ifdef MHD_MD5_HAS_FINISH MHD_MD5_finish (&da->ctx.md5_ctx, digest); +#ifdef _DEBUG + da->ready_for_hashing = false; +#endif /* _DEBUG */ +#else /* ! MHD_MD5_HAS_FINISH */ + MHD_MD5_finish_reset (&da->ctx.md5_ctx, digest); +#ifdef _DEBUG + da->ready_for_hashing = true; +#endif /* _DEBUG */ +#endif /* ! MHD_MD5_HAS_FINISH */ + } else #endif /* MHD_MD5_SUPPORT */ #ifdef MHD_SHA256_SUPPORT if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) + { +#ifdef MHD_SHA256_HAS_FINISH MHD_SHA256_finish (&da->ctx.sha256_ctx, digest); +#ifdef _DEBUG + da->ready_for_hashing = false; +#endif /* _DEBUG */ +#else /* ! MHD_SHA256_HAS_FINISH */ + MHD_SHA256_finish_reset (&da->ctx.sha256_ctx, digest); +#ifdef _DEBUG + da->ready_for_hashing = true; +#endif /* _DEBUG */ +#endif /* ! MHD_SHA256_HAS_FINISH */ + } else #endif /* MHD_SHA256_SUPPORT */ #ifdef MHD_SHA512_256_SUPPORT if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo) + { MHD_SHA512_256_finish (&da->ctx.sha512_256_ctx, digest); +#ifdef _DEBUG + da->ready_for_hashing = false; +#endif /* _DEBUG */ + } else #endif /* MHD_SHA512_256_SUPPORT */ - mhd_assert (0); /* May not happen */ + mhd_assert (0); /* Should not happen */ #ifdef _DEBUG da->hashing = false; - da->inited = false; +#endif /* _DEBUG */ +} + + +/** + * Reset the digest calculation structure. + * + * @param da the structure to reset + */ +_MHD_static_inline void +digest_reset (struct DigestAlgorithm *da) +{ + mhd_assert (! da->uninitialised); + mhd_assert (da->algo_selected); + mhd_assert (! da->hashing); +#ifdef MHD_MD5_SUPPORT + if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) + { +#ifdef MHD_MD5_HAS_FINISH + mhd_assert (! da->ready_for_hashing); +#else /* ! MHD_MD5_HAS_FINISH */ + mhd_assert (da->ready_for_hashing); +#endif /* ! MHD_MD5_HAS_FINISH */ + MHD_MD5_reset (&da->ctx.md5_ctx); +#ifdef _DEBUG + da->ready_for_hashing = true; +#endif /* _DEBUG */ + } + else +#endif /* MHD_MD5_SUPPORT */ +#ifdef MHD_SHA256_SUPPORT + if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) + { +#ifdef MHD_SHA256_HAS_FINISH + mhd_assert (! da->ready_for_hashing); +#else /* ! MHD_SHA256_HAS_FINISH */ + mhd_assert (da->ready_for_hashing); +#endif /* ! MHD_SHA256_HAS_FINISH */ + MHD_SHA256_reset (&da->ctx.sha256_ctx); +#ifdef _DEBUG + da->ready_for_hashing = true; +#endif /* _DEBUG */ + } + else +#endif /* MHD_SHA256_SUPPORT */ +#ifdef MHD_SHA512_256_SUPPORT + if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo) + { + mhd_assert (! da->ready_for_hashing); + MHD_SHA512_256_init (&da->ctx.sha512_256_ctx); +#ifdef _DEBUG + da->ready_for_hashing = true; +#endif + } + else +#endif /* MHD_SHA512_256_SUPPORT */ + { +#ifdef _DEBUG + da->ready_for_hashing = false; #endif + mhd_assert (0); /* May not happen, bad algorithm */ + } } +#if defined(MHD_MD5_HAS_EXT_ERROR) || defined(MHD_SHA256_HAS_EXT_ERROR) +/** + * Indicates that digest algorithm has external error status + */ +#define MHD_DIGEST_HAS_EXT_ERROR 1 +#endif /* MHD_MD5_HAS_EXT_ERROR || MHD_SHA256_HAS_EXT_ERROR */ + +#ifdef MHD_DIGEST_HAS_EXT_ERROR +/** + * Get external error code. + * + * When external digest calculation used, an error may occur during + * initialisation or hashing data. This function checks whether external + * error has been reported for digest calculation. + * @param da the digest calculation + * @return true if external error occurs + */ +_MHD_static_inline bool +digest_ext_error (struct DigestAlgorithm *da) +{ + mhd_assert (! da->uninitialised); + mhd_assert (da->algo_selected); +#ifdef MHD_MD5_HAS_EXT_ERROR + if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) + return 0 != da->ctx.md5_ctx.ext_error; +#endif /* MHD_MD5_HAS_EXT_ERROR */ +#ifdef MHD_SHA256_HAS_EXT_ERROR + if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) + return 0 != da->ctx.sha256_ctx.ext_error; +#endif /* MHD_MD5_HAS_EXT_ERROR */ + return false; +} + + +#else /* ! MHD_DIGEST_HAS_EXT_ERROR */ +#define digest_ext_error(da) (false) +#endif /* ! MHD_DIGEST_HAS_EXT_ERROR */ + + /** * Extract timestamp from the given nonce. * @param nonce the nonce to check @@ -1314,7 +1494,7 @@ calculate_nonce (uint64_t nonce_time, struct DigestAlgorithm *da, char *nonce) { - digest_init (da); + mhd_assert (! da->hashing); if (1) { /* Add the timestamp to the hash calculation */ @@ -1522,6 +1702,7 @@ calculate_add_nonce (struct MHD_Connection *const connection, const size_t nonce_size = NONCE_STD_LEN (digest_get_size (da)); bool ret; + mhd_assert (! da->hashing); mhd_assert (MAX_DIGEST_NONCE_LENGTH >= nonce_size); mhd_assert (0 != nonce_size); @@ -1541,6 +1722,11 @@ calculate_add_nonce (struct MHD_Connection *const connection, da, nonce); +#ifdef MHD_DIGEST_HAS_EXT_ERROR + if (digest_ext_error (da)) + return false; +#endif /* MHD_DIGEST_HAS_EXT_ERROR */ + if (0 == daemon->nonce_nc_size) return false; @@ -1589,6 +1775,7 @@ calculate_add_nonce_with_retry (struct MHD_Connection *const connection, { const uint64_t timestamp1 = MHD_monotonic_msec_counter (); const size_t realm_len = strlen (realm); + mhd_assert (! da->hashing); #ifdef HAVE_MESSAGES if (0 == MHD_get_master (connection->daemon)->digest_auth_rand_size) @@ -1612,6 +1799,10 @@ calculate_add_nonce_with_retry (struct MHD_Connection *const connection, const size_t digest_size = digest_get_size (da); char nonce2[NONCE_STD_LEN (MAX_DIGEST) + 1]; uint64_t timestamp2; +#ifdef MHD_DIGEST_HAS_EXT_ERROR + if (digest_ext_error (da)) + return false; /* No need to re-try */ +#endif /* MHD_DIGEST_HAS_EXT_ERROR */ if (0 == MHD_get_master (connection->daemon)->nonce_nc_size) return false; /* No need to re-try */ @@ -1652,6 +1843,7 @@ calculate_add_nonce_with_retry (struct MHD_Connection *const connection, if (timestamp1 == timestamp2) timestamp2 -= 2; /* Fallback value */ } + digest_reset (da); if (! calculate_add_nonce (connection, timestamp2, realm, realm_len, da, nonce2)) { @@ -1690,7 +1882,7 @@ calc_userdigest (struct DigestAlgorithm *da, const char *password, uint8_t *ha1_bin) { - digest_init (da); + mhd_assert (! da->hashing); digest_update (da, username, username_len); digest_update_with_colon (da); digest_update (da, realm, realm_len); @@ -1725,8 +1917,10 @@ calc_userdigest (struct DigestAlgorithm *da, * userdigest upon return * @param userdigest_bin the size of the @a userdigest_bin buffer, must be * at least #MHD_digest_get_hash_size(algo3) bytes long - * @return MHD_YES on success, MHD_NO if @a userdigest_bin is too small or - * if @a algo3 algorithm is not supported. + * @return MHD_YES on success, + * MHD_NO if @a userdigest_bin is too small or if @a algo3 algorithm is + * not supported (or external error has occurred, + * see #MHD_FEATURE_EXTERN_HASH). * @sa #MHD_digest_auth_check_digest3() * @note Available since #MHD_VERSION 0x00097535 * @ingroup authentication @@ -1740,18 +1934,31 @@ MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo3 algo3, size_t bin_buf_size) { struct DigestAlgorithm da; - if (! digest_setup (&da, get_base_digest_algo (algo3))) + enum MHD_Result ret; + if (! digest_init_one_time (&da, get_base_digest_algo (algo3))) return MHD_NO; + if (digest_get_size (&da) > bin_buf_size) - return MHD_NO; - calc_userdigest (&da, - username, - strlen (username), - realm, - strlen (realm), - password, - userdigest_bin); - return MHD_YES; + ret = MHD_NO; + else + { + calc_userdigest (&da, + username, + strlen (username), + realm, + strlen (realm), + password, + userdigest_bin); + ret = MHD_YES; + +#ifdef MHD_DIGEST_HAS_EXT_ERROR + if (digest_ext_error (&da)) + ret = MHD_NO; +#endif /* MHD_DIGEST_HAS_EXT_ERROR */ + } + digest_deinit (&da); + + return ret; } @@ -1775,7 +1982,7 @@ calc_userhash (struct DigestAlgorithm *da, uint8_t *digest_bin) { mhd_assert (NULL != username); - digest_init (da); + mhd_assert (! da->hashing); digest_update (da, username, username_len); digest_update_with_colon (da); digest_update (da, realm, realm_len); @@ -1815,8 +2022,10 @@ calc_userhash (struct DigestAlgorithm *da, * upon return * @param bin_buf_size the size of the @a userhash_bin buffer, must be * at least #MHD_digest_get_hash_size(algo3) bytes long - * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or - * if @a algo3 algorithm is not supported. + * @return MHD_YES on success, + * MHD_NO if @a bin_buf_size is too small or if @a algo3 algorithm is + * not supported (or external error has occurred, + * see #MHD_FEATURE_EXTERN_HASH) * @note Available since #MHD_VERSION 0x00097535 * @ingroup authentication */ @@ -1828,17 +2037,30 @@ MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 algo3, size_t bin_buf_size) { struct DigestAlgorithm da; - if (! digest_setup (&da, get_base_digest_algo (algo3))) + enum MHD_Result ret; + + if (! digest_init_one_time (&da, get_base_digest_algo (algo3))) return MHD_NO; if (digest_get_size (&da) > bin_buf_size) - return MHD_NO; - calc_userhash (&da, - username, - strlen (username), - realm, - strlen (realm), - userhash_bin); - return MHD_YES; + ret = MHD_NO; + else + { + calc_userhash (&da, + username, + strlen (username), + realm, + strlen (realm), + userhash_bin); + ret = MHD_YES; + +#ifdef MHD_DIGEST_HAS_EXT_ERROR + if (digest_ext_error (&da)) + ret = MHD_NO; +#endif /* MHD_DIGEST_HAS_EXT_ERROR */ + } + digest_deinit (&da); + + return ret; } @@ -1874,8 +2096,10 @@ MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 algo3, * userhash string * @param bin_buf_size the size of the @a userhash_bin buffer, must be * at least #MHD_digest_get_hash_size(algo3)*2+1 chars long - * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or - * if @a algo3 algorithm is not supported. + * @return MHD_YES on success, + * MHD_NO if @a bin_buf_size is too small or if @a algo3 algorithm is + * not supported (or external error has occurred, + * see #MHD_FEATURE_EXTERN_HASH). * @note Available since #MHD_VERSION 0x00097535 * @ingroup authentication */ @@ -2302,12 +2526,12 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, uint32_t max_nc, enum MHD_DigestAuthMultiQOP mqop, enum MHD_DigestAuthMultiAlgo3 malgo3, - char **pbuf) + char **pbuf, + struct DigestAlgorithm *da) { struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); enum MHD_DigestAuthAlgo3 c_algo; /**< Client's algorithm */ enum MHD_DigestAuthQOP c_qop; /**< Client's QOP */ - struct DigestAlgorithm da; unsigned int digest_size; uint8_t hash1_bin[MAX_DIGEST]; uint8_t hash2_bin[MAX_DIGEST]; @@ -2383,7 +2607,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, return MHD_DAUTH_WRONG_ALGO; } #endif /* ! MHD_SHA512_256_SUPPORT */ - if (! digest_setup (&da, get_base_digest_algo (c_algo))) + if (! digest_init_one_time (da, get_base_digest_algo (c_algo))) MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); /* Check 'mqop' value */ c_qop = params->qop; @@ -2407,7 +2631,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, "non-standard extension.\n")); #endif /* HAVE_MESSAGES */ - digest_size = digest_get_size (&da); + digest_size = digest_get_size (da); /* ** A quick check for presence of all required parameters ** */ @@ -2525,11 +2749,18 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, else { /* Userhash */ mhd_assert (NULL != params->username.value.str); - calc_userhash (&da, username, username_len, realm, realm_len, hash1_bin); + calc_userhash (da, username, username_len, realm, realm_len, hash1_bin); +#ifdef MHD_DIGEST_HAS_EXT_ERROR + if (digest_ext_error (da)) + return MHD_DAUTH_ERROR; +#endif /* MHD_DIGEST_HAS_EXT_ERROR */ mhd_assert (sizeof (tmp1) >= (2 * digest_size)); MHD_bin_to_hex (hash1_bin, digest_size, tmp1); if (! is_param_equal_caseless (&params->username, tmp1, 2 * digest_size)) return MHD_DAUTH_WRONG_USERNAME; + /* To simplify the logic, the digest is reset here instead of resetting + before the next hash calculation. */ + digest_reset (da); } /* 'username' valid */ @@ -2643,9 +2874,9 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, /* ** Build H(A2) and check URI match in the header and in the request ** */ /* Get 'uri' */ - digest_init (&da); - digest_update_str (&da, connection->rq.method); - digest_update_with_colon (&da); + mhd_assert (! da->hashing); + digest_update_str (da, connection->rq.method); + digest_update_with_colon (da); #if 0 /* TODO: add support for "auth-int" */ digest_update_str (da, hentity); @@ -2656,33 +2887,45 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, if (_MHD_UNQ_OK != unq_res) return MHD_DAUTH_ERROR; - digest_update (&da, unq_copy.str, unq_copy.len); + digest_update (da, unq_copy.str, unq_copy.len); /* The next check will modify copied URI string */ if (! check_uri_match (connection, unq_copy.str, unq_copy.len)) return MHD_DAUTH_WRONG_URI; - digest_calc_hash (&da, hash2_bin); + digest_calc_hash (da, hash2_bin); +#ifdef MHD_DIGEST_HAS_EXT_ERROR + /* Skip digest calculation external error check, the next one checks both */ +#endif /* MHD_DIGEST_HAS_EXT_ERROR */ /* Got H(A2) */ /* ** Build H(A1) ** */ if (NULL == userdigest) - calc_userdigest (&da, + { + mhd_assert (! da->hashing); + digest_reset (da); + calc_userdigest (da, username, username_len, realm, realm_len, password, hash1_bin); + } /* TODO: support '-sess' versions */ +#ifdef MHD_DIGEST_HAS_EXT_ERROR + if (digest_ext_error (da)) + return MHD_DAUTH_ERROR; +#endif /* MHD_DIGEST_HAS_EXT_ERROR */ /* Got H(A1) */ /* ** Check 'response' ** */ - digest_init (&da); + mhd_assert (! da->hashing); + digest_reset (da); /* Update digest with H(A1) */ mhd_assert (sizeof (tmp1) >= (digest_size * 2)); if (NULL == userdigest) MHD_bin_to_hex (hash1_bin, digest_size, tmp1); else MHD_bin_to_hex (userdigest, digest_size, tmp1); - digest_update (&da, (const uint8_t *) tmp1, digest_size * 2); + digest_update (da, (const uint8_t *) tmp1, digest_size * 2); /* H(A1) is not needed anymore, reuse the buffer. * Use hash1_bin for the client's 'response' decoded to binary form. */ @@ -2694,15 +2937,15 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, return MHD_DAUTH_RESPONSE_WRONG; /* Update digest with ':' */ - digest_update_with_colon (&da); + digest_update_with_colon (da); /* Update digest with 'nonce' text value */ unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size, &unquoted); if (_MHD_UNQ_OK != unq_res) return MHD_DAUTH_ERROR; - digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); + digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); /* Update digest with ':' */ - digest_update_with_colon (&da); + digest_update_with_colon (da); if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) { /* Update digest with 'nc' text value */ @@ -2710,33 +2953,37 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, &unquoted); if (_MHD_UNQ_OK != unq_res) return MHD_DAUTH_ERROR; - digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); + digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); /* Update digest with ':' */ - digest_update_with_colon (&da); + digest_update_with_colon (da); /* Update digest with 'cnonce' value */ unq_res = get_unquoted_param (&params->cnonce, tmp1, ptmp2, &tmp2_size, &unquoted); if (_MHD_UNQ_OK != unq_res) return MHD_DAUTH_ERROR; - digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); + digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); /* Update digest with ':' */ - digest_update_with_colon (&da); + digest_update_with_colon (da); /* Update digest with 'qop' value */ unq_res = get_unquoted_param (&params->qop_raw, tmp1, ptmp2, &tmp2_size, &unquoted); if (_MHD_UNQ_OK != unq_res) return MHD_DAUTH_ERROR; - digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); + digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); /* Update digest with ':' */ - digest_update_with_colon (&da); + digest_update_with_colon (da); } /* Update digest with H(A2) */ MHD_bin_to_hex (hash2_bin, digest_size, tmp1); - digest_update (&da, (const uint8_t *) tmp1, digest_size * 2); + digest_update (da, (const uint8_t *) tmp1, digest_size * 2); /* H(A2) is not needed anymore, reuse the buffer. * Use hash2_bin for the calculated response in binary form */ - digest_calc_hash (&da, hash2_bin); + digest_calc_hash (da, hash2_bin); +#ifdef MHD_DIGEST_HAS_EXT_ERROR + if (digest_ext_error (da)) + return MHD_DAUTH_ERROR; +#endif /* MHD_DIGEST_HAS_EXT_ERROR */ if (0 != memcmp (hash1_bin, hash2_bin, digest_size)) return MHD_DAUTH_RESPONSE_WRONG; @@ -2746,6 +2993,8 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, mhd_assert (sizeof(tmp1) >= (NONCE_STD_LEN (digest_size) + 1)); /* It was already checked that 'nonce' (including timestamp) was generated by MHD. */ + mhd_assert (! da->hashing); + digest_reset (da); calculate_nonce (nonce_time, connection->rq.http_mthd, connection->rq.method, @@ -2759,9 +3008,13 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, realm, realm_len, daemon->dauth_bind_type, - &da, + da, tmp1); +#ifdef MHD_DIGEST_HAS_EXT_ERROR + if (digest_ext_error (da)) + return MHD_DAUTH_ERROR; +#endif /* MHD_DIGEST_HAS_EXT_ERROR */ if (! is_param_equal (&params->nonce, tmp1, NONCE_STD_LEN (digest_size))) @@ -2816,13 +3069,16 @@ digest_auth_check_all (struct MHD_Connection *connection, { enum MHD_DigestAuthResult res; char *buf; + struct DigestAlgorithm da; buf = NULL; + digest_setup_zero (&da); res = digest_auth_check_all_inner (connection, realm, username, password, userdigest, nonce_timeout, max_nc, mqop, malgo3, - &buf); + &buf, &da); + digest_deinit (&da); if (NULL != buf) free (buf); @@ -3173,20 +3429,8 @@ MHD_digest_auth_check_digest (struct MHD_Connection *connection, /** - * Queues a response to request authentication from the client - * - * This function modifies provided @a response. The @a response must not be - * reused and should be destroyed (by #MHD_destroy_response()) after call of - * this function. - * - * If @a mqop allows both RFC 2069 (MHD_DIGEST_AUTH_QOP_NONE) and QOP with - * value, then response is formed like if MHD_DIGEST_AUTH_QOP_NONE bit was - * not set, because such response should be backward-compatible with RFC 2069. - * - * If @a mqop allows only MHD_DIGEST_AUTH_MULT_QOP_NONE, then the response is - * formed in strict accordance with RFC 2069 (no 'qop', no 'userhash', no - * 'charset'). For better compatibility with clients, it is recommended (but - * not required) to set @a domain to NULL in this mode. + * Internal version of #MHD_queue_auth_required_response3() to simplify + * cleanups. * * @param connection the MHD connection structure * @param realm the realm presented to the client @@ -3226,21 +3470,26 @@ MHD_digest_auth_check_digest (struct MHD_Connection *connection, * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is * added, indicating for the client that UTF-8 encoding * is preferred + * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is + * added, indicating for the client that UTF-8 encoding + * is preferred * @return #MHD_YES on success, #MHD_NO otherwise * @note Available since #MHD_VERSION 0x00097526 * @ingroup authentication */ -_MHD_EXTERN enum MHD_Result -MHD_queue_auth_required_response3 (struct MHD_Connection *connection, - const char *realm, - const char *opaque, - const char *domain, - struct MHD_Response *response, - int signal_stale, - enum MHD_DigestAuthMultiQOP mqop, - enum MHD_DigestAuthMultiAlgo3 malgo3, - int userhash_support, - int prefer_utf8) +static enum MHD_Result +queue_auth_required_response3_inner (struct MHD_Connection *connection, + const char *realm, + const char *opaque, + const char *domain, + struct MHD_Response *response, + int signal_stale, + enum MHD_DigestAuthMultiQOP mqop, + enum MHD_DigestAuthMultiAlgo3 malgo3, + int userhash_support, + int prefer_utf8, + char **buf_ptr, + struct DigestAlgorithm *da) { static const char prefix_realm[] = "realm=\""; static const char prefix_qop[] = "qop=\""; @@ -3258,7 +3507,6 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, size_t buf_size; char *buf; size_t p; /* The position in the buffer */ - struct DigestAlgorithm da; if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION)) { @@ -3302,7 +3550,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, (((unsigned int) mqop) & MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT)) MHD_PANIC (_ ("Wrong 'mqop' value, API violation")); - if (! digest_setup (&da, get_base_digest_algo (s_algo))) + if (! digest_init_one_time (da, get_base_digest_algo (s_algo))) MHD_PANIC (_ ("Wrong 'algo' value, API violation")); if (MHD_DIGEST_AUTH_MULT_QOP_NONE == mqop) @@ -3348,6 +3596,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, if ((NULL != memchr (realm, '\r', realm_len)) || (NULL != memchr (realm, '\n', realm_len))) return MHD_NO; + buf_size += realm_len * 2; /* Quoting may double the size */ /* 'qop="xxxx", ' */ if (MHD_DIGEST_AUTH_MULT_QOP_NONE != mqop) @@ -3379,7 +3628,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, } /* 'nonce="xxxx", ' */ buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */ - buf_size += NONCE_STD_LEN (digest_get_size (&da)); /* Escaping not needed */ + buf_size += NONCE_STD_LEN (digest_get_size (da)); /* Escaping not needed */ /* 'opaque="xxxx", ' */ if (NULL != opaque) { @@ -3388,6 +3637,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, if ((NULL != memchr (opaque, '\r', opaque_len)) || (NULL != memchr (opaque, '\n', opaque_len))) return MHD_NO; + buf_size += opaque_len * 2; /* Quoting may double the size */ } else @@ -3400,6 +3650,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, if ((NULL != memchr (domain, '\r', domain_len)) || (NULL != memchr (domain, '\n', domain_len))) return MHD_NO; + buf_size += domain_len * 2; /* Quoting may double the size */ } else @@ -3421,6 +3672,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, buf = malloc (buf_size); if (NULL == buf) return MHD_NO; + *buf_ptr = buf; /* Build the challenge string */ p = 0; @@ -3444,7 +3696,6 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, MHD_DLOG (connection->daemon, _ ("The 'realm' is too large after 'quoting'.\n")); #endif /* HAVE_MESSAGES */ - free (buf); return MHD_NO; } p += quoted_size; @@ -3507,9 +3758,20 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, memcpy (buf + p, prefix_nonce, MHD_STATICSTR_LEN_ (prefix_nonce)); p += MHD_STATICSTR_LEN_ (prefix_nonce); - mhd_assert ((buf_size - p) >= (NONCE_STD_LEN (digest_get_size (&da)))); - if (! calculate_add_nonce_with_retry (connection, realm, &da, buf + p)) + mhd_assert ((buf_size - p) >= (NONCE_STD_LEN (digest_get_size (da)))); + if (! calculate_add_nonce_with_retry (connection, realm, da, buf + p)) { +#ifdef MHD_DIGEST_HAS_EXT_ERROR + if (digest_ext_error (da)) + { +#ifdef HAVE_MESSAGES + MHD_DLOG (connection->daemon, + _ ("TLS library reported hash calculation error, nonce could " + "not be generated.\n")); +#endif /* HAVE_MESSAGES */ + return MHD_NO; + } +#endif /* MHD_DIGEST_HAS_EXT_ERROR */ #ifdef HAVE_MESSAGES MHD_DLOG (connection->daemon, _ ("Could not register nonce. Client's requests with this " @@ -3518,7 +3780,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, #endif /* HAVE_MESSAGES */ (void) 0; /* Mute compiler warning for builds without messages */ } - p += NONCE_STD_LEN (digest_get_size (&da)); + p += NONCE_STD_LEN (digest_get_size (da)); buf[p++] = '\"'; buf[p++] = ','; buf[p++] = ' '; @@ -3588,10 +3850,8 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, MHD_DLOG (connection->daemon, _ ("Failed to add Digest auth header.\n")); #endif /* HAVE_MESSAGES */ - free (buf); return MHD_NO; } - free (buf); return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response); } @@ -3600,6 +3860,101 @@ MHD_queue_auth_required_response3 (struct MHD_Connection *connection, /** * Queues a response to request authentication from the client * + * This function modifies provided @a response. The @a response must not be + * reused and should be destroyed (by #MHD_destroy_response()) after call of + * this function. + * + * If @a mqop allows both RFC 2069 (MHD_DIGEST_AUTH_QOP_NONE) and QOP with + * value, then response is formed like if MHD_DIGEST_AUTH_QOP_NONE bit was + * not set, because such response should be backward-compatible with RFC 2069. + * + * If @a mqop allows only MHD_DIGEST_AUTH_MULT_QOP_NONE, then the response is + * formed in strict accordance with RFC 2069 (no 'qop', no 'userhash', no + * 'charset'). For better compatibility with clients, it is recommended (but + * not required) to set @a domain to NULL in this mode. + * + * @param connection the MHD connection structure + * @param realm the realm presented to the client + * @param opaque the string for opaque value, can be NULL, but NULL is + * not recommended for better compatibility with clients; + * the recommended format is hex or Base64 encoded string + * @param domain the optional space-separated list of URIs for which the + * same authorisation could be used, URIs can be in form + * "path-absolute" (the path for the same host with initial slash) + * or in form "absolute-URI" (the full path with protocol), in + * any case client may assume that URI is in the same "protection + * space" if it starts with any of values specified here; + * could be NULL (clients typically assume that the same + * credentials could be used for any URI on the same host) + * @param response the reply to send; should contain the "access denied" + * body; note that this function sets the "WWW Authenticate" + * header and that the caller should not do this; + * the NULL is tolerated + * @param signal_stale set to #MHD_YES if the nonce is stale to add 'stale=true' + * to the authentication header, this instructs the client + * to retry immediately with the new nonce and the same + * credentials, without asking user for the new password + * @param mqop the QOP to use + * @param malgo3 digest algorithm to use, MHD selects; if several algorithms + * are allowed then MD5 is preferred (currently, may be changed + * in next versions) + * @param userhash_support if set to non-zero value (#MHD_YES) then support of + * userhash is indicated, the client may provide + * hash("username:realm") instead of username in + * clear text; + * note that clients are allowed to provide the username + * in cleartext even if this parameter set to non-zero; + * when userhash is used, application must be ready to + * identify users by provided userhash value instead of + * username; see #MHD_digest_auth_calc_userhash() and + * #MHD_digest_auth_calc_userhash_hex() + * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is + * added, indicating for the client that UTF-8 encoding + * is preferred + * @return #MHD_YES on success, #MHD_NO otherwise + * @note Available since #MHD_VERSION 0x00097526 + * @ingroup authentication + */ +_MHD_EXTERN enum MHD_Result +MHD_queue_auth_required_response3 (struct MHD_Connection *connection, + const char *realm, + const char *opaque, + const char *domain, + struct MHD_Response *response, + int signal_stale, + enum MHD_DigestAuthMultiQOP mqop, + enum MHD_DigestAuthMultiAlgo3 malgo3, + int userhash_support, + int prefer_utf8) +{ + struct DigestAlgorithm da; + char *buf_ptr; + enum MHD_Result ret; + + buf_ptr = NULL; + digest_setup_zero (&da); + ret = queue_auth_required_response3_inner (connection, + realm, + opaque, + domain, + response, + signal_stale, + mqop, + malgo3, + userhash_support, + prefer_utf8, + &buf_ptr, + &da); + digest_deinit (&da); + if (NULL != buf_ptr) + free (buf_ptr); + return ret; +} + + +/** + * Queues a response to request authentication from the client + * * @param connection The MHD connection structure * @param realm the realm presented to the client * @param opaque string to user for opaque value diff --git a/src/microhttpd/md5.h b/src/microhttpd/md5.h @@ -123,4 +123,9 @@ void MHD_MD5_finish (struct Md5Ctx *ctx, uint8_t digest[MD5_DIGEST_SIZE]); +/** + * Indicates that function MHD_MD5_finish() (without context reset) is available + */ +#define MHD_MD5_HAS_FINISH 1 + #endif /* MHD_MD5_H */ diff --git a/src/microhttpd/md5_ext.c b/src/microhttpd/md5_ext.c @@ -0,0 +1,95 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2022 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. + + This library 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 GNU libmicrohttpd. + If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file microhttpd/md5_ext.h + * @brief Wrapper for MD5 calculation performed by TLS library + * @author Karlson2k (Evgeny Grin) + */ + +#include <gnutls/crypto.h> +#include "md5_ext.h" +#include "mhd_assert.h" + + +/** + * Initialise structure for MD5 calculation, allocate resources. + * + * This function must not be called more than one time for @a ctx. + * + * @param ctx the calculation context + */ +void +MHD_MD5_init_one_time (struct Md5CtxExt *ctx) +{ + ctx->handle = NULL; + ctx->ext_error = gnutls_hash_init (&ctx->handle, GNUTLS_DIG_MD5); + if ((0 != ctx->ext_error) && (NULL != ctx->handle)) + { + gnutls_free (ctx->handle); + ctx->handle = NULL; + } + else + mhd_assert (NULL != ctx->handle); +} + + +/** + * Process portion of bytes. + * + * @param ctx the calculation context + * @param data bytes to add to hash + * @param length number of bytes in @a data + */ +void +MHD_MD5_update (struct Md5CtxExt *ctx, + const uint8_t *data, + size_t length) +{ + if (0 == ctx->ext_error) + ctx->ext_error = gnutls_hash (ctx->handle, data, length); +} + + +/** + * Finalise MD5 calculation, return digest, reset hash calculation. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #MD5_DIGEST_SIZE bytes + */ +void +MHD_MD5_finish_reset (struct Md5CtxExt *ctx, + uint8_t digest[MD5_DIGEST_SIZE]) +{ + if (0 == ctx->ext_error) + gnutls_hash_output (ctx->handle, digest); +} + + +/** + * Free allocated resources. + * + * @param ctx the calculation context + */ +void +MHD_MD5_deinit (struct Md5CtxExt *ctx) +{ + if (NULL != ctx->handle) + gnutls_hash_deinit (ctx->handle, NULL); +} diff --git a/src/microhttpd/md5_ext.h b/src/microhttpd/md5_ext.h @@ -0,0 +1,115 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2022 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. + + This library 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 GNU libmicrohttpd. + If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file microhttpd/md5_ext.h + * @brief Wrapper declarations for MD5 calculation performed by TLS library + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_MD5_EXT_H +#define MHD_MD5_EXT_H 1 + +#include "mhd_options.h" +#include <stdint.h> +#ifdef HAVE_STDDEF_H +#include <stddef.h> /* for size_t */ +#endif /* HAVE_STDDEF_H */ + +/** + * Size of MD5 resulting digest in bytes + * This is the final digest size, not intermediate hash. + */ +#define MD5_DIGEST_SIZE (16) + +/* Actual declaration is in GnuTLS lib header */ +struct hash_hd_st; + +/** + * Indicates that struct Md5CtxExt has 'ext_error' + */ +#define MHD_MD5_HAS_EXT_ERROR 1 + +/** + * MD5 calculation context + */ +struct Md5CtxExt +{ + struct hash_hd_st *handle; /**< Hash calculation handle */ + int ext_error; /**< Non-zero if external error occurs during init or hashing */ +}; + +/** + * Indicates that MHD_MD5_init_one_time() function is present. + */ +#define MHD_MD5_HAS_INIT_ONE_TIME 1 + +/** + * Initialise structure for MD5 calculation, allocate resources. + * + * This function must not be called more than one time for @a ctx. + * + * @param ctx the calculation context + */ +void +MHD_MD5_init_one_time (struct Md5CtxExt *ctx); + + +/** + * MD5 process portion of bytes. + * + * @param ctx the calculation context + * @param data bytes to add to hash + * @param length number of bytes in @a data + */ +void +MHD_MD5_update (struct Md5CtxExt *ctx, + const uint8_t *data, + size_t length); + + +/** + * Indicates that MHD_MD5_finish_reset() function is available + */ +#define MHD_MD5_HAS_FINISH_RESET 1 + +/** + * Finalise MD5 calculation, return digest, reset hash calculation. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #MD5_DIGEST_SIZE bytes + */ +void +MHD_MD5_finish_reset (struct Md5CtxExt *ctx, + uint8_t digest[MD5_DIGEST_SIZE]); + +/** + * Indicates that MHD_MD5_deinit() function is present + */ +#define MHD_MD5_HAS_DEINIT 1 + +/** + * Free allocated resources. + * + * @param ctx the calculation context + */ +void +MHD_MD5_deinit (struct Md5CtxExt *ctx); + +#endif /* MHD_MD5_EXT_H */ diff --git a/src/microhttpd/mhd_md5_wrap.h b/src/microhttpd/mhd_md5_wrap.h @@ -0,0 +1,98 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2022 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. + + This library 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 GNU libmicrohttpd. + If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file microhttpd/mhd_md5_wrap.h + * @brief Simple wrapper for selection of built-in/external MD5 implementation + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_MD5_WRAP_H +#define MHD_MD5_WRAP_H 1 + +#include "mhd_options.h" +#ifndef MHD_MD5_SUPPORT +#error This file must be used only when MD5 is enabled +#endif +#ifndef MHD_MD5_TLSLIB +#include "md5.h" +#else /* MHD_MD5_TLSLIB */ +#include "md5_ext.h" +#endif /* MHD_MD5_TLSLIB */ + +#ifndef MD5_DIGEST_SIZE +/** + * Size of MD5 resulting digest in bytes + * This is the final digest size, not intermediate hash. + */ +#define MD5_DIGEST_SIZE (16) +#endif /* ! MD5_DIGEST_SIZE */ + +#ifndef MD5_DIGEST_STRING_SIZE +/** + * Size of MD5 digest string in chars including termination NUL. + */ +#define MD5_DIGEST_STRING_SIZE ((MD5_DIGEST_SIZE) * 2 + 1) +#endif /* ! MD5_DIGEST_STRING_SIZE */ + +#ifndef MHD_MD5_TLSLIB +/** + * Universal ctx type mapped for chosen implementation + */ +#define Md5CtxWr Md5Ctx +#else /* MHD_MD5_TLSLIB */ +/** + * Universal ctx type mapped for chosen implementation + */ +#define Md5CtxWr Md5CtxExt +#endif /* MHD_MD5_TLSLIB */ + +#ifndef MHD_MD5_HAS_INIT_ONE_TIME +/** + * Setup and prepare ctx for hash calculation + */ +#define MHD_MD5_init_one_time(ctx) MHD_MD5_init(ctx) +#endif /* ! MHD_MD5_HAS_INIT_ONE_TIME */ + +#ifndef MHD_MD5_HAS_FINISH_RESET +/** + * Re-use the same ctx for the new hashing after digest calculated + */ +#define MHD_MD5_reset(ctx) MHD_MD5_init(ctx) +/** + * Finalise MD5 calculation, return digest, reset hash calculation. + */ +#define MHD_MD5_finish_reset(ctx,digest) MHD_MD5_finish(ctx,digest), \ + MHD_MD5_reset(ctx) + +#else /* MHD_MD5_HAS_FINISH_RESET */ +#define MHD_MD5_reset(ctx) (void)0 +#endif /* MHD_MD5_HAS_FINISH_RESET */ + +#ifndef MHD_MD5_HAS_DEINIT +#define MHD_MD5_deinit(ignore) (void)0 +#endif /* HAVE_MD5_DEINIT */ + +/* Sanity checks */ + +#if ! defined(MHD_MD5_HAS_FINISH_RESET) && ! defined(MHD_MD5_HAS_FINISH) +#error Required at least one of MHD_MD5_finish_reset(), MHD_MD5_finish_reset() +#endif /* ! MHD_MD5_HAS_FINISH_RESET && ! MHD_MD5_HAS_FINISH */ + +#endif /* MHD_MD5_WRAP_H */ diff --git a/src/microhttpd/mhd_sha256_wrap.h b/src/microhttpd/mhd_sha256_wrap.h @@ -0,0 +1,100 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2022 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. + + This library 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 GNU libmicrohttpd. + If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file microhttpd/mhd_sha256_wrap.h + * @brief Simple wrapper for selection of built-in/external SHA-256 + * implementation + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_SHA256_WRAP_H +#define MHD_SHA256_WRAP_H 1 + +#include "mhd_options.h" +#include "mhd_options.h" +#ifndef MHD_SHA256_SUPPORT +#error This file must be used only when SHA-256 is enabled +#endif +#ifndef MHD_SHA256_TLSLIB +#include "sha256.h" +#else /* MHD_SHA256_TLSLIB */ +#include "sha256_ext.h" +#endif /* MHD_SHA256_TLSLIB */ + +#ifndef SHA256_DIGEST_SIZE +/** + * Size of SHA-256 resulting digest in bytes + * This is the final digest size, not intermediate hash. + */ +#define SHA256_DIGEST_SIZE (32) +#endif /* ! SHA256_DIGEST_SIZE */ + +#ifndef SHA256_DIGEST_STRING_SIZE +/** + * Size of MD5 digest string in chars including termination NUL. + */ +#define SHA256_DIGEST_STRING_SIZE ((SHA256_DIGEST_SIZE) * 2 + 1) +#endif /* ! SHA256_DIGEST_STRING_SIZE */ + +#ifndef MHD_SHA256_TLSLIB +/** + * Universal ctx type mapped for chosen implementation + */ +#define Sha256CtxWr Sha256Ctx +#else /* MHD_SHA256_TLSLIB */ +/** + * Universal ctx type mapped for chosen implementation + */ +#define Sha256CtxWr Sha256CtxExt +#endif /* MHD_SHA256_TLSLIB */ + +#ifndef MHD_SHA256_HAS_INIT_ONE_TIME +/** + * Setup and prepare ctx for hash calculation + */ +#define MHD_SHA256_init_one_time(ctx) MHD_SHA256_init(ctx) +#endif /* ! MHD_SHA256_HAS_INIT_ONE_TIME */ + +#ifndef MHD_SHA256_HAS_FINISH_RESET +/** + * Re-use the same ctx for the new hashing after digest calculated + */ +#define MHD_SHA256_reset(ctx) MHD_SHA256_init(ctx) +/** + * Finalise MD5 calculation, return digest, reset hash calculation. + */ +#define MHD_SHA256_finish_reset(ctx,digest) MHD_SHA256_finish(ctx,digest), \ + MHD_SHA256_reset(ctx) + +#else /* MHD_SHA256_HAS_FINISH_RESET */ +#define MHD_SHA256_reset(ctx) (void)0 +#endif /* MHD_SHA256_HAS_FINISH_RESET */ + +#ifndef MHD_SHA256_HAS_DEINIT +#define MHD_SHA256_deinit(ignore) (void)0 +#endif /* HAVE_SHA256_DEINIT */ + +/* Sanity checks */ + +#if ! defined(MHD_SHA256_HAS_FINISH_RESET) && ! defined(MHD_SHA256_HAS_FINISH) +#error Required MHD_SHA256_finish_reset() or MHD_SHA256_finish_reset() +#endif /* ! MHD_SHA256_HAS_FINISH_RESET && ! MHD_SHA256_HAS_FINISH */ + +#endif /* MHD_SHA256_WRAP_H */ diff --git a/src/microhttpd/sha256.h b/src/microhttpd/sha256.h @@ -114,4 +114,9 @@ void MHD_SHA256_finish (struct Sha256Ctx *ctx, uint8_t digest[SHA256_DIGEST_SIZE]); +/** + * Indicates that function MHD_SHA256_finish() (without context reset) is available + */ +#define MHD_SHA256_HAS_FINISH 1 + #endif /* MHD_SHA256_H */ diff --git a/src/microhttpd/sha256_ext.c b/src/microhttpd/sha256_ext.c @@ -0,0 +1,95 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2022 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. + + This library 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 GNU libmicrohttpd. + If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file microhttpd/sha256_ext.h + * @brief Wrapper for SHA-256 calculation performed by TLS library + * @author Karlson2k (Evgeny Grin) + */ + +#include <gnutls/crypto.h> +#include "sha256_ext.h" +#include "mhd_assert.h" + + +/** + * Initialise structure for SHA-256 calculation, allocate resources. + * + * This function must not be called more than one time for @a ctx. + * + * @param ctx the calculation context + */ +void +MHD_SHA256_init_one_time (struct Sha256CtxExt *ctx) +{ + ctx->handle = NULL; + ctx->ext_error = gnutls_hash_init (&ctx->handle, GNUTLS_DIG_SHA256); + if ((0 != ctx->ext_error) && (NULL != ctx->handle)) + { + gnutls_free (ctx->handle); + ctx->handle = NULL; + } + else + mhd_assert (NULL != ctx->handle); +} + + +/** + * Process portion of bytes. + * + * @param ctx the calculation context + * @param data bytes to add to hash + * @param length number of bytes in @a data + */ +void +MHD_SHA256_update (struct Sha256CtxExt *ctx, + const uint8_t *data, + size_t length) +{ + if (0 == ctx->ext_error) + ctx->ext_error = gnutls_hash (ctx->handle, data, length); +} + + +/** + * Finalise SHA-256 calculation, return digest, reset hash calculation. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #SHA256_DIGEST_SIZE bytes + */ +void +MHD_SHA256_finish_reset (struct Sha256CtxExt *ctx, + uint8_t digest[SHA256_DIGEST_SIZE]) +{ + if (0 == ctx->ext_error) + gnutls_hash_output (ctx->handle, digest); +} + + +/** + * Free allocated resources. + * + * @param ctx the calculation context + */ +void +MHD_SHA256_deinit (struct Sha256CtxExt *ctx) +{ + if (NULL != ctx->handle) + gnutls_hash_deinit (ctx->handle, NULL); +} diff --git a/src/microhttpd/sha256_ext.h b/src/microhttpd/sha256_ext.h @@ -0,0 +1,115 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2022 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. + + This library 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 GNU libmicrohttpd. + If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file microhttpd/sha256_ext.h + * @brief Wrapper declarations for SHA-256 calculation performed by TLS library + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_SHA256_EXT_H +#define MHD_SHA256_EXT_H 1 + +#include "mhd_options.h" +#include <stdint.h> +#ifdef HAVE_STDDEF_H +#include <stddef.h> /* for size_t */ +#endif /* HAVE_STDDEF_H */ + +/** + * Size of SHA-256 resulting digest in bytes + * This is the final digest size, not intermediate hash. + */ +#define SHA256_DIGEST_SIZE (32) + +/* Actual declaration is in GnuTLS lib header */ +struct hash_hd_st; + +/** + * Indicates that struct Sha256CtxExt has 'ext_error' + */ +#define MHD_SHA256_HAS_EXT_ERROR 1 + +/** + * SHA-256 calculation context + */ +struct Sha256CtxExt +{ + struct hash_hd_st *handle; /**< Hash calculation handle */ + int ext_error; /**< Non-zero if external error occurs during init or hashing */ +}; + +/** + * Indicates that MHD_SHA256_init_one_time() function is present. + */ +#define MHD_SHA256_HAS_INIT_ONE_TIME 1 + +/** + * Initialise structure for SHA-256 calculation, allocate resources. + * + * This function must not be called more than one time for @a ctx. + * + * @param ctx the calculation context + */ +void +MHD_SHA256_init_one_time (struct Sha256CtxExt *ctx); + + +/** + * SHA-256 process portion of bytes. + * + * @param ctx the calculation context + * @param data bytes to add to hash + * @param length number of bytes in @a data + */ +void +MHD_SHA256_update (struct Sha256CtxExt *ctx, + const uint8_t *data, + size_t length); + + +/** + * Indicates that MHD_SHA256_finish_reset() function is available + */ +#define MHD_SHA256_HAS_FINISH_RESET 1 + +/** + * Finalise SHA-256 calculation, return digest, reset hash calculation. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #SHA256_DIGEST_SIZE bytes + */ +void +MHD_SHA256_finish_reset (struct Sha256CtxExt *ctx, + uint8_t digest[SHA256_DIGEST_SIZE]); + +/** + * Indicates that MHD_SHA256_deinit() function is present + */ +#define MHD_SHA256_HAS_DEINIT 1 + +/** + * Free allocated resources. + * + * @param ctx the calculation context + */ +void +MHD_SHA256_deinit (struct Sha256CtxExt *ctx); + +#endif /* MHD_SHA256_EXT_H */ diff --git a/src/microhttpd/test_dauth_userdigest.c b/src/microhttpd/test_dauth_userdigest.c @@ -23,12 +23,19 @@ * @author Karlson2k (Evgeny Grin) */ +#include "mhd_options.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include "microhttpd.h" #include "test_helpers.h" +#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \ + (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB)) +#define NEED_GCRYP_INIT 1 +#include <gcrypt.h> +#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */ + static int verbose = 1; /* verbose level (0-1)*/ /* Declarations and data */ @@ -619,6 +626,13 @@ main (int argc, char *argv[]) if (has_param (argc, argv, "-s") || has_param (argc, argv, "--silent")) verbose = 0; +#ifdef NEED_GCRYP_INIT + gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); +#ifdef GCRYCTL_INITIALIZATION_FINISHED + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); +#endif /* GCRYCTL_INITIALIZATION_FINISHED */ +#endif /* NEED_GCRYP_INIT */ + if (MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5)) num_failed += test_md5 (); num_failed += test_md5_failure (); diff --git a/src/microhttpd/test_dauth_userhash.c b/src/microhttpd/test_dauth_userhash.c @@ -26,9 +26,16 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include "mhd_options.h" #include "microhttpd.h" #include "test_helpers.h" +#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \ + (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB)) +#define NEED_GCRYP_INIT 1 +#include <gcrypt.h> +#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */ + static int verbose = 1; /* verbose level (0-1)*/ /* Declarations and data */ @@ -748,6 +755,13 @@ main (int argc, char *argv[]) if (has_param (argc, argv, "-s") || has_param (argc, argv, "--silent")) verbose = 0; +#ifdef NEED_GCRYP_INIT + gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); +#ifdef GCRYCTL_INITIALIZATION_FINISHED + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); +#endif /* GCRYCTL_INITIALIZATION_FINISHED */ +#endif /* NEED_GCRYP_INIT */ + if (MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5)) num_failed += test_md5 (); num_failed += test_md5_failure (); diff --git a/src/microhttpd/test_md5.c b/src/microhttpd/test_md5.c @@ -24,11 +24,16 @@ */ #include "mhd_options.h" -#include "md5.h" +#include "mhd_md5_wrap.h" #include "test_helpers.h" #include <stdio.h> #include <stdlib.h> +#if defined(MHD_MD5_TLSLIB) && defined(MHD_HTTPS_REQUIRE_GCRYPT) +#define NEED_GCRYP_INIT 1 +#include <gcrypt.h> +#endif /* MHD_MD5_TLSLIB && MHD_HTTPS_REQUIRE_GCRYPT */ + static int verbose = 0; /* verbose level (0-1)*/ @@ -293,9 +298,9 @@ check_result (const char *test_name, { char calc_str[MD5_DIGEST_STRING_SIZE]; bin2hex (calculated, MD5_DIGEST_SIZE, calc_str); - printf ( - "PASSED: %s check %u: calculated digest %s match expected digest.\n", - test_name, check_num, calc_str); + printf ("PASSED: %s check %u: calculated digest %s " + "matches expected digest.\n", + test_name, check_num, calc_str); fflush (stdout); } return failed ? 1 : 0; @@ -312,19 +317,28 @@ test1_str (void) { unsigned int i; int num_failed = 0; - struct Md5Ctx ctx; + struct Md5CtxWr ctx; + + MHD_MD5_init_one_time (&ctx); for (i = 0; i < units1_num; i++) { uint8_t digest[MD5_DIGEST_SIZE]; - MHD_MD5_init (&ctx); MHD_MD5_update (&ctx, (const uint8_t *) data_units1[i].str_l.str, data_units1[i].str_l.len); - MHD_MD5_finish (&ctx, digest); + MHD_MD5_finish_reset (&ctx, digest); +#ifdef MHD_MD5_HAS_EXT_ERROR + if (0 != ctx.ext_error) + { + fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error); + exit (99); + } +#endif num_failed += check_result (__FUNCTION__, i, digest, data_units1[i].digest); } + MHD_MD5_deinit (&ctx); return num_failed; } @@ -334,18 +348,27 @@ test1_bin (void) { unsigned int i; int num_failed = 0; - struct Md5Ctx ctx; + struct Md5CtxWr ctx; + + MHD_MD5_init_one_time (&ctx); for (i = 0; i < units2_num; i++) { uint8_t digest[MD5_DIGEST_SIZE]; - MHD_MD5_init (&ctx); MHD_MD5_update (&ctx, data_units2[i].bin_l.bin, data_units2[i].bin_l.len); - MHD_MD5_finish (&ctx, digest); + MHD_MD5_finish_reset (&ctx, digest); +#ifdef MHD_MD5_HAS_EXT_ERROR + if (0 != ctx.ext_error) + { + fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error); + exit (99); + } +#endif num_failed += check_result (__FUNCTION__, i, digest, data_units2[i].digest); } + MHD_MD5_deinit (&ctx); return num_failed; } @@ -356,24 +379,33 @@ test2_str (void) { unsigned int i; int num_failed = 0; - struct Md5Ctx ctx; + struct Md5CtxWr ctx; + + MHD_MD5_init_one_time (&ctx); for (i = 0; i < units1_num; i++) { uint8_t digest[MD5_DIGEST_SIZE]; size_t part_s = data_units1[i].str_l.len / 4; - MHD_MD5_init (&ctx); MHD_MD5_update (&ctx, (const uint8_t *) "", 0); MHD_MD5_update (&ctx, (const uint8_t *) data_units1[i].str_l.str, part_s); MHD_MD5_update (&ctx, (const uint8_t *) "", 0); MHD_MD5_update (&ctx, (const uint8_t *) data_units1[i].str_l.str + part_s, data_units1[i].str_l.len - part_s); MHD_MD5_update (&ctx, (const uint8_t *) "", 0); - MHD_MD5_finish (&ctx, digest); + MHD_MD5_finish_reset (&ctx, digest); +#ifdef MHD_MD5_HAS_EXT_ERROR + if (0 != ctx.ext_error) + { + fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error); + exit (99); + } +#endif num_failed += check_result (__FUNCTION__, i, digest, data_units1[i].digest); } + MHD_MD5_deinit (&ctx); return num_failed; } @@ -383,22 +415,31 @@ test2_bin (void) { unsigned int i; int num_failed = 0; - struct Md5Ctx ctx; + struct Md5CtxWr ctx; + + MHD_MD5_init_one_time (&ctx); for (i = 0; i < units2_num; i++) { uint8_t digest[MD5_DIGEST_SIZE]; size_t part_s = data_units2[i].bin_l.len * 2 / 3; - MHD_MD5_init (&ctx); MHD_MD5_update (&ctx, data_units2[i].bin_l.bin, part_s); MHD_MD5_update (&ctx, (const uint8_t *) "", 0); MHD_MD5_update (&ctx, data_units2[i].bin_l.bin + part_s, data_units2[i].bin_l.len - part_s); - MHD_MD5_finish (&ctx, digest); + MHD_MD5_finish_reset (&ctx, digest); +#ifdef MHD_MD5_HAS_EXT_ERROR + if (0 != ctx.ext_error) + { + fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error); + exit (99); + } +#endif num_failed += check_result (__FUNCTION__, i, digest, data_units2[i].digest); } + MHD_MD5_deinit (&ctx); return num_failed; } @@ -414,7 +455,7 @@ test_unaligned (void) unsigned int offset; uint8_t *buf; uint8_t *digest_buf; - struct Md5Ctx ctx; + struct Md5CtxWr ctx; const struct data_unit2 *const tdata = data_units2 + DATA_POS; @@ -423,6 +464,8 @@ test_unaligned (void) if ((NULL == buf) || (NULL == digest_buf)) exit (99); + MHD_MD5_init_one_time (&ctx); + for (offset = MAX_OFFSET; offset >= 1; --offset) { uint8_t *unaligned_digest; @@ -433,12 +476,19 @@ test_unaligned (void) unaligned_digest = digest_buf + MAX_OFFSET - offset; memset (unaligned_digest, 0, MD5_DIGEST_SIZE); - MHD_MD5_init (&ctx); MHD_MD5_update (&ctx, unaligned_buf, tdata->bin_l.len); - MHD_MD5_finish (&ctx, unaligned_digest); + MHD_MD5_finish_reset (&ctx, unaligned_digest); +#ifdef MHD_MD5_HAS_EXT_ERROR + if (0 != ctx.ext_error) + { + fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error); + exit (99); + } +#endif num_failed += check_result (__FUNCTION__, MAX_OFFSET - offset, unaligned_digest, tdata->digest); } + MHD_MD5_deinit (&ctx); free (digest_buf); free (buf); return num_failed; @@ -453,6 +503,13 @@ main (int argc, char *argv[]) if (has_param (argc, argv, "-v") || has_param (argc, argv, "--verbose")) verbose = 1; +#ifdef NEED_GCRYP_INIT + gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); +#ifdef GCRYCTL_INITIALIZATION_FINISHED + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); +#endif /* GCRYCTL_INITIALIZATION_FINISHED */ +#endif /* NEED_GCRYP_INIT */ + num_failed += test1_str (); num_failed += test1_bin (); diff --git a/src/microhttpd/test_sha256.c b/src/microhttpd/test_sha256.c @@ -24,11 +24,16 @@ */ #include "mhd_options.h" -#include "sha256.h" +#include "mhd_sha256_wrap.h" #include "test_helpers.h" #include <stdio.h> #include <stdlib.h> +#if defined(MHD_SHA256_TLSLIB) && defined(MHD_HTTPS_REQUIRE_GCRYPT) +#define NEED_GCRYP_INIT 1 +#include <gcrypt.h> +#endif /* MHD_SHA256_TLSLIB && MHD_HTTPS_REQUIRE_GCRYPT */ + static int verbose = 0; /* verbose level (0-1)*/ @@ -320,9 +325,9 @@ check_result (const char *test_name, { char calc_str[SHA256_DIGEST_STRING_SIZE]; bin2hex (calculated, SHA256_DIGEST_SIZE, calc_str); - printf ( - "PASSED: %s check %u: calculated digest %s matches expected digest.\n", - test_name, check_num, calc_str); + printf ("PASSED: %s check %u: calculated digest %s " + "matches expected digest.\n", + test_name, check_num, calc_str); fflush (stdout); } return failed ? 1 : 0; @@ -339,19 +344,27 @@ test1_str (void) { int num_failed = 0; unsigned int i; - struct Sha256Ctx ctx; + struct Sha256CtxWr ctx; + MHD_SHA256_init_one_time (&ctx); for (i = 0; i < units1_num; i++) { uint8_t digest[SHA256_DIGEST_SIZE]; - MHD_SHA256_init (&ctx); MHD_SHA256_update (&ctx, (const uint8_t *) data_units1[i].str_l.str, data_units1[i].str_l.len); - MHD_SHA256_finish (&ctx, digest); + MHD_SHA256_finish_reset (&ctx, digest); +#ifdef MHD_SHA256_HAS_EXT_ERROR + if (0 != ctx.ext_error) + { + fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error); + exit (99); + } +#endif /* MHD_SHA256_HAS_EXT_ERROR */ num_failed += check_result (__FUNCTION__, i, digest, data_units1[i].digest); } + MHD_SHA256_deinit (&ctx); return num_failed; } @@ -361,19 +374,27 @@ test1_bin (void) { int num_failed = 0; unsigned int i; - struct Sha256Ctx ctx; + struct Sha256CtxWr ctx; + MHD_SHA256_init_one_time (&ctx); for (i = 0; i < units2_num; i++) { uint8_t digest[SHA256_DIGEST_SIZE]; - MHD_SHA256_init (&ctx); MHD_SHA256_update (&ctx, data_units2[i].bin_l.bin, data_units2[i].bin_l.len); - MHD_SHA256_finish (&ctx, digest); + MHD_SHA256_finish_reset (&ctx, digest); +#ifdef MHD_SHA256_HAS_EXT_ERROR + if (0 != ctx.ext_error) + { + fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error); + exit (99); + } +#endif /* MHD_SHA256_HAS_EXT_ERROR */ num_failed += check_result (__FUNCTION__, i, digest, data_units2[i].digest); } + MHD_SHA256_deinit (&ctx); return num_failed; } @@ -384,14 +405,14 @@ test2_str (void) { int num_failed = 0; unsigned int i; - struct Sha256Ctx ctx; + struct Sha256CtxWr ctx; + MHD_SHA256_init_one_time (&ctx); for (i = 0; i < units1_num; i++) { uint8_t digest[SHA256_DIGEST_SIZE]; size_t part_s = data_units1[i].str_l.len / 4; - MHD_SHA256_init (&ctx); MHD_SHA256_update (&ctx, (const uint8_t *) "", 0); MHD_SHA256_update (&ctx, (const uint8_t *) data_units1[i].str_l.str, part_s); @@ -400,10 +421,18 @@ test2_str (void) + part_s, data_units1[i].str_l.len - part_s); MHD_SHA256_update (&ctx, (const uint8_t *) "", 0); - MHD_SHA256_finish (&ctx, digest); + MHD_SHA256_finish_reset (&ctx, digest); +#ifdef MHD_SHA256_HAS_EXT_ERROR + if (0 != ctx.ext_error) + { + fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error); + exit (99); + } +#endif /* MHD_SHA256_HAS_EXT_ERROR */ num_failed += check_result (__FUNCTION__, i, digest, data_units1[i].digest); } + MHD_SHA256_deinit (&ctx); return num_failed; } @@ -413,22 +442,30 @@ test2_bin (void) { int num_failed = 0; unsigned int i; - struct Sha256Ctx ctx; + struct Sha256CtxWr ctx; + MHD_SHA256_init_one_time (&ctx); for (i = 0; i < units2_num; i++) { uint8_t digest[SHA256_DIGEST_SIZE]; size_t part_s = data_units2[i].bin_l.len * 2 / 3; - MHD_SHA256_init (&ctx); MHD_SHA256_update (&ctx, data_units2[i].bin_l.bin, part_s); MHD_SHA256_update (&ctx, (const uint8_t *) "", 0); MHD_SHA256_update (&ctx, data_units2[i].bin_l.bin + part_s, data_units2[i].bin_l.len - part_s); - MHD_SHA256_finish (&ctx, digest); + MHD_SHA256_finish_reset (&ctx, digest); +#ifdef MHD_SHA256_HAS_EXT_ERROR + if (0 != ctx.ext_error) + { + fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error); + exit (99); + } +#endif /* MHD_SHA256_HAS_EXT_ERROR */ num_failed += check_result (__FUNCTION__, i, digest, data_units2[i].digest); } + MHD_SHA256_deinit (&ctx); return num_failed; } @@ -444,10 +481,11 @@ test_unaligned (void) unsigned int offset; uint8_t *buf; uint8_t *digest_buf; - struct Sha256Ctx ctx; + struct Sha256CtxWr ctx; const struct data_unit2 *const tdata = data_units2 + DATA_POS; + MHD_SHA256_init_one_time (&ctx); buf = malloc (tdata->bin_l.len + MAX_OFFSET); digest_buf = malloc (SHA256_DIGEST_SIZE + MAX_OFFSET); if ((NULL == buf) || (NULL == digest_buf)) @@ -463,14 +501,21 @@ test_unaligned (void) unaligned_digest = digest_buf + MAX_OFFSET - offset; memset (unaligned_digest, 0, SHA256_DIGEST_SIZE); - MHD_SHA256_init (&ctx); MHD_SHA256_update (&ctx, unaligned_buf, tdata->bin_l.len); - MHD_SHA256_finish (&ctx, unaligned_digest); + MHD_SHA256_finish_reset (&ctx, unaligned_digest); +#ifdef MHD_SHA256_HAS_EXT_ERROR + if (0 != ctx.ext_error) + { + fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error); + exit (99); + } +#endif /* MHD_SHA256_HAS_EXT_ERROR */ num_failed += check_result (__FUNCTION__, MAX_OFFSET - offset, unaligned_digest, tdata->digest); } free (digest_buf); free (buf); + MHD_SHA256_deinit (&ctx); return num_failed; } @@ -483,6 +528,13 @@ main (int argc, char *argv[]) if (has_param (argc, argv, "-v") || has_param (argc, argv, "--verbose")) verbose = 1; +#ifdef NEED_GCRYP_INIT + gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); +#ifdef GCRYCTL_INITIALIZATION_FINISHED + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); +#endif /* GCRYCTL_INITIALIZATION_FINISHED */ +#endif /* NEED_GCRYP_INIT */ + num_failed += test1_str (); num_failed += test1_bin (); diff --git a/src/testcurl/test_digestauth.c b/src/testcurl/test_digestauth.c @@ -26,18 +26,19 @@ * @author Karlson2k (Evgeny Grin) */ -#include "MHD_config.h" +#include "mhd_options.h" #include "platform.h" #include <curl/curl.h> #include <microhttpd.h> #include <stdlib.h> #include <string.h> #include <time.h> -#ifdef MHD_HTTPS_REQUIRE_GCRYPT -#ifdef HAVE_GCRYPT_H + +#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \ + (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB)) +#define NEED_GCRYP_INIT 1 #include <gcrypt.h> -#endif -#endif /* MHD_HTTPS_REQUIRE_GCRYPT */ +#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */ #ifndef WINDOWS #include <sys/socket.h> @@ -491,14 +492,12 @@ main (int argc, char *const *argv) unsigned int errorCount = 0; (void) argc; (void) argv; /* Unused. Silent compiler warning. */ -#ifdef MHD_HTTPS_REQUIRE_GCRYPT -#ifdef HAVE_GCRYPT_H +#ifdef NEED_GCRYP_INIT gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); #ifdef GCRYCTL_INITIALIZATION_FINISHED gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); -#endif -#endif -#endif /* MHD_HTTPS_REQUIRE_GCRYPT */ +#endif /* GCRYCTL_INITIALIZATION_FINISHED */ +#endif /* NEED_GCRYP_INIT */ if (0 != curl_global_init (CURL_GLOBAL_WIN32)) return 2; errorCount += testDigestAuth (); diff --git a/src/testcurl/test_digestauth2.c b/src/testcurl/test_digestauth2.c @@ -25,7 +25,7 @@ * @author Karlson2k (Evgeny Grin) */ -#include "MHD_config.h" +#include "mhd_options.h" #include "platform.h" #include <curl/curl.h> #include <microhttpd.h> @@ -33,6 +33,12 @@ #include <string.h> #include <time.h> +#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \ + (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB)) +#define NEED_GCRYP_INIT 1 +#include <gcrypt.h> +#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */ + #ifndef _WIN32 #include <sys/socket.h> #include <unistd.h> @@ -1464,6 +1470,12 @@ main (int argc, char *const *argv) int curl_sspi; (void) argc; (void) argv; /* Unused. Silent compiler warning. */ +#ifdef NEED_GCRYP_INIT + gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); +#ifdef GCRYCTL_INITIALIZATION_FINISHED + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); +#endif /* GCRYCTL_INITIALIZATION_FINISHED */ +#endif /* NEED_GCRYP_INIT */ /* Test type and test parameters */ verbose = ! (has_param (argc, argv, "-q") || has_param (argc, argv, "--quiet") || diff --git a/src/testcurl/test_digestauth_concurrent.c b/src/testcurl/test_digestauth_concurrent.c @@ -26,18 +26,18 @@ * @author Karlson2k (Evgeny Grin) */ -#include "MHD_config.h" +#include "mhd_options.h" #include "platform.h" #include <curl/curl.h> #include <microhttpd.h> #include <stdlib.h> #include <string.h> #include <time.h> -#ifdef MHD_HTTPS_REQUIRE_GCRYPT -#ifdef HAVE_GCRYPT_H +#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \ + (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB)) +#define NEED_GCRYP_INIT 1 #include <gcrypt.h> -#endif -#endif /* MHD_HTTPS_REQUIRE_GCRYPT */ +#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */ #ifndef WINDOWS #include <sys/socket.h> @@ -669,14 +669,12 @@ main (int argc, char *const *argv) has_param (argc, argv, "-s") || has_param (argc, argv, "--silent")); - #ifdef MHD_HTTPS_REQUIRE_GCRYPT -#ifdef HAVE_GCRYPT_H +#ifdef NEED_GCRYP_INIT gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); #ifdef GCRYCTL_INITIALIZATION_FINISHED gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); -#endif -#endif -#endif /* MHD_HTTPS_REQUIRE_GCRYPT */ +#endif /* GCRYCTL_INITIALIZATION_FINISHED */ +#endif /* NEED_GCRYP_INIT */ if (0 != curl_global_init (CURL_GLOBAL_WIN32)) return 2; errorCount += testDigestAuth (); diff --git a/src/testcurl/test_digestauth_sha256.c b/src/testcurl/test_digestauth_sha256.c @@ -27,18 +27,19 @@ * @author Karlson2k (Evgeny Grin) */ -#include "MHD_config.h" +#include "mhd_options.h" #include "platform.h" #include <curl/curl.h> #include <microhttpd.h> #include <stdlib.h> #include <string.h> #include <time.h> -#ifdef MHD_HTTPS_REQUIRE_GCRYPT -#ifdef HAVE_GCRYPT_H + +#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \ + (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB)) +#define NEED_GCRYP_INIT 1 #include <gcrypt.h> -#endif -#endif /* MHD_HTTPS_REQUIRE_GCRYPT */ +#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */ #ifndef WINDOWS #include <sys/socket.h> @@ -320,14 +321,12 @@ main (int argc, char *const *argv) /* curl added SHA256 support in 7.57 = 7.0x39 */ if (d->version_num < 0x073900) return 77; /* skip test, curl is too old */ -#ifdef MHD_HTTPS_REQUIRE_GCRYPT -#ifdef HAVE_GCRYPT_H +#ifdef NEED_GCRYP_INIT gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); #ifdef GCRYCTL_INITIALIZATION_FINISHED gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); -#endif -#endif -#endif /* MHD_HTTPS_REQUIRE_GCRYPT */ +#endif /* GCRYCTL_INITIALIZATION_FINISHED */ +#endif /* NEED_GCRYP_INIT */ if (0 != curl_global_init (CURL_GLOBAL_WIN32)) return 2; errorCount += testDigestAuth (); diff --git a/src/testcurl/test_digestauth_with_arguments.c b/src/testcurl/test_digestauth_with_arguments.c @@ -25,18 +25,19 @@ * @author Amr Ali * @author Karlson2k (Evgeny Grin) */ -#include "MHD_config.h" +#include "mhd_options.h" #include "platform.h" #include <curl/curl.h> #include <microhttpd.h> #include <stdlib.h> #include <string.h> #include <time.h> -#ifdef MHD_HTTPS_REQUIRE_GCRYPT -#ifdef HAVE_GCRYPT_H + +#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \ + (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB)) +#define NEED_GCRYP_INIT 1 #include <gcrypt.h> -#endif -#endif /* MHD_HTTPS_REQUIRE_GCRYPT */ +#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */ #ifndef WINDOWS #include <sys/socket.h> diff --git a/w32/common/MHD_config.h b/w32/common/MHD_config.h @@ -74,6 +74,12 @@ /* Enable digest Auth support */ #define DAUTH_SUPPORT 1 +/* Enable MD5 hashing support. */ +#define MHD_MD5_SUPPORT 1 + +/* Enable SHA-256 hashing support. */ +#define MHD_SHA256_SUPPORT 1 + /* Enable postprocessor.c */ #define HAVE_POSTPROCESSOR 1 diff --git a/w32/common/libmicrohttpd-files.vcxproj b/w32/common/libmicrohttpd-files.vcxproj @@ -38,7 +38,9 @@ <ClInclude Include="$(MhdSrc)microhttpd\digestauth.h" /> <ClInclude Include="$(MhdSrc)microhttpd\gen_auth.h" /> <ClInclude Include="$(MhdSrc)microhttpd\internal.h" /> + <ClInclude Include="$(MhdSrc)microhttpd\mhd_md5_wrap.h" /> <ClInclude Include="$(MhdSrc)microhttpd\md5.h" /> + <ClInclude Include="$(MhdSrc)microhttpd\mhd_sha256_wrap.h" /> <ClInclude Include="$(MhdSrc)microhttpd\sha256.h" /> <ClInclude Include="$(MhdSrc)microhttpd\memorypool.h" /> <ClInclude Include="$(MhdSrc)microhttpd\mhd_assert.h" /> diff --git a/w32/common/libmicrohttpd-filters.vcxproj b/w32/common/libmicrohttpd-filters.vcxproj @@ -58,9 +58,15 @@ <ClInclude Include="$(MhdSrc)microhttpd\internal.h"> <Filter>Internal Headers</Filter> </ClInclude> + <ClInclude Include="$(MhdSrc)microhttpd\mhd_md5_wrap.h"> + <Filter>Internal Headers</Filter> + </ClInclude> <ClInclude Include="$(MhdSrc)microhttpd\md5.h"> <Filter>Internal Headers</Filter> </ClInclude> + <ClInclude Include="$(MhdSrc)microhttpd\mhd_sha256_wrap.h"> + <Filter>Internal Headers</Filter> + </ClInclude> <ClInclude Include="$(MhdSrc)microhttpd\sha256.h"> <Filter>Internal Headers</Filter> </ClInclude>