commit 24ed886d805c3f38709311006da38573fe66c6f5
parent 8d35a95be9c4caf1ce304e744c51cd7feda44ad5
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date: Mon, 6 Jan 2025 13:23:34 +0100
Implemented Basic and Digest Auth + some fixes
Diffstat:
76 files changed, 11380 insertions(+), 1098 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -19,7 +19,7 @@ build-aux/
/.cproject
/.tgitconfig
/configure
-stamp-h?
+stamp-h[0-9]
/libtool
/aclocal.m4
/config.status
diff --git a/Makefile.am b/Makefile.am
@@ -356,8 +356,8 @@ apim_prepare = \
mostlyclean-local: mostlyclean-autopoint-update
mostlyclean-autopoint-update:
- -test -d '$(APIM_LOCK)' && rm -rf '$(APIM_LOCK)'
- -test -d '$(srcdir_po)/po' && rm -rf '$(srcdir_po)/po'
+ -rm -rf '$(APIM_LOCK)'
+ -rm -rf '$(srcdir_po)/po'
rm -f '$(APIM_TMSTMP_TMP)'
$(APIM_TRIGGER):
diff --git a/configure.ac b/configure.ac
@@ -1151,6 +1151,22 @@ int main(void)
AS_UNSET([mhd_cv_cc_compound_literals_arr_scope])
]
)
+AC_CACHE_CHECK([whether $CC supports trailing comma in enum],[mhd_cv_enum_trailing_comma],
+ [
+ SAVE_c_werror_flag="$ac_c_werror_flag"
+ ac_c_werror_flag="yes"
+ AC_COMPILE_IFELSE(
+ [AC_LANG_SOURCE([[enum TestEnum {ENM_VAL_A, ENM_VAL_B,};]])],
+ [mhd_cv_enum_trailing_comma="yes"],
+ [mhd_cv_enum_trailing_comma="no"]
+ )
+ ac_c_werror_flag="$SAVE_c_werror_flag"
+ ]
+)
+AS_VAR_IF([mhd_cv_enum_trailing_comma],["yes"],[],
+ [AC_DEFINE([MHD_ENUMS_NEED_TRAILING_VALUE],[1],[Define to '1' if your compiler does not supports enum with trailing comma])]
+)
+
AS_VAR_SET_IF([ac_cv_c_restrict],[],
[
@@ -5363,95 +5379,100 @@ AM_CONDITIONAL([USE_UPGRADE_TLS_TESTS], [[test "x${mhd_cv_gnutls_mthread_broken}
# optional: HTTP Basic Auth support. Enabled by default
AC_MSG_CHECKING([[whether to support HTTP Basic authentication]])
-AC_ARG_ENABLE([bauth],
- [AS_HELP_STRING([--disable-bauth],[disable HTTP Basic Authentication support])],
- [enable_bauth=${enableval}],
- [enable_bauth=yes])
-AS_IF([[test "x$enable_bauth" != "xno"]],
- [ enable_bauth=yes
- AC_DEFINE([BAUTH_SUPPORT],[1],[Define to 1 if libmicrohttpd is compiled with Basic Auth support.]) ])
-AM_CONDITIONAL([ENABLE_BAUTH], [test "x$enable_bauth" != "xno"])
-AC_MSG_RESULT([[$enable_bauth]])
+AC_ARG_ENABLE([basic-auth],
+ [AS_HELP_STRING([--disable-basic-auth],[disable HTTP Basic Authentication support])],
+ [],
+ [enable_basic_auth="yes"]
+)
+AS_IF([[test "x$enable_basic_auth" != "xno"]],
+ [
+ enable_basic_auth="yes"
+ AC_DEFINE([MHD_SUPPORT_AUTH_BASIC],[1],[Define to '1' if libmicrohttpd should be compiled with Basic Auth support.])
+ ]
+)
+AM_CONDITIONAL([MHD_SUPPORT_AUTH_BASIC], [test "x$enable_basic_auth" = "xyes"])
+AC_MSG_RESULT([[$enable_basic_auth]])
# optional: HTTP Digest Auth support. Enabled by default
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"])
-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.]) ])
-AM_CONDITIONAL([ENABLE_DAUTH], [test "x$enable_dauth" != "xno"])
-AC_MSG_RESULT([[$enable_dauth]])
-
-AS_VAR_IF([enable_dauth], ["yes"],
+AC_ARG_ENABLE([digest-auth],
+ [AS_HELP_STRING([--disable-digest-auth], [disable HTTP Digest Authentication support])],
+ [],
+ [enable_digest_auth="yes"]
+)
+AS_IF([[test "x$enable_digest_auth" != "xno"]],
+ [ enable_digest_auth=yes
+ AC_DEFINE([MHD_SUPPORT_AUTH_DIGEST],[1],[Define to 1 if libmicrohttpd is compiled with Digest Auth support.]) ])
+AM_CONDITIONAL([MHD_SUPPORT_AUTH_DIGEST], [test "x$enable_digest_auth" != "xno"])
+AC_MSG_RESULT([[$enable_digest_auth]])
+
+AS_VAR_IF([enable_digest_auth], ["yes"],
[
AC_MSG_CHECKING([[for Digest Authentication default nonce timeout value]])
- AC_ARG_ENABLE([dauth-def-timeout],
- [AS_HELP_STRING([--enable-dauth-def-timeout=NNN],
+ AC_ARG_ENABLE([digest-auth-def-timeout],
+ [AS_HELP_STRING([--enable-digest-auth-def-timeout=NNN],
[set default Digest Auth nonce validity time to NNN seconds])],
- [enable_dauth_def_timeout="${enableval}"],
- [enable_dauth_def_timeout=""]
+ [],
+ [enable_digest_auth_def_timeout=""]
)
- AS_VAR_IF([enable_dauth_def_timeout], ["no"],
+ AS_VAR_IF([enable_digest_auth_def_timeout], ["no"],
[
- AC_MSG_WARN([Default Diget Auth nonce validity time cannot be disabled, --disable-dauth-def-timeout parameter is ignored])
- enable_dauth_def_timeout=""
+ AC_MSG_WARN([Default Diget Auth nonce validity time cannot be disabled, --disable-digest-auth-def-timeout parameter is ignored])
+ enable_digest_auth_def_timeout=""
]
)
- AS_IF([test -z "${enable_dauth_def_timeout}"],
+ AS_IF([test -z "${enable_digest_auth_def_timeout}"],
[
- enable_dauth_def_timeout="90"
- enable_dauth_def_timeout_MSG="${enable_dauth_def_timeout} seconds (default)"
+ enable_digest_auth_def_timeout="90"
+ enable_dauth_def_timeout_MSG="${enable_digest_auth_def_timeout} seconds (default)"
],
[
- AS_IF([[test "x${enable_dauth_def_timeout}" = "x`echo ${enable_dauth_def_timeout}|${SED-sed} 's/[^0-9]//g'`" && \
- test "${enable_dauth_def_timeout}" -ge "0" 2>/dev/null ]],[:],
- [AC_MSG_ERROR([Invalid parameter --enable-dauth-def-timeout=${enable_dauth_def_timeout}. Timeout value must be a positive integer.])
+ AS_IF([[test "x${enable_digest_auth_def_timeout}" = "x`echo ${enable_digest_auth_def_timeout}|${SED-sed} 's/[^0-9]//g'`" && \
+ test "${enable_digest_auth_def_timeout}" -ge "0" 2>/dev/null ]],[:],
+ [AC_MSG_ERROR([Invalid parameter --enable-digest-auth-def-timeout=${enable_digest_auth_def_timeout}. Timeout value must be a positive integer.])
]
)
AC_COMPILE_IFELSE(
[
AC_LANG_PROGRAM([],
[[
- static int arr[((int) 2) - 4 * (int)(${enable_dauth_def_timeout} != ((unsigned int)${enable_dauth_def_timeout}))];
+ static int arr[((int) 2) - 4 * (int)(${enable_digest_auth_def_timeout} != ((unsigned int)${enable_digest_auth_def_timeout}))];
(void) arr;
]]
)
],
[],
- [AC_MSG_ERROR([The value specified by --enable-dauth-def-timeout=${enable_dauth_def_timeout} is too large.])]
+ [AC_MSG_ERROR([The value specified by --enable-digest-auth-def-timeout=${enable_digest_auth_def_timeout} is too large.])]
)
- enable_dauth_def_timeout_MSG="${enable_dauth_def_timeout} seconds (set by parameter)"
+ enable_dauth_def_timeout_MSG="${enable_digest_auth_def_timeout} seconds (set by parameter)"
]
)
- AC_DEFINE_UNQUOTED([MHD_DAUTH_DEF_TIMEOUT_],[${enable_dauth_def_timeout}],
+ AC_DEFINE_UNQUOTED([MHD_AUTH_DIGEST_DEF_TIMEOUT],[${enable_digest_auth_def_timeout}],
[The default HTTP Digest Auth default nonce timeout value (in seconds)])
AC_MSG_RESULT([[${enable_dauth_def_timeout_MSG}]])
AC_MSG_CHECKING([[for Digest Authentication default maximum nc value]])
- AC_ARG_ENABLE([dauth-def-max-nc],
- [AS_HELP_STRING([--enable-dauth-def-max-nc=NNN],
+ AC_ARG_ENABLE([digest-auth-def-max-nc],
+ [AS_HELP_STRING([--enable-digest-auth-def-max-nc=NNN],
[set default Digest Auth maximum nc (nonce count) value to NNN])],
- [enable_dauth_def_max_nc="${enableval}"],
- [enable_dauth_def_max_nc=""]
+ [],
+ [enable_digest_auth_def_max_nc=""]
)
- AS_VAR_IF([enable_dauth_def_max_nc], ["no"],
+ AS_VAR_IF([enable_digest_auth_def_max_nc], ["no"],
[
- AC_MSG_WARN([Default Diget Auth maximum nc cannot be disabled, --disable-dauth-def-max-nc parameter is ignored])
- enable_dauth_def_max_nc=""
+ AC_MSG_WARN([Default Diget Auth maximum nc cannot be disabled, --disable-digest-auth-def-max-nc parameter is ignored])
+ enable_digest_auth_def_max_nc=""
]
)
- AS_IF([test -z "${enable_dauth_def_max_nc}"],
+ AS_IF([test -z "${enable_digest_auth_def_max_nc}"],
[
- enable_dauth_def_max_nc="1000"
- enable_dauth_def_max_nc_MSG="${enable_dauth_def_max_nc} (default)"
+ enable_digest_auth_def_max_nc="1000"
+ enable_dauth_def_max_nc_MSG="${enable_digest_auth_def_max_nc} (default)"
],
[
- AS_IF([[test "x${enable_dauth_def_max_nc}" = "x`echo ${enable_dauth_def_max_nc}|${SED-sed} 's/[^0-9]//g'`" && \
- test "${enable_dauth_def_max_nc}" -ge "0" 2>/dev/null ]],[:],
- [AC_MSG_ERROR([Invalid parameter --enable-dauth-def-max-nc=${enable_dauth_def_max_nc}. The value must be a positive integer.])
+ AS_IF([[test "x${enable_digest_auth_def_max_nc}" = "x`echo ${enable_digest_auth_def_max_nc}|${SED-sed} 's/[^0-9]//g'`" && \
+ test "${enable_digest_auth_def_max_nc}" -ge "0" 2>/dev/null ]],[:],
+ [AC_MSG_ERROR([Invalid parameter --enable-digest-auth-def-max-nc=${enable_digest_auth_def_max_nc}. The value must be a positive integer.])
]
)
AC_COMPILE_IFELSE(
@@ -5461,18 +5482,18 @@ AS_VAR_IF([enable_dauth], ["yes"],
#include <stdint.h>
]],
[[
- static int arr[((int) 2) - 4 * (int)(${enable_dauth_def_max_nc} != ((uint_fast32_t)${enable_dauth_def_max_nc}))];
+ static int arr[((int) 2) - 4 * (int)(${enable_digest_auth_def_max_nc} != ((uint_fast32_t)${enable_digest_auth_def_max_nc}))];
(void) arr;
]]
)
],
[],
- [AC_MSG_ERROR([The value specified by --enable-dauth-def-max-nc=${enable_dauth_def_max_nc} is too large.])]
+ [AC_MSG_ERROR([The value specified by --enable-digest-auth-def-max-nc=${enable_digest_auth_def_max_nc} is too large.])]
)
- enable_dauth_def_max_nc_MSG="${enable_dauth_def_max_nc} (set by parameter)"
+ enable_dauth_def_max_nc_MSG="${enable_digest_auth_def_max_nc} (set by parameter)"
]
)
- AC_DEFINE_UNQUOTED([MHD_DAUTH_DEF_MAX_NC_],[${enable_dauth_def_max_nc}],
+ AC_DEFINE_UNQUOTED([MHD_AUTH_DIGEST_DEF_MAX_NC],[${enable_digest_auth_def_max_nc}],
[The default HTTP Digest Auth default maximum nc (nonce count) value])
AC_MSG_RESULT([[${enable_dauth_def_max_nc_MSG}]])
@@ -5483,7 +5504,7 @@ AS_VAR_IF([enable_dauth], ["yes"],
]
)
-AM_CONDITIONAL([HAVE_ANYAUTH],[test "x$enable_bauth" != "xno" || test "x$enable_dauth" != "xno"])
+AM_CONDITIONAL([MHD_SUPPORT_AUTH],[test "x$enable_basic_auth" != "xno" || test "x$enable_digest_auth" != "xno"])
# optional: HTTP "Upgrade" support. Enabled by default
AC_MSG_CHECKING([[whether to support HTTP "Upgrade"]])
@@ -5516,7 +5537,7 @@ AC_ARG_ENABLE([[md5]],
[
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_digest_auth],["yes"],[],
[
AS_VAR_IF([enable_md5],["no"],[],
[
@@ -5526,7 +5547,7 @@ AC_ARG_ENABLE([[md5]],
)
]
)
- ], [[enable_md5="${enable_dauth}"]]
+ ], [[enable_md5="${enable_digest_auth}"]]
)
AS_CASE([${enable_md5}],[yes|tlslib],
[
@@ -5617,18 +5638,18 @@ AS_CASE([${enable_md5}],
)
AS_IF([test "x${enable_md5}" = "xbuiltin" || test "x${enable_md5}" = "xtlslib" ],
[
- AC_DEFINE([[MHD_MD5_SUPPORT]],[[1]],
+ AC_DEFINE([[MHD_SUPPORT_MD5]],[[1]],
[Define to 1 if libmicrohttpd is compiled with MD5 hashing support.])
]
)
AS_IF([test "x${enable_md5}" = "xtlslib" ],
[
- AC_DEFINE([[MHD_MD5_TLSLIB]],[[1]],
+ AC_DEFINE([[MHD_MD5_EXTR]],[[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" ]])
+AM_CONDITIONAL([MHD_SUPPORT_MD5], [[test "x${enable_md5}" = "xbuiltin" || test "x${enable_md5}" = "xtlslib" ]])
+AM_CONDITIONAL([MHD_MD5_EXTR], [[test "x${enable_md5}" = "xtlslib" ]])
AC_MSG_RESULT([[${enable_md5_MSG}]])
# optional: SHA-256 support for Digest Auth. Enabled by default.
@@ -5638,7 +5659,7 @@ AC_ARG_ENABLE([[sha256]],
[
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_digest_auth],["yes"],[],
[
AS_VAR_IF([enable_sha256],["no"],[],
[
@@ -5648,7 +5669,7 @@ AC_ARG_ENABLE([[sha256]],
)
]
)
- ], [[enable_sha256="${enable_dauth}"]]
+ ], [[enable_sha256="${enable_digest_auth}"]]
)
AS_CASE([${enable_sha256}],[yes|tlslib],
[
@@ -5739,18 +5760,18 @@ AS_CASE([${enable_sha256}],
)
AS_IF([test "x${enable_sha256}" = "xbuiltin" || test "x${enable_sha256}" = "xtlslib" ],
[
- AC_DEFINE([[MHD_SHA256_SUPPORT]],[[1]],
+ AC_DEFINE([[MHD_SUPPORT_SHA256]],[[1]],
[Define to 1 if libmicrohttpd is compiled with SHA-256 hashing support.])
]
)
AS_IF([test "x${enable_sha256}" = "xtlslib" ],
[
- AC_DEFINE([[MHD_SHA256_TLSLIB]],[[1]],
+ AC_DEFINE([[MHD_SHA256_EXTR]],[[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" ]])
+AM_CONDITIONAL([MHD_SUPPORT_SHA256], [[test "x${enable_sha256}" = "xbuiltin" || test "x${enable_sha256}" = "xtlslib" ]])
+AM_CONDITIONAL([MHD_SHA256_EXTR], [[test "x${enable_sha256}" = "xtlslib" ]])
AC_MSG_RESULT([[${enable_sha256_MSG}]])
# optional: SHA-512/256 support for Digest Auth. Enabled by default.
@@ -5760,7 +5781,7 @@ AC_ARG_ENABLE([[sha512-256]],
[
AS_VAR_IF([[enable_sha512_256]],[["yes"]],
[
- AS_VAR_IF([enable_dauth],["yes"],[],
+ AS_VAR_IF([enable_digest_auth],["yes"],[],
[
AC_MSG_WARN([The parameter --enable-sha512-256 is ignored as Digest Authentication is disabled])
enable_sha512_256='no'
@@ -5768,7 +5789,7 @@ AC_ARG_ENABLE([[sha512-256]],
)
],[[enable_sha512_256='no']]
)
- ], [[enable_sha512_256="${enable_dauth}"]]
+ ], [[enable_sha512_256="${enable_digest_auth}"]]
)
AC_MSG_CHECKING([[whether to support SHA-512/256]])
AS_UNSET([enable_sha512_256_MSG])
@@ -5779,14 +5800,14 @@ AS_CASE([${enable_sha512_256}],
)
AS_VAR_IF([[enable_sha512_256]],[["yes"]],
[
- AC_DEFINE([[MHD_SHA512_256_SUPPORT]],[[1]],
+ AC_DEFINE([[MHD_SUPPORT_SHA512_256]],[[1]],
[Define to 1 if libmicrohttpd is compiled with SHA-512/256 hashing support.])
]
)
-AM_CONDITIONAL([ENABLE_SHA512_256], [[test "x${enable_sha512_256}" = "xyes"]])
+AM_CONDITIONAL([MHD_SUPPORT_SHA512_256], [[test "x${enable_sha512_256}" = "xyes"]])
AC_MSG_RESULT([[${enable_sha512_256_MSG}]])
-AS_IF([test "x$enable_dauth" != "xno"],
+AS_IF([test "x$enable_digest_auth" != "xno"],
[
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])]
@@ -7417,7 +7438,7 @@ AC_MSG_NOTICE([[${PACKAGE_NAME} ${PACKAGE_VERSION} Configuration Summary:
Threading lib : ${USE_THREADS}
Shutdown of listening socket triggers select: ${mhd_cv_host_shtdwn_trgr_select}
- [ Libary options and features ]
+ [ Library features and options ]
Inter-thread comm: ${use_itc}
select() support : ${enable_select}
poll() support : ${enable_poll=no}
@@ -7427,8 +7448,8 @@ AC_MSG_NOTICE([[${PACKAGE_NAME} ${PACKAGE_VERSION} Configuration Summary:
TLS backends : ${MSG_TLS_BACKENDS}
Cookie parser : ${enable_cookie}
POST parser : ${enable_postparser}
- Basic auth. : ${enable_bauth}
- Digest auth. : ${enable_dauth}
+ Basic auth. : ${enable_basic_auth}
+ Digest auth. : ${enable_digest_auth}
Digest auth. defaults: ${dauth_defs_MSG}
MD5 : ${enable_md5_MSG}
SHA-256 : ${enable_sha256_MSG}
@@ -7475,8 +7496,8 @@ AC_MSG_NOTICE([[
Library licence : ${licence_descr}
]])
-AS_IF([test "x$enable_bauth" != "xyes" || \
- test "x$enable_dauth" != "xyes" || \
+AS_IF([test "x$enable_basic_auth" != "xyes" || \
+ test "x$enable_digest_auth" != "xyes" || \
test "x${enable_md5}" = "xno" || \
test "x${enable_sha256}" = "xno" || \
test "x${enable_sha512_256}" != "xyes" || \
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
@@ -22,7 +22,7 @@ $(top_builddir)/src/microhttpd/libmicrohttpd.la: $(top_builddir)/src/microhttpd/
# example programs
noinst_PROGRAMS =
-if ENABLE_BAUTH
+if MHD_SUPPORT_AUTH_BASIC
noinst_PROGRAMS +=
if MHD_ENABLE_HTTPS
noinst_PROGRAMS +=
diff --git a/src/incl_priv/mhd_sys_options.h b/src/incl_priv/mhd_sys_options.h
@@ -487,12 +487,12 @@
(FUNC_ATTR_PTRCOMPARE_WORKS || FUNC_ATTR_NOSANITIZE_WORKS)) */
#ifndef _MSC_FULL_VER
-# define MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ /* empty */
-# define MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ /* empty */
+# define mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE /* empty */
+# define mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE /* empty */
#else /* _MSC_FULL_VER */
-# define MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ \
+# define mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE \
__pragma(runtime_checks("c", off))
-# define MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ \
+# define mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE \
__pragma(runtime_checks("c", restore))
#endif /* _MSC_FULL_VER */
@@ -584,12 +584,12 @@
# endif /* 0 != HAVE_DECL_CPU_SETSIZE */
#endif /* HAVE_DECL_CPU_SETSIZE */
-#ifndef MHD_DAUTH_DEF_TIMEOUT_
-# define MHD_DAUTH_DEF_TIMEOUT_ 90
-#endif /* ! MHD_DAUTH_DEF_TIMEOUT_ */
-#ifndef MHD_DAUTH_DEF_MAX_NC_
-# define MHD_DAUTH_DEF_MAX_NC_ 1000
-#endif /* ! MHD_DAUTH_DEF_MAX_NC_ */
+#ifndef MHD_AUTH_DIGEST_DEF_TIMEOUT
+# define MHD_AUTH_DIGEST_DEF_TIMEOUT 90
+#endif /* ! MHD_AUTH_DIGEST_DEF_TIMEOUT */
+#ifndef MHD_AUTH_DIGEST_DEF_MAX_NC
+# define MHD_AUTH_DIGEST_DEF_MAX_NC 1000
+#endif /* ! MHD_AUTH_DIGEST_DEF_MAX_NC */
/* Eclipse parse compatibility */
#ifdef __CDT_PARSER__
diff --git a/src/include/d_options.rec b/src/include/d_options.rec
@@ -81,7 +81,7 @@ CustomSetter: /* custom setter */
+ free (settings->bind_sa.v_sa);
+ settings->bind_sa.v_sa = malloc (option->val.bind_sa.v_sa_len);
+ if (NULL == settings->bind_sa.v_sa)
-+ return MHD_SC_DAEMON_MALLOC_FAILURE;
++ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
+ memcpy (settings->bind_sa.v_sa, option->val.bind_sa.v_sa,
+ option->val.bind_sa.v_sa_len);
+ settings->bind_sa.v_sa_len = option->val.bind_sa.v_sa_len;
@@ -189,7 +189,7 @@ CustomSetter: /* custom setter */
+ free (settings->tls_cert_key.v_mem_cert); // TODO: Support multiple certificates!!
+ settings->tls_cert_key.v_mem_cert = malloc (cert_size + key_size + pass_size);
+ if (NULL == settings->tls_cert_key.v_mem_cert)
-+ return MHD_SC_DAEMON_MALLOC_FAILURE;
++ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
+ memcpy (settings->tls_cert_key.v_mem_cert,
+ option->val.tls_cert_key.v_mem_cert,
+ cert_size);
@@ -423,6 +423,7 @@ Name: random_entropy
Value: 400
Comment: Set strong random data to be used by MHD.
+ Currently the data is only needed for Digest Auth module.
++ Daemon support for Digest Auth is enabled automatically if this option is used.
+ The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
+ Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
Argument1: size_t buf_size
@@ -439,7 +440,7 @@ CustomSetter: /* custom setter */
+ settings->random_entropy.v_buf =
+ malloc (option->val.random_entropy.v_buf_size);
+ if (NULL == settings->random_entropy.v_buf)
-+ return MHD_SC_DAEMON_MALLOC_FAILURE;
++ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
+ memcpy (settings->random_entropy.v_buf,
+ option->val.random_entropy.v_buf,
+ option->val.random_entropy.v_buf_size);
@@ -448,36 +449,26 @@ CustomSetter: /* custom setter */
+ }
-Name: dauth_map_size
+Name: auth_digest_map_size
Value: 401
Comment: Specify the size of the internal hash map array that tracks generated digest nonces usage.
+ When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
-+ By default the size is 8 bytes (very small).
++ By default the size is 1000 entries.
Argument1: size_t size
Description1: the size of the map array
-Name: dauth_nonce_bind_type
-Value: 402
-Type: enum MHD_DaemonOptionValueDAuthBindNonce
-Comment: Control the scope of validity of MHD-generated nonces.
-+ This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
-+ This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
-+ When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
-Argument1: enum MHD_DaemonOptionValueDAuthBindNonce bind_type
-Description1: FIXME
-
-Name: dauth_def_nonce_timeout
+Name: auth_digest_nonce_timeout
Value: 403
-Comment: Default nonce timeout value (in seconds) used for Digest Auth.
-+ Silently ignored if followed by zero value.
+Comment: Nonce validity time (in seconds) used for Digest Auth.
++ If followed by zero value the value is silently ignored.
+ @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
Argument1: unsigned int timeout
Description1: FIXME
-Name: dauth_def_max_nc
+Name: auth_digest_def_max_nc
Value: 404
Comment: Default maximum nc (nonce count) value used for Digest Auth.
-+ Silently ignored if followed by zero value.
++ If followed by zero value the value is silently ignored.
+ @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
Argument1: uint_fast32_t max_nc
Description1: FIXME
diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h
@@ -139,14 +139,49 @@
/* *INDENT-ON* */
#endif /* __cplusplus */
-#include "microhttpd2_portability.h"
+MHD_C_DECLRATIONS_START_HERE_
/**
- * Current version of the library.
- * Packed BCD: 0x01093001 = 1.9.30-1.
+ * Current version of the library in packed BCD form.
+ * (For example, version 1.9.30-1 would be 0x01093001)
*/
#define MHD_VERSION 0x01990001
+#include "microhttpd2_portability.h"
+
+/* If generic headers don't work on your platform, include headers which define
+ 'va_list', 'size_t', 'uint_least16_t', 'uint_fast32_t', 'uint_fast64_t',
+ 'struct sockaddr', and then "#define MHD_HAVE_SYS_HEADERS_INCLUDED" before
+ including "microhttpd2.h".
+ When 'MHD_HAVE_SYS_HEADERS_INCLUDED' is defined the following "standard"
+ includes won't be used (which might be a good idea, especially on platforms
+ where they do not exist).
+ */
+#ifndef MHD_HAVE_SYS_HEADERS_INCLUDED
+# include <stdarg.h>
+# ifndef MHD_SYS_BASE_TYPES_H
+/* Headers for uint_fastXX_t, size_t */
+# include <stdint.h>
+# include <stddef.h>
+# include <sys/types.h> /* This header is actually optional */
+# endif
+# ifndef MHD_SYS_SOCKET_TYPES_H
+/* Headers for 'struct sockaddr' */
+# if ! defined(_WIN32) || defined(__CYGWIN__)
+# include <sys/socket.h>
+# else
+/* Prevent conflict of <winsock.h> and <winsock2.h> */
+# if ! defined(_WINSOCK2API_) && ! defined(_WINSOCKAPI_)
+# ifndef WIN32_LEAN_AND_MEAN
+/* Do not use unneeded parts of W32 headers. */
+# define WIN32_LEAN_AND_MEAN 1
+# endif /* !WIN32_LEAN_AND_MEAN */
+# include <winsock2.h>
+# endif
+# endif
+# endif
+#endif
+
#ifndef MHD_BOOL_DEFINED
/**
@@ -205,7 +240,7 @@ struct MHD_String
struct MHD_StringNullable
{
/**
- * Number of characters in @e str, not counting 0-termination.
+ * Number of characters in @e cstr, not counting 0-termination.
* If @a cstr is NULL, it must be zero.
*/
size_t len;
@@ -220,6 +255,26 @@ struct MHD_StringNullable
#define MHD_STRINGS_DEFINED 1
#endif /* ! MHD_STRINGS_DEFINED */
+
+#ifndef MHD_INVALID_SOCKET
+# if ! defined(_WIN32) || defined(_SYS_TYPES_FD_SET)
+# define MHD_SOCKETS_KIND_POSIX 1
+/**
+ * MHD_Socket is a type for socket FDs
+ */
+typedef int MHD_Socket;
+# define MHD_INVALID_SOCKET (-1)
+# else /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
+# define MHD_SOCKETS_KIND_WINSOCK 1
+/**
+ * MHD_Socket is a type for socket FDs
+ */
+typedef SOCKET MHD_Socket;
+# define MHD_INVALID_SOCKET (INVALID_SOCKET)
+# endif /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
+#endif /* MHD_INVALID_SOCKET */
+
+
/**
* Constant used to indicate unknown size (use when creating a response).
* Any possible larger sizes are interpreted as the same value.
@@ -407,6 +462,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_APPLICATION_DATA_GENERATION_FINISHED = 20001
,
+ /**
+ * The request does not contain a particular type of Authentication
+ * credentials
+ */
+ MHD_SC_AUTH_ABSENT = 20060
+ ,
/* 30000-level status codes indicate transient failures
that might go away if the client tries again. */
@@ -461,19 +522,19 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
* Failed to allocate memory for the daemon resources.
* TODO: combine similar error codes for daemon
*/
- MHD_SC_DAEMON_MALLOC_FAILURE = 30081
+ MHD_SC_DAEMON_MEM_ALLOC_FAILURE = 30081
,
/**
* We failed to allocate memory for the connection.
* (May be transient.)
*/
- MHD_SC_CONNECTION_MALLOC_FAILURE = 30082
+ MHD_SC_CONNECTION_MEM_ALLOC_FAILURE = 30082
,
/**
* We failed to allocate memory for the connection's memory pool.
* (May be transient.)
*/
- MHD_SC_POOL_MALLOC_FAILURE = 30083
+ MHD_SC_POOL_MEM_ALLOC_FAILURE = 30083
,
/**
* We failed to forward data from a Web socket to the
@@ -487,7 +548,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
* the request. Likely the request fields are too large to leave
* enough room.
*/
- MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ = 30130
+ MHD_SC_CONNECTION_POOL_NO_MEM_REQ = 30130
,
/**
* Failed to allocate memory from our memory pool to store GET parameter.
@@ -501,6 +562,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_CONNECTION_POOL_NO_MEM_COOKIE = 30132
,
/**
+ * Failed to allocate memory from connection memory pool to store
+ * parsed Authentication data.
+ */
+ MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA = 30133
+ ,
+ /**
* Detected jump back of system clock
*/
MHD_SC_SYS_CLOCK_JUMP_BACK_LARGE = 30140
@@ -699,6 +766,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT = 40290
,
/**
+ * The data in Auth request header has invalid format.
+ * For example, for Basic Authentication base64 decoding failed.
+ */
+ MHD_SC_REQ_AUTH_DATA_BROKEN = 40320
+ ,
+ /**
* The request cannot be processed. Sending error reply.
*/
MHD_SC_REQ_PROCCESSING_ERR_REPLY = 41000
@@ -937,7 +1010,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
/**
* Failed to allocate memory for the thread pool.
*/
- MHD_SC_THREAD_POOL_MALLOC_FAILURE = 50090
+ MHD_SC_THREAD_POOL_MEM_ALLOC_FAILURE = 50090
,
/**
* We failed to allocate mutex for thread pool worker.
@@ -1158,6 +1231,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_REPLY_FILE_READ_ERROR = 50232
,
/**
+ * Failed to generate the nonce for the Digest Auth.
+ */
+ MHD_SC_REPLY_NONCE_ERROR = 50233
+ ,
+ /**
* Failed to allocate memory in connection's pool for the reply.
*/
MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE = 50250
@@ -1231,7 +1309,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
/**
* Unable to allocate memory for the response header
*/
- MHD_SC_RESPONSE_HEADER_MALLOC_FAILED = 50540
+ MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED = 50540
,
/**
* Failed to switch TCP_NODELAY option for the socket
@@ -1307,6 +1385,14 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_TLS_CONNECTION_HANDSHAKED_FAILED = 51220
,
/**
+ * Hashing failed.
+ * Internal hashing function can never fail (and this code is never returned
+ * for them). External hashing function (like TLS backend-based) may fail
+ * for various reasons, like failure of hardware acccelerated hashing.
+ */
+ MHD_SC_HASH_FAILED = 51260
+ ,
+ /**
* Something wrong in the internal MHD logic.
* This error should be never returned if MHD works as expected.
* If this code is ever returned, please report to MHD maintainers.
@@ -1318,114 +1404,121 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
logic did something wrong or generated an error. */
/**
+ * The application called function too early.
+ * For example, a header value was requested before the headers
+ * had been received.
+ */
+ MHD_SC_TOO_EARLY = 60000
+ ,
+ /**
+ * The application called this function too late.
+ * For example, MHD has already started sending reply.
+ */
+ MHD_SC_TOO_LATE = 60001
+ ,
+ /**
* MHD does not support the requested combination of
* the sockets polling syscall and the work mode.
*/
- MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID = 60000
+ MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID = 60010
,
/**
* MHD does not support quiescing if ITC was disabled
* and threads are used.
*/
- MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60001
+ MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60011
,
/**
* MHD is closing a connection because the application
* logic to generate the response data failed.
*/
- MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED = 60005
+ MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED = 60015
,
/**
* MHD is closing a connection because the application
* callback told it to do so.
*/
- MHD_SC_APPLICATION_CALLBACK_ABORT_ACTION = 60006
+ MHD_SC_APPLICATION_CALLBACK_ABORT_ACTION = 60016
,
/**
* Application only partially processed upload and did
* not suspend connection. This may result in a hung
* connection.
*/
- MHD_SC_APPLICATION_HUNG_CONNECTION = 60007
+ MHD_SC_APPLICATION_HUNG_CONNECTION = 60017
,
/**
* Application only partially processed upload and did
* not suspend connection and the read buffer was maxxed
* out, so MHD closed the connection.
*/
- MHD_SC_APPLICATION_HUNG_CONNECTION_CLOSED = 60008
- ,
- /**
- * Application called function too late, for example because
- * MHD already changed state.
- */
- MHD_SC_TOO_LATE = 60009
+ MHD_SC_APPLICATION_HUNG_CONNECTION_CLOSED = 60018
,
/**
* Attempted to set an option that conflicts with another option
* already set.
*/
- MHD_SC_OPTIONS_CONFLICT = 60010
+ MHD_SC_OPTIONS_CONFLICT = 60020
,
/**
* Attempted to set an option that not recognised by MHD.
*/
- MHD_SC_OPTION_UNKNOWN = 60011
+ MHD_SC_OPTION_UNKNOWN = 60021
,
/**
* Parameter specified unknown work mode.
*/
- MHD_SC_CONFIGURATION_UNEXPECTED_WM = 60012
+ MHD_SC_CONFIGURATION_UNEXPECTED_WM = 60022
,
/**
* Parameter specified unknown socket poll syscall.
*/
- MHD_SC_CONFIGURATION_UNEXPECTED_SPS = 60013
+ MHD_SC_CONFIGURATION_UNEXPECTED_SPS = 60023
,
/**
* The size of the provided sockaddr does not match address family.
*/
- MHD_SC_CONFIGURATION_WRONG_SA_SIZE = 60014
+ MHD_SC_CONFIGURATION_WRONG_SA_SIZE = 60024
,
/**
* The number set by #MHD_D_O_FD_NUMBER_LIMIT is too strict to run
* the daemon
*/
- MHD_SC_MAX_FD_NUMBER_LIMIT_TOO_STRICT = 60015
+ MHD_SC_MAX_FD_NUMBER_LIMIT_TOO_STRICT = 60025
,
/**
* The number set by #MHD_D_O_GLOBAL_CONNECTION_LIMIT is too for the daemon
* configuration
*/
- MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL = 60016
+ MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL = 60026
,
/**
* The application requested an unsupported TLS backend to be used.
*/
- MHD_SC_TLS_BACKEND_UNSUPPORTED = 60020
+ MHD_SC_TLS_BACKEND_UNSUPPORTED = 60030
,
/**
* The application attempted to setup TLS parameters before
* enabling TLS.
*/
- MHD_SC_TLS_BACKEND_UNINITIALIZED = 60021
+ MHD_SC_TLS_BACKEND_UNINITIALIZED = 60031
,
/**
* The application requested a TLS backend which cannot be used due
* to missing TLS dynamic library or backend initialisation problem.
*/
- MHD_SC_TLS_BACKEND_UNAVAILABLE = 60022
+ MHD_SC_TLS_BACKEND_UNAVAILABLE = 60032
,
/**
* Provided TLS certificate and/or private key are incorrect
*/
- MHD_SC_TLS_CONF_BAD_CERT = 60023
+ MHD_SC_TLS_CONF_BAD_CERT = 60033
,
/**
* The application requested a daemon setting that cannot be used with
* selected TLS backend
*/
- MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS = 60024
+ MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS = 60034
,
/**
* The response header name has forbidden characters or token
@@ -1438,6 +1531,21 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_RESP_HEADER_VALUE_INVALID = 60051
,
/**
+ * An attempt to add header conflicting with other response header
+ */
+ MHD_SC_RESP_HEADERS_CONFLICT = 60052
+ ,
+ /**
+ * The pointer to the response object is NULL
+ */
+ MHD_SC_RESP_POINTER_NULL = 60060
+ ,
+ /**
+ * The response HTTP status code is not suitable
+ */
+ MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE = 60061
+ ,
+ /**
* The provided MHD_Action is invalid
*/
MHD_SC_ACTION_INVALID = 60080
@@ -1498,14 +1606,14 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_UPGRADED_HANDLE_INVALID = 60161
,
/**
- * The requested type of information is not recognised.
+ * The provided output buffer is too small.
*/
- MHD_SC_INFO_TYPE_UNKNOWN = 60200
+ MHD_SC_OUT_BUFF_TOO_SMALL = 60180
,
/**
* The requested type of information is not recognised.
*/
- MHD_SC_INFO_GET_INFO_TYPE_UNKNOWN = 60200
+ MHD_SC_INFO_GET_TYPE_UNKNOWN = 60200
,
/**
* The information of the requested type is too large to fit into
@@ -1531,6 +1639,21 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
* cannot be provided due to some error or other reasons.
*/
MHD_SC_INFO_GET_TYPE_UNAVAILALBE = 60204
+ ,
+ /**
+ * The type of the Digest Auth algorithm is unknown or not supported.
+ */
+ MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED = 60240
+ ,
+ /**
+ * The Digest Auth QOP value is unknown or not supported.
+ */
+ MHD_SC_AUTH_DIGEST_QOP_NOT_SUPPORTED = 60241
+ ,
+ /**
+ * The Digest Auth is not supported due to configuration
+ */
+ MHD_SC_AUTH_DIGEST_UNSUPPORTED = 60242
};
/**
@@ -3231,85 +3354,88 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_SockPollSyscall
/**
- * Protocol strictness enforced by MHD on clients.
- * All levels have different parsing settings for the headers.
+ * Protocol strictness levels enforced by MHD on clients.
+ * Each level applies different parsing settings for HTTP headers and other
+ * protocol elements.
*/
enum MHD_FIXED_ENUM_APP_SET_ MHD_ProtocolStrictLevel
{
/* * Basic levels * */
/**
- * Sane level of protocol enforcement for production use.
- * A balance between extra security and broader compatibility,
- * as allowed by RFCs for HTTP servers.
+ * A sane default level of protocol enforcement for production use.
+ * Provides a balance between enhanced security and broader compatibility,
+ * as permitted by RFCs for HTTP servers.
*/
MHD_PSL_DEFAULT = 0
,
/**
- * Be strict about the protocol (as opposed to as tolerant as
- * possible), within the limits set by RFCs for HTTP servers.
- * This level (and more strict) forbids use of bare LF as
- * CRLF. It also rejects requests with both "Transfer-Encoding:"
- * and "Content-Length:".
- * It is suitable for public servers.
+ * Apply stricter protocol interpretation while remaining within
+ * RFC-defined limits for HTTP servers.
+ *
+ * At this level (and stricter), using a bare LF instead of CRLF is forbidden,
+ * and requests that include both a "Transfer-Encoding:" and
+ * a "Content-Length:" headers are rejected.
+ *
+ * Suitable for public servers.
*/
MHD_PSL_STRICT = 1
,
/**
- * Be particularly permissive about the protocol, within
- * the limits set by RFCs for HTTP servers.
+ * Be more permissive in interpreting the protocol, while still
+ * operating within the RFC-defined limits for HTTP servers.
*/
MHD_PSL_PERMISSIVE = -1
,
/* * Special levels * */
/**
- * Stricter protocol interpretation, even stricter then allowed
- * by RFCs for HTTP servers.
- * However it should be absolutely compatible with clients
- * following at least RFCs' "MUST" type of requirements
- * for HTTP clients.
- * For chunked encoding parsing this level (and more strict)
- * forbids whitespace in chunk extension.
- * For cookies parsing this (and more strict) level rejects
- * cookie in full even if a single value is encoded incorrectly
- * in it.
- * This level is recommended for testing clients against
- * MHD. Also can be used for security-centric application,
- * however it is slight violation of RFCs' requirements.
+ * A stricter protocol interpretation than what is allowed by RFCs for HTTP
+ * servers. However, it should remain fully compatible with clients correctly
+ * following all RFC "MUST" requirements for HTTP clients.
+ *
+ * For chunked encoding, this level (and more restrictive ones) forbids
+ * whitespace in chunk extensions.
+ * For cookie parsing, this level (and more restrictive ones) rejects
+ * the entire cookie if even a single value within it is incorrectly encoded.
+ *
+ * Recommended for testing clients against MHD. Can also be used for
+ * security-centric applications, though doing so slightly violates
+ * relevant RFC requirements for HTTP servers.
*/
MHD_PSL_VERY_STRICT = 2
,
/**
- * The most strict interpretation of the HTTP protocol,
- * much stricter that defined for HTTP servers by RFC.
- * However it should be absolutely compatible with clients
- * following RFCs' "SHOULD" and "MUST" types of requirements
- * for HTTP clients.
+ * The strictest interpretation of the HTTP protocol, even stricter than
+ * allowed by RFCs for HTTP servers.
+ * However, it should remain fully compatible with clients complying with both
+ * RFC "SHOULD" and "MUST" requirements for HTTP clients.
+ *
* This level can be used for testing clients against MHD.
- * It is not recommended for any public services as it may
- * reject legitimate clients (clients not following "SHOULD"
- * type of RFC requirements).
+ * It is not recommended for public services, as it may reject legitimate
+ * clients that do not follow RFC "SHOULD" requirements.
*/
MHD_PSL_EXTRA_STRICT = 3
,
/**
- * More relaxed protocol interpretation, violating RFCs'
- * "SHOULD" type of restrictions for HTTP servers.
- * For cookies parsing this (and more permissive) level
- * allows whitespaces in cookie values.
- * This level can be used in isolated environments.
+ * A more relaxed protocol interpretation that violates some RFC "SHOULD"
+ * restrictions for HTTP servers.
+ * For cookie parsing, this level (and more permissive levels) allows
+ * whitespace in cookie values.
+ *
+ * This level may be used in isolated environments.
*/
MHD_PSL_VERY_PERMISSIVE = -2
,
/**
- * The most flexible protocol interpretation, beyond
- * RFCs' "MUST" type of restrictions for HTTP server.
- * The level allow HTTP/1.1 requests without "Host:" header.
- * For cookies parsing this level adds allowance of
- * whitespaces before and after '=' character.
- * This level is not recommended unless it is absolutely
- * necessary to communicate with some client(s) with
- * badly broken HTTP implementation.
+ * The most flexible protocol interpretation, going beyond RFC "MUST"
+ * requirements for HTTP servers.
+ *
+ * This level allows HTTP/1.1 requests without a "Host:" header.
+ * For cookie parsing, whitespace is allowed before and after
+ * the '=' character.
+ *
+ * Not recommended unless absolutely necessary to communicate with clients
+ * that have severely broken HTTP implementations.
*/
MHD_PSL_EXTRA_PERMISSIVE = -3,
};
@@ -3790,6 +3916,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
MHD_REQUEST_ENDED_FILE_ERROR = 51
,
/**
+ * The request was aborted due to error generating valid nonce for Digest Auth
+ * @ingroup request
+ */
+ MHD_REQUEST_ENDED_NONCE_ERROR = 52
+ ,
+ /**
* Closing the session since MHD is being shut down.
* @ingroup request
*/
@@ -4309,6 +4441,7 @@ MHD_D_OPTION_NOTIFY_STREAM (
/**
* Set strong random data to be used by MHD.
* Currently the data is only needed for Digest Auth module.
+ * Daemon support for Digest Auth is enabled automatically if this option is used.
* The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
* Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
* @param buf_size the size of the buffer
@@ -4324,49 +4457,36 @@ MHD_D_OPTION_RANDOM_ENTROPY (
/**
* Specify the size of the internal hash map array that tracks generated digest nonces usage.
* When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
- * By default the size is 8 bytes (very small).
+ * By default the size is 1000 entries.
* @param size the size of the map array
* @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_MAP_SIZE (
+MHD_D_OPTION_AUTH_DIGEST_MAP_SIZE (
size_t size
);
/**
- * Control the scope of validity of MHD-generated nonces.
- * This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
- * This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
- * When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
- * @param bind_type FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_NONCE_BIND_TYPE (
- enum MHD_DaemonOptionValueDAuthBindNonce bind_type
- );
-
-/**
- * Default nonce timeout value (in seconds) used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * Nonce validity time (in seconds) used for Digest Auth.
+ * If followed by zero value the value is silently ignored.
* @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
* @param timeout FIXME
* @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_DEF_NONCE_TIMEOUT (
+MHD_D_OPTION_AUTH_DIGEST_NONCE_TIMEOUT (
unsigned int timeout
);
/**
* Default maximum nc (nonce count) value used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * If followed by zero value the value is silently ignored.
* @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
* @param max_nc FIXME
* @return structure with the requested setting
*/
struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_DEF_MAX_NC (
+MHD_D_OPTION_AUTH_DIGEST_DEF_MAX_NC (
uint_fast32_t max_nc
);
@@ -5013,18 +5133,18 @@ enum MHD_FLAGS_ENUM_ MHD_ValueKind
/**
* HTTP header.
*/
- MHD_VK_HEADER = (1 << 0)
+ MHD_VK_HEADER = (1u << 0)
,
/**
* Cookies. Note that the original HTTP header containing
* the cookie(s) will still be available and intact.
*/
- MHD_VK_COOKIE = (1 << 1)
+ MHD_VK_COOKIE = (1u << 1)
,
/**
* GET (URI) arguments.
*/
- MHD_VK_GET_ARGUMENT = (1 << 2)
+ MHD_VK_GET_ARGUMENT = (1u << 2)
,
/**
* POST data.
@@ -5038,12 +5158,12 @@ enum MHD_FLAGS_ENUM_ MHD_ValueKind
* to check used "Transfer-Encoding". While it is deprecated and not used
* by modern clients, formally it can be used.
*/
- MHD_VK_POSTDATA = (1 << 3)
+ MHD_VK_POSTDATA = (1u << 3)
,
/**
* HTTP footer (only for HTTP 1.1 chunked encodings).
*/
- MHD_VK_FOOTER = (1 << 4)
+ MHD_VK_FOOTER = (1u << 4)
,
/**
* Header and footer values
@@ -5174,8 +5294,8 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_OUT_SIZE_ (4,3);
*
* @param request request to get values from
* @param kind what kind of value are we looking for
- * @param key the header to look for, empty to lookup 'trailing' value
- * without a key
+ * @param key the name of the value looking for (used for case-insensetive
+ match), empty to lookup 'trailing' value without a key
* @return NULL if no such item was found
* @ingroup request
*/
@@ -6147,7 +6267,7 @@ MHD_FN_PAR_NONNULL_ (1);
/**
* Add a header line to the response.
*
- * @param response response to add a header to
+ * @param response response to add a header to, NULL is tolerated
* @param name the name of the header to add,
* an internal copy of the string will be made
* @param value the value of the header to add,
@@ -6160,7 +6280,6 @@ MHD_EXTERN_ enum MHD_StatusCode
MHD_response_add_header (struct MHD_Response *MHD_RESTRICT response,
const char *name,
const char *value)
-MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3);
@@ -6962,32 +7081,32 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestBaseAlgo
* MD5 hash algorithm.
* As specified by RFC1321
*/
- MHD_DIGEST_BASE_ALGO_MD5 = (1 << 0)
+ MHD_DIGEST_BASE_ALGO_MD5 = (1u << 0)
,
/**
* SHA-256 hash algorithm.
* As specified by FIPS PUB 180-4
*/
- MHD_DIGEST_BASE_ALGO_SHA256 = (1 << 1)
+ MHD_DIGEST_BASE_ALGO_SHA256 = (1u << 1)
,
/**
* SHA-512/256 hash algorithm.
* As specified by FIPS PUB 180-4
*/
- MHD_DIGEST_BASE_ALGO_SHA512_256 = (1 << 2)
+ MHD_DIGEST_BASE_ALGO_SHA512_256 = (1u << 2)
};
/**
* The flag indicating non-session algorithm types,
* like 'MD5', 'SHA-256' or 'SHA-512-256'.
*/
-#define MHD_DIGEST_AUTH_ALGO_NON_SESSION (1 << 6)
+#define MHD_DIGEST_AUTH_ALGO_NON_SESSION (1u << 6)
/**
* The flag indicating session algorithm types,
* like 'MD5-sess', 'SHA-256-sess' or 'SHA-512-256-sess'.
*/
-#define MHD_DIGEST_AUTH_ALGO_SESSION (1 << 7)
+#define MHD_DIGEST_AUTH_ALGO_SESSION (1u << 7)
/**
* Digest algorithm identification
@@ -6996,7 +7115,7 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthAlgo
{
/**
* Unknown or wrong algorithm type.
- * Used in struct MHD_DigestAuthInfo to indicate client value that
+ * Used in struct MHD_AuthDigestInfo to indicate client value that
* cannot by identified.
*/
MHD_DIGEST_AUTH_ALGO_INVALID = 0
@@ -7043,12 +7162,12 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthAlgo
/**
- * Get digest size for specified algorithm.
+ * Get digest size in bytes for specified algorithm.
*
* The size of the digest specifies the size of the userhash, userdigest
* and other parameters which size depends on used hash algorithm.
* @param algo the algorithm to check
- * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
+ * @return the size (in bytes) of the digest (either #MHD_MD5_DIGEST_SIZE or
* #MHD_SHA256_DIGEST_SIZE/MHD_SHA512_256_DIGEST_SIZE)
* or zero if the input value is not supported or not valid
* @sa #MHD_digest_auth_calc_userdigest()
@@ -7212,15 +7331,18 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthMultiAlgo
* upon return
* @param bin_buf_size the size of the @a userhash_bin buffer, must be
* at least #MHD_digest_get_hash_size() bytes long
- * @return MHD_SC_OK on success,
- * error code otherwise
+ * @return #MHD_SC_OK on success,
+ * #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ * #MHD_SC_HASH_FAILED if hashing failed,
+ * #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ * unknown or unsupported.
* @sa #MHD_digest_auth_calc_userhash_hex()
* @ingroup authentication
*/
MHD_EXTERN_ enum MHD_StatusCode
MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo algo,
- const char *username,
- const char *realm,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT realm,
size_t bin_buf_size,
void *MHD_RESTRICT userhash_bin)
MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_ (2)
@@ -7258,17 +7380,20 @@ MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4);
* @param[out] userhash_hex the output buffer for userhash as hex string;
* if this function succeeds, then this buffer has
* #MHD_digest_get_hash_size()*2 chars long
- * userhash zero-terminated string
- * @return MHD_SC_OK on success,
- * error code otherwise
+ * userhash string plus one zero-termination char
+ * @return #MHD_SC_OK on success,
+ * #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ * #MHD_SC_HASH_FAILED if hashing failed,
+ * #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ * unknown or unsupported.
* @sa #MHD_digest_auth_calc_userhash()
* @ingroup authentication
*/
MHD_EXTERN_ enum MHD_StatusCode
MHD_digest_auth_calc_userhash_hex (
enum MHD_DigestAuthAlgo algo,
- const char *username,
- const char *realm,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT realm,
size_t hex_buf_size,
char userhash_hex[MHD_FN_PAR_DYN_ARR_SIZE_ (hex_buf_size)])
MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_ (2)
@@ -7281,7 +7406,7 @@ MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4);
* Values are sorted so simplified checks could be used.
* For example:
* * (value <= MHD_DIGEST_AUTH_UNAME_TYPE_INVALID) is true if no valid username
- * is provided by the client
+ * is provided by the client (not used currently)
* * (value >= MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH) is true if username is
* provided in any form
* * (value >= MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD) is true if username is
@@ -7291,14 +7416,15 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthUsernameType
{
/**
* No username parameter in in Digest Authorization header.
- * This should be treated as an error.
+ * Not used currently. Value #MHD_SC_REQ_AUTH_DATA_BROKEN is returned
+ * by #MHD_request_get_info_dynamic_sz() if the request has no username.
*/
MHD_DIGEST_AUTH_UNAME_TYPE_MISSING = 0
,
/**
* The 'username' parameter is used to specify the username.
*/
- MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD = (1 << 2)
+ MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD = (1u << 2)
,
/**
* The username is specified by 'username*' parameter with
@@ -7306,24 +7432,26 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthUsernameType
* The only difference between standard and extended types is
* the way how username value is encoded in the header.
*/
- MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED = (1 << 3)
+ MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED = (1u << 3)
,
/**
* The username provided in form of 'userhash' as
* specified by RFC 7616, section-3.4.4.
* @sa #MHD_digest_auth_calc_userhash_hex(), #MHD_digest_auth_calc_userhash()
*/
- MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH = (1 << 1)
+ MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH = (1u << 1)
,
/**
* The invalid combination of username parameters are used by client.
* Either:
- * * both 'username' and 'username*' are used
- * * 'username*' is used with 'userhash=true'
- * * 'username*' used with invalid extended notation
- * * 'username' is not hexadecimal string, while 'userhash' set to 'true'
- */
- MHD_DIGEST_AUTH_UNAME_TYPE_INVALID = (1 << 0)
+ * + both 'username' and 'username*' are used
+ * + 'username*' is used with 'userhash=true'
+ * + 'username*' used with invalid extended notation
+ * + 'username' is not hexadecimal string, while 'userhash' set to 'true'
+ * Not used currently. Value #MHD_SC_REQ_AUTH_DATA_BROKEN is returned
+ * by #MHD_request_get_info_dynamic_sz() if the request has broken username.
+ */
+ MHD_DIGEST_AUTH_UNAME_TYPE_INVALID = (1u << 0)
};
/**
@@ -7333,7 +7461,7 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthQOP
{
/**
* Invalid/unknown QOP.
- * Used in struct MHD_DigestAuthInfo to indicate client value that
+ * Used in struct MHD_AuthDigestInfo to indicate client value that
* cannot by identified.
*/
MHD_DIGEST_AUTH_QOP_INVALID = 0
@@ -7346,18 +7474,18 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthQOP
* parameter).
* This mode is less secure than other modes and inefficient.
*/
- MHD_DIGEST_AUTH_QOP_NONE = 1 << 0
+ MHD_DIGEST_AUTH_QOP_NONE = (1u << 0)
,
/**
* The 'auth' QOP type.
*/
- MHD_DIGEST_AUTH_QOP_AUTH = 1 << 1
+ MHD_DIGEST_AUTH_QOP_AUTH = (1u << 1)
,
/**
* The 'auth-int' QOP type.
* Not supported by MHD for authentication.
*/
- MHD_DIGEST_AUTH_QOP_AUTH_INT = 1 << 2
+ MHD_DIGEST_AUTH_QOP_AUTH_INT = (1u << 2)
};
/**
@@ -7413,16 +7541,52 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthMultiQOP
};
/**
- * The invalid value of 'nc' parameter in client Digest Authorization header.
+ * The type of 'nc' (nonce count) value provided in the request
*/
-#define MHD_DIGEST_AUTH_INVALID_NC_VALUE (0)
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthNC
+{
+ /**
+ * Readable hexdecimal non-zero number.
+ * The decoded value is placed in @a nc member of struct MHD_AuthDigestInfo
+ */
+ MHD_DIGEST_AUTH_NC_NUMBER = 1
+ ,
+ /**
+ * Readable zero number.
+ * Compliant clients should not use such values.
+ * Can be treated as invalid request.
+ */
+ MHD_DIGEST_AUTH_NC_ZERO = 2
+ ,
+ /**
+ * 'nc' value is not provided by the client.
+ * Unless old RFC 2069 mode is allowed, this should be treated as invalid
+ * request.
+ */
+ MHD_DIGEST_AUTH_NC_NONE = 3
+ ,
+ /**
+ * 'nc' value is too long to be decoded.
+ * Compliant clients should not use such values.
+ * Can be treated as invalid request.
+ */
+ MHD_DIGEST_AUTH_NC_TOO_LONG = 4
+ ,
+ /**
+ * 'nc' value is too large for uint32_t.
+ * Compliant clients should not use such values.
+ * Can be treated as request with a stale nonce or as invalid request.
+ */
+ MHD_DIGEST_AUTH_NC_TOO_LARGE = 5
+};
+
/**
* Information from Digest Authorization client's header.
*
- * @see #MHD_REQUEST_INFO_DYNAMIC_DAUTH_REQ_INFO
+ * @see #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO
*/
-struct MHD_DigestAuthInfo
+struct MHD_AuthDigestInfo
{
/**
* The algorithm as defined by client.
@@ -7467,7 +7631,7 @@ struct MHD_DigestAuthInfo
* data.
* @sa #MHD_digest_auth_calc_userhash()
*/
- uint8_t *userhash_bin;
+ const uint8_t *userhash_bin;
/**
* The size of the data pointed by @a userhash_bin.
@@ -7503,13 +7667,17 @@ struct MHD_DigestAuthInfo
size_t cnonce_len;
/**
- * The nc parameter value.
+ * The type of 'nc' (nonce count) value provided in the request.
+ */
+ enum MHD_DigestAuthNC nc_type;
+
+ /**
+ * The nc (nonce count) parameter value.
* Can be used by application to limit the number of nonce re-uses. If @a nc
* is higher than application wants to allow, then "auth required" response
* with 'stale=true' could be used to force client to retry with the fresh
* 'nonce'.
- * If not specified by client or does not have hexadecimal digits only, the
- * value is #MHD_DIGEST_AUTH_INVALID_NC_VALUE.
+ * Set to zero when @a nc_type is not set to #MHD_DIGEST_AUTH_NC_NUMBER.
*/
uint_fast32_t nc;
};
@@ -7518,9 +7686,9 @@ struct MHD_DigestAuthInfo
/**
* Information from Digest Authorization client's header.
*
- * @see #MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO
+ * @see #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME
*/
-struct MHD_DigestAuthUsernameInfo
+struct MHD_AuthDigestUsernameInfo
{
/**
* The algorithm as defined by client.
@@ -7531,7 +7699,10 @@ struct MHD_DigestAuthUsernameInfo
/**
* The type of username used by client.
* The 'invalid' and 'missing' types are not used in this structure,
- * instead NULL is returned for #MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO.
+ * instead #MHD_SC_REQ_AUTH_DATA_BROKEN is returned when
+ * #MHD_request_get_info_dynamic_sz() called with
+ * #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME and the request has
+ * a broken username data.
*/
enum MHD_DigestAuthUsernameType uname_type;
@@ -7546,7 +7717,7 @@ struct MHD_DigestAuthUsernameInfo
* The buffer pointed by the @a username becomes invalid when a response
* for the requested is provided (or request is aborted).
*/
- struct MHD_String username;
+ struct MHD_StringNullable username;
/**
* The userhash string.
@@ -7557,7 +7728,7 @@ struct MHD_DigestAuthUsernameInfo
* for the requested is provided (or request is aborted).
* @sa #MHD_digest_auth_calc_userhash_hex()
*/
- struct MHD_String userhash_hex;
+ struct MHD_StringNullable userhash_hex;
/**
* The userhash decoded to binary form.
@@ -7573,7 +7744,7 @@ struct MHD_DigestAuthUsernameInfo
* data.
* @sa #MHD_digest_auth_calc_userhash()
*/
- uint8_t *userhash_bin;
+ const uint8_t *userhash_bin;
};
@@ -7591,45 +7762,66 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
,
/**
* General error, like "out of memory".
+ * Authentication may be valid, but cannot be checked.
*/
MHD_DAUTH_ERROR = 0
,
/**
- * No "Authorization" header or wrong format of the header.
- * Also may be returned if required parameters in client Authorisation header
- * are missing or broken (in invalid format).
+ * No "Authorization" header for Digest Authentication.
+ */
+ MHD_DAUTH_HEADER_MISSING = -1
+ ,
+ /**
+ * Wrong format of the header.
+ * Also returned if required parameters in Authorization header are missing
+ * or broken (in invalid format).
*/
- MHD_DAUTH_WRONG_HEADER = -1
+ MHD_DAUTH_HEADER_BROKEN = -9
+ ,
+ /**
+ * Unsupported algorithm.
+ */
+ MHD_DAUTH_UNSUPPORTED_ALGO = -10
+ ,
+ /**
+ * Unsupported 'qop'.
+ */
+ MHD_DAUTH_UNSUPPORTED_QOP = -11
+ ,
+ /**
+ * Incorrect userdigest size.
+ */
+ MHD_DAUTH_INVALID_USERDIGEST_SIZE = -15
,
/**
* Wrong 'username'.
*/
- MHD_DAUTH_WRONG_USERNAME = -2
+ MHD_DAUTH_WRONG_USERNAME = -17
,
/**
* Wrong 'realm'.
*/
- MHD_DAUTH_WRONG_REALM = -3
+ MHD_DAUTH_WRONG_REALM = -18
,
/**
* Wrong 'URI' (or URI parameters).
*/
- MHD_DAUTH_WRONG_URI = -4
+ MHD_DAUTH_WRONG_URI = -19
,
/**
* Wrong 'qop'.
*/
- MHD_DAUTH_WRONG_QOP = -5
+ MHD_DAUTH_WRONG_QOP = -20
,
/**
* Wrong 'algorithm'.
*/
- MHD_DAUTH_WRONG_ALGO = -6
+ MHD_DAUTH_WRONG_ALGO = -21
,
/**
* Too large (>64 KiB) Authorization parameter value.
*/
- MHD_DAUTH_TOO_LARGE = -15
+ MHD_DAUTH_TOO_LARGE = -22
,
/* The different form of naming is intentionally used for the results below,
* as they are more important */
@@ -7639,22 +7831,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
* username and password to get the fresh 'nonce'.
* The validity of the 'nonce' may be not checked.
*/
- MHD_DAUTH_NONCE_STALE = -17
- ,
- /**
- * The 'nonce' was generated by MHD for other conditions.
- * This value is only returned if #MHD_D_O_DAUTH_NONCE_BIND_TYPE is set
- * to anything other than #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
- * The interpretation of this code could be different. For example, if
- * #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_URI is set and client just used
- * the same 'nonce' for another URI, the code could be handled as
- * #MHD_DAUTH_NONCE_STALE as RFCs allow nonces re-using for other URIs
- * in the same "protection space".
- * However, if only #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_CLIENT_IP bit is set
- * and it is know that clients have fixed IP addresses, this return code could
- * be handled like #MHD_DAUTH_NONCE_WRONG.
- */
- MHD_DAUTH_NONCE_OTHER_COND = -18
+ MHD_DAUTH_NONCE_STALE = -25
,
/**
* The 'nonce' is wrong. May indicate an attack attempt.
@@ -7662,7 +7839,8 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
MHD_DAUTH_NONCE_WRONG = -33
,
/**
- * The 'response' is wrong. May indicate an attack attempt.
+ * The 'response' is wrong. May indicate a wrong password used or
+ * an attack attempt.
*/
MHD_DAUTH_RESPONSE_WRONG = -34
};
@@ -7684,9 +7862,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
* @param username the username to be authenticated, must be in clear text
* even if userhash is used by the client
* @param password the password matching the @a username (and the @a realm)
- * @param nonce_timeout the period of seconds since nonce generation, when
- * the nonce is recognised as valid and not stale;
- * if zero is specified then daemon default value is used.
* @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
* exceeds the specified value then MHD_DAUTH_NONCE_STALE is
* returned;
@@ -7699,18 +7874,15 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
* @ingroup authentication
*/
MHD_EXTERN_ enum MHD_DigestAuthResult
-MHD_digest_auth_check (struct MHD_Request *request,
- const char *realm,
- const char *username,
- const char *password,
- unsigned int nonce_timeout,
+MHD_digest_auth_check (struct MHD_Request *MHD_RESTRICT request,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT password,
uint_fast32_t max_nc,
enum MHD_DigestAuthMultiQOP mqop,
enum MHD_DigestAuthMultiAlgo malgo)
-MHD_FN_PAR_NONNULL_ (1)
-MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
-MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3)
-MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
+MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4);
/**
@@ -7739,17 +7911,20 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
* #MHD_digest_get_hash_size() bytes of
* userdigest upon return
* @return #MHD_SC_OK on success,
- * error code otherwise.
+ * #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ * #MHD_SC_HASH_FAILED if hashing failed,
+ * #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ * unknown or unsupported.
* @sa #MHD_digest_auth_check_digest()
* @ingroup authentication
*/
MHD_EXTERN_ enum MHD_StatusCode
MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo algo,
- const char *username,
- const char *realm,
- const char *password,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT password,
size_t bin_buf_size,
- void *userdigest_bin)
+ void *MHD_RESTRICT userdigest_bin)
MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_CSTR_ (3)
@@ -7773,62 +7948,70 @@ MHD_FN_PAR_OUT_SIZE_ (6,5);
* @param realm the realm for authorization of the client
* @param username the username to be authenticated, must be in clear text
* even if userhash is used by the client
- * @param userdigest the precalculated binary hash of the string
- * "username:realm:password",
- * see #MHD_digest_auth_calc_userdigest()
* @param userdigest_size the size of the @a userdigest in bytes, must match the
* hashing algorithm (see #MHD_MD5_DIGEST_SIZE,
* #MHD_SHA256_DIGEST_SIZE, #MHD_SHA512_256_DIGEST_SIZE,
* #MHD_digest_get_hash_size())
- * @param nonce_timeout the period of seconds since nonce generation, when
- * the nonce is recognised as valid and not stale;
- * if zero is specified then daemon default value is used.
+ * @param userdigest the precalculated binary hash of the string
+ * "username:realm:password",
+ * see #MHD_digest_auth_calc_userdigest()
* @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
* exceeds the specified value then MHD_DAUTH_NONCE_STALE is
* returned;
* if zero is specified then daemon default value is used.
* @param mqop the QOP to use
* @param malgo digest algorithms allowed to use, fail if algorithm used
- * by the client is not allowed by this parameter;
- * more than one base algorithms (MD5, SHA-256, SHA-512/256)
- * cannot be used at the same time for this function
- * as @a userdigest must match specified algorithm
+ * by the client is not allowed by this parameter;
+ * more than one base algorithms (MD5, SHA-256, SHA-512/256)
+ * cannot be used at the same time for this function
+ * as @a userdigest must match specified algorithm
* @return #MHD_DAUTH_OK if authenticated,
* the error code otherwise
* @sa #MHD_digest_auth_calc_userdigest()
* @ingroup authentication
*/
MHD_EXTERN_ enum MHD_DigestAuthResult
-MHD_digest_auth_check_digest (struct MHD_Request *request,
- const char *realm,
- const char *username,
- const void *userdigest,
+MHD_digest_auth_check_digest (struct MHD_Request *MHD_RESTRICT request,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT username,
size_t userdigest_size,
- unsigned int nonce_timeout,
+ const void *MHD_RESTRICT userdigest,
uint_fast32_t max_nc,
enum MHD_DigestAuthMultiQOP mqop,
enum MHD_DigestAuthMultiAlgo malgo)
MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_CSTR_ (3)
-MHD_FN_PAR_CSTR_ (4);
+MHD_FN_PAR_IN_SIZE_ (5, 4);
/**
- * Create an action to request authentication from the client
+ * Add Digest Authentication "challenge" to the response.
+ *
+ * The response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
- * 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 both RFC 2069 (#MHD_DIGEST_AUTH_QOP_NONE) and other QOP
+ * values, then the "challenge" is formed like if MHD_DIGEST_AUTH_QOP_NONE bit
+ * was not set, because such "challenge" 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.
*
- * At most one action can be created for any request.
+ * New nonces are generated each time when the resulting response is used.
*
- * @param request the request
+ * See RFC 7616, section 3.3 for details.
+ *
+ * @param response the response to update; should contain the "access denied"
+ * body;
+ * note: this function sets the "WWW Authenticate" header and
+ * the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_SC_RESP_POINTER_NULL)
* @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;
@@ -7843,24 +8026,17 @@ MHD_FN_PAR_CSTR_ (4);
* credentials could be used for any URI on the same host);
* this list provides information for the client only and does
* not actually restrict anything on the server side
- * @param response the reply to send; should contain the "access denied"
- * body;
- * note: this function sets the "WWW Authenticate" header and
- * the caller should not set this header;
- * the response must have #MHD_HTTP_STATUS_FORBIDDEN status
- * code, must not have #MHD_R_O_REUSABLE enabled;
- * the NULL is tolerated (the result is NULL)
- * @param signal_stale if set to #MHD_YES then indication of stale nonce used in
- * the client's request is signalled by adding '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 indicate_stale if set to #MHD_YES then indication of stale nonce used
+ * in the client's request is indicated by adding
+ * '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 algo digest algorithm to use; 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, allowing client to provide
+ * then one challenge for each allowed algorithm is added
+ * @param userhash_support if set to #MHD_YES then support of userhash is
+ * indicated, allowing client to provide
* hash("username:realm") instead of the username in
* clear text;
* note that clients are allowed to provide the username
@@ -7872,86 +8048,464 @@ MHD_FN_PAR_CSTR_ (4);
* @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
* added, indicating for the client that UTF-8 encoding for
* the username is preferred
- * @return pointer to the action on success,
- * NULL on failure
+ * @return #MHD_SC_OK if succeed,
+ * #MHD_SC_TOO_LATE if the response has been already "frozen" (used to
+ * create an action),
+ * #MHD_SC_RESP_HEADERS_CONFLICT if Digest Authentication "challenge"
+ * has been added already,
+ * #MHD_SC_RESP_POINTER_NULL if @a response is NULL,
+ * #MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE is response status code is wrong,
+ * #MHD_SC_RESP_HEADER_VALUE_INVALID if @a realm, @a opaque or @a domain
+ * have wrong characters or zero length (for @a realm),
+ * #MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED if memory allocation failed,
+ * or other error code if failed
* @ingroup authentication
*/
-MHD_EXTERN_ const struct MHD_Action *
-MHD_action_digest_auth_required_response (
- struct MHD_Request *request,
- const char *realm,
- const char *opaque,
- const char *domain,
- struct MHD_Response *response,
- enum MHD_Bool signal_stale,
+MHD_EXTERN_ enum MHD_StatusCode
+MHD_response_add_auth_digest_challenge (
+ struct MHD_Response *MHD_RESTRICT response,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT opaque,
+ const char *MHD_RESTRICT domain,
+ enum MHD_Bool indicate_stale,
enum MHD_DigestAuthMultiQOP mqop,
- enum MHD_DigestAuthMultiAlgo algo,
+ enum MHD_DigestAuthMultiAlgo malgo,
enum MHD_Bool userhash_support,
enum MHD_Bool prefer_utf8)
-MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
-MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_NONNULL_ (5);
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4);
+#ifndef MHD_NO_STATIC_INLINE
+
/**
- * Constant to indicate that the nonce of the provided
- * authentication code was wrong.
- * Used as return code by #MHD_digest_auth_check(),
- * #MHD_digest_auth_check_digest()
+ * Create action to reply with Digest Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * See RFC 7616, section 3.3 for details.
+ *
+ * @param request the request to create the action for
+ * @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);
+ * this list provides information for the client only and does
+ * not actually restrict anything on the server side
+ * @param indicate_stale if set to #MHD_YES then indication of stale nonce used
+ * in the client's request is indicated by adding
+ * '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 algo digest algorithm to use; if several algorithms are allowed
+ * then one challenge for each allowed algorithm is added
+ * @param userhash_support if set to #MHD_YES then support of userhash is
+ * indicated, allowing client to provide
+ * hash("username:realm") instead of the 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 for
+ * the username is preferred
+ * @param response the response to update; should contain the "access denied"
+ * body;
+ * note: this function sets the "WWW Authenticate" header and
+ * the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_SC_RESP_POINTER_NULL)
+ * @param abort_if_failed if set to #MHD_NO the response will be used even if
+ * failed to add Basic Authentication "challenge",
+ * if not set to #MHD_NO the request will be aborted
+ * if the "challenge" could not be added.
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
* @ingroup authentication
*/
-#define MHD_INVALID_NONCE -1
+MHD_STATIC_INLINE_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+const struct MHD_Action *
+MHD_action_digest_auth_challenge (struct MHD_Request *MHD_RESTRICT request,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT opaque,
+ const char *MHD_RESTRICT domain,
+ enum MHD_Bool indicate_stale,
+ enum MHD_DigestAuthMultiQOP mqop,
+ enum MHD_DigestAuthMultiAlgo malgo,
+ enum MHD_Bool userhash_support,
+ enum MHD_Bool prefer_utf8,
+ struct MHD_Response *MHD_RESTRICT response,
+ enum MHD_Bool abort_if_failed)
+{
+ if ((MHD_SC_OK !=
+ MHD_response_add_auth_digest_challenge (response, realm, opaque, domain,
+ indicate_stale, mqop, malgo,
+ userhash_support, prefer_utf8))
+ && (MHD_NO != abort_if_failed))
+ {
+ MHD_response_destroy (response);
+ return MHD_action_abort_request (request);
+ }
+ return MHD_action_from_response (request, response);
+}
+
+MHD_STATIC_INLINE_END_
/**
- * Information decoded from Basic Authentication client's header.
+ * Create action to reply with Digest Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * If the @a response object cannot be extended with the "challenge",
+ * the @a response is used to reply without the "challenge".
*
- * @see #MHD_REQUEST_INFO_DYNAMIC_BAUTH_REQ_INFO
+ * @param request the request to create the action for
+ * @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);
+ * this list provides information for the client only and does
+ * not actually restrict anything on the server side
+ * @param indicate_stale if set to #MHD_YES then indication of stale nonce used
+ * in the client's request is indicated by adding
+ * '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 algo digest algorithm to use; if several algorithms are allowed
+ * then one challenge for each allowed algorithm is added
+ * @param userhash_support if set to #MHD_YES then support of userhash is
+ * indicated, allowing client to provide
+ * hash("username:realm") instead of the 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 for
+ * the username is preferred
+ * @param response the response to update; should contain the "access denied"
+ * body;
+ * note: this function sets the "WWW Authenticate" header and
+ * the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_SC_RESP_POINTER_NULL)
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
+ * @ingroup authentication
*/
-struct MHD_BasicAuthInfo
-{
- /**
- * The username
- */
- struct MHD_String username;
+#define MHD_action_digest_auth_challenge_p(rq,rlm,opq,dmn,stl,mqop,malgo, \
+ uh,utf,resp) \
+ MHD_action_digest_auth_challenge ((rq),(rlm),(opq),(dmn),(stl),(mqop), \
+ (malgo),(uh),(utf),(resp),MHD_NO)
- /**
- * The password, string pointer may be NULL if password is not encoded
- * by the client.
- */
- struct MHD_StringNullable password;
-};
/**
- * Send a response to request basic authentication from the client.
+ * Create action to reply with Digest Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * If the @a response object cannot be extended with the "challenge",
+ * the @a response is aborted.
+ *
+ * @param request the request to create the action for
+ * @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);
+ * this list provides information for the client only and does
+ * not actually restrict anything on the server side
+ * @param indicate_stale if set to #MHD_YES then indication of stale nonce used
+ * in the client's request is indicated by adding
+ * '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 algo digest algorithm to use; if several algorithms are allowed
+ * then one challenge for each allowed algorithm is added
+ * @param userhash_support if set to #MHD_YES then support of userhash is
+ * indicated, allowing client to provide
+ * hash("username:realm") instead of the 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 for
+ * the username is preferred
+ * @param response the response to update; should contain the "access denied"
+ * body;
+ * note: this function sets the "WWW Authenticate" header and
+ * the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_SC_RESP_POINTER_NULL)
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
+ * @ingroup authentication
+ */
+#define MHD_action_digest_auth_challenge_a(rq,rlm,opq,dmn,stl,mqop,malgo, \
+ uh,utf,resp) \
+ MHD_action_digest_auth_challenge ((rq),(rlm),(opq),(dmn),(stl),(mqop), \
+ (malgo),(uh),(utf),(resp),MHD_YES)
+
+#endif /* ! MHD_NO_STATIC_INLINE */
+
+
+/**
+ * Add Basic Authentication "challenge" to the response.
+ *
+ * The response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * If access to any resource should be limited to specific users, authenticated
+ * by Basic Authentication mechanism, and the request for this resource does not
+ * have Basic Authentication information (see #MHD_AuthBasicCreds), then response
+ * with Basic Authentication "challenge" should be sent. This works as
+ * an indication that Basic Authentication should be used for the access.
*
* See RFC 7617, section-2 for details.
*
- * At most one action can be created for any request.
+ * @param response the reply to send; should contain the "access denied"
+ * body;
+ * note: this function sets the "WWW Authenticate" header and
+ * the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_SC_RESP_POINTER_NULL)
+ * @param realm the realm presented to the client
+ * @param prefer_utf8 if not set to #MHD_NO, parameter'charset="UTF-8"' will
+ * be added, indicating for client that UTF-8 encoding
+ * is preferred
+ * @return #MHD_SC_OK if succeed,
+ * #MHD_SC_TOO_LATE if the response has been already "frozen" (used to
+ * create an action),
+ * #MHD_SC_RESP_HEADERS_CONFLICT if Basic Authentication "challenge"
+ * has been added already,
+ * #MHD_SC_RESP_POINTER_NULL if @a response is NULL,
+ * #MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE is response status code is wrong,
+ * #MHD_SC_RESP_HEADER_VALUE_INVALID if realm is zero-length or has CR
+ * or LF characters,
+ * #MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED if memory allocation failed,
+ * or other error code if failed
+ * @ingroup authentication
+ */
+MHD_EXTERN_ enum MHD_StatusCode
+MHD_response_add_auth_basic_challenge (
+ struct MHD_Response *MHD_RESTRICT response,
+ const char *MHD_RESTRICT realm,
+ enum MHD_Bool prefer_utf8)
+MHD_FN_PAR_NONNULL_(2) MHD_FN_PAR_CSTR_ (2);
+
+#ifndef MHD_NO_STATIC_INLINE
+
+/**
+ * Create action to reply with Basic Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
- * @param connection the MHD connection structure
+ * If access to any resource should be limited to specific users, authenticated
+ * by Basic Authentication mechanism, and the request for this resource does not
+ * have Basic Authentication information (see #MHD_AuthBasicCreds), then response
+ * with Basic Authentication "challenge" should be sent. This works as
+ * an indication that Basic Authentication should be used for the access.
+ *
+ * See RFC 7617, section-2 for details.
+ *
+ * @param request the request to create the action for
* @param realm the realm presented to the client
* @param prefer_utf8 if not set to #MHD_NO, parameter'charset="UTF-8"' will
* be added, indicating for client that UTF-8 encoding
* is preferred
* @param response the reply to send; should contain the "access denied"
* body;
- * note: this function sets the "WWW Authenticate" header and
- * the caller should not set this header;
- * the response must have #MHD_HTTP_STATUS_FORBIDDEN status
- * code, must not have #MHD_R_O_REUSABLE enabled;
- * the NULL is tolerated (the result is NULL)
- * @return pointer to the action on success,
- * NULL on failure
+ * note: this function adds the "WWW Authenticate" header in
+ * the response and the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_action_abort_request())
+ * @param abort_if_failed if set to #MHD_NO the response will be used even if
+ * failed to add Basic Authentication "challenge",
+ * if not set to #MHD_NO the request will be aborted
+ * if the "challenge" could not be added.
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
* @ingroup authentication
*/
-MHD_EXTERN_ const struct MHD_Action *
-MHD_action_basic_auth_required_response (
- struct MHD_Connection *connection,
- const char *realm,
- enum MHD_Bool prefer_utf8,
- struct MHD_Response *response);
+MHD_STATIC_INLINE_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+const struct MHD_Action *
+MHD_action_basic_auth_challenge (struct MHD_Request *MHD_RESTRICT request,
+ const char *MHD_RESTRICT realm,
+ enum MHD_Bool prefer_utf8,
+ struct MHD_Response *MHD_RESTRICT response,
+ enum MHD_Bool abort_if_failed)
+{
+ if ((MHD_SC_OK !=
+ MHD_response_add_auth_basic_challenge (response, realm, prefer_utf8))
+ && (MHD_NO != abort_if_failed))
+ {
+ MHD_response_destroy (response);
+ return MHD_action_abort_request (request);
+ }
+ return MHD_action_from_response (request, response);
+}
+MHD_STATIC_INLINE_END_
+
+
+/**
+ * Create action to reply with Basic Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * If the @a response object cannot be extended with the "challenge",
+ * the @a response will be used to reply without the "challenge".
+ *
+ * @param request the request to create the action for
+ * @param realm the realm presented to the client
+ * @param prefer_utf8 if not set to #MHD_NO, parameter'charset="UTF-8"' will
+ * be added, indicating for client that UTF-8 encoding
+ * is preferred
+ * @param response the reply to send; should contain the "access denied"
+ * body;
+ * note: this function adds the "WWW Authenticate" header in
+ * the response and the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_action_abort_request())
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
+ * @ingroup authentication
+ */
+#define MHD_action_basic_auth_challenge_p(request,realm,prefer_utf8,response) \
+ MHD_action_basic_auth_challenge ((request), (realm), (prefer_utf8), \
+ (response), MHD_NO)
+
+/**
+ * Create action to reply with Basic Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * If the @a response object cannot be extended with the "challenge",
+ * the request will be aborted.
+ *
+ * @param request the request to create the action for
+ * @param realm the realm presented to the client
+ * @param prefer_utf8 if not set to #MHD_NO, parameter'charset="UTF-8"' will
+ * be added, indicating for client that UTF-8 encoding
+ * is preferred
+ * @param response the reply to send; should contain the "access denied"
+ * body;
+ * note: this function adds the "WWW Authenticate" header in
+ * the response and the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_action_abort_request())
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
+ * @ingroup authentication
+ */
+#define MHD_action_basic_auth_challenge_a(request,realm,prefer_utf8,response) \
+ MHD_action_basic_auth_challenge ((request), (realm), (prefer_utf8), \
+ (response), MHD_YES)
+
+#endif /* ! MHD_NO_STATIC_INLINE */
+
+
+/**
+ * Information decoded from Basic Authentication client's header.
+ *
+ * @see #MHD_REQUEST_INFO_DYNAMIC_AUTH_BASIC_CREDS
+ */
+struct MHD_AuthBasicCreds
+{
+ /**
+ * The username
+ */
+ struct MHD_String username;
+
+ /**
+ * The password, string pointer may be NULL if password is not encoded
+ * by the client.
+ */
+ struct MHD_StringNullable password;
+};
+
/* ********************** (f) Introspection ********************** */
@@ -8027,7 +8581,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_LibInfoFixed
/**
* Get whether HTTP Basic authorization is supported. If supported
* then functions #MHD_action_basic_auth_required_response ()
- * and #MHD_REQUEST_INFO_DYNAMIC_BAUTH_REQ_INFO can be used.
+ * and #MHD_REQUEST_INFO_DYNAMIC_AUTH_BASIC_CREDS can be used.
* The result is placed in @a v_bool member.
*/
MHD_LIB_INFO_FIXED_HAS_BASIC_AUTH = 20
@@ -9214,7 +9768,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_RequestInfoFixedType
* @sa #MHD_REQUEST_INFO_DYNAMIC_HTTP_METHOD_STR
* @ingroup request
*/
- MHD_REQUEST_INFO_FIXED_HTTP_METHOD = 4
+ MHD_REQUEST_INFO_FIXED_HTTP_METHOD = 5
,
/* * Sentinel * */
@@ -9275,10 +9829,11 @@ union MHD_RequestInfoFixedData
* @ingroup specialized
*/
MHD_EXTERN_ enum MHD_StatusCode
-MHD_request_get_info_fixed_sz (struct MHD_Request *request,
- enum MHD_RequestInfoFixedType info_type,
- union MHD_RequestInfoFixedData *return_value,
- size_t return_value_size)
+MHD_request_get_info_fixed_sz (
+ struct MHD_Request *MHD_RESTRICT request,
+ enum MHD_RequestInfoFixedType info_type,
+ union MHD_RequestInfoFixedData *MHD_RESTRICT return_value,
+ size_t return_value_size)
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_ (3)
MHD_FN_PURE_;
@@ -9433,9 +9988,9 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_RequestInfoDynamicType
* username is provided or the format of the username parameter is broken.
* Pointers in the returned structure (if any) are valid until response
* is provided for the request.
- * The result is placed in @a v_dauth_username member.
+ * The result is placed in @a v_auth_digest_uname member.
*/
- MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO = 41
+ MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME = 41
,
/**
* Returns pointer to information about digest auth in client request.
@@ -9443,19 +9998,21 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_RequestInfoDynamicType
* the client or the format of the digest auth header is broken.
* Pointers in the returned structure (if any) are valid until response
* is provided for the request.
- * The result is placed in @a v_dauth_info member.
+ * The result is placed in @a v_auth_digest_info member.
*/
- MHD_REQUEST_INFO_DYNAMIC_DAUTH_REQ_INFO = 42
+ MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO = 42
,
/**
- * Returns pointer to information about basic auth in client request.
- * The resulting pointer is NULL if no basic auth header is set by
- * the client or the format of the basic auth header is broken.
- * Pointers in the returned structure (if any) are valid until response
- * is provided for the request.
- * The result is placed in @a v_bauth_info member.
+ * Returns information about Basic Authentication credentials in the request.
+ * Pointers in the returned structure (if any) are valid until any MHD_Action
+ * or MHD_UploadAction is provided. If the data is needed beyond this point,
+ * it should be copied.
+ * If #MHD_request_get_info_dynamic_sz() returns #MHD_SC_OK then
+ * @a v_auth_basic_creds is NOT NULL and at least the username data
+ * is provided.
+ * The result is placed in @a v_auth_basic_creds member.
*/
- MHD_REQUEST_INFO_DYNAMIC_BAUTH_REQ_INFO = 51
+ MHD_REQUEST_INFO_DYNAMIC_AUTH_BASIC_CREDS = 51
,
/* * Sentinel * */
/**
@@ -9497,17 +10054,19 @@ union MHD_RequestInfoDynamicData
/**
* The information about client provided username for digest auth
*/
- struct MHD_DigestAuthUsernameInfo *v_dauth_username;
+ const struct MHD_AuthDigestUsernameInfo *v_auth_digest_uname;
/**
* The information about client's digest auth
*/
- struct MHD_DigestAuthInfo *v_dauth_info;
+ const struct MHD_AuthDigestInfo *v_auth_digest_info;
/**
- * The information about client's basic auth
+ * The username and password provided by the client's basic auth header.
+ * If #MHD_request_get_info_dynamic_sz() returns #MHD_SC_OK then this pointer
+ * is NOT NULL and at least the username data is provided.
*/
- struct MHD_BasicAuthInfo *v_bauth_info;
+ const struct MHD_AuthBasicCreds *v_auth_basic_creds;
};
@@ -9516,6 +10075,10 @@ union MHD_RequestInfoDynamicData
* This information may be changed during the lifetime of the request.
* The wrapper macro #MHD_request_get_info_dynamic() could be more convenient.
*
+ * Any pointers in the returned data are valid until any MHD_Action or
+ * MHD_UploadAction is provided. If the data is needed beyond this point,
+ * it should be copied.
+ *
* @param request the request to get information about
* @param info_type the type of information requested
* @param[out] return_value pointer to union where requested information will
@@ -9523,17 +10086,32 @@ union MHD_RequestInfoDynamicData
* @param return_value_size the size of the memory area pointed
* by @a return_data, in bytes
* @return #MHD_SC_OK if succeed,
- * error code otherwise
+ * #MHD_SC_INFO_GET_BUFF_TOO_SMALL if @a return_value_size is too small,
+ * #MHD_SC_TOO_LATE if request is already being closed or the response
+ * is being sent
+ * #MHD_SC_TOO_EARLY if requested data is not yet ready (for example,
+ * headers are not yet received),
+ * #MHD_SC_INFO_GET_TYPE_UNKNOWN if requested information type is
+ * not recognized by MHD,
+ * #MHD_SC_FEATURE_DISABLED if requested functionality is not supported
+ * by this MHD build,
+ * #MHD_SC_AUTH_ABSENT if request does not have particular Auth data,
+ * #MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA if connection memory pool
+ * has no space to put decoded
+ * authentication data,
+ * #MHD_SC_REQ_AUTH_DATA_BROKEN if the format of authentication data is
+ * incorrect or broken,
+ * other error code otherwise
* @ingroup specialized
*/
MHD_EXTERN_ enum MHD_StatusCode
-MHD_request_get_info_dynamic_sz (struct MHD_Request *request,
- enum MHD_RequestInfoDynamicType info_type,
- union MHD_RequestInfoDynamicData *return_value,
- size_t return_value_size)
+MHD_request_get_info_dynamic_sz (
+ struct MHD_Request *MHD_RESTRICT request,
+ enum MHD_RequestInfoDynamicType info_type,
+ union MHD_RequestInfoDynamicData *MHD_RESTRICT return_value,
+ size_t return_value_size)
MHD_FN_PAR_NONNULL_ (1)
-MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_ (3)
-MHD_FN_PURE_;
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_ (3);
/**
@@ -9549,7 +10127,8 @@ MHD_FN_PURE_;
* @ingroup specialized
*/
#define MHD_request_get_info_dynamic(request,info_type,return_value) \
- MHD_request_get_info_dynamic_sz ((request), (info_type), (return_value), \
+ MHD_request_get_info_dynamic_sz ((request), (info_type), \
+ (return_value), \
sizeof(*(return_value)))
/**
diff --git a/src/include/microhttpd2_generated_daemon_options.h b/src/include/microhttpd2_generated_daemon_options.h
@@ -298,6 +298,7 @@ The specified callback will be called one time, after network initialisation, TL
/**
* Set strong random data to be used by MHD.
* Currently the data is only needed for Digest Auth module.
+ * Daemon support for Digest Auth is enabled automatically if this option is used.
* The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
* Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
*/
@@ -307,34 +308,25 @@ The specified callback will be called one time, after network initialisation, TL
/**
* Specify the size of the internal hash map array that tracks generated digest nonces usage.
* When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
- * By default the size is 8 bytes (very small).
+ * By default the size is 1000 entries.
*/
- MHD_D_O_DAUTH_MAP_SIZE = 401
+ MHD_D_O_AUTH_DIGEST_MAP_SIZE = 401
,
/**
- * Control the scope of validity of MHD-generated nonces.
- * This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
- * This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
- * When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
- */
- MHD_D_O_DAUTH_NONCE_BIND_TYPE = 402
- ,
-
- /**
- * Default nonce timeout value (in seconds) used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * Nonce validity time (in seconds) used for Digest Auth.
+ * If followed by zero value the value is silently ignored.
* @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
*/
- MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT = 403
+ MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT = 403
,
/**
* Default maximum nc (nonce count) value used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * If followed by zero value the value is silently ignored.
* @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
*/
- MHD_D_O_DAUTH_DEF_MAX_NC = 404
+ MHD_D_O_AUTH_DIGEST_DEF_MAX_NC = 404
,
/**
@@ -799,28 +791,22 @@ union MHD_DaemonOptionValue
struct MHD_DaemonOptionEntropySeed random_entropy;
/**
- * Value for #MHD_D_O_DAUTH_MAP_SIZE.
+ * Value for #MHD_D_O_AUTH_DIGEST_MAP_SIZE.
* the size of the map array
*/
- size_t dauth_map_size;
-
- /**
- * Value for #MHD_D_O_DAUTH_NONCE_BIND_TYPE.
- * FIXME
- */
- enum MHD_DaemonOptionValueDAuthBindNonce dauth_nonce_bind_type;
+ size_t auth_digest_map_size;
/**
- * Value for #MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT.
+ * Value for #MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT.
* FIXME
*/
- unsigned int dauth_def_nonce_timeout;
+ unsigned int auth_digest_nonce_timeout;
/**
- * Value for #MHD_D_O_DAUTH_DEF_MAX_NC.
+ * Value for #MHD_D_O_AUTH_DIGEST_DEF_MAX_NC.
* FIXME
*/
- uint_fast32_t dauth_def_max_nc;
+ uint_fast32_t auth_digest_def_max_nc;
};
@@ -1396,6 +1382,7 @@ The specified callback will be called one time, after network initialisation, TL
/**
* Set strong random data to be used by MHD.
* Currently the data is only needed for Digest Auth module.
+ * Daemon support for Digest Auth is enabled automatically if this option is used.
* The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
* Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
* @param buf_size the size of the buffer
@@ -1414,62 +1401,46 @@ The specified callback will be called one time, after network initialisation, TL
/**
* Specify the size of the internal hash map array that tracks generated digest nonces usage.
* When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
- * By default the size is 8 bytes (very small).
+ * By default the size is 1000 entries.
* @param size the size of the map array
* @return structure with the requested setting
*/
-# define MHD_D_OPTION_DAUTH_MAP_SIZE(size) \
+# define MHD_D_OPTION_AUTH_DIGEST_MAP_SIZE(size) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
- .opt = MHD_D_O_DAUTH_MAP_SIZE, \
- .val.dauth_map_size = (size) \
+ .opt = MHD_D_O_AUTH_DIGEST_MAP_SIZE, \
+ .val.auth_digest_map_size = (size) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
- * Control the scope of validity of MHD-generated nonces.
- * This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
- * This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
- * When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
- * @param bind_type FIXME
- * @return structure with the requested setting
- */
-# define MHD_D_OPTION_DAUTH_NONCE_BIND_TYPE(bind_type) \
- MHD_NOWARN_COMPOUND_LITERALS_ \
- (const struct MHD_DaemonOptionAndValue) \
- { \
- .opt = MHD_D_O_DAUTH_NONCE_BIND_TYPE, \
- .val.dauth_nonce_bind_type = (bind_type) \
- } \
- MHD_RESTORE_WARN_COMPOUND_LITERALS_
-/**
- * Default nonce timeout value (in seconds) used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * Nonce validity time (in seconds) used for Digest Auth.
+ * If followed by zero value the value is silently ignored.
* @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
* @param timeout FIXME
* @return structure with the requested setting
*/
-# define MHD_D_OPTION_DAUTH_DEF_NONCE_TIMEOUT(timeout) \
+# define MHD_D_OPTION_AUTH_DIGEST_NONCE_TIMEOUT(timeout) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
- .opt = MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT, \
- .val.dauth_def_nonce_timeout = (timeout) \
+ .opt = MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT, \
+ .val.auth_digest_nonce_timeout = (timeout) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
* Default maximum nc (nonce count) value used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * If followed by zero value the value is silently ignored.
* @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
* @param max_nc FIXME
* @return structure with the requested setting
*/
-# define MHD_D_OPTION_DAUTH_DEF_MAX_NC(max_nc) \
+# define MHD_D_OPTION_AUTH_DIGEST_DEF_MAX_NC(max_nc) \
MHD_NOWARN_COMPOUND_LITERALS_ \
(const struct MHD_DaemonOptionAndValue) \
{ \
- .opt = MHD_D_O_DAUTH_DEF_MAX_NC, \
- .val.dauth_def_max_nc = (max_nc) \
+ .opt = MHD_D_O_AUTH_DIGEST_DEF_MAX_NC, \
+ .val.auth_digest_def_max_nc = (max_nc) \
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
@@ -2268,6 +2239,7 @@ MHD_D_OPTION_NOTIFY_STREAM (
/**
* Set strong random data to be used by MHD.
* Currently the data is only needed for Digest Auth module.
+ * Daemon support for Digest Auth is enabled automatically if this option is used.
* The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
* Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
* @param buf_size the size of the buffer
@@ -2293,62 +2265,40 @@ MHD_D_OPTION_RANDOM_ENTROPY (
/**
* Specify the size of the internal hash map array that tracks generated digest nonces usage.
* When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
- * By default the size is 8 bytes (very small).
+ * By default the size is 1000 entries.
* @param size the size of the map array
* @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_MAP_SIZE (
+MHD_D_OPTION_AUTH_DIGEST_MAP_SIZE (
size_t size
)
{
struct MHD_DaemonOptionAndValue opt_val;
- opt_val.opt = MHD_D_O_DAUTH_MAP_SIZE;
- opt_val.val.dauth_map_size = size;
-
- return opt_val;
-}
-
-
-/**
- * Control the scope of validity of MHD-generated nonces.
- * This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
- * This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
- * When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
- * @param bind_type FIXME
- * @return structure with the requested setting
- */
-static MHD_INLINE struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_NONCE_BIND_TYPE (
- enum MHD_DaemonOptionValueDAuthBindNonce bind_type
- )
-{
- struct MHD_DaemonOptionAndValue opt_val;
-
- opt_val.opt = MHD_D_O_DAUTH_NONCE_BIND_TYPE;
- opt_val.val.dauth_nonce_bind_type = bind_type;
+ opt_val.opt = MHD_D_O_AUTH_DIGEST_MAP_SIZE;
+ opt_val.val.auth_digest_map_size = size;
return opt_val;
}
/**
- * Default nonce timeout value (in seconds) used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * Nonce validity time (in seconds) used for Digest Auth.
+ * If followed by zero value the value is silently ignored.
* @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
* @param timeout FIXME
* @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_DEF_NONCE_TIMEOUT (
+MHD_D_OPTION_AUTH_DIGEST_NONCE_TIMEOUT (
unsigned int timeout
)
{
struct MHD_DaemonOptionAndValue opt_val;
- opt_val.opt = MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT;
- opt_val.val.dauth_def_nonce_timeout = timeout;
+ opt_val.opt = MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT;
+ opt_val.val.auth_digest_nonce_timeout = timeout;
return opt_val;
}
@@ -2356,20 +2306,20 @@ MHD_D_OPTION_DAUTH_DEF_NONCE_TIMEOUT (
/**
* Default maximum nc (nonce count) value used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * If followed by zero value the value is silently ignored.
* @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
* @param max_nc FIXME
* @return structure with the requested setting
*/
static MHD_INLINE struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_DEF_MAX_NC (
+MHD_D_OPTION_AUTH_DIGEST_DEF_MAX_NC (
uint_fast32_t max_nc
)
{
struct MHD_DaemonOptionAndValue opt_val;
- opt_val.opt = MHD_D_O_DAUTH_DEF_MAX_NC;
- opt_val.val.dauth_def_max_nc = max_nc;
+ opt_val.opt = MHD_D_O_AUTH_DIGEST_DEF_MAX_NC;
+ opt_val.val.auth_digest_def_max_nc = max_nc;
return opt_val;
}
diff --git a/src/include/microhttpd2_main.h.in b/src/include/microhttpd2_main.h.in
@@ -524,18 +524,18 @@ enum MHD_FLAGS_ENUM_ MHD_ValueKind
/**
* HTTP header.
*/
- MHD_VK_HEADER = (1 << 0)
+ MHD_VK_HEADER = (1u << 0)
,
/**
* Cookies. Note that the original HTTP header containing
* the cookie(s) will still be available and intact.
*/
- MHD_VK_COOKIE = (1 << 1)
+ MHD_VK_COOKIE = (1u << 1)
,
/**
* GET (URI) arguments.
*/
- MHD_VK_GET_ARGUMENT = (1 << 2)
+ MHD_VK_GET_ARGUMENT = (1u << 2)
,
/**
* POST data.
@@ -549,12 +549,12 @@ enum MHD_FLAGS_ENUM_ MHD_ValueKind
* to check used "Transfer-Encoding". While it is deprecated and not used
* by modern clients, formally it can be used.
*/
- MHD_VK_POSTDATA = (1 << 3)
+ MHD_VK_POSTDATA = (1u << 3)
,
/**
* HTTP footer (only for HTTP 1.1 chunked encodings).
*/
- MHD_VK_FOOTER = (1 << 4)
+ MHD_VK_FOOTER = (1u << 4)
,
/**
* Header and footer values
@@ -685,8 +685,8 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_OUT_SIZE_ (4,3);
*
* @param request request to get values from
* @param kind what kind of value are we looking for
- * @param key the header to look for, empty to lookup 'trailing' value
- * without a key
+ * @param key the name of the value looking for (used for case-insensetive
+ match), empty to lookup 'trailing' value without a key
* @return NULL if no such item was found
* @ingroup request
*/
@@ -1658,7 +1658,7 @@ MHD_FN_PAR_NONNULL_ (1);
/**
* Add a header line to the response.
*
- * @param response response to add a header to
+ * @param response response to add a header to, NULL is tolerated
* @param name the name of the header to add,
* an internal copy of the string will be made
* @param value the value of the header to add,
@@ -1671,7 +1671,6 @@ MHD_EXTERN_ enum MHD_StatusCode
MHD_response_add_header (struct MHD_Response *MHD_RESTRICT response,
const char *name,
const char *value)
-MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3);
@@ -2473,32 +2472,32 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestBaseAlgo
* MD5 hash algorithm.
* As specified by RFC1321
*/
- MHD_DIGEST_BASE_ALGO_MD5 = (1 << 0)
+ MHD_DIGEST_BASE_ALGO_MD5 = (1u << 0)
,
/**
* SHA-256 hash algorithm.
* As specified by FIPS PUB 180-4
*/
- MHD_DIGEST_BASE_ALGO_SHA256 = (1 << 1)
+ MHD_DIGEST_BASE_ALGO_SHA256 = (1u << 1)
,
/**
* SHA-512/256 hash algorithm.
* As specified by FIPS PUB 180-4
*/
- MHD_DIGEST_BASE_ALGO_SHA512_256 = (1 << 2)
+ MHD_DIGEST_BASE_ALGO_SHA512_256 = (1u << 2)
};
/**
* The flag indicating non-session algorithm types,
* like 'MD5', 'SHA-256' or 'SHA-512-256'.
*/
-#define MHD_DIGEST_AUTH_ALGO_NON_SESSION (1 << 6)
+#define MHD_DIGEST_AUTH_ALGO_NON_SESSION (1u << 6)
/**
* The flag indicating session algorithm types,
* like 'MD5-sess', 'SHA-256-sess' or 'SHA-512-256-sess'.
*/
-#define MHD_DIGEST_AUTH_ALGO_SESSION (1 << 7)
+#define MHD_DIGEST_AUTH_ALGO_SESSION (1u << 7)
/**
* Digest algorithm identification
@@ -2507,7 +2506,7 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthAlgo
{
/**
* Unknown or wrong algorithm type.
- * Used in struct MHD_DigestAuthInfo to indicate client value that
+ * Used in struct MHD_AuthDigestInfo to indicate client value that
* cannot by identified.
*/
MHD_DIGEST_AUTH_ALGO_INVALID = 0
@@ -2554,12 +2553,12 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthAlgo
/**
- * Get digest size for specified algorithm.
+ * Get digest size in bytes for specified algorithm.
*
* The size of the digest specifies the size of the userhash, userdigest
* and other parameters which size depends on used hash algorithm.
* @param algo the algorithm to check
- * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
+ * @return the size (in bytes) of the digest (either #MHD_MD5_DIGEST_SIZE or
* #MHD_SHA256_DIGEST_SIZE/MHD_SHA512_256_DIGEST_SIZE)
* or zero if the input value is not supported or not valid
* @sa #MHD_digest_auth_calc_userdigest()
@@ -2723,15 +2722,18 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthMultiAlgo
* upon return
* @param bin_buf_size the size of the @a userhash_bin buffer, must be
* at least #MHD_digest_get_hash_size() bytes long
- * @return MHD_SC_OK on success,
- * error code otherwise
+ * @return #MHD_SC_OK on success,
+ * #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ * #MHD_SC_HASH_FAILED if hashing failed,
+ * #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ * unknown or unsupported.
* @sa #MHD_digest_auth_calc_userhash_hex()
* @ingroup authentication
*/
MHD_EXTERN_ enum MHD_StatusCode
MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo algo,
- const char *username,
- const char *realm,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT realm,
size_t bin_buf_size,
void *MHD_RESTRICT userhash_bin)
MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_ (2)
@@ -2769,17 +2771,20 @@ MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4);
* @param[out] userhash_hex the output buffer for userhash as hex string;
* if this function succeeds, then this buffer has
* #MHD_digest_get_hash_size()*2 chars long
- * userhash zero-terminated string
- * @return MHD_SC_OK on success,
- * error code otherwise
+ * userhash string plus one zero-termination char
+ * @return #MHD_SC_OK on success,
+ * #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ * #MHD_SC_HASH_FAILED if hashing failed,
+ * #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ * unknown or unsupported.
* @sa #MHD_digest_auth_calc_userhash()
* @ingroup authentication
*/
MHD_EXTERN_ enum MHD_StatusCode
MHD_digest_auth_calc_userhash_hex (
enum MHD_DigestAuthAlgo algo,
- const char *username,
- const char *realm,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT realm,
size_t hex_buf_size,
char userhash_hex[MHD_FN_PAR_DYN_ARR_SIZE_ (hex_buf_size)])
MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_ (2)
@@ -2792,7 +2797,7 @@ MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4);
* Values are sorted so simplified checks could be used.
* For example:
* * (value <= MHD_DIGEST_AUTH_UNAME_TYPE_INVALID) is true if no valid username
- * is provided by the client
+ * is provided by the client (not used currently)
* * (value >= MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH) is true if username is
* provided in any form
* * (value >= MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD) is true if username is
@@ -2802,14 +2807,15 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthUsernameType
{
/**
* No username parameter in in Digest Authorization header.
- * This should be treated as an error.
+ * Not used currently. Value #MHD_SC_REQ_AUTH_DATA_BROKEN is returned
+ * by #MHD_request_get_info_dynamic_sz() if the request has no username.
*/
MHD_DIGEST_AUTH_UNAME_TYPE_MISSING = 0
,
/**
* The 'username' parameter is used to specify the username.
*/
- MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD = (1 << 2)
+ MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD = (1u << 2)
,
/**
* The username is specified by 'username*' parameter with
@@ -2817,24 +2823,26 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthUsernameType
* The only difference between standard and extended types is
* the way how username value is encoded in the header.
*/
- MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED = (1 << 3)
+ MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED = (1u << 3)
,
/**
* The username provided in form of 'userhash' as
* specified by RFC 7616, section-3.4.4.
* @sa #MHD_digest_auth_calc_userhash_hex(), #MHD_digest_auth_calc_userhash()
*/
- MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH = (1 << 1)
+ MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH = (1u << 1)
,
/**
* The invalid combination of username parameters are used by client.
* Either:
- * * both 'username' and 'username*' are used
- * * 'username*' is used with 'userhash=true'
- * * 'username*' used with invalid extended notation
- * * 'username' is not hexadecimal string, while 'userhash' set to 'true'
- */
- MHD_DIGEST_AUTH_UNAME_TYPE_INVALID = (1 << 0)
+ * + both 'username' and 'username*' are used
+ * + 'username*' is used with 'userhash=true'
+ * + 'username*' used with invalid extended notation
+ * + 'username' is not hexadecimal string, while 'userhash' set to 'true'
+ * Not used currently. Value #MHD_SC_REQ_AUTH_DATA_BROKEN is returned
+ * by #MHD_request_get_info_dynamic_sz() if the request has broken username.
+ */
+ MHD_DIGEST_AUTH_UNAME_TYPE_INVALID = (1u << 0)
};
/**
@@ -2844,7 +2852,7 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthQOP
{
/**
* Invalid/unknown QOP.
- * Used in struct MHD_DigestAuthInfo to indicate client value that
+ * Used in struct MHD_AuthDigestInfo to indicate client value that
* cannot by identified.
*/
MHD_DIGEST_AUTH_QOP_INVALID = 0
@@ -2857,18 +2865,18 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthQOP
* parameter).
* This mode is less secure than other modes and inefficient.
*/
- MHD_DIGEST_AUTH_QOP_NONE = 1 << 0
+ MHD_DIGEST_AUTH_QOP_NONE = (1u << 0)
,
/**
* The 'auth' QOP type.
*/
- MHD_DIGEST_AUTH_QOP_AUTH = 1 << 1
+ MHD_DIGEST_AUTH_QOP_AUTH = (1u << 1)
,
/**
* The 'auth-int' QOP type.
* Not supported by MHD for authentication.
*/
- MHD_DIGEST_AUTH_QOP_AUTH_INT = 1 << 2
+ MHD_DIGEST_AUTH_QOP_AUTH_INT = (1u << 2)
};
/**
@@ -2924,16 +2932,52 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthMultiQOP
};
/**
- * The invalid value of 'nc' parameter in client Digest Authorization header.
+ * The type of 'nc' (nonce count) value provided in the request
*/
-#define MHD_DIGEST_AUTH_INVALID_NC_VALUE (0)
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthNC
+{
+ /**
+ * Readable hexdecimal non-zero number.
+ * The decoded value is placed in @a nc member of struct MHD_AuthDigestInfo
+ */
+ MHD_DIGEST_AUTH_NC_NUMBER = 1
+ ,
+ /**
+ * Readable zero number.
+ * Compliant clients should not use such values.
+ * Can be treated as invalid request.
+ */
+ MHD_DIGEST_AUTH_NC_ZERO = 2
+ ,
+ /**
+ * 'nc' value is not provided by the client.
+ * Unless old RFC 2069 mode is allowed, this should be treated as invalid
+ * request.
+ */
+ MHD_DIGEST_AUTH_NC_NONE = 3
+ ,
+ /**
+ * 'nc' value is too long to be decoded.
+ * Compliant clients should not use such values.
+ * Can be treated as invalid request.
+ */
+ MHD_DIGEST_AUTH_NC_TOO_LONG = 4
+ ,
+ /**
+ * 'nc' value is too large for uint32_t.
+ * Compliant clients should not use such values.
+ * Can be treated as request with a stale nonce or as invalid request.
+ */
+ MHD_DIGEST_AUTH_NC_TOO_LARGE = 5
+};
+
/**
* Information from Digest Authorization client's header.
*
- * @see #MHD_REQUEST_INFO_DYNAMIC_DAUTH_REQ_INFO
+ * @see #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO
*/
-struct MHD_DigestAuthInfo
+struct MHD_AuthDigestInfo
{
/**
* The algorithm as defined by client.
@@ -2978,7 +3022,7 @@ struct MHD_DigestAuthInfo
* data.
* @sa #MHD_digest_auth_calc_userhash()
*/
- uint8_t *userhash_bin;
+ const uint8_t *userhash_bin;
/**
* The size of the data pointed by @a userhash_bin.
@@ -3014,13 +3058,17 @@ struct MHD_DigestAuthInfo
size_t cnonce_len;
/**
- * The nc parameter value.
+ * The type of 'nc' (nonce count) value provided in the request.
+ */
+ enum MHD_DigestAuthNC nc_type;
+
+ /**
+ * The nc (nonce count) parameter value.
* Can be used by application to limit the number of nonce re-uses. If @a nc
* is higher than application wants to allow, then "auth required" response
* with 'stale=true' could be used to force client to retry with the fresh
* 'nonce'.
- * If not specified by client or does not have hexadecimal digits only, the
- * value is #MHD_DIGEST_AUTH_INVALID_NC_VALUE.
+ * Set to zero when @a nc_type is not set to #MHD_DIGEST_AUTH_NC_NUMBER.
*/
uint_fast32_t nc;
};
@@ -3029,9 +3077,9 @@ struct MHD_DigestAuthInfo
/**
* Information from Digest Authorization client's header.
*
- * @see #MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO
+ * @see #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME
*/
-struct MHD_DigestAuthUsernameInfo
+struct MHD_AuthDigestUsernameInfo
{
/**
* The algorithm as defined by client.
@@ -3042,7 +3090,10 @@ struct MHD_DigestAuthUsernameInfo
/**
* The type of username used by client.
* The 'invalid' and 'missing' types are not used in this structure,
- * instead NULL is returned for #MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO.
+ * instead #MHD_SC_REQ_AUTH_DATA_BROKEN is returned when
+ * #MHD_request_get_info_dynamic_sz() called with
+ * #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME and the request has
+ * a broken username data.
*/
enum MHD_DigestAuthUsernameType uname_type;
@@ -3057,7 +3108,7 @@ struct MHD_DigestAuthUsernameInfo
* The buffer pointed by the @a username becomes invalid when a response
* for the requested is provided (or request is aborted).
*/
- struct MHD_String username;
+ struct MHD_StringNullable username;
/**
* The userhash string.
@@ -3068,7 +3119,7 @@ struct MHD_DigestAuthUsernameInfo
* for the requested is provided (or request is aborted).
* @sa #MHD_digest_auth_calc_userhash_hex()
*/
- struct MHD_String userhash_hex;
+ struct MHD_StringNullable userhash_hex;
/**
* The userhash decoded to binary form.
@@ -3084,7 +3135,7 @@ struct MHD_DigestAuthUsernameInfo
* data.
* @sa #MHD_digest_auth_calc_userhash()
*/
- uint8_t *userhash_bin;
+ const uint8_t *userhash_bin;
};
@@ -3102,45 +3153,66 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
,
/**
* General error, like "out of memory".
+ * Authentication may be valid, but cannot be checked.
*/
MHD_DAUTH_ERROR = 0
,
/**
- * No "Authorization" header or wrong format of the header.
- * Also may be returned if required parameters in client Authorisation header
- * are missing or broken (in invalid format).
+ * No "Authorization" header for Digest Authentication.
+ */
+ MHD_DAUTH_HEADER_MISSING = -1
+ ,
+ /**
+ * Wrong format of the header.
+ * Also returned if required parameters in Authorization header are missing
+ * or broken (in invalid format).
+ */
+ MHD_DAUTH_HEADER_BROKEN = -9
+ ,
+ /**
+ * Unsupported algorithm.
+ */
+ MHD_DAUTH_UNSUPPORTED_ALGO = -10
+ ,
+ /**
+ * Unsupported 'qop'.
*/
- MHD_DAUTH_WRONG_HEADER = -1
+ MHD_DAUTH_UNSUPPORTED_QOP = -11
+ ,
+ /**
+ * Incorrect userdigest size.
+ */
+ MHD_DAUTH_INVALID_USERDIGEST_SIZE = -15
,
/**
* Wrong 'username'.
*/
- MHD_DAUTH_WRONG_USERNAME = -2
+ MHD_DAUTH_WRONG_USERNAME = -17
,
/**
* Wrong 'realm'.
*/
- MHD_DAUTH_WRONG_REALM = -3
+ MHD_DAUTH_WRONG_REALM = -18
,
/**
* Wrong 'URI' (or URI parameters).
*/
- MHD_DAUTH_WRONG_URI = -4
+ MHD_DAUTH_WRONG_URI = -19
,
/**
* Wrong 'qop'.
*/
- MHD_DAUTH_WRONG_QOP = -5
+ MHD_DAUTH_WRONG_QOP = -20
,
/**
* Wrong 'algorithm'.
*/
- MHD_DAUTH_WRONG_ALGO = -6
+ MHD_DAUTH_WRONG_ALGO = -21
,
/**
* Too large (>64 KiB) Authorization parameter value.
*/
- MHD_DAUTH_TOO_LARGE = -15
+ MHD_DAUTH_TOO_LARGE = -22
,
/* The different form of naming is intentionally used for the results below,
* as they are more important */
@@ -3150,22 +3222,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
* username and password to get the fresh 'nonce'.
* The validity of the 'nonce' may be not checked.
*/
- MHD_DAUTH_NONCE_STALE = -17
- ,
- /**
- * The 'nonce' was generated by MHD for other conditions.
- * This value is only returned if #MHD_D_O_DAUTH_NONCE_BIND_TYPE is set
- * to anything other than #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
- * The interpretation of this code could be different. For example, if
- * #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_URI is set and client just used
- * the same 'nonce' for another URI, the code could be handled as
- * #MHD_DAUTH_NONCE_STALE as RFCs allow nonces re-using for other URIs
- * in the same "protection space".
- * However, if only #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_CLIENT_IP bit is set
- * and it is know that clients have fixed IP addresses, this return code could
- * be handled like #MHD_DAUTH_NONCE_WRONG.
- */
- MHD_DAUTH_NONCE_OTHER_COND = -18
+ MHD_DAUTH_NONCE_STALE = -25
,
/**
* The 'nonce' is wrong. May indicate an attack attempt.
@@ -3173,7 +3230,8 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
MHD_DAUTH_NONCE_WRONG = -33
,
/**
- * The 'response' is wrong. May indicate an attack attempt.
+ * The 'response' is wrong. May indicate a wrong password used or
+ * an attack attempt.
*/
MHD_DAUTH_RESPONSE_WRONG = -34
};
@@ -3195,9 +3253,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
* @param username the username to be authenticated, must be in clear text
* even if userhash is used by the client
* @param password the password matching the @a username (and the @a realm)
- * @param nonce_timeout the period of seconds since nonce generation, when
- * the nonce is recognised as valid and not stale;
- * if zero is specified then daemon default value is used.
* @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
* exceeds the specified value then MHD_DAUTH_NONCE_STALE is
* returned;
@@ -3210,18 +3265,15 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
* @ingroup authentication
*/
MHD_EXTERN_ enum MHD_DigestAuthResult
-MHD_digest_auth_check (struct MHD_Request *request,
- const char *realm,
- const char *username,
- const char *password,
- unsigned int nonce_timeout,
+MHD_digest_auth_check (struct MHD_Request *MHD_RESTRICT request,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT password,
uint_fast32_t max_nc,
enum MHD_DigestAuthMultiQOP mqop,
enum MHD_DigestAuthMultiAlgo malgo)
-MHD_FN_PAR_NONNULL_ (1)
-MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
-MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3)
-MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
+MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4);
/**
@@ -3250,17 +3302,20 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
* #MHD_digest_get_hash_size() bytes of
* userdigest upon return
* @return #MHD_SC_OK on success,
- * error code otherwise.
+ * #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ * #MHD_SC_HASH_FAILED if hashing failed,
+ * #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ * unknown or unsupported.
* @sa #MHD_digest_auth_check_digest()
* @ingroup authentication
*/
MHD_EXTERN_ enum MHD_StatusCode
MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo algo,
- const char *username,
- const char *realm,
- const char *password,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT password,
size_t bin_buf_size,
- void *userdigest_bin)
+ void *MHD_RESTRICT userdigest_bin)
MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_CSTR_ (3)
@@ -3284,62 +3339,70 @@ MHD_FN_PAR_OUT_SIZE_ (6,5);
* @param realm the realm for authorization of the client
* @param username the username to be authenticated, must be in clear text
* even if userhash is used by the client
- * @param userdigest the precalculated binary hash of the string
- * "username:realm:password",
- * see #MHD_digest_auth_calc_userdigest()
* @param userdigest_size the size of the @a userdigest in bytes, must match the
* hashing algorithm (see #MHD_MD5_DIGEST_SIZE,
* #MHD_SHA256_DIGEST_SIZE, #MHD_SHA512_256_DIGEST_SIZE,
* #MHD_digest_get_hash_size())
- * @param nonce_timeout the period of seconds since nonce generation, when
- * the nonce is recognised as valid and not stale;
- * if zero is specified then daemon default value is used.
+ * @param userdigest the precalculated binary hash of the string
+ * "username:realm:password",
+ * see #MHD_digest_auth_calc_userdigest()
* @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
* exceeds the specified value then MHD_DAUTH_NONCE_STALE is
* returned;
* if zero is specified then daemon default value is used.
* @param mqop the QOP to use
* @param malgo digest algorithms allowed to use, fail if algorithm used
- * by the client is not allowed by this parameter;
- * more than one base algorithms (MD5, SHA-256, SHA-512/256)
- * cannot be used at the same time for this function
- * as @a userdigest must match specified algorithm
+ * by the client is not allowed by this parameter;
+ * more than one base algorithms (MD5, SHA-256, SHA-512/256)
+ * cannot be used at the same time for this function
+ * as @a userdigest must match specified algorithm
* @return #MHD_DAUTH_OK if authenticated,
* the error code otherwise
* @sa #MHD_digest_auth_calc_userdigest()
* @ingroup authentication
*/
MHD_EXTERN_ enum MHD_DigestAuthResult
-MHD_digest_auth_check_digest (struct MHD_Request *request,
- const char *realm,
- const char *username,
- const void *userdigest,
+MHD_digest_auth_check_digest (struct MHD_Request *MHD_RESTRICT request,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT username,
size_t userdigest_size,
- unsigned int nonce_timeout,
+ const void *MHD_RESTRICT userdigest,
uint_fast32_t max_nc,
enum MHD_DigestAuthMultiQOP mqop,
enum MHD_DigestAuthMultiAlgo malgo)
MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_CSTR_ (3)
-MHD_FN_PAR_CSTR_ (4);
+MHD_FN_PAR_IN_SIZE_ (5, 4);
/**
- * Create an action to request authentication from the client
+ * Add Digest Authentication "challenge" to the response.
+ *
+ * The response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
- * 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 both RFC 2069 (#MHD_DIGEST_AUTH_QOP_NONE) and other QOP
+ * values, then the "challenge" is formed like if MHD_DIGEST_AUTH_QOP_NONE bit
+ * was not set, because such "challenge" 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.
*
- * At most one action can be created for any request.
+ * New nonces are generated each time when the resulting response is used.
*
- * @param request the request
+ * See RFC 7616, section 3.3 for details.
+ *
+ * @param response the response to update; should contain the "access denied"
+ * body;
+ * note: this function sets the "WWW Authenticate" header and
+ * the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_SC_RESP_POINTER_NULL)
* @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;
@@ -3354,24 +3417,17 @@ MHD_FN_PAR_CSTR_ (4);
* credentials could be used for any URI on the same host);
* this list provides information for the client only and does
* not actually restrict anything on the server side
- * @param response the reply to send; should contain the "access denied"
- * body;
- * note: this function sets the "WWW Authenticate" header and
- * the caller should not set this header;
- * the response must have #MHD_HTTP_STATUS_FORBIDDEN status
- * code, must not have #MHD_R_O_REUSABLE enabled;
- * the NULL is tolerated (the result is NULL)
- * @param signal_stale if set to #MHD_YES then indication of stale nonce used in
- * the client's request is signalled by adding '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 indicate_stale if set to #MHD_YES then indication of stale nonce used
+ * in the client's request is indicated by adding
+ * '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 algo digest algorithm to use; 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, allowing client to provide
+ * then one challenge for each allowed algorithm is added
+ * @param userhash_support if set to #MHD_YES then support of userhash is
+ * indicated, allowing client to provide
* hash("username:realm") instead of the username in
* clear text;
* note that clients are allowed to provide the username
@@ -3383,85 +3439,463 @@ MHD_FN_PAR_CSTR_ (4);
* @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
* added, indicating for the client that UTF-8 encoding for
* the username is preferred
- * @return pointer to the action on success,
- * NULL on failure
+ * @return #MHD_SC_OK if succeed,
+ * #MHD_SC_TOO_LATE if the response has been already "frozen" (used to
+ * create an action),
+ * #MHD_SC_RESP_HEADERS_CONFLICT if Digest Authentication "challenge"
+ * has been added already,
+ * #MHD_SC_RESP_POINTER_NULL if @a response is NULL,
+ * #MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE is response status code is wrong,
+ * #MHD_SC_RESP_HEADER_VALUE_INVALID if @a realm, @a opaque or @a domain
+ * have wrong characters or zero length (for @a realm),
+ * #MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED if memory allocation failed,
+ * or other error code if failed
* @ingroup authentication
*/
-MHD_EXTERN_ const struct MHD_Action *
-MHD_action_digest_auth_required_response (
- struct MHD_Request *request,
- const char *realm,
- const char *opaque,
- const char *domain,
- struct MHD_Response *response,
- enum MHD_Bool signal_stale,
+MHD_EXTERN_ enum MHD_StatusCode
+MHD_response_add_auth_digest_challenge (
+ struct MHD_Response *MHD_RESTRICT response,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT opaque,
+ const char *MHD_RESTRICT domain,
+ enum MHD_Bool indicate_stale,
enum MHD_DigestAuthMultiQOP mqop,
- enum MHD_DigestAuthMultiAlgo algo,
+ enum MHD_DigestAuthMultiAlgo malgo,
enum MHD_Bool userhash_support,
enum MHD_Bool prefer_utf8)
-MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
-MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_NONNULL_ (5);
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4);
+
+#ifndef MHD_NO_STATIC_INLINE
/**
- * Constant to indicate that the nonce of the provided
- * authentication code was wrong.
- * Used as return code by #MHD_digest_auth_check(),
- * #MHD_digest_auth_check_digest()
+ * Create action to reply with Digest Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * See RFC 7616, section 3.3 for details.
+ *
+ * @param request the request to create the action for
+ * @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);
+ * this list provides information for the client only and does
+ * not actually restrict anything on the server side
+ * @param indicate_stale if set to #MHD_YES then indication of stale nonce used
+ * in the client's request is indicated by adding
+ * '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 algo digest algorithm to use; if several algorithms are allowed
+ * then one challenge for each allowed algorithm is added
+ * @param userhash_support if set to #MHD_YES then support of userhash is
+ * indicated, allowing client to provide
+ * hash("username:realm") instead of the 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 for
+ * the username is preferred
+ * @param response the response to update; should contain the "access denied"
+ * body;
+ * note: this function sets the "WWW Authenticate" header and
+ * the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_SC_RESP_POINTER_NULL)
+ * @param abort_if_failed if set to #MHD_NO the response will be used even if
+ * failed to add Basic Authentication "challenge",
+ * if not set to #MHD_NO the request will be aborted
+ * if the "challenge" could not be added.
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
* @ingroup authentication
*/
-#define MHD_INVALID_NONCE -1
+MHD_STATIC_INLINE_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+const struct MHD_Action *
+MHD_action_digest_auth_challenge (struct MHD_Request *MHD_RESTRICT request,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT opaque,
+ const char *MHD_RESTRICT domain,
+ enum MHD_Bool indicate_stale,
+ enum MHD_DigestAuthMultiQOP mqop,
+ enum MHD_DigestAuthMultiAlgo malgo,
+ enum MHD_Bool userhash_support,
+ enum MHD_Bool prefer_utf8,
+ struct MHD_Response *MHD_RESTRICT response,
+ enum MHD_Bool abort_if_failed)
+{
+ if ((MHD_SC_OK !=
+ MHD_response_add_auth_digest_challenge (response, realm, opaque, domain,
+ indicate_stale, mqop, malgo,
+ userhash_support, prefer_utf8))
+ && (MHD_NO != abort_if_failed))
+ {
+ MHD_response_destroy (response);
+ return MHD_action_abort_request (request);
+ }
+ return MHD_action_from_response (request, response);
+}
+
+MHD_STATIC_INLINE_END_
/**
- * Information decoded from Basic Authentication client's header.
+ * Create action to reply with Digest Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
- * @see #MHD_REQUEST_INFO_DYNAMIC_BAUTH_REQ_INFO
+ * If the @a response object cannot be extended with the "challenge",
+ * the @a response is used to reply without the "challenge".
+ *
+ * @param request the request to create the action for
+ * @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);
+ * this list provides information for the client only and does
+ * not actually restrict anything on the server side
+ * @param indicate_stale if set to #MHD_YES then indication of stale nonce used
+ * in the client's request is indicated by adding
+ * '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 algo digest algorithm to use; if several algorithms are allowed
+ * then one challenge for each allowed algorithm is added
+ * @param userhash_support if set to #MHD_YES then support of userhash is
+ * indicated, allowing client to provide
+ * hash("username:realm") instead of the 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 for
+ * the username is preferred
+ * @param response the response to update; should contain the "access denied"
+ * body;
+ * note: this function sets the "WWW Authenticate" header and
+ * the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_SC_RESP_POINTER_NULL)
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
+ * @ingroup authentication
*/
-struct MHD_BasicAuthInfo
-{
- /**
- * The username
- */
- struct MHD_String username;
+#define MHD_action_digest_auth_challenge_p(rq,rlm,opq,dmn,stl,mqop,malgo, \
+ uh,utf,resp) \
+ MHD_action_digest_auth_challenge ((rq),(rlm),(opq),(dmn),(stl),(mqop), \
+ (malgo),(uh),(utf),(resp),MHD_NO)
- /**
- * The password, string pointer may be NULL if password is not encoded
- * by the client.
- */
- struct MHD_StringNullable password;
-};
/**
- * Send a response to request basic authentication from the client.
+ * Create action to reply with Digest Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * If the @a response object cannot be extended with the "challenge",
+ * the @a response is aborted.
+ *
+ * @param request the request to create the action for
+ * @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);
+ * this list provides information for the client only and does
+ * not actually restrict anything on the server side
+ * @param indicate_stale if set to #MHD_YES then indication of stale nonce used
+ * in the client's request is indicated by adding
+ * '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 algo digest algorithm to use; if several algorithms are allowed
+ * then one challenge for each allowed algorithm is added
+ * @param userhash_support if set to #MHD_YES then support of userhash is
+ * indicated, allowing client to provide
+ * hash("username:realm") instead of the 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 for
+ * the username is preferred
+ * @param response the response to update; should contain the "access denied"
+ * body;
+ * note: this function sets the "WWW Authenticate" header and
+ * the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_SC_RESP_POINTER_NULL)
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
+ * @ingroup authentication
+ */
+#define MHD_action_digest_auth_challenge_a(rq,rlm,opq,dmn,stl,mqop,malgo, \
+ uh,utf,resp) \
+ MHD_action_digest_auth_challenge ((rq),(rlm),(opq),(dmn),(stl),(mqop), \
+ (malgo),(uh),(utf),(resp),MHD_YES)
+
+#endif /* ! MHD_NO_STATIC_INLINE */
+
+
+/**
+ * Add Basic Authentication "challenge" to the response.
+ *
+ * The response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * If access to any resource should be limited to specific users, authenticated
+ * by Basic Authentication mechanism, and the request for this resource does not
+ * have Basic Authentication information (see #MHD_AuthBasicCreds), then response
+ * with Basic Authentication "challenge" should be sent. This works as
+ * an indication that Basic Authentication should be used for the access.
*
* See RFC 7617, section-2 for details.
*
- * At most one action can be created for any request.
+ * @param response the reply to send; should contain the "access denied"
+ * body;
+ * note: this function sets the "WWW Authenticate" header and
+ * the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_SC_RESP_POINTER_NULL)
+ * @param realm the realm presented to the client
+ * @param prefer_utf8 if not set to #MHD_NO, parameter'charset="UTF-8"' will
+ * be added, indicating for client that UTF-8 encoding
+ * is preferred
+ * @return #MHD_SC_OK if succeed,
+ * #MHD_SC_TOO_LATE if the response has been already "frozen" (used to
+ * create an action),
+ * #MHD_SC_RESP_HEADERS_CONFLICT if Basic Authentication "challenge"
+ * has been added already,
+ * #MHD_SC_RESP_POINTER_NULL if @a response is NULL,
+ * #MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE is response status code is wrong,
+ * #MHD_SC_RESP_HEADER_VALUE_INVALID if realm is zero-length or has CR
+ * or LF characters,
+ * #MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED if memory allocation failed,
+ * or other error code if failed
+ * @ingroup authentication
+ */
+MHD_EXTERN_ enum MHD_StatusCode
+MHD_response_add_auth_basic_challenge (
+ struct MHD_Response *MHD_RESTRICT response,
+ const char *MHD_RESTRICT realm,
+ enum MHD_Bool prefer_utf8)
+MHD_FN_PAR_NONNULL_(2) MHD_FN_PAR_CSTR_ (2);
+
+#ifndef MHD_NO_STATIC_INLINE
+
+/**
+ * Create action to reply with Basic Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * If access to any resource should be limited to specific users, authenticated
+ * by Basic Authentication mechanism, and the request for this resource does not
+ * have Basic Authentication information (see #MHD_AuthBasicCreds), then response
+ * with Basic Authentication "challenge" should be sent. This works as
+ * an indication that Basic Authentication should be used for the access.
+ *
+ * See RFC 7617, section-2 for details.
*
- * @param connection the MHD connection structure
+ * @param request the request to create the action for
* @param realm the realm presented to the client
* @param prefer_utf8 if not set to #MHD_NO, parameter'charset="UTF-8"' will
* be added, indicating for client that UTF-8 encoding
* is preferred
* @param response the reply to send; should contain the "access denied"
* body;
- * note: this function sets the "WWW Authenticate" header and
- * the caller should not set this header;
- * the response must have #MHD_HTTP_STATUS_FORBIDDEN status
- * code, must not have #MHD_R_O_REUSABLE enabled;
- * the NULL is tolerated (the result is NULL)
- * @return pointer to the action on success,
- * NULL on failure
+ * note: this function adds the "WWW Authenticate" header in
+ * the response and the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_action_abort_request())
+ * @param abort_if_failed if set to #MHD_NO the response will be used even if
+ * failed to add Basic Authentication "challenge",
+ * if not set to #MHD_NO the request will be aborted
+ * if the "challenge" could not be added.
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
* @ingroup authentication
*/
-MHD_EXTERN_ const struct MHD_Action *
-MHD_action_basic_auth_required_response (
- struct MHD_Connection *connection,
- const char *realm,
- enum MHD_Bool prefer_utf8,
- struct MHD_Response *response);
+MHD_STATIC_INLINE_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+const struct MHD_Action *
+MHD_action_basic_auth_challenge (struct MHD_Request *MHD_RESTRICT request,
+ const char *MHD_RESTRICT realm,
+ enum MHD_Bool prefer_utf8,
+ struct MHD_Response *MHD_RESTRICT response,
+ enum MHD_Bool abort_if_failed)
+{
+ if ((MHD_SC_OK !=
+ MHD_response_add_auth_basic_challenge (response, realm, prefer_utf8))
+ && (MHD_NO != abort_if_failed))
+ {
+ MHD_response_destroy (response);
+ return MHD_action_abort_request (request);
+ }
+ return MHD_action_from_response (request, response);
+}
+
+MHD_STATIC_INLINE_END_
+
+
+/**
+ * Create action to reply with Basic Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * If the @a response object cannot be extended with the "challenge",
+ * the @a response will be used to reply without the "challenge".
+ *
+ * @param request the request to create the action for
+ * @param realm the realm presented to the client
+ * @param prefer_utf8 if not set to #MHD_NO, parameter'charset="UTF-8"' will
+ * be added, indicating for client that UTF-8 encoding
+ * is preferred
+ * @param response the reply to send; should contain the "access denied"
+ * body;
+ * note: this function adds the "WWW Authenticate" header in
+ * the response and the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_action_abort_request())
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
+ * @ingroup authentication
+ */
+#define MHD_action_basic_auth_challenge_p(request,realm,prefer_utf8,response) \
+ MHD_action_basic_auth_challenge ((request), (realm), (prefer_utf8), \
+ (response), MHD_NO)
+
+/**
+ * Create action to reply with Basic Authentication "challenge".
+ *
+ * The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
+ *
+ * If the @a response object cannot be extended with the "challenge",
+ * the request will be aborted.
+ *
+ * @param request the request to create the action for
+ * @param realm the realm presented to the client
+ * @param prefer_utf8 if not set to #MHD_NO, parameter'charset="UTF-8"' will
+ * be added, indicating for client that UTF-8 encoding
+ * is preferred
+ * @param response the reply to send; should contain the "access denied"
+ * body;
+ * note: this function adds the "WWW Authenticate" header in
+ * the response and the caller should not set this header;
+ * the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
+ * code;
+ * the NULL is tolerated (the result is
+ * #MHD_action_abort_request())
+ * @return pointer to the action, the action must be consumed
+ * otherwise response object may leak;
+ * NULL if failed or if any action has been already created for
+ * the @a request;
+ * when failed the response object is consumed and need not
+ * to be "destroyed"
+ * @ingroup authentication
+ */
+#define MHD_action_basic_auth_challenge_a(request,realm,prefer_utf8,response) \
+ MHD_action_basic_auth_challenge ((request), (realm), (prefer_utf8), \
+ (response), MHD_YES)
+
+#endif /* ! MHD_NO_STATIC_INLINE */
+
+
+/**
+ * Information decoded from Basic Authentication client's header.
+ *
+ * @see #MHD_REQUEST_INFO_DYNAMIC_AUTH_BASIC_CREDS
+ */
+struct MHD_AuthBasicCreds
+{
+ /**
+ * The username
+ */
+ struct MHD_String username;
+
+ /**
+ * The password, string pointer may be NULL if password is not encoded
+ * by the client.
+ */
+ struct MHD_StringNullable password;
+};
/* ********************** (f) Introspection ********************** */
@@ -3538,7 +3972,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_LibInfoFixed
/**
* Get whether HTTP Basic authorization is supported. If supported
* then functions #MHD_action_basic_auth_required_response ()
- * and #MHD_REQUEST_INFO_DYNAMIC_BAUTH_REQ_INFO can be used.
+ * and #MHD_REQUEST_INFO_DYNAMIC_AUTH_BASIC_CREDS can be used.
* The result is placed in @a v_bool member.
*/
MHD_LIB_INFO_FIXED_HAS_BASIC_AUTH = 20
@@ -4725,7 +5159,7 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_RequestInfoFixedType
* @sa #MHD_REQUEST_INFO_DYNAMIC_HTTP_METHOD_STR
* @ingroup request
*/
- MHD_REQUEST_INFO_FIXED_HTTP_METHOD = 4
+ MHD_REQUEST_INFO_FIXED_HTTP_METHOD = 5
,
/* * Sentinel * */
@@ -4786,10 +5220,11 @@ union MHD_RequestInfoFixedData
* @ingroup specialized
*/
MHD_EXTERN_ enum MHD_StatusCode
-MHD_request_get_info_fixed_sz (struct MHD_Request *request,
- enum MHD_RequestInfoFixedType info_type,
- union MHD_RequestInfoFixedData *return_value,
- size_t return_value_size)
+MHD_request_get_info_fixed_sz (
+ struct MHD_Request *MHD_RESTRICT request,
+ enum MHD_RequestInfoFixedType info_type,
+ union MHD_RequestInfoFixedData *MHD_RESTRICT return_value,
+ size_t return_value_size)
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_ (3)
MHD_FN_PURE_;
@@ -4944,9 +5379,9 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_RequestInfoDynamicType
* username is provided or the format of the username parameter is broken.
* Pointers in the returned structure (if any) are valid until response
* is provided for the request.
- * The result is placed in @a v_dauth_username member.
+ * The result is placed in @a v_auth_digest_uname member.
*/
- MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO = 41
+ MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME = 41
,
/**
* Returns pointer to information about digest auth in client request.
@@ -4954,19 +5389,21 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_RequestInfoDynamicType
* the client or the format of the digest auth header is broken.
* Pointers in the returned structure (if any) are valid until response
* is provided for the request.
- * The result is placed in @a v_dauth_info member.
+ * The result is placed in @a v_auth_digest_info member.
*/
- MHD_REQUEST_INFO_DYNAMIC_DAUTH_REQ_INFO = 42
+ MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO = 42
,
/**
- * Returns pointer to information about basic auth in client request.
- * The resulting pointer is NULL if no basic auth header is set by
- * the client or the format of the basic auth header is broken.
- * Pointers in the returned structure (if any) are valid until response
- * is provided for the request.
- * The result is placed in @a v_bauth_info member.
+ * Returns information about Basic Authentication credentials in the request.
+ * Pointers in the returned structure (if any) are valid until any MHD_Action
+ * or MHD_UploadAction is provided. If the data is needed beyond this point,
+ * it should be copied.
+ * If #MHD_request_get_info_dynamic_sz() returns #MHD_SC_OK then
+ * @a v_auth_basic_creds is NOT NULL and at least the username data
+ * is provided.
+ * The result is placed in @a v_auth_basic_creds member.
*/
- MHD_REQUEST_INFO_DYNAMIC_BAUTH_REQ_INFO = 51
+ MHD_REQUEST_INFO_DYNAMIC_AUTH_BASIC_CREDS = 51
,
/* * Sentinel * */
/**
@@ -5008,17 +5445,19 @@ union MHD_RequestInfoDynamicData
/**
* The information about client provided username for digest auth
*/
- struct MHD_DigestAuthUsernameInfo *v_dauth_username;
+ const struct MHD_AuthDigestUsernameInfo *v_auth_digest_uname;
/**
* The information about client's digest auth
*/
- struct MHD_DigestAuthInfo *v_dauth_info;
+ const struct MHD_AuthDigestInfo *v_auth_digest_info;
/**
- * The information about client's basic auth
+ * The username and password provided by the client's basic auth header.
+ * If #MHD_request_get_info_dynamic_sz() returns #MHD_SC_OK then this pointer
+ * is NOT NULL and at least the username data is provided.
*/
- struct MHD_BasicAuthInfo *v_bauth_info;
+ const struct MHD_AuthBasicCreds *v_auth_basic_creds;
};
@@ -5027,6 +5466,10 @@ union MHD_RequestInfoDynamicData
* This information may be changed during the lifetime of the request.
* The wrapper macro #MHD_request_get_info_dynamic() could be more convenient.
*
+ * Any pointers in the returned data are valid until any MHD_Action or
+ * MHD_UploadAction is provided. If the data is needed beyond this point,
+ * it should be copied.
+ *
* @param request the request to get information about
* @param info_type the type of information requested
* @param[out] return_value pointer to union where requested information will
@@ -5034,17 +5477,32 @@ union MHD_RequestInfoDynamicData
* @param return_value_size the size of the memory area pointed
* by @a return_data, in bytes
* @return #MHD_SC_OK if succeed,
- * error code otherwise
+ * #MHD_SC_INFO_GET_BUFF_TOO_SMALL if @a return_value_size is too small,
+ * #MHD_SC_TOO_LATE if request is already being closed or the response
+ * is being sent
+ * #MHD_SC_TOO_EARLY if requested data is not yet ready (for example,
+ * headers are not yet received),
+ * #MHD_SC_INFO_GET_TYPE_UNKNOWN if requested information type is
+ * not recognized by MHD,
+ * #MHD_SC_FEATURE_DISABLED if requested functionality is not supported
+ * by this MHD build,
+ * #MHD_SC_AUTH_ABSENT if request does not have particular Auth data,
+ * #MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA if connection memory pool
+ * has no space to put decoded
+ * authentication data,
+ * #MHD_SC_REQ_AUTH_DATA_BROKEN if the format of authentication data is
+ * incorrect or broken,
+ * other error code otherwise
* @ingroup specialized
*/
MHD_EXTERN_ enum MHD_StatusCode
-MHD_request_get_info_dynamic_sz (struct MHD_Request *request,
- enum MHD_RequestInfoDynamicType info_type,
- union MHD_RequestInfoDynamicData *return_value,
- size_t return_value_size)
+MHD_request_get_info_dynamic_sz (
+ struct MHD_Request *MHD_RESTRICT request,
+ enum MHD_RequestInfoDynamicType info_type,
+ union MHD_RequestInfoDynamicData *MHD_RESTRICT return_value,
+ size_t return_value_size)
MHD_FN_PAR_NONNULL_ (1)
-MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_ (3)
-MHD_FN_PURE_;
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_ (3);
/**
@@ -5060,7 +5518,8 @@ MHD_FN_PURE_;
* @ingroup specialized
*/
#define MHD_request_get_info_dynamic(request,info_type,return_value) \
- MHD_request_get_info_dynamic_sz ((request), (info_type), (return_value), \
+ MHD_request_get_info_dynamic_sz ((request), (info_type), \
+ (return_value), \
sizeof(*(return_value)))
/**
diff --git a/src/include/microhttpd2_portability.h b/src/include/microhttpd2_portability.h
@@ -1,41 +1,28 @@
-MHD_C_DECLRATIONS_START_HERE_
-/* *INDENT-OFF* */
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Evgeny Grin (Karlson2k)
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+#ifndef MICROHTTPD2_PORTABILITY_H
+#define MICROHTTPD2_PORTABILITY_H 1
+
+/* *INDENT-OFF* */
-// FIXME: Move this block to the main header for higher visibility?
-
-/* If generic headers don't work on your platform, include headers which define
- 'va_list', 'size_t', 'uint_least16_t', 'uint_fast32_t', 'uint_fast64_t',
- 'struct sockaddr', and then "#define MHD_HAVE_SYS_HEADERS_INCLUDED" before
- including "microhttpd2.h".
- When 'MHD_HAVE_SYS_HEADERS_INCLUDED' is defined the following "standard"
- includes won't be used (which might be a good idea, especially on platforms
- where they do not exist).
- */
-#ifndef MHD_HAVE_SYS_HEADERS_INCLUDED
-# include <stdarg.h>
-# ifndef MHD_SYS_BASE_TYPES_H
- /* Headers for uint_fastXX_t, size_t */
-# include <stdint.h>
-# include <stddef.h>
-# include <sys/types.h> /* This header is actually optional */
-# endif
-# ifndef MHD_SYS_SOCKET_TYPES_H
- /* Headers for 'struct sockaddr' */
-# if !defined(_WIN32) || defined(__CYGWIN__)
-# include <sys/socket.h>
-# else
- /* Prevent conflict of <winsock.h> and <winsock2.h> */
-# if !defined(_WINSOCK2API_) && !defined(_WINSOCKAPI_)
-# ifndef WIN32_LEAN_AND_MEAN
- /* Do not use unneeded parts of W32 headers. */
-# define WIN32_LEAN_AND_MEAN 1
-# endif /* !WIN32_LEAN_AND_MEAN */
-# include <winsock2.h>
-# endif
-# endif
-# endif
-#endif
#ifndef __cplusplus
# define MHD_STATIC_CAST_(type,value) \
@@ -61,23 +48,6 @@ MHD_C_DECLRATIONS_START_HERE_
# endif
#endif
-// FIXME: Move this block to the main header for higher visibility? Users should know what is the 'MHD_Socket'
-
-#ifndef MHD_INVALID_SOCKET
-/**
- * MHD_Socket is type for socket FDs
- */
-# if ! defined(_WIN32) || defined(_SYS_TYPES_FD_SET)
-# define MHD_SOCKETS_KIND_POSIX 1
-typedef int MHD_Socket;
-# define MHD_INVALID_SOCKET (-1)
-# else /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
-# define MHD_SOCKETS_KIND_WINSOCK 1
-typedef SOCKET MHD_Socket;
-# define MHD_INVALID_SOCKET (INVALID_SOCKET)
-# endif /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
-#endif /* MHD_INVALID_SOCKET */
-
/* Compiler macros for internal needs */
@@ -125,6 +95,8 @@ typedef SOCKET MHD_Socket;
#endif
#define MHD_C_MINV_99 MHD_C_MINV (199901)
+#define MHD_C_MINV_11 MHD_C_MINV (201112)
+#define MHD_C_MINV_23 MHD_C_MINV (202311)
#ifndef __cplusplus
@@ -274,23 +246,24 @@ typedef SOCKET MHD_Socket;
/* Use variable-length arrays? */
-#if ! defined(MHD_NO_VLA)
-# if ! defined(MHD_USE_VLA)
-# if MHD_C_MINV_99 && (! defined(__STDC_NO_VLA__))
-# if defined(__GNUC__) || defined(__clang__)
-# define MHD_USE_VLA 1
-# elif defined(_MSC_VER)
-# define MHD_NO_VLA 1
-# else
-/* Assume 'not supported' */
-# define MHD_NO_VLA 1
-# endif
+#if ! defined(MHD_NO_VLA_TYPES)
+# if ! defined(MHD_USE_VLA_TYPES)
+# if MHD_C_MINV_23
+# define MHD_USE_VLA_TYPES 1
+# elif defined(__STDC_NO_VLA__)
+# define MHD_NO_VLA_TYPES 1
+# elif defined(_MSC_VER)
+# define MHD_NO_VLA_TYPES 1
+# elif MHD_C_MINV_99 && \
+ (defined(__GNUC__) || defined(__clang__) || defined(__llvm__))
+# define MHD_USE_VLA_TYPES 1
# else
-# define MHD_NO_VLA 1
+/* Assume 'not supported' */
+# define MHD_NO_VLA_TYPES 1
# endif
# endif
-#elif defined(MHD_USE_VLA)
-#error MHD_USE_VLA and MHD_NO_VLA are both defined
+#elif defined(MHD_USE_VLA_TYPES)
+#error MHD_USE_VLA_TYPES and MHD_NO_VLA_TYPES are both defined
#endif /* MHD_NO_VARARG_MACROS */
#if ! defined(MHD_INLINE)
@@ -389,6 +362,16 @@ typedef SOCKET MHD_Socket;
# endif
#endif /*! MHD_NO__PRAGMA */
+#ifndef MHD_NOWARN_EXPRESSION_
+# if MHD_GNUC_MINV (2,8)
+# define MHD_NOWARN_EXPRESSION_ __extension__
+/* Indicate that MHD_NOWARN_EXPRESSION macro is functional */
+# define MHD_HAS_NOWARN_EXPRESSION_ 1
+# else
+# define MHD_NOWARN_EXPRESSION_ /* empty */
+# endif
+#endif
+
#ifndef MHD_WARN_PUSH_
# define MHD_WARN_PUSH_ /* empty */
#endif
@@ -418,11 +401,25 @@ typedef SOCKET MHD_Socket;
#endif
#ifndef MHD_NOWARN_UNUSED_FUNC_
# define MHD_NOWARN_UNUSED_FUNC_ /* empty */
+#else
+/* Indicate that MHD_NOWARN_UNUSED_FUNC_ macro is functional */
+# define MHD_HAS_NOWARN_UNUSED_FUNC_ 1
#endif
#ifndef MHD_RESTORE_WARN_UNUSED_FUNC_
# define MHD_RESTORE_WARN_UNUSED_FUNC_ /* empty */
#endif
+#ifdef MHD_HAS_NOWARN_UNUSED_FUNC_
+# define MHD_STATIC_INLINE_ \
+ MHD_NOWARN_UNUSED_FUNC_ static MHD_INLINE
+# define MHD_STATIC_INLINE_END_ \
+ MHD_RESTORE_WARN_UNUSED_FUNC_
+#else
+# define MHD_STATIC_INLINE_ \
+ MHD_NOWARN_EXPRESSION_ static MHD_INLINE
+# define MHD_STATIC_INLINE_END_ /* empty */
+#endif
+
/**
* Define MHD_NO_DEPRECATION before including "microhttpd2.h" to disable deprecation messages
*/
@@ -760,15 +757,11 @@ typedef SOCKET MHD_Socket;
/* Override detected value of MHD_FN_PAR_DYN_ARR_SIZE_() by defining it
* before including the header */
#ifndef MHD_FN_PAR_DYN_ARR_SIZE_
-# if MHD_C_MINV_99
-# if MHD_USE_VLA
-# define MHD_FN_PAR_DYN_ARR_SIZE_(size) static size
-# else
-# define MHD_FN_PAR_DYN_ARR_SIZE_(size) 1
-# endif
-# else /* ! MHD_C_MINV_99 */
-# define MHD_FN_PAR_DYN_ARR_SIZE_(size) 1
-# endif /* ! MHD_C_MINV_99 */
+# if MHD_USE_VLA_TYPES
+# define MHD_FN_PAR_DYN_ARR_SIZE_(size) static size
+# else
+# define MHD_FN_PAR_DYN_ARR_SIZE_(size) 1
+# endif
#endif /* MHD_FN_PAR_DYN_ARR_SIZE_ */
/* Override detected value of MHD_FN_PAR_FIX_ARR_SIZE_() by defining it
@@ -826,3 +819,5 @@ typedef SOCKET MHD_Socket;
#ifndef MHD_FN_PAR_CSTR_
# define MHD_FN_PAR_CSTR_(param_num) /* empty */
#endif /* ! MHD_FN_PAR_CSTR_ */
+
+#endif /* ! MICROHTTPD2_PORTABILITY_H */
diff --git a/src/include/microhttpd2_preamble.h.in b/src/include/microhttpd2_preamble.h.in
@@ -139,14 +139,49 @@
/* *INDENT-ON* */
#endif /* __cplusplus */
-#include "microhttpd2_portability.h"
+MHD_C_DECLRATIONS_START_HERE_
/**
- * Current version of the library.
- * Packed BCD: 0x01093001 = 1.9.30-1.
+ * Current version of the library in packed BCD form.
+ * (For example, version 1.9.30-1 would be 0x01093001)
*/
#define MHD_VERSION 0x01990001
+#include "microhttpd2_portability.h"
+
+/* If generic headers don't work on your platform, include headers which define
+ 'va_list', 'size_t', 'uint_least16_t', 'uint_fast32_t', 'uint_fast64_t',
+ 'struct sockaddr', and then "#define MHD_HAVE_SYS_HEADERS_INCLUDED" before
+ including "microhttpd2.h".
+ When 'MHD_HAVE_SYS_HEADERS_INCLUDED' is defined the following "standard"
+ includes won't be used (which might be a good idea, especially on platforms
+ where they do not exist).
+ */
+#ifndef MHD_HAVE_SYS_HEADERS_INCLUDED
+# include <stdarg.h>
+# ifndef MHD_SYS_BASE_TYPES_H
+/* Headers for uint_fastXX_t, size_t */
+# include <stdint.h>
+# include <stddef.h>
+# include <sys/types.h> /* This header is actually optional */
+# endif
+# ifndef MHD_SYS_SOCKET_TYPES_H
+/* Headers for 'struct sockaddr' */
+# if ! defined(_WIN32) || defined(__CYGWIN__)
+# include <sys/socket.h>
+# else
+/* Prevent conflict of <winsock.h> and <winsock2.h> */
+# if ! defined(_WINSOCK2API_) && ! defined(_WINSOCKAPI_)
+# ifndef WIN32_LEAN_AND_MEAN
+/* Do not use unneeded parts of W32 headers. */
+# define WIN32_LEAN_AND_MEAN 1
+# endif /* !WIN32_LEAN_AND_MEAN */
+# include <winsock2.h>
+# endif
+# endif
+# endif
+#endif
+
#ifndef MHD_BOOL_DEFINED
/**
@@ -205,7 +240,7 @@ struct MHD_String
struct MHD_StringNullable
{
/**
- * Number of characters in @e str, not counting 0-termination.
+ * Number of characters in @e cstr, not counting 0-termination.
* If @a cstr is NULL, it must be zero.
*/
size_t len;
@@ -220,6 +255,26 @@ struct MHD_StringNullable
#define MHD_STRINGS_DEFINED 1
#endif /* ! MHD_STRINGS_DEFINED */
+
+#ifndef MHD_INVALID_SOCKET
+# if ! defined(_WIN32) || defined(_SYS_TYPES_FD_SET)
+# define MHD_SOCKETS_KIND_POSIX 1
+/**
+ * MHD_Socket is a type for socket FDs
+ */
+typedef int MHD_Socket;
+# define MHD_INVALID_SOCKET (-1)
+# else /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
+# define MHD_SOCKETS_KIND_WINSOCK 1
+/**
+ * MHD_Socket is a type for socket FDs
+ */
+typedef SOCKET MHD_Socket;
+# define MHD_INVALID_SOCKET (INVALID_SOCKET)
+# endif /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
+#endif /* MHD_INVALID_SOCKET */
+
+
/**
* Constant used to indicate unknown size (use when creating a response).
* Any possible larger sizes are interpreted as the same value.
@@ -407,6 +462,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_APPLICATION_DATA_GENERATION_FINISHED = 20001
,
+ /**
+ * The request does not contain a particular type of Authentication
+ * credentials
+ */
+ MHD_SC_AUTH_ABSENT = 20060
+ ,
/* 30000-level status codes indicate transient failures
that might go away if the client tries again. */
@@ -461,19 +522,19 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
* Failed to allocate memory for the daemon resources.
* TODO: combine similar error codes for daemon
*/
- MHD_SC_DAEMON_MALLOC_FAILURE = 30081
+ MHD_SC_DAEMON_MEM_ALLOC_FAILURE = 30081
,
/**
* We failed to allocate memory for the connection.
* (May be transient.)
*/
- MHD_SC_CONNECTION_MALLOC_FAILURE = 30082
+ MHD_SC_CONNECTION_MEM_ALLOC_FAILURE = 30082
,
/**
* We failed to allocate memory for the connection's memory pool.
* (May be transient.)
*/
- MHD_SC_POOL_MALLOC_FAILURE = 30083
+ MHD_SC_POOL_MEM_ALLOC_FAILURE = 30083
,
/**
* We failed to forward data from a Web socket to the
@@ -487,7 +548,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
* the request. Likely the request fields are too large to leave
* enough room.
*/
- MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ = 30130
+ MHD_SC_CONNECTION_POOL_NO_MEM_REQ = 30130
,
/**
* Failed to allocate memory from our memory pool to store GET parameter.
@@ -501,6 +562,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_CONNECTION_POOL_NO_MEM_COOKIE = 30132
,
/**
+ * Failed to allocate memory from connection memory pool to store
+ * parsed Authentication data.
+ */
+ MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA = 30133
+ ,
+ /**
* Detected jump back of system clock
*/
MHD_SC_SYS_CLOCK_JUMP_BACK_LARGE = 30140
@@ -699,6 +766,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT = 40290
,
/**
+ * The data in Auth request header has invalid format.
+ * For example, for Basic Authentication base64 decoding failed.
+ */
+ MHD_SC_REQ_AUTH_DATA_BROKEN = 40320
+ ,
+ /**
* The request cannot be processed. Sending error reply.
*/
MHD_SC_REQ_PROCCESSING_ERR_REPLY = 41000
@@ -937,7 +1010,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
/**
* Failed to allocate memory for the thread pool.
*/
- MHD_SC_THREAD_POOL_MALLOC_FAILURE = 50090
+ MHD_SC_THREAD_POOL_MEM_ALLOC_FAILURE = 50090
,
/**
* We failed to allocate mutex for thread pool worker.
@@ -1158,6 +1231,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_REPLY_FILE_READ_ERROR = 50232
,
/**
+ * Failed to generate the nonce for the Digest Auth.
+ */
+ MHD_SC_REPLY_NONCE_ERROR = 50233
+ ,
+ /**
* Failed to allocate memory in connection's pool for the reply.
*/
MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE = 50250
@@ -1231,7 +1309,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
/**
* Unable to allocate memory for the response header
*/
- MHD_SC_RESPONSE_HEADER_MALLOC_FAILED = 50540
+ MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED = 50540
,
/**
* Failed to switch TCP_NODELAY option for the socket
@@ -1307,6 +1385,14 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_TLS_CONNECTION_HANDSHAKED_FAILED = 51220
,
/**
+ * Hashing failed.
+ * Internal hashing function can never fail (and this code is never returned
+ * for them). External hashing function (like TLS backend-based) may fail
+ * for various reasons, like failure of hardware acccelerated hashing.
+ */
+ MHD_SC_HASH_FAILED = 51260
+ ,
+ /**
* Something wrong in the internal MHD logic.
* This error should be never returned if MHD works as expected.
* If this code is ever returned, please report to MHD maintainers.
@@ -1318,114 +1404,121 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
logic did something wrong or generated an error. */
/**
+ * The application called function too early.
+ * For example, a header value was requested before the headers
+ * had been received.
+ */
+ MHD_SC_TOO_EARLY = 60000
+ ,
+ /**
+ * The application called this function too late.
+ * For example, MHD has already started sending reply.
+ */
+ MHD_SC_TOO_LATE = 60001
+ ,
+ /**
* MHD does not support the requested combination of
* the sockets polling syscall and the work mode.
*/
- MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID = 60000
+ MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID = 60010
,
/**
* MHD does not support quiescing if ITC was disabled
* and threads are used.
*/
- MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60001
+ MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60011
,
/**
* MHD is closing a connection because the application
* logic to generate the response data failed.
*/
- MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED = 60005
+ MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED = 60015
,
/**
* MHD is closing a connection because the application
* callback told it to do so.
*/
- MHD_SC_APPLICATION_CALLBACK_ABORT_ACTION = 60006
+ MHD_SC_APPLICATION_CALLBACK_ABORT_ACTION = 60016
,
/**
* Application only partially processed upload and did
* not suspend connection. This may result in a hung
* connection.
*/
- MHD_SC_APPLICATION_HUNG_CONNECTION = 60007
+ MHD_SC_APPLICATION_HUNG_CONNECTION = 60017
,
/**
* Application only partially processed upload and did
* not suspend connection and the read buffer was maxxed
* out, so MHD closed the connection.
*/
- MHD_SC_APPLICATION_HUNG_CONNECTION_CLOSED = 60008
- ,
- /**
- * Application called function too late, for example because
- * MHD already changed state.
- */
- MHD_SC_TOO_LATE = 60009
+ MHD_SC_APPLICATION_HUNG_CONNECTION_CLOSED = 60018
,
/**
* Attempted to set an option that conflicts with another option
* already set.
*/
- MHD_SC_OPTIONS_CONFLICT = 60010
+ MHD_SC_OPTIONS_CONFLICT = 60020
,
/**
* Attempted to set an option that not recognised by MHD.
*/
- MHD_SC_OPTION_UNKNOWN = 60011
+ MHD_SC_OPTION_UNKNOWN = 60021
,
/**
* Parameter specified unknown work mode.
*/
- MHD_SC_CONFIGURATION_UNEXPECTED_WM = 60012
+ MHD_SC_CONFIGURATION_UNEXPECTED_WM = 60022
,
/**
* Parameter specified unknown socket poll syscall.
*/
- MHD_SC_CONFIGURATION_UNEXPECTED_SPS = 60013
+ MHD_SC_CONFIGURATION_UNEXPECTED_SPS = 60023
,
/**
* The size of the provided sockaddr does not match address family.
*/
- MHD_SC_CONFIGURATION_WRONG_SA_SIZE = 60014
+ MHD_SC_CONFIGURATION_WRONG_SA_SIZE = 60024
,
/**
* The number set by #MHD_D_O_FD_NUMBER_LIMIT is too strict to run
* the daemon
*/
- MHD_SC_MAX_FD_NUMBER_LIMIT_TOO_STRICT = 60015
+ MHD_SC_MAX_FD_NUMBER_LIMIT_TOO_STRICT = 60025
,
/**
* The number set by #MHD_D_O_GLOBAL_CONNECTION_LIMIT is too for the daemon
* configuration
*/
- MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL = 60016
+ MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL = 60026
,
/**
* The application requested an unsupported TLS backend to be used.
*/
- MHD_SC_TLS_BACKEND_UNSUPPORTED = 60020
+ MHD_SC_TLS_BACKEND_UNSUPPORTED = 60030
,
/**
* The application attempted to setup TLS parameters before
* enabling TLS.
*/
- MHD_SC_TLS_BACKEND_UNINITIALIZED = 60021
+ MHD_SC_TLS_BACKEND_UNINITIALIZED = 60031
,
/**
* The application requested a TLS backend which cannot be used due
* to missing TLS dynamic library or backend initialisation problem.
*/
- MHD_SC_TLS_BACKEND_UNAVAILABLE = 60022
+ MHD_SC_TLS_BACKEND_UNAVAILABLE = 60032
,
/**
* Provided TLS certificate and/or private key are incorrect
*/
- MHD_SC_TLS_CONF_BAD_CERT = 60023
+ MHD_SC_TLS_CONF_BAD_CERT = 60033
,
/**
* The application requested a daemon setting that cannot be used with
* selected TLS backend
*/
- MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS = 60024
+ MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS = 60034
,
/**
* The response header name has forbidden characters or token
@@ -1438,6 +1531,21 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_RESP_HEADER_VALUE_INVALID = 60051
,
/**
+ * An attempt to add header conflicting with other response header
+ */
+ MHD_SC_RESP_HEADERS_CONFLICT = 60052
+ ,
+ /**
+ * The pointer to the response object is NULL
+ */
+ MHD_SC_RESP_POINTER_NULL = 60060
+ ,
+ /**
+ * The response HTTP status code is not suitable
+ */
+ MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE = 60061
+ ,
+ /**
* The provided MHD_Action is invalid
*/
MHD_SC_ACTION_INVALID = 60080
@@ -1498,14 +1606,14 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_UPGRADED_HANDLE_INVALID = 60161
,
/**
- * The requested type of information is not recognised.
+ * The provided output buffer is too small.
*/
- MHD_SC_INFO_TYPE_UNKNOWN = 60200
+ MHD_SC_OUT_BUFF_TOO_SMALL = 60180
,
/**
* The requested type of information is not recognised.
*/
- MHD_SC_INFO_GET_INFO_TYPE_UNKNOWN = 60200
+ MHD_SC_INFO_GET_TYPE_UNKNOWN = 60200
,
/**
* The information of the requested type is too large to fit into
@@ -1531,6 +1639,21 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
* cannot be provided due to some error or other reasons.
*/
MHD_SC_INFO_GET_TYPE_UNAVAILALBE = 60204
+ ,
+ /**
+ * The type of the Digest Auth algorithm is unknown or not supported.
+ */
+ MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED = 60240
+ ,
+ /**
+ * The Digest Auth QOP value is unknown or not supported.
+ */
+ MHD_SC_AUTH_DIGEST_QOP_NOT_SUPPORTED = 60241
+ ,
+ /**
+ * The Digest Auth is not supported due to configuration
+ */
+ MHD_SC_AUTH_DIGEST_UNSUPPORTED = 60242
};
/**
@@ -3231,85 +3354,88 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_SockPollSyscall
/**
- * Protocol strictness enforced by MHD on clients.
- * All levels have different parsing settings for the headers.
+ * Protocol strictness levels enforced by MHD on clients.
+ * Each level applies different parsing settings for HTTP headers and other
+ * protocol elements.
*/
enum MHD_FIXED_ENUM_APP_SET_ MHD_ProtocolStrictLevel
{
/* * Basic levels * */
/**
- * Sane level of protocol enforcement for production use.
- * A balance between extra security and broader compatibility,
- * as allowed by RFCs for HTTP servers.
+ * A sane default level of protocol enforcement for production use.
+ * Provides a balance between enhanced security and broader compatibility,
+ * as permitted by RFCs for HTTP servers.
*/
MHD_PSL_DEFAULT = 0
,
/**
- * Be strict about the protocol (as opposed to as tolerant as
- * possible), within the limits set by RFCs for HTTP servers.
- * This level (and more strict) forbids use of bare LF as
- * CRLF. It also rejects requests with both "Transfer-Encoding:"
- * and "Content-Length:".
- * It is suitable for public servers.
+ * Apply stricter protocol interpretation while remaining within
+ * RFC-defined limits for HTTP servers.
+ *
+ * At this level (and stricter), using a bare LF instead of CRLF is forbidden,
+ * and requests that include both a "Transfer-Encoding:" and
+ * a "Content-Length:" headers are rejected.
+ *
+ * Suitable for public servers.
*/
MHD_PSL_STRICT = 1
,
/**
- * Be particularly permissive about the protocol, within
- * the limits set by RFCs for HTTP servers.
+ * Be more permissive in interpreting the protocol, while still
+ * operating within the RFC-defined limits for HTTP servers.
*/
MHD_PSL_PERMISSIVE = -1
,
/* * Special levels * */
/**
- * Stricter protocol interpretation, even stricter then allowed
- * by RFCs for HTTP servers.
- * However it should be absolutely compatible with clients
- * following at least RFCs' "MUST" type of requirements
- * for HTTP clients.
- * For chunked encoding parsing this level (and more strict)
- * forbids whitespace in chunk extension.
- * For cookies parsing this (and more strict) level rejects
- * cookie in full even if a single value is encoded incorrectly
- * in it.
- * This level is recommended for testing clients against
- * MHD. Also can be used for security-centric application,
- * however it is slight violation of RFCs' requirements.
+ * A stricter protocol interpretation than what is allowed by RFCs for HTTP
+ * servers. However, it should remain fully compatible with clients correctly
+ * following all RFC "MUST" requirements for HTTP clients.
+ *
+ * For chunked encoding, this level (and more restrictive ones) forbids
+ * whitespace in chunk extensions.
+ * For cookie parsing, this level (and more restrictive ones) rejects
+ * the entire cookie if even a single value within it is incorrectly encoded.
+ *
+ * Recommended for testing clients against MHD. Can also be used for
+ * security-centric applications, though doing so slightly violates
+ * relevant RFC requirements for HTTP servers.
*/
MHD_PSL_VERY_STRICT = 2
,
/**
- * The most strict interpretation of the HTTP protocol,
- * much stricter that defined for HTTP servers by RFC.
- * However it should be absolutely compatible with clients
- * following RFCs' "SHOULD" and "MUST" types of requirements
- * for HTTP clients.
+ * The strictest interpretation of the HTTP protocol, even stricter than
+ * allowed by RFCs for HTTP servers.
+ * However, it should remain fully compatible with clients complying with both
+ * RFC "SHOULD" and "MUST" requirements for HTTP clients.
+ *
* This level can be used for testing clients against MHD.
- * It is not recommended for any public services as it may
- * reject legitimate clients (clients not following "SHOULD"
- * type of RFC requirements).
+ * It is not recommended for public services, as it may reject legitimate
+ * clients that do not follow RFC "SHOULD" requirements.
*/
MHD_PSL_EXTRA_STRICT = 3
,
/**
- * More relaxed protocol interpretation, violating RFCs'
- * "SHOULD" type of restrictions for HTTP servers.
- * For cookies parsing this (and more permissive) level
- * allows whitespaces in cookie values.
- * This level can be used in isolated environments.
+ * A more relaxed protocol interpretation that violates some RFC "SHOULD"
+ * restrictions for HTTP servers.
+ * For cookie parsing, this level (and more permissive levels) allows
+ * whitespace in cookie values.
+ *
+ * This level may be used in isolated environments.
*/
MHD_PSL_VERY_PERMISSIVE = -2
,
/**
- * The most flexible protocol interpretation, beyond
- * RFCs' "MUST" type of restrictions for HTTP server.
- * The level allow HTTP/1.1 requests without "Host:" header.
- * For cookies parsing this level adds allowance of
- * whitespaces before and after '=' character.
- * This level is not recommended unless it is absolutely
- * necessary to communicate with some client(s) with
- * badly broken HTTP implementation.
+ * The most flexible protocol interpretation, going beyond RFC "MUST"
+ * requirements for HTTP servers.
+ *
+ * This level allows HTTP/1.1 requests without a "Host:" header.
+ * For cookie parsing, whitespace is allowed before and after
+ * the '=' character.
+ *
+ * Not recommended unless absolutely necessary to communicate with clients
+ * that have severely broken HTTP implementations.
*/
MHD_PSL_EXTRA_PERMISSIVE = -3,
};
@@ -3790,6 +3916,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
MHD_REQUEST_ENDED_FILE_ERROR = 51
,
/**
+ * The request was aborted due to error generating valid nonce for Digest Auth
+ * @ingroup request
+ */
+ MHD_REQUEST_ENDED_NONCE_ERROR = 52
+ ,
+ /**
* Closing the session since MHD is being shut down.
* @ingroup request
*/
diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am
@@ -32,6 +32,7 @@ libmicrohttpd2_la_SOURCES = \
sys_sendfile.h \
compat_calloc.h \
sys_w32_ver.h \
+ mhd_align.h mhd_bithelpers.h mhd_byteorder.h \
mhd_assert.h mhd_unreachable.h \
mhd_cntnr_ptr.h mhd_arr_num_elems.h \
mhd_tristate.h mhd_status_code_int.h \
@@ -82,6 +83,7 @@ libmicrohttpd2_la_SOURCES = \
request_funcs.c request_funcs.h \
request_get_value.c request_get_value.h \
respond_with_error.c respond_with_error.h \
+ request_get_info.c \
response_from.c response_from.h \
response_add_header.c response_add_header.h \
response_destroy.c response_destroy.h \
@@ -97,56 +99,116 @@ libmicrohttpd2_la_SOURCES += \
compat_calloc.c
endif
-post_parser_files = \
+post_parser_OPTSOURCES = \
http_post_enc.h \
mhd_post_parser.h mhd_post_result.h mhd_postfield_int.h \
post_parser_funcs.c post_parser_funcs.h
-upgrade_files = \
+auth_OPTSOURCES = \
+ request_auth_get.c request_auth_get.h
+
+auth_basic_OPTSOURCES = \
+ auth_basic.c auth_basic.h \
+ response_auth_basic.c
+
+if MHD_MD5_EXTR
+md5_OPTSOURCES = \
+ md5_ext.c md5_ext.h \
+ mhd_md5.h
+else
+md5_OPTSOURCES = \
+ md5_int.c md5_int.h \
+ mhd_md5.h
+endif
+
+if MHD_SHA256_EXTR
+sha256_OPTSOURCES = \
+ sha256_ext.c sha256_ext.h \
+ mhd_sha256.h
+else
+sha256_OPTSOURCES = \
+ sha256_int.c sha256_int.h \
+ mhd_sha256.h
+endif
+
+sha512_256_OPTSOURCES = \
+ sha512_256_int.c sha512_256_int.h \
+ mhd_sha512_256.h
+
+auth_digest_OPTSOURCES = \
+ mhd_digest_auth_data.h mhd_auth_digest_hdr.h \
+ response_auth_digest.c response_auth_digest.h \
+ auth_digest.c auth_digest.h
+
+if MHD_SUPPORT_MD5
+auth_digest_OPTSOURCES += $(md5_OPTSOURCES)
+endif
+
+if MHD_SUPPORT_SHA256
+auth_digest_OPTSOURCES += $(sha256_OPTSOURCES)
+endif
+
+if MHD_SUPPORT_SHA512_256
+auth_digest_OPTSOURCES += $(sha512_256_OPTSOURCES)
+endif
+
+upgrade_OPTSOURCES = \
mhd_upgrade.h \
upgrade_prep.c upgrade_prep.h \
upgrade_proc.c upgrade_proc.h \
upgraded_net.c
-tls_common_files = \
+tls_common_OPTSOURCES = \
mhd_tls_choice.h \
mhd_tls_funcs.c mhd_tls_funcs.h mhd_tls_enums.h \
tls_dh_params.h \
conn_tls_check.c conn_tls_check.h
-tls_multi_files = \
+tls_multi_OPTSOURCES = \
tls_multi_tls_lib.h tls_multi_daemon_data.h tls_multi_conn_data.h \
tls_multi_funcs.c tls_multi_funcs.h
-tls_gnu_files = \
+tls_gnu_OPTSOURCES = \
tls_gnu_tls_lib.h tls_gnu_daemon_data.h tls_gnu_conn_data.h \
tls_gnu_funcs.c tls_gnu_funcs.h
-tls_open_files = \
+tls_open_OPTSOURCES = \
tls_open_tls_lib.h tls_open_daemon_data.h tls_open_conn_data.h \
tls_open_funcs.c tls_open_funcs.h
if HAVE_POST_PARSER
- libmicrohttpd2_la_SOURCES += $(post_parser_files)
+ libmicrohttpd2_la_SOURCES += $(post_parser_OPTSOURCES)
+endif
+
+if MHD_SUPPORT_AUTH
+ libmicrohttpd2_la_SOURCES += $(auth_OPTSOURCES)
+endif
+
+if MHD_SUPPORT_AUTH_BASIC
+ libmicrohttpd2_la_SOURCES += $(auth_basic_OPTSOURCES)
+endif
+
+if MHD_SUPPORT_AUTH_DIGEST
+ libmicrohttpd2_la_SOURCES += $(auth_digest_OPTSOURCES)
endif
if MHD_UPGRADE_SUPPORT
- libmicrohttpd2_la_SOURCES += $(upgrade_files)
+ libmicrohttpd2_la_SOURCES += $(upgrade_OPTSOURCES)
endif
if MHD_ENABLE_HTTPS
- libmicrohttpd2_la_SOURCES += $(tls_common_files)
+ libmicrohttpd2_la_SOURCES += $(tls_common_OPTSOURCES)
if MHD_ENABLE_MULTITLS
- libmicrohttpd2_la_SOURCES += $(tls_multi_files)
+ libmicrohttpd2_la_SOURCES += $(tls_multi_OPTSOURCES)
endif
if MHD_USE_GNUTLS
- libmicrohttpd2_la_SOURCES += $(tls_gnu_files)
+ libmicrohttpd2_la_SOURCES += $(tls_gnu_OPTSOURCES)
endif
if MHD_USE_OPENSSL
- libmicrohttpd2_la_SOURCES += $(tls_open_files)
+ libmicrohttpd2_la_SOURCES += $(tls_open_OPTSOURCES)
endif
endif
@@ -239,6 +301,8 @@ check-sources-missing:
echo " done" ; \
test -z "$$res" || echo "The following sources are missing in Makefile.am:$${res}"
-.PHONY: update-po-POTFILES.in check-sources-missing
+check-missing-sources: check-sources-missing
+
+.PHONY: update-po-POTFILES.in check-sources-missing check-missing-sources
.NOTPARALLEL: $(srcdir)/daemon_options.h $(srcdir)/daemon_set_options.c $(srcdir)/response_options.h $(srcdir)/response_set_options.c
diff --git a/src/mhd2/action.c b/src/mhd2/action.c
@@ -26,8 +26,12 @@
#include "mhd_sys_options.h"
+#include "mhd_cntnr_ptr.h"
+#include "mhd_connection.h"
+#include "mhd_daemon.h"
#include "mhd_action.h"
#include "mhd_request.h"
+#include "mhd_response.h"
#include "daemon_logger.h"
@@ -60,14 +64,31 @@ MHD_action_from_response (struct MHD_Request *request,
struct MHD_Response *response)
{
struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
- if (mhd_ACTION_NO_ACTION != head_act->act)
- return (const struct MHD_Action *) NULL;
if (NULL == response)
return (const struct MHD_Action *) NULL;
mhd_response_check_frozen_freeze (response);
mhd_response_inc_use_count (response);
+ if (mhd_ACTION_NO_ACTION != head_act->act)
+ {
+ mhd_response_dec_use_count (response);
+ return (const struct MHD_Action *) NULL;
+ }
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ if (mhd_RESP_HAD_AUTH_DIGEST (response) &&
+ ! mhd_D_HAS_AUTH_DIGEST ( \
+ mhd_CNTNR_CPTR (request, struct MHD_Connection, rq)->daemon))
+ {
+ mhd_response_dec_use_count (response);
+ mhd_LOG_MSG (mhd_CNTNR_PTR (request, struct MHD_Connection, rq)->daemon, \
+ MHD_SC_AUTH_DIGEST_UNSUPPORTED, \
+ "Attempted to use a response with Digest Auth challenge on " \
+ "the daemon without enabled Digest Auth support");
+ return (const struct MHD_Action *) NULL;
+ }
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
head_act->act = mhd_ACTION_RESPONSE;
head_act->data.response = response;
@@ -249,12 +270,29 @@ MHD_upload_action_from_response (struct MHD_Request *request,
&(request->app_act.upl_act);
if (NULL == response)
return (const struct MHD_UploadAction *) NULL;
- if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
- return (const struct MHD_UploadAction *) NULL;
mhd_response_check_frozen_freeze (response);
mhd_response_inc_use_count (response);
+ if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
+ {
+ mhd_response_dec_use_count (response);
+ return (const struct MHD_UploadAction *) NULL;
+ }
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ if (mhd_RESP_HAD_AUTH_DIGEST (response) &&
+ ! mhd_D_HAS_AUTH_DIGEST ( \
+ mhd_CNTNR_CPTR (request, struct MHD_Connection, rq)->daemon))
+ {
+ mhd_response_dec_use_count (response);
+ mhd_LOG_MSG (mhd_CNTNR_PTR (request, struct MHD_Connection, rq)->daemon, \
+ MHD_SC_AUTH_DIGEST_UNSUPPORTED, \
+ "Attempted to use a response with Digest Auth challenge on " \
+ "the daemon without enabled Digest Auth support");
+ return (const struct MHD_UploadAction *) NULL;
+ }
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
upl_act->act = mhd_UPLOAD_ACTION_RESPONSE;
upl_act->data.response = response;
diff --git a/src/mhd2/auth_basic.c b/src/mhd2/auth_basic.c
@@ -0,0 +1,139 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/auth_basic.c
+ * @brief The implementation of the Basic Authorization header parser
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include <string.h>
+
+#include "mhd_cntnr_ptr.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_str_types.h"
+#include "mhd_connection.h"
+
+#include "mhd_str.h"
+#include "stream_funcs.h"
+#include "request_auth_get.h"
+
+#include "auth_basic.h"
+
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+enum MHD_StatusCode
+find_and_parse_auth_basic (struct MHD_Request *restrict req)
+{
+ struct MHD_String token68;
+ size_t alloc_size;
+ size_t dec_size;
+ char *dec_buf;
+ char *colon_ptr;
+
+ mhd_assert (NULL == req->auth.basic.intr.username.cstr);
+
+ if (! mhd_request_get_auth_header_value (req,
+ mhd_AUTH_HDR_BASIC,
+ &token68))
+ return MHD_SC_AUTH_ABSENT;
+
+ mhd_assert (NULL != token68.cstr);
+
+ if (0 == token68.len)
+ {
+ /* Zero-length token68 */
+ req->auth.basic.intr.username.cstr = token68.cstr;
+ mhd_assert (0 == req->auth.basic.intr.username.len);
+ mhd_assert (NULL == req->auth.basic.intr.password.cstr);
+ mhd_assert (0 == req->auth.basic.intr.password.len);
+ return MHD_SC_OK;
+ }
+
+ alloc_size = mhd_base64_max_dec_size (token68.len) + 1;
+ dec_buf = (char *)
+ mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, \
+ struct MHD_Connection, \
+ rq),
+ alloc_size);
+ if (NULL == dec_buf)
+ return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA;
+
+ /* The 'dec_buf' remains allocated until start of sending reply or until
+ end of the request processing. */
+
+ dec_size = mhd_base64_to_bin_n (token68.cstr,
+ token68.len,
+ dec_buf,
+ alloc_size);
+ if (0 == dec_size)
+ return MHD_SC_REQ_AUTH_DATA_BROKEN;
+
+ dec_buf[dec_size] = 0; /* Zero-terminate the result */
+ req->auth.basic.intr.username.cstr = dec_buf;
+ colon_ptr = memchr (dec_buf, ':', dec_size);
+ if (NULL == colon_ptr)
+ {
+ /* No password provided. Only username. */
+ req->auth.basic.intr.username.len = dec_size;
+ mhd_assert (NULL == req->auth.basic.intr.password.cstr);
+ mhd_assert (0 == req->auth.basic.intr.password.len);
+ return MHD_SC_OK;
+ }
+ *colon_ptr = 0; /* Zero-terminate the username */
+ req->auth.basic.intr.username.len = (size_t) (colon_ptr - dec_buf);
+ req->auth.basic.intr.password.cstr = colon_ptr + 1;
+ mhd_assert ((req->auth.basic.intr.username.len + 1) <= dec_size);
+ req->auth.basic.intr.password.len =
+ dec_size - (req->auth.basic.intr.username.len + 1);
+ return MHD_SC_OK;
+}
+
+
+MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) enum MHD_StatusCode
+mhd_request_get_auth_basic_creds (
+ struct MHD_Request *restrict req,
+ const struct MHD_AuthBasicCreds **restrict v_auth_basic_creds)
+{
+ enum MHD_StatusCode res;
+ mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <=
+ mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+ mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >=
+ mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+
+ if (NULL != req->auth.basic.intr.username.cstr)
+ res = MHD_SC_OK;
+ else
+ res = find_and_parse_auth_basic (req);
+
+ if (MHD_SC_OK != res)
+ return res; /* Failure exit point */
+
+ *v_auth_basic_creds = &(req->auth.basic.extr);
+
+ return MHD_SC_OK; /* Success exit point */
+}
diff --git a/src/mhd2/auth_basic.h b/src/mhd2/auth_basic.h
@@ -0,0 +1,51 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/auth_basic.h
+ * @brief The declaration of the Basic Authorization header parser
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_AUTH_BASIC_H
+#define MHD_AUTH_BASIC_H 1
+
+#include "mhd_sys_options.h"
+
+#if ! defined(MHD_SUPPORT_AUTH_BASIC)
+#error Basic Authorization must be enabled
+#endif
+
+#include "mhd_public_api.h"
+
+/**
+ * Find in request and parse Basic Authentication information
+ * @param req the request to use
+ * @param[out] v_auth_basic_creds the pointer to set to the found data
+ * @return #MHD_SC_OK on success,
+ * error code otherwise
+ */
+MHD_INTERNAL enum MHD_StatusCode
+mhd_request_get_auth_basic_creds (
+ struct MHD_Request *restrict req,
+ const struct MHD_AuthBasicCreds **restrict v_auth_basic_creds)
+MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+#endif /* ! MHD_AUTH_BASIC_H */
diff --git a/src/mhd2/auth_digest.c b/src/mhd2/auth_digest.c
@@ -0,0 +1,3394 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2014-2025 Evgeny Grin (Karlson2k)
+ Copyright (C) 2010, 2011, 2012, 2015, 2018 Christian Grothoff
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/auth_digest.c
+ * @brief The implementation of the Digest Authorization internal functions
+ * @author Karlson2k (Evgeny Grin)
+ * Based on the MHD v0.xx code by Amr Ali, Matthieu Speder, Christian Grothoff,
+ * Dirk Brinkmeier and Evgeny Grin.
+ */
+
+#include "mhd_sys_options.h"
+
+#include "mhd_digest_auth_data.h"
+
+#include "mhd_assert.h"
+#include "mhd_unreachable.h"
+
+#include <string.h>
+#include "sys_malloc.h"
+
+#include "mhd_str_macros.h"
+#include "mhd_bithelpers.h"
+#include "mhd_arr_num_elems.h"
+#include "mhd_cntnr_ptr.h"
+#include "mhd_limits.h"
+
+#include "mhd_str_types.h"
+#include "mhd_buffer.h"
+#include "mhd_daemon.h"
+#include "mhd_request.h"
+#include "mhd_connection.h"
+
+#ifdef MHD_SUPPORT_SHA512_256
+# include "mhd_sha512_256.h"
+#endif /* MHD_SUPPORT_SHA512_256 */
+#ifdef MHD_SUPPORT_SHA256
+# include "mhd_sha256.h"
+#endif
+#ifdef MHD_SUPPORT_MD5
+# include "mhd_md5.h"
+#endif
+
+#include "mhd_str.h"
+#include "mhd_mono_clock.h"
+#include "mhd_atomic_counter.h"
+#include "mhd_locks.h"
+
+#include "request_auth_get.h"
+#include "daemon_funcs.h"
+#include "stream_funcs.h"
+#include "stream_process_request.h"
+
+#include "auth_digest.h"
+
+/*
+ * The maximum size of the hash digest, in bytes
+ */
+#if defined(MHD_SUPPORT_SHA512_256)
+# define mhd_MAX_DIGEST mhd_SHA512_256_DIGEST_SIZE
+#elif defined(MHD_SUPPORT_SHA256)
+# define mhd_MAX_DIGEST mhd_SHA256_DIGEST_SIZE
+#else
+# define mhd_MAX_DIGEST mhd_MD5_DIGEST_SIZE
+#endif
+
+/**
+ * MD5 algorithm identifier for Digest Auth headers
+ */
+#define mhd_MD5_TOKEN "MD5"
+
+/**
+ * SHA-256 algorithm identifier for Digest Auth headers
+ */
+#define mhd_SHA256_TOKEN "SHA-256"
+
+/**
+ * SHA-512/256 algorithm for Digest Auth headers.
+ */
+#define mhd_SHA512_256_TOKEN "SHA-512-256"
+
+/**
+ * The suffix token for "session" algorithms for Digest Auth headers.
+ */
+#define mhd_SESS_TOKEN "-sess"
+
+/**
+ * The "auth" token for QOP for Digest Auth headers.
+ */
+#define mhd_TOKEN_AUTH "auth"
+
+/**
+ * The "auth-int" token for QOP for Digest Auth headers.
+ */
+#define mhd_TOKEN_AUTH_INT "auth-int"
+
+
+/**
+ * The required prefix of parameter with the extended notation
+ */
+#define mhd_DAUTH_EXT_PARAM_PREFIX "UTF-8'"
+
+/**
+ * The minimal size of the prefix for parameter with the extended notation
+ */
+#define mhd_DAUTH_EXT_PARAM_MIN_LEN \
+ mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX "'")
+
+/**
+ * The maximum supported size for Digest Auth parameters, like "realm",
+ * "username" etc.
+ * This limitation is used only for quoted parameters.
+ * Parameters without quoted backslash character will be processed as long
+ * as they fit connection memory pool (buffer) size.
+ */
+#define mhd_AUTH_DIGEST_MAX_PARAM_SIZE (65535)
+
+/**
+ * Parameter of request's Digest Authorization header
+ */
+struct mhd_RqDAuthParam
+{
+ /**
+ * The string with length, NOT zero-terminated
+ */
+ struct MHD_StringNullable value;
+ /**
+ * True if string must be "unquoted" before processing.
+ * This member is false if the string is used in DQUOTE marks, but no
+ * backslash-escape is used in the string.
+ */
+ bool quoted;
+};
+
+/**
+ * Client's Digest Authorization header parameters
+ */
+struct mhd_AuthDigesReqParams
+{
+ struct mhd_RqDAuthParam nonce;
+ struct mhd_RqDAuthParam opaque;
+ struct mhd_RqDAuthParam response;
+ struct mhd_RqDAuthParam username;
+ struct mhd_RqDAuthParam username_ext;
+ struct mhd_RqDAuthParam realm;
+ struct mhd_RqDAuthParam uri;
+ /* The raw QOP value, used in the 'response' calculation */
+ struct mhd_RqDAuthParam qop_raw;
+ struct mhd_RqDAuthParam cnonce;
+ struct mhd_RqDAuthParam nc;
+
+ /* Decoded values are below */
+ bool userhash; /* True if 'userhash' parameter has value 'true'. */
+ enum MHD_DigestAuthAlgo algo;
+ enum MHD_DigestAuthQOP qop;
+};
+
+/**
+ * Digest context data
+ */
+union DigestCtx
+{
+#ifdef MHD_SUPPORT_SHA512_256
+ struct mhd_Sha512_256Ctx sha512_256_ctx;
+#endif /* MHD_SUPPORT_SHA512_256 */
+#ifdef MHD_SUPPORT_SHA256
+ struct mhd_Sha256Ctx sha256_ctx;
+#endif /* MHD_SUPPORT_SHA256 */
+#ifdef MHD_SUPPORT_MD5
+ struct mhd_Md5Ctx md5_ctx;
+#endif /* MHD_SUPPORT_MD5 */
+};
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
+
+/**
+ * Generate simple hash.
+ * Very limited avalanche effect. To be used mainly for the table slot choice.
+ * @param data_size the size of the data to hash
+ * @param data the data to hash
+ * @return the hash value
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (2, 1) uint_fast64_t
+simple_hash (size_t data_size,
+ const uint8_t *restrict data)
+{
+ static const uint_fast64_t c[] = { /* Some fractional parts of Euler's number */
+ UINT64_C (0xCC64D3484C3475A1),
+ UINT64_C (0xCF4DEBCB9ED801F2),
+ UINT64_C (0x0C8737A803CF46AD),
+ UINT64_C (0x294C9E0E0F9F14AB),
+ UINT64_C (0xAD786D855D4EBB1A)
+ };
+ uint_fast64_t res;
+ size_t i;
+
+ res = UINT64_C (0x8316A8FE31A2228E); /* Some fractional part of Pi */
+ i = 0;
+ while (1)
+ {
+ uint_fast64_t a = 0;
+
+ if (8 <= data_size)
+ memcpy (&a, data, 8);
+ else
+ memcpy (&a, data, data_size);
+ a ^= c[(i++) % mhd_ARR_NUM_ELEMS (c)];
+ a = (uint_fast64_t) mhd_ROTR64 ((uint64_t) a, \
+ (int) (res >> 58u));
+ res ^= a;
+ if (8 > data_size)
+ break;
+ data_size -= 8;
+ data += 8;
+ }
+ return res;
+}
+
+
+/**
+ * Find index of the provided nonce in the nonces table
+ * @param nonce the nonce to use
+ * @param arr_size the size of the nonces table
+ * @return the index
+ */
+static MHD_FN_PAR_NONNULL_ALL_ size_t
+nonce_to_index (const uint8_t nonce[mhd_AUTH_DIGEST_NONCE_BIN_SIZE],
+ size_t arr_size)
+{
+ uint_fast64_t hash;
+ hash = simple_hash (mhd_AUTH_DIGEST_NONCE_BIN_SIZE,
+ nonce);
+ if (arr_size == (arr_size & UINT32_C (0xFFFFFFFF)))
+ { /* 'arr_size' <=32-bit */
+ hash = (hash ^ (hash >> 32)) & UINT32_C (0xFFFFFFFF); /* Fold hash */
+ if (arr_size == (arr_size & UINT16_C (0xFFFF)))
+ { /* 'arr_size' <=16-bit */
+ hash = (hash ^ (hash >> 16)) & UINT16_C (0xFFFF); /* Fold hash */
+ if (arr_size == (arr_size & 0xFFu))
+ hash = (hash ^ (hash >> 8)) & 0xFFu; /* 'arr_size' <=8-bit, fold hash */
+ }
+ }
+ return ((size_t) hash) % arr_size;
+}
+
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE
+
+
+/**
+ * Generate a new nonce
+ * @param d the daemon to use (must match @a c connection)
+ * @param c the connection to generate nonce for
+ * @param[out] out_buf the output buffer to pull full nonce, including
+ * "expiration" tail
+ * @param[out] expir the expiration mark, duplicated for convenience
+ * @return 'true' if succeed,
+ * 'false' if failed
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (3)
+MHD_FN_PAR_OUT_ (4) bool
+gen_new_nonce (struct MHD_Daemon *restrict d,
+ struct MHD_Connection *restrict c,
+ uint8_t out_buf[mhd_AUTH_DIGEST_NONCE_BIN_SIZE],
+ uint_fast32_t *restrict expir)
+{
+ uint_fast64_t expiration;
+ size_t gen_num;
+ union DigestCtx d_ctx;
+
+ mhd_assert (! mhd_D_HAS_MASTER (d)); /* only master daemon should be used */
+ mhd_assert (d == c->daemon);
+ mhd_assert (0 != d->auth_dg.cfg.nonce_tmout);
+
+ gen_num = mhd_atomic_counter_inc_wrap_get (&(d->auth_dg.num_gen_nonces));
+
+ expiration = mhd_monotonic_msec_counter ()
+ + d->auth_dg.cfg.nonce_tmout * (uint_fast64_t) 1000;
+
+ // TODO: replace with pure random number
+
+#if defined(MHD_SUPPORT_SHA512_256)
+ mhd_SHA512_256_init_one_time (&(d_ctx.sha512_256_ctx));
+ mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+ d->auth_dg.entropy.size,
+ (const void*) d->auth_dg.entropy.data);
+ mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+ sizeof(gen_num),
+ (const void*) &gen_num);
+ if (0 != c->sk.addr.size)
+ mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+ c->sk.addr.size,
+ (const void*) c->sk.addr.data);
+ mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+ sizeof(expiration),
+ (const void*) &expiration);
+ mhd_SHA512_256_finish_deinit (&(d_ctx.sha512_256_ctx), \
+ out_buf);
+ if (mhd_SHA512_256_has_err (&(d_ctx.sha512_256_ctx)))
+ return false;
+#elif defined(MHD_SUPPORT_SHA256)
+ mhd_SHA256_init_one_time (&(d_ctx.sha256_ctx));
+ mhd_SHA256_update (&(d_ctx.sha256_ctx),
+ d->auth_dg.entropy.size,
+ (const void*) d->auth_dg.entropy.data);
+ mhd_SHA256_update (&(d_ctx.sha256_ctx),
+ sizeof(gen_num),
+ (const void*) &gen_num);
+ if (0 != c->sk.addr.size)
+ mhd_SHA256_update (&(d_ctx.sha256_ctx),
+ c->sk.addr.size,
+ (const void*) c->sk.addr.data);
+ mhd_SHA256_update (&(d_ctx.sha256_ctx),
+ sizeof(expiration),
+ (const void*) &expiration);
+ mhd_SHA256_finish_deinit (&(d_ctx.sha256_ctx), \
+ out_buf);
+ if (mhd_SHA256_has_err (&(d_ctx.sha256_ctx)))
+ return false;
+#else /* MHD_SUPPORT_MD5 */
+#ifndef MHD_SUPPORT_MD5
+#error At least one hashing algorithm must be enabled
+#endif
+ mhd_MD5_init_one_time (&(d_ctx.md5_ctx));
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ d->auth_dg.entropy.size,
+ (const void*) d->auth_dg.entropy.data);
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ sizeof(gen_num),
+ (const void*) &gen_num);
+ if (0 != c->sk.addr.size)
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ c->sk.addr.size,
+ (const void*) c->sk.addr.data);
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ sizeof(expiration),
+ (const void*) &expiration);
+ mhd_MD5_finish_deinit (&(d_ctx.md5_ctx), \
+ out_buf);
+ if (mhd_MD5_has_err (&(d_ctx.md5_ctx)))
+ return false;
+
+ /* One more hash, for the second part */
+ gen_num = mhd_atomic_counter_inc_wrap_get (&(d->auth_dg.num_gen_nonces));
+
+ mhd_MD5_init_one_time (&(d_ctx.md5_ctx));
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ d->auth_dg.entropy.size,
+ (const void*) d->auth_dg.entropy.data);
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ sizeof(gen_num),
+ (const void*) &gen_num);
+ if (0 != c->sk.addr.size)
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ c->sk.addr.size,
+ (const void*) c->sk.addr.data);
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ sizeof(expiration),
+ (const void*) &expiration);
+ mhd_MD5_finish_deinit (&(d_ctx.md5_ctx), \
+ out_buf + mhd_MD5_DIGEST_SIZE);
+ if (mhd_MD5_has_err (&(d_ctx.md5_ctx)))
+ return false;
+#endif /* MHD_SUPPORT_MD5 */
+
+ *expir = (uint_fast32_t) (expiration / 1000u);
+ mhd_PUT_32BIT_LE_UNALIGN (out_buf + mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE, \
+ (uint32_t) (*expir & UINT32_C (0xFFFFFFFF)));
+
+ return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) bool
+mhd_auth_digest_get_new_nonce (struct MHD_Connection *restrict c,
+ char out_buf[mhd_AUTH_DIGEST_NONCE_LEN])
+{
+ static const int max_retries = 3;
+ struct MHD_Daemon *restrict d = mhd_daemon_get_master_daemon (c->daemon);
+ uint8_t nonce_bin[mhd_AUTH_DIGEST_NONCE_BIN_SIZE];
+ uint_fast32_t expir;
+ bool nonce_generated;
+ int i;
+
+ mhd_assert (0 != d->auth_dg.cfg.nonces_num);
+ mhd_assert (NULL != d->auth_dg.nonces);
+
+ nonce_generated = false;
+ for (i = 0; i < max_retries; ++i)
+ {
+ bool good_nonce;
+ struct mhd_DaemonAuthDigestNonceData *nonce_slot;
+ if (! gen_new_nonce (d,
+ c,
+ nonce_bin,
+ &expir))
+ continue; /* Failed, re-try */
+
+ nonce_generated = true;
+ nonce_slot = d->auth_dg.nonces
+ + nonce_to_index (nonce_bin,
+ d->auth_dg.cfg.nonces_num);
+ if (! mhd_mutex_lock (&(d->auth_dg.nonces_lock)))
+ return false; /* Failure exit point */
+ /* Check whether the same nonce has been used before */
+ good_nonce = (0 != memcmp (nonce_slot->nonce,
+ nonce_bin,
+ sizeof(nonce_slot->nonce)));
+ if (good_nonce)
+ {
+ memcpy (nonce_slot->nonce,
+ nonce_bin,
+ sizeof(nonce_slot->nonce));
+ nonce_slot->valid_time = expir;
+ nonce_slot->max_recvd_nc = 0;
+ nonce_slot->nmask = 0;
+ }
+ else
+ {
+ /* Check whether the same nonce has been used with different expiration
+ time. */
+ nonce_generated = (nonce_slot->valid_time == expir);
+ }
+ mhd_mutex_unlock_chk (&(d->auth_dg.nonces_lock));
+ if (good_nonce)
+ break;
+ }
+ if (! nonce_generated)
+ return false; /* Failure exit point */
+
+ /* Use the generated nonce even if it is duplicated.
+ One of the clients will just get "nonce stale" response with
+ the new nonce. */
+ (void) mhd_bin_to_hex (nonce_bin,
+ sizeof(nonce_bin),
+ out_buf);
+ return true; /* Success exit point */
+}
+
+
+/**
+ * Get client's Digest Authorization algorithm type.
+ * If no algorithm is specified by client, MD5 is assumed.
+ * @param params the Digest Authorization 'algorithm' parameter
+ * @return the algorithm type
+ */
+static enum MHD_DigestAuthAlgo
+get_rq_dauth_algo (const struct mhd_RqDAuthParam *const algo_param)
+{
+ if (NULL == algo_param->value.cstr)
+ return MHD_DIGEST_AUTH_ALGO_MD5; /* Assume MD5 by default */
+
+ if (algo_param->quoted)
+ {
+ if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+ algo_param->value.len, \
+ mhd_MD5_TOKEN))
+ return MHD_DIGEST_AUTH_ALGO_MD5;
+ if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+ algo_param->value.len, \
+ mhd_SHA256_TOKEN))
+ return MHD_DIGEST_AUTH_ALGO_SHA256;
+ if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+ algo_param->value.len, \
+ mhd_MD5_TOKEN mhd_SESS_TOKEN))
+ return MHD_DIGEST_AUTH_ALGO_SHA512_256;
+ if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+ algo_param->value.len, \
+ mhd_SHA512_256_TOKEN \
+ mhd_SESS_TOKEN))
+
+ /* Algorithms below are not supported by MHD for authentication */
+
+ return MHD_DIGEST_AUTH_ALGO_MD5_SESSION;
+ if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+ algo_param->value.len, \
+ mhd_SHA256_TOKEN \
+ mhd_SESS_TOKEN))
+ return MHD_DIGEST_AUTH_ALGO_SHA256_SESSION;
+ if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+ algo_param->value.len, \
+ mhd_SHA512_256_TOKEN))
+ return MHD_DIGEST_AUTH_ALGO_SHA512_256_SESSION;
+
+ /* No known algorithm has been detected */
+ return MHD_DIGEST_AUTH_ALGO_INVALID;
+ }
+ /* The algorithm value is not quoted */
+ if (mhd_str_equal_caseless_n_st (mhd_MD5_TOKEN, \
+ algo_param->value.cstr, \
+ algo_param->value.len))
+ return MHD_DIGEST_AUTH_ALGO_MD5;
+ if (mhd_str_equal_caseless_n_st (mhd_SHA256_TOKEN, \
+ algo_param->value.cstr, \
+ algo_param->value.len))
+ return MHD_DIGEST_AUTH_ALGO_SHA256;
+ if (mhd_str_equal_caseless_n_st (mhd_SHA512_256_TOKEN, \
+ algo_param->value.cstr, \
+ algo_param->value.len))
+ return MHD_DIGEST_AUTH_ALGO_SHA512_256;
+
+ /* Algorithms below are not supported by MHD for authentication */
+
+ if (mhd_str_equal_caseless_n_st (mhd_MD5_TOKEN mhd_SESS_TOKEN, \
+ algo_param->value.cstr, \
+ algo_param->value.len))
+ return MHD_DIGEST_AUTH_ALGO_MD5_SESSION;
+ if (mhd_str_equal_caseless_n_st (mhd_SHA256_TOKEN mhd_SESS_TOKEN, \
+ algo_param->value.cstr, \
+ algo_param->value.len))
+ return MHD_DIGEST_AUTH_ALGO_SHA256_SESSION;
+ if (mhd_str_equal_caseless_n_st (mhd_SHA512_256_TOKEN mhd_SESS_TOKEN, \
+ algo_param->value.cstr, \
+ algo_param->value.len))
+ return MHD_DIGEST_AUTH_ALGO_SHA512_256_SESSION;
+
+ /* No known algorithm has been detected */
+ return MHD_DIGEST_AUTH_ALGO_INVALID;
+}
+
+
+/**
+ * Get QOP ('quality of protection') type.
+ * @param qop_param the Digest Authorization 'QOP' parameter
+ * @return detected QOP ('quality of protection') type.
+ */
+static enum MHD_DigestAuthQOP
+get_rq_dauth_qop (const struct mhd_RqDAuthParam *const qop_param)
+{
+ if (NULL == qop_param->value.cstr)
+ return MHD_DIGEST_AUTH_QOP_NONE;
+ if (qop_param->quoted)
+ {
+ if (mhd_str_equal_caseless_quoted_s_bin_n (qop_param->value.cstr, \
+ qop_param->value.len, \
+ mhd_TOKEN_AUTH))
+ return MHD_DIGEST_AUTH_QOP_AUTH;
+ if (mhd_str_equal_caseless_quoted_s_bin_n (qop_param->value.cstr, \
+ qop_param->value.len, \
+ mhd_TOKEN_AUTH_INT))
+ return MHD_DIGEST_AUTH_QOP_AUTH_INT;
+ }
+ else
+ {
+ if (mhd_str_equal_caseless_n_st (mhd_TOKEN_AUTH, \
+ qop_param->value.cstr, \
+ qop_param->value.len))
+ return MHD_DIGEST_AUTH_QOP_AUTH;
+ if (mhd_str_equal_caseless_n_st (mhd_TOKEN_AUTH_INT, \
+ qop_param->value.cstr, \
+ qop_param->value.len))
+ return MHD_DIGEST_AUTH_QOP_AUTH_INT;
+ }
+ /* No know QOP has been detected */
+ return MHD_DIGEST_AUTH_QOP_INVALID;
+}
+
+
+/**
+ * Parse request Authorization header parameters for Digest Authentication
+ * @param str the header string, everything after "Digest " substring
+ * @param str_len the length of @a str in characters
+ * @param[out] pdauth the pointer to the structure with Digest Authentication
+ * parameters
+ * @return true if parameters has been successfully parsed,
+ * false if format of the @a str is invalid
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) bool
+parse_dauth_params (const struct MHD_String *restrict val,
+ struct mhd_AuthDigesReqParams *restrict pdauth)
+{
+ /* The tokens */
+ static const struct MHD_String nonce_tk = mhd_MSTR_INIT ("nonce");
+ static const struct MHD_String opaque_tk = mhd_MSTR_INIT ("opaque");
+ static const struct MHD_String algorithm_tk = mhd_MSTR_INIT ("algorithm");
+ static const struct MHD_String response_tk = mhd_MSTR_INIT ("response");
+ static const struct MHD_String username_tk = mhd_MSTR_INIT ("username");
+ static const struct MHD_String username_ext_tk = mhd_MSTR_INIT ("username*");
+ static const struct MHD_String realm_tk = mhd_MSTR_INIT ("realm");
+ static const struct MHD_String uri_tk = mhd_MSTR_INIT ("uri");
+ static const struct MHD_String qop_tk = mhd_MSTR_INIT ("qop");
+ static const struct MHD_String cnonce_tk = mhd_MSTR_INIT ("cnonce");
+ static const struct MHD_String nc_tk = mhd_MSTR_INIT ("nc");
+ static const struct MHD_String userhash_tk = mhd_MSTR_INIT ("userhash");
+ /* The locally processed parameters */
+ struct mhd_RqDAuthParam userhash = { {0, NULL}, false};
+ struct mhd_RqDAuthParam algorithm = { {0, NULL}, false};
+ /* Indexes */
+ size_t i;
+ size_t p;
+ /* The list of the tokens.
+ The order of the elements matches the next array. */
+ static const struct MHD_String *const tk_names[] = {
+ &nonce_tk, /* 0 */
+ &opaque_tk, /* 1 */
+ &algorithm_tk, /* 2 */
+ &response_tk, /* 3 */
+ &username_tk, /* 4 */
+ &username_ext_tk, /* 5 */
+ &realm_tk, /* 6 */
+ &uri_tk, /* 7 */
+ &qop_tk, /* 8 */
+ &cnonce_tk, /* 9 */
+ &nc_tk, /* 10 */
+ &userhash_tk /* 11 */
+ };
+ /* The list of the parameters.
+ The order of the elements matches the previous array. */
+ struct mhd_RqDAuthParam *params[sizeof(tk_names) / sizeof(tk_names[0])];
+
+ params[0 ] = &(pdauth->nonce); /* 0 */
+ params[1 ] = &(pdauth->opaque); /* 1 */
+ params[2 ] = &algorithm; /* 2 */
+ params[3 ] = &(pdauth->response); /* 3 */
+ params[4 ] = &(pdauth->username); /* 4 */
+ params[5 ] = &(pdauth->username_ext); /* 5 */
+ params[6 ] = &(pdauth->realm); /* 6 */
+ params[7 ] = &(pdauth->uri); /* 7 */
+ params[8 ] = &(pdauth->qop_raw); /* 8 */
+ params[9 ] = &(pdauth->cnonce); /* 9 */
+ params[10] = &(pdauth->nc); /* 10 */
+ params[11] = &userhash; /* 11 */
+
+ mhd_assert (mhd_ARR_NUM_ELEMS (tk_names) == \
+ mhd_ARR_NUM_ELEMS (params));
+ i = 0;
+
+ mhd_assert (' ' != val->cstr[0]);
+ mhd_assert ('\t' != val->cstr[0]);
+
+ while (val->len > i)
+ {
+ size_t left;
+ mhd_assert (' ' != val->cstr[i]);
+ mhd_assert ('\t' != val->cstr[i]);
+
+ left = val->len - i;
+ if ('=' == val->cstr[i])
+ return false; /* The equal sign is not allowed as the first character */
+ for (p = 0; p < mhd_ARR_NUM_ELEMS (tk_names); ++p)
+ {
+ const struct MHD_String *const tk_name = tk_names[p];
+ struct mhd_RqDAuthParam *const param = params[p];
+ if ( (tk_name->len <= left) &&
+ mhd_str_equal_caseless_bin_n (val->cstr + i, tk_name->cstr,
+ tk_name->len) &&
+ ((tk_name->len == left) ||
+ ('=' == val->cstr[i + tk_name->len]) ||
+ (' ' == val->cstr[i + tk_name->len]) ||
+ ('\t' == val->cstr[i + tk_name->len]) ||
+ (',' == val->cstr[i + tk_name->len]) ||
+ (';' == val->cstr[i + tk_name->len])) )
+ {
+ size_t value_start;
+ size_t value_len;
+ bool quoted; /* Only mark as "quoted" if backslash-escape used */
+
+ if (tk_name->len == left)
+ return false; /* No equal sign after parameter name, broken data */
+
+ quoted = false;
+ i += tk_name->len;
+ /* Skip all whitespaces before '=' */
+ while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
+ i++;
+ if ((i == val->len) || ('=' != val->cstr[i]))
+ return false; /* No equal sign, broken data */
+ i++;
+ /* Skip all whitespaces after '=' */
+ while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
+ i++;
+ if ((val->len > i) && ('"' == val->cstr[i]))
+ { /* Value is in quotation marks */
+ i++; /* Advance after the opening quote */
+ value_start = i;
+ while (val->len > i && '"' != val->cstr[i])
+ {
+ if ('\\' == val->cstr[i])
+ {
+ i++;
+ quoted = true; /* Have escaped chars */
+ }
+ if (0 == val->cstr[i])
+ return false; /* Binary zero in parameter value */
+ i++;
+ }
+ if (val->len <= i)
+ return false; /* No closing quote */
+ mhd_assert ('"' == val->cstr[i]);
+ value_len = i - value_start;
+ i++; /* Advance after the closing quote */
+ }
+ else
+ {
+ value_start = i;
+ while ((val->len > i) && (',' != val->cstr[i])
+ && (' ' != val->cstr[i]) && ('\t' != val->cstr[i])
+ && (';' != val->cstr[i]))
+ {
+ if (0 == val->cstr[i])
+ return false; /* Binary zero in parameter value */
+ i++;
+ }
+ if (';' == val->cstr[i])
+ return false; /* Semicolon in parameter value */
+ value_len = i - value_start;
+ }
+ /* Skip all whitespaces after parameter value */
+ while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
+ i++;
+ if ((val->len > i) && (',' != val->cstr[i]))
+ return false; /* Garbage after parameter value */
+
+ /* Have valid parameter name and value */
+ mhd_assert (! quoted || 0 != value_len);
+ param->value.cstr = val->cstr + value_start;
+ param->value.len = value_len;
+ param->quoted = quoted;
+
+ break; /* Found matching parameter name */
+ }
+ }
+ if (p == mhd_ARR_NUM_ELEMS (tk_names))
+ {
+ /* No matching parameter name */
+ while (val->len > i && ',' != val->cstr[i])
+ {
+ if ((0 == val->cstr[i]) || (';' == val->cstr[i]))
+ return false; /* Not allowed characters */
+ if ('"' == val->cstr[i])
+ { /* Skip quoted part */
+ i++; /* Advance after the opening quote */
+ while (val->len > i && '"' != val->cstr[i])
+ {
+ if (0 == val->cstr[i])
+ return false; /* Binary zero is not allowed */
+ if ('\\' == val->cstr[i])
+ i++; /* Skip escaped char */
+ i++;
+ }
+ if (val->len <= i)
+ return false; /* No closing quote */
+ mhd_assert ('"' == val->cstr[i]);
+ }
+ i++;
+ }
+ }
+ mhd_assert (val->len == i || ',' == val->cstr[i]);
+ if (val->len > i)
+ i++; /* Advance after ',' */
+ /* Skip all whitespaces before next parameter name */
+ while (i < val->len && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
+ i++;
+ }
+
+ /* Postprocess values */
+
+ if (NULL != userhash.value.cstr)
+ {
+ if (userhash.quoted)
+ pdauth->userhash =
+ mhd_str_equal_caseless_quoted_s_bin_n (userhash.value.cstr, \
+ userhash.value.len, \
+ "true");
+ else
+ pdauth->userhash =
+ mhd_str_equal_caseless_n_st ("true", userhash.value.cstr, \
+ userhash.value.len);
+
+ }
+ else
+ pdauth->userhash = false;
+
+ pdauth->algo = get_rq_dauth_algo (&algorithm);
+ pdauth->qop = get_rq_dauth_qop (&pdauth->qop_raw);
+
+ return true;
+}
+
+
+/**
+ * Find and pre-parse request's Digest Authorisation parameters.
+ *
+ * Function returns result of pre-parsing of the request's "Authorization"
+ * header or returns cached result if the header has been already pre-parsed for
+ * the current request.
+ * @param req the request to process
+ * @return #MHD_SC_OK if succeed,
+ * #MHD_SC_AUTH_ABSENT if request has no Digest Authorisation,
+ * #MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA if not enough memory,
+ * #MHD_SC_REQ_AUTH_DATA_BROKEN if the header is broken.
+ */
+static MHD_FN_PAR_NONNULL_ALL_ enum MHD_StatusCode
+get_rq_auth_digest_params (struct MHD_Request *restrict req)
+{
+ struct MHD_String h_auth_value;
+ struct mhd_AuthDigesReqParams *dauth;
+
+ mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \
+ mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+ mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= \
+ mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+
+ if (NULL != req->auth.digest.rqp)
+ return MHD_SC_OK;
+
+ if (! mhd_request_get_auth_header_value (req,
+ mhd_AUTH_HDR_DIGEST,
+ &h_auth_value))
+ return MHD_SC_AUTH_ABSENT;
+
+ dauth =
+ (struct mhd_AuthDigesReqParams *)
+ mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, \
+ struct MHD_Connection, \
+ rq),
+ sizeof (struct mhd_AuthDigesReqParams));
+
+ if (NULL == dauth)
+ return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA;
+
+ memset (dauth, 0, sizeof(struct mhd_AuthDigesReqParams));
+ if (! parse_dauth_params (&h_auth_value,
+ dauth))
+ return MHD_SC_REQ_AUTH_DATA_BROKEN;
+
+ req->auth.digest.rqp = dauth;
+
+ return MHD_SC_OK;
+}
+
+
+/**
+ * Get username type used by the client.
+ * This function does not check whether userhash can be decoded or
+ * extended notation (if used) is valid.
+ * @param params the Digest Authorization parameters
+ * @return the type of username
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ enum MHD_DigestAuthUsernameType
+get_rq_uname_type (const struct mhd_AuthDigesReqParams *params)
+{
+ if (NULL != params->username.value.cstr)
+ {
+ if (NULL == params->username_ext.value.cstr)
+ return params->userhash ?
+ MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH :
+ MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD;
+ else /* Both 'username' and 'username*' are used */
+ return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
+ }
+ else if (NULL != params->username_ext.value.cstr)
+ {
+ if (! params->username_ext.quoted && ! params->userhash &&
+ (mhd_DAUTH_EXT_PARAM_MIN_LEN <= params->username_ext.value.len) )
+ return MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED;
+ else
+ return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
+ }
+
+ return MHD_DIGEST_AUTH_UNAME_TYPE_MISSING;
+}
+
+
+/**
+ * Get total size required for 'username' and 'userhash_bin'
+ * @param params the Digest Authorization parameters
+ * @param uname_type the type of username
+ * @return the total size required for 'username' and
+ * 'userhash_bin' is userhash is used
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ size_t
+get_rq_unames_size (const struct mhd_AuthDigesReqParams *params,
+ enum MHD_DigestAuthUsernameType uname_type)
+{
+ size_t s;
+
+ mhd_assert (get_rq_uname_type (params) == uname_type);
+ s = 0;
+ if ((MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type) ||
+ (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type) )
+ {
+ s += params->username.value.len + 1; /* Add one byte for zero-termination */
+ if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
+ s += (params->username.value.len + 1) / 2;
+ }
+ else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type)
+ s += params->username_ext.value.len
+ - mhd_DAUTH_EXT_PARAM_MIN_LEN + 1; /* Add one byte for zero-termination */
+ return s;
+}
+
+
+/**
+ * Get unquoted version of Digest Authorization parameter.
+ * This function automatically zero-teminate the result.
+ * @param param the parameter to extract
+ * @param[out] buf the output buffer, must have enough size to hold the result,
+ * the recommended size is 'param->value.len + 1'
+ * @return the size of the result, not including the terminating zero
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) size_t
+get_rq_param_unquoted_copy_z (const struct mhd_RqDAuthParam *restrict param,
+ char *restrict buf)
+{
+ size_t len;
+ mhd_assert (NULL != param->value.cstr);
+ if (! param->quoted)
+ {
+ memcpy (buf, param->value.cstr, param->value.len);
+ buf [param->value.len] = 0;
+ return param->value.len;
+ }
+
+ len = mhd_str_unquote (param->value.cstr, param->value.len, buf);
+ mhd_assert (0 != len);
+ mhd_assert (len < param->value.len);
+ buf[len] = 0;
+ return len;
+}
+
+
+/**
+ * Get decoded version of username from extended notation.
+ * This function automatically zero-teminate the result.
+ * @param uname_ext the string of client's 'username*' parameter value
+ * @param uname_ext_len the length of @a uname_ext in chars
+ * @param[out] buf the output buffer to put decoded username value
+ * @param buf_size the size of @a buf
+ * @return the number of characters copied to the output buffer or
+ * -1 if wrong extended notation is used.
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (1,2)
+MHD_FN_PAR_OUT_SIZE_ (3,4) ssize_t
+get_rq_extended_uname_copy_z (const char *restrict uname_ext,
+ size_t uname_ext_len,
+ char *restrict buf,
+ size_t buf_size)
+{
+ size_t r;
+ size_t w;
+ if ((size_t) SSIZE_MAX < uname_ext_len)
+ return -1; /* Too long input string */
+
+ if (mhd_DAUTH_EXT_PARAM_MIN_LEN > uname_ext_len)
+ return -1; /* Required prefix is missing */
+
+ if (! mhd_str_equal_caseless_bin_n (
+ uname_ext,
+ mhd_DAUTH_EXT_PARAM_PREFIX,
+ mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX)))
+ return -1; /* Only UTF-8 is supported, as it is implied by RFC 7616 */
+
+ r = mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX);
+ /* Skip language tag */
+ while (r < uname_ext_len && '\'' != uname_ext[r])
+ {
+ const char chr = uname_ext[r];
+ if ((' ' == chr) || ('\t' == chr) || ('\"' == chr) || (',' == chr) ||
+ (';' == chr) )
+ return -1; /* Wrong char in language tag */
+ r++;
+ }
+ if (r >= uname_ext_len)
+ return -1; /* The end of the language tag was not found */
+ r++; /* Advance to the next char */
+
+ w = mhd_str_pct_decode_strict_n (uname_ext + r, uname_ext_len - r,
+ buf, buf_size);
+ if ((0 == w) && (0 != uname_ext_len - r))
+ return -1; /* Broken percent encoding */
+ buf[w] = 0; /* Zero terminate the result */
+ mhd_assert (SSIZE_MAX > w);
+ return (ssize_t) w;
+}
+
+
+/**
+ * Get copy of username used by the client.
+ * @param params the Digest Authorization parameters
+ * @param uname_type the type of username
+ * @param[out] uname_info the pointer to the structure to be filled
+ * @param buf the buffer to be used for usernames
+ * @param buf_size the size of the @a buf
+ * @return the size of the @a buf used by pointers in @a unames structure
+ */
+static size_t
+get_rq_uname (const struct mhd_AuthDigesReqParams *restrict params,
+ enum MHD_DigestAuthUsernameType uname_type,
+ struct MHD_AuthDigestUsernameInfo *restrict uname_info,
+ uint8_t *restrict buf,
+ size_t buf_size)
+{
+ size_t buf_used;
+
+ buf_used = 0;
+ mhd_assert (get_rq_uname_type (params) == uname_type);
+ mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type);
+ mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type);
+
+ uname_info->username.cstr = NULL;
+ uname_info->username.len = 0;
+ uname_info->userhash_hex.cstr = NULL;
+ uname_info->userhash_hex.len = 0;
+ uname_info->userhash_bin = NULL;
+
+ if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type)
+ {
+ // TODO: Avoid copying string if not quoted
+ uname_info->username.cstr = (char *) (buf + buf_used);
+ uname_info->username.len =
+ get_rq_param_unquoted_copy_z (¶ms->username,
+ (char *) (buf + buf_used));
+ buf_used += uname_info->username.len + 1;
+ uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD;
+ }
+ else if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
+ {
+ size_t res;
+
+ uname_info->userhash_hex.cstr = (char *) (buf + buf_used);
+ uname_info->userhash_hex.len =
+ get_rq_param_unquoted_copy_z (¶ms->username,
+ (char *) (buf + buf_used));
+ buf_used += uname_info->userhash_hex.len + 1;
+ uname_info->userhash_bin = (uint8_t *) (buf + buf_used);
+ res = mhd_hex_to_bin (uname_info->userhash_hex.cstr,
+ uname_info->userhash_hex.len,
+ (uint8_t *) (buf + buf_used));
+ if (res != uname_info->userhash_hex.len / 2)
+ {
+ uname_info->userhash_bin = NULL;
+ uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
+ }
+ else
+ {
+ /* Avoid pointers outside allocated region when the size is zero */
+ if (0 == res)
+ uname_info->userhash_bin = (const uint8_t *) uname_info->username.cstr;
+ uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH;
+ buf_used += res;
+ }
+ }
+ else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type)
+ {
+ ssize_t res;
+ res = get_rq_extended_uname_copy_z (params->username_ext.value.cstr,
+ params->username_ext.value.len,
+ (char *) (buf + buf_used),
+ buf_size - buf_used);
+ if (0 > res)
+ uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
+ else
+ {
+ uname_info->username.cstr = (char *) (buf + buf_used);
+ uname_info->username.len = (size_t) res;
+ uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED;
+ buf_used += uname_info->username.len + 1;
+ }
+ }
+ else
+ {
+ mhd_assert (0);
+ uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
+ }
+ mhd_assert (buf_size >= buf_used);
+ return buf_used;
+}
+
+
+/**
+ * Result of request's Digest Authorization 'nc' value extraction
+ */
+enum MHD_FIXED_ENUM_ mhd_GetRqNCResult
+{
+ mhd_GET_RQ_NC_NONE = MHD_DIGEST_AUTH_NC_NONE, /**< No 'nc' value */
+ mhd_GET_RQ_NC_VALID = MHD_DIGEST_AUTH_NC_NUMBER, /**< Readable 'nc' value */
+ mhd_GET_RQ_NC_TOO_LONG = MHD_DIGEST_AUTH_NC_TOO_LONG, /**< The 'nc' value is too long */
+ mhd_GET_RQ_NC_TOO_LARGE = MHD_DIGEST_AUTH_NC_TOO_LARGE, /**< The 'nc' value is too big to fit uint32_t */
+ mhd_GET_RQ_NC_BROKEN = 0 /**< The 'nc' value is not a number */
+};
+
+
+/**
+ * Get 'nc' value from request's Authorization header
+ * @param params the request digest authentication
+ * @param[out] nc the pointer to put nc value to
+ * @return enum value indicating the result
+ */
+static enum mhd_GetRqNCResult
+get_rq_nc (const struct mhd_AuthDigesReqParams *params,
+ uint_fast32_t *nc)
+{
+ const struct mhd_RqDAuthParam *const nc_param =
+ ¶ms->nc;
+ char unq[16];
+ const char *val;
+ size_t val_len;
+ size_t res;
+ uint64_t nc_val;
+
+ if (NULL == nc_param->value.cstr)
+ return mhd_GET_RQ_NC_NONE;
+
+ if (0 == nc_param->value.len)
+ return mhd_GET_RQ_NC_BROKEN;
+
+ if (! nc_param->quoted)
+ {
+ val = nc_param->value.cstr;
+ val_len = nc_param->value.len;
+ }
+ else
+ {
+ /* Actually no backslashes must be used in 'nc' */
+ if (sizeof(unq) < params->nc.value.len)
+ return mhd_GET_RQ_NC_TOO_LONG;
+ val_len = mhd_str_unquote (nc_param->value.cstr, nc_param->value.len, unq);
+ if (0 == val_len)
+ return mhd_GET_RQ_NC_BROKEN;
+ val = unq;
+ }
+
+ res = mhd_strx_to_uint64_n (val,
+ val_len,
+ &nc_val);
+ if (0 == res)
+ {
+ const char f = val[0];
+ if ( (('9' >= f) && ('0' <= f)) ||
+ (('F' >= f) && ('A' <= f)) ||
+ (('a' <= f) && ('f' >= f)) )
+ return mhd_GET_RQ_NC_TOO_LARGE;
+ else
+ return mhd_GET_RQ_NC_BROKEN;
+ }
+ if (val_len != res)
+ return mhd_GET_RQ_NC_BROKEN;
+ if (nc_val != (nc_val & UINT64_C (0xFFFFFFFF)))
+ return mhd_GET_RQ_NC_TOO_LARGE;
+ *nc = (uint_fast32_t) nc_val;
+ return mhd_GET_RQ_NC_VALID;
+}
+
+
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+enum MHD_StatusCode
+find_and_parse_auth_digest_uname (struct MHD_Request *restrict req)
+{
+ enum MHD_StatusCode res;
+ struct MHD_AuthDigestUsernameInfo *uname_info;
+ enum MHD_DigestAuthUsernameType uname_type;
+ size_t unif_buf_size;
+ uint8_t *unif_buf_ptr;
+ size_t unif_buf_used;
+
+ mhd_assert (NULL == req->auth.digest.info);
+ mhd_assert (NULL == req->auth.digest.uname);
+
+ res = get_rq_auth_digest_params (req);
+ if (MHD_SC_OK != res)
+ return res;
+
+ mhd_assert (NULL != req->auth.digest.rqp);
+
+ uname_type = get_rq_uname_type (req->auth.digest.rqp);
+ if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING == uname_type) ||
+ (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_type) )
+ return MHD_SC_REQ_AUTH_DATA_BROKEN;
+
+ unif_buf_size = get_rq_unames_size (req->auth.digest.rqp, uname_type);
+
+ uname_info =
+ (struct MHD_AuthDigestUsernameInfo *)
+ mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, struct MHD_Connection, rq),
+ (sizeof(struct MHD_AuthDigestUsernameInfo))
+ + unif_buf_size);
+ if (NULL == uname_info)
+ return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA;
+ memset (uname_info,
+ 0,
+ (sizeof(struct MHD_AuthDigestUsernameInfo)) + unif_buf_size);
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+ uname_info->username.cstr = NULL;
+ uname_info->userhash_hex.cstr = NULL;
+ uname_info->userhash_bin = NULL;
+#endif
+
+ uname_info->algo = req->auth.digest.rqp->algo;
+ unif_buf_ptr = (uint8_t *) (uname_info + 1);
+ unif_buf_used = get_rq_uname (req->auth.digest.rqp,
+ uname_type, uname_info,
+ unif_buf_ptr,
+ unif_buf_size);
+ mhd_assert (unif_buf_size >= unif_buf_used);
+ (void) unif_buf_used; /* Mute compiler warning on non-debug builds */
+ mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_info->uname_type);
+
+ req->auth.digest.uname = uname_info;
+ if (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_info->uname_type)
+ return MHD_SC_REQ_AUTH_DATA_BROKEN;
+ mhd_assert (uname_type == uname_info->uname_type);
+
+ return MHD_SC_OK;
+}
+
+
+MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) enum MHD_StatusCode
+mhd_request_get_auth_digest_username (
+ struct MHD_Request *restrict req,
+ const struct MHD_AuthDigestUsernameInfo **restrict v_auth_digest_uname)
+{
+ mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \
+ mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+ mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= \
+ mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+
+ if (MHD_SC_OK != req->auth.digest.parse_result)
+ return req->auth.digest.parse_result;
+
+ if (NULL == req->auth.digest.uname)
+ req->auth.digest.parse_result = find_and_parse_auth_digest_uname (req);
+
+ if (MHD_SC_OK != req->auth.digest.parse_result)
+ return req->auth.digest.parse_result; /* Failure exit point */
+
+ mhd_assert (NULL != req->auth.digest.uname);
+ *v_auth_digest_uname = req->auth.digest.uname;
+
+ return MHD_SC_OK; /* Success exit point */
+}
+
+
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+enum MHD_StatusCode
+find_and_parse_auth_digest_info (struct MHD_Request *restrict req)
+{
+ enum MHD_StatusCode res;
+ struct MHD_AuthDigestInfo *info;
+ enum MHD_DigestAuthUsernameType uname_type;
+ size_t unif_buf_size;
+ uint8_t *unif_buf_ptr;
+ size_t unif_buf_used;
+ enum mhd_GetRqNCResult nc_res;
+
+ mhd_assert (NULL == req->auth.digest.info);
+
+ res = get_rq_auth_digest_params (req);
+ if (MHD_SC_OK != res)
+ return res;
+
+ unif_buf_size = 0;
+
+ uname_type = get_rq_uname_type (req->auth.digest.rqp);
+
+ unif_buf_size += get_rq_unames_size (req->auth.digest.rqp,
+ uname_type);
+
+ if (NULL != req->auth.digest.rqp->opaque.value.cstr)
+ unif_buf_size += req->auth.digest.rqp->opaque.value.len + 1; /* Add one for zero-termination */
+ if (NULL != req->auth.digest.rqp->realm.value.cstr)
+ unif_buf_size += req->auth.digest.rqp->realm.value.len + 1; /* Add one for zero-termination */
+ info =
+ (struct MHD_AuthDigestInfo *)
+ mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, struct MHD_Connection, rq),
+ (sizeof(struct MHD_AuthDigestInfo))
+ + unif_buf_size);
+ if (NULL == info)
+ return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA;
+
+ memset (info,
+ 0,
+ (sizeof(struct MHD_AuthDigestInfo)) + unif_buf_size);
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+ info->username.cstr = NULL;
+ info->userhash_hex.cstr = NULL;
+ info->userhash_bin = NULL;
+ info->opaque.cstr = NULL;
+ info->realm.cstr = NULL;
+#endif
+
+ unif_buf_ptr = (uint8_t *) (info + 1);
+ unif_buf_used = 0;
+
+ info->algo = req->auth.digest.rqp->algo;
+
+ if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type) &&
+ (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type) )
+ unif_buf_used +=
+ get_rq_uname (req->auth.digest.rqp, uname_type,
+ (struct MHD_AuthDigestUsernameInfo *) info,
+ unif_buf_ptr + unif_buf_used,
+ unif_buf_size - unif_buf_used);
+ else
+ info->uname_type = uname_type;
+
+ if (NULL != req->auth.digest.rqp->opaque.value.cstr)
+ {
+ info->opaque.cstr = (char *) (unif_buf_ptr + unif_buf_used);
+ info->opaque.len =
+ get_rq_param_unquoted_copy_z (&(req->auth.digest.rqp->opaque),
+ (char *) (unif_buf_ptr + unif_buf_used));
+ unif_buf_used += info->opaque.len + 1;
+ }
+ if (NULL != req->auth.digest.rqp->realm.value.cstr)
+ {
+ info->realm.cstr = (char *) (unif_buf_ptr + unif_buf_used);
+ info->realm.len =
+ get_rq_param_unquoted_copy_z (&(req->auth.digest.rqp->realm),
+ (char *) (unif_buf_ptr + unif_buf_used));
+ unif_buf_used += info->realm.len + 1;
+ }
+
+ mhd_assert (unif_buf_size >= unif_buf_used);
+
+ info->qop = req->auth.digest.rqp->qop;
+
+ if (NULL != req->auth.digest.rqp->cnonce.value.cstr)
+ info->cnonce_len = req->auth.digest.rqp->cnonce.value.len;
+ else
+ info->cnonce_len = 0;
+
+ nc_res = get_rq_nc (req->auth.digest.rqp, &info->nc);
+ if (mhd_GET_RQ_NC_VALID == nc_res)
+ {
+ if (0 == info->nc)
+ info->nc_type = MHD_DIGEST_AUTH_NC_ZERO;
+ else
+ info->nc_type = MHD_DIGEST_AUTH_NC_NUMBER;
+ }
+ else
+ {
+ info->nc = 0;
+ if (mhd_GET_RQ_NC_BROKEN == nc_res)
+ info->nc_type = MHD_DIGEST_AUTH_NC_NONE;
+ else
+ info->nc_type = (enum MHD_DigestAuthNC) nc_res;
+ }
+
+ req->auth.digest.info = info;
+ if (NULL == req->auth.digest.uname)
+ req->auth.digest.uname = (struct MHD_AuthDigestUsernameInfo *) info;
+
+ mhd_assert (uname_type == info->uname_type);
+ mhd_assert (uname_type == req->auth.digest.uname->uname_type);
+
+ if ((MHD_DIGEST_AUTH_UNAME_TYPE_MISSING == uname_type) ||
+ (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_type) ||
+ ((mhd_GET_RQ_NC_BROKEN == nc_res)))
+ return MHD_SC_REQ_AUTH_DATA_BROKEN;
+
+ return MHD_SC_OK;
+}
+
+
+MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) enum MHD_StatusCode
+mhd_request_get_auth_digest_info (
+ struct MHD_Request *restrict req,
+ const struct MHD_AuthDigestInfo **restrict v_auth_digest_info)
+{
+ mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \
+ mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+ mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= \
+ mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+
+ if (MHD_SC_OK != req->auth.digest.parse_result)
+ return req->auth.digest.parse_result;
+
+ if (NULL == req->auth.digest.info)
+ req->auth.digest.parse_result = find_and_parse_auth_digest_info (req);
+
+ if (MHD_SC_OK != req->auth.digest.parse_result)
+ return req->auth.digest.parse_result; /* Failure exit point */
+
+ mhd_assert (NULL != req->auth.digest.info);
+ mhd_assert (NULL != req->auth.digest.uname);
+ *v_auth_digest_info = req->auth.digest.info;
+
+ return MHD_SC_OK; /* Success exit point */
+}
+
+
+/**
+ * Get base hash calculation algorithm from #MHD_DigestAuthAlgo value.
+ * @param algo the MHD_DigestAuthAlgo value
+ * @return the base hash calculation algorithm
+ */
+MHD_static_inline_ enum MHD_DigestBaseAlgo
+get_base_digest_algo (enum MHD_DigestAuthAlgo algo)
+{
+ unsigned int base_algo;
+
+ base_algo =
+ ((unsigned int) algo)
+ & ~((unsigned int)
+ (MHD_DIGEST_AUTH_ALGO_NON_SESSION
+ | MHD_DIGEST_AUTH_ALGO_SESSION));
+ return (enum MHD_DigestBaseAlgo) base_algo;
+}
+
+
+/**
+ * Get digest size in bytes for specified algorithm.
+ *
+ * Internal inline version.
+ * @param algo the algorithm to check
+ * @return the size of the digest (in bytes) or zero if the input value is not
+ * supported/valid
+ */
+MHD_static_inline_ size_t
+digest_get_hash_size (enum MHD_DigestAuthAlgo algo)
+{
+#ifdef MHD_SUPPORT_MD5
+ mhd_assert (MHD_MD5_DIGEST_SIZE == mhd_MD5_DIGEST_SIZE);
+#endif /* MHD_SUPPORT_MD5 */
+#ifdef MHD_SUPPORT_SHA256
+ mhd_assert (MHD_SHA256_DIGEST_SIZE == mhd_SHA256_DIGEST_SIZE);
+#endif /* MHD_SUPPORT_SHA256 */
+#ifdef MHD_SUPPORT_SHA512_256
+ mhd_assert (MHD_SHA512_256_DIGEST_SIZE == mhd_SHA512_256_DIGEST_SIZE);
+#ifdef MHD_SUPPORT_SHA256
+ mhd_assert (mhd_SHA256_DIGEST_SIZE == mhd_SHA512_256_DIGEST_SIZE);
+#endif /* MHD_SUPPORT_SHA256 */
+#endif /* MHD_SUPPORT_SHA512_256 */
+ /* Only one algorithm must be specified */
+ mhd_assert (1 == \
+ (((0 != (algo & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0) \
+ + ((0 != (algo & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0) \
+ + ((0 != (algo & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0)));
+#ifdef MHD_SUPPORT_MD5
+ if (0 != (((unsigned int) algo)
+ & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5)))
+ return MHD_MD5_DIGEST_SIZE;
+ else
+#endif /* MHD_SUPPORT_MD5 */
+#if defined(MHD_SUPPORT_SHA256) && defined(MHD_SUPPORT_SHA512_256)
+ if (0 != (((unsigned int) algo)
+ & ( ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256)
+ | ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA512_256))))
+ return MHD_SHA256_DIGEST_SIZE; /* The same as mhd_SHA512_256_DIGEST_SIZE */
+ else
+#elif defined(MHD_SUPPORT_SHA256)
+ if (0 != (((unsigned int) algo)
+ & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256)))
+ return MHD_SHA256_DIGEST_SIZE;
+ else
+#elif defined(MHD_SUPPORT_SHA512_256)
+ if (0 != (((unsigned int) algo)
+ & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA512_256)))
+ return MHD_SHA512_256_DIGEST_SIZE;
+ else
+#endif /* MHD_SUPPORT_SHA512_256 */
+ (void) 0; /* Unsupported algorithm */
+
+ return 0; /* Wrong input or unsupported algorithm */
+}
+
+
+/**
+ * Get digest size for specified algorithm.
+ *
+ * The size of the digest specifies the size of the userhash, userdigest
+ * and other parameters which size depends on used hash algorithm.
+ * @param algo the algorithm to check
+ * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
+ * #MHD_SHA256_DIGEST_SIZE/MHD_SHA512_256_DIGEST_SIZE)
+ * or zero if the input value is not supported or not valid
+ * @sa #MHD_digest_auth_calc_userdigest()
+ * @sa #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_calc_userhash_hex()
+ * @note Available since #MHD_VERSION 0x00097701
+ * @ingroup authentication
+ */
+MHD_EXTERN_ MHD_FN_CONST_ size_t
+MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo algo)
+{
+ return digest_get_hash_size (algo);
+}
+
+
+/**
+ * The digest calculation structure.
+ */
+struct DigestAlgorithm
+{
+ /**
+ * A context for the digest algorithm, already initialized to be
+ * useful for @e init, @e update and @e digest.
+ */
+ union DigestCtx ctx;
+
+ /**
+ * The hash calculation algorithm.
+ */
+ enum MHD_DigestBaseAlgo algo;
+
+ /**
+ * Buffer for hex-print of the final digest.
+ */
+#ifdef _DEBUG
+ 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 */
+};
+
+
+/**
+ * Return the size of the digest.
+ * @param da the digest calculation structure to identify
+ * @return the size of the digest.
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ unsigned int
+digest_get_size (struct DigestAlgorithm *da)
+{
+ mhd_assert (! da->uninitialised);
+ mhd_assert (da->algo_selected);
+#ifdef MHD_SUPPORT_MD5
+ if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
+ return mhd_MD5_DIGEST_SIZE;
+#endif /* MHD_SUPPORT_MD5 */
+#ifdef MHD_SUPPORT_SHA256
+ if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
+ return mhd_SHA256_DIGEST_SIZE;
+#endif /* MHD_SUPPORT_SHA256 */
+#ifdef MHD_SUPPORT_SHA512_256
+ if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
+ return mhd_SHA512_256_DIGEST_SIZE;
+#endif /* MHD_SUPPORT_SHA512_256 */
+ mhd_UNREACHABLE ();
+ return 0;
+}
+
+
+#if defined(mhd_MD5_HAS_DEINIT) \
+ || defined(mhd_SHA256_HAS_DEINIT) \
+ || defined(mhd_SHA512_256_HAS_DEINIT)
+/**
+ * Indicates presence of digest_deinit() function
+ */
+#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->uninitialised = false;
+ da->algo_selected = false;
+ da->ready_for_hashing = false;
+ da->hashing = false;
+#endif /* _DEBUG */
+ da->algo = MHD_DIGEST_BASE_ALGO_INVALID;
+}
+
+
+/**
+ * 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_deinit (struct DigestAlgorithm *da)
+{
+ 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 */
+#ifdef mhd_SHA512_256_HAS_DEINIT
+ if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
+ mhd_SHA512_256_deinit (&(da->ctx.sha256_ctx));
+ else
+#endif /* mhd_SHA512_256_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_ MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ 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 */
+ switch (algo)
+ {
+ case MHD_DIGEST_BASE_ALGO_MD5:
+#ifdef MHD_SUPPORT_MD5
+ da->algo = MHD_DIGEST_BASE_ALGO_MD5;
+# ifdef _DEBUG
+ da->algo_selected = true;
+# endif
+ mhd_MD5_init_one_time (&(da->ctx.md5_ctx));
+# ifdef _DEBUG
+ da->ready_for_hashing = true;
+# endif
+ return true;
+#endif /* MHD_SUPPORT_MD5 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_SHA256:
+#ifdef MHD_SUPPORT_SHA256
+ da->algo = MHD_DIGEST_BASE_ALGO_SHA256;
+# ifdef _DEBUG
+ da->algo_selected = true;
+# endif
+ mhd_SHA256_init_one_time (&(da->ctx.sha256_ctx));
+# ifdef _DEBUG
+ da->ready_for_hashing = true;
+# endif
+ return true;
+#endif /* MHD_SUPPORT_SHA256 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_SHA512_256:
+#ifdef MHD_SUPPORT_SHA512_256
+ da->algo = MHD_DIGEST_BASE_ALGO_SHA512_256;
+# ifdef _DEBUG
+ da->algo_selected = true;
+# endif
+ mhd_SHA512_256_init_one_time (&(da->ctx.sha512_256_ctx));
+# ifdef _DEBUG
+ da->ready_for_hashing = true;
+# endif
+ return true;
+#endif /* MHD_SUPPORT_SHA512_256 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_INVALID:
+ default:
+ mhd_UNREACHABLE ();
+ break;
+ }
+ da->algo = MHD_DIGEST_BASE_ALGO_INVALID;
+ return false; /* Unsupported or bad algorithm */
+}
+
+
+/**
+ * Hash more data for digest calculation.
+ * @param da the digest calculation
+ * @param size the size of the @a data in bytes
+ * @param data the data to process
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) void
+digest_update (struct DigestAlgorithm *restrict da,
+ size_t size,
+ const void *restrict data)
+{
+ mhd_assert (! da->uninitialised);
+ mhd_assert (da->algo_selected);
+ mhd_assert (da->ready_for_hashing);
+ switch (da->algo)
+ {
+ case MHD_DIGEST_BASE_ALGO_MD5:
+#ifdef MHD_SUPPORT_MD5
+ mhd_MD5_update (&da->ctx.md5_ctx,
+ size,
+ (const uint8_t *) data);
+#else
+ mhd_UNREACHABLE ();
+#endif
+ break;
+ case MHD_DIGEST_BASE_ALGO_SHA256:
+#ifdef MHD_SUPPORT_SHA256
+ mhd_SHA256_update (&da->ctx.sha256_ctx,
+ size,
+ (const uint8_t *) data);
+#else
+ mhd_UNREACHABLE ();
+#endif
+ break;
+ case MHD_DIGEST_BASE_ALGO_SHA512_256:
+#ifdef MHD_SUPPORT_SHA512_256
+ mhd_SHA512_256_update (&da->ctx.sha512_256_ctx,
+ size,
+ (const uint8_t *) data);
+#else
+ mhd_UNREACHABLE ();
+#endif
+ break;
+ case MHD_DIGEST_BASE_ALGO_INVALID:
+ default:
+ mhd_UNREACHABLE ();
+ break;
+ }
+#ifdef _DEBUG
+ da->hashing = true;
+#endif
+}
+
+
+/**
+ * Feed digest calculation with more data from string.
+ * @param da the digest calculation
+ * @param str the zero-terminated string to process
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) void
+digest_update_str (struct DigestAlgorithm *restrict da,
+ const char *restrict str)
+{
+ digest_update (da,
+ strlen (str),
+ (const uint8_t *) str);
+}
+
+
+/**
+ * Feed digest calculation with more data from string.
+ * @param da the digest calculation
+ * @param buf the sized buffer with the data
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) void
+digest_update_cbuf (struct DigestAlgorithm *restrict da,
+ const struct mhd_BufferConst *restrict buf)
+{
+ digest_update (da,
+ buf->size,
+ (const uint8_t *) buf->data);
+}
+
+
+/**
+ * Feed digest calculation with more data from string.
+ * @param da the digest calculation
+ * @param buf the sized buffer with the data
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) void
+digest_update_buf (struct DigestAlgorithm *restrict da,
+ const struct mhd_Buffer *restrict buf)
+{
+ digest_update (da,
+ buf->size,
+ (const uint8_t *) buf->data);
+}
+
+
+/**
+ * Feed digest calculation with single colon ':' character.
+ * @param da the digest calculation
+ * @param str the zero-terminated string to process
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ void
+digest_update_with_colon (struct DigestAlgorithm *da)
+{
+ static const uint8_t colon = (uint8_t) ':';
+ digest_update (da,
+ 1,
+ &colon);
+}
+
+
+/**
+ * Finally calculate hash (the digest).
+ * @param da the digest calculation
+ * @param[out] digest the pointer to the buffer to put calculated digest,
+ * must be at least digest_get_size(da) bytes large
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) void
+digest_calc_hash (struct DigestAlgorithm *da,
+ uint8_t *digest)
+{
+ mhd_assert (! da->uninitialised);
+ mhd_assert (da->algo_selected);
+ mhd_assert (da->ready_for_hashing);
+ switch (da->algo)
+ {
+ case MHD_DIGEST_BASE_ALGO_MD5:
+#ifdef MHD_SUPPORT_MD5
+# 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 /* ! MHD_SUPPORT_MD5 */
+ mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_MD5 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_SHA256:
+#ifdef MHD_SUPPORT_SHA256
+# 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 /* ! MHD_SUPPORT_SHA256 */
+ mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA256 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_SHA512_256:
+#ifdef MHD_SUPPORT_SHA512_256
+#ifdef mhd_SHA512_256_HAS_FINISH
+ mhd_SHA512_256_finish (&da->ctx.sha512_256_ctx, digest);
+#ifdef _DEBUG
+ da->ready_for_hashing = false;
+#endif /* _DEBUG */
+#else /* ! mhd_SHA512_256_HAS_FINISH */
+ mhd_SHA512_256_finish_reset (&da->ctx.sha512_256_ctx, digest);
+#ifdef _DEBUG
+ da->ready_for_hashing = true;
+#endif /* _DEBUG */
+#endif /* ! mhd_SHA512_256_HAS_FINISH */
+#else /* ! MHD_SUPPORT_SHA512_256 */
+ mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA512_256 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_INVALID:
+ default:
+ mhd_UNREACHABLE ();
+ break;
+ }
+#ifdef _DEBUG
+ da->hashing = false;
+#endif /* _DEBUG */
+}
+
+
+/**
+ * Reset the digest calculation structure and prepare for new calculation.
+ *
+ * @param da the structure to reset
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ void
+digest_reset (struct DigestAlgorithm *da)
+{
+ mhd_assert (! da->uninitialised);
+ mhd_assert (da->algo_selected);
+ mhd_assert (! da->hashing);
+ switch (da->algo)
+ {
+ case MHD_DIGEST_BASE_ALGO_MD5:
+#ifdef MHD_SUPPORT_MD5
+# 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 /* ! MHD_SUPPORT_MD5 */
+ mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_MD5 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_SHA256:
+#ifdef MHD_SUPPORT_SHA256
+#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 /* ! MHD_SUPPORT_SHA256 */
+ mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA256 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_SHA512_256:
+#ifdef MHD_SUPPORT_SHA512_256
+# ifdef mhd_SHA512_256_HAS_FINISH
+ mhd_assert (! da->ready_for_hashing);
+# else /* ! mhd_SHA512_256_HAS_FINISH */
+ mhd_assert (da->ready_for_hashing);
+# endif /* ! mhd_SHA512_256_HAS_FINISH */
+ mhd_SHA512_256_reset (&(da->ctx.sha512_256_ctx));
+# ifdef _DEBUG
+ da->ready_for_hashing = true;
+# endif /* _DEBUG */
+#else /* ! MHD_SUPPORT_SHA512_256 */
+ mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA512_256 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_INVALID:
+ default:
+#ifdef _DEBUG
+ da->ready_for_hashing = false;
+#endif
+ mhd_UNREACHABLE ();
+ break;
+ }
+}
+
+
+#if defined(mhd_MD5_HAS_EXT_ERROR) \
+ || defined(mhd_SHA256_HAS_EXT_ERROR) \
+ || defined(mhd_SHA512_256_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
+ || mhd_SHA512_256_HAS_EXT_ERROR*/
+
+#ifdef mhd_DIGEST_HAS_EXT_ERROR
+/**
+ * Get external error state.
+ *
+ * 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,
+ * 'false' otherwise
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ bool
+digest_has_error (struct DigestAlgorithm *da)
+{
+ mhd_assert (! da->uninitialised);
+ mhd_assert (da->algo_selected);
+ switch (da->algo)
+ {
+ case MHD_DIGEST_BASE_ALGO_MD5:
+#ifdef MHD_SUPPORT_MD5
+ return mhd_MD5_has_err (&(da->ctx.md5_ctx));
+#else /* ! MHD_SUPPORT_MD5 */
+ mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_MD5 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_SHA256:
+#ifdef MHD_SUPPORT_SHA256
+ return mhd_SHA256_has_err (&(da->ctx.sha256_ctx));
+#else /* ! MHD_SUPPORT_SHA256 */
+ mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA256 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_SHA512_256:
+#ifdef MHD_SUPPORT_SHA512_256
+ return mhd_SHA512_256_has_err (&(da->ctx.sha512_256_ctx));
+#else /* ! MHD_SUPPORT_SHA512_256 */
+ mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA512_256 */
+ break;
+
+ case MHD_DIGEST_BASE_ALGO_INVALID:
+ default:
+ break;
+ }
+ mhd_UNREACHABLE ();
+ return true;
+}
+
+
+#else /* ! mhd_DIGEST_HAS_EXT_ERROR */
+#define digest_has_error(da) (((void) (da)), ! ! 0)
+#endif /* ! mhd_DIGEST_HAS_EXT_ERROR */
+
+
+/**
+ * Calculate userdigest, return it as binary data.
+ *
+ * It is equal to H(A1) for non-session algorithms.
+ *
+ * MHD internal version.
+ *
+ * @param da the digest algorithm
+ * @param username the username to use
+ * @param username_len the length of the @a username
+ * @param realm the realm to use
+ * @param realm_len the length of the @a realm
+ * @param password the password, must be zero-terminated
+ * @param[out] ha1_bin the output buffer, must have at least
+ * #digest_get_size(da) bytes available
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (2, 3) MHD_FN_PAR_IN_SIZE_ (4, 5)
+MHD_FN_PAR_CSTR_ (6) MHD_FN_PAR_OUT_ (7) void
+calc_userdigest (struct DigestAlgorithm *restrict da,
+ const char *restrict username, const size_t username_len,
+ const char *restrict realm, const size_t realm_len,
+ const char *restrict password,
+ uint8_t *ha1_bin)
+{
+ mhd_assert (! da->uninitialised);
+ mhd_assert (da->algo_selected);
+ mhd_assert (! da->hashing);
+ digest_update (da, username_len, username);
+ digest_update_with_colon (da);
+ digest_update (da, realm_len, realm);
+ digest_update_with_colon (da);
+ digest_update_str (da, password);
+ digest_calc_hash (da, ha1_bin);
+}
+
+
+MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4)
+MHD_FN_PAR_OUT_SIZE_ (6,5) enum MHD_StatusCode
+MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo algo,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT password,
+ size_t bin_buf_size,
+ void *MHD_RESTRICT userdigest_bin)
+{
+ struct DigestAlgorithm da;
+ enum MHD_StatusCode ret;
+ if (! digest_init_one_time (&da, get_base_digest_algo (algo)))
+ return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED;
+
+ if (digest_get_size (&da) > bin_buf_size)
+ ret = MHD_SC_OUT_BUFF_TOO_SMALL;
+ else
+ {
+ calc_userdigest (&da,
+ username,
+ strlen (username),
+ realm,
+ strlen (realm),
+ password,
+ userdigest_bin);
+ ret = digest_has_error (&da) ? MHD_SC_HASH_FAILED : MHD_SC_OK;
+ }
+ digest_deinit (&da);
+
+ return ret;
+}
+
+
+/**
+ * Calculate userhash, return it as binary data.
+ *
+ * MHD internal version.
+ *
+ * @param da the digest algorithm
+ * @param username_len the length of the @a username
+ * @param username the username to use
+ * @param realm_len the length of the @a realm
+ * @param realm the realm to use
+ * @param[out] digest_bin the output buffer, must have at least
+ * #MHD_digest_get_hash_size(algo) bytes available
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) MHD_FN_PAR_OUT_ (6) void
+calc_userhash (struct DigestAlgorithm *da,
+ const size_t username_len,
+ const char *username,
+ const size_t realm_len,
+ const char *realm,
+ uint8_t *digest_bin)
+{
+ mhd_assert (! da->uninitialised);
+ mhd_assert (da->algo_selected);
+ mhd_assert (! da->hashing);
+ digest_update (da, username_len, username);
+ digest_update_with_colon (da);
+ digest_update (da, realm_len, realm);
+ digest_calc_hash (da, digest_bin);
+}
+
+
+MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4) enum MHD_StatusCode
+MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo algo,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT realm,
+ size_t bin_buf_size,
+ void *MHD_RESTRICT userhash_bin)
+{
+ struct DigestAlgorithm da;
+ enum MHD_StatusCode ret;
+
+ if (! digest_init_one_time (&da, get_base_digest_algo (algo)))
+ return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED;
+ if (digest_get_size (&da) > bin_buf_size)
+ ret = MHD_SC_OUT_BUFF_TOO_SMALL;
+ else
+ {
+ calc_userhash (&da,
+ strlen (username),
+ username,
+ strlen (realm),
+ realm,
+ userhash_bin);
+
+ ret = digest_has_error (&da) ? MHD_SC_HASH_FAILED : MHD_SC_OK;
+ }
+ digest_deinit (&da);
+
+ return ret;
+}
+
+
+MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4) enum MHD_StatusCode
+MHD_digest_auth_calc_userhash_hex (
+ enum MHD_DigestAuthAlgo algo,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT realm,
+ size_t hex_buf_size,
+ char userhash_hex[MHD_FN_PAR_DYN_ARR_SIZE_ (hex_buf_size)])
+{
+ uint8_t userhash_bin[mhd_MAX_DIGEST];
+ size_t digest_size;
+ enum MHD_StatusCode res;
+
+ digest_size = digest_get_hash_size (algo);
+ if (digest_size * 2 + 1 > hex_buf_size)
+ return MHD_SC_OUT_BUFF_TOO_SMALL;
+ res = MHD_digest_auth_calc_userhash (algo,
+ username,
+ realm,
+ sizeof(userhash_bin),
+ userhash_bin);
+ if (MHD_SC_OK != res)
+ return res;
+
+ (void) mhd_bin_to_hex_z (userhash_bin,
+ digest_size,
+ userhash_hex);
+ return MHD_SC_OK;
+}
+
+
+/**
+ * Extract timestamp from the given nonce.
+ * @param nonce the nonce to check in binary form
+ * @return 'true' if timestamp was extracted,
+ * 'false' if nonce does not have valid timestamp.
+ */
+MHD_static_inline_ uint_fast32_t
+get_nonce_timestamp (const uint8_t nonce[mhd_AUTH_DIGEST_NONCE_BIN_SIZE])
+{
+ return (uint_fast32_t)
+ mhd_GET_32BIT_LE_UNALIGN (nonce + mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE);
+}
+
+
+/**
+ * The result of nonce-nc map array check.
+ */
+enum mhd_CheckNonceNC
+{
+ /**
+ * The nonce and NC are OK (valid and NC was not used before).
+ */
+ mhd_CHECK_NONCENC_OK = MHD_DAUTH_OK,
+
+ /**
+ * The 'nonce' is too old, has been overwritten with newer 'nonce' in
+ * the same slot or 'nc' value has been used already.
+ * The validity of the 'nonce' was not be checked.
+ */
+ mhd_CHECK_NONCENC_STALE = MHD_DAUTH_NONCE_STALE,
+
+ /**
+ * The 'nonce' is wrong, it was not generated before.
+ */
+ mhd_CHECK_NONCENC_WRONG = MHD_DAUTH_NONCE_WRONG
+};
+
+
+/**
+ * Check nonce-nc map array with the new nonce counter.
+ *
+ * @param daemon the master daemon object
+ * @param noncelen the length of @a nonce, in characters
+ * @param nonce the pointer that referenced hex nonce, does not need to be
+ * zero-terminated
+ * @param nc the nonce counter
+ * @param time_now the current timestamp
+ * @return #MHD_DAUTH_NONCENC_OK if successful,
+ * #MHD_DAUTH_NONCENC_STALE if nonce is stale (or no nonce-nc array
+ * is available),
+ * #MHD_DAUTH_NONCENC_WRONG if nonce was not recodered in nonce-nc map
+ * array, while it should.
+ */
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) enum mhd_CheckNonceNC
+check_nonce_nc (struct MHD_Daemon *restrict d,
+ size_t noncelen,
+ const char *restrict nonce,
+ uint_fast32_t nc,
+ uint_fast32_t time_now)
+{
+ uint8_t nonce_bin[mhd_AUTH_DIGEST_NONCE_BIN_SIZE];
+ struct mhd_DaemonAuthDigestNonceData *nonce_slot;
+ uint_fast32_t valid_time;
+ uint_fast32_t slot_valid_time;
+ enum mhd_CheckNonceNC ret;
+
+ mhd_assert (! mhd_D_HAS_MASTER (d)); /* only master daemon should be used */
+ mhd_assert (0 != noncelen);
+ mhd_assert (0 != nc);
+ if (mhd_AUTH_DIGEST_NONCE_LEN != noncelen)
+ return mhd_CHECK_NONCENC_WRONG;
+
+ if (mhd_AUTH_DIGEST_NONCE_BIN_SIZE !=
+ mhd_hex_to_bin (nonce,
+ mhd_AUTH_DIGEST_NONCE_LEN,
+ nonce_bin))
+ return mhd_CHECK_NONCENC_WRONG;
+
+ if ((NULL != memchr (nonce, 'A', mhd_AUTH_DIGEST_NONCE_LEN))
+ || (NULL != memchr (nonce, 'B', mhd_AUTH_DIGEST_NONCE_LEN))
+ || (NULL != memchr (nonce, 'C', mhd_AUTH_DIGEST_NONCE_LEN))
+ || (NULL != memchr (nonce, 'D', mhd_AUTH_DIGEST_NONCE_LEN))
+ || (NULL != memchr (nonce, 'E', mhd_AUTH_DIGEST_NONCE_LEN))
+ || (NULL != memchr (nonce, 'F', mhd_AUTH_DIGEST_NONCE_LEN)))
+ return mhd_CHECK_NONCENC_WRONG; /* Upper case chars are not produced by MHD */
+
+ valid_time = get_nonce_timestamp (nonce_bin);
+
+ nonce_slot = d->auth_dg.nonces
+ + nonce_to_index (nonce_bin,
+ d->auth_dg.cfg.nonces_num);
+
+ mhd_mutex_lock_chk (&(d->auth_dg.nonces_lock));
+
+ slot_valid_time = nonce_slot->valid_time;
+ if ((0 == memcmp (nonce_slot->nonce,
+ nonce_bin,
+ sizeof(nonce_slot->nonce)))
+ && (slot_valid_time == valid_time))
+ {
+ /* The nonce matches the stored nonce */
+ if (nonce_slot->max_recvd_nc < nc)
+ {
+ /* 'nc' is larger, shift bitmask and bump limit */
+ const uint_fast32_t jump_size =
+ (uint_fast32_t) nc - nonce_slot->max_recvd_nc;
+ if (64 > jump_size)
+ {
+ /* small jump, less than mask width */
+ nonce_slot->nmask <<= jump_size;
+ /* Set bit for the old 'nc' value */
+ nonce_slot->nmask |= (UINT64_C (1) << (jump_size - 1));
+ }
+ else if (64 == jump_size)
+ nonce_slot->nmask = (UINT64_C (1) << 63);
+ else
+ nonce_slot->nmask = 0; /* big jump, unset all bits in the mask */
+ nonce_slot->max_recvd_nc = nc;
+ ret = mhd_CHECK_NONCENC_OK;
+ }
+ else if (nonce_slot->max_recvd_nc == nc)
+ /* 'nc' was already used */
+ ret = mhd_CHECK_NONCENC_STALE;
+ else /* (nonce_slot->max_recvd_nc > nc) */
+ {
+ /* Out-of-order 'nc' value. Check whether was used before */
+ if (64 <= nonce_slot->max_recvd_nc - nc)
+ {
+ if (0 ==
+ ((UINT64_C (1) << (nonce_slot->max_recvd_nc - nc - 1))
+ & nonce_slot->nmask))
+ {
+ /* 'nc' has not been used before. Set the bit. */
+ nonce_slot->nmask |=
+ (UINT64_C (1) << (nonce_slot->max_recvd_nc - nc - 1));
+ ret = mhd_CHECK_NONCENC_OK;
+ }
+ else
+ ret = mhd_CHECK_NONCENC_STALE; /* 'nc' has been used before */
+ }
+ else
+ ret = mhd_CHECK_NONCENC_STALE; /* 'nc' is too old (more than 64 value before) */
+ }
+ }
+ else
+ {
+ /* The nonce does not match the stored nonce */
+ if (((valid_time - slot_valid_time) & UINT32_C (0xFFFFFFFF)) <=
+ ((slot_valid_time - valid_time) & UINT32_C (0xFFFFFFFF)))
+ {
+ /* The stored nonce was generated before the checked nonce */
+ ret = mhd_CHECK_NONCENC_WRONG;
+ }
+ else
+ {
+ /* The stored nonce was generated after the checked nonce */
+ const uint_fast32_t nonce_gen_time =
+ ((valid_time - d->auth_dg.cfg.nonce_tmout) & UINT32_C (0xFFFFFFFF));
+ if (((time_now - nonce_gen_time) & UINT32_C (0xFFFFFFFF)) <
+ ((nonce_gen_time - time_now) & UINT32_C (0xFFFFFFFF)))
+ ret = mhd_CHECK_NONCENC_WRONG; /* The nonce is generated in "future" */
+ else
+ /* Probably the nonce has been overwritten with a newer nonce */
+ ret = mhd_CHECK_NONCENC_STALE;
+ }
+ }
+
+ mhd_mutex_unlock_chk (&(d->auth_dg.nonces_lock));
+
+ return ret;
+}
+
+
+struct test_header_param
+{
+ struct MHD_Request *request;
+ size_t num_get_params;
+};
+
+/**
+ * Test if the given key-value pair is in the headers for the
+ * given request.
+ *
+ * @param cls the test context
+ * @param name the name of the key
+ * @param value the value of the key
+ * @return 'true' if the key-value pair is in the headers,
+ * 'false' if not
+ */
+static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) bool
+test_header (void *restrict cls,
+ const struct MHD_String *restrict name,
+ const struct MHD_StringNullable *restrict value)
+{
+ struct test_header_param *const param = (struct test_header_param *) cls;
+ struct MHD_Request *req = param->request;
+ struct mhd_RequestField *pos;
+ size_t i;
+
+ param->num_get_params++;
+ i = 0;
+ for (pos = mhd_DLINKEDL_GET_FIRST (req, fields);
+ NULL != pos;
+ pos = mhd_DLINKEDL_GET_NEXT (pos, fields))
+ {
+ if (MHD_VK_GET_ARGUMENT != pos->field.kind)
+ continue;
+ if (++i == param->num_get_params)
+ {
+ if (name->len != pos->field.nv.name.len)
+ return false;
+ if (value->len != pos->field.nv.value.len)
+ return false;
+ if (0 != name->len)
+ {
+ mhd_assert (NULL != name->cstr);
+ mhd_assert (NULL != pos->field.nv.name.cstr);
+ if (0 != memcmp (name->cstr,
+ pos->field.nv.name.cstr,
+ name->len))
+ return false;
+ }
+ if (0 != value->len)
+ {
+ mhd_assert (NULL != value->cstr);
+ mhd_assert (NULL != pos->field.nv.value.cstr);
+ if (0 != memcmp (value->cstr,
+ pos->field.nv.value.cstr,
+ value->len))
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * Check that the arguments given by the client as part
+ * of the authentication header match the arguments we
+ * got as part of the HTTP request URI.
+ *
+ * @param req the request with get arguments to compare against
+ * @param args the copy of argument URI string (after "?" in URI), will be
+ * modified by this function
+ * @return 'true' if the arguments match,
+ * 'false' if not
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (3)
+MHD_FN_PAR_INOUT_SIZE_ (3, 2) bool
+check_argument_match (struct MHD_Request *restrict req,
+ size_t args_len,
+ char *restrict args)
+{
+ struct mhd_RequestField *pos;
+ struct test_header_param param;
+
+ param.request = req;
+ param.num_get_params = 0;
+ if (! mhd_parse_get_args (args_len,
+ args,
+ &test_header,
+ ¶m))
+ return false;
+
+ /* Check that the number of arguments matches */
+ for (pos = mhd_DLINKEDL_GET_FIRST (req, fields);
+ NULL != pos;
+ pos = mhd_DLINKEDL_GET_NEXT (pos, fields))
+ {
+ if (MHD_VK_GET_ARGUMENT != pos->field.kind)
+ continue;
+ param.num_get_params--;
+ }
+
+ if (0 != param.num_get_params)
+ return false; /* argument count mismatch */
+
+ return true;
+}
+
+
+/**
+ * Check that the URI provided by the client as part
+ * of the authentication header match the real HTTP request URI.
+ *
+ * @param req the request to compare URI
+ * @param uri the copy of URI in the authentication header, should point to
+ * modifiable buffer at least @a uri_len + 1 characters long,
+ * will be modified by this function, not valid upon return
+ * @param uri_len the length of the @a uri string in characters
+ * @return boolean true if the URIs match,
+ * boolean false if not
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (3) bool
+check_uri_match (struct MHD_Request *restrict req,
+ const size_t uri_len,
+ char *restrict uri)
+{
+ char *qmark;
+ char *args;
+ size_t url_len; /* The part before '?' char */
+ size_t args_len;
+
+ if (uri_len != req->req_target_len)
+ return false;
+
+ uri[uri_len] = 0;
+ qmark = memchr (uri,
+ '?',
+ uri_len);
+ if (NULL != qmark)
+ {
+ *qmark = 0;
+ url_len = (size_t) (qmark - uri);
+ }
+ else
+ url_len = uri_len;
+
+ /* Need to unescape URI before comparing with req->url */
+ url_len = mhd_str_pct_decode_lenient_n (uri,
+ url_len,
+ uri,
+ url_len,
+ NULL);
+ if ((url_len != req->url_len) ||
+ (0 != memcmp (uri,
+ req->url,
+ url_len)))
+ return false;
+
+ args = (NULL != qmark) ? (qmark + 1) : uri + uri_len;
+ args_len = (size_t) (uri + uri_len - args);
+
+ if (! check_argument_match (req,
+ args_len,
+ args))
+ return false;
+
+ return true;
+}
+
+
+/**
+ * The size of the unquoting buffer in stack
+ */
+#define mhd_STATIC_UNQ_BUFFER_SIZE 128
+
+
+/**
+ * Get the pointer to buffer with required size
+ * @param tmp1 the first buffer with fixed size
+ * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer
+ * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
+ * @param required_size the required size in buffer
+ * @return the pointer to the buffer or NULL if failed to allocate buffer with
+ * requested size
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) char *
+get_buffer_for_size (char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE],
+ char **restrict ptmp2,
+ size_t *restrict ptmp2_size,
+ size_t required_size)
+{
+ mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2));
+ mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size));
+ mhd_assert ((0 == *ptmp2_size) || \
+ (mhd_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size));
+
+ if (required_size <= mhd_STATIC_UNQ_BUFFER_SIZE)
+ return tmp1;
+
+ if (required_size <= *ptmp2_size)
+ return *ptmp2;
+
+ if (required_size > mhd_AUTH_DIGEST_MAX_PARAM_SIZE)
+ return NULL;
+ if (NULL != *ptmp2)
+ free (*ptmp2);
+ *ptmp2 = (char *) malloc (required_size);
+ if (NULL == *ptmp2)
+ *ptmp2_size = 0;
+ else
+ *ptmp2_size = required_size;
+ return *ptmp2;
+}
+
+
+/**
+ * The result of parameter unquoting
+ */
+enum mhd_GetUnqResult
+{
+ mhd_UNQ_OK = MHD_DAUTH_OK, /**< Got unquoted string */
+ mhd_UNQ_TOO_LARGE = MHD_DAUTH_TOO_LARGE, /**< The string is too large to unquote */
+ mhd_UNQ_OUT_OF_MEM = MHD_DAUTH_ERROR /**< Out of memory error */
+};
+
+/**
+ * Get Digest authorisation parameter as unquoted string.
+ * @param param the parameter to process
+ * @param[in,out] tmp1 the small buffer in stack
+ * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer
+ * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
+ * @param[out] unquoted the pointer to store the result, NOT zero terminated
+ * @return enum code indicating result of the process
+ */
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_INOUT_ (3) MHD_FN_PAR_INOUT_ (4)
+MHD_FN_PAR_OUT_ (5) enum mhd_GetUnqResult
+get_unquoted_param (const struct mhd_RqDAuthParam *param,
+ char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE],
+ char **restrict ptmp2,
+ size_t *restrict ptmp2_size,
+ struct mhd_BufferConst *restrict unquoted)
+{
+ char *str;
+ size_t len;
+ mhd_assert (NULL != param->value.cstr);
+ mhd_assert (0 != param->value.len);
+
+ if (! param->quoted)
+ {
+ unquoted->data = param->value.cstr;
+ unquoted->size = param->value.len;
+ return mhd_UNQ_OK;
+ }
+ /* The value is present and is quoted, needs to be copied and unquoted */
+ str = get_buffer_for_size (tmp1,
+ ptmp2,
+ ptmp2_size,
+ param->value.len);
+ if (NULL == str)
+ return (param->value.len > mhd_AUTH_DIGEST_MAX_PARAM_SIZE) ?
+ mhd_UNQ_TOO_LARGE : mhd_UNQ_OUT_OF_MEM;
+
+ len = mhd_str_unquote (param->value.cstr,
+ param->value.len,
+ str);
+ unquoted->data = str;
+ unquoted->size = len;
+ mhd_assert (0 != unquoted->size);
+ mhd_assert (unquoted->size < param->value.len);
+ return mhd_UNQ_OK;
+}
+
+
+/**
+ * Get copy of Digest authorisation parameter as unquoted string.
+ * @param param the parameter to process
+ * @param[in,out] tmp1 the small buffer in stack
+ * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer
+ * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
+ * @param[out] unquoted the pointer to store the result, NOT zero terminated,
+ * but with enough space to zero-terminate
+ * @return enum code indicating result of the process
+ */
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_INOUT_ (3) MHD_FN_PAR_INOUT_ (4)
+MHD_FN_PAR_OUT_ (5) enum mhd_GetUnqResult
+get_unquoted_param_copy (const struct mhd_RqDAuthParam *param,
+ char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE],
+ char **restrict ptmp2,
+ size_t *restrict ptmp2_size,
+ struct mhd_Buffer *restrict unquoted)
+{
+ mhd_assert (NULL != param->value.cstr);
+ mhd_assert (0 != param->value.len);
+
+ /* The value is present and is quoted, needs to be copied and unquoted */
+ /* Allocate buffer with one more additional byte for zero-termination */
+ unquoted->data =
+ get_buffer_for_size (tmp1,
+ ptmp2,
+ ptmp2_size,
+ param->value.len + 1);
+
+ if (NULL == unquoted->data)
+ return (param->value.len + 1 > mhd_AUTH_DIGEST_MAX_PARAM_SIZE) ?
+ mhd_UNQ_TOO_LARGE : mhd_UNQ_OUT_OF_MEM;
+
+ if (! param->quoted)
+ {
+ memcpy (unquoted->data,
+ param->value.cstr,
+ param->value.len);
+ unquoted->size = param->value.len;
+ return mhd_UNQ_OK;
+ }
+
+ unquoted->size =
+ mhd_str_unquote (param->value.cstr,
+ param->value.len,
+ unquoted->data);
+ mhd_assert (0 != unquoted->size);
+ mhd_assert (unquoted->size < param->value.len);
+ return mhd_UNQ_OK;
+}
+
+
+/**
+ * Check whether Digest Auth request parameter is equal to given string
+ * @param param the parameter to check
+ * @param str_len the length of the @a str
+ * @param str the string to compare with, does not need to be zero-terminated
+ * @return true is parameter is equal to the given string,
+ * false otherwise
+ */
+MHD_static_inline_ MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3,2) bool
+is_param_equal (const struct mhd_RqDAuthParam *restrict param,
+ const size_t str_len,
+ const char *restrict str)
+{
+ mhd_assert (NULL != param->value.cstr);
+ mhd_assert (0 != param->value.len);
+ if (param->quoted)
+ return mhd_str_equal_quoted_bin_n (param->value.cstr,
+ param->value.len,
+ str,
+ str_len);
+ return (str_len == param->value.len) &&
+ (0 == memcmp (str, param->value.cstr, str_len));
+}
+
+
+/**
+ * Check whether Digest Auth request parameter is caseless equal to given string
+ * @param param the parameter to check
+ * @param str_len the length of the @a str
+ * @param str the string to compare with, does not need to be zero-terminated
+ * @return true is parameter is caseless equal to the given string,
+ * false otherwise
+ */
+MHD_static_inline_ MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3,2) bool
+is_param_equal_caseless (const struct mhd_RqDAuthParam *restrict param,
+ const size_t str_len,
+ const char *restrict str)
+{
+ mhd_assert (NULL != param->value.cstr);
+ mhd_assert (0 != param->value.len);
+ if (param->quoted)
+ return mhd_str_equal_caseless_quoted_bin_n (param->value.cstr,
+ param->value.len,
+ str,
+ str_len);
+ return (str_len == param->value.len) &&
+ (mhd_str_equal_caseless_bin_n (str, param->value.cstr, str_len));
+}
+
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not supported in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request new nonce must be
+ * generated and client repeat all requests twice (the first time to get a new
+ * nonce and the second time to perform an authorised request).
+ *
+ * @param req the request handle
+ * @param realm the realm for authorization of the client
+ * @param username the username to be authenticated, must be in clear text
+ * even if userhash is used by the client
+ * @param password the password used in the authentication,
+ * must be NULL if @a userdigest is not NULL
+ * @param userdigest the precalculated binary hash of the string
+ * "username:realm:password",
+ * must be NULL if @a password is not NULL
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ * exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ * returned;
+ * unlike #digest_auth_check_all() zero is treated as "no limit"
+ * @param mqop the QOP to use
+ * @param malgo digest algorithms allowed to use, fail if algorithm specified
+ * by the client is not allowed by this parameter
+ * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer,
+ * to be freed if not NULL upon return
+ * @return #MHD_DAUTH_OK if authenticated,
+ * error code otherwise.
+ * @ingroup authentication
+ */
+static MHD_FN_MUST_CHECK_RESULT_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3)
+MHD_FN_PAR_CSTR_ (4)
+enum MHD_DigestAuthResult
+digest_auth_check_all_inner (struct MHD_Request *restrict req,
+ const char *restrict realm,
+ const char *restrict username,
+ const char *restrict password,
+ const uint8_t *restrict userdigest,
+ uint_fast32_t max_nc,
+ enum MHD_DigestAuthMultiQOP mqop,
+ enum MHD_DigestAuthMultiAlgo malgo,
+ char **pbuf,
+ struct DigestAlgorithm *da)
+{
+ struct MHD_Daemon *const daemon =
+ mhd_daemon_get_master_daemon (
+ mhd_CNTNR_PTR (req, struct MHD_Connection, rq)->daemon);
+ enum MHD_DigestAuthAlgo c_algo; /**< Client's algorithm */
+ enum MHD_DigestAuthQOP c_qop; /**< Client's QOP */
+ unsigned int digest_size;
+ uint8_t hash1_bin[mhd_MAX_DIGEST];
+ uint8_t hash2_bin[mhd_MAX_DIGEST];
+ uint_fast32_t nc;
+ const struct mhd_AuthDigesReqParams *restrict params;
+ /**
+ * Temporal buffer in stack for unquoting and other needs
+ */
+ char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE];
+ char **const ptmp2 = pbuf; /**< Temporal malloc'ed buffer for unquoting */
+ size_t tmp2_size; /**< The size of @a tmp2 buffer */
+ struct mhd_BufferConst unquoted;
+ struct mhd_Buffer unq_copy;
+ enum mhd_GetUnqResult unq_res;
+ size_t username_len;
+ size_t realm_len;
+
+ mhd_assert ((NULL == password) != (NULL == userdigest));
+
+ tmp2_size = 0;
+
+ if (1)
+ {
+ enum MHD_StatusCode res;
+
+ res = get_rq_auth_digest_params (req);
+ if (MHD_SC_OK != res)
+ {
+ if (MHD_SC_AUTH_ABSENT == res)
+ return MHD_DAUTH_HEADER_MISSING;
+ else if (MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA == res)
+ return MHD_DAUTH_ERROR;
+ else if (MHD_SC_REQ_AUTH_DATA_BROKEN == res)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else
+ mhd_UNREACHABLE ();
+ }
+ params = req->auth.digest.rqp;
+ }
+ mhd_assert (NULL != params);
+
+ /* ** Initial parameters checks and setup ** */
+ /* Get client's algorithm */
+ c_algo = params->algo;
+ /* Check whether client's algorithm is allowed by function parameter */
+ if (((unsigned int) c_algo) !=
+ (((unsigned int) c_algo) & ((unsigned int) malgo)))
+ return MHD_DAUTH_WRONG_ALGO;
+ /* Check whether client's algorithm is supported */
+ if (0 != (((unsigned int) c_algo) & MHD_DIGEST_AUTH_ALGO_SESSION))
+ return MHD_DAUTH_UNSUPPORTED_ALGO;
+#ifndef MHD_SUPPORT_MD5
+ if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5))
+ return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_MD5 */
+#ifndef MHD_SUPPORT_SHA256
+ if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA256))
+ return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_SHA256 */
+#ifndef MHD_SUPPORT_SHA512_256
+ if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA512_256))
+ return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_SHA512_256 */
+ if (! digest_init_one_time (da, get_base_digest_algo (c_algo)))
+ mhd_UNREACHABLE ();
+ /* Check 'mqop' value */
+ c_qop = params->qop;
+ /* Check whether client's QOP is allowed by function parameter */
+ if (((unsigned int) c_qop) !=
+ (((unsigned int) c_qop) & ((unsigned int) mqop)))
+ return MHD_DAUTH_WRONG_QOP;
+ if (0 != (((unsigned int) c_qop) & MHD_DIGEST_AUTH_QOP_AUTH_INT))
+ return MHD_DAUTH_UNSUPPORTED_QOP;
+
+ digest_size = digest_get_size (da);
+
+ /* ** A quick check for presence of all required parameters ** */
+
+ if ((NULL == params->username.value.cstr) &&
+ (NULL == params->username_ext.value.cstr))
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if ((NULL != params->username.value.cstr) &&
+ (NULL != params->username_ext.value.cstr))
+ return MHD_DAUTH_HEADER_BROKEN; /* Parameters cannot be used together */
+ else if ((NULL != params->username_ext.value.cstr) &&
+ (mhd_DAUTH_EXT_PARAM_MIN_LEN > params->username_ext.value.len))
+ return MHD_DAUTH_HEADER_BROKEN; /* Broken extended notation */
+ else if (params->userhash && (NULL == params->username.value.cstr))
+ return MHD_DAUTH_HEADER_BROKEN; /* Userhash cannot be used with extended notation */
+ else if (params->userhash && (digest_size * 2 > params->username.value.len))
+ return MHD_DAUTH_WRONG_USERNAME; /* Too few chars for correct userhash */
+ else if (params->userhash && (digest_size * 4 < params->username.value.len))
+ return MHD_DAUTH_WRONG_USERNAME; /* Too many chars for correct userhash */
+
+ if (NULL == params->realm.value.cstr)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (((NULL == userdigest) || params->userhash) &&
+ (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len))
+ return MHD_DAUTH_TOO_LARGE; /* Realm is too large and should be used in hash calculations */
+
+ if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
+ {
+ if (NULL == params->nc.value.cstr)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (0 == params->nc.value.len)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (4 * 8 < params->nc.value.len) /* Four times more than needed */
+ return MHD_DAUTH_HEADER_BROKEN;
+
+ if (NULL == params->cnonce.value.cstr)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (0 == params->cnonce.value.len)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len)
+ return MHD_DAUTH_TOO_LARGE;
+ }
+
+ /* The QOP parameter was checked already */
+
+ if (NULL == params->uri.value.cstr)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (0 == params->uri.value.len)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->uri.value.len)
+ return MHD_DAUTH_TOO_LARGE;
+
+ if (NULL == params->nonce.value.cstr)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (0 == params->nonce.value.len)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (mhd_AUTH_DIGEST_NONCE_LEN * 2 < params->nonce.value.len)
+ return MHD_DAUTH_NONCE_WRONG;
+
+ if (NULL == params->response.value.cstr)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (0 == params->response.value.len)
+ return MHD_DAUTH_HEADER_BROKEN;
+ else if (digest_size * 4 < params->response.value.len)
+ return MHD_DAUTH_RESPONSE_WRONG;
+
+ /* ** Check simple parameters match ** */
+
+ /* Check 'algorithm' */
+ /* The 'algorithm' was checked at the start of the function */
+ /* 'algorithm' valid */
+
+ /* Check 'qop' */
+ /* The 'qop' was checked at the start of the function */
+ /* 'qop' valid */
+
+ /* Check 'realm' */
+ realm_len = strlen (realm);
+ if (! is_param_equal (¶ms->realm,
+ realm_len,
+ realm))
+ return MHD_DAUTH_WRONG_REALM;
+ /* 'realm' valid */
+
+ /* Check 'username' */
+ username_len = strlen (username);
+ if (! params->userhash)
+ {
+ if (NULL != params->username.value.cstr)
+ { /* Username in standard notation */
+ if (! is_param_equal (¶ms->username, username_len, username))
+ return MHD_DAUTH_WRONG_USERNAME;
+ }
+ else
+ { /* Username in extended notation */
+ char *r_uname;
+ size_t buf_size = params->username_ext.value.len;
+ ssize_t res;
+
+ mhd_assert (NULL != params->username_ext.value.cstr);
+ mhd_assert (mhd_DAUTH_EXT_PARAM_MIN_LEN <= buf_size); /* It was checked already */
+ buf_size += 1; /* For zero-termination */
+ buf_size -= mhd_DAUTH_EXT_PARAM_MIN_LEN;
+ r_uname = get_buffer_for_size (tmp1, ptmp2, &tmp2_size, buf_size);
+ if (NULL == r_uname)
+ return (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < buf_size) ?
+ MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR;
+ res = get_rq_extended_uname_copy_z (params->username_ext.value.cstr,
+ params->username_ext.value.len,
+ r_uname, buf_size);
+ if (0 > res)
+ return MHD_DAUTH_HEADER_BROKEN; /* Broken extended notation */
+ if ((username_len != (size_t) res) ||
+ (0 != memcmp (username, r_uname, username_len)))
+ return MHD_DAUTH_WRONG_USERNAME;
+ }
+ }
+ else
+ { /* Userhash */
+ mhd_assert (NULL != params->username.value.cstr);
+ calc_userhash (da,
+ username_len,
+ username,
+ realm_len,
+ realm,
+ hash1_bin);
+ if (digest_has_error (da))
+ return MHD_DAUTH_ERROR;
+ mhd_assert (sizeof (tmp1) >= (2 * digest_size));
+ mhd_bin_to_hex (hash1_bin, digest_size, tmp1);
+ if (! is_param_equal_caseless (¶ms->username, 2 * digest_size, tmp1))
+ 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 */
+
+ /* ** Do basic nonce and nonce-counter checks (size, timestamp) ** */
+
+ /* Get 'nc' digital value */
+ nc = 0;
+ switch (get_rq_nc (params,
+ &nc))
+ {
+ case mhd_GET_RQ_NC_NONE:
+ if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
+ return MHD_DAUTH_HEADER_BROKEN;
+ nc = 1; /* Force 'nc' value */
+ break;
+ case mhd_GET_RQ_NC_VALID:
+ if (MHD_DIGEST_AUTH_QOP_NONE == c_qop)
+ return MHD_DAUTH_HEADER_BROKEN;
+ break;
+ case mhd_GET_RQ_NC_TOO_LONG:
+ case mhd_GET_RQ_NC_TOO_LARGE:
+ return MHD_DAUTH_NONCE_STALE;
+ break;
+ case mhd_GET_RQ_NC_BROKEN:
+ return MHD_DAUTH_HEADER_BROKEN;
+ break;
+ default:
+ mhd_UNREACHABLE ();
+ break;
+ }
+ if (0 == nc)
+ return MHD_DAUTH_HEADER_BROKEN;
+ if (0 == max_nc)
+ max_nc = daemon->auth_dg.cfg.def_max_nc;
+ if (max_nc < nc)
+ return MHD_DAUTH_NONCE_STALE; /* Too large 'nc' value */
+ /* Got 'nc' digital value */
+
+ /* Get 'nonce' with basic checks */
+ unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size,
+ &unquoted);
+ if (mhd_UNQ_TOO_LARGE == unq_res)
+ return MHD_DAUTH_TOO_LARGE;
+ if (mhd_UNQ_OUT_OF_MEM == unq_res)
+ return MHD_DAUTH_ERROR;
+
+
+ switch (check_nonce_nc (daemon,
+ unquoted.size,
+ unquoted.data,
+ nc,
+ (uint_fast32_t)
+ ((mhd_monotonic_msec_counter () / 1000)
+ & UINT32_C (0xFFFFFFFF))))
+ {
+ case mhd_CHECK_NONCENC_OK:
+ break;
+ case mhd_CHECK_NONCENC_STALE:
+ return MHD_DAUTH_NONCE_STALE;
+ case mhd_CHECK_NONCENC_WRONG:
+ return MHD_DAUTH_NONCE_WRONG;
+ default:
+ mhd_UNREACHABLE ();
+ break;
+ }
+ /* The nonce was generated by MHD, is not stale and nonce-nc combination has
+ not been used before */
+
+ /* ** Build H(A2) and check URI match in the header and in the request ** */
+
+ /* Get 'uri' */
+ mhd_assert (! da->hashing);
+ digest_update (da, req->method.len, req->method.cstr);
+ digest_update_with_colon (da);
+#if 0
+ /* TODO: add support for "auth-int" */
+ digest_update_str (da, hentity);
+ digest_update_with_colon (da);
+#endif
+ unq_res = get_unquoted_param_copy (¶ms->uri, tmp1, ptmp2, &tmp2_size,
+ &unq_copy);
+ if (mhd_UNQ_TOO_LARGE == unq_res)
+ return MHD_DAUTH_TOO_LARGE;
+ if (mhd_UNQ_OUT_OF_MEM == unq_res)
+ return MHD_DAUTH_ERROR;
+
+ digest_update_buf (da, &unq_copy);
+ /* The next check will modify copied URI string */
+ if (! check_uri_match (req, unq_copy.size, unq_copy.data))
+ return MHD_DAUTH_WRONG_URI;
+ 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)
+ {
+ 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_has_error (da))
+ return MHD_DAUTH_ERROR;
+#endif /* mhd_DIGEST_HAS_EXT_ERROR */
+ /* Got H(A1) */
+
+ /* ** Check 'response' ** */
+
+ 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, digest_size * 2, (const uint8_t *) tmp1);
+
+ /* H(A1) is not needed anymore, reuse the buffer.
+ * Use hash1_bin for the client's 'response' decoded to binary form. */
+ unq_res = get_unquoted_param (¶ms->response, tmp1, ptmp2, &tmp2_size,
+ &unquoted);
+ if (mhd_UNQ_TOO_LARGE == unq_res)
+ return MHD_DAUTH_TOO_LARGE;
+ if (mhd_UNQ_OUT_OF_MEM == unq_res)
+ return MHD_DAUTH_ERROR;
+ if (digest_size != mhd_hex_to_bin (unquoted.data, unquoted.size, hash1_bin))
+ return MHD_DAUTH_RESPONSE_WRONG;
+
+ /* Update digest with ':' */
+ digest_update_with_colon (da);
+ /* Update digest with 'nonce' text value */
+ unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size,
+ &unquoted);
+ if (mhd_UNQ_TOO_LARGE == unq_res)
+ return MHD_DAUTH_TOO_LARGE;
+ if (mhd_UNQ_OUT_OF_MEM == unq_res)
+ return MHD_DAUTH_ERROR;
+ digest_update_cbuf (da, &unquoted);
+ /* Update digest with ':' */
+ digest_update_with_colon (da);
+ if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
+ {
+ /* Update digest with 'nc' text value */
+ unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size,
+ &unquoted);
+ if (mhd_UNQ_TOO_LARGE == unq_res)
+ return MHD_DAUTH_TOO_LARGE;
+ if (mhd_UNQ_OUT_OF_MEM == unq_res)
+ return MHD_DAUTH_ERROR;
+ digest_update_cbuf (da, &unquoted);
+ /* Update digest with ':' */
+ digest_update_with_colon (da);
+ /* Update digest with 'cnonce' value */
+ unq_res = get_unquoted_param (¶ms->cnonce, tmp1, ptmp2, &tmp2_size,
+ &unquoted);
+ if (mhd_UNQ_TOO_LARGE == unq_res)
+ return MHD_DAUTH_TOO_LARGE;
+ if (mhd_UNQ_OUT_OF_MEM == unq_res)
+ return MHD_DAUTH_ERROR;
+ digest_update_cbuf (da, &unquoted);
+ /* Update digest with ':' */
+ digest_update_with_colon (da);
+ /* Update digest with 'qop' value */
+ unq_res = get_unquoted_param (¶ms->qop_raw, tmp1, ptmp2, &tmp2_size,
+ &unquoted);
+ if (mhd_UNQ_TOO_LARGE == unq_res)
+ return MHD_DAUTH_TOO_LARGE;
+ if (mhd_UNQ_OUT_OF_MEM == unq_res)
+ return MHD_DAUTH_ERROR;
+ digest_update_cbuf (da, &unquoted);
+ /* Update digest with ':' */
+ digest_update_with_colon (da);
+ }
+ /* Update digest with H(A2) */
+ mhd_bin_to_hex (hash2_bin, digest_size, tmp1);
+ digest_update (da, digest_size * 2, (const uint8_t *) tmp1);
+
+ /* 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);
+#ifdef mhd_DIGEST_HAS_EXT_ERROR
+ if (digest_has_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;
+
+ return MHD_DAUTH_OK;
+}
+
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not supported in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request new nonce must be
+ * generated and client repeat all requests twice (the first time to get a new
+ * nonce and the second time to perform an authorised request).
+ *
+ * @param req the request handle
+ * @param realm the realm for authorization of the client
+ * @param username the username to be authenticated, must be in clear text
+ * even if userhash is used by the client
+ * @param password the password used in the authentication,
+ * must be NULL if @a userdigest is not NULL
+ * @param userdigest the precalculated binary hash of the string
+ * "username:realm:password",
+ * must be NULL if @a password is not NULL
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ * exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ * returned;
+ * if set to zero then daemon's default value is used
+ * @param mqop the QOP to use
+ * @param malgo digest algorithms allowed to use, fail if algorithm specified
+ * by the client is not allowed by this parameter
+ * @return #MHD_DAUTH_OK if authenticated,
+ * error code otherwise.
+ * @ingroup authentication
+ */
+static enum MHD_DigestAuthResult
+digest_auth_check_all (struct MHD_Request *restrict req,
+ const char *restrict realm,
+ const char *restrict username,
+ const char *restrict password,
+ const uint8_t *restrict userdigest,
+ uint_fast32_t max_nc,
+ enum MHD_DigestAuthMultiQOP mqop,
+ enum MHD_DigestAuthMultiAlgo malgo)
+{
+ enum MHD_DigestAuthResult res;
+ char *buf;
+ struct DigestAlgorithm da;
+
+ buf = NULL;
+ digest_setup_zero (&da);
+ res = digest_auth_check_all_inner (req,
+ realm,
+ username,
+ password,
+ userdigest,
+ max_nc,
+ mqop,
+ malgo,
+ &buf,
+ &da);
+ digest_deinit (&da);
+ if (NULL != buf)
+ free (buf);
+
+ return res;
+}
+
+
+/**
+ * Authenticates the authorization header sent by the client.
+ *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not supported in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request a new nonce must
+ * be generated and client repeats all requests twice (first time to get a new
+ * nonce and second time to perform an authorised request).
+ *
+ * @param request the request
+ * @param realm the realm for authorization of the client
+ * @param username the username to be authenticated, must be in clear text
+ * even if userhash is used by the client
+ * @param password the password matching the @a username (and the @a realm)
+ * @param nonce_timeout the period of seconds since nonce generation, when
+ * the nonce is recognised as valid and not stale;
+ * if zero is specified then daemon default value is used.
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ * exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ * returned;
+ * if zero is specified then daemon default value is used.
+ * @param mqop the QOP to use
+ * @param malgo digest algorithms allowed to use, fail if algorithm used
+ * by the client is not allowed by this parameter
+ * @return #MHD_DAUTH_OK if authenticated,
+ * the error code otherwise
+ * @ingroup authentication
+ */
+MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4)
+enum MHD_DigestAuthResult
+MHD_digest_auth_check (struct MHD_Request *MHD_RESTRICT request,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT username,
+ const char *MHD_RESTRICT password,
+ uint_fast32_t max_nc,
+ enum MHD_DigestAuthMultiQOP mqop,
+ enum MHD_DigestAuthMultiAlgo malgo)
+{
+ return digest_auth_check_all (request,
+ realm,
+ username,
+ password,
+ NULL,
+ max_nc,
+ mqop,
+ malgo);
+}
+
+
+MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_CSTR_ (3)
+MHD_FN_PAR_IN_SIZE_ (5, 4) enum MHD_DigestAuthResult
+MHD_digest_auth_check_digest (struct MHD_Request *MHD_RESTRICT request,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT username,
+ size_t userdigest_size,
+ const void *MHD_RESTRICT userdigest,
+ uint_fast32_t max_nc,
+ enum MHD_DigestAuthMultiQOP mqop,
+ enum MHD_DigestAuthMultiAlgo malgo)
+{
+ if (1 != (((0 != (malgo & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0)
+ + ((0 != (malgo & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0)
+ + ((0 != (malgo & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0)))
+ return MHD_DAUTH_UNSUPPORTED_ALGO;
+
+#ifndef MHD_SUPPORT_MD5
+ if (0 != (((unsigned int) malgo) & MHD_DIGEST_BASE_ALGO_MD5))
+ return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_MD5 */
+#ifndef MHD_SUPPORT_SHA256
+ if (0 != (((unsigned int) malgo) & MHD_DIGEST_BASE_ALGO_SHA256))
+ return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_SHA256 */
+#ifndef MHD_SUPPORT_SHA512_256
+ if (0 != (((unsigned int) malgo) & MHD_DIGEST_BASE_ALGO_SHA512_256))
+ return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_SHA512_256 */
+
+ if (digest_get_hash_size ((enum MHD_DigestAuthAlgo) malgo) !=
+ userdigest_size)
+ return MHD_DAUTH_INVALID_USERDIGEST_SIZE;
+
+ return digest_auth_check_all (request,
+ realm,
+ username,
+ NULL,
+ (const uint8_t *) userdigest,
+ max_nc,
+ mqop,
+ malgo);
+}
diff --git a/src/mhd2/auth_digest.h b/src/mhd2/auth_digest.h
@@ -0,0 +1,85 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/auth_digest.h
+ * @brief The declaration of the Digest Authorization internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_AUTH_DIGEST_H
+#define MHD_AUTH_DIGEST_H 1
+
+#include "mhd_sys_options.h"
+
+#if ! defined(MHD_SUPPORT_AUTH_DIGEST)
+#error Digest Authorization must be enabled
+#endif
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_digest_auth_data.h"
+
+#include "mhd_public_api.h"
+
+struct MHD_Connection; /* forward declaration */
+struct MHD_Request; /* forward declaration */
+
+/**
+ * Generate new nonce for Digest Auth, put the nonce in text form to the buffer
+ * @param c the connection to use // TODO: replace with daemon object
+ * @param out_buf the output buffer to put the generated nonce,
+ * NOT zero terminated
+ * @return 'true' if succeed,
+ * 'false' otherwise
+ */
+MHD_INTERNAL bool
+mhd_auth_digest_get_new_nonce (struct MHD_Connection *restrict c,
+ char out_buf[mhd_AUTH_DIGEST_NONCE_LEN])
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+/**
+ * Find in request and parse Digest Authentication username information
+ * @param req the request to use
+ * @param[out] v_auth_digest_username the pointer to set to the found data
+ * @return #MHD_SC_OK on success,
+ * error code otherwise
+ */
+MHD_INTERNAL enum MHD_StatusCode
+mhd_request_get_auth_digest_username (
+ struct MHD_Request *restrict req,
+ const struct MHD_AuthDigestUsernameInfo **restrict v_auth_digest_uname)
+MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+/**
+ * Find in request and parse Digest Authentication information
+ * @param req the request to use
+ * @param[out] v_auth_basic_creds the pointer to set to the found data
+ * @return #MHD_SC_OK on success,
+ * error code otherwise
+ */
+MHD_INTERNAL enum MHD_StatusCode
+mhd_request_get_auth_digest_info (
+ struct MHD_Request *restrict req,
+ const struct MHD_AuthDigestInfo **restrict v_auth_digest_info)
+MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+#endif /* ! MHD_AUTH_DIGEST_H */
diff --git a/src/mhd2/conn_data_process.c b/src/mhd2/conn_data_process.c
@@ -72,6 +72,7 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
default:
mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
+ break;
}
}
#endif /* MHD_ENABLE_HTTPS */
diff --git a/src/mhd2/daemon_add_conn.c b/src/mhd2/daemon_add_conn.c
@@ -178,9 +178,9 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon,
if (NULL == c)
{
mhd_LOG_MSG (daemon, \
- MHD_SC_CONNECTION_MALLOC_FAILURE, \
+ MHD_SC_CONNECTION_MEM_ALLOC_FAILURE, \
"Failed to allocate memory for the new connection");
- ret = MHD_SC_CONNECTION_MALLOC_FAILURE;
+ ret = MHD_SC_CONNECTION_MEM_ALLOC_FAILURE;
}
else
{
@@ -211,9 +211,9 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon,
if (NULL == c->sk.addr.data)
{
mhd_LOG_MSG (daemon, \
- MHD_SC_CONNECTION_MALLOC_FAILURE, \
+ MHD_SC_CONNECTION_MEM_ALLOC_FAILURE, \
"Failed to allocate memory for the new connection");
- ret = MHD_SC_CONNECTION_MALLOC_FAILURE;
+ ret = MHD_SC_CONNECTION_MEM_ALLOC_FAILURE;
}
else
memcpy (c->sk.addr.data,
@@ -304,9 +304,9 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
connection->pool = mdh_pool_create (daemon->conns.cfg.mem_pool_size);
if (NULL == connection->pool)
{ /* 'pool' creation failed */
- mhd_LOG_MSG (daemon, MHD_SC_POOL_MALLOC_FAILURE, \
+ mhd_LOG_MSG (daemon, MHD_SC_POOL_MEM_ALLOC_FAILURE, \
"Failed to allocate memory for the connection memory pool.");
- res = MHD_SC_POOL_MALLOC_FAILURE;
+ res = MHD_SC_POOL_MEM_ALLOC_FAILURE;
}
else
{ /* 'pool' creation succeed */
diff --git a/src/mhd2/daemon_get_info.c b/src/mhd2/daemon_get_info.c
@@ -83,5 +83,5 @@ MHD_daemon_get_info_fixed_sz (struct MHD_Daemon *daemon,
default:
break;
}
- return MHD_SC_INFO_TYPE_UNKNOWN;
+ return MHD_SC_INFO_GET_TYPE_UNKNOWN;
}
diff --git a/src/mhd2/daemon_options.h b/src/mhd2/daemon_options.h
@@ -260,31 +260,24 @@ struct DaemonOptions
/**
- * Value for #MHD_D_O_DAUTH_MAP_SIZE.
+ * Value for #MHD_D_O_AUTH_DIGEST_MAP_SIZE.
* the size of the map array
*/
- size_t dauth_map_size;
+ size_t auth_digest_map_size;
/**
- * Value for #MHD_D_O_DAUTH_NONCE_BIND_TYPE.
+ * Value for #MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT.
* FIXME
*/
- enum MHD_DaemonOptionValueDAuthBindNonce dauth_nonce_bind_type;
+ unsigned int auth_digest_nonce_timeout;
/**
- * Value for #MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT.
+ * Value for #MHD_D_O_AUTH_DIGEST_DEF_MAX_NC.
* FIXME
*/
- unsigned int dauth_def_nonce_timeout;
-
-
- /**
- * Value for #MHD_D_O_DAUTH_DEF_MAX_NC.
- * FIXME
- */
- uint_fast32_t dauth_def_max_nc;
+ uint_fast32_t auth_digest_def_max_nc;
};
diff --git a/src/mhd2/daemon_set_options.c b/src/mhd2/daemon_set_options.c
@@ -60,7 +60,7 @@ MHD_daemon_set_options (
free (settings->bind_sa.v_sa);
settings->bind_sa.v_sa = malloc (option->val.bind_sa.v_sa_len);
if (NULL == settings->bind_sa.v_sa)
- return MHD_SC_DAEMON_MALLOC_FAILURE;
+ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
memcpy (settings->bind_sa.v_sa, option->val.bind_sa.v_sa,
option->val.bind_sa.v_sa_len);
settings->bind_sa.v_sa_len = option->val.bind_sa.v_sa_len;
@@ -113,7 +113,7 @@ MHD_daemon_set_options (
free (settings->tls_cert_key.v_mem_cert); // TODO: Support multiple certificates!!
settings->tls_cert_key.v_mem_cert = malloc (cert_size + key_size + pass_size);
if (NULL == settings->tls_cert_key.v_mem_cert)
- return MHD_SC_DAEMON_MALLOC_FAILURE;
+ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
memcpy (settings->tls_cert_key.v_mem_cert,
option->val.tls_cert_key.v_mem_cert,
cert_size);
@@ -220,7 +220,7 @@ MHD_daemon_set_options (
settings->random_entropy.v_buf =
malloc (option->val.random_entropy.v_buf_size);
if (NULL == settings->random_entropy.v_buf)
- return MHD_SC_DAEMON_MALLOC_FAILURE;
+ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
memcpy (settings->random_entropy.v_buf,
option->val.random_entropy.v_buf,
option->val.random_entropy.v_buf_size);
@@ -228,17 +228,14 @@ MHD_daemon_set_options (
option->val.random_entropy.v_buf_size;
}
continue;
- case MHD_D_O_DAUTH_MAP_SIZE:
- settings->dauth_map_size = option->val.dauth_map_size;
+ case MHD_D_O_AUTH_DIGEST_MAP_SIZE:
+ settings->auth_digest_map_size = option->val.auth_digest_map_size;
continue;
- case MHD_D_O_DAUTH_NONCE_BIND_TYPE:
- settings->dauth_nonce_bind_type = option->val.dauth_nonce_bind_type;
+ case MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT:
+ settings->auth_digest_nonce_timeout = option->val.auth_digest_nonce_timeout;
continue;
- case MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT:
- settings->dauth_def_nonce_timeout = option->val.dauth_def_nonce_timeout;
- continue;
- case MHD_D_O_DAUTH_DEF_MAX_NC:
- settings->dauth_def_max_nc = option->val.dauth_def_max_nc;
+ case MHD_D_O_AUTH_DIGEST_DEF_MAX_NC:
+ settings->auth_digest_def_max_nc = option->val.auth_digest_def_max_nc;
continue;
case MHD_D_O_SENTINEL:
default: /* for -Wswitch-default -Wswitch-enum */
diff --git a/src/mhd2/daemon_start.c b/src/mhd2/daemon_start.c
@@ -28,6 +28,7 @@
#include "sys_base_types.h"
#include "sys_malloc.h"
+#include "compat_calloc.h"
#include <string.h>
#include "sys_sockets_types.h"
@@ -399,6 +400,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
default:
sk_type = mhd_SKT_UNKNOWN;
p_use_sa = NULL; /* To be set below */
+ break;
}
if (s->bind_sa.v_dual)
@@ -594,7 +596,6 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
case mhd_SKT_UNIX:
case mhd_SKT_NO_SOCKET:
default:
- mhd_assert (0);
mhd_UNREACHABLE ();
return MHD_SC_INTERNAL_ERROR;
}
@@ -971,7 +972,6 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
break;
case mhd_SKT_NO_SOCKET:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
return MHD_SC_INTERNAL_ERROR;
}
@@ -1339,10 +1339,8 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d,
#endif /* ! MHD_USE_EPOLL */
case mhd_POLL_TYPE_NOT_SET_YET:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
return MHD_SC_INTERNAL_ERROR;
- break;
}
return MHD_SC_OK;
}
@@ -1471,6 +1469,111 @@ dauth_init (struct MHD_Daemon *restrict d,
#endif
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+/**
+ * Initialise daemon Digest Auth data
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ * the error code otherwise
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
+daemon_init_auth_digest (struct MHD_Daemon *restrict d,
+ struct DaemonOptions *restrict s)
+{
+ mhd_StatusCodeInt ret;
+ size_t nonces_num;
+
+ if (0 == s->random_entropy.v_buf_size)
+ {
+ /* No initialisation needed */
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+ d->auth_dg.entropy.data = NULL;
+ d->auth_dg.nonces = NULL;
+#endif
+ return MHD_SC_OK;
+ }
+ nonces_num = s->auth_digest_map_size;
+ if (0 == nonces_num)
+ nonces_num = 1000;
+ d->auth_dg.nonces = (struct mhd_DaemonAuthDigestNonceData *)
+ mhd_calloc (nonces_num, \
+ sizeof(struct mhd_DaemonAuthDigestNonceData));
+ if (NULL == d->auth_dg.nonces)
+ {
+ mhd_LOG_MSG (d, \
+ MHD_SC_DAEMON_MEM_ALLOC_FAILURE, \
+ "Failed to allocate memory for Digest Auth array");
+ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
+ }
+ d->auth_dg.cfg.nonces_num = nonces_num;
+
+ if (! mhd_mutex_init (&(d->auth_dg.nonces_lock)))
+ {
+ mhd_LOG_MSG (d, MHD_SC_MUTEX_INIT_FAILURE, \
+ "Failed to initialise mutex for the Digest Auth data");
+ ret = MHD_SC_MUTEX_INIT_FAILURE;
+ }
+ else
+ {
+ if (! mhd_atomic_counter_init (&(d->auth_dg.num_gen_nonces), 0))
+ {
+ mhd_LOG_MSG (d, MHD_SC_MUTEX_INIT_FAILURE, \
+ "Failed to initialise mutex for the Digest Auth data");
+ ret = MHD_SC_MUTEX_INIT_FAILURE;
+ }
+ else
+ {
+ /* Move ownership of the entropy buffer */
+ d->auth_dg.entropy.data = s->random_entropy.v_buf;
+ d->auth_dg.entropy.size = s->random_entropy.v_buf_size;
+ s->random_entropy.v_buf = NULL;
+ s->random_entropy.v_buf_size = 0;
+
+ d->auth_dg.cfg.nonce_tmout = s->auth_digest_nonce_timeout;
+ if (0 == d->auth_dg.cfg.nonce_tmout)
+ d->auth_dg.cfg.nonce_tmout = MHD_AUTH_DIGEST_DEF_TIMEOUT;
+ d->auth_dg.cfg.def_max_nc = s->auth_digest_def_max_nc;
+ if (0 == d->auth_dg.cfg.def_max_nc)
+ d->auth_dg.cfg.def_max_nc = MHD_AUTH_DIGEST_DEF_MAX_NC;
+
+ return MHD_SC_OK; /* Success exit point */
+ }
+ mhd_mutex_destroy_chk (&(d->auth_dg.nonces_lock));
+ }
+
+ free (d->auth_dg.nonces);
+ mhd_assert (MHD_SC_OK != ret);
+ return ret; /* Failure exit point */
+}
+
+
+/**
+ * Deinitialise daemon Digest Auth data
+ * @param d the daemon object
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+daemon_deinit_auth_digest (struct MHD_Daemon *restrict d)
+{
+ if (0 == d->auth_dg.entropy.size)
+ return; /* Digest Auth not used, nothing to deinitialise */
+
+ mhd_assert (NULL != d->auth_dg.entropy.data);
+ free (d->auth_dg.entropy.data);
+ mhd_atomic_counter_deinit (&(d->auth_dg.num_gen_nonces));
+ mhd_mutex_destroy_chk (&(d->auth_dg.nonces_lock));
+ mhd_assert (NULL != d->auth_dg.nonces);
+ mhd_assert (0 != d->auth_dg.nonces);
+ free (d->auth_dg.nonces);
+}
+
+
+#else /* MHD_SUPPORT_AUTH_DIGEST */
+#define daemon_init_auth_digest(d,s) (MHD_SC_OK)
+#define daemon_deinit_auth_digest(d) ((void) 0)
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
/**
* Initialise daemon TLS data
@@ -1753,7 +1856,8 @@ allocate_events (struct MHD_Daemon *restrict d)
#endif /* ! MHD_USE_EPOLL */
case mhd_POLL_TYPE_NOT_SET_YET:
default:
- mhd_assert (0 && "Impossible value");
+ mhd_UNREACHABLE ();
+ break;
}
mhd_UNREACHABLE ();
return MHD_SC_INTERNAL_ERROR;
@@ -1988,7 +2092,8 @@ add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d)
#endif /* ! MHD_USE_EPOLL */
case mhd_POLL_TYPE_NOT_SET_YET:
default:
- mhd_assert (0 && "Impossible value");
+ mhd_UNREACHABLE ();
+ break;
}
mhd_UNREACHABLE ();
return MHD_SC_INTERNAL_ERROR;
@@ -2366,7 +2471,7 @@ set_d_threading_type (struct MHD_Daemon *restrict d)
case mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL:
#endif /* ! MHD_USE_THREADS */
default:
- mhd_assert (0 && "Impossible value");
+ break;
}
mhd_UNREACHABLE ();
return MHD_SC_INTERNAL_ERROR;
@@ -2421,6 +2526,11 @@ deinit_workers_pool (struct MHD_Daemon *restrict d,
static MHD_FN_PAR_NONNULL_ (1) void
reset_master_only_areas (struct MHD_Daemon *restrict d)
{
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ memset (&(d->auth_dg.nonces_lock),
+ 0x7F,
+ sizeof(d->auth_dg.nonces_lock));
+#endif
/* Not needed. It is initialised later */
/* memset (&(d->req_cfg.large_buf), 0, sizeof(d->req_cfg.large_buf)); */
(void) d;
@@ -2464,9 +2574,9 @@ init_workers_pool (struct MHD_Daemon *restrict d,
if (workers_pool_size / num_workers != sizeof(struct MHD_Daemon))
{ /* Overflow */
mhd_LOG_MSG ( \
- d, MHD_SC_THREAD_POOL_MALLOC_FAILURE, \
+ d, MHD_SC_THREAD_POOL_MEM_ALLOC_FAILURE, \
"The size of the thread pool is too large.");
- return MHD_SC_THREAD_POOL_MALLOC_FAILURE;
+ return MHD_SC_THREAD_POOL_MEM_ALLOC_FAILURE;
}
#ifndef NDEBUG
@@ -2478,9 +2588,9 @@ init_workers_pool (struct MHD_Daemon *restrict d,
if (NULL == d->threading.hier.pool.workers)
{
mhd_LOG_MSG ( \
- d, MHD_SC_THREAD_POOL_MALLOC_FAILURE, \
+ d, MHD_SC_THREAD_POOL_MEM_ALLOC_FAILURE, \
"Failed to allocate memory for the thread pool.");
- return MHD_SC_THREAD_POOL_MALLOC_FAILURE;
+ return MHD_SC_THREAD_POOL_MEM_ALLOC_FAILURE;
}
conn_per_daemon = d->conns.cfg.count_limit / num_workers;
@@ -2945,34 +3055,39 @@ daemon_start_internal (struct MHD_Daemon *restrict d,
return res;
mhd_assert (d->dbg.net_inited);
- res = daemon_init_tls (d, s);
+
+ res = daemon_init_auth_digest (d, s);
if (MHD_SC_OK == res)
{
- mhd_assert (d->dbg.tls_inited);
- res = daemon_init_threading_and_conn (d, s);
+ res = daemon_init_tls (d, s);
if (MHD_SC_OK == res)
{
- mhd_assert (d->dbg.threading_inited);
- mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type));
-
- res = daemon_init_large_buf (d, s);
+ mhd_assert (d->dbg.tls_inited);
+ res = daemon_init_threading_and_conn (d, s);
if (MHD_SC_OK == res)
{
- res = daemon_start_threads (d);
+ mhd_assert (d->dbg.threading_inited);
+ mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type));
+
+ res = daemon_init_large_buf (d, s);
if (MHD_SC_OK == res)
{
- return MHD_SC_OK;
- }
+ res = daemon_start_threads (d);
+ if (MHD_SC_OK == res)
+ {
+ return MHD_SC_OK;
+ }
- /* Below is a clean-up path */
- daemon_deinit_large_buf (d);
+ /* Below is a clean-up path */
+ daemon_deinit_large_buf (d);
+ }
+ daemon_deinit_threading_and_conn (d);
}
- daemon_deinit_threading_and_conn (d);
+ daemon_deinit_tls (d);
}
- daemon_deinit_tls (d);
+ daemon_deinit_auth_digest (d);
}
-
daemon_deinit_net (d);
mhd_assert (MHD_SC_OK != res);
return res;
@@ -3032,6 +3147,8 @@ MHD_daemon_destroy (struct MHD_Daemon *daemon)
daemon_deinit_tls (daemon);
+ daemon_deinit_auth_digest (daemon);
+
daemon_deinit_net (daemon);
}
daemon->state = mhd_DAEMON_STATE_STOPPED; /* Useful only for debugging */
diff --git a/src/mhd2/events_process.c b/src/mhd2/events_process.c
@@ -1099,9 +1099,9 @@ process_all_events_and_data (struct MHD_Daemon *restrict d)
#endif /* ! MHD_USE_EPOLL */
case mhd_POLL_TYPE_NOT_SET_YET:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
MHD_PANIC ("Daemon data integrity broken");
+ break;
}
if (d->events.act_req.accept)
{
diff --git a/src/mhd2/lib_get_info.c b/src/mhd2/lib_get_info.c
@@ -116,8 +116,26 @@ MHD_lib_get_info_fixed_sz (enum MHD_LibInfoFixed info_type,
case MHD_LIB_INFO_FIXED_HAS_COOKIE_PARSER:
case MHD_LIB_INFO_FIXED_HAS_POST_PARSER:
case MHD_LIB_INFO_FIXED_HAS_UPGRADE:
+ mhd_assert (0 && "Not implemented yet");
+ break;
case MHD_LIB_INFO_FIXED_HAS_BASIC_AUTH:
+ if (sizeof(return_data->v_bool) > return_data_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+#ifdef MHD_SUPPORT_AUTH_BASIC
+ return_data->v_bool = MHD_YES;
+#else
+ return_data->v_bool = MHD_NO;
+#endif
+ return MHD_SC_OK;
case MHD_LIB_INFO_FIXED_HAS_DIGEST_AUTH:
+ if (sizeof(return_data->v_bool) > return_data_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ return_data->v_bool = MHD_YES;
+#else
+ return_data->v_bool = MHD_NO;
+#endif
+ return MHD_SC_OK;
case MHD_LIB_INFO_FIXED_HAS_DIGEST_AUTH_RFC2069:
case MHD_LIB_INFO_FIXED_TYPE_DIGEST_AUTH_MD5:
case MHD_LIB_INFO_FIXED_TYPE_DIGEST_AUTH_SHA256:
@@ -167,7 +185,7 @@ MHD_lib_get_info_fixed_sz (enum MHD_LibInfoFixed info_type,
default:
break;
}
- return MHD_SC_INFO_TYPE_UNKNOWN;
+ return MHD_SC_INFO_GET_TYPE_UNKNOWN;
}
@@ -215,5 +233,5 @@ MHD_lib_get_info_dynamic_sz (enum MHD_LibInfoDynamic info_type,
default:
break;
}
- return MHD_SC_INFO_TYPE_UNKNOWN;
+ return MHD_SC_INFO_GET_TYPE_UNKNOWN;
}
diff --git a/src/mhd2/md5_ext.c b/src/mhd2/md5_ext.c
@@ -0,0 +1,102 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2023 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.c
+ * @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 mhd_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 may return initialisation error and set the handle at the
+ same time. Such handle cannot be used for calculations.
+ Note: GnuTLS may also return an error and NOT set the handle. */
+ gnutls_free (ctx->handle);
+ ctx->handle = NULL;
+ }
+
+ /* If handle is NULL, the error must be set */
+ mhd_assert ((NULL != ctx->handle) || (0 != ctx->ext_error));
+ /* If error is set, the handle must be NULL */
+ mhd_assert ((0 == ctx->ext_error) || (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 mhd_Md5CtxExt *ctx,
+ size_t size,
+ const uint8_t *data)
+{
+ if (0 == ctx->ext_error)
+ ctx->ext_error = gnutls_hash (ctx->handle, data, size);
+}
+
+
+/**
+ * Finalise MD5 calculation, return digest, reset hash calculation.
+ *
+ * @param ctx the calculation context
+ * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes
+ */
+void
+mhd_MD5_finish_reset (struct mhd_Md5CtxExt *ctx,
+ uint8_t digest[mhd_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 mhd_Md5CtxExt *ctx)
+{
+ if (NULL != ctx->handle)
+ gnutls_hash_deinit (ctx->handle, NULL);
+}
diff --git a/src/mhd2/md5_ext.h b/src/mhd2/md5_ext.h
@@ -0,0 +1,112 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+/**
+ * @file src/mhd2/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_sys_options.h"
+
+#include "sys_base_types.h"
+
+/**
+ * Size of MD5 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+#define mhd_MD5_DIGEST_SIZE (16)
+
+/* Actual declaration is in GnuTLS lib header */
+struct hash_hd_st;
+
+/**
+ * Indicates that struct mhd_Md5CtxExt has 'ext_error'
+ */
+#define mhd_MD5_HAS_EXT_ERROR 1
+
+/**
+ * MD5 calculation context
+ */
+struct mhd_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 mhd_Md5CtxExt *ctx);
+
+
+/**
+ * MD5 process portion of bytes.
+ *
+ * @param ctx the calculation context
+ * @param size number of bytes in @a data
+ * @param data bytes to add to hash
+ */
+void
+mhd_MD5_update (struct mhd_Md5CtxExt *restrict ctx,
+ size_t size,
+ const uint8_t *restrict data);
+
+
+/**
+ * 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 #mhd_MD5_DIGEST_SIZE bytes
+ */
+void
+mhd_MD5_finish_reset (struct mhd_Md5CtxExt *restrict ctx,
+ uint8_t digest[mhd_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 mhd_Md5CtxExt *ctx);
+
+#endif /* MHD_MD5_EXT_H */
diff --git a/src/mhd2/md5_int.c b/src/mhd2/md5_int.c
@@ -0,0 +1,543 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/md5_int.c
+ * @brief Calculation of MD5 digest as defined in RFC 1321
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+#include <string.h>
+#include "mhd_bithelpers.h"
+#include "mhd_assert.h"
+
+#include "md5_int.h"
+
+MHD_INTERNAL void MHD_FN_PAR_NONNULL_ALL_
+mhd_MD5_init (struct mhd_Md5CtxInt *ctx)
+{
+ /* Initial hash values, see RFC 1321, Clause 3.3 (step 3). */
+ /* Note: values specified in RFC by bytes and should be loaded in
+ little-endian mode, therefore hash values here are initialised with
+ original bytes used in little-endian order. */
+ ctx->H[0] = UINT32_C (0x67452301);
+ ctx->H[1] = UINT32_C (0xefcdab89);
+ ctx->H[2] = UINT32_C (0x98badcfe);
+ ctx->H[3] = UINT32_C (0x10325476);
+
+ /* Initialise the number of bytes. */
+ ctx->count = 0;
+}
+
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
+
+/**
+ * Base of MD5 transformation.
+ * Gets full 64 bytes block of data and updates hash values;
+ * @param H hash values
+ * @param M the data buffer with #mhd_MD5_BLOCK_SIZE bytes block
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+md5_transform (uint32_t H[mhd_MD5_HASH_SIZE_WORDS],
+ const void *restrict M)
+{
+ /* Working variables,
+ See RFC 1321, Clause 3.4 (step 4). */
+ uint32_t A = H[0];
+ uint32_t B = H[1];
+ uint32_t C = H[2];
+ uint32_t D = H[3];
+
+ /* The data buffer. See RFC 1321, Clause 3.4 (step 4). */
+ uint32_t X[16];
+
+#ifndef mhd_GET_32BIT_LE_UNALIGNED
+ if (0 != (((uintptr_t) M) % mhd_UINT32_ALIGN))
+ { /* The input data is unaligned. */
+ /* Copy the unaligned input data to the aligned buffer. */
+ memcpy (X, M, sizeof(X));
+ /* The X[] buffer itself will be used as the source of the data,
+ * but the data will be reloaded in correct bytes order on
+ * the next steps. */
+ M = (const void *) X;
+ }
+#endif /* mhd_GET_32BIT_LE_UNALIGNED */
+
+ /* Four auxiliary functions, see RFC 1321, Clause 3.4 (step 4). */
+ /* Some optimisations used. */
+/* #define F_FUNC(x,y,z) (((x)&(y)) | ((~(x))&(z))) */ /* Original version */
+#define F_FUNC(x,y,z) ((((y) ^ (z)) & (x)) ^ (z))
+/* #define G_FUNC_1(x,y,z) (((x)&(z)) | ((y)&(~(z)))) */ /* Original version */
+/* #define G_FUNC_2(x,y,z) UINT32_C(0) */ /* Original version */
+#ifndef MHD_FAVOR_SMALL_CODE
+# define G_FUNC_1(x,y,z) ((~(z)) & (y))
+# define G_FUNC_2(x,y,z) ((z) & (x))
+#else /* MHD_FAVOR_SMALL_CODE */
+# define G_FUNC_1(x,y,z) ((((x) ^ (y)) & (z)) ^ (y))
+# define G_FUNC_2(x,y,z) UINT32_C (0)
+#endif /* MHD_FAVOR_SMALL_CODE */
+#define H_FUNC(x,y,z) ((x) ^ (y) ^ (z)) /* Original version */
+/* #define I_FUNC(x,y,z) ((y) ^ ((x) | (~(z)))) */ /* Original version */
+#define I_FUNC(x,y,z) (((~(z)) | (x)) ^ (y))
+
+ /* One step of round 1 of MD5 computation, see RFC 1321, Clause 3.4 (step 4).
+ The original function was modified to use X[k] and T[i] as
+ direct inputs. */
+#define MD5STEP_R1(va,vb,vc,vd,vX,vs,vT) do { \
+ (va) += (vX) + (vT); \
+ (va) += F_FUNC ((vb),(vc),(vd)); \
+ (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0)
+
+ /* Get value of X(k) from input data buffer.
+ See RFC 1321 Clause 3.4 (step 4). */
+#define GET_X_FROM_DATA(buf,t) \
+ mhd_GET_32BIT_LE (((const uint32_t*) (buf)) + (t))
+
+ /* One step of round 2 of MD5 computation, see RFC 1321, Clause 3.4 (step 4).
+ The original function was modified to use X[k] and T[i] as
+ direct inputs. */
+#define MD5STEP_R2(va,vb,vc,vd,vX,vs,vT) do { \
+ (va) += (vX) + (vT); \
+ (va) += G_FUNC_1 ((vb),(vc),(vd)); \
+ (va) += G_FUNC_2 ((vb),(vc),(vd)); \
+ (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0)
+
+ /* One step of round 3 of MD5 computation, see RFC 1321, Clause 3.4 (step 4).
+ The original function was modified to use X[k] and T[i] as
+ direct inputs. */
+#define MD5STEP_R3(va,vb,vc,vd,vX,vs,vT) do { \
+ (va) += (vX) + (vT); \
+ (va) += H_FUNC ((vb),(vc),(vd)); \
+ (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0)
+
+ /* One step of round 4 of MD5 computation, see RFC 1321, Clause 3.4 (step 4).
+ The original function was modified to use X[k] and T[i] as
+ direct inputs. */
+#define MD5STEP_R4(va,vb,vc,vd,vX,vs,vT) do { \
+ (va) += (vX) + (vT); \
+ (va) += I_FUNC ((vb),(vc),(vd)); \
+ (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0)
+
+#if ! defined(MHD_FAVOR_SMALL_CODE)
+
+ /* Round 1. */
+
+#if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+ if ((const void *) X == M)
+ {
+ /* The input data is already in the data buffer X[] in correct bytes
+ order. */
+ MD5STEP_R1 (A, B, C, D, X[0], 7, UINT32_C (0xd76aa478));
+ MD5STEP_R1 (D, A, B, C, X[1], 12, UINT32_C (0xe8c7b756));
+ MD5STEP_R1 (C, D, A, B, X[2], 17, UINT32_C (0x242070db));
+ MD5STEP_R1 (B, C, D, A, X[3], 22, UINT32_C (0xc1bdceee));
+
+ MD5STEP_R1 (A, B, C, D, X[4], 7, UINT32_C (0xf57c0faf));
+ MD5STEP_R1 (D, A, B, C, X[5], 12, UINT32_C (0x4787c62a));
+ MD5STEP_R1 (C, D, A, B, X[6], 17, UINT32_C (0xa8304613));
+ MD5STEP_R1 (B, C, D, A, X[7], 22, UINT32_C (0xfd469501));
+
+ MD5STEP_R1 (A, B, C, D, X[8], 7, UINT32_C (0x698098d8));
+ MD5STEP_R1 (D, A, B, C, X[9], 12, UINT32_C (0x8b44f7af));
+ MD5STEP_R1 (C, D, A, B, X[10], 17, UINT32_C (0xffff5bb1));
+ MD5STEP_R1 (B, C, D, A, X[11], 22, UINT32_C (0x895cd7be));
+
+ MD5STEP_R1 (A, B, C, D, X[12], 7, UINT32_C (0x6b901122));
+ MD5STEP_R1 (D, A, B, C, X[13], 12, UINT32_C (0xfd987193));
+ MD5STEP_R1 (C, D, A, B, X[14], 17, UINT32_C (0xa679438e));
+ MD5STEP_R1 (B, C, D, A, X[15], 22, UINT32_C (0x49b40821));
+ }
+ else /* Combined with the next 'if' */
+#endif /* mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN */
+ if (1)
+ {
+ /* The input data is loaded in correct (little-endian) format before
+ calculations on each step. */
+ MD5STEP_R1 (A, B, C, D, X[0] = GET_X_FROM_DATA (M, 0), 7, \
+ UINT32_C (0xd76aa478));
+ MD5STEP_R1 (D, A, B, C, X[1] = GET_X_FROM_DATA (M, 1), 12, \
+ UINT32_C (0xe8c7b756));
+ MD5STEP_R1 (C, D, A, B, X[2] = GET_X_FROM_DATA (M, 2), 17, \
+ UINT32_C (0x242070db));
+ MD5STEP_R1 (B, C, D, A, X[3] = GET_X_FROM_DATA (M, 3), 22, \
+ UINT32_C (0xc1bdceee));
+
+ MD5STEP_R1 (A, B, C, D, X[4] = GET_X_FROM_DATA (M, 4), 7, \
+ UINT32_C (0xf57c0faf));
+ MD5STEP_R1 (D, A, B, C, X[5] = GET_X_FROM_DATA (M, 5), 12, \
+ UINT32_C (0x4787c62a));
+ MD5STEP_R1 (C, D, A, B, X[6] = GET_X_FROM_DATA (M, 6), 17, \
+ UINT32_C (0xa8304613));
+ MD5STEP_R1 (B, C, D, A, X[7] = GET_X_FROM_DATA (M, 7), 22, \
+ UINT32_C (0xfd469501));
+
+ MD5STEP_R1 (A, B, C, D, X[8] = GET_X_FROM_DATA (M, 8), 7, \
+ UINT32_C (0x698098d8));
+ MD5STEP_R1 (D, A, B, C, X[9] = GET_X_FROM_DATA (M, 9), 12, \
+ UINT32_C (0x8b44f7af));
+ MD5STEP_R1 (C, D, A, B, X[10] = GET_X_FROM_DATA (M, 10), 17, \
+ UINT32_C (0xffff5bb1));
+ MD5STEP_R1 (B, C, D, A, X[11] = GET_X_FROM_DATA (M, 11), 22, \
+ UINT32_C (0x895cd7be));
+
+ MD5STEP_R1 (A, B, C, D, X[12] = GET_X_FROM_DATA (M, 12), 7, \
+ UINT32_C (0x6b901122));
+ MD5STEP_R1 (D, A, B, C, X[13] = GET_X_FROM_DATA (M, 13), 12, \
+ UINT32_C (0xfd987193));
+ MD5STEP_R1 (C, D, A, B, X[14] = GET_X_FROM_DATA (M, 14), 17, \
+ UINT32_C (0xa679438e));
+ MD5STEP_R1 (B, C, D, A, X[15] = GET_X_FROM_DATA (M, 15), 22, \
+ UINT32_C (0x49b40821));
+ }
+
+ /* Round 2. */
+
+ MD5STEP_R2 (A, B, C, D, X[1], 5, UINT32_C (0xf61e2562));
+ MD5STEP_R2 (D, A, B, C, X[6], 9, UINT32_C (0xc040b340));
+ MD5STEP_R2 (C, D, A, B, X[11], 14, UINT32_C (0x265e5a51));
+ MD5STEP_R2 (B, C, D, A, X[0], 20, UINT32_C (0xe9b6c7aa));
+
+ MD5STEP_R2 (A, B, C, D, X[5], 5, UINT32_C (0xd62f105d));
+ MD5STEP_R2 (D, A, B, C, X[10], 9, UINT32_C (0x02441453));
+ MD5STEP_R2 (C, D, A, B, X[15], 14, UINT32_C (0xd8a1e681));
+ MD5STEP_R2 (B, C, D, A, X[4], 20, UINT32_C (0xe7d3fbc8));
+
+ MD5STEP_R2 (A, B, C, D, X[9], 5, UINT32_C (0x21e1cde6));
+ MD5STEP_R2 (D, A, B, C, X[14], 9, UINT32_C (0xc33707d6));
+ MD5STEP_R2 (C, D, A, B, X[3], 14, UINT32_C (0xf4d50d87));
+ MD5STEP_R2 (B, C, D, A, X[8], 20, UINT32_C (0x455a14ed));
+
+ MD5STEP_R2 (A, B, C, D, X[13], 5, UINT32_C (0xa9e3e905));
+ MD5STEP_R2 (D, A, B, C, X[2], 9, UINT32_C (0xfcefa3f8));
+ MD5STEP_R2 (C, D, A, B, X[7], 14, UINT32_C (0x676f02d9));
+ MD5STEP_R2 (B, C, D, A, X[12], 20, UINT32_C (0x8d2a4c8a));
+
+ /* Round 3. */
+
+ MD5STEP_R3 (A, B, C, D, X[5], 4, UINT32_C (0xfffa3942));
+ MD5STEP_R3 (D, A, B, C, X[8], 11, UINT32_C (0x8771f681));
+ MD5STEP_R3 (C, D, A, B, X[11], 16, UINT32_C (0x6d9d6122));
+ MD5STEP_R3 (B, C, D, A, X[14], 23, UINT32_C (0xfde5380c));
+
+ MD5STEP_R3 (A, B, C, D, X[1], 4, UINT32_C (0xa4beea44));
+ MD5STEP_R3 (D, A, B, C, X[4], 11, UINT32_C (0x4bdecfa9));
+ MD5STEP_R3 (C, D, A, B, X[7], 16, UINT32_C (0xf6bb4b60));
+ MD5STEP_R3 (B, C, D, A, X[10], 23, UINT32_C (0xbebfbc70));
+
+ MD5STEP_R3 (A, B, C, D, X[13], 4, UINT32_C (0x289b7ec6));
+ MD5STEP_R3 (D, A, B, C, X[0], 11, UINT32_C (0xeaa127fa));
+ MD5STEP_R3 (C, D, A, B, X[3], 16, UINT32_C (0xd4ef3085));
+ MD5STEP_R3 (B, C, D, A, X[6], 23, UINT32_C (0x04881d05));
+
+ MD5STEP_R3 (A, B, C, D, X[9], 4, UINT32_C (0xd9d4d039));
+ MD5STEP_R3 (D, A, B, C, X[12], 11, UINT32_C (0xe6db99e5));
+ MD5STEP_R3 (C, D, A, B, X[15], 16, UINT32_C (0x1fa27cf8));
+ MD5STEP_R3 (B, C, D, A, X[2], 23, UINT32_C (0xc4ac5665));
+
+ /* Round 4. */
+
+ MD5STEP_R4 (A, B, C, D, X[0], 6, UINT32_C (0xf4292244));
+ MD5STEP_R4 (D, A, B, C, X[7], 10, UINT32_C (0x432aff97));
+ MD5STEP_R4 (C, D, A, B, X[14], 15, UINT32_C (0xab9423a7));
+ MD5STEP_R4 (B, C, D, A, X[5], 21, UINT32_C (0xfc93a039));
+
+ MD5STEP_R4 (A, B, C, D, X[12], 6, UINT32_C (0x655b59c3));
+ MD5STEP_R4 (D, A, B, C, X[3], 10, UINT32_C (0x8f0ccc92));
+ MD5STEP_R4 (C, D, A, B, X[10], 15, UINT32_C (0xffeff47d));
+ MD5STEP_R4 (B, C, D, A, X[1], 21, UINT32_C (0x85845dd1));
+
+ MD5STEP_R4 (A, B, C, D, X[8], 6, UINT32_C (0x6fa87e4f));
+ MD5STEP_R4 (D, A, B, C, X[15], 10, UINT32_C (0xfe2ce6e0));
+ MD5STEP_R4 (C, D, A, B, X[6], 15, UINT32_C (0xa3014314));
+ MD5STEP_R4 (B, C, D, A, X[13], 21, UINT32_C (0x4e0811a1));
+
+ MD5STEP_R4 (A, B, C, D, X[4], 6, UINT32_C (0xf7537e82));
+ MD5STEP_R4 (D, A, B, C, X[11], 10, UINT32_C (0xbd3af235));
+ MD5STEP_R4 (C, D, A, B, X[2], 15, UINT32_C (0x2ad7d2bb));
+ MD5STEP_R4 (B, C, D, A, X[9], 21, UINT32_C (0xeb86d391));
+#else /* MHD_FAVOR_SMALL_CODE */
+ if (1)
+ {
+ static const uint32_t T[64] =
+ { UINT32_C (0xd76aa478), UINT32_C (0xe8c7b756), UINT32_C (0x242070db),
+ UINT32_C (0xc1bdceee), UINT32_C (0xf57c0faf), UINT32_C (0x4787c62a),
+ UINT32_C (0xa8304613), UINT32_C (0xfd469501), UINT32_C (0x698098d8),
+ UINT32_C (0x8b44f7af), UINT32_C (0xffff5bb1), UINT32_C (0x895cd7be),
+ UINT32_C (0x6b901122), UINT32_C (0xfd987193), UINT32_C (0xa679438e),
+ UINT32_C (0x49b40821), UINT32_C (0xf61e2562), UINT32_C (0xc040b340),
+ UINT32_C (0x265e5a51), UINT32_C (0xe9b6c7aa), UINT32_C (0xd62f105d),
+ UINT32_C (0x02441453), UINT32_C (0xd8a1e681), UINT32_C (0xe7d3fbc8),
+ UINT32_C (0x21e1cde6), UINT32_C (0xc33707d6), UINT32_C (0xf4d50d87),
+ UINT32_C (0x455a14ed), UINT32_C (0xa9e3e905), UINT32_C (0xfcefa3f8),
+ UINT32_C (0x676f02d9), UINT32_C (0x8d2a4c8a), UINT32_C (0xfffa3942),
+ UINT32_C (0x8771f681), UINT32_C (0x6d9d6122), UINT32_C (0xfde5380c),
+ UINT32_C (0xa4beea44), UINT32_C (0x4bdecfa9), UINT32_C (0xf6bb4b60),
+ UINT32_C (0xbebfbc70), UINT32_C (0x289b7ec6), UINT32_C (0xeaa127fa),
+ UINT32_C (0xd4ef3085), UINT32_C (0x04881d05), UINT32_C (0xd9d4d039),
+ UINT32_C (0xe6db99e5), UINT32_C (0x1fa27cf8), UINT32_C (0xc4ac5665),
+ UINT32_C (0xf4292244), UINT32_C (0x432aff97), UINT32_C (0xab9423a7),
+ UINT32_C (0xfc93a039), UINT32_C (0x655b59c3), UINT32_C (0x8f0ccc92),
+ UINT32_C (0xffeff47d), UINT32_C (0x85845dd1), UINT32_C (0x6fa87e4f),
+ UINT32_C (0xfe2ce6e0), UINT32_C (0xa3014314), UINT32_C (0x4e0811a1),
+ UINT32_C (0xf7537e82), UINT32_C (0xbd3af235), UINT32_C (0x2ad7d2bb),
+ UINT32_C (0xeb86d391) };
+ unsigned int i; /**< Zero-based index */
+
+ /* Round 1. */
+
+ i = 0;
+ do
+ {
+ /* The input data is loaded in correct (little-endian) format before
+ calculations on each step. */
+ MD5STEP_R1 (A, B, C, D, X[i] = GET_X_FROM_DATA (M, i), 7, T[i]);
+ ++i;
+ MD5STEP_R1 (D, A, B, C, X[i] = GET_X_FROM_DATA (M, i), 12, T[i]);
+ ++i;
+ MD5STEP_R1 (C, D, A, B, X[i] = GET_X_FROM_DATA (M, i), 17, T[i]);
+ ++i;
+ MD5STEP_R1 (B, C, D, A, X[i] = GET_X_FROM_DATA (M, i), 22, T[i]);
+ ++i;
+ } while (i < 16);
+
+ /* Round 2. */
+
+ do
+ {
+ const unsigned int idx_add = i;
+ MD5STEP_R2 (A, B, C, D, X[(1U + idx_add) & 15U], 5, T[i]);
+ ++i;
+ MD5STEP_R2 (D, A, B, C, X[(6U + idx_add) & 15U], 9, T[i]);
+ ++i;
+ MD5STEP_R2 (C, D, A, B, X[(11U + idx_add) & 15U], 14, T[i]);
+ ++i;
+ MD5STEP_R2 (B, C, D, A, X[(0U + idx_add) & 15U], 20, T[i]);
+ ++i;
+ } while (i < 32);
+
+ /* Round 3. */
+
+ do
+ {
+ const unsigned int idx_add = i;
+ MD5STEP_R3 (A, B, C, D, X[(5U + 64U - idx_add) & 15U], 4, T[i]);
+ ++i;
+ MD5STEP_R3 (D, A, B, C, X[(8U + 64U - idx_add) & 15U], 11, T[i]);
+ ++i;
+ MD5STEP_R3 (C, D, A, B, X[(11U + 64U - idx_add) & 15U], 16, T[i]);
+ ++i;
+ MD5STEP_R3 (B, C, D, A, X[(14U + 64U - idx_add) & 15U], 23, T[i]);
+ ++i;
+ } while (i < 48);
+
+ /* Round 4. */
+
+ do
+ {
+ const unsigned int idx_add = i;
+ MD5STEP_R4 (A, B, C, D, X[(0U + 64U - idx_add) & 15U], 6, T[i]);
+ ++i;
+ MD5STEP_R4 (D, A, B, C, X[(7U + 64U - idx_add) & 15U], 10, T[i]);
+ ++i;
+ MD5STEP_R4 (C, D, A, B, X[(14U + 64U - idx_add) & 15U], 15, T[i]);
+ ++i;
+ MD5STEP_R4 (B, C, D, A, X[(5U + 64U - idx_add) & 15U], 21, T[i]);
+ ++i;
+ } while (i < 64);
+ }
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+ /* Finally increment and store working variables.
+ See RFC 1321, end of Clause 3.4 (step 4). */
+
+ H[0] += A;
+ H[1] += B;
+ H[2] += C;
+ H[3] += D;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) void
+mhd_MD5_update (struct mhd_Md5CtxInt *restrict ctx,
+ size_t size,
+ const uint8_t *restrict data)
+{
+ unsigned int bytes_have; /**< Number of bytes in the context buffer */
+
+ mhd_assert ((data != NULL) || (size == 0));
+
+#ifndef MHD_FAVOR_SMALL_CODE
+ if (0 == size)
+ return; /* Shortcut, do nothing */
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+ /* Note: (count & (mhd_MD5_BLOCK_SIZE-1))
+ equals (count % mhd_MD5_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned int) (ctx->count & (mhd_MD5_BLOCK_SIZE - 1));
+ ctx->count += size;
+
+ if (0 != bytes_have)
+ {
+ unsigned int bytes_left = mhd_MD5_BLOCK_SIZE - bytes_have;
+ if (size >= bytes_left)
+ { /* Combine new data with data in the buffer and
+ process the full block. */
+ memcpy (((uint8_t *) ctx->buffer) + bytes_have,
+ data,
+ bytes_left);
+ data += bytes_left;
+ size -= bytes_left;
+ md5_transform (ctx->H, ctx->buffer);
+ bytes_have = 0;
+ }
+ }
+
+ while (mhd_MD5_BLOCK_SIZE <= size)
+ { /* Process any full blocks of new data directly,
+ without copying to the buffer. */
+ md5_transform (ctx->H, data);
+ data += mhd_MD5_BLOCK_SIZE;
+ size -= mhd_MD5_BLOCK_SIZE;
+ }
+
+ if (0 != size)
+ { /* Copy incomplete block of new data (if any)
+ to the buffer. */
+ memcpy (((uint8_t *) ctx->buffer) + bytes_have, data, size);
+ }
+}
+
+
+/**
+ * Size of "length" insertion in bits.
+ * See RFC 1321, end of Clause 3.2 (step 2).
+ */
+#define MD5_SIZE_OF_LEN_ADD_BITS 64
+
+/**
+ * Size of "length" insertion in bytes.
+ */
+#define MD5_SIZE_OF_LEN_ADD (MD5_SIZE_OF_LEN_ADD_BITS / 8)
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) void
+mhd_MD5_finish (struct mhd_Md5CtxInt *restrict ctx,
+ uint8_t digest[mhd_MD5_DIGEST_SIZE])
+{
+ uint64_t num_bits; /**< Number of processed bits */
+ unsigned int bytes_have; /**< Number of bytes in the context buffer */
+
+ /* Memorise the number of processed bits.
+ The padding and other data added here during the postprocessing must
+ not change the amount of hashed data. */
+ num_bits = ctx->count << 3;
+
+ /* Note: (count & (mhd_MD5_BLOCK_SIZE-1))
+ equals (count % mhd_MD5_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned int) (ctx->count & (mhd_MD5_BLOCK_SIZE - 1));
+
+ /* Input data must be padded with a single bit "1", then with zeros and
+ the finally the length of data in bits must be added as the final bytes
+ of the last block.
+ See RFC 1321, Clauses 3.1 and 3.2 (steps 1 and 2). */
+ /* Data is always processed in form of bytes (not by individual bits),
+ therefore position of the first padding bit in byte is always
+ predefined (0x80). */
+ /* Buffer always have space for one byte at least (as full buffers are
+ processed immediately). */
+ ((uint8_t *) ctx->buffer)[bytes_have++] = 0x80;
+
+ if (mhd_MD5_BLOCK_SIZE - bytes_have < MD5_SIZE_OF_LEN_ADD)
+ { /* No space in the current block to put the total length of message.
+ Pad the current block with zeros and process it. */
+ if (bytes_have < mhd_MD5_BLOCK_SIZE)
+ memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
+ mhd_MD5_BLOCK_SIZE - bytes_have);
+ /* Process the full block. */
+ md5_transform (ctx->H, ctx->buffer);
+ /* Start the new block. */
+ bytes_have = 0;
+ }
+
+ /* Pad the rest of the buffer with zeros. */
+ memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
+ mhd_MD5_BLOCK_SIZE - MD5_SIZE_OF_LEN_ADD - bytes_have);
+ /* Put the number of bits in processed data as little-endian value.
+ See RFC 1321, clauses 2 and 3.2 (step 2). */
+ mhd_PUT_64BIT_LE_UNALIGN (ctx->buffer + mhd_MD5_BLOCK_SIZE_WORDS - 2,
+ num_bits);
+ /* Process the full final block. */
+ md5_transform (ctx->H, ctx->buffer);
+
+ /* Put in LE mode the hash as the final digest.
+ See RFC 1321, clauses 2 and 3.5 (step 5). */
+ if (1)
+ {
+ bool use_tmp_buf_to_align_result;
+
+#if defined(mhd_PUT_32BIT_LE_UNALIGNED)
+ use_tmp_buf_to_align_result = false;
+#elif defined (MHD_FAVOR_SMALL_CODE)
+ use_tmp_buf_to_align_result = true; /* smaller code: eliminated branch below */
+#else
+ use_tmp_buf_to_align_result =
+ (0 != ((uintptr_t) digest) % mhd_UINT32_ALIGN);
+#endif
+ if (use_tmp_buf_to_align_result)
+ {
+ /* If storing of the final result requires aligned address and
+ the destination address is not aligned or compact code is used,
+ store the final digest in aligned temporary buffer first, then
+ copy it to the destination. */
+ uint32_t alig_dgst[mhd_MD5_DIGEST_SIZE_WORDS];
+ mhd_PUT_32BIT_LE (alig_dgst + 0, ctx->H[0]);
+ mhd_PUT_32BIT_LE (alig_dgst + 1, ctx->H[1]);
+ mhd_PUT_32BIT_LE (alig_dgst + 2, ctx->H[2]);
+ mhd_PUT_32BIT_LE (alig_dgst + 3, ctx->H[3]);
+ /* Copy result to the unaligned destination address. */
+ memcpy (digest, alig_dgst, mhd_MD5_DIGEST_SIZE);
+ }
+ else
+ {
+ /* Use cast to (void*) here to mute compiler alignment warnings.
+ * Compilers are not smart enough to see that alignment has been checked. */
+ mhd_PUT_32BIT_LE ((void *) (digest + 0 * mhd_MD5_BYTES_IN_WORD), \
+ ctx->H[0]);
+ mhd_PUT_32BIT_LE ((void *) (digest + 1 * mhd_MD5_BYTES_IN_WORD), \
+ ctx->H[1]);
+ mhd_PUT_32BIT_LE ((void *) (digest + 2 * mhd_MD5_BYTES_IN_WORD), \
+ ctx->H[2]);
+ mhd_PUT_32BIT_LE ((void *) (digest + 3 * mhd_MD5_BYTES_IN_WORD), \
+ ctx->H[3]);
+ }
+ }
+
+ /* Erase potentially sensitive data. */
+ memset (ctx, 0, sizeof(struct mhd_Md5CtxInt));
+}
+
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE
diff --git a/src/mhd2/md5_int.h b/src/mhd2/md5_int.h
@@ -0,0 +1,129 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/md5_int.h
+ * @brief Calculation of MD5 digest
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_MD5_INT_H
+#define MHD_MD5_INT_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+/**
+ * Number of bits in single MD5 word.
+ */
+#define mhd_MD5_WORD_SIZE_BITS (32)
+
+/**
+ * Number of bytes in single MD5 word.
+ */
+#define mhd_MD5_BYTES_IN_WORD (mhd_MD5_WORD_SIZE_BITS / 8)
+
+/**
+ * Hash is kept internally as four 32-bit words.
+ * This is intermediate hash size, used during computing the final digest.
+ */
+#define mhd_MD5_HASH_SIZE_WORDS (4)
+
+/**
+ * Size of MD5 resulting digest in bytes.
+ * This is the final digest size, not intermediate hash.
+ */
+#define mhd_MD5_DIGEST_SIZE_WORDS mhd_MD5_HASH_SIZE_WORDS
+
+/**
+ * Size of MD5 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+#define mhd_MD5_DIGEST_SIZE (mhd_MD5_DIGEST_SIZE_WORDS * mhd_MD5_BYTES_IN_WORD)
+
+/**
+ * Size of MD5 single processing block in bits.
+ */
+#define mhd_MD5_BLOCK_SIZE_BITS 512
+
+/**
+ * Size of MD5 single processing block in bytes.
+ */
+#define mhd_MD5_BLOCK_SIZE (mhd_MD5_BLOCK_SIZE_BITS / 8)
+
+/**
+ * Size of MD5 single processing block in words.
+ */
+#define mhd_MD5_BLOCK_SIZE_WORDS \
+ (mhd_MD5_BLOCK_SIZE_BITS / mhd_MD5_WORD_SIZE_BITS)
+
+
+/**
+ * MD5 calculation context
+ */
+struct mhd_Md5CtxInt
+{
+ uint32_t H[mhd_MD5_HASH_SIZE_WORDS]; /**< Intermediate hash value / digest at end of calculation */
+ uint32_t buffer[mhd_MD5_BLOCK_SIZE_WORDS]; /**< MD5 input data buffer */
+ uint64_t count; /**< number of bytes, mod 2^64 */
+};
+
+/**
+ * Initialise structure for MD5 calculation.
+ *
+ * @param ctx the calculation context
+ */
+MHD_INTERNAL void
+mhd_MD5_init (struct mhd_Md5CtxInt *ctx)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * MD5 process portion of bytes.
+ *
+ * @param ctx the calculation context
+ * @param size number of bytes in @a data
+ * @param data bytes to add to hash
+ */
+MHD_INTERNAL void
+mhd_MD5_update (struct mhd_Md5CtxInt *restrict ctx,
+ size_t size,
+ const uint8_t *restrict data)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3, 2);
+
+
+/**
+ * Finalise MD5 calculation, return digest.
+ *
+ * @param ctx the calculation context
+ * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes
+ */
+MHD_INTERNAL void
+mhd_MD5_finish (struct mhd_Md5CtxInt *restrict ctx,
+ uint8_t digest[mhd_MD5_DIGEST_SIZE])
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+/**
+ * Indicates that function mhd_MD5_finish() (without context reset) is available
+ */
+#define mhd_MD5_HAS_FINISH 1
+
+#endif /* MHD_MD5_INT_H */
diff --git a/src/mhd2/mhd_align.h b/src/mhd2/mhd_align.h
@@ -0,0 +1,91 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2021-2022 Karlson2k (Evgeny Grin)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_align.h
+ * @brief types alignment macros
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_ALIGN_H
+#define MHD_ALIGN_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+#include "sys_offsetof.h"
+
+#ifdef HAVE_C_ALIGNOF
+# ifdef HAVE_STDALIGN_H
+# include <stdalign.h>
+# endif /* HAVE_STDALIGN_H */
+# define mhd_ALIGNOF(type) alignof(type)
+#endif /* HAVE_C_ALIGNOF */
+
+#ifndef mhd_ALIGNOF
+# if defined(_MSC_VER) && ! defined(__clang__) && _MSC_VER >= 1700
+# define mhd_ALIGNOF(type) __alignof (type)
+# endif /* _MSC_VER >= 1700 */
+#endif /* !mhd_ALIGNOF */
+
+#ifdef mhd_ALIGNOF
+# if (defined(__GNUC__) && __GNUC__ < 4 && __GNUC_MINOR__ < 9 && \
+ ! defined(__clang__)) || \
+ (defined(__clang__) && __clang_major__ < 8) || \
+ (defined(__clang__) && __clang_major__ < 11 && \
+ defined(__apple_build_version__))
+/* GCC before 4.9 and clang before 8.0 have incorrect implementation of 'alignof()'
+ which returns preferred alignment instead of minimal required alignment */
+# define mhd_ALIGNOF_UNRELIABLE 1
+# endif
+
+# if defined(_MSC_VER) && ! defined(__clang__) && _MSC_VER < 1900
+/* MSVC has the same problem as old GCC versions:
+ '__alignof()' may return "preferred" alignment instead of "required". */
+# define mhd_ALIGNOF_UNRELIABLE 1
+# endif /* _MSC_VER < 1900 */
+#endif /* mhd_ALIGNOF */
+
+
+/* Provide a limited set of alignment macros */
+/* The set could be extended as needed */
+#if defined(mhd_ALIGNOF) && ! defined(mhd_ALIGNOF_UNRELIABLE)
+# define mhd_UINT32_ALIGN mhd_ALIGNOF (uint32_t)
+# define mhd_UINT64_ALIGN mhd_ALIGNOF (uint64_t)
+#else /* ! mhd_ALIGNOF */
+struct mhd_dummy_uint32_offset_test_
+{
+ char dummy;
+ uint32_t ui32;
+};
+# define mhd_UINT32_ALIGN \
+ offsetof (struct mhd_dummy_uint32_offset_test_, ui32)
+
+struct mhd_dummy_uint64_offset_test_
+{
+ char dummy;
+ uint64_t ui64;
+};
+# define mhd_UINT64_ALIGN \
+ offsetof (struct mhd_dummy_uint64_offset_test_, ui64)
+#endif /* ! mhd_ALIGNOF */
+
+#endif /* ! MHD_ALIGN_H */
diff --git a/src/mhd2/mhd_atomic_counter.c b/src/mhd2/mhd_atomic_counter.c
@@ -47,6 +47,23 @@ mhd_atomic_counter_inc_get (struct mhd_AtomicCounter *pcnt)
}
+#ifndef NDEBUG
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_inc_wrap_get (struct mhd_AtomicCounter *pcnt)
+{
+ mhd_ATOMIC_COUNTER_TYPE ret;
+
+ mhd_mutex_lock_chk (&(pcnt->lock));
+ ret = ++(pcnt->count);
+ mhd_mutex_unlock_chk (&(pcnt->lock));
+
+ return ret;
+}
+
+
+#endif /* ! NDEBUG */
+
+
MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
mhd_atomic_counter_dec_get (struct mhd_AtomicCounter *pcnt)
{
diff --git a/src/mhd2/mhd_atomic_counter.h b/src/mhd2/mhd_atomic_counter.h
@@ -137,6 +137,26 @@ struct mhd_AtomicCounter
MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
mhd_atomic_counter_inc_get (struct mhd_AtomicCounter *pcnt);
+#ifdef NDEBUG
+/**
+ * Atomically increment the value of the counter and return the result.
+ * The value is allowed to overflow and get back to zero.
+ * @param pcnt the pointer to the counter to increment
+ * @return the final/resulting counter value
+ */
+#define mhd_atomic_counter_inc_wrap_get(pcnt) mhd_atomic_counter_inc_get (pcnt)
+#else
+/**
+ * Atomically increment the value of the counter and return the result.
+ * The value is allowed to overflow and get back to zero.
+ * @param pcnt the pointer to the counter to increment
+ * @return the final/resulting counter value
+ */
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_inc_wrap_get (struct mhd_AtomicCounter *pcnt);
+
+#endif
+
/**
* Atomically decrement the value of the counter and return the result
* @param pcnt the pointer to the counter to decrement
diff --git a/src/mhd2/mhd_auth_digest_hdr.h b/src/mhd2/mhd_auth_digest_hdr.h
@@ -0,0 +1,66 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_auth_digest_hdr.h
+ * @brief The definition of the Digest Auth response header structure
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_AUTH_DIGEST_HDR_H
+#define MHD_AUTH_DIGEST_HDR_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_SUPPORT_AUTH_DIGEST
+#error This header should be used only if Digest Auth is build
+#endif
+
+#include "mhd_str_types.h"
+#include "mhd_dlinked_list.h"
+
+struct MHD_Response; /* forward declaration */
+struct mhd_RespAuthDigestHeader; /* forward declaration */
+
+mhd_DLINKEDL_STRUCTS_DEFS (mhd_RespAuthDigestHeader);
+
+/**
+ * The Digest Auth response header (the challenge)
+ */
+struct mhd_RespAuthDigestHeader
+{
+ /**
+ * The complete header string, including the name, the colon, the value and
+ * CRLF terminating chars
+ */
+ struct MHD_String hdr;
+
+ /**
+ * The position of the nonce placeholder in the @a hdr
+ */
+ size_t nonce_pos;
+
+ /**
+ * The links to the other headers for the same response
+ */
+ mhd_DLNKDL_LINKS_TYPE (mhd_RespAuthDigestHeader) auth_d_hdrs;
+};
+
+#endif /* ! MHD_AUTH_DIGEST_HDR_H */
diff --git a/src/mhd2/mhd_bithelpers.h b/src/mhd2/mhd_bithelpers.h
@@ -0,0 +1,425 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2019-2023 Karlson2k (Evgeny Grin)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_bithelpers.h
+ * @brief macros for bits manipulations
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_BITHELPERS_H
+#define MHD_BITHELPERS_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+ defined(__OPTIMIZE__)))
+/* Declarations for VC & Clang/C2 built-ins */
+#include <intrin.h>
+#endif /* _MSC_FULL_VER */
+#include "mhd_byteorder.h"
+#if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN || mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#include "mhd_align.h"
+#endif /* mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN ||
+ mhd_BYTE_ORDER == mhd_BIG_ENDIAN */
+
+#ifndef __has_builtin
+# define mhd_HAS_BUILTIN(x) (0)
+#else
+# define mhd_HAS_BUILTIN(x) __has_builtin (x)
+#endif
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
+
+#ifdef MHD_HAVE___BUILTIN_BSWAP32
+# define mhd_BYTES_SWAP32(value32) \
+ ((uint32_t) __builtin_bswap32 ((uint32_t) value32))
+#elif defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+ defined(__OPTIMIZE__)))
+/* Clang/C2 may not inline this function if optimizations are turned off. */
+# ifndef __clang__
+# pragma intrinsic(_byteswap_ulong)
+# endif /* ! __clang__ */
+# define mhd_BYTES_SWAP32(value32) \
+ ((uint32_t) _byteswap_ulong ((uint32_t) value32))
+#elif \
+ mhd_HAS_BUILTIN (__builtin_bswap32)
+# define mhd_BYTES_SWAP32(value32) \
+ ((uint32_t)__builtin_bswap32 ((uint32_t) value32))
+#else /* ! mhd_HAS_BUILTIN(__builtin_bswap32) */
+# define mhd_BYTES_SWAP32(value32) \
+ ( (((uint32_t) (value32)) << 24) \
+ | ((((uint32_t) (value32)) & ((uint32_t) 0x0000FF00)) << 8) \
+ | ((((uint32_t) (value32)) & ((uint32_t) 0x00FF0000)) >> 8) \
+ | (((uint32_t) (value32)) >> 24) )
+#endif /* ! mhd_HAS_BUILTIN(__builtin_bswap32) */
+
+#ifdef MHD_HAVE___BUILTIN_BSWAP64
+# define mhd_BYTES_SWAP64(value64) \
+ ((uint64_t)__builtin_bswap64 ((uint64_t) value64))
+#elif defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+ defined(__OPTIMIZE__)))
+/* Clang/C2 may not inline this function if optimizations are turned off. */
+# ifndef __clang__
+# pragma intrinsic(_byteswap_uint64)
+# endif /* ! __clang__ */
+# define mhd_BYTES_SWAP64(value64) \
+ ((uint64_t)_byteswap_uint64 ((uint64_t) value64))
+#elif \
+ mhd_HAS_BUILTIN (__builtin_bswap64)
+# define mhd_BYTES_SWAP64(value64) \
+ ((uint64_t)__builtin_bswap64 ((uint64_t) value64))
+#else /* ! mhd_HAS_BUILTIN(__builtin_bswap64) */
+# define mhd_BYTES_SWAP64(value64) \
+ ( (((uint64_t) (value64)) << 56) \
+ | ((((uint64_t) (value64)) & ((uint64_t) 0x000000000000FF00)) << 40) \
+ | ((((uint64_t) (value64)) & ((uint64_t) 0x0000000000FF0000)) << 24) \
+ | ((((uint64_t) (value64)) & ((uint64_t) 0x00000000FF000000)) << 8) \
+ | ((((uint64_t) (value64)) & ((uint64_t) 0x000000FF00000000)) >> 8) \
+ | ((((uint64_t) (value64)) & ((uint64_t) 0x0000FF0000000000)) >> 24) \
+ | ((((uint64_t) (value64)) & ((uint64_t) 0x00FF000000000000)) >> 40) \
+ | (((uint64_t) (value64)) >> 56) )
+#endif /* ! mhd_HAS_BUILTIN(__builtin_bswap64) */
+
+
+/* mhd_PUT_64BIT_LE (addr, value64)
+ * put native-endian 64-bit value64 to addr
+ * in little-endian mode.
+ */
+/* Slow version that works with unaligned addr and with any bytes order */
+#define mhd_PUT_64BIT_LE_SLOW(addr, value64) do { \
+ ((uint8_t*) (addr))[0] = (uint8_t) ((uint64_t) (value64)); \
+ ((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 8); \
+ ((uint8_t*) (addr))[2] = (uint8_t) (((uint64_t) (value64)) >> 16); \
+ ((uint8_t*) (addr))[3] = (uint8_t) (((uint64_t) (value64)) >> 24); \
+ ((uint8_t*) (addr))[4] = (uint8_t) (((uint64_t) (value64)) >> 32); \
+ ((uint8_t*) (addr))[5] = (uint8_t) (((uint64_t) (value64)) >> 40); \
+ ((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 48); \
+ ((uint8_t*) (addr))[7] = (uint8_t) (((uint64_t) (value64)) >> 56); \
+} while (0)
+#if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+# define mhd_PUT_64BIT_LE(addr, value64) \
+ ((*(uint64_t*) (addr)) = (uint64_t) (value64))
+#elif mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+# define mhd_PUT_64BIT_LE(addr, value64) \
+ ((*(uint64_t*) (addr)) = mhd_BYTES_SWAP64 (value64))
+#else /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+# define mhd_PUT_64BIT_LE(addr, value64) \
+ mhd_PUT_64BIT_LE_SLOW ((addr),(value64))
+/* Indicate that mhd_PUT_64BIT_LE does not need aligned pointer */
+# define mhd_PUT_64BIT_LE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+
+/* Put result safely to unaligned address */
+MHD_static_inline_ void
+mhd_PUT_64BIT_LE_UNALIGN (void *dst, uint64_t value)
+{
+#ifndef mhd_PUT_64BIT_LE_UNALIGNED
+ if (0 != ((uintptr_t) dst) % (mhd_UINT64_ALIGN))
+ mhd_PUT_64BIT_LE_SLOW (dst, value);
+ else
+#endif /* ! mhd_PUT_64BIT_LE_UNALIGNED */
+ mhd_PUT_64BIT_LE (dst, value);
+}
+
+
+/* mhd_PUT_32BIT_LE (addr, value32)
+ * put native-endian 32-bit value32 to addr
+ * in little-endian mode.
+ */
+/* Slow version that works with unaligned addr and with any bytes order */
+#define mhd_PUT_32BIT_LE_SLOW(addr, value32) do { \
+ ((uint8_t*) (addr))[0] = (uint8_t) ((uint32_t) (value32)); \
+ ((uint8_t*) (addr))[1] = (uint8_t) (((uint32_t) (value32)) >> 8); \
+ ((uint8_t*) (addr))[2] = (uint8_t) (((uint32_t) (value32)) >> 16); \
+ ((uint8_t*) (addr))[3] = (uint8_t) (((uint32_t) (value32)) >> 24); \
+} while (0)
+#if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+# define mhd_PUT_32BIT_LE(addr,value32) \
+ ((*(uint32_t*) (addr)) = (uint32_t) (value32))
+#elif mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+# define mhd_PUT_32BIT_LE(addr, value32) \
+ ((*(uint32_t*) (addr)) = mhd_BYTES_SWAP32 (value32))
+#else /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+# define mhd_PUT_32BIT_LE(addr, value32) \
+ mhd_PUT_32BIT_LE_SLOW ((addr),(value32))
+/* Indicate that mhd_PUT_32BIT_LE does not need aligned pointer */
+# define mhd_PUT_32BIT_LE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+
+/* Put result safely to unaligned address */
+MHD_static_inline_ void
+mhd_PUT_32BIT_LE_UNALIGN (void *dst, uint32_t value)
+{
+#ifndef mhd_PUT_32BIT_LE_UNALIGNED
+ if (0 != ((uintptr_t) dst) % (mhd_UINT32_ALIGN))
+ mhd_PUT_32BIT_LE_SLOW (dst, value);
+ else
+#endif /* ! mhd_PUT_64BIT_LE_UNALIGNED */
+ mhd_PUT_32BIT_LE (dst, value);
+}
+
+
+/* mhd_GET_32BIT_LE (addr)
+ * get little-endian 32-bit value storied at addr
+ * and return it in native-endian mode.
+ */
+/* Slow version that works with unaligned addr and with any bytes order */
+#define mhd_GET_32BIT_LE_SLOW(addr) \
+ ( ( (uint32_t) (((const uint8_t*) addr)[0])) \
+ | (((uint32_t) (((const uint8_t*) addr)[1])) << 8) \
+ | (((uint32_t) (((const uint8_t*) addr)[2])) << 16) \
+ | (((uint32_t) (((const uint8_t*) addr)[3])) << 24) )
+#if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+# define mhd_GET_32BIT_LE(addr) \
+ (*(const uint32_t*) (addr))
+#elif mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+# define mhd_GET_32BIT_LE(addr) \
+ mhd_BYTES_SWAP32 (*(const uint32_t*) (addr))
+#else /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+# define mhd_GET_32BIT_LE(addr) \
+ ( ( (uint32_t) (((const uint8_t*) addr)[0])) \
+ | (((uint32_t) (((const uint8_t*) addr)[1])) << 8) \
+ | (((uint32_t) (((const uint8_t*) addr)[2])) << 16) \
+ | (((uint32_t) (((const uint8_t*) addr)[3])) << 24) )
+/* Indicate that mhd_GET_32BIT_LE does not need aligned pointer */
+# define mhd_GET_32BIT_LE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+
+/* Get value safely from an unaligned address */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ uint32_t
+mhd_GET_32BIT_LE_UNALIGN (const void *addr)
+{
+#ifndef mhd_GET_32BIT_LE_UNALIGNED
+ if (0 != ((uintptr_t) addr) % (mhd_UINT32_ALIGN))
+ return mhd_GET_32BIT_LE_SLOW (addr);
+ else
+#endif /* ! mhd_PUT_64BIT_LE_UNALIGNED */
+ return mhd_GET_32BIT_LE (addr);
+}
+
+
+/* mhd_PUT_64BIT_BE (addr, value64)
+ * put native-endian 64-bit value64 to addr
+ * in big-endian mode.
+ */
+/* Slow version that works with unaligned addr and with any bytes order */
+#define mhd_PUT_64BIT_BE_SLOW(addr, value64) do { \
+ ((uint8_t*) (addr))[7] = (uint8_t) ((uint64_t) (value64)); \
+ ((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 8); \
+ ((uint8_t*) (addr))[5] = (uint8_t) (((uint64_t) (value64)) >> 16); \
+ ((uint8_t*) (addr))[4] = (uint8_t) (((uint64_t) (value64)) >> 24); \
+ ((uint8_t*) (addr))[3] = (uint8_t) (((uint64_t) (value64)) >> 32); \
+ ((uint8_t*) (addr))[2] = (uint8_t) (((uint64_t) (value64)) >> 40); \
+ ((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 48); \
+ ((uint8_t*) (addr))[0] = (uint8_t) (((uint64_t) (value64)) >> 56); \
+} while (0)
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+# define mhd_PUT_64BIT_BE(addr, value64) \
+ ((*(uint64_t*) (addr)) = (uint64_t) (value64))
+#elif mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+# define mhd_PUT_64BIT_BE(addr, value64) \
+ ((*(uint64_t*) (addr)) = mhd_BYTES_SWAP64 (value64))
+#else /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+# define mhd_PUT_64BIT_BE(addr, value64) mhd_PUT_64BIT_BE_SLOW (addr, value64)
+/* Indicate that mhd_PUT_64BIT_BE does not need aligned pointer */
+# define mhd_PUT_64BIT_BE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+
+/* Put result safely to unaligned address */
+MHD_static_inline_ void
+mhd_PUT_64BIT_BE_UNALIGN (void *dst, uint64_t value)
+{
+#ifndef mhd_PUT_64BIT_BE_UNALIGNED
+ if (0 != ((uintptr_t) dst) % (mhd_UINT64_ALIGN))
+ mhd_PUT_64BIT_BE_SLOW (dst, value);
+ else
+#endif /* ! mhd_PUT_64BIT_BE_UNALIGNED */
+ mhd_PUT_64BIT_BE (dst, value);
+}
+
+
+/* mhd_GET_64BIT_BE (addr)
+ * load 64-bit value located at addr in big endian mode.
+ */
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+# define mhd_GET_64BIT_BE(addr) \
+ (*(const uint64_t*) (addr))
+#elif mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+# define mhd_GET_64BIT_BE(addr) \
+ mhd_BYTES_SWAP64 (*(const uint64_t*) (addr))
+#else /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+# define mhd_GET_64BIT_BE(addr) \
+ ( (((uint64_t) (((const uint8_t*) addr)[0])) << 56) \
+ | (((uint64_t) (((const uint8_t*) addr)[1])) << 48) \
+ | (((uint64_t) (((const uint8_t*) addr)[2])) << 40) \
+ | (((uint64_t) (((const uint8_t*) addr)[3])) << 32) \
+ | (((uint64_t) (((const uint8_t*) addr)[4])) << 24) \
+ | (((uint64_t) (((const uint8_t*) addr)[5])) << 16) \
+ | (((uint64_t) (((const uint8_t*) addr)[6])) << 8) \
+ | ((uint64_t) (((const uint8_t*) addr)[7])) )
+/* Indicate that mhd_GET_64BIT_BE does not need aligned pointer */
+# define mhd_GET_64BIT_BE_ALLOW_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+
+
+/* mhd_PUT_32BIT_BE (addr, value32)
+ * put native-endian 32-bit value32 to addr
+ * in big-endian mode.
+ */
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+# define mhd_PUT_32BIT_BE(addr, value32) \
+ ((*(uint32_t*) (addr)) = (uint32_t) (value32))
+#elif mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+# define mhd_PUT_32BIT_BE(addr, value32) \
+ ((*(uint32_t*) (addr)) = mhd_BYTES_SWAP32 (value32))
+#else /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+# define mhd_PUT_32BIT_BE(addr, value32) do { \
+ ((uint8_t*) (addr))[3] = (uint8_t) ((uint32_t) (value32)); \
+ ((uint8_t*) (addr))[2] = (uint8_t) (((uint32_t) (value32)) >> 8); \
+ ((uint8_t*) (addr))[1] = (uint8_t) (((uint32_t) (value32)) >> 16); \
+ ((uint8_t*) (addr))[0] = (uint8_t) (((uint32_t) (value32)) >> 24); \
+} while (0)
+/* Indicate that mhd_PUT_32BIT_BE does not need aligned pointer */
+# define mhd_PUT_32BIT_BE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+
+/* mhd_GET_32BIT_BE (addr)
+ * get big-endian 32-bit value storied at addr
+ * and return it in native-endian mode.
+ */
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+# define mhd_GET_32BIT_BE(addr) \
+ (*(const uint32_t*) (addr))
+#elif mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+# define mhd_GET_32BIT_BE(addr) \
+ mhd_BYTES_SWAP32 (*(const uint32_t*) (addr))
+#else /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+# define mhd_GET_32BIT_BE(addr) \
+ ( (((uint32_t) (((const uint8_t*) addr)[0])) << 24) \
+ | (((uint32_t) (((const uint8_t*) addr)[1])) << 16) \
+ | (((uint32_t) (((const uint8_t*) addr)[2])) << 8) \
+ | ((uint32_t) (((const uint8_t*) addr)[3])) )
+/* Indicate that mhd_GET_32BIT_BE does not need aligned pointer */
+# define mhd_GET_32BIT_BE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+
+
+/**
+ * Rotate right 32-bit value by number of bits.
+ * bits parameter must be more than zero and must be less than 32.
+ */
+#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+ defined(__OPTIMIZE__)))
+/* Clang/C2 do not inline this function if optimizations are turned off. */
+# ifndef __clang__
+# pragma intrinsic(_rotr)
+# endif /* ! __clang__ */
+# define mhd_ROTR32(value32, bits) \
+ ((uint32_t) _rotr ((uint32_t) (value32),(bits)))
+#elif mhd_HAS_BUILTIN (__builtin_rotateright32)
+# define mhd_ROTR32(value32, bits) \
+ ((uint32_t) __builtin_rotateright32 ((value32), (bits)))
+#else /* ! __builtin_rotateright32 */
+MHD_static_inline_ uint32_t
+mhd_ROTR32 (uint32_t value32, int bits)
+{
+ bits %= 32;
+ if (0 == bits)
+ return value32;
+ /* Defined in form which modern compiler could optimize. */
+ return (value32 >> bits) | (value32 << (32 - bits));
+}
+
+
+#endif /* ! __builtin_rotateright32 */
+
+
+/**
+ * Rotate left 32-bit value by number of bits.
+ * bits parameter must be more than zero and must be less than 32.
+ */
+#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+ defined(__OPTIMIZE__)))
+/* Clang/C2 do not inline this function if optimizations are turned off. */
+# ifndef __clang__
+# pragma intrinsic(_rotl)
+# endif /* ! __clang__ */
+# define mhd_ROTL32(value32, bits) \
+ ((uint32_t) _rotl ((uint32_t) (value32),(bits)))
+#elif mhd_HAS_BUILTIN (__builtin_rotateleft32)
+# define mhd_ROTL32(value32, bits) \
+ ((uint32_t) __builtin_rotateleft32 ((value32), (bits)))
+#else /* ! __builtin_rotateleft32 */
+MHD_static_inline_ uint32_t
+mhd_ROTL32 (uint32_t value32, int bits)
+{
+ bits %= 32;
+ if (0 == bits)
+ return value32;
+ /* Defined in form which modern compiler could optimize. */
+ return (value32 << bits) | (value32 >> (32 - bits));
+}
+
+
+#endif /* ! __builtin_rotateleft32 */
+
+
+/**
+ * Rotate right 64-bit value by number of bits.
+ * bits parameter must be more than zero and must be less than 64.
+ */
+#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+ defined(__OPTIMIZE__)))
+/* Clang/C2 do not inline this function if optimisations are turned off. */
+# ifndef __clang__
+# pragma intrinsic(_rotr64)
+# endif /* ! __clang__ */
+# define mhd_ROTR64(value64, bits) \
+ ((uint64_t) _rotr64 ((uint64_t) (value64),(bits)))
+#elif mhd_HAS_BUILTIN (__builtin_rotateright64)
+# define mhd_ROTR64(value64, bits) \
+ ((uint64_t) __builtin_rotateright64 ((value64), (bits)))
+#else /* ! __builtin_rotateright64 */
+MHD_static_inline_ uint64_t
+mhd_ROTR64 (uint64_t value64, int bits)
+{
+ bits %= 64;
+ if (0 == bits)
+ return value64;
+ /* Defined in form which modern compiler could optimise. */
+ return (value64 >> bits) | (value64 << (64 - bits));
+}
+
+
+#endif /* ! __builtin_rotateright64 */
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE
+
+#endif /* ! MHD_BITHELPERS_H */
diff --git a/src/mhd2/mhd_byteorder.h b/src/mhd2/mhd_byteorder.h
@@ -0,0 +1,173 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2015-2022 Karlson2k (Evgeny Grin)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_byteorder.h
+ * @brief macro definitions for host byte order
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_BYTEORDER_H
+#define MHD_BYTEORDER_H
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+#ifdef HAVE_MACHINE_ENDIAN_H
+# include <machine/endian.h>
+#endif
+
+#ifdef HAVE_SYS_ENDIAN_H
+# include <sys/endian.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_BYTEORDER_H
+# include <sys/byteorder.h>
+#endif
+
+#ifdef HAVE_SYS_MACHINE_H
+# include <sys/machine.h>
+#endif
+
+#ifdef HAVE_MACHINE_PARAM_H
+# include <machine/param.h>
+#endif
+
+#ifdef HAVE_SYS_ISA_DEFS_H
+# include <sys/isa_defs.h>
+#endif
+
+#define mhd_BIG_ENDIAN 1234
+#define mhd_LITTLE_ENDIAN 4321
+#define mhd_PDP_ENDIAN 2143
+
+#if defined(__BYTE_ORDER__)
+# if defined(__ORDER_BIG_ENDIAN__) && \
+ __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+# elif defined(__ORDER_LITTLE_ENDIAN__) && \
+ __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+# elif defined(__ORDER_PDP_ENDIAN__) && \
+ __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__
+# define mhd_BYTE_ORDER mhd_PDP_ENDIAN
+# endif /* __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__ */
+#elif defined(__BYTE_ORDER)
+# if defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN
+# define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+# elif defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
+# define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+# elif defined(__PDP_ENDIAN) && __BYTE_ORDER == __PDP_ENDIAN
+# define mhd_BYTE_ORDER mhd_PDP_ENDIAN
+# endif /* __BYTE_ORDER == __PDP_ENDIAN */
+#elif defined(BYTE_ORDER)
+# if defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN
+# define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+# elif defined(LITTLE_ENDIAN) && BYTE_ORDER == LITTLE_ENDIAN
+# define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+# elif defined(PDP_ENDIAN) && BYTE_ORDER == PDP_ENDIAN
+# define mhd_BYTE_ORDER mhd_PDP_ENDIAN
+# endif /* __BYTE_ORDER == _PDP_ENDIAN */
+#elif defined(_BYTE_ORDER)
+# if defined(_BIG_ENDIAN) && _BYTE_ORDER == _BIG_ENDIAN
+# define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+# elif defined(_LITTLE_ENDIAN) && _BYTE_ORDER == _LITTLE_ENDIAN
+# define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+# elif defined(_PDP_ENDIAN) && _BYTE_ORDER == _PDP_ENDIAN
+# define mhd_BYTE_ORDER mhd_PDP_ENDIAN
+# endif /* _BYTE_ORDER == _PDP_ENDIAN */
+#endif /* _BYTE_ORDER */
+
+#ifndef mhd_BYTE_ORDER
+/* Byte order specification didn't detected in system headers */
+/* Try some guessing */
+
+# if (defined(__BIG_ENDIAN__) && ! defined(__LITTLE_ENDIAN__)) || \
+ (defined(_BIG_ENDIAN) && ! defined(_LITTLE_ENDIAN))
+/* Seems that this is a big endian platform */
+# define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+# elif (defined(__LITTLE_ENDIAN__) && ! defined(__BIG_ENDIAN__)) || \
+ (defined(_LITTLE_ENDIAN) && ! defined(_BIG_ENDIAN))
+/* Seems that this is a little endian platform */
+# define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+# elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || \
+ defined(__x86_64) || \
+ defined(_M_X64) || defined(_M_AMD64) || defined(i386) || defined(__i386) || \
+ defined(__i386__) || defined(__i486__) || defined(__i586__) || \
+ defined(__i686__) || \
+ defined(_M_IX86) || defined(_X86_) || defined(__THW_INTEL__)
+/* x86 family is little endian */
+# define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+# elif defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
+ defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__)
+/* Looks like this is ARM/MIPS in big endian mode */
+# define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+# elif defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \
+ defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
+/* Looks like this is ARM/MIPS in little endian mode */
+# define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+# elif defined(__m68k__) || defined(M68000) || defined(__hppa__) || \
+ defined(__hppa) || \
+ defined(__HPPA__) || defined(__370__) || defined(__THW_370__) || \
+ defined(__s390__) || defined(__s390x__) || defined(__SYSC_ZARCH__)
+/* Looks like this is a big endian platform */
+# define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+# elif defined(__ia64__) || defined(_IA64) || defined(__IA64__) || \
+ defined(__ia64) || \
+ defined(_M_IA64) || defined(__itanium__) || defined(__bfin__) || \
+ defined(__BFIN__) || defined(bfin) || defined(BFIN)
+/* Looks like this is a little endian platform */
+# define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+# elif defined(_WIN32)
+/* W32 is always little endian on all platforms, except XBOX 360 */
+# if ! defined(_M_PPC) && ! defined(XBOX360)
+# define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+# endif
+# elif defined(WORDS_BIGENDIAN)
+/* Use byte order detected by configure */
+# define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+# endif /* _WIN32 */
+#endif /* !mhd_BYTE_ORDER */
+
+#ifdef mhd_BYTE_ORDER
+/* Some sanity checks */
+# if defined(WORDS_BIGENDIAN) && mhd_BYTE_ORDER != mhd_BIG_ENDIAN
+#error \
+ Configure detected big endian byte order but headers specify different byte order
+# elif ! defined(WORDS_BIGENDIAN) && mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#error \
+ Configure did not detect big endian byte order but headers specify big endian byte order
+# endif /* !WORDS_BIGENDIAN && mhd_BYTE_ORDER == mhd_BIG_ENDIAN */
+#endif /* mhd_BYTE_ORDER */
+
+#endif /* !MHD_BYTEORDER_H */
diff --git a/src/mhd2/mhd_cntnr_ptr.h b/src/mhd2/mhd_cntnr_ptr.h
@@ -20,7 +20,7 @@
/**
* @file src/mhd2/mhd_cntnr_ptr.h
- * @brief The definition of mhd_cntnr_ptr() macro.
+ * @brief The definition of mhd_CNTNR_PTR() and mhd_CNTNR_CPTR() macros.
* @author Karlson2k (Evgeny Grin)
*/
@@ -45,10 +45,28 @@
* @param membr_name the name of the member pointed by @a membr_ptr
* @return the pointer to the outer structure
*/
-#define mhd_cntnr_ptr(membr_ptr,cntnr_type,membr_name) \
- ((cntnr_type*) (void*) \
- (((char*) (0 ? \
+#define mhd_CNTNR_PTR(membr_ptr,cntnr_type,membr_name) \
+ ((cntnr_type*) (void*) \
+ (((char*) (0 ? \
(&(((cntnr_type*) NULL)->membr_name)) : \
(membr_ptr))) - offsetof (cntnr_type,membr_name)))
+/**
+ * Get the const pointer to the outer @a cntnr_type structure
+ * containing @a membr_name member by the @a membr_ptr pointer to the member.
+ *
+ * This macro checks at compile time whether pointer to the @a membr_name in
+ * the @a cntnr_type is compatible with the provided @a membr_ptr pointer.
+ *
+ * @param membr_ptr the pointer to the member
+ * @param cntnr_type the type of the container with the @a membr_name member
+ * @param membr_name the name of the member pointed by @a membr_ptr
+ * @return the pointer to the outer structure
+ */
+#define mhd_CNTNR_CPTR(membr_ptr,cntnr_type,membr_name) \
+ ((const cntnr_type*) (const void*) \
+ (((const char*) (0 ? \
+ (&(((cntnr_type*) NULL)->membr_name)) : \
+ (membr_ptr))) - offsetof (cntnr_type,membr_name)))
+
#endif /* ! MHD_CNTNR_PTR_H */
diff --git a/src/mhd2/mhd_daemon.h b/src/mhd2/mhd_daemon.h
@@ -30,10 +30,17 @@
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
+#include "sys_base_types.h"
+#include "mhd_dlinked_list.h"
+
+#include "mhd_buffer.h"
#include "mhd_socket_type.h"
+#include "mhd_atomic_counter.h"
-#include "mhd_public_api.h"
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+# include "mhd_digest_auth_data.h"
+#endif
#ifdef MHD_ENABLE_HTTPS
# include "mhd_tls_choice.h"
@@ -51,7 +58,7 @@
# include <sys/epoll.h>
#endif
-#include "mhd_dlinked_list.h"
+#include "mhd_public_api.h"
struct DaemonOptions; /* Forward declaration */
struct MHD_Connection; /* Forward declaration */
@@ -499,6 +506,96 @@ struct mhd_DaemonNetwork
struct mhd_DaemonNetworkSettings cfg;
};
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+
+/**
+ * Digest Auth nonce data
+ */
+struct mhd_DaemonAuthDigestNonceData
+{
+ /**
+ * The nonce value in the binary form, excluding validity tail
+ */
+ uint8_t nonce[mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE];
+
+ /**
+ * The nonce validity time
+ */
+ uint_fast32_t valid_time;
+
+ /**
+ * The largest last received 'nc' value.
+ * This 'nc' value has been already used by the client.
+ */
+ uint_fast32_t max_recvd_nc;
+
+ /**
+ * Bitmask over the previous 64 nc values (down to to nc-64).
+ * Used to allow out-of-order 'nc'.
+ * If bit in the bitmask is set to one, then this 'nc' value was already used
+ * by the client.
+ */
+ uint_fast64_t nmask;
+};
+
+/**
+ * Digest Auth daemon configuration data
+ */
+struct mhd_DaemonAuthDigestCfg
+{
+ /**
+ * The number of elements in the nonces array
+ */
+ size_t nonces_num;
+
+ /**
+ * The nonce validity time (in seconds)
+ */
+ unsigned int nonce_tmout;
+
+ /**
+ * The default maximum value of nc
+ */
+ uint_fast32_t def_max_nc;
+};
+
+/**
+ * The Digest Auth daemon's data
+ */
+struct mhd_DaemonAuthDigestData
+{
+ /**
+ * The entropy data used for Digests generation
+ */
+ struct mhd_Buffer entropy;
+
+ /**
+ * The array of generated nonce and related nc
+ */
+ struct mhd_DaemonAuthDigestNonceData *nonces;
+
+ /**
+ * Number of nonces has been generated.
+ * Used as addition for the nonce source data to ensure unique nonce value.
+ * TODO: remove and directly use random generator for nonce generation.
+ */
+ struct mhd_AtomicCounter num_gen_nonces;
+
+#ifdef MHD_USE_THREADS
+ /**
+ * The mutex to change or access the @a nonces data
+ */
+ mhd_mutex nonces_lock;
+#endif
+
+ /**
+ * Digest Auth daemon configuration data
+ */
+ struct mhd_DaemonAuthDigestCfg cfg;
+};
+
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
#ifdef MHD_USE_THREADS
/**
@@ -841,7 +938,7 @@ struct mhd_DaemonRequestProcessingSettings
/**
* Protocol strictness enforced by MHD on clients.
*/
- enum MHD_ProtocolStrictLevel strictnees;
+ enum MHD_ProtocolStrictLevel strictness;
/**
* Early URI callback
@@ -866,7 +963,7 @@ struct mhd_DaemonRequestProcessingSettings
* strictness level.
* RFC 9112, section 2.2
*/
-#define mhd_ALLOW_BARE_LF_AS_CRLF(discp_lvl) (0 >= discp_lvl)
+#define mhd_ALLOW_BARE_LF_AS_CRLF(strictness) (0 >= strictness)
#ifndef NDEBUG
@@ -918,6 +1015,13 @@ struct MHD_Daemon
*/
struct mhd_DaemonNetwork net;
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ /**
+ * The Digest Auth daemon's data
+ */
+ struct mhd_DaemonAuthDigestData auth_dg;
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
#ifdef MHD_ENABLE_HTTPS
/**
* The pointer to the daemon TLS data.
@@ -1035,5 +1139,13 @@ struct MHD_Daemon
# define mhd_D_HAS_TLS(d) (0)
#endif
+/*
+ * Check whether the daemon support Digest Auth
+ */
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+# define mhd_D_HAS_AUTH_DIGEST(d) (NULL != (d)->auth_dg.nonces)
+#else
+# define mhd_D_HAS_AUTH_DIGEST(d) (! ! 0)
+#endif
#endif /* ! MHD_DAEMON_H */
diff --git a/src/mhd2/mhd_digest_auth_data.h b/src/mhd2/mhd_digest_auth_data.h
@@ -0,0 +1,68 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_digest_auth_data.h
+ * @brief The macros and other data for Digest Auth
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_DIGEST_AUTH_DATA_H
+#define MHD_DIGEST_AUTH_DATA_H 1
+
+#include "mhd_sys_options.h"
+
+/**
+ * The name of the Auth scheme
+ */
+#define mhd_AUTH_DIGEST_SCHEME "Digest"
+
+/**
+ * The size of the random part of the nonce (in bytes)
+ */
+#define mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE 32
+/**
+ * The length of the random part of the nonce (in chars)
+ */
+#define mhd_AUTH_DIGEST_NONCE_RAND_LEN \
+ (mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE * 2)
+
+/**
+ * The size of the validity time part of the nonce (in bytes)
+ */
+#define mhd_AUTH_DIGEST_NONCE_VALD_BIN_SIZE 4
+/**
+ * The length of the validity time part of the nonce (in chars)
+ */
+#define mhd_AUTH_DIGEST_NONCE_VALD_LEN \
+ (mhd_AUTH_DIGEST_NONCE_VALD_BIN_SIZE * 2)
+
+/**
+ * The total size of the binary form of the nonce (in bytes)
+ */
+#define mhd_AUTH_DIGEST_NONCE_BIN_SIZE \
+ (mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE \
+ + mhd_AUTH_DIGEST_NONCE_VALD_BIN_SIZE)
+/**
+ * The total length of the nonce (in chars), without zero termination
+ */
+#define mhd_AUTH_DIGEST_NONCE_LEN (mhd_AUTH_DIGEST_NONCE_BIN_SIZE * 2)
+
+#endif /* ! MHD_DIGEST_AUTH_DATA_H */
diff --git a/src/mhd2/mhd_md5.h b/src/mhd2/mhd_md5.h
@@ -0,0 +1,110 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_md5.h
+ * @brief Simple wrapper for selection of built-in/external MD5 implementation
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_MD5_H
+#define MHD_MD5_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_SUPPORT_MD5
+#error This file must be used only when MD5 is enabled
+#endif
+
+#ifndef MHD_MD5_EXTR
+#include "md5_int.h"
+#else /* MHD_MD5_EXTR */
+#include "md5_ext.h"
+#endif /* MHD_MD5_EXTR */
+
+#ifndef mhd_MD5_DIGEST_SIZE
+/**
+ * Size of MD5 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+# define mhd_MD5_DIGEST_SIZE (16)
+#endif /* ! mhd_MD5_DIGEST_SIZE */
+
+#ifndef MHD_MD5_EXTR
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+# define mhd_Md5Ctx mhd_Md5CtxInt
+#else /* MHD_MD5_EXTR */
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+# define mhd_Md5Ctx mhd_Md5CtxExt
+#endif /* MHD_MD5_EXTR */
+
+#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))
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+# define mhd_MD5_finish_deinit(ctx,digest) \
+ (mhd_MD5_finish (ctx,digest), mhd_MD5_deinit (ctx))
+#else /* mhd_MD5_HAS_FINISH_RESET */
+# define mhd_MD5_reset(ctx) ((void) 0)
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+# define mhd_MD5_finish_deinit(ctx,digest) \
+ (mhd_MD5_finish_reset (ctx,digest), mhd_MD5_deinit (ctx))
+#endif /* mhd_MD5_HAS_FINISH_RESET */
+
+#ifndef mhd_MD5_HAS_DEINIT
+# define mhd_MD5_deinit(ctx) ((void) 0)
+#endif /* HAVE_MD5_DEINIT */
+
+
+#ifdef mhd_MD5_HAS_EXT_ERROR
+#define mhd_MD5_has_err(ctx) (0 != ((ctx)->ext_error))
+#else /* ! mhd_MD5_HAS_EXT_ERROR */
+#define mhd_MD5_has_err(ctx) (((void) (ctx)), ! ! 0)
+#endif /* ! mhd_SHA512_256_HAS_EXT_ERROR */
+
+/* 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()
+#endif /* ! mhd_MD5_HAS_FINISH_RESET && ! mhd_MD5_HAS_FINISH */
+
+#endif /* MHD_MD5_H */
diff --git a/src/mhd2/mhd_mono_clock.c b/src/mhd2/mhd_mono_clock.c
@@ -705,7 +705,6 @@ mhd_monotonic_msec_counter (void)
#endif /* _WIN32_WINNT < 0x0600 */
#endif /* _WIN32 */
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
break;
}
diff --git a/src/mhd2/mhd_recv.c b/src/mhd2/mhd_recv.c
@@ -107,9 +107,18 @@ mhd_recv_tls (struct MHD_Connection *restrict c,
(((unsigned int) c->sk.ready)
& (~(enum mhd_SocketNetState)
mhd_SOCKET_NET_STATE_RECV_READY));
- else if ((mhd_SOCKET_ERR_NO_ERROR == res) &&
- mhd_tls_conn_has_data_in (c->tls))
- c->tls_has_data_in = mhd_TLS_BUF_HAS_DATA_IN;
+ else if (mhd_SOCKET_ERR_NO_ERROR == res)
+ {
+ if (res == buf_size)
+ {
+ if (mhd_tls_conn_has_data_in (c->tls))
+ c->tls_has_data_in = mhd_TLS_BUF_HAS_DATA_IN;
+ }
+#ifndef NDEBUG
+ else
+ mhd_assert (! mhd_tls_conn_has_data_in (c->tls));
+#endif
+ }
return res;
}
diff --git a/src/mhd2/mhd_request.h b/src/mhd2/mhd_request.h
@@ -283,6 +283,105 @@ union mhd_ReqContentParsingData
// TODO: move "raw" upload processing data here
};
+
+#ifdef MHD_SUPPORT_AUTH_BASIC
+/**
+ * Request Basic Auth internal data
+ * The same format as struct MHD_AuthBasicCreds, but wiht nullable username.
+ * Keep in sync with MHD_AuthBasicCreds!
+ */
+struct mhd_ReqAuthBasicInternalData
+{
+ /**
+ * The user name
+ */
+ struct MHD_StringNullable username;
+ /**
+ * The user password
+ */
+ struct MHD_StringNullable password;
+};
+
+/**
+ * Request Basic Auth data
+ */
+union mhd_ReqAuthBasicData
+{
+ /**
+ * The internal representation of the Basic Auth data
+ */
+ struct mhd_ReqAuthBasicInternalData intr;
+
+ /**
+ * The external (application) Basic Auth data
+ */
+ struct MHD_AuthBasicCreds extr;
+};
+
+#endif /* MHD_SUPPORT_AUTH_BASIC */
+
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+
+struct mhd_AuthDigesReqParams; /* forward declaration */
+
+/**
+ * Request Digest Auth data
+ */
+struct mhd_ReqAuthDigestData
+{
+ /**
+ * Request Digest Auth pre-parsed data
+ */
+ struct mhd_AuthDigesReqParams *rqp;
+ /**
+ * When set to value other then #MHD_SC_OK,
+ * indicates request Digest Auth header parsing error.
+ */
+ enum MHD_StatusCode parse_result;
+ /**
+ * The information about client's Digest Auth header.
+ * NULL if not yet parsed or not found.
+ */
+ struct MHD_AuthDigestInfo *info;
+ /**
+ * The information about client's provided username.
+ * May point to the same address as @a info.
+ * NULL if not yet parsed or not found.
+ */
+ struct MHD_AuthDigestUsernameInfo *uname;
+};
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
+#if defined(MHD_SUPPORT_AUTH_BASIC) || defined(MHD_SUPPORT_AUTH_DIGEST)
+/**
+ * Defined if any Authentication scheme is supported
+ */
+# define mhd_SUPPORT_AUTH 1
+#endif /* MHD_SUPPORT_AUTH_BASIC */
+
+
+#ifdef mhd_SUPPORT_AUTH
+/**
+ * Request Basic Auth data
+ */
+struct mhd_ReqAuthData
+{
+#ifdef MHD_SUPPORT_AUTH_BASIC
+ /**
+ * Request Basic Auth data
+ */
+ union mhd_ReqAuthBasicData basic;
+#endif /* MHD_SUPPORT_AUTH_BASIC */
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ /**
+ * Request Digest Auth data
+ */
+ struct mhd_ReqAuthDigestData digest;
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+};
+
+#endif /* mhd_SUPPORT_AUTH */
+
/**
* Request-specific values.
*
@@ -327,6 +426,13 @@ struct MHD_Request
*/
bool have_expect_100;
+#ifdef mhd_SUPPORT_AUTH
+ /**
+ * Request Basic Auth data
+ */
+ struct mhd_ReqAuthData auth;
+#endif /* mhd_SUPPORT_AUTH */
+
/**
* HTTP version string (i.e. http/1.1). Allocated
* in pool.
@@ -341,7 +447,7 @@ struct MHD_Request
/**
* Request method. Should be GET/POST/etc. Allocated in pool.
*/
- const char *method;
+ struct MHD_String method;
/**
* The request method as enum.
diff --git a/src/mhd2/mhd_response.h b/src/mhd2/mhd_response.h
@@ -36,6 +36,9 @@
#include "mhd_dlinked_list.h"
#include "mhd_str_types.h"
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+# include "mhd_auth_digest_hdr.h"
+#endif
#include "mhd_iovec.h"
@@ -264,6 +267,13 @@ struct mhd_ResponseConfiguration
* Response is internal-only error response
*/
bool int_err_resp;
+
+#ifdef MHD_SUPPORT_AUTH_BASIC
+ /**
+ * Response has Basic Auth "challenge" header
+ */
+ bool has_bauth;
+#endif /* MHD_SUPPORT_AUTH_BASIC */
};
/**
@@ -349,6 +359,13 @@ struct MHD_Response
*/
mhd_DLNKDL_LIST (mhd_ResponseHeader,headers);
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ /**
+ * The double linked list of the Digest Auth response headers
+ */
+ mhd_DLNKDL_LIST (mhd_RespAuthDigestHeader,auth_d_hdrs);
+#endif
+
/**
* Special data for internal error responses
*/
@@ -364,4 +381,14 @@ struct MHD_Response
#endif
};
+/*
+ * Check whether the response has Digest Auth headers
+ */
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+#define mhd_RESP_HAD_AUTH_DIGEST(resp) \
+ (NULL != mhd_DLINKEDL_GET_FIRST (resp, auth_d_hdrs))
+#else
+#define mhd_RESP_HAD_AUTH_DIGEST(resp) (((void) (resp)), ! ! 0)
+#endif
+
#endif /* ! MHD_RESPONSE_H */
diff --git a/src/mhd2/mhd_sha256.h b/src/mhd2/mhd_sha256.h
@@ -0,0 +1,109 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_sha256.h
+ * @brief Simple wrapper for selection of built-in/external SHA-256
+ * implementation
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA256_H
+#define MHD_SHA256_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_SUPPORT_SHA256
+#error This file must be used only when SHA-256 is enabled
+#endif
+#ifndef MHD_SHA256_EXTR
+# include "sha256_int.h"
+#else /* MHD_SHA256_EXTR */
+# include "sha256_ext.h"
+#endif /* MHD_SHA256_EXTR */
+
+#ifndef mhd_SHA256_DIGEST_SIZE
+/**
+ * Size of SHA-256 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+# define mhd_SHA256_DIGEST_SIZE (32)
+#endif /* ! mhd_SHA256_DIGEST_SIZE */
+
+#ifndef MHD_SHA256_EXTR
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+# define mhd_Sha256Ctx mhd_Sha256CtxInt
+#else /* MHD_SHA256_EXTR */
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+# define mhd_Sha256Ctx mhd_Sha256CtxExt
+#endif /* MHD_SHA256_EXTR */
+
+#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 SHA-512/256 calculation, return digest, reset hash calculation.
+ */
+# define mhd_SHA256_finish_reset(ctx,digest) \
+ (mhd_SHA256_finish (ctx,digest), mhd_SHA256_reset (ctx))
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+# define mhd_SHA256_finish_deinit(ctx,digest) \
+ (mhd_SHA256_finish (ctx,digest), mhd_SHA256_deinit (ctx))
+#else /* mhd_SHA256_HAS_FINISH_RESET */
+# define mhd_SHA256_reset(ctx) ((void) 0)
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+# define mhd_SHA256_finish_deinit(ctx,digest) \
+ (mhd_SHA256_finish_reset (ctx,digest), mhd_SHA256_deinit (ctx))
+#endif /* mhd_SHA256_HAS_FINISH_RESET */
+
+#ifndef mhd_SHA256_HAS_DEINIT
+# define mhd_SHA256_deinit(ctx) ((void) 0)
+#endif /* HAVE_SHA256_DEINIT */
+
+#ifdef mhd_SHA256_HAS_EXT_ERROR
+#define mhd_SHA256_has_err(ctx) (0 != ((ctx)->ext_error))
+#else /* ! mhd_SHA256_HAS_EXT_ERROR */
+#define mhd_SHA256_has_err(ctx) (((void) (ctx)), ! ! 0)
+#endif /* ! mhd_SHA512_256_HAS_EXT_ERROR */
+
+/* Sanity checks */
+
+#if ! defined(mhd_SHA256_HAS_FINISH_RESET) && ! defined(mhd_SHA256_HAS_FINISH)
+#error Required mhd_SHA256_finish_reset() or mhd_SHA256_finish()
+#endif /* ! mhd_SHA256_HAS_FINISH_RESET && ! mhd_SHA256_HAS_FINISH */
+
+#endif /* MHD_SHA256_H */
diff --git a/src/mhd2/mhd_sha512_256.h b/src/mhd2/mhd_sha512_256.h
@@ -0,0 +1,110 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_sha512_256.h
+ * @brief Simple wrapper for selection of built-in/external SHA-512/256
+ * implementation
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA512_256_H
+#define MHD_SHA512_256_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_SUPPORT_SHA512_256
+#error This file must be used only when SHA-512/256 is enabled
+#endif
+#ifndef MHD_SHA512_256_EXTR
+# include "sha512_256_int.h"
+#else /* MHD_SHA512_256_EXTR */
+# include "sha512_256_ext.h"
+#endif /* MHD_SHA512_256_EXTR */
+
+#ifndef mhd_SHA512_256_DIGEST_SIZE
+/**
+ * Size of SHA-512/256 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+# define mhd_SHA512_256_DIGEST_SIZE (32)
+#endif /* ! mhd_SHA512_256_DIGEST_SIZE */
+
+#ifndef MHD_SHA512_256_EXTR
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+# define mhd_Sha512_256Ctx mhd_Sha512_256CtxInt
+#else /* MHD_SHA512_256_EXTR */
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+# define mhd_Sha512_256Ctx mhd_Sha512_256CtxExt
+#endif /* MHD_SHA512_256_EXTR */
+
+#ifndef mhd_SHA512_256_HAS_INIT_ONE_TIME
+/**
+ * Setup and prepare ctx for hash calculation
+ */
+# define mhd_SHA512_256_init_one_time(ctx) mhd_SHA512_256_init (ctx)
+#endif /* ! mhd_SHA512_256_HAS_INIT_ONE_TIME */
+
+#ifndef mhd_SHA512_256_HAS_DEINIT
+# define mhd_SHA512_256_deinit(ctx) ((void) 0)
+#endif /* HAVE_SHA512_256_DEINIT */
+
+#ifndef mhd_SHA512_256_HAS_FINISH_RESET
+/**
+ * Re-use the same ctx for the new hashing after digest calculated
+ */
+# define mhd_SHA512_256_reset(ctx) mhd_SHA512_256_init (ctx)
+/**
+ * Finalise hash calculation, return digest, reset hash calculation.
+ */
+# define mhd_SHA512_256_finish_reset(ctx,digest) \
+ (mhd_SHA512_256_finish (ctx,digest), mhd_SHA512_256_reset (ctx))
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+# define mhd_SHA512_256_finish_deinit(ctx,digest) \
+ (mhd_SHA512_256_finish (ctx,digest), mhd_SHA512_256_deinit (ctx))
+#else /* mhd_SHA512_256_HAS_FINISH_RESET */
+# define mhd_SHA512_256_reset(ctx) ((void) 0)
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+# define mhd_SHA512_256_finish_deinit(ctx,digest) \
+ (mhd_SHA512_256_finish_reset (ctx,digest), mhd_SHA512_256_deinit (ctx))
+#endif /* mhd_SHA512_256_HAS_FINISH_RESET */
+
+/* Sanity checks */
+
+#if ! defined(mhd_SHA512_256_HAS_FINISH_RESET) && \
+ ! defined(mhd_SHA512_256_HAS_FINISH)
+#error Required mhd_SHA512_256_finish_reset() or mhd_SHA512_256_finish()
+#endif /* ! mhd_SHA512_256_HAS_FINISH_RESET && ! mhd_SHA512_256_HAS_FINISH */
+
+#ifdef mhd_SHA512_256_HAS_EXT_ERROR
+#define mhd_SHA512_256_has_err(ctx) (0 != ((ctx)->ext_error))
+#else /* ! mhd_SHA512_256_HAS_EXT_ERROR */
+#define mhd_SHA512_256_has_err(ctx) (((void) (ctx)), ! ! 0)
+#endif /* ! mhd_SHA512_256_HAS_EXT_ERROR */
+
+#endif /* MHD_SHA512_256_H */
diff --git a/src/mhd2/mhd_str.c b/src/mhd2/mhd_str.c
@@ -1944,7 +1944,7 @@ mhd_str_pct_decode_in_place_lenient (char *restrict str,
}
-#ifdef DAUTH_SUPPORT
+#ifdef MHD_SUPPORT_AUTH_DIGEST
MHD_INTERNAL bool
mhd_str_equal_quoted_bin_n (const char *quoted,
size_t quoted_len,
@@ -2005,9 +2005,9 @@ mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
}
-#endif /* DAUTH_SUPPORT */
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
-#if defined(DAUTH_SUPPORT) || defined(HAVE_POST_PARSER)
+#if defined(MHD_SUPPORT_AUTH_DIGEST) || defined(HAVE_POST_PARSER)
MHD_INTERNAL size_t
mhd_str_unquote (const char *quoted,
@@ -2034,11 +2034,13 @@ mhd_str_unquote (const char *quoted,
}
-#endif /* DAUTH_SUPPORT HAVE_POST_PARSER */
+#endif /* MHD_SUPPORT_AUTH_DIGEST HAVE_POST_PARSER */
-#if defined(DAUTH_SUPPORT) || defined(BAUTH_SUPPORT)
+#if defined(MHD_SUPPORT_AUTH_DIGEST) || defined(MHD_SUPPORT_AUTH_BASIC)
-MHD_INTERNAL size_t
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (1,2)
+MHD_FN_PAR_OUT_SIZE_ (3,4) size_t
mhd_str_quote (const char *unquoted,
size_t unquoted_len,
char *result,
@@ -2095,9 +2097,9 @@ mhd_str_quote (const char *unquoted,
}
-#endif /* DAUTH_SUPPORT || BAUTH_SUPPORT */
+#endif /* MHD_SUPPORT_AUTH_DIGEST || MHD_SUPPORT_AUTH_BASIC */
-#ifdef BAUTH_SUPPORT
+#ifdef MHD_SUPPORT_AUTH_BASIC
/*
* MHD_BASE64_FUNC_VERSION
@@ -2154,7 +2156,7 @@ base64_char_to_value_ (uint8_t c)
#endif /* MHD_BASE64_FUNC_VERSION == 1 */
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
MHD_INTERNAL size_t
@@ -2326,12 +2328,12 @@ mhd_base64_to_bin_n (const char *base64,
}
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE
#undef mhd_base64_map_type
-#endif /* BAUTH_SUPPORT */
+#endif /* MHD_SUPPORT_AUTH_BASIC */
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
diff --git a/src/mhd2/mhd_str.h b/src/mhd2/mhd_str.h
@@ -598,7 +598,7 @@ MHD_INTERNAL size_t
mhd_str_pct_decode_in_place_lenient (char *restrict str,
bool *restrict broken_encoding);
-#ifdef DAUTH_SUPPORT
+#ifdef MHD_SUPPORT_AUTH_DIGEST
/**
* Check two strings for equality, "unquoting" the first string from quoted
* form as specified by RFC7230#section-3.2.6 and RFC7694#quoted.strings.
@@ -689,9 +689,9 @@ mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
#define mhd_str_equal_caseless_quoted_s_bin_n(q,l,u) \
mhd_str_equal_caseless_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
-#endif /* DAUTH_SUPPORT */
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
-#if defined(DAUTH_SUPPORT) || defined(HAVE_POST_PARSER)
+#if defined(MHD_SUPPORT_AUTH_DIGEST) || defined(HAVE_POST_PARSER)
/**
* Convert string from quoted to unquoted form as specified by
@@ -713,9 +713,9 @@ mhd_str_unquote (const char *quoted,
size_t quoted_len,
char *result);
-#endif /* DAUTH_SUPPORT HAVE_POST_PARSER */
+#endif /* MHD_SUPPORT_AUTH_DIGEST HAVE_POST_PARSER */
-#if defined(DAUTH_SUPPORT) || defined(BAUTH_SUPPORT)
+#if defined(MHD_SUPPORT_AUTH_DIGEST) || defined(MHD_SUPPORT_AUTH_BASIC)
/**
* Convert string from unquoted to quoted form as specified by
@@ -736,11 +736,12 @@ MHD_INTERNAL size_t
mhd_str_quote (const char *unquoted,
size_t unquoted_len,
char *result,
- size_t buf_size);
+ size_t buf_size)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (1,2) MHD_FN_PAR_OUT_SIZE_ (3,4);
-#endif /* DAUTH_SUPPORT || BAUTH_SUPPORT */
+#endif /* MHD_SUPPORT_AUTH_DIGEST || MHD_SUPPORT_AUTH_BASIC */
-#ifdef BAUTH_SUPPORT
+#ifdef MHD_SUPPORT_AUTH_BASIC
/**
* Returns the maximum possible size of the Base64 decoded data.
@@ -775,7 +776,7 @@ mhd_base64_to_bin_n (const char *base64,
void *bin,
size_t bin_size);
-#endif /* BAUTH_SUPPORT */
+#endif /* MHD_SUPPORT_AUTH_BASIC */
/**
diff --git a/src/mhd2/mhd_str_types.h b/src/mhd2/mhd_str_types.h
@@ -40,7 +40,7 @@
struct MHD_String
{
/**
- * Number of characters in @e str, not counting 0-termination.
+ * Number of characters in @e cstr, not counting 0-termination.
*/
size_t len;
@@ -58,7 +58,7 @@ struct MHD_String
struct MHD_StringNullable
{
/**
- * Number of characters in @e str, not counting 0-termination.
+ * Number of characters in @e cstr, not counting 0-termination.
* If @a cstr is NULL, it must be zero.
*/
size_t len;
diff --git a/src/mhd2/post_parser_funcs.c b/src/mhd2/post_parser_funcs.c
@@ -401,8 +401,8 @@ init_post_parse_data (struct MHD_Connection *restrict c)
break;
case MHD_HTTP_POST_ENCODING_OTHER:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
+ break;
}
}
@@ -1326,7 +1326,6 @@ parse_post_urlenc (struct MHD_Connection *restrict c,
reset_parse_field_data_urlenc (p_data);
continue; /* Process the next char */
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
break;
}
@@ -1418,7 +1417,7 @@ parse_post_mpart (struct MHD_Connection *restrict c,
size_t *restrict pdata_size,
char *restrict buf)
{
- const int discp_lvl = c->daemon->req_cfg.strictnees;
+ const int discp_lvl = c->daemon->req_cfg.strictness;
const bool bare_lf_as_crlf = (-2 >= discp_lvl); /* Bare LF termination is dangerous when used in "multipart/form-data" */
struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
struct mhd_PostParserMPartFormData *const mf = &(p_data->e_d.m_form); /**< the current "form-data" parsing details */
@@ -2069,7 +2068,6 @@ parse_post_mpart (struct MHD_Connection *restrict c,
c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
return true;
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
break;
}
@@ -2146,7 +2144,7 @@ parse_post_text (struct MHD_Connection *restrict c,
size_t *restrict pdata_size,
char *restrict buf)
{
- const int discp_lvl = c->daemon->req_cfg.strictnees;
+ const int discp_lvl = c->daemon->req_cfg.strictness;
/* Treat bare LF as the end of the line.
The same logic used here as for parsing HTTP headers.
Bare LF is processed as the end of the line or rejected as broken
@@ -2291,7 +2289,6 @@ parse_post_text (struct MHD_Connection *restrict c,
reset_parse_field_data_text (p_data);
continue; /* Process the next char */
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
enc_broken = true;
break;
@@ -2425,7 +2422,6 @@ mhd_stream_post_parse (struct MHD_Connection *restrict c,
break;
case MHD_HTTP_POST_ENCODING_OTHER:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
p_data->parse_result =
MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
@@ -2570,7 +2566,6 @@ check_post_leftovers_urlenc (struct MHD_Connection *restrict c,
case mhd_POST_UENC_ST_AT_EQ:
case mhd_POST_UENC_ST_AT_AMPRSND:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
return false;
@@ -2707,7 +2702,6 @@ check_post_leftovers_mpart (struct MHD_Connection *restrict c,
case mhd_POST_MPART_ST_VALUE_END_FOUND:
case mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
return false;
@@ -2839,7 +2833,6 @@ check_post_leftovers_text (struct MHD_Connection *restrict c,
case mhd_POST_TEXT_ST_AT_LF_BARE:
case mhd_POST_TEXT_ST_AT_CR:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
return false;
@@ -2907,7 +2900,6 @@ check_post_leftovers (struct MHD_Connection *restrict c)
c->rq.cntn.lbuf.data);
case MHD_HTTP_POST_ENCODING_OTHER:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
p_data->parse_result =
MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
diff --git a/src/mhd2/request_auth_get.c b/src/mhd2/request_auth_get.c
@@ -0,0 +1,147 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/request_auth_get.c
+ * @brief The implementation of the request Authorization header parsing helper
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_assert.h"
+#include "mhd_unreachable.h"
+#include "mhd_cntnr_ptr.h"
+#include "mhd_str_macros.h"
+
+#include "mhd_str_types.h"
+#include "mhd_request.h"
+#include "mhd_connection.h"
+#include "mhd_daemon.h"
+
+#include "mhd_str.h"
+
+#include "request_auth_get.h"
+
+MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (3) bool
+mhd_request_get_auth_header_value (struct MHD_Request *restrict request,
+ enum mhd_AuthHeaderKind auth_type,
+ struct MHD_String *restrict header_value)
+{
+ static const struct MHD_String hdr_name = mhd_MSTR_INIT ("Authorization");
+#ifdef MHD_SUPPORT_AUTH_BASIC
+ static const struct MHD_String prefix_basic = mhd_MSTR_INIT ("Basic");
+#endif
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ static const struct MHD_String prefix_digest = mhd_MSTR_INIT ("Digest");
+#endif
+ const int strict_lvl =
+ mhd_CNTNR_PTR (request, \
+ struct MHD_Connection, rq)->daemon->req_cfg.strictness;
+ const bool allow_tab_as_sep = (-2 >= strict_lvl);
+ const char *prefix_str;
+ size_t prefix_len;
+ struct mhd_RequestField *f;
+ size_t p_start;
+
+ mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \
+ mhd_CNTNR_PTR (request, struct MHD_Connection, rq)->stage);
+
+ switch (auth_type)
+ {
+#ifdef MHD_SUPPORT_AUTH_BASIC
+ case mhd_AUTH_HDR_BASIC:
+ prefix_str = prefix_basic.cstr;
+ prefix_len = prefix_basic.len;
+ break;
+#endif
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ case mhd_AUTH_HDR_DIGEST:
+ prefix_str = prefix_digest.cstr;
+ prefix_len = prefix_digest.len;
+ break;
+#endif
+#ifdef MHD_ENUMS_NEED_TRAILING_VALUE
+ case mhd_AUTH_HDR_KIND_SENTINEL:
+#endif
+ default:
+ mhd_UNREACHABLE ();
+ return false;
+ }
+
+ for (f = mhd_DLINKEDL_GET_FIRST (request, fields); NULL != f;
+ f = mhd_DLINKEDL_GET_NEXT (f, fields))
+ {
+ if (hdr_name.len != f->field.nv.name.len)
+ continue;
+ if (MHD_VK_HEADER != f->field.kind)
+ continue;
+ if (prefix_len > f->field.nv.name.len)
+ continue;
+ if (! mhd_str_equal_caseless_bin_n (hdr_name.cstr,
+ f->field.nv.name.cstr,
+ hdr_name.len))
+ continue;
+ if (! mhd_str_equal_caseless_bin_n (prefix_str,
+ f->field.nv.value.cstr,
+ prefix_len))
+ continue;
+ /* Match only if the search token string is the full header value or
+ the search token is followed by space */
+ if (prefix_len == f->field.nv.name.len)
+ {
+ header_value->cstr = f->field.nv.value.cstr + f->field.nv.name.len;
+ header_value->len = 0;
+ return true; /* Success exit point */
+ }
+ if (' ' == f->field.nv.value.cstr[prefix_len])
+ break;
+ /* Note: RFC 7235 (Section 2.1) only allows the space character.
+ However, as a slight violation of the specifications, a tab character
+ is also recognised here for additional flexibility and
+ uniformity (tabs are supported as separators between parameters). */
+ if (allow_tab_as_sep &&
+ ('\t' == f->field.nv.value.cstr[prefix_len]))
+ break;
+ }
+ if (NULL == f)
+ return false; /* Failure exit point */
+
+ mhd_assert (prefix_len + 1 <= f->field.nv.name.len);
+ /* Skip leading whitespaces */
+ for (p_start = prefix_len + 1; p_start < f->field.nv.name.len; ++p_start)
+ {
+ if ((' ' != f->field.nv.value.cstr[p_start]) &&
+ ('\t' != f->field.nv.value.cstr[p_start]))
+ break;
+ }
+ header_value->cstr = f->field.nv.value.cstr + p_start;
+ header_value->len = f->field.nv.value.len - p_start;
+
+ mhd_assert (0 == header_value->cstr[header_value->len]);
+ mhd_assert ((0 != header_value->len) && \
+ "Trailing header whitespaces must be already stripped");
+
+ return true; /* Success exit point */
+}
diff --git a/src/mhd2/request_auth_get.h b/src/mhd2/request_auth_get.h
@@ -0,0 +1,79 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/request_auth_get.h
+ * @brief The declaration of the request Authorization header parsing helper
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_REQUEST_AUTH_GET_H
+#define MHD_REQUEST_AUTH_GET_H 1
+
+#include "mhd_sys_options.h"
+
+#if ! defined(MHD_SUPPORT_AUTH_BASIC) && ! defined(MHD_SUPPORT_AUTH_DIGEST)
+#error Authorization header parsing must be enabled
+#endif
+
+#include "sys_bool_type.h"
+
+struct MHD_String; /* forward declaration */
+struct MHD_Request; /* forward declaration */
+
+/**
+ * The type of Authorization header to look for
+ */
+enum MHD_FIXED_ENUM_ mhd_AuthHeaderKind
+{
+#ifdef MHD_SUPPORT_AUTH_BASIC
+ mhd_AUTH_HDR_BASIC,
+#endif
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ mhd_AUTH_HDR_DIGEST,
+#endif
+#ifdef MHD_ENUMS_NEED_TRAILING_VALUE
+ mhd_AUTH_HDR_KIND_SENTINEL
+#endif
+};
+
+
+/**
+ * Get value of the specific Authorization header
+ *
+ * This function supports multiple types of the Authorization headers in
+ * one request, but does not parse each header for several types of
+ * Authorization. This should not be a limitation as RFC 7235 (section 4.2)
+ * allows only one type of Authorization in each request.
+ *
+ * @param request the request to use
+ * @param auth_type the type of Authorization to get
+ * @param[out] header_value the value of the Authorization header, excluding
+ * the prefix (type) and initial whitespaces
+ * @return 'true' if requested header found,
+ * 'false' otherwise (@a header_value not set)
+ */
+MHD_INTERNAL bool
+mhd_request_get_auth_header_value (struct MHD_Request *restrict request,
+ enum mhd_AuthHeaderKind auth_type,
+ struct MHD_String *restrict header_value)
+MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (3);
+
+#endif /* ! MHD_REQUEST_AUTH_GET_H */
diff --git a/src/mhd2/request_get_info.c b/src/mhd2/request_get_info.c
@@ -0,0 +1,188 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/request_get_info.c
+ * @brief The implementation of MHD_request_get_info_*() functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_cntnr_ptr.h"
+
+#include "mhd_request.h"
+#include "mhd_connection.h"
+
+#ifdef MHD_SUPPORT_AUTH_BASIC
+# include "auth_basic.h"
+#endif
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+# include "auth_digest.h"
+#endif
+
+#include "mhd_public_api.h"
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_ (3)
+MHD_FN_PURE_ enum MHD_StatusCode
+MHD_request_get_info_fixed_sz (
+ struct MHD_Request *MHD_RESTRICT request,
+ enum MHD_RequestInfoFixedType info_type,
+ union MHD_RequestInfoFixedData *MHD_RESTRICT return_value,
+ size_t return_value_size)
+{
+ switch (info_type)
+ {
+ case MHD_REQUEST_INFO_FIXED_STREAM:
+ mhd_assert (0 && "Not implemented yet");
+ break;
+ case MHD_REQUEST_INFO_FIXED_CONNECTION:
+ if (sizeof(return_value->v_connection) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return_value->v_connection =
+ mhd_CNTNR_PTR (request, struct MHD_Connection, rq);
+ return MHD_SC_OK;
+ case MHD_REQUEST_INFO_FIXED_DAEMON:
+ if (sizeof(return_value->v_daemon) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return_value->v_daemon =
+ mhd_CNTNR_PTR (request, struct MHD_Connection, rq)->daemon;
+ return MHD_SC_OK;
+ case MHD_REQUEST_INFO_FIXED_HTTP_VER:
+ if (sizeof(return_value->v_http_ver) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return_value->v_http_ver = request->http_ver;
+ return MHD_SC_OK;
+ case MHD_REQUEST_INFO_FIXED_HTTP_METHOD:
+ if (sizeof(return_value->v_http_method) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ mhd_assert (mhd_HTTP_METHOD_NO_METHOD != request->http_mthd);
+ return_value->v_http_method = (enum MHD_HTTP_Method) request->http_mthd;
+ return MHD_SC_OK;
+ case MHD_REQUEST_INFO_FIXED_SENTINEL:
+ default:
+ break;
+ }
+
+ return MHD_SC_INFO_GET_TYPE_UNKNOWN;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_ (3) enum MHD_StatusCode
+MHD_request_get_info_dynamic_sz (
+ struct MHD_Request *MHD_RESTRICT request,
+ enum MHD_RequestInfoDynamicType info_type,
+ union MHD_RequestInfoDynamicData *MHD_RESTRICT return_value,
+ size_t return_value_size)
+{
+ if (mhd_HTTP_STAGE_REQ_RECV_FINISHED <
+ mhd_CNTNR_PTR (request, struct MHD_Connection, rq)->stage)
+ return MHD_SC_TOO_LATE;
+ if ((mhd_HTTP_STAGE_HEADERS_PROCESSED >
+ mhd_CNTNR_PTR (request, struct MHD_Connection, rq)->stage) &&
+ (MHD_REQUEST_INFO_DYNAMIC_NUMBER_GET_PARAMS < info_type))
+ return MHD_SC_TOO_EARLY;
+ switch (info_type)
+ {
+ case MHD_REQUEST_INFO_DYNAMIC_HTTP_METHOD_STR:
+ mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVED <= \
+ mhd_CNTNR_PTR (request, struct MHD_Connection, rq)->stage);
+ if (sizeof(return_value->v_str) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return_value->v_str = request->method;
+ return MHD_SC_OK;
+ case MHD_REQUEST_INFO_DYNAMIC_URI:
+ if (mhd_HTTP_STAGE_REQ_LINE_RECEIVED <
+ mhd_CNTNR_PTR (request, struct MHD_Connection, rq)->stage)
+ return MHD_SC_TOO_EARLY;
+ if (sizeof(return_value->v_str) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return_value->v_str.cstr = request->url;
+ return_value->v_str.len = request->url_len;
+ return MHD_SC_OK;
+ case MHD_REQUEST_INFO_DYNAMIC_NUMBER_GET_PARAMS:
+ case MHD_REQUEST_INFO_DYNAMIC_NUMBER_COOKIES:
+ case MHD_REQUEST_INFO_DYNAMIC_NUMBER_POST_PARAMS:
+ case MHD_REQUEST_INFO_DYNAMIC_UPLOAD_PRESENT:
+ case MHD_REQUEST_INFO_DYNAMIC_UPLOAD_SIZE_TOTAL:
+ case MHD_REQUEST_INFO_DYNAMIC_UPLOAD_SIZE_RECIEVED:
+ case MHD_REQUEST_INFO_DYNAMIC_UPLOAD_SIZE_TO_RECIEVE:
+ case MHD_REQUEST_INFO_DYNAMIC_UPLOAD_SIZE_PROCESSED:
+ case MHD_REQUEST_INFO_DYNAMIC_UPLOAD_SIZE_TO_PROCESS:
+ mhd_assert (0 && "Not implemented yet");
+ break;
+ case MHD_REQUEST_INFO_DYNAMIC_HEADER_SIZE:
+ if (sizeof(return_value->v_sizet) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return_value->v_sizet = request->header_size;
+ return MHD_SC_OK;
+ case MHD_REQUEST_INFO_DYNAMIC_APP_CONTEXT:
+ mhd_assert (0 && "Not implemented yet");
+ break;
+ case MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME:
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ if (sizeof(return_value->v_auth_basic_creds) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return mhd_request_get_auth_digest_username (request,
+ &(return_value->
+ v_auth_digest_uname));
+#else /* ! MHD_SUPPORT_AUTH_DIGEST */
+ return MHD_SC_FEATURE_DISABLED;
+#endif /* ! MHD_SUPPORT_AUTH_DIGEST */
+ break;
+ case MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO:
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ if (sizeof(return_value->v_auth_basic_creds) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return mhd_request_get_auth_digest_info (request,
+ &(return_value->
+ v_auth_digest_info));
+#else /* ! MHD_SUPPORT_AUTH_DIGEST */
+ return MHD_SC_FEATURE_DISABLED;
+#endif /* ! MHD_SUPPORT_AUTH_DIGEST */
+ break;
+ case MHD_REQUEST_INFO_DYNAMIC_AUTH_BASIC_CREDS:
+#ifdef MHD_SUPPORT_AUTH_BASIC
+ if (sizeof(return_value->v_auth_basic_creds) > return_value_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ return mhd_request_get_auth_basic_creds (request,
+ &(return_value->
+ v_auth_basic_creds));
+#else /* MHD_SUPPORT_AUTH_BASIC */
+ return MHD_SC_FEATURE_DISABLED;
+#endif /* MHD_SUPPORT_AUTH_BASIC */
+ mhd_assert (0 && "Not implemented yet");
+ break;
+ case MHD_REQUEST_INFO_DYNAMIC_SENTINEL:
+ default:
+ break;
+ }
+
+ return MHD_SC_INFO_GET_TYPE_UNKNOWN;
+}
diff --git a/src/mhd2/request_get_value.c b/src/mhd2/request_get_value.c
@@ -61,7 +61,9 @@ mhd_request_get_value_n (struct MHD_Request *restrict request,
{
if ((key_len == f->field.nv.name.len) &&
(0 != (kind & f->field.kind)) &&
- (0 == memcmp (key, f->field.nv.name.cstr, key_len)))
+ mhd_str_equal_caseless_bin_n (key,
+ f->field.nv.name.cstr,
+ key_len))
return &(f->field.nv.value);
}
}
@@ -75,7 +77,9 @@ mhd_request_get_value_n (struct MHD_Request *restrict request,
f = mhd_DLINKEDL_GET_NEXT (f, post_fields))
{
if ((key_len == f->field.name.len) &&
- (0 == memcmp (key, buf + f->field.name.pos, key_len)))
+ mhd_str_equal_caseless_bin_n (key,
+ buf + f->field.name.pos,
+ key_len))
{
f->field_for_app.value.cstr =
(0 == f->field.value.pos) ?
diff --git a/src/mhd2/respond_with_error.c b/src/mhd2/respond_with_error.c
@@ -73,7 +73,8 @@ respond_with_error_len (struct MHD_Connection *c,
mhd_DLINKEDL_INIT_LIST (&(c->rq), fields);
c->rq.version = NULL;
- c->rq.method = NULL;
+ c->rq.method.len = 0;
+ c->rq.method.cstr = NULL;
c->rq.url = NULL;
c->continue_message_write_offset = 0;
if (0 != c->read_buffer_size)
diff --git a/src/mhd2/response_add_header.c b/src/mhd2/response_add_header.c
@@ -112,14 +112,13 @@ response_add_header_int (struct MHD_Response *response,
if (! response_add_header_no_check (response, name_len, name,
value_len, value))
- return MHD_SC_RESPONSE_HEADER_MALLOC_FAILED;
+ return MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED;
return MHD_SC_OK;
}
MHD_EXTERN_
-MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode
MHD_response_add_header (struct MHD_Response *response,
@@ -129,6 +128,8 @@ MHD_response_add_header (struct MHD_Response *response,
bool need_unlock;
enum MHD_StatusCode res;
+ if (NULL == response)
+ return MHD_SC_RESP_POINTER_NULL;
if (response->frozen)
return MHD_SC_TOO_LATE;
diff --git a/src/mhd2/response_auth_basic.c b/src/mhd2/response_auth_basic.c
@@ -0,0 +1,181 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/response_auth_basic.c
+ * @brief The definitions of MHD_response_add_auth_basic_challenge() function
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "mhd_response.h"
+#include "mhd_locks.h"
+#include "mhd_str_types.h"
+
+#include <string.h>
+#include "sys_malloc.h"
+
+#include "mhd_str_macros.h"
+#include "mhd_str.h"
+
+#include "mhd_public_api.h"
+
+
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) enum MHD_StatusCode
+response_add_auth_basic_challenge_int (struct MHD_Response *restrict response,
+ const char *restrict realm,
+ enum MHD_Bool prefer_utf8)
+{
+ static const struct MHD_String hdr_name =
+ mhd_MSTR_INIT (MHD_HTTP_HEADER_WWW_AUTHENTICATE);
+ static const struct MHD_String prefix =
+ mhd_MSTR_INIT ("Basic realm=\"");
+ static const struct MHD_String add_charset =
+ mhd_MSTR_INIT (", charset=\"UTF-8\"");
+ const size_t realm_len = strlen (realm);
+ char *val_str;
+ size_t hval_maxlen;
+ size_t suffix_len;
+ size_t realm_quoted_len;
+ size_t pos;
+ struct mhd_ResponseHeader *new_hdr;
+
+ if ((NULL != memchr (realm, '\n', realm_len)) ||
+ (NULL != memchr (realm, '\r', realm_len)))
+ return MHD_SC_RESP_HEADER_VALUE_INVALID;
+ if (0 == realm_len)
+ return MHD_SC_RESP_HEADER_VALUE_INVALID;
+
+ suffix_len = 1; /* for (closing) quote char */
+ if (MHD_NO != prefer_utf8)
+ suffix_len += add_charset.len;
+ hval_maxlen = prefix.len + realm_len * 2 + suffix_len;
+
+ new_hdr = (struct mhd_ResponseHeader *)
+ malloc (sizeof(struct mhd_ResponseHeader)
+ + hdr_name.len + 1 + hval_maxlen + 1);
+
+ if (NULL == new_hdr)
+ return MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED;
+
+ /* Set the name of the header */
+ memcpy ((char *) (new_hdr + 1),
+ hdr_name.cstr,
+ hdr_name.len + 1);
+
+ /* Set the value of the header */
+ val_str = ((char *) (new_hdr + 1)) + hdr_name.len + 1;
+ memcpy (val_str, prefix.cstr, prefix.len);
+ pos = prefix.len;
+ realm_quoted_len = mhd_str_quote (realm,
+ realm_len,
+ val_str + pos,
+ hval_maxlen - prefix.len - suffix_len);
+ mhd_assert (0 != realm_quoted_len);
+ pos += realm_quoted_len;
+ val_str[pos++] = '\"';
+ mhd_assert (pos + suffix_len <= hval_maxlen);
+
+ if (MHD_NO != prefer_utf8)
+ {
+ mhd_assert (pos + add_charset.len <= hval_maxlen);
+ memcpy (val_str + pos, add_charset.cstr, add_charset.len);
+ pos += add_charset.len;
+ }
+ val_str[pos] = 0; /* Zero terminate the result */
+ mhd_assert (pos <= hval_maxlen);
+
+ if (1)
+ { /* Try to shrink malloc'ed area */
+ void *new_ptr;
+ new_ptr = realloc (new_hdr,
+ sizeof(struct mhd_ResponseHeader)
+ + hdr_name.len + 1 + pos + 1);
+ /* Just use the old pointer if realloc() failed */
+ if (NULL != new_ptr)
+ new_hdr = (struct mhd_ResponseHeader *) new_ptr;
+ }
+
+ new_hdr->name.cstr = (char *) (new_hdr + 1);
+ new_hdr->name.len = hdr_name.len;
+ mhd_assert (0 == memcmp (hdr_name.cstr, \
+ new_hdr->name.cstr, \
+ new_hdr->name.len + 1));
+
+ new_hdr->value.cstr = ((char *) (new_hdr + 1)) + hdr_name.len + 1;
+ new_hdr->value.len = pos;
+ mhd_assert (0 == memcmp (prefix.cstr, \
+ new_hdr->value.cstr, \
+ prefix.len));
+ mhd_assert (0 == new_hdr->value.cstr[new_hdr->value.len]);
+
+ mhd_DLINKEDL_INIT_LINKS (new_hdr, headers);
+ mhd_DLINKEDL_INS_LAST (response, new_hdr, headers);
+
+ response->cfg.has_bauth = true;
+
+ return MHD_SC_OK;
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (2)
+MHD_FN_PAR_CSTR_ (2) enum MHD_StatusCode
+MHD_response_add_auth_basic_challenge (
+ struct MHD_Response *MHD_RESTRICT response,
+ const char *realm,
+ enum MHD_Bool prefer_utf8)
+{
+ bool need_unlock;
+ enum MHD_StatusCode res;
+
+ if (NULL == response)
+ return MHD_SC_RESP_POINTER_NULL;
+ if (response->frozen)
+ return MHD_SC_TOO_LATE;
+ if (MHD_HTTP_STATUS_UNAUTHORIZED != response->sc)
+ return MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE;
+
+ if (response->reuse.reusable)
+ {
+ need_unlock = true;
+ if (! mhd_mutex_lock (&(response->reuse.settings_lock)))
+ return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
+ mhd_assert (1 == mhd_atomic_counter_get (&(response->reuse.counter)));
+ }
+ else
+ need_unlock = false;
+
+ if (response->frozen) /* Re-check with the lock held */
+ res = MHD_SC_TOO_LATE;
+ else if (response->cfg.has_bauth)
+ res = MHD_SC_RESP_HEADERS_CONFLICT;
+ else
+ res = response_add_auth_basic_challenge_int (response,
+ realm,
+ prefer_utf8);
+
+ if (need_unlock)
+ mhd_mutex_unlock_chk (&(response->reuse.settings_lock));
+
+ return res;
+}
diff --git a/src/mhd2/response_auth_digest.c b/src/mhd2/response_auth_digest.c
@@ -0,0 +1,523 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/response_auth_digest.c
+ * @brief The definitions of MHD_response_add_auth_basic_challenge() function
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+#include "mhd_unreachable.h"
+#include "mhd_assert.h"
+
+#include "mhd_digest_auth_data.h"
+
+#include "mhd_response.h"
+#include "mhd_locks.h"
+#include "mhd_str_types.h"
+#include <string.h>
+#include "sys_malloc.h"
+
+#include "mhd_str_macros.h"
+#include "mhd_str.h"
+
+#include "mhd_public_api.h"
+
+#include "response_auth_digest.h"
+#include "mhd_auth_digest_hdr.h"
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1) void
+mhd_response_remove_auth_digest_headers (struct MHD_Response*response)
+{
+ struct mhd_RespAuthDigestHeader *hdr_d;
+
+ for (hdr_d = mhd_DLINKEDL_GET_LAST (response, auth_d_hdrs);
+ NULL != hdr_d;
+ hdr_d = mhd_DLINKEDL_GET_LAST (response, auth_d_hdrs))
+ {
+ mhd_DLINKEDL_DEL (response, hdr_d, auth_d_hdrs);
+ free (hdr_d);
+ }
+}
+
+
+/**
+ * Create and add Digest Auth challenge header with specified algorithm
+ * @param response the response to update
+ * @param rlm the realm to use
+ * @param opq the "opaque" string to use
+ * @param dmn the "domain" string to use
+ * @param indicate_stale whether to indicate "stale" nonce
+ * @param qop_none whether to use RFC 2069 subset only
+ * @param algo the algorithm to use
+ * @param userhash_support whether to indicate support for "userhash"
+ * @param prefer_utf8 whether to indicate UTF-8 support
+ * @return #MHD_SC_OK on success,
+ * error code otherwise
+ */
+static MHD_FN_PAR_NONNULL_ALL_ enum MHD_StatusCode
+response_add_auth_digest_challenge_alg (
+ struct MHD_Response *restrict response,
+ const struct MHD_String *restrict rlm,
+ const struct MHD_StringNullable *restrict opq,
+ const struct MHD_StringNullable *restrict dmn,
+ enum MHD_Bool indicate_stale,
+ bool qop_none,
+ enum MHD_DigestAuthAlgo algo,
+ enum MHD_Bool userhash_support,
+ enum MHD_Bool prefer_utf8)
+{
+ static const struct MHD_String empty_str =
+ mhd_MSTR_INIT ("");
+ static const struct MHD_String hdr_pref_realm_pref =
+ mhd_MSTR_INIT (MHD_HTTP_HEADER_WWW_AUTHENTICATE ": " \
+ mhd_AUTH_DIGEST_SCHEME " realm=\"");
+ static const struct MHD_String qop_str =
+ mhd_MSTR_INIT (", qop=\"auth\"");
+#ifdef MHD_SUPPORT_MD5
+ static const struct MHD_String algo_mh5_str =
+ mhd_MSTR_INIT (", algorithm=MD5");
+#endif
+#ifdef MHD_SUPPORT_SHA256
+ static const struct MHD_String algo_sha256_str =
+ mhd_MSTR_INIT (", algorithm=SHA-256");
+#endif
+#ifdef MHD_SUPPORT_SHA512_256
+ static const struct MHD_String algo_sha512_256_str =
+ mhd_MSTR_INIT (", algorithm=SHA-512-256");
+#endif
+ static const struct MHD_String nonce_str =
+ mhd_MSTR_INIT (", nonce=\"" \
+ "################################" \
+ "################################" \
+ "########" \
+ "\"");
+ static size_t nonce_off = 9; /* Position of nonce value in the nonce_str */
+ static const struct MHD_String opaque_pref =
+ mhd_MSTR_INIT (", opaque=\"");
+ static const struct MHD_String domain_pref =
+ mhd_MSTR_INIT (", domain=\"");
+ static const struct MHD_String stale_str =
+ mhd_MSTR_INIT (", stale=true");
+ static const struct MHD_String charset_str =
+ mhd_MSTR_INIT (", charset=UTF-8");
+ static const struct MHD_String userhash_str =
+ mhd_MSTR_INIT (", userhash=true");
+
+ /* Header content:
+ + Scheme name and space;
+ + realm, quoted;
+ + qop="auth", quoted (optional);
+ + algorithm, NOT quoted (optional);
+ + nonce (placeholder), quoted;
+ + opaque, quoted (optional);
+ + domain, quoted (optional);
+ + stale=true (optional);
+ + charset=UTF-8 (optional);
+ + userhash=true (optional).
+ */
+
+ struct MHD_String algo_str;
+ char *hdr_str;
+ size_t hdr_maxlen;
+ struct mhd_RespAuthDigestHeader *new_hdr;
+ size_t pos;
+ size_t elm_len;
+
+ mhd_assert ('#' == nonce_str.cstr[nonce_off]);
+ mhd_assert ('#' == nonce_str.cstr[nonce_off + mhd_AUTH_DIGEST_NONCE_LEN - 1]);
+ mhd_assert ('"' == nonce_str.cstr[nonce_off - 1]);
+ mhd_assert ('"' == nonce_str.cstr[nonce_off + mhd_AUTH_DIGEST_NONCE_LEN]);
+
+#ifdef MHD_SUPPORT_MD5
+ if (MHD_DIGEST_AUTH_ALGO_MD5 == algo)
+ algo_str = qop_none ? empty_str : algo_mh5_str;
+#endif
+#ifdef MHD_SUPPORT_SHA256
+ else if (MHD_DIGEST_AUTH_ALGO_SHA256 == algo)
+ algo_str = algo_sha256_str;
+#endif
+#ifdef MHD_SUPPORT_SHA512_256
+ else if (MHD_DIGEST_AUTH_ALGO_SHA512_256 == algo)
+ algo_str = algo_sha512_256_str;
+#endif
+ else
+ mhd_UNREACHABLE ();
+
+ /* ** Calculate the maximum length of the header string ** */
+ hdr_maxlen = 0;
+
+ /* realm */
+ hdr_maxlen += hdr_pref_realm_pref.len;
+ hdr_maxlen += rlm->len * 2; /* Double length for quoting */
+ hdr_maxlen += 1; /* closing quote (") */
+
+ /* qop */
+ hdr_maxlen += qop_str.len;
+
+ /* algorithm */
+ hdr_maxlen += algo_str.len;
+
+ /* nonce */
+ hdr_maxlen += nonce_str.len;
+
+ /* opaque */
+ hdr_maxlen += opaque_pref.len;
+ hdr_maxlen += opq->len * 2; /* Double length for quoting */
+ hdr_maxlen += 1; /* closing quote (") */
+
+ /* domain */
+ hdr_maxlen += domain_pref.len;
+ hdr_maxlen += dmn->len;
+ hdr_maxlen += 1; /* closing quote (") */
+
+ /* stale */
+ hdr_maxlen += stale_str.len;
+
+ /* charset */
+ hdr_maxlen += charset_str.len;
+
+ /* userhash */
+ hdr_maxlen += userhash_str.len;
+
+ /* CRLF */
+ hdr_maxlen += 2;
+
+ /* ** Allocate ** */
+ new_hdr = (struct mhd_RespAuthDigestHeader *)
+ malloc (sizeof(struct mhd_RespAuthDigestHeader *)
+ + hdr_maxlen + 1);
+ if (NULL == new_hdr)
+ return MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED;
+ hdr_str = (char *) (new_hdr + 1);
+
+ /* ** Build the header ** */
+ pos = 0;
+
+ /* realm */
+ memcpy (hdr_str + pos,
+ hdr_pref_realm_pref.cstr,
+ hdr_pref_realm_pref.len);
+ pos += hdr_pref_realm_pref.len;
+ elm_len = mhd_str_quote (rlm->cstr,
+ rlm->len,
+ hdr_str + pos,
+ hdr_maxlen - pos);
+ mhd_assert (0 != elm_len);
+ pos += elm_len;
+ hdr_str[pos++] = '"';
+
+ /* qop */
+ if (! qop_none)
+ {
+ memcpy (hdr_str + pos,
+ qop_str.cstr,
+ qop_str.len);
+ pos += qop_str.len;
+ }
+
+ /* algorithm */
+ if (0 != algo_str.len)
+ {
+ memcpy (hdr_str + pos,
+ algo_str.cstr,
+ algo_str.len);
+ pos += algo_str.len;
+ }
+
+ /* nonce */
+ memcpy (hdr_str + pos,
+ nonce_str.cstr,
+ nonce_str.len);
+ new_hdr->nonce_pos = pos + nonce_off;
+ pos += nonce_str.len;
+
+ /* opaque */
+ if (0 != opq->len)
+ {
+ memcpy (hdr_str + pos,
+ opaque_pref.cstr,
+ opaque_pref.len);
+ pos += opaque_pref.len;
+ elm_len = mhd_str_quote (opq->cstr,
+ opq->len,
+ hdr_str + pos,
+ hdr_maxlen - pos);
+ mhd_assert (0 != elm_len);
+ pos += elm_len;
+ hdr_str[pos++] = '"';
+ }
+
+ /* domain */
+ if (0 != dmn->len)
+ {
+ memcpy (hdr_str + pos,
+ domain_pref.cstr,
+ domain_pref.len);
+ pos += domain_pref.len;
+ memcpy (hdr_str + pos,
+ dmn->cstr,
+ dmn->len);
+ pos += dmn->len;
+ hdr_str[pos++] = '"';
+ }
+
+ /* stale */
+ if (MHD_NO != indicate_stale)
+ {
+ memcpy (hdr_str + pos,
+ stale_str.cstr,
+ stale_str.len);
+ pos += stale_str.len;
+ }
+
+ /* charset */
+ if ((! qop_none) &&
+ (MHD_NO != prefer_utf8))
+ {
+ memcpy (hdr_str + pos,
+ charset_str.cstr,
+ charset_str.len);
+ pos += charset_str.len;
+ }
+
+ /* userhash */
+ if ((! qop_none) &&
+ (MHD_NO != userhash_support))
+ {
+ memcpy (hdr_str + pos,
+ userhash_str.cstr,
+ userhash_str.len);
+ pos += userhash_str.len;
+ }
+
+ /* CRLF */
+ hdr_str[pos++] = '\r';
+ hdr_str[pos++] = '\n';
+
+ mhd_assert (pos <= hdr_maxlen);
+ hdr_str[pos] = 0; /* Zero-terminate the string */
+
+ if (1)
+ { /* Try to shrink malloc'ed area */
+ void *new_ptr;
+ new_ptr = realloc (new_hdr,
+ sizeof(struct mhd_RespAuthDigestHeader)
+ + pos + 1);
+ /* Just use the old pointer if realloc() failed */
+ if (NULL != new_ptr)
+ new_hdr = (struct mhd_RespAuthDigestHeader *) new_ptr;
+ }
+
+ new_hdr->hdr.cstr = (char *) (new_hdr + 1);
+ new_hdr->hdr.len = pos;
+ mhd_assert (0 == \
+ memcmp (new_hdr->hdr.cstr, \
+ MHD_HTTP_HEADER_WWW_AUTHENTICATE ": ", \
+ mhd_SSTR_LEN (MHD_HTTP_HEADER_WWW_AUTHENTICATE ": ")));
+ mhd_assert (0 == new_hdr->hdr.cstr[new_hdr->hdr.len]);
+ mhd_assert ('\r' == new_hdr->hdr.cstr[new_hdr->hdr.len - 2]);
+ mhd_assert ('\n' == new_hdr->hdr.cstr[new_hdr->hdr.len - 1]);
+
+ mhd_DLINKEDL_INIT_LINKS (new_hdr, auth_d_hdrs);
+ mhd_DLINKEDL_INS_LAST (response, new_hdr, auth_d_hdrs);
+
+ return MHD_SC_OK;
+}
+
+
+/**
+ * Create and add Digest Auth challenge headers for all specified algorithms
+ * @param response the response to update
+ * @param realm the real to use
+ * @param opaque the "opaque" string, could be NULL
+ * @param domain the "domain" string, could be NULL
+ * @param indicate_stale whether to indicate "stale" nonce
+ * @param mqop the QOP values to use
+ * @param malgo the algorithms to use
+ * @param userhash_support whether to indicate support for "userhash"
+ * @param prefer_utf8 whether to indicate UTF-8 support
+ * @return #MHD_SC_OK on success,
+ * error code otherwise
+ */
+static MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4) enum MHD_StatusCode
+response_add_auth_digest_challenge_int (struct MHD_Response *restrict response,
+ const char *restrict realm,
+ const char *restrict opaque,
+ const char *restrict domain,
+ enum MHD_Bool indicate_stale,
+ enum MHD_DigestAuthMultiQOP mqop,
+ enum MHD_DigestAuthMultiAlgo malgo,
+ enum MHD_Bool userhash_support,
+ enum MHD_Bool prefer_utf8)
+{
+ const struct MHD_String rlm = { strlen (realm), realm };
+ const struct MHD_StringNullable opq =
+ { (NULL != opaque ? strlen (opaque) : 0), opaque };
+ const struct MHD_StringNullable dmn =
+ { (NULL != domain ? strlen (domain) : 0), domain };
+ const bool qop_none =
+ (0 != (MHD_DIGEST_AUTH_QOP_NONE & ((unsigned int) mqop)));
+ enum MHD_StatusCode res;
+
+ /* Check validity of the input data */
+
+ if (0 == rlm.len)
+ return MHD_SC_RESP_HEADER_VALUE_INVALID; /* Failure exit point */
+ if ((NULL != memchr (rlm.cstr, '\n', rlm.len)) ||
+ (NULL != memchr (rlm.cstr, '\r', rlm.len)))
+ return MHD_SC_RESP_HEADER_VALUE_INVALID; /* Failure exit point */
+
+ if ((0 != opq.len) &&
+ ((NULL != memchr (opq.cstr, '\n', opq.len)) ||
+ (NULL != memchr (opq.cstr, '\r', opq.len))))
+ return MHD_SC_RESP_HEADER_VALUE_INVALID; /* Failure exit point */
+
+ if ((0 != dmn.len) &&
+ ((NULL != memchr (dmn.cstr, '\n', dmn.len)) ||
+ (NULL != memchr (dmn.cstr, '\r', dmn.len)) ||
+ (NULL != memchr (dmn.cstr, '"', dmn.len))))
+ return MHD_SC_RESP_HEADER_VALUE_INVALID; /* Failure exit point */
+
+ if (0 == (MHD_DIGEST_AUTH_ALGO_NON_SESSION & ((unsigned int) malgo)))
+ return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED; /* Failure exit point */
+
+ if (0 == ((MHD_DIGEST_AUTH_QOP_NONE | MHD_DIGEST_AUTH_QOP_AUTH)
+ & ((unsigned int) mqop)))
+ return MHD_SC_AUTH_DIGEST_QOP_NOT_SUPPORTED; /* Failure exit point */
+
+ res = MHD_SC_OK;
+
+#ifdef MHD_SUPPORT_MD5
+ if ((MHD_SC_OK == res) &&
+ (0 != (MHD_DIGEST_BASE_ALGO_MD5 & ((unsigned int) malgo))))
+ res = response_add_auth_digest_challenge_alg (response,
+ &rlm,
+ &opq,
+ &dmn,
+ indicate_stale,
+ qop_none,
+ MHD_DIGEST_AUTH_ALGO_MD5,
+ userhash_support,
+ prefer_utf8);
+#endif
+#ifdef MHD_SUPPORT_SHA256
+ if ((MHD_SC_OK == res) &&
+ (0 != (MHD_DIGEST_BASE_ALGO_SHA256 & ((unsigned int) malgo))))
+ res = response_add_auth_digest_challenge_alg (response,
+ &rlm,
+ &opq,
+ &dmn,
+ indicate_stale,
+ qop_none,
+ MHD_DIGEST_AUTH_ALGO_SHA256,
+ userhash_support,
+ prefer_utf8);
+#endif
+#ifdef MHD_SUPPORT_SHA512_256
+ if ((MHD_SC_OK == res) &&
+ (0 != (MHD_DIGEST_BASE_ALGO_SHA512_256 & ((unsigned int) malgo))))
+ res = response_add_auth_digest_challenge_alg (
+ response,
+ &rlm,
+ &opq,
+ &dmn,
+ indicate_stale,
+ qop_none,
+ MHD_DIGEST_AUTH_ALGO_SHA512_256,
+ userhash_support,
+ prefer_utf8);
+#endif
+
+ if (MHD_SC_OK != res)
+ {
+ mhd_response_remove_auth_digest_headers (response);
+ return res; /* Failure exit point */
+ }
+
+ if (NULL == mhd_DLINKEDL_GET_FIRST (response, auth_d_hdrs))
+ return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED; /* Failure exit point */
+
+ return MHD_SC_OK; /* Success exit point */
+}
+
+
+MHD_EXTERN_
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4) enum MHD_StatusCode
+MHD_response_add_auth_digest_challenge (
+ struct MHD_Response *MHD_RESTRICT response,
+ const char *MHD_RESTRICT realm,
+ const char *MHD_RESTRICT opaque,
+ const char *MHD_RESTRICT domain,
+ enum MHD_Bool indicate_stale,
+ enum MHD_DigestAuthMultiQOP mqop,
+ enum MHD_DigestAuthMultiAlgo malgo,
+ enum MHD_Bool userhash_support,
+ enum MHD_Bool prefer_utf8)
+{
+ bool need_unlock;
+ enum MHD_StatusCode res;
+
+ if (NULL == response)
+ return MHD_SC_RESP_POINTER_NULL;
+ if (response->frozen)
+ return MHD_SC_TOO_LATE;
+ if (MHD_HTTP_STATUS_UNAUTHORIZED != response->sc)
+ return MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE;
+
+ if (response->reuse.reusable)
+ {
+ need_unlock = true;
+ if (! mhd_mutex_lock (&(response->reuse.settings_lock)))
+ return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
+ mhd_assert (1 == mhd_atomic_counter_get (&(response->reuse.counter)));
+ }
+ else
+ need_unlock = false;
+
+ if (response->frozen) /* Re-check with the lock held */
+ res = MHD_SC_TOO_LATE;
+ else if (NULL != mhd_DLINKEDL_GET_FIRST (response, auth_d_hdrs))
+ res = MHD_SC_RESP_HEADERS_CONFLICT;
+ else
+ res = response_add_auth_digest_challenge_int (response,
+ realm,
+ opaque,
+ domain,
+ indicate_stale,
+ mqop,
+ malgo,
+ userhash_support,
+ prefer_utf8);
+
+ if (need_unlock)
+ mhd_mutex_unlock_chk (&(response->reuse.settings_lock));
+
+ return res;
+}
diff --git a/src/mhd2/response_auth_digest.h b/src/mhd2/response_auth_digest.h
@@ -0,0 +1,42 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/response_auth_digest.h
+ * @brief The declaration of the Digest Auth response header helper functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPONSE_AUTH_DIGEST_H
+#define MHD_RESPONSE_AUTH_DIGEST_H 1
+
+#include "mhd_sys_options.h"
+
+struct MHD_Response; /* forward declaration */
+
+/**
+ * Remove all Digest Auth headers (if any) from the response
+ * @param response the response to clean
+ */
+MHD_INTERNAL void
+mhd_response_remove_auth_digest_headers (struct MHD_Response*response)
+MHD_FN_PAR_NONNULL_ (1);
+
+#endif /* ! MHD_RESPONSE_AUTH_DIGEST_H */
diff --git a/src/mhd2/response_destroy.c b/src/mhd2/response_destroy.c
@@ -40,6 +40,10 @@
#include "response_funcs.h"
#include "response_from.h"
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+# include "response_auth_digest.h"
+#endif
+
#define mhd_RESPONSE_DESTOYED "Attempt to use destroyed response, " \
"re-use non-reusable response or wrong MHD_Response pointer"
@@ -52,6 +56,9 @@
static MHD_FN_PAR_NONNULL_ (1) void
response_full_deinit (struct MHD_Response *restrict r)
{
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ mhd_response_remove_auth_digest_headers (r);
+#endif
mhd_response_remove_all_headers (r);
if (NULL != r->special_resp.spec_hdr)
free (r->special_resp.spec_hdr);
@@ -109,7 +116,8 @@ MHD_response_destroy (struct MHD_Response *response)
free (response->settings);
#ifndef NDEBUG
/* Decrement counter to avoid triggering assert in deinit function */
- mhd_assert (0 == mhd_atomic_counter_dec_get (&(response->reuse.counter)));
+ if (response->reuse.reusable)
+ mhd_assert (0 == mhd_atomic_counter_dec_get (&(response->reuse.counter)));
#endif
response_full_deinit (response);
return;
diff --git a/src/mhd2/response_from.c b/src/mhd2/response_from.c
@@ -68,12 +68,15 @@ response_create_basic (enum MHD_HTTP_StatusCode sc,
{
#ifndef HAVE_NULL_PTR_ALL_ZEROS
mhd_DLINKEDL_INIT_LIST (r, headers);
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ mhd_DLINKEDL_INIT_LIST (r, auth_d_hdrs);
+#endif
r->free.cb = NULL;
r->free.cls = NULL;
r->special_resp.spec_hdr = NULL;
- s->termination_callback.v_term_cb = NULL;
- s->termination_callback.v_term_cb_cls = NULL;
+ s->termination_callback.v_ended_cb = NULL;
+ s->termination_callback.v_ended_cb_cls = NULL;
#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
r->sc = sc;
diff --git a/src/mhd2/sha256_ext.c b/src/mhd2/sha256_ext.c
@@ -0,0 +1,101 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2023 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 mhd_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 may return initialisation error and set the handle at the
+ same time. Such handle cannot be used for calculations.
+ Note: GnuTLS may also return an error and NOT set the handle. */
+ gnutls_free (ctx->handle);
+ ctx->handle = NULL;
+ }
+
+ /* If handle is NULL, the error must be set */
+ mhd_assert ((NULL != ctx->handle) || (0 != ctx->ext_error));
+ /* If error is set, the handle must be NULL */
+ mhd_assert ((0 == ctx->ext_error) || (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 mhd_Sha256CtxExt *ctx,
+ size_t size,
+ const uint8_t *data)
+{
+ if (0 == ctx->ext_error)
+ ctx->ext_error = gnutls_hash (ctx->handle, data, size);
+}
+
+
+/**
+ * Finalise SHA-256 calculation, return digest, reset hash calculation.
+ *
+ * @param ctx the calculation context
+ * @param[out] digest set to the hash, must be #mhd_SHA256_DIGEST_SIZE bytes
+ */
+void
+mhd_SHA256_finish_reset (struct mhd_Sha256CtxExt *ctx,
+ uint8_t digest[mhd_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 mhd_Sha256CtxExt *ctx)
+{
+ if (NULL != ctx->handle)
+ gnutls_hash_deinit (ctx->handle, NULL);
+}
diff --git a/src/mhd2/sha256_ext.h b/src/mhd2/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 mhd_SHA256_DIGEST_SIZE (32)
+
+/* Actual declaration is in GnuTLS lib header */
+struct hash_hd_st;
+
+/**
+ * Indicates that struct mhd_Sha256CtxExt has 'ext_error'
+ */
+#define mhd_SHA256_HAS_EXT_ERROR 1
+
+/**
+ * SHA-256 calculation context
+ */
+struct mhd_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 mhd_Sha256CtxExt *ctx);
+
+
+/**
+ * SHA-256 process portion of bytes.
+ *
+ * @param ctx the calculation context
+ * @param size number of bytes in @a data
+ * @param data bytes to add to hash
+ */
+void
+mhd_SHA256_update (struct mhd_Sha256CtxExt *ctx,
+ size_t size,
+ const uint8_t *data);
+
+
+/**
+ * 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 #mhd_SHA256_DIGEST_SIZE bytes
+ */
+void
+mhd_SHA256_finish_reset (struct mhd_Sha256CtxExt *ctx,
+ uint8_t digest[mhd_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 mhd_Sha256CtxExt *ctx);
+
+#endif /* MHD_SHA256_EXT_H */
diff --git a/src/mhd2/sha256_int.c b/src/mhd2/sha256_int.c
@@ -0,0 +1,556 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2019-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/sha256.c
+ * @brief Calculation of SHA-256 digest as defined in FIPS PUB 180-4 (2015)
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+#include <string.h>
+#include "mhd_bithelpers.h"
+#include "mhd_assert.h"
+
+#include "sha256_int.h"
+
+MHD_INTERNAL void MHD_FN_PAR_NONNULL_ALL_
+mhd_SHA256_init (struct mhd_Sha256CtxInt *ctx)
+{
+ /* Initial hash values, see FIPS PUB 180-4 paragraph 5.3.3 */
+ /* First thirty-two bits of the fractional parts of the square
+ * roots of the first eight prime numbers: 2, 3, 5, 7, 11, 13,
+ * 17, 19." */
+ ctx->H[0] = UINT32_C (0x6a09e667);
+ ctx->H[1] = UINT32_C (0xbb67ae85);
+ ctx->H[2] = UINT32_C (0x3c6ef372);
+ ctx->H[3] = UINT32_C (0xa54ff53a);
+ ctx->H[4] = UINT32_C (0x510e527f);
+ ctx->H[5] = UINT32_C (0x9b05688c);
+ ctx->H[6] = UINT32_C (0x1f83d9ab);
+ ctx->H[7] = UINT32_C (0x5be0cd19);
+
+ /* Initialise number of bytes. */
+ ctx->count = 0;
+}
+
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
+
+static MHD_FN_PAR_NONNULL_ALL_ void
+sha256_transform (uint32_t H[mhd_SHA256_DIGEST_SIZE_WORDS],
+ const void *restrict data)
+{
+ /* Working variables,
+ see FIPS PUB 180-4 paragraph 6.2. */
+ uint32_t a = H[0];
+ uint32_t b = H[1];
+ uint32_t c = H[2];
+ uint32_t d = H[3];
+ uint32_t e = H[4];
+ uint32_t f = H[5];
+ uint32_t g = H[6];
+ uint32_t h = H[7];
+
+ /* Data buffer, used as cyclic buffer.
+ See FIPS PUB 180-4 paragraphs 5.2.1, 6.2. */
+ uint32_t W[16];
+
+#ifndef mhd_GET_32BIT_BE_UNALIGNED
+ if (0 != (((uintptr_t) data) % mhd_UINT32_ALIGN))
+ {
+ /* Copy the unaligned input data to the aligned buffer */
+ memcpy (W, data, mhd_SHA256_BLOCK_SIZE);
+ /* The W[] buffer itself will be used as the source of the data,
+ * but data will be reloaded in correct bytes order during
+ * the next steps */
+ data = (const void *) W;
+ }
+#endif /* mhd_GET_32BIT_BE_UNALIGNED */
+
+ /* 'Ch' and 'Maj' macro functions are defined with
+ widely-used optimization.
+ See FIPS PUB 180-4 formulae 4.2, 4.3. */
+#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) )
+#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
+ /* Unoptimized (original) versions: */
+/* #define Ch(x,y,z) ( ( (x) & (y) ) ^ ( ~(x) & (z) ) ) */
+/* #define Maj(x,y,z) ( ((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)) ) */
+
+ /* Four 'Sigma' macro functions.
+ See FIPS PUB 180-4 formulae 4.4, 4.5, 4.6, 4.7. */
+#define SIG0(x) (mhd_ROTR32 ((x), 2) ^ mhd_ROTR32 ((x), 13) ^ \
+ mhd_ROTR32 ((x), 22) )
+#define SIG1(x) (mhd_ROTR32 ((x), 6) ^ mhd_ROTR32 ((x), 11) ^ \
+ mhd_ROTR32 ((x), 25) )
+#define sig0(x) (mhd_ROTR32 ((x), 7) ^ mhd_ROTR32 ((x), 18) ^ \
+ ((x) >> 3) )
+#define sig1(x) (mhd_ROTR32 ((x), 17) ^ mhd_ROTR32 ((x),19) ^ \
+ ((x) >> 10) )
+
+ /* One step of SHA-256 computation,
+ see FIPS PUB 180-4 paragraph 6.2.2 step 3.
+ * Note: this macro updates working variables in-place, without rotation.
+ * Note: first (vH += SIG1(vE) + Ch(vE,vF,vG) + kt + wt) equals T1 in FIPS PUB 180-4 paragraph 6.2.2 step 3.
+ second (vH += SIG0(vA) + Maj(vE,vF,vC) equals T1 + T2 in FIPS PUB 180-4 paragraph 6.2.2 step 3.
+ * Note: 'wt' must be used exactly one time in this macro as it change other data as well
+ every time when used. */
+#define SHA2STEP32(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
+ (vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt)); \
+ (vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0)
+
+ /* Get value of W(t) from input data buffer,
+ See FIPS PUB 180-4 paragraph 6.2.
+ Input data must be read in big-endian bytes order,
+ see FIPS PUB 180-4 paragraph 3.1.2. */
+ /* Use cast to (const void*) to mute compiler alignment warning,
+ * data was already aligned in previous step */
+#define GET_W_FROM_DATA(buf,t) \
+ mhd_GET_32BIT_BE ((const void*) (((const uint8_t*) (buf)) + \
+ (t) * mhd_SHA256_BYTES_IN_WORD))
+
+ /* 'W' generation and assignment for 16 <= t <= 63.
+ See FIPS PUB 180-4 paragraph 6.2.2.
+ As only last 16 'W' are used in calculations, it is possible to
+ use 16 elements array of W as cyclic buffer.
+ * Note: ((t-16)&0xf) have same value as (t&0xf) */
+#define Wgen(w,t) ( (w)[(t - 16) & 0xf] + sig1 ((w)[((t) - 2) & 0xf]) \
+ + (w)[((t) - 7) & 0xf] + sig0 ((w)[((t) - 15) & 0xf]) )
+
+#ifndef MHD_FAVOR_SMALL_CODE
+
+ /* Note: instead of using K constants as array, all K values are specified
+ individually for each step, see FIPS PUB 180-4 paragraph 4.2.2 for
+ K values. */
+ /* Note: instead of reassigning all working variables on each step,
+ variables are rotated for each step:
+ SHA2STEP32(a, b, c, d, e, f, g, h, K[0], data[0]);
+ SHA2STEP32(h, a, b, c, d, e, f, g, K[1], data[1]);
+ so current 'vD' will be used as 'vE' on next step,
+ current 'vH' will be used as 'vA' on next step. */
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+ if ((const void *) W == data)
+ {
+ /* The input data is already in the cyclic data buffer W[] in correct bytes
+ order. */
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x428a2f98), W[0]);
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x71374491), W[1]);
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0xb5c0fbcf), W[2]);
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0xe9b5dba5), W[3]);
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x3956c25b), W[4]);
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x59f111f1), W[5]);
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x923f82a4), W[6]);
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0xab1c5ed5), W[7]);
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0xd807aa98), W[8]);
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x12835b01), W[9]);
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x243185be), W[10]);
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x550c7dc3), W[11]);
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x72be5d74), W[12]);
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x80deb1fe), W[13]);
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x9bdc06a7), W[14]);
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0xc19bf174), W[15]);
+ }
+ else /* Combined with the next 'if' */
+#endif /* mhd_BYTE_ORDER == mhd_BIG_ENDIAN */
+ if (1)
+ {
+ /* During first 16 steps, before making any calculations on each step,
+ the W element is read from input data buffer as big-endian value and
+ stored in array of W elements. */
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x428a2f98), W[0] = \
+ GET_W_FROM_DATA (data, 0));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x71374491), W[1] = \
+ GET_W_FROM_DATA (data, 1));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0xb5c0fbcf), W[2] = \
+ GET_W_FROM_DATA (data, 2));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0xe9b5dba5), W[3] = \
+ GET_W_FROM_DATA (data, 3));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x3956c25b), W[4] = \
+ GET_W_FROM_DATA (data, 4));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x59f111f1), W[5] = \
+ GET_W_FROM_DATA (data, 5));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x923f82a4), W[6] = \
+ GET_W_FROM_DATA (data, 6));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0xab1c5ed5), W[7] = \
+ GET_W_FROM_DATA (data, 7));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0xd807aa98), W[8] = \
+ GET_W_FROM_DATA (data, 8));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x12835b01), W[9] = \
+ GET_W_FROM_DATA (data, 9));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x243185be), W[10] = \
+ GET_W_FROM_DATA (data, 10));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x550c7dc3), W[11] = \
+ GET_W_FROM_DATA (data, 11));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x72be5d74), W[12] = \
+ GET_W_FROM_DATA (data, 12));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x80deb1fe), W[13] = \
+ GET_W_FROM_DATA (data, 13));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x9bdc06a7), W[14] = \
+ GET_W_FROM_DATA (data, 14));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0xc19bf174), W[15] = \
+ GET_W_FROM_DATA (data, 15));
+ }
+
+ /* During last 48 steps, before making any calculations on each step,
+ current W element is generated from other W elements of the cyclic buffer
+ and the generated value is stored back in the cyclic buffer. */
+ /* Note: instead of using K constants as array, all K values are specified
+ individually for each step, see FIPS PUB 180-4 paragraph 4.2.2 for K values. */
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0xe49b69c1), W[16 & 0xf] = \
+ Wgen (W,16));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0xefbe4786), W[17 & 0xf] = \
+ Wgen (W,17));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x0fc19dc6), W[18 & 0xf] = \
+ Wgen (W,18));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x240ca1cc), W[19 & 0xf] = \
+ Wgen (W,19));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x2de92c6f), W[20 & 0xf] = \
+ Wgen (W,20));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x4a7484aa), W[21 & 0xf] = \
+ Wgen (W,21));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x5cb0a9dc), W[22 & 0xf] = \
+ Wgen (W,22));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0x76f988da), W[23 & 0xf] = \
+ Wgen (W,23));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x983e5152), W[24 & 0xf] = \
+ Wgen (W,24));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0xa831c66d), W[25 & 0xf] = \
+ Wgen (W,25));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0xb00327c8), W[26 & 0xf] = \
+ Wgen (W,26));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0xbf597fc7), W[27 & 0xf] = \
+ Wgen (W,27));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0xc6e00bf3), W[28 & 0xf] = \
+ Wgen (W,28));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0xd5a79147), W[29 & 0xf] = \
+ Wgen (W,29));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x06ca6351), W[30 & 0xf] = \
+ Wgen (W,30));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0x14292967), W[31 & 0xf] = \
+ Wgen (W,31));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x27b70a85), W[32 & 0xf] = \
+ Wgen (W,32));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x2e1b2138), W[33 & 0xf] = \
+ Wgen (W,33));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x4d2c6dfc), W[34 & 0xf] = \
+ Wgen (W,34));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x53380d13), W[35 & 0xf] = \
+ Wgen (W,35));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x650a7354), W[36 & 0xf] = \
+ Wgen (W,36));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x766a0abb), W[37 & 0xf] = \
+ Wgen (W,37));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x81c2c92e), W[38 & 0xf] = \
+ Wgen (W,38));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0x92722c85), W[39 & 0xf] = \
+ Wgen (W,39));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0xa2bfe8a1), W[40 & 0xf] = \
+ Wgen (W,40));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0xa81a664b), W[41 & 0xf] = \
+ Wgen (W,41));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0xc24b8b70), W[42 & 0xf] = \
+ Wgen (W,42));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0xc76c51a3), W[43 & 0xf] = \
+ Wgen (W,43));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0xd192e819), W[44 & 0xf] = \
+ Wgen (W,44));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0xd6990624), W[45 & 0xf] = \
+ Wgen (W,45));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0xf40e3585), W[46 & 0xf] = \
+ Wgen (W,46));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0x106aa070), W[47 & 0xf] = \
+ Wgen (W,47));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x19a4c116), W[48 & 0xf] = \
+ Wgen (W,48));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x1e376c08), W[49 & 0xf] = \
+ Wgen (W,49));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x2748774c), W[50 & 0xf] = \
+ Wgen (W,50));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x34b0bcb5), W[51 & 0xf] = \
+ Wgen (W,51));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x391c0cb3), W[52 & 0xf] = \
+ Wgen (W,52));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x4ed8aa4a), W[53 & 0xf] = \
+ Wgen (W,53));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x5b9cca4f), W[54 & 0xf] = \
+ Wgen (W,54));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0x682e6ff3), W[55 & 0xf] = \
+ Wgen (W,55));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x748f82ee), W[56 & 0xf] = \
+ Wgen (W,56));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x78a5636f), W[57 & 0xf] = \
+ Wgen (W,57));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x84c87814), W[58 & 0xf] = \
+ Wgen (W,58));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x8cc70208), W[59 & 0xf] = \
+ Wgen (W,59));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x90befffa), W[60 & 0xf] = \
+ Wgen (W,60));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0xa4506ceb), W[61 & 0xf] = \
+ Wgen (W,61));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0xbef9a3f7), W[62 & 0xf] = \
+ Wgen (W,62));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0xc67178f2), W[63 & 0xf] = \
+ Wgen (W,63));
+#else /* ! MHD_FAVOR_SMALL_CODE */
+ if (1)
+ {
+ unsigned int t;
+ /* K constants array.
+ See FIPS PUB 180-4 paragraph 4.2.2 for K values. */
+ static const uint32_t K[80] =
+ { UINT32_C (0x428a2f98), UINT32_C (0x71374491), UINT32_C (0xb5c0fbcf),
+ UINT32_C (0xe9b5dba5), UINT32_C (0x3956c25b), UINT32_C (0x59f111f1),
+ UINT32_C (0x923f82a4), UINT32_C (0xab1c5ed5), UINT32_C (0xd807aa98),
+ UINT32_C (0x12835b01), UINT32_C (0x243185be), UINT32_C (0x550c7dc3),
+ UINT32_C (0x72be5d74), UINT32_C (0x80deb1fe), UINT32_C (0x9bdc06a7),
+ UINT32_C (0xc19bf174), UINT32_C (0xe49b69c1), UINT32_C (0xefbe4786),
+ UINT32_C (0x0fc19dc6), UINT32_C (0x240ca1cc), UINT32_C (0x2de92c6f),
+ UINT32_C (0x4a7484aa), UINT32_C (0x5cb0a9dc), UINT32_C (0x76f988da),
+ UINT32_C (0x983e5152), UINT32_C (0xa831c66d), UINT32_C (0xb00327c8),
+ UINT32_C (0xbf597fc7), UINT32_C (0xc6e00bf3), UINT32_C (0xd5a79147),
+ UINT32_C (0x06ca6351), UINT32_C (0x14292967), UINT32_C (0x27b70a85),
+ UINT32_C (0x2e1b2138), UINT32_C (0x4d2c6dfc), UINT32_C (0x53380d13),
+ UINT32_C (0x650a7354), UINT32_C (0x766a0abb), UINT32_C (0x81c2c92e),
+ UINT32_C (0x92722c85), UINT32_C (0xa2bfe8a1), UINT32_C (0xa81a664b),
+ UINT32_C (0xc24b8b70), UINT32_C (0xc76c51a3), UINT32_C (0xd192e819),
+ UINT32_C (0xd6990624), UINT32_C (0xf40e3585), UINT32_C (0x106aa070),
+ UINT32_C (0x19a4c116), UINT32_C (0x1e376c08), UINT32_C (0x2748774c),
+ UINT32_C (0x34b0bcb5), UINT32_C (0x391c0cb3), UINT32_C (0x4ed8aa4a),
+ UINT32_C (0x5b9cca4f), UINT32_C (0x682e6ff3), UINT32_C (0x748f82ee),
+ UINT32_C (0x78a5636f), UINT32_C (0x84c87814), UINT32_C (0x8cc70208),
+ UINT32_C (0x90befffa), UINT32_C (0xa4506ceb), UINT32_C (0xbef9a3f7),
+ UINT32_C (0xc67178f2) };
+ /* One step of SHA-256 computation with working variables rotation,
+ see FIPS PUB 180-4 paragraph 6.2.2 step 3.
+ * Note: this version of macro reassign all working variable on
+ each step. */
+#define SHA2STEP32RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
+ uint32_t tmp_h_ = (vH); \
+ SHA2STEP32 ((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt)); \
+ (vH) = (vG); \
+ (vG) = (vF); \
+ (vF) = (vE); \
+ (vE) = (vD); \
+ (vD) = (vC); \
+ (vC) = (vB); \
+ (vB) = (vA); \
+ (vA) = tmp_h_; \
+} \
+ while (0)
+
+ /* During first 16 steps, before making any calculations on each step,
+ the W element is read from input data buffer as big-endian value and
+ stored in array of W elements. */
+ for (t = 0; t < 16; ++t)
+ {
+ SHA2STEP32RV (a, b, c, d, e, f, g, h, K[t], \
+ W[t] = GET_W_FROM_DATA (data, t));
+ }
+
+ /* During last 48 steps, before making any calculations on each step,
+ current W element is generated from other W elements of the cyclic buffer
+ and the generated value is stored back in the cyclic buffer. */
+ for (t = 16; t < 64; ++t)
+ {
+ SHA2STEP32RV (a, b, c, d, e, f, g, h, K[t], W[t & 15] = Wgen (W,t));
+ }
+ }
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+
+ /* Compute intermediate hash.
+ See FIPS PUB 180-4 paragraph 6.2.2 step 4. */
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+ H[5] += f;
+ H[6] += g;
+ H[7] += h;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) void
+mhd_SHA256_update (struct mhd_Sha256CtxInt *restrict ctx,
+ size_t size,
+ const uint8_t *restrict data)
+{
+ unsigned bytes_have; /**< Number of bytes in buffer */
+
+ mhd_assert ((data != NULL) || (size == 0));
+
+#ifndef MHD_FAVOR_SMALL_CODE
+ if (0 == size)
+ return; /* Shortcut, do nothing */
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+ /* Note: (count & (mhd_SHA256_BLOCK_SIZE-1))
+ equals (count % mhd_SHA256_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned) (ctx->count & (mhd_SHA256_BLOCK_SIZE - 1));
+ ctx->count += size;
+
+ if (0 != bytes_have)
+ {
+ unsigned bytes_left = mhd_SHA256_BLOCK_SIZE - bytes_have;
+ if (size >= bytes_left)
+ { /* Combine new data with data in the buffer and
+ process full block. */
+ memcpy (((uint8_t *) ctx->buffer) + bytes_have,
+ data,
+ bytes_left);
+ data += bytes_left;
+ size -= bytes_left;
+ sha256_transform (ctx->H, ctx->buffer);
+ bytes_have = 0;
+ }
+ }
+
+ while (mhd_SHA256_BLOCK_SIZE <= size)
+ { /* Process any full blocks of new data directly,
+ without copying to the buffer. */
+ sha256_transform (ctx->H, data);
+ data += mhd_SHA256_BLOCK_SIZE;
+ size -= mhd_SHA256_BLOCK_SIZE;
+ }
+
+ if (0 != size)
+ { /* Copy incomplete block of new data (if any)
+ to the buffer. */
+ memcpy (((uint8_t *) ctx->buffer) + bytes_have, data, size);
+ }
+}
+
+
+/**
+ * Size of "length" padding addition in bytes.
+ * See FIPS PUB 180-4 paragraph 5.1.1.
+ */
+#define SHA256_SIZE_OF_LEN_ADD (64 / 8)
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_SHA256_finish (struct mhd_Sha256CtxInt *restrict ctx,
+ uint8_t digest[mhd_SHA256_DIGEST_SIZE])
+{
+ uint64_t num_bits; /**< Number of processed bits */
+ unsigned bytes_have; /**< Number of bytes in buffer */
+
+ num_bits = ctx->count << 3;
+ /* Note: (count & (mhd_SHA256_BLOCK_SIZE-1))
+ equal (count % mhd_SHA256_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned) (ctx->count & (mhd_SHA256_BLOCK_SIZE - 1));
+
+ /* Input data must be padded with a single bit "1", then with zeros and
+ the finally the length of data in bits must be added as the final bytes
+ of the last block.
+ See FIPS PUB 180-4 paragraph 5.1.1. */
+
+ /* Data is always processed in form of bytes (not by individual bits),
+ therefore position of first padding bit in byte is always
+ predefined (0x80). */
+ /* Buffer always have space at least for one byte (as full buffers are
+ processed immediately). */
+ ((uint8_t *) ctx->buffer)[bytes_have++] = 0x80;
+
+ if (mhd_SHA256_BLOCK_SIZE - bytes_have < SHA256_SIZE_OF_LEN_ADD)
+ { /* No space in current block to put total length of message.
+ Pad current block with zeros and process it. */
+ if (bytes_have < mhd_SHA256_BLOCK_SIZE)
+ memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
+ mhd_SHA256_BLOCK_SIZE - bytes_have);
+ /* Process full block. */
+ sha256_transform (ctx->H, ctx->buffer);
+ /* Start new block. */
+ bytes_have = 0;
+ }
+
+ /* Pad the rest of the buffer with zeros. */
+ memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
+ mhd_SHA256_BLOCK_SIZE - SHA256_SIZE_OF_LEN_ADD - bytes_have);
+ /* Put the number of bits in processed message as big-endian value. */
+ mhd_PUT_64BIT_BE_UNALIGN (ctx->buffer + mhd_SHA256_BLOCK_SIZE_WORDS - 2,
+ num_bits);
+ /* Process full final block. */
+ sha256_transform (ctx->H, ctx->buffer);
+
+ /* Put final hash/digest in BE mode */
+ if (1)
+ {
+ bool use_tmp_buf_to_align_result;
+
+#if defined(mhd_PUT_32BIT_BE_UNALIGNED)
+ use_tmp_buf_to_align_result = false;
+#elif defined (MHD_FAVOR_SMALL_CODE)
+ use_tmp_buf_to_align_result = true; /* smaller code: eliminated branch below */
+#else
+ use_tmp_buf_to_align_result =
+ (0 != ((uintptr_t) digest) % mhd_UINT32_ALIGN);
+#endif
+ if (use_tmp_buf_to_align_result)
+ {
+ /* If storing of the final result requires aligned address and
+ the destination address is not aligned or compact code is used,
+ store the final digest in aligned temporary buffer first, then
+ copy it to the destination. */
+ uint32_t alig_dgst[mhd_SHA256_DIGEST_SIZE_WORDS];
+ mhd_PUT_32BIT_BE (alig_dgst + 0, ctx->H[0]);
+ mhd_PUT_32BIT_BE (alig_dgst + 1, ctx->H[1]);
+ mhd_PUT_32BIT_BE (alig_dgst + 2, ctx->H[2]);
+ mhd_PUT_32BIT_BE (alig_dgst + 3, ctx->H[3]);
+ mhd_PUT_32BIT_BE (alig_dgst + 4, ctx->H[4]);
+ mhd_PUT_32BIT_BE (alig_dgst + 5, ctx->H[5]);
+ mhd_PUT_32BIT_BE (alig_dgst + 6, ctx->H[6]);
+ mhd_PUT_32BIT_BE (alig_dgst + 7, ctx->H[7]);
+ /* Copy result to unaligned destination address */
+ memcpy (digest, alig_dgst, mhd_SHA256_DIGEST_SIZE);
+ }
+ else
+ {
+ /* Use cast to (void*) here to mute compiler alignment warnings.
+ * Compilers are not smart enough to see that alignment has been checked. */
+ mhd_PUT_32BIT_BE ((void *) (digest + 0 * mhd_SHA256_BYTES_IN_WORD), \
+ ctx->H[0]);
+ mhd_PUT_32BIT_BE ((void *) (digest + 1 * mhd_SHA256_BYTES_IN_WORD), \
+ ctx->H[1]);
+ mhd_PUT_32BIT_BE ((void *) (digest + 2 * mhd_SHA256_BYTES_IN_WORD), \
+ ctx->H[2]);
+ mhd_PUT_32BIT_BE ((void *) (digest + 3 * mhd_SHA256_BYTES_IN_WORD), \
+ ctx->H[3]);
+ mhd_PUT_32BIT_BE ((void *) (digest + 4 * mhd_SHA256_BYTES_IN_WORD), \
+ ctx->H[4]);
+ mhd_PUT_32BIT_BE ((void *) (digest + 5 * mhd_SHA256_BYTES_IN_WORD), \
+ ctx->H[5]);
+ mhd_PUT_32BIT_BE ((void *) (digest + 6 * mhd_SHA256_BYTES_IN_WORD), \
+ ctx->H[6]);
+ mhd_PUT_32BIT_BE ((void *) (digest + 7 * mhd_SHA256_BYTES_IN_WORD), \
+ ctx->H[7]);
+ }
+ }
+
+ /* Erase potentially sensitive data. */
+ memset (ctx, 0, sizeof(struct mhd_Sha256CtxInt));
+}
+
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE
diff --git a/src/mhd2/sha256_int.h b/src/mhd2/sha256_int.h
@@ -0,0 +1,121 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2019-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/sha256.h
+ * @brief Calculation of SHA-256 digest
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA256_INT_H
+#define MHD_SHA256_INT_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+
+/**
+ * Digest is kept internally as 8 32-bit words.
+ */
+#define mhd_SHA256_DIGEST_SIZE_WORDS 8
+
+/**
+ * Number of bits in single SHA-256 word
+ */
+#define mhd_SHA256_WORD_SIZE_BITS 32
+
+/**
+ * Number of bytes in single SHA-256 word
+ * used to process data
+ */
+#define mhd_SHA256_BYTES_IN_WORD (mhd_SHA256_WORD_SIZE_BITS / 8)
+
+/**
+ * Size of SHA-256 digest in bytes
+ */
+#define mhd_SHA256_DIGEST_SIZE \
+ (mhd_SHA256_DIGEST_SIZE_WORDS * mhd_SHA256_BYTES_IN_WORD)
+
+/**
+ * Size of single processing block in bits
+ */
+#define mhd_SHA256_BLOCK_SIZE_BITS 512
+
+/**
+ * Size of single processing block in bytes
+ */
+#define mhd_SHA256_BLOCK_SIZE (mhd_SHA256_BLOCK_SIZE_BITS / 8)
+
+/**
+ * Size of single processing block in bytes
+ */
+#define mhd_SHA256_BLOCK_SIZE_WORDS \
+ (mhd_SHA256_BLOCK_SIZE_BITS / mhd_SHA256_WORD_SIZE_BITS)
+
+
+struct mhd_Sha256CtxInt
+{
+ uint32_t H[mhd_SHA256_DIGEST_SIZE_WORDS]; /**< Intermediate hash value / digest at end of calculation */
+ uint32_t buffer[mhd_SHA256_BLOCK_SIZE_WORDS]; /**< SHA256 input data buffer */
+ uint64_t count; /**< number of bytes, mod 2^64 */
+};
+
+/**
+ * Initialise structure for SHA-256 calculation.
+ *
+ * @param ctx must be a `struct mhd_Sha256CtxInt *`
+ */
+MHD_INTERNAL void
+mhd_SHA256_init (struct mhd_Sha256CtxInt *ctx)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param ctx must be a `struct mhd_Sha256CtxInt *`
+ * @param size number of bytes in @a data
+ * @param data bytes to add to hash
+ */
+MHD_INTERNAL void
+mhd_SHA256_update (struct mhd_Sha256CtxInt *restrict ctx,
+ size_t size,
+ const uint8_t *restrict data)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3, 2);
+
+
+/**
+ * Finalise SHA256 calculation, return digest.
+ *
+ * @param ctx must be a `struct mhd_Sha256CtxInt *`
+ * @param[out] digest set to the hash, must be #mhd_SHA256_DIGEST_SIZE bytes
+ */
+MHD_INTERNAL void
+mhd_SHA256_finish (struct mhd_Sha256CtxInt *restrict ctx,
+ uint8_t digest[mhd_SHA256_DIGEST_SIZE])
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+/**
+ * Indicates that function mhd_SHA256_finish() (without context reset) is available
+ */
+#define mhd_SHA256_HAS_FINISH 1
+
+#endif /* MHD_SHA256_INT_H */
diff --git a/src/mhd2/sha512_256_int.c b/src/mhd2/sha512_256_int.c
@@ -0,0 +1,628 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/sha512_256.c
+ * @brief Calculation of SHA-512/256 digest as defined in FIPS PUB 180-4 (2015)
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+#include <string.h>
+#include "mhd_bithelpers.h"
+#include "mhd_assert.h"
+
+#include "sha512_256_int.h"
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_SHA512_256_init (struct mhd_Sha512_256CtxInt *ctx)
+{
+ /* Initial hash values, see FIPS PUB 180-4 clause 5.3.6.2 */
+ /* Values generated by "IV Generation Function" as described in
+ * clause 5.3.6 */
+ ctx->H[0] = UINT64_C (0x22312194FC2BF72C);
+ ctx->H[1] = UINT64_C (0x9F555FA3C84C64C2);
+ ctx->H[2] = UINT64_C (0x2393B86B6F53B151);
+ ctx->H[3] = UINT64_C (0x963877195940EABD);
+ ctx->H[4] = UINT64_C (0x96283EE2A88EFFE3);
+ ctx->H[5] = UINT64_C (0xBE5E1E2553863992);
+ ctx->H[6] = UINT64_C (0x2B0199FC2C85B8AA);
+ ctx->H[7] = UINT64_C (0x0EB72DDC81C52CA2);
+
+ /* Initialise number of bytes and high part of number of bits. */
+ ctx->count = 0;
+ ctx->count_bits_hi = 0;
+}
+
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
+
+/**
+ * Base of SHA-512/256 transformation.
+ * Gets full 128 bytes block of data and updates hash values;
+ * @param H hash values
+ * @param data the data buffer with #mhd_SHA512_256_BLOCK_SIZE bytes block
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+sha512_256_transform (uint64_t H[mhd_SHA512_256_HASH_SIZE_WORDS],
+ const void *restrict data)
+{
+ /* Working variables,
+ see FIPS PUB 180-4 clause 6.7, 6.4. */
+ uint64_t a = H[0];
+ uint64_t b = H[1];
+ uint64_t c = H[2];
+ uint64_t d = H[3];
+ uint64_t e = H[4];
+ uint64_t f = H[5];
+ uint64_t g = H[6];
+ uint64_t h = H[7];
+
+ /* Data buffer, used as a cyclic buffer.
+ See FIPS PUB 180-4 clause 5.2.2, 6.7, 6.4. */
+ uint64_t W[16];
+
+#ifndef mhd_GET_64BIT_BE_ALLOW_UNALIGNED
+ if (0 != (((uintptr_t) data) % mhd_UINT64_ALIGN))
+ { /* The input data is unaligned */
+ /* Copy the unaligned input data to the aligned buffer */
+ memcpy (W, data, sizeof(W));
+ /* The W[] buffer itself will be used as the source of the data,
+ * but the data will be reloaded in correct bytes order on
+ * the next steps */
+ data = (const void *) W;
+ }
+#endif /* mhd_GET_64BIT_BE_ALLOW_UNALIGNED */
+
+ /* 'Ch' and 'Maj' macro functions are defined with
+ widely-used optimisation.
+ See FIPS PUB 180-4 formulae 4.8, 4.9. */
+#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) )
+#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
+ /* Unoptimized (original) versions: */
+/* #define Ch(x,y,z) ( ( (x) & (y) ) ^ ( ~(x) & (z) ) ) */
+/* #define Maj(x,y,z) ( ((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)) ) */
+
+ /* Four 'Sigma' macro functions.
+ See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */
+#define SIG0(x) \
+ (mhd_ROTR64 ((x), 28) ^ mhd_ROTR64 ((x), 34) ^ mhd_ROTR64 ((x), 39) )
+#define SIG1(x) \
+ (mhd_ROTR64 ((x), 14) ^ mhd_ROTR64 ((x), 18) ^ mhd_ROTR64 ((x), 41) )
+#define sig0(x) \
+ (mhd_ROTR64 ((x), 1) ^ mhd_ROTR64 ((x), 8) ^ ((x) >> 7) )
+#define sig1(x) \
+ (mhd_ROTR64 ((x), 19) ^ mhd_ROTR64 ((x), 61) ^ ((x) >> 6) )
+
+ /* One step of SHA-512/256 computation,
+ see FIPS PUB 180-4 clause 6.4.2 step 3.
+ * Note: this macro updates working variables in-place, without rotation.
+ * Note: the first (vH += SIG1(vE) + Ch(vE,vF,vG) + kt + wt) equals T1 in
+ FIPS PUB 180-4 clause 6.4.2 step 3.
+ the second (vH += SIG0(vA) + Maj(vE,vF,vC) equals T1 + T2 in
+ FIPS PUB 180-4 clause 6.4.2 step 3.
+ * Note: 'wt' must be used exactly one time in this macro as it change other
+ data as well every time when used. */
+#define SHA2STEP64(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
+ (vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt)); \
+ (vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0)
+
+ /* Get value of W(t) from input data buffer for 0 <= t <= 15,
+ See FIPS PUB 180-4 clause 6.2.
+ Input data must be read in big-endian bytes order,
+ see FIPS PUB 180-4 clause 3.1.2. */
+#define GET_W_FROM_DATA(buf,t) \
+ mhd_GET_64BIT_BE (((const uint64_t*) (buf)) + (t))
+
+ /* 'W' generation and assignment for 16 <= t <= 79.
+ See FIPS PUB 180-4 clause 6.4.2.
+ As only last 16 'W' are used in calculations, it is possible to
+ use 16 elements array of W as a cyclic buffer.
+ * Note: ((t-16) & 15) have same value as (t & 15) */
+#define Wgen(w,t) ( (w)[(t - 16) & 15] + sig1 ((w)[((t) - 2) & 15]) \
+ + (w)[((t) - 7) & 15] + sig0 ((w)[((t) - 15) & 15]) )
+
+#ifndef MHD_FAVOR_SMALL_CODE
+
+ /* Note: instead of using K constants as array, all K values are specified
+ individually for each step, see FIPS PUB 180-4 clause 4.2.3 for
+ K values. */
+ /* Note: instead of reassigning all working variables on each step,
+ variables are rotated for each step:
+ SHA2STEP64(a, b, c, d, e, f, g, h, K[0], data[0]);
+ SHA2STEP64(h, a, b, c, d, e, f, g, K[1], data[1]);
+ so current 'vD' will be used as 'vE' on next step,
+ current 'vH' will be used as 'vA' on next step. */
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+ if ((const void *) W == data)
+ {
+ /* The input data is already in the cyclic data buffer W[] in correct bytes
+ order. */
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0x428a2f98d728ae22), W[0]);
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0x7137449123ef65cd), W[1]);
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0xb5c0fbcfec4d3b2f), W[2]);
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0xe9b5dba58189dbbc), W[3]);
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0x3956c25bf348b538), W[4]);
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0x59f111f1b605d019), W[5]);
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0x923f82a4af194f9b), W[6]);
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0xab1c5ed5da6d8118), W[7]);
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0xd807aa98a3030242), W[8]);
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0x12835b0145706fbe), W[9]);
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0x243185be4ee4b28c), W[10]);
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0x550c7dc3d5ffb4e2), W[11]);
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0x72be5d74f27b896f), W[12]);
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0x80deb1fe3b1696b1), W[13]);
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0x9bdc06a725c71235), W[14]);
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0xc19bf174cf692694), W[15]);
+ }
+ else /* Combined with the next 'if' */
+#endif /* mhd_BYTE_ORDER == mhd_BIG_ENDIAN */
+ if (1)
+ {
+ /* During first 16 steps, before making any calculations on each step,
+ the W element is read from the input data buffer as big-endian value and
+ stored in the array of W elements. */
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0x428a2f98d728ae22), \
+ W[0] = GET_W_FROM_DATA (data, 0));
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0x7137449123ef65cd), \
+ W[1] = GET_W_FROM_DATA (data, 1));
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0xb5c0fbcfec4d3b2f), \
+ W[2] = GET_W_FROM_DATA (data, 2));
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0xe9b5dba58189dbbc), \
+ W[3] = GET_W_FROM_DATA (data, 3));
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0x3956c25bf348b538), \
+ W[4] = GET_W_FROM_DATA (data, 4));
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0x59f111f1b605d019), \
+ W[5] = GET_W_FROM_DATA (data, 5));
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0x923f82a4af194f9b), \
+ W[6] = GET_W_FROM_DATA (data, 6));
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0xab1c5ed5da6d8118), \
+ W[7] = GET_W_FROM_DATA (data, 7));
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0xd807aa98a3030242), \
+ W[8] = GET_W_FROM_DATA (data, 8));
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0x12835b0145706fbe), \
+ W[9] = GET_W_FROM_DATA (data, 9));
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0x243185be4ee4b28c), \
+ W[10] = GET_W_FROM_DATA (data, 10));
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0x550c7dc3d5ffb4e2), \
+ W[11] = GET_W_FROM_DATA (data, 11));
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0x72be5d74f27b896f), \
+ W[12] = GET_W_FROM_DATA (data, 12));
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0x80deb1fe3b1696b1), \
+ W[13] = GET_W_FROM_DATA (data, 13));
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0x9bdc06a725c71235), \
+ W[14] = GET_W_FROM_DATA (data, 14));
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0xc19bf174cf692694), \
+ W[15] = GET_W_FROM_DATA (data, 15));
+ }
+
+ /* During last 64 steps, before making any calculations on each step,
+ current W element is generated from other W elements of the cyclic buffer
+ and the generated value is stored back in the cyclic buffer. */
+ /* Note: instead of using K constants as array, all K values are specified
+ individually for each step, see FIPS PUB 180-4 clause 4.2.3 for
+ K values. */
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0xe49b69c19ef14ad2), \
+ W[16 & 15] = Wgen (W,16));
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0xefbe4786384f25e3), \
+ W[17 & 15] = Wgen (W,17));
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0x0fc19dc68b8cd5b5), \
+ W[18 & 15] = Wgen (W,18));
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0x240ca1cc77ac9c65), \
+ W[19 & 15] = Wgen (W,19));
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0x2de92c6f592b0275), \
+ W[20 & 15] = Wgen (W,20));
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0x4a7484aa6ea6e483), \
+ W[21 & 15] = Wgen (W,21));
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0x5cb0a9dcbd41fbd4), \
+ W[22 & 15] = Wgen (W,22));
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0x76f988da831153b5), \
+ W[23 & 15] = Wgen (W,23));
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0x983e5152ee66dfab), \
+ W[24 & 15] = Wgen (W,24));
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0xa831c66d2db43210), \
+ W[25 & 15] = Wgen (W,25));
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0xb00327c898fb213f), \
+ W[26 & 15] = Wgen (W,26));
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0xbf597fc7beef0ee4), \
+ W[27 & 15] = Wgen (W,27));
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0xc6e00bf33da88fc2), \
+ W[28 & 15] = Wgen (W,28));
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0xd5a79147930aa725), \
+ W[29 & 15] = Wgen (W,29));
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0x06ca6351e003826f), \
+ W[30 & 15] = Wgen (W,30));
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0x142929670a0e6e70), \
+ W[31 & 15] = Wgen (W,31));
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0x27b70a8546d22ffc), \
+ W[32 & 15] = Wgen (W,32));
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0x2e1b21385c26c926), \
+ W[33 & 15] = Wgen (W,33));
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0x4d2c6dfc5ac42aed), \
+ W[34 & 15] = Wgen (W,34));
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0x53380d139d95b3df), \
+ W[35 & 15] = Wgen (W,35));
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0x650a73548baf63de), \
+ W[36 & 15] = Wgen (W,36));
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0x766a0abb3c77b2a8), \
+ W[37 & 15] = Wgen (W,37));
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0x81c2c92e47edaee6), \
+ W[38 & 15] = Wgen (W,38));
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0x92722c851482353b), \
+ W[39 & 15] = Wgen (W,39));
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0xa2bfe8a14cf10364), \
+ W[40 & 15] = Wgen (W,40));
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0xa81a664bbc423001), \
+ W[41 & 15] = Wgen (W,41));
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0xc24b8b70d0f89791), \
+ W[42 & 15] = Wgen (W,42));
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0xc76c51a30654be30), \
+ W[43 & 15] = Wgen (W,43));
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0xd192e819d6ef5218), \
+ W[44 & 15] = Wgen (W,44));
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0xd69906245565a910), \
+ W[45 & 15] = Wgen (W,45));
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0xf40e35855771202a), \
+ W[46 & 15] = Wgen (W,46));
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0x106aa07032bbd1b8), \
+ W[47 & 15] = Wgen (W,47));
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0x19a4c116b8d2d0c8), \
+ W[48 & 15] = Wgen (W,48));
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0x1e376c085141ab53), \
+ W[49 & 15] = Wgen (W,49));
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0x2748774cdf8eeb99), \
+ W[50 & 15] = Wgen (W,50));
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0x34b0bcb5e19b48a8), \
+ W[51 & 15] = Wgen (W,51));
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0x391c0cb3c5c95a63), \
+ W[52 & 15] = Wgen (W,52));
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0x4ed8aa4ae3418acb), \
+ W[53 & 15] = Wgen (W,53));
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0x5b9cca4f7763e373), \
+ W[54 & 15] = Wgen (W,54));
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0x682e6ff3d6b2b8a3), \
+ W[55 & 15] = Wgen (W,55));
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0x748f82ee5defb2fc), \
+ W[56 & 15] = Wgen (W,56));
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0x78a5636f43172f60), \
+ W[57 & 15] = Wgen (W,57));
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0x84c87814a1f0ab72), \
+ W[58 & 15] = Wgen (W,58));
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0x8cc702081a6439ec), \
+ W[59 & 15] = Wgen (W,59));
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0x90befffa23631e28), \
+ W[60 & 15] = Wgen (W,60));
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0xa4506cebde82bde9), \
+ W[61 & 15] = Wgen (W,61));
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0xbef9a3f7b2c67915), \
+ W[62 & 15] = Wgen (W,62));
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0xc67178f2e372532b), \
+ W[63 & 15] = Wgen (W,63));
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0xca273eceea26619c), \
+ W[64 & 15] = Wgen (W,64));
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0xd186b8c721c0c207), \
+ W[65 & 15] = Wgen (W,65));
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0xeada7dd6cde0eb1e), \
+ W[66 & 15] = Wgen (W,66));
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0xf57d4f7fee6ed178), \
+ W[67 & 15] = Wgen (W,67));
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0x06f067aa72176fba), \
+ W[68 & 15] = Wgen (W,68));
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0x0a637dc5a2c898a6), \
+ W[69 & 15] = Wgen (W,69));
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0x113f9804bef90dae), \
+ W[70 & 15] = Wgen (W,70));
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0x1b710b35131c471b), \
+ W[71 & 15] = Wgen (W,71));
+ SHA2STEP64 (a, b, c, d, e, f, g, h, UINT64_C (0x28db77f523047d84), \
+ W[72 & 15] = Wgen (W,72));
+ SHA2STEP64 (h, a, b, c, d, e, f, g, UINT64_C (0x32caab7b40c72493), \
+ W[73 & 15] = Wgen (W,73));
+ SHA2STEP64 (g, h, a, b, c, d, e, f, UINT64_C (0x3c9ebe0a15c9bebc), \
+ W[74 & 15] = Wgen (W,74));
+ SHA2STEP64 (f, g, h, a, b, c, d, e, UINT64_C (0x431d67c49c100d4c), \
+ W[75 & 15] = Wgen (W,75));
+ SHA2STEP64 (e, f, g, h, a, b, c, d, UINT64_C (0x4cc5d4becb3e42b6), \
+ W[76 & 15] = Wgen (W,76));
+ SHA2STEP64 (d, e, f, g, h, a, b, c, UINT64_C (0x597f299cfc657e2a), \
+ W[77 & 15] = Wgen (W,77));
+ SHA2STEP64 (c, d, e, f, g, h, a, b, UINT64_C (0x5fcb6fab3ad6faec), \
+ W[78 & 15] = Wgen (W,78));
+ SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0x6c44198c4a475817), \
+ W[79 & 15] = Wgen (W,79));
+#else /* MHD_FAVOR_SMALL_CODE */
+ if (1)
+ {
+ unsigned int t;
+ /* K constants array.
+ See FIPS PUB 180-4 clause 4.2.3 for K values. */
+ static const uint64_t K[80] =
+ { UINT64_C (0x428a2f98d728ae22), UINT64_C (0x7137449123ef65cd),
+ UINT64_C (0xb5c0fbcfec4d3b2f), UINT64_C (0xe9b5dba58189dbbc),
+ UINT64_C (0x3956c25bf348b538), UINT64_C (0x59f111f1b605d019),
+ UINT64_C (0x923f82a4af194f9b), UINT64_C (0xab1c5ed5da6d8118),
+ UINT64_C (0xd807aa98a3030242), UINT64_C (0x12835b0145706fbe),
+ UINT64_C (0x243185be4ee4b28c), UINT64_C (0x550c7dc3d5ffb4e2),
+ UINT64_C (0x72be5d74f27b896f), UINT64_C (0x80deb1fe3b1696b1),
+ UINT64_C (0x9bdc06a725c71235), UINT64_C (0xc19bf174cf692694),
+ UINT64_C (0xe49b69c19ef14ad2), UINT64_C (0xefbe4786384f25e3),
+ UINT64_C (0x0fc19dc68b8cd5b5), UINT64_C (0x240ca1cc77ac9c65),
+ UINT64_C (0x2de92c6f592b0275), UINT64_C (0x4a7484aa6ea6e483),
+ UINT64_C (0x5cb0a9dcbd41fbd4), UINT64_C (0x76f988da831153b5),
+ UINT64_C (0x983e5152ee66dfab), UINT64_C (0xa831c66d2db43210),
+ UINT64_C (0xb00327c898fb213f), UINT64_C (0xbf597fc7beef0ee4),
+ UINT64_C (0xc6e00bf33da88fc2), UINT64_C (0xd5a79147930aa725),
+ UINT64_C (0x06ca6351e003826f), UINT64_C (0x142929670a0e6e70),
+ UINT64_C (0x27b70a8546d22ffc), UINT64_C (0x2e1b21385c26c926),
+ UINT64_C (0x4d2c6dfc5ac42aed), UINT64_C (0x53380d139d95b3df),
+ UINT64_C (0x650a73548baf63de), UINT64_C (0x766a0abb3c77b2a8),
+ UINT64_C (0x81c2c92e47edaee6), UINT64_C (0x92722c851482353b),
+ UINT64_C (0xa2bfe8a14cf10364), UINT64_C (0xa81a664bbc423001),
+ UINT64_C (0xc24b8b70d0f89791), UINT64_C (0xc76c51a30654be30),
+ UINT64_C (0xd192e819d6ef5218), UINT64_C (0xd69906245565a910),
+ UINT64_C (0xf40e35855771202a), UINT64_C (0x106aa07032bbd1b8),
+ UINT64_C (0x19a4c116b8d2d0c8), UINT64_C (0x1e376c085141ab53),
+ UINT64_C (0x2748774cdf8eeb99), UINT64_C (0x34b0bcb5e19b48a8),
+ UINT64_C (0x391c0cb3c5c95a63), UINT64_C (0x4ed8aa4ae3418acb),
+ UINT64_C (0x5b9cca4f7763e373), UINT64_C (0x682e6ff3d6b2b8a3),
+ UINT64_C (0x748f82ee5defb2fc), UINT64_C (0x78a5636f43172f60),
+ UINT64_C (0x84c87814a1f0ab72), UINT64_C (0x8cc702081a6439ec),
+ UINT64_C (0x90befffa23631e28), UINT64_C (0xa4506cebde82bde9),
+ UINT64_C (0xbef9a3f7b2c67915), UINT64_C (0xc67178f2e372532b),
+ UINT64_C (0xca273eceea26619c), UINT64_C (0xd186b8c721c0c207),
+ UINT64_C (0xeada7dd6cde0eb1e), UINT64_C (0xf57d4f7fee6ed178),
+ UINT64_C (0x06f067aa72176fba), UINT64_C (0x0a637dc5a2c898a6),
+ UINT64_C (0x113f9804bef90dae), UINT64_C (0x1b710b35131c471b),
+ UINT64_C (0x28db77f523047d84), UINT64_C (0x32caab7b40c72493),
+ UINT64_C (0x3c9ebe0a15c9bebc), UINT64_C (0x431d67c49c100d4c),
+ UINT64_C (0x4cc5d4becb3e42b6), UINT64_C (0x597f299cfc657e2a),
+ UINT64_C (0x5fcb6fab3ad6faec), UINT64_C (0x6c44198c4a475817)};
+
+ /* One step of SHA-512/256 computation with working variables rotation,
+ see FIPS PUB 180-4 clause 6.4.2 step 3.
+ * Note: this version of macro reassign all working variable on
+ each step. */
+#define SHA2STEP64RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) \
+ do { \
+ uint64_t tmp_h_ = (vH); \
+ SHA2STEP64 ((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt)); \
+ (vH) = (vG); \
+ (vG) = (vF); \
+ (vF) = (vE); \
+ (vE) = (vD); \
+ (vD) = (vC); \
+ (vC) = (vB); \
+ (vB) = (vA); \
+ (vA) = tmp_h_; \
+ } \
+ while (0)
+
+ /* During first 16 steps, before making any calculations on each step,
+ the W element is read from the input data buffer as big-endian value and
+ stored in the array of W elements. */
+ for (t = 0; t < 16; ++t)
+ {
+ SHA2STEP64RV (a, b, c, d, e, f, g, h, K[t], \
+ W[t] = GET_W_FROM_DATA (data, t));
+ }
+ /* During last 64 steps, before making any calculations on each step,
+ current W element is generated from other W elements of the cyclic buffer
+ and the generated value is stored back in the cyclic buffer. */
+ for (t = 16; t < 80; ++t)
+ {
+ SHA2STEP64RV (a, b, c, d, e, f, g, h, K[t], \
+ W[t & 15] = Wgen (W,t));
+ }
+ }
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+ /* Compute and store the intermediate hash.
+ See FIPS PUB 180-4 clause 6.4.2 step 4. */
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+ H[5] += f;
+ H[6] += g;
+ H[7] += h;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) void
+mhd_SHA512_256_update (struct mhd_Sha512_256CtxInt *restrict ctx,
+ size_t size,
+ const uint8_t *restrict data)
+{
+ unsigned int bytes_have; /**< Number of bytes in the context buffer */
+ uint64_t count_hi; /**< The high part to be moved to another variable */
+
+ mhd_assert ((data != NULL) || (size == 0));
+
+#ifndef MHD_FAVOR_SMALL_CODE
+ if (0 == size)
+ return; /* Shortcut, do nothing */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+
+ /* Note: (count & (mhd_SHA512_256_BLOCK_SIZE-1))
+ equals (count % mhd_SHA512_256_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned int) (ctx->count & (mhd_SHA512_256_BLOCK_SIZE - 1));
+ ctx->count += size;
+#if SIZEOF_SIZE_T > 7
+ if (size > ctx->count)
+ ctx->count_bits_hi += 1U << 3; /* Value wrap */
+#endif /* SIZEOF_SIZE_T > 7 */
+ count_hi = ctx->count >> 61;
+ if (0 != count_hi)
+ {
+ ctx->count_bits_hi += count_hi;
+ ctx->count &= UINT64_C (0x1FFFFFFFFFFFFFFF);
+ }
+
+ if (0 != bytes_have)
+ {
+ unsigned int bytes_left = mhd_SHA512_256_BLOCK_SIZE - bytes_have;
+ if (size >= bytes_left)
+ { /* Combine new data with data in the buffer and
+ process the full block. */
+ memcpy (((uint8_t *) ctx->buffer) + bytes_have,
+ data,
+ bytes_left);
+ data += bytes_left;
+ size -= bytes_left;
+ sha512_256_transform (ctx->H, ctx->buffer);
+ bytes_have = 0;
+ }
+ }
+
+ while (mhd_SHA512_256_BLOCK_SIZE <= size)
+ { /* Process any full blocks of new data directly,
+ without copying to the buffer. */
+ sha512_256_transform (ctx->H, data);
+ data += mhd_SHA512_256_BLOCK_SIZE;
+ size -= mhd_SHA512_256_BLOCK_SIZE;
+ }
+
+ if (0 != size)
+ { /* Copy incomplete block of new data (if any)
+ to the buffer. */
+ memcpy (((uint8_t *) ctx->buffer) + bytes_have, data, size);
+ }
+}
+
+
+/**
+ * Size of "length" insertion in bits.
+ * See FIPS PUB 180-4 clause 5.1.2.
+ */
+#define SHA512_256_SIZE_OF_LEN_ADD_BITS 128
+
+/**
+ * Size of "length" insertion in bytes.
+ */
+#define SHA512_256_SIZE_OF_LEN_ADD (SHA512_256_SIZE_OF_LEN_ADD_BITS / 8)
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_SHA512_256_finish (struct mhd_Sha512_256CtxInt *restrict ctx,
+ uint8_t digest[mhd_SHA512_256_DIGEST_SIZE])
+{
+ uint64_t num_bits; /**< Number of processed bits */
+ unsigned int bytes_have; /**< Number of bytes in the context buffer */
+
+ /* Memorise the number of processed bits.
+ The padding and other data added here during the postprocessing must
+ not change the amount of hashed data. */
+ num_bits = ctx->count << 3;
+
+ /* Note: (count & (mhd_SHA512_256_BLOCK_SIZE-1))
+ equals (count % mhd_SHA512_256_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned int) (ctx->count & (mhd_SHA512_256_BLOCK_SIZE - 1));
+
+ /* Input data must be padded with a single bit "1", then with zeros and
+ the finally the length of data in bits must be added as the final bytes
+ of the last block.
+ See FIPS PUB 180-4 clause 5.1.2. */
+
+ /* Data is always processed in form of bytes (not by individual bits),
+ therefore position of the first padding bit in byte is always
+ predefined (0x80). */
+ /* Buffer always have space for one byte at least (as full buffers are
+ processed immediately). */
+ ((uint8_t *) ctx->buffer)[bytes_have++] = 0x80;
+
+ if (mhd_SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD)
+ { /* No space in the current block to put the total length of message.
+ Pad the current block with zeros and process it. */
+ if (bytes_have < mhd_SHA512_256_BLOCK_SIZE)
+ memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
+ mhd_SHA512_256_BLOCK_SIZE - bytes_have);
+ /* Process the full block. */
+ sha512_256_transform (ctx->H, ctx->buffer);
+ /* Start the new block. */
+ bytes_have = 0;
+ }
+
+ /* Pad the rest of the buffer with zeros. */
+ memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
+ mhd_SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have);
+ /* Put high part of number of bits in processed message and then lower
+ part of number of bits as big-endian values.
+ See FIPS PUB 180-4 clause 5.1.2. */
+ /* Note: the target location is predefined and buffer is always aligned */
+ mhd_PUT_64BIT_BE (ctx->buffer + mhd_SHA512_256_BLOCK_SIZE_WORDS - 2,
+ ctx->count_bits_hi);
+ mhd_PUT_64BIT_BE (ctx->buffer + mhd_SHA512_256_BLOCK_SIZE_WORDS - 1,
+ num_bits);
+ /* Process the full final block. */
+ sha512_256_transform (ctx->H, ctx->buffer);
+
+ /* Put in BE mode the leftmost part of the hash as the final digest.
+ See FIPS PUB 180-4 clause 6.7. */
+ if (1)
+ {
+ bool use_tmp_buf_to_align_result;
+
+#if defined(mhd_PUT_64BIT_BE_UNALIGNED)
+ use_tmp_buf_to_align_result = false;
+#elif defined (MHD_FAVOR_SMALL_CODE)
+ use_tmp_buf_to_align_result = true; /* smaller code: eliminated branch below */
+#else
+ use_tmp_buf_to_align_result =
+ (0 != ((uintptr_t) digest) % mhd_UINT64_ALIGN);
+#endif
+ if (use_tmp_buf_to_align_result)
+ {
+ /* If storing of the final result requires aligned address and
+ the destination address is not aligned or compact code is used,
+ store the final digest in aligned temporary buffer first, then
+ copy it to the destination. */
+ uint64_t alig_dgst[mhd_SHA512_256_DIGEST_SIZE_WORDS];
+ mhd_PUT_64BIT_BE (alig_dgst + 0, ctx->H[0]);
+ mhd_PUT_64BIT_BE (alig_dgst + 1, ctx->H[1]);
+ mhd_PUT_64BIT_BE (alig_dgst + 2, ctx->H[2]);
+ mhd_PUT_64BIT_BE (alig_dgst + 3, ctx->H[3]);
+ /* Copy result to the unaligned destination address */
+ memcpy (digest, alig_dgst, mhd_SHA512_256_DIGEST_SIZE);
+ }
+ else
+ {
+ /* Use cast to (void*) here to mute compiler alignment warnings.
+ * Compilers are not smart enough to see that alignment has been checked. */
+ mhd_PUT_64BIT_BE ((void *) (digest + 0 * mhd_SHA512_256_BYTES_IN_WORD), \
+ ctx->H[0]);
+ mhd_PUT_64BIT_BE ((void *) (digest + 1 * mhd_SHA512_256_BYTES_IN_WORD), \
+ ctx->H[1]);
+ mhd_PUT_64BIT_BE ((void *) (digest + 2 * mhd_SHA512_256_BYTES_IN_WORD), \
+ ctx->H[2]);
+ mhd_PUT_64BIT_BE ((void *) (digest + 3 * mhd_SHA512_256_BYTES_IN_WORD), \
+ ctx->H[3]);
+ }
+ }
+
+ /* Erase potentially sensitive data. */
+ memset (ctx, 0, sizeof(struct mhd_Sha512_256CtxInt));
+}
+
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE
diff --git a/src/mhd2/sha512_256_int.h b/src/mhd2/sha512_256_int.h
@@ -0,0 +1,140 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/mhd2/sha512_256.h
+ * @brief Calculation of SHA-512/256 digest
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA512_256_INT_H
+#define MHD_SHA512_256_INT_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+
+/**
+ * Number of bits in single SHA-512/256 word.
+ */
+#define mhd_SHA512_256_WORD_SIZE_BITS 64
+
+/**
+ * Number of bytes in single SHA-512/256 word.
+ */
+#define mhd_SHA512_256_BYTES_IN_WORD (mhd_SHA512_256_WORD_SIZE_BITS / 8)
+
+/**
+ * Hash is kept internally as 8 64-bit words.
+ * This is intermediate hash size, used during computing the final digest.
+ */
+#define mhd_SHA512_256_HASH_SIZE_WORDS 8
+
+/**
+ * Size of SHA-512/256 resulting digest in words.
+ * This is the final digest size, not intermediate hash.
+ */
+#define mhd_SHA512_256_DIGEST_SIZE_WORDS (mhd_SHA512_256_HASH_SIZE_WORDS / 2)
+
+/**
+ * Size of SHA-512/256 resulting digest in bytes.
+ * This is the final digest size, not intermediate hash.
+ */
+#define mhd_SHA512_256_DIGEST_SIZE \
+ (mhd_SHA512_256_DIGEST_SIZE_WORDS * mhd_SHA512_256_BYTES_IN_WORD)
+
+/**
+ * Size of SHA-512/256 single processing block in bits.
+ */
+#define mhd_SHA512_256_BLOCK_SIZE_BITS 1024
+
+/**
+ * Size of SHA-512/256 single processing block in bytes.
+ */
+#define mhd_SHA512_256_BLOCK_SIZE (mhd_SHA512_256_BLOCK_SIZE_BITS / 8)
+
+/**
+ * Size of SHA-512/256 single processing block in words.
+ */
+#define mhd_SHA512_256_BLOCK_SIZE_WORDS \
+ (mhd_SHA512_256_BLOCK_SIZE_BITS / mhd_SHA512_256_WORD_SIZE_BITS)
+
+
+/**
+ * SHA-512/256 calculation context
+ */
+struct mhd_Sha512_256CtxInt
+{
+ uint64_t H[mhd_SHA512_256_HASH_SIZE_WORDS]; /**< Intermediate hash value */
+ uint64_t buffer[mhd_SHA512_256_BLOCK_SIZE_WORDS]; /**< SHA512_256 input data buffer */
+ /**
+ * The number of bytes, lower part
+ */
+ uint64_t count;
+ /**
+ * The number of bits, high part.
+ * Unlike lower part, this counts the number of bits, not bytes.
+ */
+ uint64_t count_bits_hi;
+};
+
+/**
+ * Initialise structure for SHA-512/256 calculation.
+ *
+ * @param ctx the calculation context
+ */
+MHD_INTERNAL void
+mhd_SHA512_256_init (struct mhd_Sha512_256CtxInt *ctx)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param ctx the calculation context
+ * @param size number of bytes in @a data
+ * @param data bytes to add to hash
+ */
+MHD_INTERNAL void
+mhd_SHA512_256_update (struct mhd_Sha512_256CtxInt *restrict ctx,
+ size_t size,
+ const uint8_t *restrict data)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3, 2);
+
+
+/**
+ * Finalise SHA-512/256 calculation, return digest.
+ *
+ * @param ctx the calculation context
+ * @param[out] digest set to the hash, must be #mhd_SHA512_256_DIGEST_SIZE bytes
+ */
+MHD_INTERNAL void
+mhd_SHA512_256_finish (struct mhd_Sha512_256CtxInt *restrict ctx,
+ uint8_t digest[mhd_SHA512_256_DIGEST_SIZE])
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+/**
+ * Indicates that function mhd_SHA512_256_finish() (without context reset) is
+ * available
+ */
+#define mhd_SHA512_256_HAS_FINISH 1
+
+#endif /* MHD_SHA512_256_H */
diff --git a/src/mhd2/stream_funcs.c b/src/mhd2/stream_funcs.c
@@ -404,8 +404,9 @@ mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c,
method_size = 0; /* Do not recommend shorter request method */
else
{
- mhd_assert (NULL != c->rq.method);
- method_size = strlen (c->rq.method);
+ mhd_assert (NULL != c->rq.method.cstr);
+ method_size = c->rq.method.len;
+ mhd_assert (method_size == strlen (c->rq.method.cstr));
}
if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
@@ -737,6 +738,14 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c,
end_code = MHD_REQUEST_ENDED_BY_APP_ERROR;
sc = MHD_SC_REPLY_FILE_TOO_SHORT;
break;
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ case mhd_CONN_CLOSE_NONCE_ERROR:
+ close_hard = true;
+ end_code = MHD_REQUEST_ENDED_NONCE_ERROR;
+ sc = MHD_SC_REPLY_NONCE_ERROR;
+ break;
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
case mhd_CONN_CLOSE_INT_ERROR:
close_hard = true;
end_code = MHD_REQUEST_ENDED_NO_RESOURCES;
@@ -778,8 +787,8 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c,
case mhd_SOCKET_ERR_AGAIN:
case mhd_SOCKET_ERR_INTR:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
+ break;
}
break;
case mhd_CONN_CLOSE_DAEMON_SHUTDOWN:
@@ -820,6 +829,7 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c,
mhd_UNREACHABLE ();
end_code = MHD_REQUEST_ENDED_COMPLETED_OK;
close_hard = false;
+ break;
}
mhd_assert ((NULL == log_msg) || (MHD_SC_INTERNAL_ERROR != sc));
@@ -845,24 +855,29 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c,
{
bool use_graceful_closing;
- mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c->sk.state.discnt_err));
+ mhd_assert (c->sk.state.rmt_shut_wr || \
+ ! mhd_SOCKET_ERR_IS_HARD (c->sk.state.discnt_err));
- use_graceful_closing = true;
-#ifdef MHD_ENABLE_HTTPS
- if (mhd_C_HAS_TLS (c))
+ use_graceful_closing = ! c->sk.state.rmt_shut_wr;
+ if (use_graceful_closing)
{
- if ((0 != (((unsigned int) c->sk.ready)
- & mhd_SOCKET_NET_STATE_SEND_READY))
- || c->sk.props.is_nonblck)
- use_graceful_closing =
- (mhd_TLS_PROCED_FAILED != mhd_tls_conn_shutdown (c->tls));
- }
+#ifdef MHD_ENABLE_HTTPS
+ if (mhd_C_HAS_TLS (c))
+ {
+ if ((0 != (((unsigned int) c->sk.ready)
+ & mhd_SOCKET_NET_STATE_SEND_READY))
+ || c->sk.props.is_nonblck)
+ use_graceful_closing =
+ (mhd_TLS_PROCED_FAILED != mhd_tls_conn_shutdown (c->tls));
+ }
+ else
#endif /* MHD_ENABLE_HTTPS */
- else if (1)
- {
- use_graceful_closing = mhd_socket_shut_wr (c->sk.fd);
- if (use_graceful_closing)
- use_graceful_closing = (! c->sk.state.rmt_shut_wr); /* Skip as already closed */
+ if (1)
+ {
+ use_graceful_closing = mhd_socket_shut_wr (c->sk.fd);
+ if (use_graceful_closing)
+ use_graceful_closing = (! c->sk.state.rmt_shut_wr); /* Skip as already closed */
+ }
}
if (use_graceful_closing)
{
diff --git a/src/mhd2/stream_funcs.h b/src/mhd2/stream_funcs.h
@@ -194,7 +194,7 @@ enum mhd_ConnCloseReason
mhd_CONN_CLOSE_APP_ERROR
,
/**
- * Application requested about of the stream
+ * Application requested abort of the stream
*/
mhd_CONN_CLOSE_APP_ABORTED
,
@@ -213,6 +213,13 @@ enum mhd_ConnCloseReason
*/
mhd_CONN_CLOSE_FILE_TOO_SHORT
,
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ /**
+ * Error generating nonce for Digest Auth
+ */
+ mhd_CONN_CLOSE_NONCE_ERROR
+ ,
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
/* Hard problem while receiving or sending */
/**
diff --git a/src/mhd2/stream_process_reply.c b/src/mhd2/stream_process_reply.c
@@ -54,6 +54,10 @@
#include "stream_process_reply.h"
#include "stream_funcs.h"
#include "request_get_value.h"
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+# include "mhd_digest_auth_data.h"
+# include "auth_digest.h"
+#endif
#include "mhd_read_file.h"
@@ -325,7 +329,6 @@ setup_reply_properties (struct MHD_Connection *restrict c)
break;
case mhd_RESPONSE_CONTENT_DATA_INVALID:
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
break;
@@ -632,6 +635,21 @@ add_user_headers (char *restrict buf,
#define buffer_append_s(buf,ppos,buf_size,str) \
buffer_append (buf,ppos,buf_size,str, mhd_SSTR_LEN (str))
+
+/**
+ * Append MHD_String to the buffer if enough space is available,
+ * update position.
+ * @param[out] buf the buffer to append data to
+ * @param[in,out] ppos the pointer to position in the @a buffer
+ * @param buf_size the size of the @a buffer
+ * @param pmhdstr the pointer to string to append
+ * @return true if data has been added and position has been updated,
+ * false if not enough space is available
+ */
+#define buffer_append_mstr(buf,ppos,buf_size,pmhdstr) \
+ buffer_append ((buf),(ppos),(buf_size), \
+ (pmhdstr)->cstr, (pmhdstr)->len)
+
/**
* Allocate the connection's write buffer and fill it with all of the
* headers from the response.
@@ -817,6 +835,36 @@ build_header_response_inn (struct MHD_Connection *restrict c)
use_conn_k_alive = false;
}
+ /* Special headers */
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ if (mhd_RESP_HAD_AUTH_DIGEST (r))
+ {
+ char noncestr[mhd_AUTH_DIGEST_NONCE_LEN];
+ const struct mhd_RespAuthDigestHeader *dg_hdr;
+ if (! mhd_auth_digest_get_new_nonce (c,
+ noncestr))
+ {
+ mhd_STREAM_ABORT (c,
+ mhd_CONN_CLOSE_NONCE_ERROR,
+ "Failed to generate a new nonce for Digest Auth.");
+ return false;
+ }
+ for (dg_hdr = mhd_DLINKEDL_GET_FIRST (r, auth_d_hdrs);
+ NULL != dg_hdr;
+ dg_hdr = mhd_DLINKEDL_GET_NEXT (dg_hdr, auth_d_hdrs))
+ {
+ size_t nonce_pos;
+ nonce_pos = pos + dg_hdr->nonce_pos;
+ if (! buffer_append_mstr (buf, &pos, buf_size, \
+ &(dg_hdr->hdr)))
+ return false;
+ memcpy (buf + nonce_pos,
+ noncestr,
+ sizeof(noncestr));
+ }
+ }
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
/* User-defined headers */
if (! add_user_headers (buf, &pos, buf_size, r,
@@ -889,6 +937,12 @@ mhd_stream_build_header_response (struct MHD_Connection *restrict c)
{
if (! build_header_response_inn (c))
{
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+ if (mhd_HTTP_STAGE_PRE_CLOSING <= c->stage)
+ return false; /* Already started closing */
+#else /* ! MHD_SUPPORT_AUTH_DIGEST */
+ mhd_assert (mhd_HTTP_STAGE_START_REPLY == c->stage);
+#endif /* ! MHD_SUPPORT_AUTH_DIGEST */
mhd_STREAM_ABORT (c,
mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
"No memory in the pool for the reply headers.");
@@ -959,7 +1013,6 @@ preprocess_dcc_action (struct MHD_Connection *restrict c,
default:
break;
}
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
mhd_STREAM_ABORT (c,
mhd_CONN_CLOSE_INT_ERROR,
@@ -1018,7 +1071,6 @@ read_response_file (struct MHD_Connection *restrict c,
"than specified by application.");
return false;
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
return false;
diff --git a/src/mhd2/stream_process_request.c b/src/mhd2/stream_process_request.c
@@ -447,17 +447,13 @@
/**
* Detect standard HTTP request method
*
- * @param connection the connection
- * @param method the pointer to HTTP request method string
- * @param len the length of @a method in bytes
+ * @param connection the connection to process
*/
-static MHD_FN_PAR_NONNULL_ALL_
-MHD_FN_PAR_IN_SIZE_ (3,2) void
-parse_http_std_method (struct MHD_Connection *restrict connection,
- size_t len,
- const char *restrict method)
+static MHD_FN_PAR_NONNULL_ALL_ void
+parse_http_std_method (struct MHD_Connection *restrict connection)
{
- const char *const m = method; /**< short alias */
+ const char *const restrict m = connection->rq.method.cstr; /**< short alias */
+ const size_t len = connection->rq.method.len; /**< short alias */
mhd_assert (NULL != m);
mhd_assert (0 != len);
@@ -581,7 +577,7 @@ static MHD_FN_PAR_NONNULL_ALL_ bool
get_request_line_inner (struct MHD_Connection *restrict c)
{
size_t p; /**< The current processing position */
- const int discp_lvl = c->daemon->req_cfg.strictnees;
+ const int discp_lvl = c->daemon->req_cfg.strictness;
/* Allow to skip one or more empty lines before the request line.
RFC 9112, section 2.2 */
const bool skip_empty_lines = (1 >= discp_lvl);
@@ -620,7 +616,7 @@ get_request_line_inner (struct MHD_Connection *restrict c)
mhd_assert (mhd_HTTP_STAGE_INIT == c->stage || \
mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage);
- mhd_assert (NULL == c->rq.method || \
+ mhd_assert (NULL == c->rq.method.cstr || \
mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage);
mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \
mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage);
@@ -644,7 +640,8 @@ get_request_line_inner (struct MHD_Connection *restrict c)
See RFC 9112, section 2.2 */
bool is_empty_line;
mhd_assert (mhd_HTTP_STAGE_INIT == c->stage);
- mhd_assert (NULL == c->rq.method);
+ mhd_assert (0 == c->rq.method.len);
+ mhd_assert (NULL == c->rq.method.cstr);
mhd_assert (NULL == c->rq.url);
mhd_assert (0 == c->rq.url_len);
mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
@@ -797,7 +794,7 @@ get_request_line_inner (struct MHD_Connection *restrict c)
{
/* Handle the end of the request line */
- if (NULL != c->rq.method)
+ if (NULL != c->rq.method.cstr)
{
if (wsp_in_uri)
{
@@ -964,7 +961,7 @@ get_request_line_inner (struct MHD_Connection *restrict c)
(! wsp_blocks))
{
/* Found first whitespace char of the new whitespace block */
- if (NULL == c->rq.method)
+ if (NULL == c->rq.method.cstr)
{
/* Found the end of the HTTP method string */
mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_start);
@@ -979,8 +976,9 @@ get_request_line_inner (struct MHD_Connection *restrict c)
return true; /* Error in the request */
}
read_buffer[p] = 0; /* Zero-terminate the request method string */
- c->rq.method = read_buffer;
- parse_http_std_method (c, p, c->rq.method);
+ c->rq.method.cstr = read_buffer;
+ c->rq.method.len = p;
+ parse_http_std_method (c);
}
else
{
@@ -1143,7 +1141,7 @@ request_add_get_arg (void *restrict cls,
MHD_INTERNAL
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
-MHD_FN_PAR_INOUT_ (2) bool
+MHD_FN_PAR_INOUT_SIZE_ (2, 1) bool
// TODO: detect and report errors
mhd_parse_get_args (size_t args_len,
char *restrict args,
@@ -1406,7 +1404,7 @@ send_redirect_fixed_rq_target (struct MHD_Connection *restrict c)
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_stream_get_request_line (struct MHD_Connection *restrict c)
{
- const int discp_lvl = c->daemon->req_cfg.strictnees;
+ const int discp_lvl = c->daemon->req_cfg.strictness;
/* Parse whitespace in URI, special parsing of the request line */
const bool wsp_in_uri = (0 >= discp_lvl);
/* Keep whitespace in URI, give app URI with whitespace instead of
@@ -1564,7 +1562,7 @@ get_req_header (struct MHD_Connection *restrict c,
struct MHD_String *restrict hdr_name,
struct MHD_String *restrict hdr_value)
{
- const int discp_lvl = c->daemon->req_cfg.strictnees;
+ const int discp_lvl = c->daemon->req_cfg.strictness;
/* Treat bare LF as the end of the line.
RFC 9112, section 2.2-3
Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
@@ -2079,11 +2077,11 @@ mhd_stream_get_request_headers (struct MHD_Connection *restrict c,
mhd_assert (hdr_name.cstr < hdr_value.cstr);
if (! process_footers)
- mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
+ mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_REQ, \
"Failed to allocate memory in the connection memory " \
"pool to store header.");
else
- mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
+ mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_REQ, \
"Failed to allocate memory in the connection memory " \
"pool to store footer.");
@@ -2177,10 +2175,10 @@ mhd_stream_get_request_headers (struct MHD_Connection *restrict c,
(uint_fast64_t) c->rq.skipped_broken_lines);
}
- mhd_assert (c->rq.method < c->read_buffer);
+ mhd_assert (c->rq.method.cstr < c->read_buffer);
if (! process_footers)
{
- c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
+ c->rq.header_size = (size_t) (c->read_buffer - c->rq.method.cstr);
mhd_assert (NULL != c->rq.field_lines.start);
c->rq.field_lines.size =
(size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
@@ -2262,15 +2260,15 @@ parse_cookies_string (const size_t str_len,
size_t i;
bool non_strict;
/* Skip extra whitespaces and empty cookies */
- const bool allow_wsp_empty = (0 >= connection->daemon->req_cfg.strictnees);
+ const bool allow_wsp_empty = (0 >= connection->daemon->req_cfg.strictness);
/* Allow whitespaces around '=' character */
- const bool wsp_around_eq = (-3 >= connection->daemon->req_cfg.strictnees);
+ const bool wsp_around_eq = (-3 >= connection->daemon->req_cfg.strictness);
/* Allow whitespaces in quoted cookie value */
- const bool wsp_in_quoted = (-2 >= connection->daemon->req_cfg.strictnees);
+ const bool wsp_in_quoted = (-2 >= connection->daemon->req_cfg.strictness);
/* Allow tab as space after semicolon between cookies */
- const bool tab_as_sp = (0 >= connection->daemon->req_cfg.strictnees);
+ const bool tab_as_sp = (0 >= connection->daemon->req_cfg.strictness);
/* Allow no space after semicolon between cookies */
- const bool allow_no_space = (0 >= connection->daemon->req_cfg.strictnees);
+ const bool allow_no_space = (0 >= connection->daemon->req_cfg.strictness);
non_strict = false;
i = 0;
@@ -2473,7 +2471,7 @@ parse_cookie_header (struct MHD_Connection *restrict connection,
struct mhd_RequestField *const saved_tail =
connection->rq.fields.last; // FIXME: a better way?
const bool allow_partially_correct_cookie =
- (1 >= connection->daemon->req_cfg.strictnees);
+ (1 >= connection->daemon->req_cfg.strictness);
if (NULL == cookie_val)
return MHD_PARSE_COOKIE_OK;
@@ -2537,7 +2535,6 @@ parse_cookie_header (struct MHD_Connection *restrict connection,
"parse client cookies!\n");
break;
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
break;
}
@@ -2608,7 +2605,7 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
f->field.nv.name.len))
{
if ((has_host)
- && (-3 < c->daemon->req_cfg.strictnees))
+ && (-3 < c->daemon->req_cfg.strictness))
{
mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_SEVERAL, \
"Received request with more than one 'Host' header.");
@@ -2664,7 +2661,7 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
send_err = false;
if (c->rq.cntn.cntn_size == cntn_size)
{
- if (0 < c->daemon->req_cfg.strictnees)
+ if (0 < c->daemon->req_cfg.strictness)
{
mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_SAME, \
"Received request with more than one " \
@@ -2774,7 +2771,7 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
c->rq.have_expect_100 = true;
else
{
- if (0 < c->daemon->req_cfg.strictnees)
+ if (0 < c->daemon->req_cfg.strictness)
{
mhd_LOG_MSG (c->daemon, MHD_SC_EXPECT_HEADER_VALUE_UNSUPPORTED, \
"The 'Expect' header value used in request is " \
@@ -2791,7 +2788,7 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
if (has_trenc && has_cntnlen)
{
- if (0 < c->daemon->req_cfg.strictnees)
+ if (0 < c->daemon->req_cfg.strictness)
{
mhd_RESPOND_WITH_ERROR_STATIC ( \
c, \
@@ -2812,7 +2809,7 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
if (MHD_HTTP_VERSION_1_1 <= c->rq.http_ver)
{
if ((! has_host) &&
- (-3 < c->daemon->req_cfg.strictnees))
+ (-3 < c->daemon->req_cfg.strictness))
{
mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MISSING, \
"Received HTTP/1.1 request without 'Host' header.");
@@ -3090,7 +3087,7 @@ process_request_chunked_body (struct MHD_Connection *restrict c)
size_t available;
bool has_more_data;
char *restrict buffer_head;
- const int discp_lvl = d->req_cfg.strictnees;
+ const int discp_lvl = d->req_cfg.strictness;
/* Treat bare LF as the end of the line.
RFC 9112, section 2.2-3
Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
@@ -3729,7 +3726,7 @@ handle_recv_no_space (struct MHD_Connection *c,
default:
break;
}
- mhd_assert (0 && "Should be unreachable");
+ mhd_UNREACHABLE ();
}
@@ -3944,9 +3941,9 @@ mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
case mhd_HTTP_STAGE_UPGRADED_CLEANING:
#endif /* MHD_UPGRADE_SUPPORT */
default:
- mhd_assert (0);
mhd_UNREACHABLE ();
stage = MHD_PROC_RECV_BODY_NORMAL;
+ break;
}
handle_recv_no_space (c, stage);
diff --git a/src/mhd2/stream_process_request.h b/src/mhd2/stream_process_request.h
@@ -74,7 +74,7 @@ mhd_parse_get_args (size_t args_len,
char *restrict args,
mhd_GetArgumentInter cb,
void *restrict cls)
-MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_INOUT_ (2);
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_INOUT_SIZE_ (2, 1);
/**
diff --git a/src/mhd2/stream_process_states.c b/src/mhd2/stream_process_states.c
@@ -173,8 +173,8 @@ mhd_conn_event_loop_state_update (struct MHD_Connection *restrict c)
c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
break;
default:
- mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
+ break;
}
}
diff --git a/src/mhd2/tls_gnu_funcs.c b/src/mhd2/tls_gnu_funcs.c
@@ -367,7 +367,7 @@ daemon_init_priorities_cache (struct MHD_Daemon *restrict d,
if (GNUTLS_E_SUCCESS == res)
break;
if (GNUTLS_E_MEMORY_ERROR == res)
- return MHD_SC_DAEMON_MALLOC_FAILURE;
+ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
}
if (i < mhd_ARR_NUM_ELEMS (tlsgnulib_base_priorities))
@@ -415,7 +415,7 @@ mhd_tls_gnu_daemon_init3 (struct MHD_Daemon *restrict d,
mhd_calloc (1, sizeof (struct mhd_TlsGnuDaemonData));
*p_d_tls = d_tls;
if (NULL == d_tls)
- return MHD_SC_DAEMON_MALLOC_FAILURE;
+ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
res = daemon_init_credentials (d,
d_tls,
diff --git a/src/mhd2/tls_multi_funcs.c b/src/mhd2/tls_multi_funcs.c
@@ -149,6 +149,7 @@ mhd_tls_multi_is_edge_trigg_supported (struct DaemonOptions *s)
break;
default:
mhd_UNREACHABLE ();
+ break;
}
return false;
}
@@ -220,6 +221,7 @@ tls_daemon_init_try (enum mhd_TlsMultiRoute route,
#endif
case mhd_TLS_MULTI_ROUTE_NONE:
default:
+ break;
}
mhd_assert (0 && "Impossible value");
mhd_UNREACHABLE ();
@@ -240,7 +242,7 @@ mhd_tls_multi_daemon_init (struct MHD_Daemon *restrict d,
mhd_calloc (1,
sizeof (struct mhd_TlsMultiDaemonData));
if (NULL == d_tls)
- return MHD_SC_DAEMON_MALLOC_FAILURE;
+ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
res = MHD_SC_INTERNAL_ERROR; /* Mute compiler warning, the value should not be used */
switch (s->tls)
@@ -300,10 +302,9 @@ mhd_tls_multi_daemon_init (struct MHD_Daemon *restrict d,
#endif /* ! MHD_USE_OPENSSL */
case MHD_TLS_BACKEND_NONE:
default:
- break;
- mhd_assert (0 && "Should not be reachable");
mhd_UNREACHABLE ();
res = MHD_SC_TLS_BACKEND_UNSUPPORTED;
+ break;
}
mhd_assert (NULL != d_tls);
if (MHD_SC_OK == res)
@@ -341,6 +342,7 @@ mhd_tls_multi_daemon_deinit (struct mhd_TlsMultiDaemonData *restrict d_tls)
case mhd_TLS_MULTI_ROUTE_NONE:
default:
mhd_UNREACHABLE ();
+ break;
}
free (d_tls);
}
@@ -415,6 +417,7 @@ mhd_tls_multi_conn_init (const struct mhd_TlsMultiDaemonData *restrict d_tls,
case mhd_TLS_MULTI_ROUTE_NONE:
default:
mhd_UNREACHABLE ();
+ break;
}
return false;
@@ -479,6 +482,7 @@ mhd_tls_multi_conn_handshake (struct mhd_TlsMultiConnData *restrict c_tls)
case mhd_TLS_MULTI_ROUTE_NONE:
default:
mhd_UNREACHABLE ();
+ break;
}
return mhd_TLS_PROCED_FAILED;
}
@@ -507,6 +511,7 @@ mhd_tls_multi_conn_shutdown (struct mhd_TlsMultiConnData *restrict c_tls)
case mhd_TLS_MULTI_ROUTE_NONE:
default:
mhd_UNREACHABLE ();
+ break;
}
return mhd_TLS_PROCED_FAILED;
}
@@ -547,6 +552,7 @@ mhd_tls_multi_conn_recv (struct mhd_TlsMultiConnData *restrict c_tls,
case mhd_TLS_MULTI_ROUTE_NONE:
default:
mhd_UNREACHABLE ();
+ break;
}
return mhd_SOCKET_ERR_INTERNAL;
}
@@ -574,6 +580,7 @@ mhd_tls_multi_conn_has_data_in (struct mhd_TlsMultiConnData *restrict c_tls)
case mhd_TLS_MULTI_ROUTE_NONE:
default:
mhd_UNREACHABLE ();
+ break;
}
return false;
}
@@ -612,6 +619,7 @@ mhd_tls_multi_conn_send (struct mhd_TlsMultiConnData *restrict c_tls,
case mhd_TLS_MULTI_ROUTE_NONE:
default:
mhd_UNREACHABLE ();
+ break;
}
return mhd_SOCKET_ERR_INTERNAL;
}
diff --git a/src/mhd2/tls_open_funcs.c b/src/mhd2/tls_open_funcs.c
@@ -657,7 +657,7 @@ daemon_load_certs_chain (struct MHD_Daemon *restrict d,
if (NULL == m_bio)
{
mhd_DBG_PRINT_TLS_ERRS ();
- return MHD_SC_DAEMON_MALLOC_FAILURE;
+ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
}
ret = daemon_load_certs_chain_obio (d,
d_tls,
@@ -723,7 +723,7 @@ daemon_init_cert (struct MHD_Daemon *restrict d,
if (NULL == m_bio)
{
mhd_DBG_PRINT_TLS_ERRS ();
- return MHD_SC_DAEMON_MALLOC_FAILURE;
+ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
}
pr_key =
PEM_read_bio_PrivateKey_ex (m_bio,
@@ -788,7 +788,7 @@ mhd_tls_open_daemon_init (struct MHD_Daemon *restrict d,
mhd_calloc (1, sizeof (struct mhd_TlsOpenDaemonData));
*p_d_tls = d_tls;
if (NULL == d_tls)
- return MHD_SC_DAEMON_MALLOC_FAILURE;
+ return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
res = daemon_init_lib_ctx (d,
d_tls,
diff --git a/src/mhd2/upgrade_prep.c b/src/mhd2/upgrade_prep.c
@@ -420,7 +420,7 @@ mhd_upgrade_prep_for_action (struct MHD_Request *restrict req,
bool is_upload_act)
{
struct MHD_Connection *const c =
- mhd_cntnr_ptr (req, struct MHD_Connection, rq);
+ mhd_CNTNR_PTR (req, struct MHD_Connection, rq);
mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= c->stage);
mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED >= c->stage);