curl: ask for proxy credentials
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
diff --git a/src/curl_stream.c b/src/curl_stream.c
index f48dd22..98de187 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;
}