summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2014-08-30 00:40:37 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2014-09-16 17:01:31 +0200
commit08545d366b24186631b08e3c232f35222d9de5ea (patch)
treec7de24db595e433397a95957d30672daca96b80b
parent23ca0ad5ebcba3173ba3ff51e8114c33f795e62a (diff)
downloadlibgit2-08545d366b24186631b08e3c232f35222d9de5ea.tar.gz
winhttp: credential check on successful connect
On successful connection, still ask the user whether they accept the server's certificate, indicating that WinHTTP would let it though.
-rw-r--r--src/transports/winhttp.c41
-rw-r--r--tests/online/clone.c6
2 files changed, 45 insertions, 2 deletions
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 6523086da..3ddf49d60 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -16,6 +16,8 @@
#include "remote.h"
#include "repository.h"
+#include <wincrypt.h>
+#pragma comment(lib, "crypt32")
#include <winhttp.h>
#pragma comment(lib, "winhttp")
@@ -203,6 +205,31 @@ static int fallback_cred_acquire_cb(
return error;
}
+static int certificate_check(winhttp_stream *s, int valid)
+{
+ int error;
+ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+ PCERT_CONTEXT cert_ctx;
+ DWORD cert_ctx_size = sizeof(cert_ctx);
+
+ if (t->owner->certificate_check_cb == NULL)
+ return 0;
+
+ if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) {
+ giterr_set(GITERR_OS, "failed to get server certificate");
+ return -1;
+ }
+
+ giterr_clear();
+ error = t->owner->certificate_check_cb(GIT_CERT_X509, cert_ctx->pbCertEncoded, cert_ctx->cbCertEncoded, valid, t->owner->cred_acquire_payload);
+ CertFreeCertificateContext(cert_ctx);
+
+ if (error < 0 && !giterr_last())
+ giterr_set(GITERR_NET, "user cancelled certificate check");
+
+ return error;
+}
+
static int winhttp_stream_connect(winhttp_stream *s)
{
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
@@ -384,6 +411,8 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error;
}
+ /* set up the certificate failure callback */
+
/* We've done everything up to calling WinHttpSendRequest. */
error = 0;
@@ -537,6 +566,7 @@ static int winhttp_stream_read(
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
DWORD dw_bytes_read;
char replay_count = 0;
+ int error;
replay:
/* Enforce a reasonable cap on the number of replays */
@@ -566,6 +596,9 @@ replay:
s->sent_request = 1;
}
+ if ((error = certificate_check(s, 1)) < 0)
+ return error;
+
if (s->chunked) {
assert(s->verb == post_verb);
@@ -815,6 +848,7 @@ static int winhttp_stream_write_single(
winhttp_stream *s = (winhttp_stream *)stream;
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
DWORD bytes_written;
+ int error;
if (!s->request && winhttp_stream_connect(s) < 0)
return -1;
@@ -835,6 +869,9 @@ static int winhttp_stream_write_single(
s->sent_request = 1;
+ if ((error = certificate_check(s, 1)) < 0)
+ return error;
+
if (!WinHttpWriteData(s->request,
(LPCVOID)buffer,
(DWORD)len,
@@ -954,6 +991,7 @@ static int winhttp_stream_write_chunked(
{
winhttp_stream *s = (winhttp_stream *)stream;
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+ int error;
if (!s->request && winhttp_stream_connect(s) < 0)
return -1;
@@ -978,6 +1016,9 @@ static int winhttp_stream_write_chunked(
s->sent_request = 1;
}
+ if ((error = certificate_check(s, 1)) < 0)
+ return error;
+
if (len > CACHED_POST_BODY_BUF_SIZE) {
/* Flush, if necessary */
if (s->chunk_buffer_len > 0) {
diff --git a/tests/online/clone.c b/tests/online/clone.c
index a880d47d9..cebe3b2ba 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -485,11 +485,13 @@ void test_online_clone__certificate_invalid(void)
{
g_options.remote_callbacks.certificate_check = fail_certificate_check;
- cl_git_fail_with(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options),
+ cl_git_fail_with(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options),
GIT_ECERTIFICATE);
+#ifdef GIT_SSH
cl_git_fail_with(git_clone(&g_repo, "ssh://github.com/libgit2/TestGitRepository", "./foo", &g_options),
GIT_ECERTIFICATE);
+#endif
}
static int succeed_certificate_check(git_cert_t type, void *data, size_t len, int valid, void *payload)
@@ -507,5 +509,5 @@ void test_online_clone__certificate_valid(void)
{
g_options.remote_callbacks.certificate_check = succeed_certificate_check;
- cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
}