diff --git a/http.c b/http.c index 54ddff03fb..906eb098c8 100644 --- a/http.c +++ b/http.c @@ -561,18 +561,34 @@ static int curl_empty_auth_enabled(void) return 0; } +struct curl_slist *http_append_auth_header(const struct credential *c, + struct curl_slist *headers) +{ + if (c->authtype && c->credential) { + struct strbuf auth = STRBUF_INIT; + strbuf_addf(&auth, "Authorization: %s %s", + c->authtype, c->credential); + headers = curl_slist_append(headers, auth.buf); + strbuf_release(&auth); + } + return headers; +} + static void init_curl_http_auth(CURL *result) { - if (!http_auth.username || !*http_auth.username) { + if ((!http_auth.username || !*http_auth.username) && + (!http_auth.credential || !*http_auth.credential)) { if (curl_empty_auth_enabled()) curl_easy_setopt(result, CURLOPT_USERPWD, ":"); return; } - credential_fill(&http_auth, 0); + credential_fill(&http_auth, 1); - curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username); - curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password); + if (http_auth.password) { + curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username); + curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password); + } } /* *var must be free-able */ @@ -586,17 +602,22 @@ static void var_override(const char **var, char *value) static void set_proxyauth_name_password(CURL *result) { + if (proxy_auth.password) { curl_easy_setopt(result, CURLOPT_PROXYUSERNAME, proxy_auth.username); curl_easy_setopt(result, CURLOPT_PROXYPASSWORD, proxy_auth.password); + } else if (proxy_auth.authtype && proxy_auth.credential) { + curl_easy_setopt(result, CURLOPT_PROXYHEADER, + http_append_auth_header(&proxy_auth, NULL)); + } } static void init_curl_proxy_auth(CURL *result) { if (proxy_auth.username) { - if (!proxy_auth.password) - credential_fill(&proxy_auth, 0); + if (!proxy_auth.password && !proxy_auth.credential) + credential_fill(&proxy_auth, 1); set_proxyauth_name_password(result); } @@ -1468,7 +1489,7 @@ struct active_request_slot *get_active_slot(void) curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve); curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods); - if (http_auth.password || curl_empty_auth_enabled()) + if (http_auth.password || http_auth.credential || curl_empty_auth_enabled()) init_curl_http_auth(slot->curl); return slot; @@ -1757,7 +1778,8 @@ static int handle_curl_result(struct slot_results *results) } else if (missing_target(results)) return HTTP_MISSING_TARGET; else if (results->http_code == 401) { - if (http_auth.username && http_auth.password) { + if ((http_auth.username && http_auth.password) ||\ + (http_auth.authtype && http_auth.credential)) { credential_reject(&http_auth); return HTTP_NOAUTH; } else { @@ -2065,11 +2087,15 @@ static int http_request(const char *url, /* Add additional headers here */ if (options && options->extra_headers) { const struct string_list_item *item; - for_each_string_list_item(item, options->extra_headers) { - headers = curl_slist_append(headers, item->string); + if (options && options->extra_headers) { + for_each_string_list_item(item, options->extra_headers) { + headers = curl_slist_append(headers, item->string); + } } } + headers = http_append_auth_header(&http_auth, headers); + curl_easy_setopt(slot->curl, CURLOPT_URL, url); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(slot->curl, CURLOPT_ENCODING, ""); @@ -2190,7 +2216,7 @@ static int http_request_reauth(const char *url, BUG("Unknown http_request target"); } - credential_fill(&http_auth, 0); + credential_fill(&http_auth, 1); return http_request(url, result, target, options); } diff --git a/http.h b/http.h index c5f8cc4620..a516ca4a9a 100644 --- a/http.h +++ b/http.h @@ -175,6 +175,9 @@ int http_get_file(const char *url, const char *filename, int http_fetch_ref(const char *base, struct ref *ref); +struct curl_slist *http_append_auth_header(const struct credential *c, + struct curl_slist *headers); + /* Helpers for fetching packs */ int http_get_info_packs(const char *base_url, struct packed_git **packs_head); diff --git a/remote-curl.c b/remote-curl.c index f96bda2431..1c5416812a 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -931,7 +931,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece if (err != HTTP_OK) return -1; - if (results.auth_avail & CURLAUTH_GSSNEGOTIATE) + if (results.auth_avail & CURLAUTH_GSSNEGOTIATE || http_auth.authtype) needs_100_continue = 1; } @@ -942,6 +942,8 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece headers = curl_slist_append(headers, needs_100_continue ? "Expect: 100-continue" : "Expect:"); + headers = http_append_auth_header(&http_auth, headers); + /* Add Accept-Language header */ if (rpc->hdr_accept_language) headers = curl_slist_append(headers, rpc->hdr_accept_language); diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh index ab8a721ccc..b3ed0d9fc2 100755 --- a/t/t5563-simple-http-auth.sh +++ b/t/t5563-simple-http-auth.sh @@ -74,6 +74,7 @@ test_expect_success 'access using basic auth' ' git ls-remote "$HTTPD_URL/custom_auth/repo.git" && expect_credential_query get <<-EOF && + capability[]=authtype protocol=http host=$HTTPD_DEST wwwauth[]=Basic realm="example.com" @@ -87,6 +88,43 @@ test_expect_success 'access using basic auth' ' EOF ' +test_expect_success 'access using basic auth via authtype' ' + test_when_finished "per_test_cleanup" && + + set_credential_reply get <<-EOF && + capability[]=authtype + authtype=Basic + credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA== + EOF + + # Basic base64(alice:secret-passwd) + cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF && + Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA== + EOF + + cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF && + WWW-Authenticate: Basic realm="example.com" + EOF + + test_config_global credential.helper test-helper && + GIT_CURL_VERBOSE=1 git ls-remote "$HTTPD_URL/custom_auth/repo.git" && + + expect_credential_query get <<-EOF && + capability[]=authtype + protocol=http + host=$HTTPD_DEST + wwwauth[]=Basic realm="example.com" + EOF + + expect_credential_query store <<-EOF + capability[]=authtype + authtype=Basic + credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA== + protocol=http + host=$HTTPD_DEST + EOF +' + test_expect_success 'access using basic auth invalid credentials' ' test_when_finished "per_test_cleanup" && @@ -108,6 +146,7 @@ test_expect_success 'access using basic auth invalid credentials' ' test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" && expect_credential_query get <<-EOF && + capability[]=authtype protocol=http host=$HTTPD_DEST wwwauth[]=Basic realm="example.com" @@ -145,6 +184,7 @@ test_expect_success 'access using basic auth with extra challenges' ' git ls-remote "$HTTPD_URL/custom_auth/repo.git" && expect_credential_query get <<-EOF && + capability[]=authtype protocol=http host=$HTTPD_DEST wwwauth[]=FooBar param1="value1" param2="value2" @@ -183,6 +223,7 @@ test_expect_success 'access using basic auth mixed-case wwwauth header name' ' git ls-remote "$HTTPD_URL/custom_auth/repo.git" && expect_credential_query get <<-EOF && + capability[]=authtype protocol=http host=$HTTPD_DEST wwwauth[]=foobar param1="value1" param2="value2" @@ -226,6 +267,7 @@ test_expect_success 'access using basic auth with wwwauth header continuations' git ls-remote "$HTTPD_URL/custom_auth/repo.git" && expect_credential_query get <<-EOF && + capability[]=authtype protocol=http host=$HTTPD_DEST wwwauth[]=FooBar param1="value1" param2="value2" @@ -271,6 +313,7 @@ test_expect_success 'access using basic auth with wwwauth header empty continuat git ls-remote "$HTTPD_URL/custom_auth/repo.git" && expect_credential_query get <<-EOF && + capability[]=authtype protocol=http host=$HTTPD_DEST wwwauth[]=FooBar param1="value1" param2="value2" @@ -312,6 +355,7 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi git ls-remote "$HTTPD_URL/custom_auth/repo.git" && expect_credential_query get <<-EOF && + capability[]=authtype protocol=http host=$HTTPD_DEST wwwauth[]=FooBar param1="value1" param2="value2" @@ -326,4 +370,93 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi EOF ' +test_expect_success 'access using bearer auth' ' + test_when_finished "per_test_cleanup" && + + set_credential_reply get <<-EOF && + capability[]=authtype + authtype=Bearer + credential=YS1naXQtdG9rZW4= + EOF + + # Basic base64(a-git-token) + cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF && + Bearer YS1naXQtdG9rZW4= + EOF + + CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" && + + cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF && + WWW-Authenticate: FooBar param1="value1" param2="value2" + WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0 + WWW-Authenticate: Basic realm="example.com" + EOF + + test_config_global credential.helper test-helper && + git ls-remote "$HTTPD_URL/custom_auth/repo.git" && + + expect_credential_query get <<-EOF && + capability[]=authtype + protocol=http + host=$HTTPD_DEST + wwwauth[]=FooBar param1="value1" param2="value2" + wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0 + wwwauth[]=Basic realm="example.com" + EOF + + expect_credential_query store <<-EOF + capability[]=authtype + authtype=Bearer + credential=YS1naXQtdG9rZW4= + protocol=http + host=$HTTPD_DEST + EOF +' + +test_expect_success 'access using bearer auth with invalid credentials' ' + test_when_finished "per_test_cleanup" && + + set_credential_reply get <<-EOF && + capability[]=authtype + authtype=Bearer + credential=incorrect-token + EOF + + # Basic base64(a-git-token) + cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF && + Bearer YS1naXQtdG9rZW4= + EOF + + CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" && + + cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF && + WWW-Authenticate: FooBar param1="value1" param2="value2" + WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0 + WWW-Authenticate: Basic realm="example.com" + EOF + + test_config_global credential.helper test-helper && + test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" && + + expect_credential_query get <<-EOF && + capability[]=authtype + protocol=http + host=$HTTPD_DEST + wwwauth[]=FooBar param1="value1" param2="value2" + wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0 + wwwauth[]=Basic realm="example.com" + EOF + + expect_credential_query erase <<-EOF + capability[]=authtype + authtype=Bearer + credential=incorrect-token + protocol=http + host=$HTTPD_DEST + wwwauth[]=FooBar param1="value1" param2="value2" + wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0 + wwwauth[]=Basic realm="example.com" + EOF +' + test_done