Skip to content

Commit ed0f7a6

Browse files
aaronp24gitster
authored andcommitted
remote-curl: use auth for probe_rpc() requests too
If a large request requires post_rpc() to call probe_rpc(), the latter does not use the authorization credentials used for other requests. If this fails with an HTTP 401 error and http_auth.multistage isn't set, then the whole request just fails. For example, using git-credential-msal [1], the following attempt to clone a large repository fails partway through because the initial request to download the commit history and promisor packs succeeds, but the subsequent request to download the blobs needed to construct the working tree fails with a 401 error and the checkout fails. (lines removed for brevity) git clone --filter=blob:none https://secure-server.example/repo 11:03:26.855369 git.c:502 trace: built-in: git clone --filter=blob:none https://secure-server.example/repo Cloning into 'sw'... warning: templates not found in /home/aaron/share/git-core/templates 11:03:26.857169 run-command.c:673 trace: run_command: git remote-https origin https://secure-server.example/repo 11:03:27.012104 http.c:849 => Send header: GET repo/info/refs?service=git-upload-pack HTTP/1.1 11:03:27.049243 http.c:849 <= Recv header: HTTP/1.1 401 Unauthorized 11:03:27.049270 http.c:849 <= Recv header: WWW-Authenticate: Bearer error="invalid_request", error_description="No bearer token found in the request", msal-tenant-id="<tenant>", msal-client-id="<client>" 11:03:27.053786 run-command.c:673 trace: run_command: 'git credential-msal get' 11:03:27.952830 http.c:849 => Send header: GET repo/info/refs?service=git-upload-pack HTTP/1.1 11:03:27.952849 http.c:849 => Send header: Authorization: Bearer <redacted> 11:03:27.995419 http.c:849 <= Recv header: HTTP/1.1 200 OK 11:03:28.230039 http.c:890 == Info: Reusing existing https: connection with host secure-server.example 11:03:28.230208 http.c:849 => Send header: POST repo/git-upload-pack HTTP/1.1 11:03:28.230216 http.c:849 => Send header: Content-Type: application/x-git-upload-pack-request 11:03:28.230221 http.c:849 => Send header: Authorization: Bearer <redacted> 11:03:28.269085 http.c:849 <= Recv header: HTTP/1.1 200 OK 11:03:28.684163 http.c:890 == Info: Reusing existing https: connection with host secure-server.example 11:03:28.684379 http.c:849 => Send header: POST repo/git-upload-pack HTTP/1.1 11:03:28.684391 http.c:849 => Send header: Accept: application/x-git-upload-pack-result 11:03:28.684393 http.c:849 => Send header: Authorization: Bearer <redacted> 11:03:28.869546 run-command.c:673 trace: run_command: git index-pack --stdin --fix-thin '--keep=fetch-pack 43856 on dgx-spark' --promisor 11:06:39.861237 run-command.c:673 trace: run_command: git -c fetch.negotiationAlgorithm=noop fetch origin --no-tags --no-write-fetch-head --recurse-submodules=no --filter=blob:none --stdin 11:06:39.865981 run-command.c:673 trace: run_command: git remote-https origin https://secure-server.example/repo 11:06:39.868039 run-command.c:673 trace: run_command: git-remote-https origin https://secure-server.example/repo 11:07:30.412575 http.c:849 => Send header: GET repo/info/refs?service=git-upload-pack HTTP/1.1 11:07:30.456285 http.c:849 <= Recv header: HTTP/1.1 401 Unauthorized 11:07:30.456318 http.c:849 <= Recv header: WWW-Authenticate: Bearer error="invalid_request", error_description="No bearer token found in the request", msal-tenant-id="<tenant>", msal-client-id="<client>" 11:07:30.456439 run-command.c:673 trace: run_command: 'git credential-cache get' 11:07:30.461266 http.c:849 => Send header: GET repo/info/refs?service=git-upload-pack HTTP/1.1 11:07:30.461282 http.c:849 => Send header: Authorization: Bearer <redacted> 11:07:30.501628 http.c:849 <= Recv header: HTTP/1.1 200 OK 11:07:34.725262 http.c:849 => Send header: POST repo/git-upload-pack HTTP/1.1 11:07:34.725279 http.c:849 => Send header: Content-Type: application/x-git-upload-pack-request 11:07:34.761407 http.c:849 <= Recv header: HTTP/1.1 401 Unauthorized 11:07:34.761443 http.c:890 == Info: Bearer authentication problem, ignoring. 11:07:34.761453 http.c:849 <= Recv header: WWW-Authenticate: Bearer error="invalid_request", error_description="No bearer token found in the request", msal-tenant-id="<tenant>", msal-client-id="<client>" 11:07:34.761509 http.c:890 == Info: The requested URL returned error: 401 11:07:34.761530 http.c:890 == Info: closing connection #0 11:07:34.761913 run-command.c:673 trace: run_command: 'git credential-cache erase' 11:07:34.761927 run-command.c:765 trace: start_command: /bin/sh -c 'git credential-cache erase' 'git credential-cache erase' 11:07:34.768069 git.c:502 trace: built-in: git credential-cache erase 11:07:34.768690 run-command.c:673 trace: run_command: 'git credential-msal erase' 11:07:34.768713 run-command.c:765 trace: start_command: /bin/sh -c 'git credential-msal erase' 'git credential-msal erase' 11:07:34.772742 git.c:808 trace: exec: git-credential-msal erase 11:07:34.772783 run-command.c:673 trace: run_command: git-credential-msal erase 11:07:34.772819 run-command.c:765 trace: start_command: /usr/bin/git-credential-msal erase error: RPC failed; HTTP 401 curl 22 The requested URL returned error: 401 fatal: unable to write request to remote: Broken pipe fatal: could not fetch c4fff0229c9be06ecf576356a4d39a8a755b8d81 from promisor remote warning: Clone succeeded, but checkout failed. You can inspect what was checked out with 'git status' and retry with 'git restore --source=HEAD :/' In this case, the HTTP_REAUTH retry logic is not used because the credential helper didn't set the 'continue' flag, so http_auth.multistage is false and handle_curl_result() fails with HTTP_NOAUTH instead. Fix the immediate problem by including the authorization headers in the probe_rpc() request as well. Add a test for this scenario: 1. Create a repository with two thousand refs. 2. Clone that into the web root used by t5563-simple-http-auth.sh. 3. Configure http.postBuffer to be very small in order to trigger the probe_rpc() path that fails. 4. Clone using a valid Bearer token. [1] https://github.com/Binary-Eater/git-credential-msal Tested-by: Lucas De Marchi <[email protected]> Signed-off-by: Aaron Plattner <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9a2fb14 commit ed0f7a6

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

remote-curl.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,7 @@ static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
876876

877877
headers = curl_slist_append(headers, rpc->hdr_content_type);
878878
headers = curl_slist_append(headers, rpc->hdr_accept);
879+
headers = http_append_auth_header(&http_auth, headers);
879880

880881
curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0L);
881882
curl_easy_setopt(slot->curl, CURLOPT_POST, 1L);

t/t5563-simple-http-auth.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,51 @@ test_expect_success 'access using bearer auth with invalid credentials' '
605605
EOF
606606
'
607607

608+
test_expect_success 'clone with bearer auth and probe_rpc' '
609+
test_when_finished "per_test_cleanup" &&
610+
test_when_finished "rm -rf large.git" &&
611+
612+
# Set up a repository large enough to trigger probe_rpc
613+
git init large.git &&
614+
(
615+
cd large.git &&
616+
git config set maintenance.auto false &&
617+
git commit --allow-empty --message "initial" &&
618+
# Create many refs to trigger probe_rpc, which is called when
619+
# the request body is larger than http.postBuffer.
620+
#
621+
# In the test later, http.postBuffer is set to 70000. Each
622+
# "want" line is ~45 bytes, so we need at least 70000/45 = ~1600
623+
# refs
624+
test_seq -f "create refs/heads/branch-%d @" 2000 |
625+
git update-ref --stdin
626+
) &&
627+
git clone --bare large.git "$HTTPD_DOCUMENT_ROOT_PATH/large.git" &&
628+
629+
# Clone it through HTTP with a Bearer token
630+
set_credential_reply get <<-EOF &&
631+
capability[]=authtype
632+
authtype=Bearer
633+
credential=YS1naXQtdG9rZW4=
634+
EOF
635+
636+
# Bearer token
637+
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
638+
id=1 creds=Bearer YS1naXQtdG9rZW4=
639+
EOF
640+
641+
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
642+
id=1 status=200
643+
id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com"
644+
EOF
645+
646+
# Set a small buffer to force probe_rpc to be called
647+
# Must be > LARGE_PACKET_MAX (65520)
648+
test_config_global http.postBuffer 70000 &&
649+
test_config_global credential.helper test-helper &&
650+
git clone "$HTTPD_URL/custom_auth/large.git" partial-auth-clone 2>clone-error
651+
'
652+
608653
test_expect_success 'access using three-legged auth' '
609654
test_when_finished "per_test_cleanup" &&
610655

0 commit comments

Comments
 (0)