From e5a0aec384d1eb6d3e98c41e7c85de297dc18812 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 29 Jan 2026 10:49:03 -0800 Subject: [PATCH 1/3] Make digest auth support conditional (issue #232) When libmicrohttpd is built with --disable-dauth, the digest auth functions (MHD_digest_auth_get_username, MHD_digest_auth_check, MHD_queue_auth_fail_response) are not available, causing linker errors. This change detects digest auth availability at configure time and conditionally compiles the digest auth code, following the existing pattern used for GnuTLS support. Changes: - Add AC_CHECK_LIB detection for MHD_queue_auth_fail_response - Add HAVE_DAUTH preprocessor flag when digest auth is available - Wrap digest auth code in #ifdef HAVE_DAUTH guards - Show digest auth status in configure summary --- .github/workflows/verify-build.yml | 47 +++++++++++++++++++- configure.ac | 13 ++++++ src/digest_auth_fail_response.cpp | 4 ++ src/http_request.cpp | 4 ++ src/httpserver.hpp | 2 + src/httpserver/digest_auth_fail_response.hpp | 4 ++ src/httpserver/http_request.hpp | 6 +++ test/integ/authentication.cpp | 7 ++- 8 files changed, 84 insertions(+), 3 deletions(-) diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml index 7a42e5bf..7815711f 100644 --- a/.github/workflows/verify-build.yml +++ b/.github/workflows/verify-build.yml @@ -248,6 +248,18 @@ jobs: debug: nodebug coverage: nocoverage shell: bash + # Test build without digest auth support (issue #232) + - test-group: extra + os: ubuntu-latest + os-type: ubuntu + build-type: no-dauth + compiler-family: gcc + c-compiler: gcc + cc-compiler: g++ + debug: nodebug + coverage: nocoverage + linking: dynamic + shell: bash - test-group: extra os: ubuntu-latest os-type: ubuntu @@ -467,7 +479,7 @@ jobs: with: path: libmicrohttpd-0.9.77 key: ${{ matrix.os }}-${{ matrix.c-compiler }}-libmicrohttpd-0.9.77-pre-built - if: ${{ matrix.os-type != 'windows' }} + if: ${{ matrix.os-type != 'windows' && matrix.build-type != 'no-dauth' }} - name: Build libmicrohttpd dependency (if not cached) run: | @@ -476,12 +488,31 @@ jobs: cd libmicrohttpd-0.9.77 ; ./configure --disable-examples ; make ; - if: ${{ matrix.os-type != 'windows' && steps.cache-libmicrohttpd.outputs.cache-hit != 'true' }} + if: ${{ matrix.os-type != 'windows' && matrix.build-type != 'no-dauth' && steps.cache-libmicrohttpd.outputs.cache-hit != 'true' }} + + - name: Build libmicrohttpd without digest auth (no-dauth test) + run: | + curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz ; + tar -xzf libmicrohttpd-0.9.77.tar.gz ; + cd libmicrohttpd-0.9.77 ; + ./configure --disable-examples --disable-dauth ; + make ; + if: ${{ matrix.build-type == 'no-dauth' }} - name: Install libmicrohttpd run: cd libmicrohttpd-0.9.77 ; sudo make install ; if: ${{ matrix.os-type != 'windows' }} + - name: Verify digest auth is disabled (no-dauth test) + run: | + # Verify that MHD_queue_auth_fail_response is NOT present in libmicrohttpd + if nm /usr/local/lib/libmicrohttpd.so 2>/dev/null | grep -q MHD_queue_auth_fail_response; then + echo "ERROR: libmicrohttpd was built WITH digest auth support" ; + exit 1 ; + fi + echo "Verified: libmicrohttpd built without digest auth support" ; + if: ${{ matrix.build-type == 'no-dauth' }} + - name: Build and install libmicrohttpd (Windows) if: ${{ matrix.os-type == 'windows' }} run: | @@ -532,6 +563,18 @@ jobs: ../configure --disable-fastopen; fi + - name: Verify libhttpserver detected no digest auth (no-dauth test) + run: | + cd build ; + if grep -q "Digest Auth.*:.*no" config.log; then + echo "Verified: libhttpserver correctly detected digest auth is disabled" ; + else + echo "ERROR: libhttpserver did not detect that digest auth is disabled" ; + grep "Digest Auth" config.log || echo "Digest Auth line not found" ; + exit 1 ; + fi + if: ${{ matrix.build-type == 'no-dauth' }} + - name: Print config.log shell: bash run: | diff --git a/configure.ac b/configure.ac index 2ddc13b6..50aa008a 100644 --- a/configure.ac +++ b/configure.ac @@ -149,6 +149,11 @@ fi AM_CONDITIONAL([COND_CROSS_COMPILE],[test x"$cond_cross_compile" = x"yes"]) AC_SUBST(COND_CROSS_COMPILE) +# Check for digest auth support in libmicrohttpd +AC_CHECK_LIB([microhttpd], [MHD_queue_auth_fail_response], + [have_dauth="yes"], + [have_dauth="no"; AC_MSG_WARN("libmicrohttpd digest auth support not found. Digest auth will be disabled")]) + AC_MSG_CHECKING([whether to build with TCP_FASTOPEN support]) AC_ARG_ENABLE([fastopen], [AS_HELP_STRING([--enable-fastopen], @@ -259,6 +264,13 @@ fi AM_CONDITIONAL([HAVE_GNUTLS],[test x"$have_gnutls" = x"yes"]) +if test x"$have_dauth" = x"yes"; then + AM_CXXFLAGS="$AM_CXXFLAGS -DHAVE_DAUTH" + AM_CFLAGS="$AM_CXXFLAGS -DHAVE_DAUTH" +fi + +AM_CONDITIONAL([HAVE_DAUTH],[test x"$have_dauth" = x"yes"]) + DX_HTML_FEATURE(ON) DX_CHM_FEATURE(OFF) DX_CHI_FEATURE(OFF) @@ -309,6 +321,7 @@ AC_MSG_NOTICE([Configuration Summary: License : LGPL only Debug : ${debugit} TLS Enabled : ${have_gnutls} + Digest Auth : ${have_dauth} TCP_FASTOPEN : ${is_fastopen_supported} Static : ${static} Windows build : ${is_windows} diff --git a/src/digest_auth_fail_response.cpp b/src/digest_auth_fail_response.cpp index 82749710..cb24325f 100644 --- a/src/digest_auth_fail_response.cpp +++ b/src/digest_auth_fail_response.cpp @@ -18,6 +18,8 @@ USA */ +#ifdef HAVE_DAUTH + #include "httpserver/digest_auth_fail_response.hpp" #include #include @@ -32,3 +34,5 @@ int digest_auth_fail_response::enqueue_response(MHD_Connection* connection, MHD_ } } // namespace httpserver + +#endif // HAVE_DAUTH diff --git a/src/http_request.cpp b/src/http_request.cpp index d589262b..1a2b7c14 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -42,6 +42,7 @@ void http_request::set_method(const std::string& method) { this->method = string_utilities::to_upper_copy(method); } +#ifdef HAVE_DAUTH bool http_request::check_digest_auth(const std::string& realm, const std::string& password, int nonce_timeout, bool* reload_nonce) const { std::string_view digested_user = get_digested_user(); @@ -57,6 +58,7 @@ bool http_request::check_digest_auth(const std::string& realm, const std::string *reload_nonce = false; return true; } +#endif // HAVE_DAUTH std::string_view http_request::get_connection_value(std::string_view key, enum MHD_ValueKind kind) const { const char* header_c = MHD_lookup_connection_value(underlying_connection, kind, key.data()); @@ -257,6 +259,7 @@ std::string_view http_request::get_pass() const { return cache->password; } +#ifdef HAVE_DAUTH std::string_view http_request::get_digested_user() const { if (!cache->digested_user.empty()) { return cache->digested_user; @@ -272,6 +275,7 @@ std::string_view http_request::get_digested_user() const { return cache->digested_user; } +#endif // HAVE_DAUTH #ifdef HAVE_GNUTLS bool http_request::has_tls_session() const { diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 8b706613..4f19de48 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -29,7 +29,9 @@ #include "httpserver/basic_auth_fail_response.hpp" #include "httpserver/deferred_response.hpp" +#ifdef HAVE_DAUTH #include "httpserver/digest_auth_fail_response.hpp" +#endif // HAVE_DAUTH #include "httpserver/file_response.hpp" #include "httpserver/http_arg_value.hpp" #include "httpserver/http_request.hpp" diff --git a/src/httpserver/digest_auth_fail_response.hpp b/src/httpserver/digest_auth_fail_response.hpp index bbc1543a..f3697c31 100644 --- a/src/httpserver/digest_auth_fail_response.hpp +++ b/src/httpserver/digest_auth_fail_response.hpp @@ -25,6 +25,8 @@ #ifndef SRC_HTTPSERVER_DIGEST_AUTH_FAIL_RESPONSE_HPP_ #define SRC_HTTPSERVER_DIGEST_AUTH_FAIL_RESPONSE_HPP_ +#ifdef HAVE_DAUTH + #include #include "httpserver/http_utils.hpp" #include "httpserver/string_response.hpp" @@ -66,4 +68,6 @@ class digest_auth_fail_response : public string_response { } // namespace httpserver +#endif // HAVE_DAUTH + #endif // SRC_HTTPSERVER_DIGEST_AUTH_FAIL_RESPONSE_HPP_ diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index b435cf7f..749e49ab 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -65,11 +65,13 @@ class http_request { **/ std::string_view get_user() const; +#ifdef HAVE_DAUTH /** * Method used to get the username extracted from a digest authentication * @return the username **/ std::string_view get_digested_user() const; +#endif // HAVE_DAUTH /** * Method used to get the password eventually passed through basic authentication. @@ -250,7 +252,9 @@ class http_request { **/ uint16_t get_requestor_port() const; +#ifdef HAVE_DAUTH bool check_digest_auth(const std::string& realm, const std::string& password, int nonce_timeout, bool* reload_nonce) const; +#endif // HAVE_DAUTH friend std::ostream &operator<< (std::ostream &os, http_request &r); @@ -412,7 +416,9 @@ class http_request { std::string password; std::string querystring; std::string requestor_ip; +#ifdef HAVE_DAUTH std::string digested_user; +#endif // HAVE_DAUTH std::map, http::arg_comparator> unescaped_args; bool args_populated = false; diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index 4f4096d3..eba87172 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -44,7 +44,9 @@ using httpserver::webserver; using httpserver::create_webserver; using httpserver::http_response; using httpserver::basic_auth_fail_response; +#ifdef HAVE_DAUTH using httpserver::digest_auth_fail_response; +#endif // HAVE_DAUTH using httpserver::string_response; using httpserver::http_resource; using httpserver::http_request; @@ -74,6 +76,7 @@ class user_pass_resource : public http_resource { } }; +#ifdef HAVE_DAUTH class digest_resource : public http_resource { public: shared_ptr render_GET(const http_request& req) { @@ -88,6 +91,7 @@ class digest_resource : public http_resource { return std::make_shared("SUCCESS", 200, "text/plain"); } }; +#endif // HAVE_DAUTH LT_BEGIN_SUITE(authentication_suite) void set_up() { @@ -150,7 +154,8 @@ LT_END_AUTO_TEST(base_auth_fail) // do not run the digest auth tests on windows as curl // appears to have problems with it. // Will fix this separately -#ifndef _WINDOWS +// Also skip if libmicrohttpd was built without digest auth support +#if !defined(_WINDOWS) && defined(HAVE_DAUTH) LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth) webserver ws = create_webserver(PORT) From c2b9f4a36cf57e0ab876a5a6f0e24dd694313dba Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 29 Jan 2026 11:46:01 -0800 Subject: [PATCH 2/3] Make digest_authentication example conditional (issue #232) Build the digest_authentication example only when HAVE_DAUTH is defined, consistent with the conditional digest auth support added in the library. --- examples/Makefile.am | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/Makefile.am b/examples/Makefile.am index 14a228c1..c71065dc 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator file_upload file_upload_with_callback +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator file_upload file_upload_with_callback hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp @@ -31,7 +31,6 @@ hello_with_get_arg_SOURCES = hello_with_get_arg.cpp setting_headers_SOURCES = setting_headers.cpp custom_access_log_SOURCES = custom_access_log.cpp basic_authentication_SOURCES = basic_authentication.cpp -digest_authentication_SOURCES = digest_authentication.cpp minimal_https_SOURCES = minimal_https.cpp minimal_file_response_SOURCES = minimal_file_response.cpp minimal_deferred_SOURCES = minimal_deferred.cpp @@ -49,3 +48,8 @@ LDADD += -lgnutls noinst_PROGRAMS += minimal_https_psk minimal_https_psk_SOURCES = minimal_https_psk.cpp endif + +if HAVE_DAUTH +noinst_PROGRAMS += digest_authentication +digest_authentication_SOURCES = digest_authentication.cpp +endif From ae03b6c3445b200439be302e223ed822ab8619f9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 29 Jan 2026 11:55:02 -0800 Subject: [PATCH 3/3] Guard digest auth MHD options with HAVE_DAUTH (issue #232) When libmicrohttpd is built with --disable-dauth, passing MHD_OPTION_NONCE_NC_SIZE or MHD_OPTION_DIGEST_AUTH_RANDOM to MHD_create_daemon() causes daemon creation to fail. This fixes the ws_start_stop test failure in the no-dauth CI build. --- src/webserver.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/webserver.cpp b/src/webserver.cpp index cd8b9b50..48745966 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -254,9 +254,11 @@ bool webserver::start(bool blocking) { iov.push_back(gen(MHD_OPTION_THREAD_STACK_SIZE, max_thread_stack_size)); } +#ifdef HAVE_DAUTH if (nonce_nc_size != 0) { iov.push_back(gen(MHD_OPTION_NONCE_NC_SIZE, nonce_nc_size)); } +#endif // HAVE_DAUTH if (use_ssl) { // Need for const_cast to respect MHD interface that needs a void* @@ -278,10 +280,12 @@ bool webserver::start(bool blocking) { iov.push_back(gen(MHD_OPTION_HTTPS_PRIORITIES, 0, reinterpret_cast(const_cast(https_priorities.c_str())))); } +#ifdef HAVE_DAUTH if (digest_auth_random != "") { // Need for const_cast to respect MHD interface that needs a char* iov.push_back(gen(MHD_OPTION_DIGEST_AUTH_RANDOM, digest_auth_random.size(), const_cast(digest_auth_random.c_str()))); } +#endif // HAVE_DAUTH #ifdef HAVE_GNUTLS if (cred_type != http_utils::NONE) {