From 467e2cb1d71b9ec01878e05f71d50e87f110ec89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 2 Oct 2015 10:11:43 +0200 Subject: [PATCH] curl: ask for proxy credentials --- src/curl_stream.c | 85 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/src/curl_stream.c b/src/curl_stream.c index f48dd2249..98de187dd 100644 --- a/src/curl_stream.c +++ b/src/curl_stream.c @@ -23,6 +23,7 @@ typedef struct { git_cert_x509 cert_info; git_strarray cert_info_strings; git_proxy_options proxy; + git_cred *proxy_cred; } curl_stream; static int seterr_curl(curl_stream *s) @@ -31,21 +32,94 @@ static int seterr_curl(curl_stream *s) return -1; } +GIT_INLINE(int) error_no_credentials(void) +{ + giterr_set(GITERR_NET, "proxy authentication required, but no callback provided"); + return GIT_EAUTH; +} + +static int apply_proxy_creds(curl_stream *s) +{ + CURLcode res; + git_cred_userpass_plaintext *userpass; + + if (!s->proxy_cred) + return GIT_ENOTFOUND; + + userpass = (git_cred_userpass_plaintext *) s->proxy_cred; + if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK) + return seterr_curl(s); + if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK) + return seterr_curl(s); + + return 0; +} + +static int ask_and_apply_proxy_creds(curl_stream *s) +{ + int error; + git_proxy_options *opts = &s->proxy; + + if (!opts->credentials) + return error_no_credentials(); + + /* TODO: see if PROXYAUTH_AVAIL helps us here */ + git_cred_free(s->proxy_cred); + s->proxy_cred = NULL; + giterr_clear(); + error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload); + if (error == GIT_PASSTHROUGH) + return error_no_credentials(); + if (error < 0) { + if (!giterr_last()) + giterr_set(GITERR_NET, "proxy authentication was aborted by the user"); + return error; + } + + if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) { + giterr_set(GITERR_NET, "credentials callback returned invalid credential type"); + return -1; + } + + return apply_proxy_creds(s); +} + static int curls_connect(git_stream *stream) { curl_stream *s = (curl_stream *) stream; - long sockextr; - int failed_cert = 0; + long sockextr, connect_last = 0; + int failed_cert = 0, error; + bool retry_connect; CURLcode res; - res = curl_easy_perform(s->handle); + + /* Apply any credentials we've already established */ + error = apply_proxy_creds(s); + if (error < 0 && error != GIT_ENOTFOUND) + return seterr_curl(s); + + do { + retry_connect = 0; + res = curl_easy_perform(s->handle); + + curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last); + + /* HTTP 407 Proxy Authentication Required */ + if (connect_last == 407) { + if ((error = ask_and_apply_proxy_creds(s)) < 0) + return error; + + retry_connect = true; + } + } while (retry_connect); if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION) return seterr_curl(s); if (res == CURLE_PEER_FAILED_VERIFICATION) failed_cert = 1; - if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK) + if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK) { return seterr_curl(s); + } s->socket = sockextr; @@ -109,6 +183,9 @@ static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_op if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK) return seterr_curl(s); + if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK) + return seterr_curl(s); + return 0; }