libmicrohttpd

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

commit 70f1ba0b221ef63ec49c3056310e469d4d382dfa
parent 669daf7a4690b106d323bc40d175ab702405095a
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 29 Jul 2026 17:11:57 +0200

use MHD_check_response_header_s_token_ci() as it was designed, avoid crash on add_connection followed immediately by daemon destruction; enforce minimum psk length

Diffstat:
Msrc/include/microhttpd.h | 4+++-
Msrc/microhttpd/connection.c | 15+++++++++------
Msrc/microhttpd/daemon.c | 35++++++++++++++++++++++++++++++++---
3 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -1583,7 +1583,9 @@ typedef void * @param username the user name claimed by the other side * @param[out] psk to be set to the pre-shared-key; should be allocated with malloc(), * will be freed by MHD - * @param[out] psk_size to be set to the number of bytes in @a psk + * @param[out] psk_size to be set to the number of bytes in @a psk; + * must be at least 16 (RFC 4279 section 7.1), otherwise + * MHD discards the key and fails the authentication * @return 0 on success, -1 on errors */ typedef int diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -8293,7 +8293,6 @@ MHD_queue_response (struct MHD_Connection *connection, #ifdef UPGRADE_SUPPORT if (NULL != response->upgrade_handler) { - struct MHD_HTTP_Res_Header *conn_header; if (0 == (daemon->options & MHD_ALLOW_UPGRADE)) { #ifdef HAVE_MESSAGES @@ -8321,12 +8320,16 @@ MHD_queue_response (struct MHD_Connection *connection, #endif return MHD_NO; } - conn_header = response->first_header; - mhd_assert (NULL != conn_header); - mhd_assert (MHD_str_equal_caseless_ (conn_header->header, + /* MHD_add_response_header() keeps the "Connection" header first, so + response->first_header could be read directly here; look it up by + name instead, so that this does not silently break if that ever + stops being true. */ + mhd_assert (NULL != response->first_header); + mhd_assert (MHD_str_equal_caseless_ (response->first_header->header, MHD_HTTP_HEADER_CONNECTION)); - if (! MHD_str_has_s_token_caseless_ (conn_header->value, - "upgrade")) + if (! MHD_check_response_header_s_token_ci (response, + MHD_HTTP_HEADER_CONNECTION, + "upgrade")) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -2484,6 +2484,16 @@ MHD_tls_push_func_ (gnutls_transport_ptr_t trnsp, /** + * The shortest pre-shared key #MHD_OPTION_GNUTLS_PSK_CRED_HANDLER may + * hand back, in bytes. RFC 4279 section 7.1 requires at least 16 bytes + * of entropy in a PSK and recommends the length of the hash of the + * negotiated cipher suite; anything shorter is brute-forceable offline + * from a single recorded handshake, so MHD refuses it rather than let + * the application weaken the connection by accident. + */ +#define MHD_PSK_MIN_SIZE 16 + +/** * Function called by GNUtls to obtain the PSK for a given session. * * @param session the session to lookup PSK for @@ -2528,6 +2538,15 @@ psk_gnutls_adapter (gnutls_session_t session, &app_psk, &app_psk_size)) return -1; + if (MHD_PSK_MIN_SIZE > app_psk_size) + { +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + _ ("PSK authentication failed: PSK too short.\n")); +#endif + free (app_psk); + return -1; + } if (UINT_MAX < app_psk_size) { #ifdef HAVE_MESSAGES @@ -2537,6 +2556,9 @@ psk_gnutls_adapter (gnutls_session_t session, free (app_psk); return -1; } + /* @a app_psk_size is at least MHD_PSK_MIN_SIZE here, so this is never + gnutls_malloc(0) -- whose result is implementation defined and would + be reported below as an allocation failure. */ if (NULL == (key->data = gnutls_malloc (app_psk_size))) { #ifdef HAVE_MESSAGES @@ -9193,12 +9215,19 @@ close_all_connections (struct MHD_Daemon *daemon) mhd_assert (daemon->shutdown); #ifdef MHD_USE_THREADS -/* Remove externally added new connections that are - * not processed by the daemon thread. */ +/* Remove new connections that MHD_add_connection() queued and that were + * never processed. This is not limited to daemons with an internal + * polling thread: internal_add_connection() queues whenever the daemon + * is thread-safe, which is every daemon that was not started with + * MHD_USE_NO_THREAD_SAFETY, and new_connections_list_process_() + * asserts the same condition. An application driving an external + * event loop can therefore call MHD_add_connection() and then + * MHD_stop_daemon() before the next MHD_run(), and this list is what + * holds the connection at that point. */ MHD_mutex_lock_chk_ (&daemon->new_connections_mutex); while (NULL != (pos = daemon->new_connections_tail)) { - mhd_assert (MHD_D_IS_USING_THREADS_ (daemon)); + mhd_assert (MHD_D_IS_THREAD_SAFE_ (daemon)); DLL_remove (daemon->new_connections_head, daemon->new_connections_tail, pos);