summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2015-05-07 12:57:56 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2016-04-19 13:54:19 +0200
commit07bd3e57d9a9930727695be690c8757f79117d45 (patch)
tree487cd3795e1f0ae208d53889c9a90bbed9924d03 /src
parenta7bece2014ec043cfe58418dc13e982f79dcfcba (diff)
downloadlibgit2-07bd3e57d9a9930727695be690c8757f79117d45.tar.gz
proxy: ask the user for credentials if necessary
Diffstat (limited to 'src')
-rw-r--r--src/proxy.c16
-rw-r--r--src/proxy.h14
-rw-r--r--src/push.c2
-rw-r--r--src/remote.c26
-rw-r--r--src/transports/local.c5
-rw-r--r--src/transports/smart.c5
-rw-r--r--src/transports/smart.h1
-rw-r--r--src/transports/winhttp.c128
8 files changed, 178 insertions, 19 deletions
diff --git a/src/proxy.c b/src/proxy.c
index 2112596e9..f53ac1151 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -14,3 +14,19 @@ int git_proxy_init_options(git_proxy_options *opts, unsigned int version)
opts, version, git_proxy_options, GIT_PROXY_OPTIONS_INIT);
return 0;
}
+
+int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src)
+{
+ if (!src) {
+ git_proxy_init_options(tgt, GIT_PROXY_OPTIONS_VERSION);
+ return 0;
+ }
+
+ memcpy(tgt, src, sizeof(git_proxy_options));
+ if (src->url) {
+ tgt->url = git__strdup(src->url);
+ GITERR_CHECK_ALLOC(tgt->url);
+ }
+
+ return 0;
+}
diff --git a/src/proxy.h b/src/proxy.h
new file mode 100644
index 000000000..bf9382737
--- /dev/null
+++ b/src/proxy.h
@@ -0,0 +1,14 @@
+/*
+* Copyright (C) the libgit2 contributors. All rights reserved.
+*
+* This file is part of libgit2, distributed under the GNU GPL v2 with
+* a Linking Exception. For full terms see the included COPYING file.
+*/
+#ifndef INCLUDE_proxy_h__
+#define INCLUDE_proxy_h__
+
+#include "git2/proxy.h"
+
+extern int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src);
+
+#endif \ No newline at end of file
diff --git a/src/push.c b/src/push.c
index 0747259c8..b4901388b 100644
--- a/src/push.c
+++ b/src/push.c
@@ -639,7 +639,7 @@ int git_push_finish(git_push *push, const git_remote_callbacks *callbacks)
int error;
if (!git_remote_connected(push->remote) &&
- (error = git_remote_connect(push->remote, GIT_DIRECTION_PUSH, callbacks, push->custom_headers)) < 0)
+ (error = git_remote_connect(push->remote, GIT_DIRECTION_PUSH, callbacks, NULL, push->custom_headers)) < 0)
return error;
if ((error = filter_refs(push->remote)) < 0 ||
diff --git a/src/remote.c b/src/remote.c
index 8b7203ee2..5ff7f6826 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -695,7 +695,7 @@ static int set_transport_custom_headers(git_transport *t, const git_strarray *cu
return t->set_custom_headers(t, custom_headers);
}
-int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_strarray *custom_headers)
+int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy, const git_strarray *custom_headers)
{
git_transport *t;
const char *url;
@@ -714,6 +714,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re
payload = callbacks->payload;
}
+ if (proxy)
+ GITERR_CHECK_VERSION(proxy, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
+
t = remote->transport;
url = git_remote__urlfordirection(remote, direction);
@@ -738,7 +741,7 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re
goto on_error;
if ((error = set_transport_callbacks(t, callbacks)) < 0 ||
- (error = t->connect(t, url, credentials, payload, direction, flags)) != 0)
+ (error = t->connect(t, url, credentials, payload, proxy, direction, flags)) != 0)
goto on_error;
remote->transport = t;
@@ -896,6 +899,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL;
+ const git_proxy_options *proxy = NULL;
assert(remote);
@@ -903,10 +907,12 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks;
custom_headers = &opts->custom_headers;
+ GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
+ proxy = &opts->proxy_opts;
}
if (!git_remote_connected(remote) &&
- (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, custom_headers)) < 0)
+ (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) < 0)
goto on_error;
if (ls_to_vector(&refs, remote) < 0)
@@ -971,6 +977,7 @@ int git_remote_fetch(
git_buf reflog_msg_buf = GIT_BUF_INIT;
const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL;
+ const git_proxy_options *proxy = NULL;
if (opts) {
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
@@ -978,10 +985,12 @@ int git_remote_fetch(
custom_headers = &opts->custom_headers;
update_fetchhead = opts->update_fetchhead;
tagopt = opts->download_tags;
+ GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
+ proxy = &opts->proxy_opts;
}
/* Connect and download everything */
- if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, custom_headers)) != 0)
+ if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) != 0)
return error;
error = git_remote_download(remote, refspecs, opts);
@@ -2393,16 +2402,18 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi
git_refspec *spec;
const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL;
+ const git_proxy_options *proxy = NULL;
assert(remote);
if (opts) {
cbs = &opts->callbacks;
custom_headers = &opts->custom_headers;
+ proxy = &opts->proxy_opts;
}
if (!git_remote_connected(remote) &&
- (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, custom_headers)) < 0)
+ (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
goto cleanup;
free_refspecs(&remote->active_refspecs);
@@ -2452,16 +2463,19 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_
int error;
const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL;
+ const git_proxy_options *proxy = NULL;
if (opts) {
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks;
custom_headers = &opts->custom_headers;
+ GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
+ proxy = &opts->proxy_opts;
}
assert(remote && refspecs);
- if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, custom_headers)) < 0)
+ if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
return error;
if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
diff --git a/src/transports/local.c b/src/transports/local.c
index 1c6e5f01e..4eae9dead 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -25,6 +25,7 @@
#include "odb.h"
#include "push.h"
#include "remote.h"
+#include "proxy.h"
typedef struct {
git_transport parent;
@@ -199,6 +200,7 @@ static int local_connect(
const char *url,
git_cred_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
+ const git_proxy_options *proxy,
int direction, int flags)
{
git_repository *repo;
@@ -209,6 +211,7 @@ static int local_connect(
GIT_UNUSED(cred_acquire_cb);
GIT_UNUSED(cred_acquire_payload);
+ GIT_UNUSED(proxy);
if (t->connected)
return 0;
@@ -439,7 +442,7 @@ static int local_push(
if (!url || t->parent.close(&t->parent) < 0 ||
t->parent.connect(&t->parent, url,
- NULL, NULL, GIT_DIRECTION_PUSH, flags))
+ NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags))
goto on_error;
}
diff --git a/src/transports/smart.c b/src/transports/smart.c
index b0611c35e..a78b57218 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -8,6 +8,7 @@
#include "smart.h"
#include "refs.h"
#include "refspec.h"
+#include "proxy.h"
static int git_smart__recv_cb(gitno_buffer *buf)
{
@@ -199,6 +200,7 @@ static int git_smart__connect(
const char *url,
git_cred_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
+ const git_proxy_options *proxy,
int direction,
int flags)
{
@@ -216,6 +218,9 @@ static int git_smart__connect(
t->url = git__strdup(url);
GITERR_CHECK_ALLOC(t->url);
+ if (git_proxy_options_dup(&t->proxy, proxy) < 0)
+ return -1;
+
t->direction = direction;
t->flags = flags;
t->cred_acquire_cb = cred_acquire_cb;
diff --git a/src/transports/smart.h b/src/transports/smart.h
index 800466adf..0a0c3fc1b 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -133,6 +133,7 @@ typedef struct {
char *url;
git_cred_acquire_cb cred_acquire_cb;
void *cred_acquire_payload;
+ git_proxy_options proxy;
int direction;
int flags;
git_transport_message_cb progress_cb;
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 32b838084..22be39c6c 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -91,13 +91,39 @@ typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
gitno_connection_data connection_data;
+ gitno_connection_data proxy_connection_data;
git_cred *cred;
git_cred *url_cred;
+ git_cred *proxy_cred;
int auth_mechanism;
HINTERNET session;
HINTERNET connection;
} winhttp_subtransport;
+static int apply_basic_credential_proxy(HINTERNET request, git_cred *cred)
+{
+ git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+ wchar_t *user, *pass;
+ int error;
+
+ if ((error = git__utf8_to_16_alloc(&user, c->username)) < 0)
+ return error;
+
+ if ((error = git__utf8_to_16_alloc(&pass, c->password)) < 0)
+ return error;
+
+ if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_PROXY, WINHTTP_AUTH_SCHEME_BASIC,
+ user, pass, NULL)) {
+ giterr_set(GITERR_OS, "failed to set proxy auth");
+ error = -1;
+ }
+
+ git__free(user);
+ git__free(pass);
+
+ return error;
+}
+
static int apply_basic_credential(HINTERNET request, git_cred *cred)
{
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
@@ -271,6 +297,34 @@ static void winhttp_stream_close(winhttp_stream *s)
s->sent_request = 0;
}
+/**
+ * Extract the url and password from a URL. The outputs are pointers
+ * into the input.
+ */
+static int userpass_from_url(wchar_t **user, int *user_len,
+ wchar_t **pass, int *pass_len,
+ const wchar_t *url, int url_len)
+{
+ URL_COMPONENTS components = { 0 };
+
+ components.dwStructSize = sizeof(components);
+ /* These tell WinHttpCrackUrl that we're interested in the fields */
+ components.dwUserNameLength = 1;
+ components.dwPasswordLength = 1;
+
+ if (!WinHttpCrackUrl(url, url_len, 0, &components)) {
+ giterr_set(GITERR_OS, "failed to extract user/pass from url");
+ return -1;
+ }
+
+ *user = components.lpszUserName;
+ *user_len = components.dwUserNameLength;
+ *pass = components.lpszPassword;
+ *pass_len = components.dwPasswordLength;
+
+ return 0;
+}
+
static int winhttp_stream_connect(winhttp_stream *s)
{
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
@@ -284,6 +338,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
int default_timeout = TIMEOUT_INFINITE;
int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
size_t i;
+ const git_proxy_options *proxy_opts;
/* Prepare URL */
git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url);
@@ -317,26 +372,49 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error;
}
- /* Set proxy if necessary */
- if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
- goto on_error;
+ proxy_opts = &t->owner->proxy;
+ if (proxy_opts->type == GIT_PROXY_AUTO) {
+ /* Set proxy if necessary */
+ if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
+ goto on_error;
+ }
+ else if (proxy_opts->type == GIT_PROXY_HTTP) {
+ proxy_url = git__strdup(proxy_opts->url);
+ GITERR_CHECK_ALLOC(proxy_url);
+ }
if (proxy_url) {
+ git_buf processed_url = GIT_BUF_INIT;
WINHTTP_PROXY_INFO proxy_info;
wchar_t *proxy_wide;
- /* Convert URL to wide characters */
- int proxy_wide_len = git__utf8_to_16_alloc(&proxy_wide, proxy_url);
+ if ((error = gitno_connection_data_from_url(&t->proxy_connection_data, proxy_url, NULL)) < 0)
+ goto on_error;
- if (proxy_wide_len < 0) {
- giterr_set(GITERR_OS, "Failed to convert string to wide form");
+ if (t->proxy_connection_data.user && t->proxy_connection_data.pass) {
+ if ((error = git_cred_userpass_plaintext_new(&t->proxy_cred, t->proxy_connection_data.user, t->proxy_connection_data.pass)) < 0)
+ goto on_error;
+ }
+
+ if (t->proxy_connection_data.use_ssl)
+ git_buf_puts(&processed_url, "https://");
+ else
+ git_buf_puts(&processed_url, "http://");
+
+ git_buf_puts(&processed_url, t->proxy_connection_data.host);
+ if (t->proxy_connection_data.port)
+ git_buf_printf(&processed_url, ":%s", t->proxy_connection_data.port);
+
+ if (git_buf_oom(&processed_url)) {
+ giterr_set_oom();
+ error = -1;
goto on_error;
}
- /* Strip any trailing forward slash on the proxy URL;
- * WinHTTP doesn't like it if one is present */
- if (proxy_wide_len > 1 && L'/' == proxy_wide[proxy_wide_len - 2])
- proxy_wide[proxy_wide_len - 2] = L'\0';
+ /* Convert URL to wide characters */
+ if ((error = git__utf8_to_16_alloc(&proxy_wide, processed_url.ptr)) < 0)
+ goto on_error;
+
proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
proxy_info.lpszProxy = proxy_wide;
@@ -352,6 +430,14 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
git__free(proxy_wide);
+
+ if (t->proxy_cred) {
+ if (t->proxy_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT) {
+ if ((error = apply_basic_credential_proxy(s->request, t->proxy_cred)) < 0)
+ goto on_error;
+ }
+ }
+
}
/* Disable WinHTTP redirects so we can handle them manually. Why, you ask?
@@ -919,6 +1005,26 @@ replay:
goto replay;
}
+ /* Handle proxy authentication failures */
+ if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) {
+ int allowed_types;
+
+ if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
+ return -1;
+
+ /* TODO: extract the username from the url, no payload? */
+ if (t->owner->proxy.credentials) {
+ int cred_error = 1;
+ cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, NULL);
+
+ if (cred_error < 0)
+ return cred_error;
+ }
+
+ winhttp_stream_close(s);
+ goto replay;
+ }
+
/* Handle authentication failures */
if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) {
int allowed_types;