Commit 08545d36 by Carlos Martín Nieto

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.
parent 23ca0ad5
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include "remote.h" #include "remote.h"
#include "repository.h" #include "repository.h"
#include <wincrypt.h>
#pragma comment(lib, "crypt32")
#include <winhttp.h> #include <winhttp.h>
#pragma comment(lib, "winhttp") #pragma comment(lib, "winhttp")
...@@ -203,6 +205,31 @@ static int fallback_cred_acquire_cb( ...@@ -203,6 +205,31 @@ static int fallback_cred_acquire_cb(
return error; 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) static int winhttp_stream_connect(winhttp_stream *s)
{ {
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
...@@ -384,6 +411,8 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -384,6 +411,8 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error; goto on_error;
} }
/* set up the certificate failure callback */
/* We've done everything up to calling WinHttpSendRequest. */ /* We've done everything up to calling WinHttpSendRequest. */
error = 0; error = 0;
...@@ -537,6 +566,7 @@ static int winhttp_stream_read( ...@@ -537,6 +566,7 @@ static int winhttp_stream_read(
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
DWORD dw_bytes_read; DWORD dw_bytes_read;
char replay_count = 0; char replay_count = 0;
int error;
replay: replay:
/* Enforce a reasonable cap on the number of replays */ /* Enforce a reasonable cap on the number of replays */
...@@ -566,6 +596,9 @@ replay: ...@@ -566,6 +596,9 @@ replay:
s->sent_request = 1; s->sent_request = 1;
} }
if ((error = certificate_check(s, 1)) < 0)
return error;
if (s->chunked) { if (s->chunked) {
assert(s->verb == post_verb); assert(s->verb == post_verb);
...@@ -815,6 +848,7 @@ static int winhttp_stream_write_single( ...@@ -815,6 +848,7 @@ static int winhttp_stream_write_single(
winhttp_stream *s = (winhttp_stream *)stream; winhttp_stream *s = (winhttp_stream *)stream;
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
DWORD bytes_written; DWORD bytes_written;
int error;
if (!s->request && winhttp_stream_connect(s) < 0) if (!s->request && winhttp_stream_connect(s) < 0)
return -1; return -1;
...@@ -835,6 +869,9 @@ static int winhttp_stream_write_single( ...@@ -835,6 +869,9 @@ static int winhttp_stream_write_single(
s->sent_request = 1; s->sent_request = 1;
if ((error = certificate_check(s, 1)) < 0)
return error;
if (!WinHttpWriteData(s->request, if (!WinHttpWriteData(s->request,
(LPCVOID)buffer, (LPCVOID)buffer,
(DWORD)len, (DWORD)len,
...@@ -954,6 +991,7 @@ static int winhttp_stream_write_chunked( ...@@ -954,6 +991,7 @@ static int winhttp_stream_write_chunked(
{ {
winhttp_stream *s = (winhttp_stream *)stream; winhttp_stream *s = (winhttp_stream *)stream;
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
int error;
if (!s->request && winhttp_stream_connect(s) < 0) if (!s->request && winhttp_stream_connect(s) < 0)
return -1; return -1;
...@@ -978,6 +1016,9 @@ static int winhttp_stream_write_chunked( ...@@ -978,6 +1016,9 @@ static int winhttp_stream_write_chunked(
s->sent_request = 1; s->sent_request = 1;
} }
if ((error = certificate_check(s, 1)) < 0)
return error;
if (len > CACHED_POST_BODY_BUF_SIZE) { if (len > CACHED_POST_BODY_BUF_SIZE) {
/* Flush, if necessary */ /* Flush, if necessary */
if (s->chunk_buffer_len > 0) { if (s->chunk_buffer_len > 0) {
......
...@@ -485,11 +485,13 @@ void test_online_clone__certificate_invalid(void) ...@@ -485,11 +485,13 @@ void test_online_clone__certificate_invalid(void)
{ {
g_options.remote_callbacks.certificate_check = fail_certificate_check; 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); GIT_ECERTIFICATE);
#ifdef GIT_SSH
cl_git_fail_with(git_clone(&g_repo, "ssh://github.com/libgit2/TestGitRepository", "./foo", &g_options), cl_git_fail_with(git_clone(&g_repo, "ssh://github.com/libgit2/TestGitRepository", "./foo", &g_options),
GIT_ECERTIFICATE); GIT_ECERTIFICATE);
#endif
} }
static int succeed_certificate_check(git_cert_t type, void *data, size_t len, int valid, void *payload) 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) ...@@ -507,5 +509,5 @@ void test_online_clone__certificate_valid(void)
{ {
g_options.remote_callbacks.certificate_check = succeed_certificate_check; 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));
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment