Merge branch 'bc/http-proactive-auth'
The http transport can now be told to send request with authentication material without first getting a 401 response. * bc/http-proactive-auth: http: allow authenticating proactively
This commit is contained in:
@ -56,6 +56,26 @@ http.emptyAuth::
|
|||||||
a username in the URL, as libcurl normally requires a username for
|
a username in the URL, as libcurl normally requires a username for
|
||||||
authentication.
|
authentication.
|
||||||
|
|
||||||
|
http.proactiveAuth::
|
||||||
|
Attempt authentication without first making an unauthenticated attempt and
|
||||||
|
receiving a 401 response. This can be used to ensure that all requests are
|
||||||
|
authenticated. If `http.emptyAuth` is set to true, this value has no effect.
|
||||||
|
+
|
||||||
|
If the credential helper used specifies an authentication scheme (i.e., via the
|
||||||
|
`authtype` field), that value will be used; if a username and password is
|
||||||
|
provided without a scheme, then Basic authentication is used. The value of the
|
||||||
|
option determines the scheme requested from the helper. Possible values are:
|
||||||
|
+
|
||||||
|
--
|
||||||
|
* `basic` - Request Basic authentication from the helper.
|
||||||
|
* `auto` - Allow the helper to pick an appropriate scheme.
|
||||||
|
* `none` - Disable proactive authentication.
|
||||||
|
--
|
||||||
|
+
|
||||||
|
Note that TLS should always be used with this configuration, since otherwise it
|
||||||
|
is easy to accidentally expose plaintext credentials if Basic authentication
|
||||||
|
is selected.
|
||||||
|
|
||||||
http.delegation::
|
http.delegation::
|
||||||
Control GSSAPI credential delegation. The delegation is disabled
|
Control GSSAPI credential delegation. The delegation is disabled
|
||||||
by default in libcurl since version 7.21.7. Set parameter to tell
|
by default in libcurl since version 7.21.7. Set parameter to tell
|
||||||
|
62
http.c
62
http.c
@ -108,12 +108,19 @@ static struct {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum proactive_auth {
|
||||||
|
PROACTIVE_AUTH_NONE = 0,
|
||||||
|
PROACTIVE_AUTH_IF_CREDENTIALS,
|
||||||
|
PROACTIVE_AUTH_AUTO,
|
||||||
|
PROACTIVE_AUTH_BASIC,
|
||||||
|
};
|
||||||
|
|
||||||
static struct credential proxy_auth = CREDENTIAL_INIT;
|
static struct credential proxy_auth = CREDENTIAL_INIT;
|
||||||
static const char *curl_proxyuserpwd;
|
static const char *curl_proxyuserpwd;
|
||||||
static char *curl_cookie_file;
|
static char *curl_cookie_file;
|
||||||
static int curl_save_cookies;
|
static int curl_save_cookies;
|
||||||
struct credential http_auth = CREDENTIAL_INIT;
|
struct credential http_auth = CREDENTIAL_INIT;
|
||||||
static int http_proactive_auth;
|
static enum proactive_auth http_proactive_auth;
|
||||||
static char *user_agent;
|
static char *user_agent;
|
||||||
static int curl_empty_auth = -1;
|
static int curl_empty_auth = -1;
|
||||||
|
|
||||||
@ -148,6 +155,12 @@ static int http_schannel_check_revoke = 1;
|
|||||||
*/
|
*/
|
||||||
static int http_schannel_use_ssl_cainfo;
|
static int http_schannel_use_ssl_cainfo;
|
||||||
|
|
||||||
|
static int always_auth_proactively(void)
|
||||||
|
{
|
||||||
|
return http_proactive_auth != PROACTIVE_AUTH_NONE &&
|
||||||
|
http_proactive_auth != PROACTIVE_AUTH_IF_CREDENTIALS;
|
||||||
|
}
|
||||||
|
|
||||||
size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
|
size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
|
||||||
{
|
{
|
||||||
size_t size = eltsize * nmemb;
|
size_t size = eltsize * nmemb;
|
||||||
@ -539,6 +552,20 @@ static int http_options(const char *var, const char *value,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp("http.proactiveauth", var)) {
|
||||||
|
if (!value)
|
||||||
|
return config_error_nonbool(var);
|
||||||
|
if (!strcmp(value, "auto"))
|
||||||
|
http_proactive_auth = PROACTIVE_AUTH_AUTO;
|
||||||
|
else if (!strcmp(value, "basic"))
|
||||||
|
http_proactive_auth = PROACTIVE_AUTH_BASIC;
|
||||||
|
else if (!strcmp(value, "none"))
|
||||||
|
http_proactive_auth = PROACTIVE_AUTH_NONE;
|
||||||
|
else
|
||||||
|
warning(_("Unknown value for http.proactiveauth"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fall back on the default ones */
|
/* Fall back on the default ones */
|
||||||
return git_default_config(var, value, ctx, data);
|
return git_default_config(var, value, ctx, data);
|
||||||
}
|
}
|
||||||
@ -580,14 +607,29 @@ 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)) {
|
(!http_auth.credential || !*http_auth.credential)) {
|
||||||
if (curl_empty_auth_enabled())
|
int empty_auth = curl_empty_auth_enabled();
|
||||||
|
if ((empty_auth != -1 && !always_auth_proactively()) || empty_auth == 1) {
|
||||||
curl_easy_setopt(result, CURLOPT_USERPWD, ":");
|
curl_easy_setopt(result, CURLOPT_USERPWD, ":");
|
||||||
return;
|
return;
|
||||||
|
} else if (!always_auth_proactively()) {
|
||||||
|
return;
|
||||||
|
} else if (http_proactive_auth == PROACTIVE_AUTH_BASIC) {
|
||||||
|
strvec_push(&http_auth.wwwauth_headers, "Basic");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
credential_fill(&http_auth, 1);
|
credential_fill(&http_auth, 1);
|
||||||
|
|
||||||
if (http_auth.password) {
|
if (http_auth.password) {
|
||||||
|
if (always_auth_proactively()) {
|
||||||
|
/*
|
||||||
|
* We got a credential without an authtype and we don't
|
||||||
|
* know what's available. Since our only two options at
|
||||||
|
* the moment are auto (which defaults to basic) and
|
||||||
|
* basic, use basic for now.
|
||||||
|
*/
|
||||||
|
curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||||
|
}
|
||||||
curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
|
curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
|
||||||
curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
|
curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
|
||||||
}
|
}
|
||||||
@ -1050,7 +1092,7 @@ static CURL *get_curl_handle(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (http_proactive_auth)
|
if (http_proactive_auth != PROACTIVE_AUTH_NONE)
|
||||||
init_curl_http_auth(result);
|
init_curl_http_auth(result);
|
||||||
|
|
||||||
if (getenv("GIT_SSL_VERSION"))
|
if (getenv("GIT_SSL_VERSION"))
|
||||||
@ -1294,7 +1336,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
|
|||||||
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
|
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
|
||||||
die("curl_global_init failed");
|
die("curl_global_init failed");
|
||||||
|
|
||||||
http_proactive_auth = proactive_auth;
|
if (proactive_auth && http_proactive_auth == PROACTIVE_AUTH_NONE)
|
||||||
|
http_proactive_auth = PROACTIVE_AUTH_IF_CREDENTIALS;
|
||||||
|
|
||||||
if (remote && remote->http_proxy)
|
if (remote && remote->http_proxy)
|
||||||
curl_http_proxy = xstrdup(remote->http_proxy);
|
curl_http_proxy = xstrdup(remote->http_proxy);
|
||||||
@ -1790,6 +1833,8 @@ static int handle_curl_result(struct slot_results *results)
|
|||||||
return HTTP_REAUTH;
|
return HTTP_REAUTH;
|
||||||
}
|
}
|
||||||
credential_reject(&http_auth);
|
credential_reject(&http_auth);
|
||||||
|
if (always_auth_proactively())
|
||||||
|
http_proactive_auth = PROACTIVE_AUTH_NONE;
|
||||||
return HTTP_NOAUTH;
|
return HTTP_NOAUTH;
|
||||||
} else {
|
} else {
|
||||||
http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
|
http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
|
||||||
@ -2186,7 +2231,12 @@ static int http_request_reauth(const char *url,
|
|||||||
struct http_get_options *options)
|
struct http_get_options *options)
|
||||||
{
|
{
|
||||||
int i = 3;
|
int i = 3;
|
||||||
int ret = http_request(url, result, target, options);
|
int ret;
|
||||||
|
|
||||||
|
if (always_auth_proactively())
|
||||||
|
credential_fill(&http_auth, 1);
|
||||||
|
|
||||||
|
ret = http_request(url, result, target, options);
|
||||||
|
|
||||||
if (ret != HTTP_OK && ret != HTTP_REAUTH)
|
if (ret != HTTP_OK && ret != HTTP_REAUTH)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -178,6 +178,122 @@ test_expect_success 'access using basic auth invalid credentials' '
|
|||||||
EOF
|
EOF
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'access using basic proactive 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 status=403
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_config_global credential.helper test-helper &&
|
||||||
|
test_config_global http.proactiveAuth basic &&
|
||||||
|
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
|
||||||
|
|
||||||
|
expect_credential_query get <<-EOF &&
|
||||||
|
capability[]=authtype
|
||||||
|
capability[]=state
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
wwwauth[]=Basic
|
||||||
|
EOF
|
||||||
|
|
||||||
|
expect_credential_query store <<-EOF
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'access using auto proactive auth with basic default' '
|
||||||
|
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 status=403
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_config_global credential.helper test-helper &&
|
||||||
|
test_config_global http.proactiveAuth auto &&
|
||||||
|
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
|
||||||
|
|
||||||
|
expect_credential_query get <<-EOF &&
|
||||||
|
capability[]=authtype
|
||||||
|
capability[]=state
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
EOF
|
||||||
|
|
||||||
|
expect_credential_query store <<-EOF
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'access using auto proactive auth with authtype from credential helper' '
|
||||||
|
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 &&
|
||||||
|
id=1 creds=Bearer YS1naXQtdG9rZW4=
|
||||||
|
EOF
|
||||||
|
|
||||||
|
CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
|
||||||
|
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
|
||||||
|
id=1 status=200
|
||||||
|
id=default status=403
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_config_global credential.helper test-helper &&
|
||||||
|
test_config_global http.proactiveAuth auto &&
|
||||||
|
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
|
||||||
|
|
||||||
|
expect_credential_query get <<-EOF &&
|
||||||
|
capability[]=authtype
|
||||||
|
capability[]=state
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
EOF
|
||||||
|
|
||||||
|
expect_credential_query store <<-EOF
|
||||||
|
capability[]=authtype
|
||||||
|
authtype=Bearer
|
||||||
|
credential=YS1naXQtdG9rZW4=
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'access using basic auth with extra challenges' '
|
test_expect_success 'access using basic auth with extra challenges' '
|
||||||
test_when_finished "per_test_cleanup" &&
|
test_when_finished "per_test_cleanup" &&
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user