libmicrohttpd2

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

commit e154ae81202e274d51d869d4d4834c96dd6668d4
parent 9e8304ce62bc5690b49b4c0552ee18ea79af7a47
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri, 21 Nov 2025 19:55:05 +0100

implement reading of crypto-quality random generators (TLS backends, /dev/urandom, others) to offload application when digest auth is needed; for now hidden behind #if OLD in auth_digest.c and also still lacking configure.ac check for MBEDTLS

Diffstat:
Msrc/mhd2/Makefile.am | 7++++---
Msrc/mhd2/auth_digest.c | 18++++++++++++++++--
Msrc/mhd2/mhd_recv.h | 2+-
Asrc/mhd2/mhd_rng.c | 357+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/mhd_rng.h | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/tests/client_server/test_http2.c | 2+-
6 files changed, 452 insertions(+), 7 deletions(-)

diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am @@ -45,6 +45,7 @@ libmicrohttpd2_la_SOURCES = \ mhd_atomic_counter.c mhd_atomic_counter.h \ mhd_bool.h \ mhd_str.c mhd_str.h \ + mhd_rng.c mhd_rng.h \ mhd_str_macros.h mhd_str_types.h \ mhd_buffer.h \ mhd_comm_layer_state.h \ @@ -108,7 +109,7 @@ libmicrohttpd2_la_SOURCES += \ compat_calloc.c endif -# +# httptwo_OPTSOURCES = \ mhd_http_layer_state.h \ h2/h2_bit_masks.h \ @@ -293,7 +294,7 @@ $(srcdir)/response_set_options.c: $(INCL_SRC_DIR)/r_options.rec $(INCL_SRC_DIR)/ AM_V_RC = $(am__v_RC_@AM_V@) am__v_RC_ = $(am__v_RC_@AM_DEFAULT_V@) am__v_RC_0 = @echo " RC " $@; -am__v_RC_1 = +am__v_RC_1 = # General rule is not required, but keep it just in case # Note: windres does not understand '-isystem' flag, so all @@ -311,7 +312,7 @@ libmicrohttpd2_la-w32_lib_res.lo: $(builddir)/w32_lib_res.rc if HAVE_W32 MHD_DLL_RES_LO = libmicrohttpd2_la-w32_lib_res.lo else -MHD_DLL_RES_LO = +MHD_DLL_RES_LO = endif EXTRA_libmicrohttpd2_la_DEPENDENCIES = $(MHD_DLL_RES_LO) diff --git a/src/mhd2/auth_digest.c b/src/mhd2/auth_digest.c @@ -60,6 +60,7 @@ #include "mhd_arr_num_elems.h" #include "mhd_cntnr_ptr.h" #include "mhd_limits.h" +#include "mhd_rng.h" #include "mhd_str_types.h" #include "mhd_buffer.h" @@ -303,20 +304,31 @@ gen_new_nonce (struct MHD_Daemon *restrict d, uint_fast32_t *restrict expir) { uint_fast64_t expiration; +#define OLD 1 +#if OLD size_t gen_num; union DigestCtx d_ctx; +#endif 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_get_inc_wrap (&(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 ! OLD + if (! mhd_rng (d, + c, + mhd_AUTH_DIGEST_NONCE_BIN_SIZE, + out_buf)) + return false; +#else + + gen_num = mhd_atomic_counter_get_inc_wrap (&(d->auth_dg.num_gen_nonces)); + #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), @@ -401,6 +413,8 @@ gen_new_nonce (struct MHD_Daemon *restrict d, return false; #endif /* MHD_SUPPORT_MD5 */ +#endif /* old code */ + *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))); diff --git a/src/mhd2/mhd_recv.h b/src/mhd2/mhd_recv.h @@ -69,7 +69,7 @@ mhd_recv (struct MHD_Connection *restrict c, size_t buf_size, char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], size_t *restrict received) -MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_(3,2) MHD_FN_PAR_OUT_ (4); +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4); #endif /* ! MHD_RECV_H */ diff --git a/src/mhd2/mhd_rng.c b/src/mhd2/mhd_rng.c @@ -0,0 +1,357 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ +/* + This file is part of GNU libmicrohttpd. + Copyright (C) 2025 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. + + Alternatively, you can redistribute GNU libmicrohttpd and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version, together + with the eCos exception, as follows: + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile this + file and link it with other works to produce a work based on this + file, this file does not by itself cause the resulting work to be + covered by the GNU General Public License. However the source code + for this file must still be made available in accordance with + section (3) of the GNU General Public License v2. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + + You should have received copies of the GNU Lesser General Public + License and the GNU General Public License along with this library; + if not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file src/mhd2/mhd_rng.c + * @brief generate random numbers using the best available method; + * we begin by trying the TLS libraries, then common operating-system + * specific methods, then fall back to /dev/urandom or /dev/random + * and if nothing works hash our entropy pool mixing in data from + * the context + * @author Christian Grothoff + */ + +#include "mhd_sys_options.h" +#include "mhd_digest_auth_data.h" + +#include <stddef.h> +#include <string.h> +#include <errno.h> + +#if defined(MHD_SUPPORT_OPENSSL) +#include <openssl/rand.h> +#endif +#if defined(MHD_SUPPORT_GNUTLS) +#include <gnutls/crypto.h> +#endif +#if defined(MHD_SUPPORT_MBEDTLS) +#include <mbedtls/entropy.h> +#include <mbedtls/ctr_drbg.h> +#endif +#if defined(__linux__) +#include <sys/random.h> +#include <unistd.h> +#elif defined(_WIN32) || defined(_WIN64) +#include <windows.h> +#include <bcrypt.h> +#pragma comment(lib, "bcrypt.lib") +#elif defined(__FreeBSD__) +#include <sys/random.h> +#else +#include <stdio.h> +#include <unistd.h> +#endif + +#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_daemon.h" +#include "mhd_connection.h" +#include "mhd_mono_clock.h" +#include "mhd_atomic_counter.h" + +#include "mhd_rng.h" + +/** + * 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_INTERNAL bool +mhd_rng (struct MHD_Daemon *restrict d, + struct MHD_Connection *restrict c, + size_t buf_size, + uint8_t buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)]) +{ + #if defined(MHD_SUPPORT_OPENSSL) + /* OpenSSL - RAND_bytes() */ + if (1 == RAND_bytes ((unsigned char *) buf, + buf_size)) + return true; +#endif +#if defined(MHD_SUPPORT_GNUTLS) + /* GnuTLS - gnutls_rnd() */ + if (0 == + gnutls_rnd (GNUTLS_RND_RANDOM, buf, + buf_size)) + return true; +#endif +#if defined(MHD_SUPPORT_MBEDTLS) + { + /* mbedTLS - requires entropy context and DRBG */ + static mbedtls_entropy_context entropy; + static mbedtls_ctr_drbg_context ctr_drbg; + static int initialized = 0; + + if (0 == initialized) + { + mbedtls_entropy_init (&entropy); + mbedtls_ctr_drbg_init (&ctr_drbg); + + if (0 != + mbedtls_ctr_drbg_seed (&ctr_drbg, + mbedtls_entropy_func, + &entropy, + NULL, + 0)) + { + initialized = -1; + } + else + { + initialized = 1; + } + } + + if ( (1 == initialized) && + (0 == + mbedtls_ctr_drbg_random (&ctr_drbg, + (unsigned char *) buf, + buf_size)) ) + return true; + } +#endif +#if defined(__linux__) && defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) + { + /* Modern Linux with glibc >= 2.25 - getrandom() syscall */ + size_t offset = 0; + unsigned char *ptr = (unsigned char *) buf; + + while (1) + { + ssize_t ret; + + ret = getrandom (ptr + offset, + buf_size - offset, + 0); + if (ret < 0) + { + if (EINTR == errno) + { + continue; + } + break; /* failure */ + } + offset += ret; + if (offset == buf_size) + return true; + } + } +#elif defined(_WIN32) || defined(_WIN64) + /* Windows - BCryptGenRandom() */ + if (STATUS_SUCCESS == + BCryptGenRandom (NULL, + (PUCHAR) buf, + (ULONG) buf_size, + BCRYPT_USE_SYSTEM_PREFERRED_RNG)) + return true; +#elif defined(__FreeBSD__) + /* FreeBSD - arc4random_buf() */ + arc4random_buf (buf, + buf_size); + return true; +#else + /* Generic UNIX fallback - read from /dev/urandom */ + { + static int tried = 0; + static FILE *fp = NULL; + + if ( (0 == tried) && + (NULL == fp) ) + { + tried = 1; + fp = fopen ("/dev/urandom", + "rb"); + if (NULL == fp) + { + /* Try /dev/random as last resort */ + fp = fopen ("/dev/random", + "rb"); + } + } + if (NULL != fp) + { + size_t bytes_read; + + bytes_read = fread (buf, + 1, + buf_size, + fp); + if (bytes_read == buf_size) + return true; + } + } +#endif + /* Nothing worked, use hash functions and our entropy pool and + combine it with current time, generation counter and (if + available) client address to make the inputs as unique as + possible. */ + { + size_t off = 0; + + while (off < buf_size) + { + size_t gen_num; + union DigestCtx d_ctx; + uint_fast64_t now; + /* size of the output buffer, same for all of our hash functions */ + uint8_t out_buf[mhd_AUTH_DIGEST_NONCE_BIN_SIZE]; + size_t left; + + gen_num = mhd_atomic_counter_get_inc_wrap (&(d->auth_dg.num_gen_nonces)); + now = mhd_monotonic_msec_counter (); + +#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 uint8_t*) d->auth_dg.entropy.data); + mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx), + sizeof(gen_num), + (const uint8_t*) &gen_num); + if ( (NULL != c) && + (0 != c->sk.addr.size) ) + mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx), + c->sk.addr.size, + (const uint8_t*) c->sk.addr.data); + mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx), + sizeof(now), + (const uint8_t*) &now); + 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 ( (NULL != c) && + (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(now), + (const void*) &now); + 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 ( (NULL != c) && + (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(now), + (const void*) &now); + 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_get_inc_wrap (&(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 ( (NULL != c) && + (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(now), + (const void*) &now); + 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 */ + + left = mhd_AUTH_DIGEST_NONCE_BIN_SIZE < (buf_size - off) + ? mhd_AUTH_DIGEST_NONCE_BIN_SIZE + : (buf_size - off); + memcpy (buf + off, + out_buf, + left); + off += left; + } /* end while off < buf_size */ + } /* end hash-based approach scope */ + return true; +} diff --git a/src/mhd2/mhd_rng.h b/src/mhd2/mhd_rng.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ +/* + This file is part of GNU libmicrohttpd. + Copyright (C) 2025 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. + + Alternatively, you can redistribute GNU libmicrohttpd and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version, together + with the eCos exception, as follows: + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile this + file and link it with other works to produce a work based on this + file, this file does not by itself cause the resulting work to be + covered by the GNU General Public License. However the source code + for this file must still be made available in accordance with + section (3) of the GNU General Public License v2. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + + You should have received copies of the GNU Lesser General Public + License and the GNU General Public License along with this library; + if not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file src/mhd2/mhd_rng.h + * @brief The definition of the mhd_rng() function + * @author Christian Grothoff + */ + +#ifndef MHD_RECV_H +#define MHD_RECV_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" +#include "mhd_socket_error.h" + +struct MHD_Daemon; /* forward declaration */ +struct MHD_Connection; /* forward declaration */ + +/** + * Initialize @a buf with random data. + * + * @param d the daemon to use + * @param c client to create entropy for, can be NULL + * @param buf_size the size of the @a buf buffer + * @param[out] buf the buffer to fill with the random bytes + * @return true on success + */ +MHD_INTERNAL bool +mhd_rng (struct MHD_Daemon *restrict d, + struct MHD_Connection *restrict c, + size_t buf_size, + uint8_t buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)]) +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_OUT_SIZE_ (4,3); + +#endif diff --git a/src/tests/client_server/test_http2.c b/src/tests/client_server/test_http2.c @@ -178,7 +178,6 @@ main (int argc, char *argv[]) .timeout_ms = 2500, .http_version = 2, }, -#if 0 { .label = "GET with sendfile", .server_cb = &MHDT_server_reply_file, @@ -188,6 +187,7 @@ main (int argc, char *argv[]) .timeout_ms = 2500, .http_version = 2, }, +#if 0 { .label = "client PUT with content-length", .server_cb = &MHDT_server_reply_check_upload,