From 0267a63dc04e3972d64d51f6e288b22f4ecf2756 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Tue, 14 Apr 2026 14:11:00 +0100 Subject: [PATCH] spnego: add --disable-negotiate-ntlm compile-time option Add configure and CMake options to define CURL_DISABLE_NEGOTIATE_NTLM, which gates the NTLM blocking logic in the SSPI and GSS-API SPNEGO code paths behind a compile-time flag. Add a 'SPNEGO-no-NTLM' feature string to curl --version output and gate the SPNEGO NTLM blocking tests on the negotiate-ntlm-disabled feature. Signed-off-by: Matthew John Cheetham --- CMakeLists.txt | 2 ++ configure.ac | 20 ++++++++++++++++++++ docs/CURL-DISABLE.md | 4 ++++ docs/libcurl/curl_version_info.md | 6 ++++++ lib/curl_config-cmake.h.in | 3 +++ lib/vauth/spnego_gssapi.c | 4 ++++ lib/vauth/spnego_sspi.c | 2 ++ lib/version.c | 3 +++ tests/data/test2092 | 1 + tests/data/test2093 | 1 + tests/runtests.pl | 2 ++ 11 files changed, 48 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cc86d2a1f..6d7f77a3e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,6 +426,8 @@ option(CURL_DISABLE_KERBEROS_AUTH "Disable Kerberos authentication" OFF) mark_as_advanced(CURL_DISABLE_KERBEROS_AUTH) option(CURL_DISABLE_NEGOTIATE_AUTH "Disable negotiate authentication" OFF) mark_as_advanced(CURL_DISABLE_NEGOTIATE_AUTH) +option(CURL_DISABLE_NEGOTIATE_NTLM "Block NTLM within SPNEGO negotiation" OFF) +mark_as_advanced(CURL_DISABLE_NEGOTIATE_NTLM) option(CURL_DISABLE_AWS "Disable aws-sigv4" OFF) mark_as_advanced(CURL_DISABLE_AWS) option(CURL_DISABLE_DICT "Disable DICT" OFF) diff --git a/configure.ac b/configure.ac index 98535dada8..b9190f4166 100644 --- a/configure.ac +++ b/configure.ac @@ -4507,6 +4507,26 @@ AS_HELP_STRING([--disable-negotiate-auth],[Disable negotiate authentication]), AC_MSG_RESULT(yes) ) +dnl ************************************************************ +dnl disable NTLM within SPNEGO negotiation +dnl +AC_MSG_CHECKING([whether to allow NTLM within SPNEGO]) +AC_ARG_ENABLE(negotiate-ntlm, +AS_HELP_STRING([--enable-negotiate-ntlm],[Allow NTLM within SPNEGO (default)]) +AS_HELP_STRING([--disable-negotiate-ntlm],[Block NTLM within SPNEGO]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_NEGOTIATE_NTLM, 1, [to block NTLM within SPNEGO]) + CURL_DISABLE_NEGOTIATE_NTLM=1 + ;; + *) + AC_MSG_RESULT(yes) + ;; + esac ], + AC_MSG_RESULT(yes) +) + dnl ************************************************************ dnl disable aws dnl diff --git a/docs/CURL-DISABLE.md b/docs/CURL-DISABLE.md index c266f0c0ad..f3664898af 100644 --- a/docs/CURL-DISABLE.md +++ b/docs/CURL-DISABLE.md @@ -38,6 +38,10 @@ Disable support for the Kerberos authentication methods. Disable support for the negotiate authentication methods. +## `CURL_DISABLE_NEGOTIATE_NTLM` + +Block NTLM authentication within SPNEGO negotiation. + ## `CURL_DISABLE_AWS` Disable **aws-sigv4** support. diff --git a/docs/libcurl/curl_version_info.md b/docs/libcurl/curl_version_info.md index 83c7cdb9fe..8a389fa0c6 100644 --- a/docs/libcurl/curl_version_info.md +++ b/docs/libcurl/curl_version_info.md @@ -320,6 +320,12 @@ libcurl ignore cookies with a domain that is on the list. libcurl was built with support for SPNEGO authentication (Simple and Protected GSS-API Negotiation Mechanism, defined in RFC 2478.) +## `SPNEGO-no-NTLM` + +*features* mask bit: none + +NTLM authentication is blocked within SPNEGO negotiation. + ## `SSL` *features* mask bit: CURL_VERSION_SSL diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index 1dcab9d897..f5a16d2d2b 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -58,6 +58,9 @@ /* disables negotiate authentication */ #cmakedefine CURL_DISABLE_NEGOTIATE_AUTH 1 +/* blocks NTLM within SPNEGO negotiation */ +#cmakedefine CURL_DISABLE_NEGOTIATE_NTLM 1 + /* disables aws-sigv4 */ #cmakedefine CURL_DISABLE_AWS 1 diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index 5519ae4c85..1a1c7b212b 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -160,6 +160,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, #endif #ifdef HAVE_GSS_SET_NEG_MECHS +#ifdef CURL_DISABLE_NEGOTIATE_NTLM /* Acquire explicit credentials and restrict SPNEGO sub-mechanisms to * exclude NTLM. We enumerate all available mechanisms and filter out * the NTLMSSP OID, matching SSPI's "!ntlm". */ @@ -203,6 +204,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, gss_release_oid_set(&minor_status, &available_mechs); } } +#endif /* CURL_DISABLE_NEGOTIATE_NTLM */ #endif /* HAVE_GSS_SET_NEG_MECHS */ /* Generate our challenge-response message */ @@ -240,6 +242,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, } /* Check if NTLM was selected and is disallowed */ +#ifdef CURL_DISABLE_NEGOTIATE_NTLM if(nego->context != GSS_C_NO_CONTEXT) { /* OID 1.3.6.1.4.1.311.2.2.10 (NTLMSSP) */ static const gss_OID_desc ntlmssp_oid = { @@ -261,6 +264,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, return CURLE_AUTH_ERROR; } } +#endif /* CURL_DISABLE_NEGOTIATE_NTLM */ /* Free previous token */ if(nego->output_token.length && nego->output_token.value) diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index e0029ba04a..e07978fb64 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -146,6 +146,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, /* Use the current Windows user */ nego->p_identity = NULL; +#ifdef CURL_DISABLE_NEGOTIATE_NTLM /* Exclude NTLM from SPNEGO negotiation via the PackageList field */ if(!nego->p_identity) { memset(&nego->identity, 0, sizeof(nego->identity)); @@ -166,6 +167,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, nego->identity.PackageList = (unsigned TCHAR *)CURL_UNCONST(TEXT("!ntlm")); nego->identity.PackageListLength = 5; +#endif /* CURL_DISABLE_NEGOTIATE_NTLM */ /* Allocate our credentials handle */ nego->credentials = curlx_calloc(1, sizeof(CredHandle)); diff --git a/lib/version.c b/lib/version.c index 7ccd875dc8..3976490163 100644 --- a/lib/version.c +++ b/lib/version.c @@ -525,6 +525,9 @@ static const struct feat features_table[] = { #endif /* USE_SSL */ #ifdef USE_SPNEGO FEATURE("SPNEGO", NULL, CURL_VERSION_SPNEGO), +#ifdef CURL_DISABLE_NEGOTIATE_NTLM + FEATURE("SPNEGO-no-NTLM", NULL, 0), +#endif #endif #ifdef USE_SSL FEATURE("SSL", NULL, CURL_VERSION_SSL), diff --git a/tests/data/test2092 b/tests/data/test2092 index 3cfdd9cab4..5423e15055 100644 --- a/tests/data/test2092 +++ b/tests/data/test2092 @@ -30,6 +30,7 @@ SPNEGO skips auth when NTLM blocked by CURL_DISABLE_NEGOTIATE_NTLM GSS-API Debug +negotiate-ntlm-disabled CURL_STUB_GSS_CREDS="NTLM_Alice" diff --git a/tests/data/test2093 b/tests/data/test2093 index b74979f384..95f84c2656 100644 --- a/tests/data/test2093 +++ b/tests/data/test2093 @@ -42,6 +42,7 @@ SPNEGO with Kerberos still works when built with CURL_DISABLE_NEGOTIATE_NTLM GSS-API Debug +negotiate-ntlm-disabled CURL_STUB_GSS_CREDS="KRB5_Alice" diff --git a/tests/runtests.pl b/tests/runtests.pl index 2a180bfeb4..e444a5e23a 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -690,6 +690,8 @@ sub checksystemfeatures { $feature{"Kerberos"} = $feat =~ /Kerberos/i; # SPNEGO enabled $feature{"SPNEGO"} = $feat =~ /SPNEGO/i; + # SPNEGO NTLM disabled (compile-time) + $feature{"negotiate-ntlm-disabled"} = $feat =~ /SPNEGO-no-NTLM/i; # TLS-SRP enabled $feature{"TLS-SRP"} = $feat =~ /TLS-SRP/i; # PSL enabled