diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2021-03-10 11:19:14 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-10 11:19:14 +0000 |
commit | a0fca809aca16f080b2e7446b2bcba37c52fe27e (patch) | |
tree | a3c36930ac6045eff3410deb38f50a7ebea9fd6e | |
parent | 752dbecf9b0969145ed13dee11cf64fd515ef3c8 (diff) | |
parent | edffea1548d87cdb1c684792195166e7a1b36a4a (diff) | |
download | libgit2-a0fca809aca16f080b2e7446b2bcba37c52fe27e.tar.gz |
Merge pull request #5814 from ianhattendorf/fix/winhttp-proxy-https
winhttp: skip certificate check if unable to send request
-rw-r--r-- | src/transports/winhttp.c | 72 | ||||
-rw-r--r-- | tests/online/clone.c | 22 |
2 files changed, 67 insertions, 27 deletions
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index a2a71be95..3da625aca 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -111,7 +111,8 @@ typedef struct { DWORD post_body_len; unsigned sent_request : 1, received_response : 1, - chunked : 1; + chunked : 1, + status_sending_request_reached: 1; } winhttp_stream; typedef struct { @@ -713,30 +714,36 @@ static void CALLBACK winhttp_status( DWORD status; GIT_UNUSED(connection); - GIT_UNUSED(ctx); GIT_UNUSED(info_len); - if (code != WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) - return; - - status = *((DWORD *)info); - - if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)) - git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name"); - else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)) - git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired"); - else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)) - git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA"); - else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT)) - git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid"); - else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)) - git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed"); - else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED)) - git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked"); - else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR)) - git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded"); - else - git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status); + switch (code) { + case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: + status = *((DWORD *)info); + + if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)) + git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)) + git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)) + git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT)) + git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)) + git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED)) + git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR)) + git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded"); + else + git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status); + + break; + + case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: + ((winhttp_stream *) ctx)->status_sending_request_reached = 1; + + break; + } } static int winhttp_connect( @@ -836,7 +843,12 @@ static int winhttp_connect( goto on_error; } - if (WinHttpSetStatusCallback(t->connection, winhttp_status, WINHTTP_CALLBACK_FLAG_SECURE_FAILURE, 0) == WINHTTP_INVALID_STATUS_CALLBACK) { + if (WinHttpSetStatusCallback( + t->connection, + winhttp_status, + WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST, + 0 + ) == WINHTTP_INVALID_STATUS_CALLBACK) { git_error_set(GIT_ERROR_OS, "failed to set status callback"); goto on_error; } @@ -869,12 +881,12 @@ static int do_send_request(winhttp_stream *s, size_t len, bool chunked) success = WinHttpSendRequest(s->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0); + WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, (DWORD_PTR)s); } else { success = WinHttpSendRequest(s->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, - (DWORD)len, 0); + (DWORD)len, (DWORD_PTR)s); } if (success || GetLastError() != (DWORD)SEC_E_BUFFER_TOO_SMALL) @@ -911,7 +923,13 @@ static int send_request(winhttp_stream *s, size_t len, bool chunked) } } - if (!request_failed || !cert_valid) { + /* + * Only check the certificate if we were able to reach the sending request phase, or + * received a secure failure error. Otherwise, the server certificate won't be available + * since the request wasn't able to complete (e.g. proxy auth required) + */ + if (!cert_valid || + (!request_failed && s->status_sending_request_reached)) { git_error_clear(); if ((error = certificate_check(s, cert_valid)) < 0) { if (!git_error_last()) diff --git a/tests/online/clone.c b/tests/online/clone.c index c62baac2d..6de687b35 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -869,6 +869,28 @@ void test_online_clone__proxy_credentials_in_environment(void) git_buf_dispose(&url); } +void test_online_clone__proxy_credentials_in_url_https(void) +{ + git_buf url = GIT_BUF_INIT; + + if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass) + cl_skip(); + + cl_git_pass(git_buf_printf(&url, "%s://%s:%s@%s/", + _remote_proxy_scheme ? _remote_proxy_scheme : "http", + _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host)); + + g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED; + g_options.fetch_opts.proxy_opts.url = url.ptr; + g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb; + g_options.fetch_opts.callbacks.certificate_check = ssl_cert; + called_proxy_creds = 0; + cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options)); + cl_assert(called_proxy_creds == 0); + + git_buf_dispose(&url); +} + void test_online_clone__proxy_auto_not_detected(void) { g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO; |