diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index fc646447d5..68823c6ed2 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -168,6 +168,7 @@ prepare_httpd() {
install_script apply-one-time-script.sh
install_script nph-custom-auth.sh
install_script http-429.sh
+ install_script ntlm-handshake.sh
ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 40a690b0bb..7a5c3620cf 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -155,6 +155,13 @@ SetEnv PERL_PATH ${PERL_PATH}
CGIPassAuth on
+
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+
+ CGIPassAuth on
+
+
ScriptAlias /smart/incomplete_length/git-upload-pack incomplete-length-upload-pack-v2-http.sh/
ScriptAlias /smart/incomplete_body/git-upload-pack incomplete-body-upload-pack-v2-http.sh/
ScriptAlias /smart/no_report/git-receive-pack error-no-report.sh/
@@ -166,6 +173,7 @@ ScriptAlias /error/ error.sh/
ScriptAliasMatch /one_time_script/(.*) apply-one-time-script.sh/$1
ScriptAliasMatch /http_429/(.*) http-429.sh/$1
ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
+ScriptAliasMatch /ntlm_auth/(.*) ntlm-handshake.sh/$1
Options FollowSymlinks
diff --git a/t/lib-httpd/ntlm-handshake.sh b/t/lib-httpd/ntlm-handshake.sh
new file mode 100755
index 0000000000..3cf1266e40
--- /dev/null
+++ b/t/lib-httpd/ntlm-handshake.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+case "$HTTP_AUTHORIZATION" in
+'')
+ # No Authorization header -> send NTLM challenge
+ echo "Status: 401 Unauthorized"
+ echo "WWW-Authenticate: NTLM"
+ echo
+ ;;
+"NTLM TlRMTVNTUAAB"*)
+ # Type 1 -> respond with Type 2 challenge (hardcoded)
+ echo "Status: 401 Unauthorized"
+ # Base64-encoded version of the Type 2 challenge:
+ # signature: 'NTLMSSP\0'
+ # message_type: 2
+ # target_name: 'NTLM-GIT-SERVER'
+ # flags: 0xa2898205 =
+ # NEGOTIATE_UNICODE, REQUEST_TARGET, NEGOTIATE_NT_ONLY,
+ # TARGET_TYPE_SERVER, TARGET_TYPE_SHARE, REQUEST_NON_NT_SESSION_KEY,
+ # NEGOTIATE_VERSION, NEGOTIATE_128, NEGOTIATE_56
+ # challenge: 0xfa3dec518896295b
+ # context: '0000000000000000'
+ # target_info_present: true
+ # target_info_len: 128
+ # version: '10.0 (build 19041)'
+ echo "WWW-Authenticate: NTLM TlRMTVNTUAACAAAAHgAeADgAAAAFgomi+j3sUYiWKVsAAAAAAAAAAIAAgABWAAAACgBhSgAAAA9OAFQATABNAC0ARwBJAFQALQBTAEUAUgBWAEUAUgACABIAVwBPAFIASwBHAFIATwBVAFAAAQAeAE4AVABMAE0ALQBHAEkAVAAtAFMARQBSAFYARQBSAAQAEgBXAE8AUgBLAEcAUgBPAFUAUAADAB4ATgBUAEwATQAtAEcASQBUAC0AUwBFAFIAVgBFAFIABwAIAACfOcZKYNwBAAAAAA=="
+ echo
+ ;;
+"NTLM TlRMTVNTUAAD"*)
+ # Type 3 -> accept without validation
+ exec "$GIT_EXEC_PATH"/git-http-backend
+ ;;
+*)
+ echo "Status: 500 Unrecognized"
+ echo
+ echo "Unhandled auth: '$HTTP_AUTHORIZATION'"
+ ;;
+esac
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
index a7d475dd68..b8cef9dd5b 100755
--- a/t/t5563-simple-http-auth.sh
+++ b/t/t5563-simple-http-auth.sh
@@ -719,78 +719,19 @@ test_expect_success 'access using three-legged auth' '
EOF
'
-test_lazy_prereq SPNEGO 'curl --version | grep -qi "SPNEGO\|GSS-API\|Kerberos\|negotiate"'
+test_lazy_prereq NTLM 'curl --version | grep -q NTLM'
-test_expect_success SPNEGO 'http.emptyAuth=auto attempts Negotiate before credential_fill' '
+test_expect_success NTLM 'access using NTLM auth' '
test_when_finished "per_test_cleanup" &&
set_credential_reply get <<-EOF &&
- username=alice
- password=secret-passwd
- EOF
-
- # Basic base64(alice:secret-passwd)
- cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
- EOF
-
- cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- id=1 status=200
- id=default response=WWW-Authenticate: Negotiate
- id=default response=WWW-Authenticate: Basic realm="example.com"
+ username=user
+ password=pwd
EOF
test_config_global credential.helper test-helper &&
- GIT_TRACE_CURL="$TRASH_DIRECTORY/trace-auto" \
- git -c http.emptyAuth=auto \
- ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
-
- # In auto mode with a Negotiate+Basic server, there should be
- # three 401 responses: (1) initial no-auth request, (2) empty-auth
- # retry where Negotiate fails (no Kerberos ticket), (3) libcurl
- # internal Negotiate retry. The fourth attempt uses Basic
- # credentials from credential_fill and succeeds.
- grep "HTTP/[0-9.]* 401" "$TRASH_DIRECTORY/trace-auto" >actual_401s &&
- test_line_count = 3 actual_401s &&
-
- expect_credential_query get <<-EOF
- capability[]=authtype
- capability[]=state
- protocol=http
- host=$HTTPD_DEST
- wwwauth[]=Negotiate
- wwwauth[]=Basic realm="example.com"
- EOF
-'
-
-test_expect_success SPNEGO 'http.emptyAuth=false skips Negotiate' '
- test_when_finished "per_test_cleanup" &&
-
- set_credential_reply get <<-EOF &&
- username=alice
- password=secret-passwd
- EOF
-
- # Basic base64(alice:secret-passwd)
- cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
- EOF
-
- cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- id=1 status=200
- id=default response=WWW-Authenticate: Negotiate
- id=default response=WWW-Authenticate: Basic realm="example.com"
- EOF
-
- test_config_global credential.helper test-helper &&
- GIT_TRACE_CURL="$TRASH_DIRECTORY/trace-false" \
- git -c http.emptyAuth=false \
- ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
-
- # With emptyAuth=false, Negotiate is stripped immediately and
- # credential_fill is called right away. Only one 401 response.
- grep "HTTP/[0-9.]* 401" "$TRASH_DIRECTORY/trace-false" >actual_401s &&
- test_line_count = 1 actual_401s
+ GIT_TRACE_CURL=1 \
+ git ls-remote "$HTTPD_URL/ntlm_auth/repo.git"
'
test_done