diff options
-rw-r--r-- | src/transports/winhttp.c | 78 |
1 files changed, 52 insertions, 26 deletions
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index ee8cb1543..9d3f05d0b 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -864,42 +864,68 @@ static int do_send_request(winhttp_stream *s, size_t len, bool chunked) static int send_request(winhttp_stream *s, size_t len, bool chunked) { - int request_failed = 0, cert_valid = 1, error = 0; - DWORD ignore_flags; + int request_failed = 1, cert_valid, client_cert_requested, error, attempts = 0; + DWORD ignore_flags, send_request_error; git_error_clear(); - if ((error = do_send_request(s, len, chunked)) < 0) { - if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) { - git_error_set(GIT_ERROR_OS, "failed to send request"); - return -1; - } - request_failed = 1; - cert_valid = 0; - } + while (request_failed && attempts++ < 3) { + request_failed = 0; + cert_valid = 1; + client_cert_requested = 0; + if ((error = do_send_request(s, len, chunked)) < 0) { + send_request_error = GetLastError(); + request_failed = 1; + switch (send_request_error) { + case ERROR_WINHTTP_SECURE_FAILURE: { + cert_valid = 0; + break; + } + case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED: { + client_cert_requested = 1; + break; + } + default: { + git_error_set(GIT_ERROR_OS, "failed to send request"); + return -1; + } + } + } - git_error_clear(); - if ((error = certificate_check(s, cert_valid)) < 0) { - if (!git_error_last()) - git_error_set(GIT_ERROR_OS, "user cancelled certificate check"); + if (!request_failed || send_request_error == ERROR_WINHTTP_SECURE_FAILURE) { + git_error_clear(); + if ((error = certificate_check(s, cert_valid)) < 0) { + if (!git_error_last()) + git_error_set(GIT_ERROR_OS, "user cancelled certificate check"); - return error; - } + return error; + } + } - /* if neither the request nor the certificate check returned errors, we're done */ - if (!request_failed) - return 0; + /* if neither the request nor the certificate check returned errors, we're done */ + if (!request_failed) + return 0; - ignore_flags = no_check_cert_flags; + if (!cert_valid) { + ignore_flags = no_check_cert_flags; + if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) { + git_error_set(GIT_ERROR_OS, "failed to set security options"); + return -1; + } + } - if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) { - git_error_set(GIT_ERROR_OS, "failed to set security options"); - return -1; + if (client_cert_requested) { + /* + * Client certificates are not supported, explicitly tell the server that + * (it's possible a client certificate was requested but is not required) + */ + if (!WinHttpSetOption(s->request, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, WINHTTP_NO_CLIENT_CERT_CONTEXT, 0)) { + git_error_set(GIT_ERROR_OS, "failed to set client cert context"); + return -1; + } + } } - if ((error = do_send_request(s, len, chunked)) < 0) - git_error_set(GIT_ERROR_OS, "failed to send request with unchecked certificate"); - return error; } |