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
|
||||
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::
|
||||
Control GSSAPI credential delegation. The delegation is disabled
|
||||
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
|
||||
|
||||
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 const char *curl_proxyuserpwd;
|
||||
static char *curl_cookie_file;
|
||||
static int curl_save_cookies;
|
||||
struct credential http_auth = CREDENTIAL_INIT;
|
||||
static int http_proactive_auth;
|
||||
static enum proactive_auth http_proactive_auth;
|
||||
static char *user_agent;
|
||||
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 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 size = eltsize * nmemb;
|
||||
@ -539,6 +552,20 @@ static int http_options(const char *var, const char *value,
|
||||
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 */
|
||||
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) &&
|
||||
(!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, ":");
|
||||
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);
|
||||
|
||||
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_PASSWORD, http_auth.password);
|
||||
}
|
||||
@ -1050,7 +1092,7 @@ static CURL *get_curl_handle(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (http_proactive_auth)
|
||||
if (http_proactive_auth != PROACTIVE_AUTH_NONE)
|
||||
init_curl_http_auth(result);
|
||||
|
||||
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)
|
||||
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)
|
||||
curl_http_proxy = xstrdup(remote->http_proxy);
|
||||
@ -1790,6 +1833,8 @@ static int handle_curl_result(struct slot_results *results)
|
||||
return HTTP_REAUTH;
|
||||
}
|
||||
credential_reject(&http_auth);
|
||||
if (always_auth_proactively())
|
||||
http_proactive_auth = PROACTIVE_AUTH_NONE;
|
||||
return HTTP_NOAUTH;
|
||||
} else {
|
||||
http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
|
||||
@ -2186,7 +2231,12 @@ static int http_request_reauth(const char *url,
|
||||
struct http_get_options *options)
|
||||
{
|
||||
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)
|
||||
return ret;
|
||||
|
@ -178,6 +178,122 @@ test_expect_success 'access using basic auth invalid credentials' '
|
||||
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_when_finished "per_test_cleanup" &&
|
||||
|
||||
|
Reference in New Issue
Block a user