Skip to content

Commit b0878af

Browse files
committed
curl: disable NTLM via SPNEGO by default
When users choose Kerberos as authentication method, little do they know that there's a provision to fall back to the very weak NTLM authentication instead of using the otherwise quite strong Kerberos authentication methods. The mechanism to choose e.g. NTLM is called "SPNEGO". By somewhat lucky happenstance, a recent security fix that wanted to disable NTLM in Git for Windows by default was _not_ affected by this, due to a quite long-standing bug in Git: Kerberos authentication is simply never attempted by default. Users need to configure `http.emptyAuth=true` to enable it, even though the `http.emptyAuth=auto` default promises to behave in the same way. In preparation for fixing that `http.emptyAuth` bug _without_ weakening the security bug fix that disables NTLM by default, these two patches disable NTLM via SPNEGO altogether. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 034a745 commit b0878af

5 files changed

+247
-4
lines changed

mingw-w64-curl/0001-Make-cURL-relocatable.patch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
From b0761795838b6645cf1bc7d0c290decc1f57b4ca Mon Sep 17 00:00:00 2001
22
From: Ray Donnelly <mingw.android@gmail.com>
33
Date: Wed, 22 Feb 2017 11:03:04 +0100
4-
Subject: [PATCH 1/2] Make cURL relocatable
4+
Subject: [PATCH 1/4] Make cURL relocatable
55

66
This adds the ability to specify CA_BUNDLE paths that are relative to
77
the MSYS2 pseudo-root directory.

mingw-w64-curl/0002-Hack-make-relocation-work-inside-libexec-git-core-an.patch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
From 0d1cc065356bcb23642563c3046b0b0e860fa9bb Mon Sep 17 00:00:00 2001
22
From: Johannes Schindelin <johannes.schindelin@gmx.de>
33
Date: Wed, 31 Oct 2018 10:52:59 +0100
4-
Subject: [PATCH 2/2] Hack: make relocation work inside libexec/git-core/ and
4+
Subject: [PATCH 2/4] Hack: make relocation work inside libexec/git-core/ and
55
bin/
66

77
In Git for Windows, there is a copy of libcurl-4.dll in
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
From 0aa2cd8539608511e34a58473f2974ae0ae50444 Mon Sep 17 00:00:00 2001
2+
From: Matthew John Cheetham <mjcheetham@outlook.com>
3+
Date: Tue, 24 Mar 2026 11:53:02 +0000
4+
Subject: [PATCH 3/4] auth: upgrade SSPI identity to SEC_WINNT_AUTH_IDENTITY_EX
5+
6+
Replace SEC_WINNT_AUTH_IDENTITY with SEC_WINNT_AUTH_IDENTITY_EX across all
7+
SSPI authentication code. The extended structure adds Version, Length, and
8+
PackageList fields while remaining backwards compatible with all SSPI
9+
functions. Available since Windows XP.
10+
11+
Curl_create_sspi_identity now sets the Version and Length fields when
12+
initializing the structure.
13+
14+
Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
15+
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
16+
---
17+
lib/curl_sspi.c | 6 ++++--
18+
lib/curl_sspi.h | 6 +++---
19+
lib/ldap.c | 2 +-
20+
lib/vauth/digest_sspi.c | 10 +++++-----
21+
lib/vauth/vauth.h | 12 ++++++------
22+
5 files changed, 19 insertions(+), 17 deletions(-)
23+
24+
diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c
25+
index 1d4cf925d6..018bfff28e 100644
26+
--- a/lib/curl_sspi.c
27+
+++ b/lib/curl_sspi.c
28+
@@ -93,7 +93,7 @@ void Curl_sspi_global_cleanup(void)
29+
* Returns CURLE_OK on success.
30+
*/
31+
CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
32+
- SEC_WINNT_AUTH_IDENTITY *identity)
33+
+ SEC_WINNT_AUTH_IDENTITY_EX *identity)
34+
{
35+
xcharp_u useranddomain;
36+
xcharp_u user, dup_user;
37+
@@ -105,6 +105,8 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
38+
39+
/* Initialize the identity */
40+
memset(identity, 0, sizeof(*identity));
41+
+ identity->Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
42+
+ identity->Length = sizeof(*identity);
43+
44+
useranddomain.tchar_ptr = curlx_convert_UTF8_to_tchar(userp);
45+
if(!useranddomain.tchar_ptr)
46+
@@ -195,7 +197,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
47+
*
48+
* identity [in/out] - The identity structure.
49+
*/
50+
-void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity)
51+
+void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY_EX *identity)
52+
{
53+
if(identity) {
54+
Curl_safefree(identity->User);
55+
diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h
56+
index 3779d51753..ea405f53a7 100644
57+
--- a/lib/curl_sspi.h
58+
+++ b/lib/curl_sspi.h
59+
@@ -34,14 +34,14 @@ void Curl_sspi_global_cleanup(void);
60+
61+
/* This is used to populate the domain in an SSPI identity structure */
62+
CURLcode Curl_override_sspi_http_realm(const char *chlg,
63+
- SEC_WINNT_AUTH_IDENTITY *identity);
64+
+ SEC_WINNT_AUTH_IDENTITY_EX *identity);
65+
66+
/* This is used to generate an SSPI identity structure */
67+
CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
68+
- SEC_WINNT_AUTH_IDENTITY *identity);
69+
+ SEC_WINNT_AUTH_IDENTITY_EX *identity);
70+
71+
/* This is used to free an SSPI identity structure */
72+
-void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity);
73+
+void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY_EX *identity);
74+
75+
/* Forward-declaration of global variables defined in curl_sspi.c */
76+
extern PSecurityFunctionTable Curl_pSecFn;
77+
diff --git a/lib/ldap.c b/lib/ldap.c
78+
index e223078b03..59369a556d 100644
79+
--- a/lib/ldap.c
80+
+++ b/lib/ldap.c
81+
@@ -157,7 +157,7 @@ static ULONG ldap_win_bind_auth(LDAP *server, const char *user,
82+
const char *passwd, unsigned long authflags)
83+
{
84+
ULONG method = 0;
85+
- SEC_WINNT_AUTH_IDENTITY cred;
86+
+ SEC_WINNT_AUTH_IDENTITY_EX cred;
87+
ULONG rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
88+
89+
memset(&cred, 0, sizeof(cred));
90+
diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c
91+
index f29e569cd1..5f4b5e2735 100644
92+
--- a/lib/vauth/digest_sspi.c
93+
+++ b/lib/vauth/digest_sspi.c
94+
@@ -95,8 +95,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
95+
CredHandle credentials;
96+
CtxtHandle context;
97+
PSecPkgInfo SecurityPackage;
98+
- SEC_WINNT_AUTH_IDENTITY identity;
99+
- SEC_WINNT_AUTH_IDENTITY *p_identity;
100+
+ SEC_WINNT_AUTH_IDENTITY_EX identity;
101+
+ SEC_WINNT_AUTH_IDENTITY_EX *p_identity;
102+
SecBuffer chlg_buf;
103+
SecBuffer resp_buf;
104+
SecBufferDesc chlg_desc;
105+
@@ -240,7 +240,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
106+
* Returns CURLE_OK on success.
107+
*/
108+
CURLcode Curl_override_sspi_http_realm(const char *chlg,
109+
- SEC_WINNT_AUTH_IDENTITY *identity)
110+
+ SEC_WINNT_AUTH_IDENTITY_EX *identity)
111+
{
112+
xcharp_u domain, dup_domain;
113+
114+
@@ -466,8 +466,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
115+
116+
if(!digest->http_context) {
117+
CredHandle credentials;
118+
- SEC_WINNT_AUTH_IDENTITY identity;
119+
- SEC_WINNT_AUTH_IDENTITY *p_identity;
120+
+ SEC_WINNT_AUTH_IDENTITY_EX identity;
121+
+ SEC_WINNT_AUTH_IDENTITY_EX *p_identity;
122+
SecBuffer resp_buf;
123+
SecBufferDesc resp_desc;
124+
unsigned long attrs;
125+
diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h
126+
index 3e66c89cb5..10a02321e3 100644
127+
--- a/lib/vauth/vauth.h
128+
+++ b/lib/vauth/vauth.h
129+
@@ -170,8 +170,8 @@ struct ntlmdata {
130+
#endif
131+
CredHandle *credentials;
132+
CtxtHandle *context;
133+
- SEC_WINNT_AUTH_IDENTITY identity;
134+
- SEC_WINNT_AUTH_IDENTITY *p_identity;
135+
+ SEC_WINNT_AUTH_IDENTITY_EX identity;
136+
+ SEC_WINNT_AUTH_IDENTITY_EX *p_identity;
137+
size_t token_max;
138+
BYTE *output_token;
139+
BYTE *input_token;
140+
@@ -241,8 +241,8 @@ struct kerberos5data {
141+
CredHandle *credentials;
142+
CtxtHandle *context;
143+
TCHAR *spn;
144+
- SEC_WINNT_AUTH_IDENTITY identity;
145+
- SEC_WINNT_AUTH_IDENTITY *p_identity;
146+
+ SEC_WINNT_AUTH_IDENTITY_EX identity;
147+
+ SEC_WINNT_AUTH_IDENTITY_EX *p_identity;
148+
size_t token_max;
149+
BYTE *output_token;
150+
#else
151+
@@ -309,8 +309,8 @@ struct negotiatedata {
152+
SECURITY_STATUS status;
153+
CredHandle *credentials;
154+
CtxtHandle *context;
155+
- SEC_WINNT_AUTH_IDENTITY identity;
156+
- SEC_WINNT_AUTH_IDENTITY *p_identity;
157+
+ SEC_WINNT_AUTH_IDENTITY_EX identity;
158+
+ SEC_WINNT_AUTH_IDENTITY_EX *p_identity;
159+
TCHAR *spn;
160+
size_t token_max;
161+
BYTE *output_token;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
From cab1cced8a4c48054183192a80d09112d788e868 Mon Sep 17 00:00:00 2001
2+
From: Matthew John Cheetham <mjcheetham@outlook.com>
3+
Date: Mon, 23 Mar 2026 13:34:27 +0000
4+
Subject: [PATCH 4/4] spnego(windows): disallow NTLM via SPNEGO
5+
6+
SPNEGO (Negotiate) authentication can silently fall back to NTLM when
7+
Kerberos is unavailable. This is undesirable because:
8+
9+
1. Kerberos is supposed to be a strong authentication method, and
10+
allowing to downgrade it to NTLM weakens that.
11+
12+
2. If the caller wants to allow or disallow NTLM authentication, they
13+
can do so explicitly; But with NTLM via SPNEGO, there is no such
14+
option.
15+
16+
3. The cURL project deprecated NTLM support and plans on removing it in
17+
September 2026 altogether. If NTLM via SPNEGO was still supported,
18+
that would seriously sabotage the security implications of this plan.
19+
20+
With this patch, libcurl checks the sub-mechanism selected by SPNEGO
21+
after the security context is initialized but before any tokens are
22+
sent on the wire. If NTLM was chosen, the authentication fails with
23+
CURLE_AUTH_ERROR.
24+
25+
Bare NTLM auth (CURLAUTH_NTLM) is not affected.
26+
27+
Note that this patch only changes the Windows (SSPI) side by restricting
28+
SSPI from using NTLM by specifying the special string "!ntlm" in the
29+
PackageList field of the SEC_WINNT_AUTH_IDENTITY_EX structure. A more
30+
comprehensive solution that includes GSS-API is being developed in
31+
https://github.com/curl/curl/pull/21076.
32+
33+
Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
34+
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
35+
---
36+
lib/vauth/spnego_sspi.c | 27 +++++++++++++++++++++++++++
37+
1 file changed, 27 insertions(+)
38+
39+
diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c
40+
index 1f73123a0d..57d7599361 100644
41+
--- a/lib/vauth/spnego_sspi.c
42+
+++ b/lib/vauth/spnego_sspi.c
43+
@@ -146,6 +146,33 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
44+
/* Use the current Windows user */
45+
nego->p_identity = NULL;
46+
47+
+ /* Disallow NTLM via SPNEGO */ {
48+
+ /* Exclude NTLM from SPNEGO negotiation via the PackageList field */
49+
+ if(!nego->p_identity) {
50+
+ memset(&nego->identity, 0, sizeof(nego->identity));
51+
+ nego->identity.Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
52+
+ nego->identity.Length = sizeof(nego->identity);
53+
+ nego->identity.Flags =
54+
+#ifdef UNICODE
55+
+ SEC_WINNT_AUTH_IDENTITY_UNICODE;
56+
+#else
57+
+ SEC_WINNT_AUTH_IDENTITY_ANSI;
58+
+#endif
59+
+ nego->p_identity = &nego->identity;
60+
+ }
61+
+
62+
+ /* Use the special name "!ntlm" to prevent NTLM from being used:
63+
+ * https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-sec_winnt_auth_identity_exa
64+
+ */
65+
+ nego->identity.PackageList =
66+
+#ifdef UNICODE
67+
+ (unsigned short *)CURL_UNCONST(TEXT("!ntlm"));
68+
+#else
69+
+ (unsigned char *)CURL_UNCONST(TEXT("!ntlm"));
70+
+#endif
71+
+ nego->identity.PackageListLength = 5;
72+
+ }
73+
+
74+
/* Allocate our credentials handle */
75+
nego->credentials = curlx_calloc(1, sizeof(CredHandle));
76+
if(!nego->credentials)

mingw-w64-curl/PKGBUILD

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,17 @@ source=("https://github.com/curl/curl/releases/download/${_realname}-${pkgver//.
3636
"pathtools.h"
3737
"0001-Make-cURL-relocatable.patch"
3838
"0002-Hack-make-relocation-work-inside-libexec-git-core-an.patch"
39+
"0003-auth-upgrade-SSPI-identity-to-SEC_WINNT_AUTH_IDENTIT.patch"
40+
"0004-spnego-windows-disallow-NTLM-via-SPNEGO.patch"
3941
"70bb0db76720c152f6a55bbe12cf162b55cb105b.patch")
4042
sha256sums=('eba3230c1b659211a7afa0fbf475978cbf99c412e4d72d9aa92d020c460742d4'
4143
'SKIP'
4244
'08209cbf1633fa92eae7e5d28f95f8df9d6184cc20fa878c99aec4709bb257fd'
4345
'965d3921ec4fdeec94a2718bc2c85ce5e1a00ea0e499330a554074a7ae15dfc6'
44-
'9cdae4bbdc44a6d342c31ea567e79d4e1a435227f2236cdb7e75d72d94d62905'
45-
'6911afe3d7a3158447ff29a06c0f652eafd1d169928843777a5f69d27c87fc1e'
46+
'8567c3601664836e12e142f4c51c4f023aaa3477e9985ea4168301af22a1141f'
47+
'b9a7ca284f881c2e5e15daf109d8c42d7e92ef32949031b3da42ef9b7bf924bd'
48+
'e834995b7adcb573c8441ea17a54e71a359c4ec5049d31093ff25f898a48abf9'
49+
'f597923ca8ec8757bc7600a420cc5f6f5acd6ab85e005054273bc63805af6d48'
4650
'ffaadb16a5f1aaa4e0a33473b905a6650e6291afecb39f56805eaffc26a20932')
4751
validpgpkeys=('27EDEAF22F3ABCEB50DB9A125CC908FDB71E12C2') # Daniel Stenberg
4852

@@ -94,6 +98,8 @@ prepare() {
9498
apply_patch_with_msg \
9599
0001-Make-cURL-relocatable.patch \
96100
0002-Hack-make-relocation-work-inside-libexec-git-core-an.patch \
101+
0003-auth-upgrade-SSPI-identity-to-SEC_WINNT_AUTH_IDENTIT.patch \
102+
0004-spnego-windows-disallow-NTLM-via-SPNEGO.patch \
97103
70bb0db76720c152f6a55bbe12cf162b55cb105b.patch
98104

99105
autoreconf -vfi

0 commit comments

Comments
 (0)