winhttp: support optional client cert
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index ee8cb15..9d3f05d 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;
}