Skip to content

Commit a0fca80

Browse files
authored
Merge pull request libgit2#5814 from ianhattendorf/fix/winhttp-proxy-https
winhttp: skip certificate check if unable to send request
2 parents 752dbec + edffea1 commit a0fca80

File tree

2 files changed

+67
-27
lines changed

2 files changed

+67
-27
lines changed

src/transports/winhttp.c

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ typedef struct {
111111
DWORD post_body_len;
112112
unsigned sent_request : 1,
113113
received_response : 1,
114-
chunked : 1;
114+
chunked : 1,
115+
status_sending_request_reached: 1;
115116
} winhttp_stream;
116117

117118
typedef struct {
@@ -713,30 +714,36 @@ static void CALLBACK winhttp_status(
713714
DWORD status;
714715

715716
GIT_UNUSED(connection);
716-
GIT_UNUSED(ctx);
717717
GIT_UNUSED(info_len);
718718

719-
if (code != WINHTTP_CALLBACK_STATUS_SECURE_FAILURE)
720-
return;
721-
722-
status = *((DWORD *)info);
723-
724-
if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID))
725-
git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name");
726-
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID))
727-
git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired");
728-
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA))
729-
git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA");
730-
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT))
731-
git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid");
732-
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED))
733-
git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed");
734-
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED))
735-
git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked");
736-
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR))
737-
git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded");
738-
else
739-
git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status);
719+
switch (code) {
720+
case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
721+
status = *((DWORD *)info);
722+
723+
if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID))
724+
git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name");
725+
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID))
726+
git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired");
727+
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA))
728+
git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA");
729+
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT))
730+
git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid");
731+
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED))
732+
git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed");
733+
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED))
734+
git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked");
735+
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR))
736+
git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded");
737+
else
738+
git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status);
739+
740+
break;
741+
742+
case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
743+
((winhttp_stream *) ctx)->status_sending_request_reached = 1;
744+
745+
break;
746+
}
740747
}
741748

742749
static int winhttp_connect(
@@ -836,7 +843,12 @@ static int winhttp_connect(
836843
goto on_error;
837844
}
838845

839-
if (WinHttpSetStatusCallback(t->connection, winhttp_status, WINHTTP_CALLBACK_FLAG_SECURE_FAILURE, 0) == WINHTTP_INVALID_STATUS_CALLBACK) {
846+
if (WinHttpSetStatusCallback(
847+
t->connection,
848+
winhttp_status,
849+
WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST,
850+
0
851+
) == WINHTTP_INVALID_STATUS_CALLBACK) {
840852
git_error_set(GIT_ERROR_OS, "failed to set status callback");
841853
goto on_error;
842854
}
@@ -869,12 +881,12 @@ static int do_send_request(winhttp_stream *s, size_t len, bool chunked)
869881
success = WinHttpSendRequest(s->request,
870882
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
871883
WINHTTP_NO_REQUEST_DATA, 0,
872-
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0);
884+
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, (DWORD_PTR)s);
873885
} else {
874886
success = WinHttpSendRequest(s->request,
875887
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
876888
WINHTTP_NO_REQUEST_DATA, 0,
877-
(DWORD)len, 0);
889+
(DWORD)len, (DWORD_PTR)s);
878890
}
879891

880892
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)
911923
}
912924
}
913925

914-
if (!request_failed || !cert_valid) {
926+
/*
927+
* Only check the certificate if we were able to reach the sending request phase, or
928+
* received a secure failure error. Otherwise, the server certificate won't be available
929+
* since the request wasn't able to complete (e.g. proxy auth required)
930+
*/
931+
if (!cert_valid ||
932+
(!request_failed && s->status_sending_request_reached)) {
915933
git_error_clear();
916934
if ((error = certificate_check(s, cert_valid)) < 0) {
917935
if (!git_error_last())

tests/online/clone.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,28 @@ void test_online_clone__proxy_credentials_in_environment(void)
869869
git_buf_dispose(&url);
870870
}
871871

872+
void test_online_clone__proxy_credentials_in_url_https(void)
873+
{
874+
git_buf url = GIT_BUF_INIT;
875+
876+
if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
877+
cl_skip();
878+
879+
cl_git_pass(git_buf_printf(&url, "%s://%s:%s@%s/",
880+
_remote_proxy_scheme ? _remote_proxy_scheme : "http",
881+
_remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
882+
883+
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
884+
g_options.fetch_opts.proxy_opts.url = url.ptr;
885+
g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
886+
g_options.fetch_opts.callbacks.certificate_check = ssl_cert;
887+
called_proxy_creds = 0;
888+
cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
889+
cl_assert(called_proxy_creds == 0);
890+
891+
git_buf_dispose(&url);
892+
}
893+
872894
void test_online_clone__proxy_auto_not_detected(void)
873895
{
874896
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;

0 commit comments

Comments
 (0)