From 373fc5b97bccbc3b2dae5af10b7fa5e4fec13401 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 26 Nov 2025 18:47:19 +0100 Subject: [PATCH] http: disallow NTLM authentication by default NTLM authentication is relatively weak. This is the case even with the default setting of modern Windows versions, where NTLMv1 and LanManager are disabled and only NTLMv2 is enabled: NTLMv2 hashes of even reasonably complex 8-character passwords can be broken in a matter of days, given enough compute resources. Even worse: On Windows, NTLM authentication uses Security Support Provider Interface ("SSPI"), which provides the credentials without requiring the user to type them in. Which means that an attacker could talk an unsuspecting user into cloning from a server that is under the attacker's control and extracts the user's NTLMv2 hash without their knowledge. For that reason, let's disallow NTLM authentication by default. NTLM authentication is quite simple to set up, though, and therefore there are still some on-prem Azure DevOps setups out there whose users and/or automation rely on this type of authentication. To give them an escape hatch, introduce the `http..allowNTLMAuth` config setting that can be set to `true` to opt back into using NTLM for a specific remote repository. Signed-off-by: Johannes Schindelin --- Documentation/config/http.adoc | 5 +++++ http.c | 20 ++++++++++++++++---- t/t5563-simple-http-auth.sh | 6 ++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc index 849c89f36c..36f331d301 100644 --- a/Documentation/config/http.adoc +++ b/Documentation/config/http.adoc @@ -231,6 +231,11 @@ http.sslKeyType:: See also libcurl `CURLOPT_SSLKEYTYPE`. Can be overridden by the `GIT_SSL_KEY_TYPE` environment variable. +http.allowNTLMAuth:: + Whether or not to allow NTLM authentication. While very convenient to set + up, and therefore still used in many on-prem scenarios, NTLM is a weak + authentication method and therefore deprecated. Defaults to "false". + http.schannelCheckRevoke:: Used to enforce or disable certificate revocation checks in cURL when http.sslBackend is set to "schannel". Defaults to `true` if diff --git a/http.c b/http.c index d8d016891b..2209006b56 100644 --- a/http.c +++ b/http.c @@ -131,7 +131,8 @@ enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL; static struct credential cert_auth = CREDENTIAL_INIT; static int ssl_cert_password_required; -static unsigned long http_auth_methods = CURLAUTH_ANY; +static unsigned long http_auth_any = CURLAUTH_ANY & ~CURLAUTH_NTLM; +static unsigned long http_auth_methods; static int http_auth_methods_restricted; /* Modes for which empty_auth cannot actually help us. */ static unsigned long empty_auth_useless = @@ -429,6 +430,15 @@ static int http_options(const char *var, const char *value, return 0; } + if (!strcmp("http.allowntlmauth", var)) { + if (git_config_bool(var, value)) { + http_auth_any |= CURLAUTH_NTLM; + } else { + http_auth_any &= ~CURLAUTH_NTLM; + } + return 0; + } + if (!strcmp("http.schannelcheckrevoke", var)) { http_schannel_check_revoke = git_config_bool(var, value); return 0; @@ -709,11 +719,11 @@ static void init_curl_proxy_auth(CURL *result) if (i == ARRAY_SIZE(proxy_authmethods)) { warning("unsupported proxy authentication method %s: using anyauth", http_proxy_authmethod); - curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY); + curl_easy_setopt(result, CURLOPT_PROXYAUTH, http_auth_any); } } else - curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY); + curl_easy_setopt(result, CURLOPT_PROXYAUTH, http_auth_any); } static int has_cert_password(void) @@ -1060,7 +1070,7 @@ static CURL *get_curl_handle(void) } curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); - curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + curl_easy_setopt(result, CURLOPT_HTTPAUTH, http_auth_any); #ifdef CURLGSSAPI_DELEGATION_FLAG if (curl_deleg) { @@ -1448,6 +1458,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) set_long_from_env(&http_max_retries, "GIT_HTTP_MAX_RETRIES"); set_long_from_env(&http_max_retry_time, "GIT_HTTP_MAX_RETRY_TIME"); + http_auth_methods = http_auth_any; + curl_default = get_curl_handle(); } diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh index b8cef9dd5b..822d64ed5e 100755 --- a/t/t5563-simple-http-auth.sh +++ b/t/t5563-simple-http-auth.sh @@ -730,8 +730,10 @@ test_expect_success NTLM 'access using NTLM auth' ' EOF test_config_global credential.helper test-helper && - GIT_TRACE_CURL=1 \ - git ls-remote "$HTTPD_URL/ntlm_auth/repo.git" + test_must_fail env GIT_TRACE_CURL=1 git \ + ls-remote "$HTTPD_URL/ntlm_auth/repo.git" && + GIT_TRACE_CURL=1 git -c http.$HTTPD_URL.allowNTLMAuth=true \ + ls-remote "$HTTPD_URL/ntlm_auth/repo.git" ' test_done