diff options
Diffstat (limited to 'Utilities/cmcurl/lib/vtls/schannel.c')
-rw-r--r-- | Utilities/cmcurl/lib/vtls/schannel.c | 539 |
1 files changed, 336 insertions, 203 deletions
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 0818d947a4..1996526066 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -27,16 +27,6 @@ * but vtls.c should ever call or use these functions. */ -/* - * Based upon the PolarSSL implementation in polarssl.c and polarssl.h: - * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> - * - * Based upon the CyaSSL implementation in cyassl.c and cyassl.h: - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * Thanks for code and inspiration! - */ - #include "curl_setup.h" #ifdef USE_SCHANNEL @@ -49,10 +39,11 @@ #include "schannel.h" #include "vtls.h" +#include "strcase.h" #include "sendf.h" #include "connect.h" /* for the connect timeout */ #include "strerror.h" -#include "select.h" /* for the socket readyness */ +#include "select.h" /* for the socket readiness */ #include "inet_pton.h" /* for IP addr SNI check */ #include "curl_multibyte.h" #include "warnless.h" @@ -61,7 +52,7 @@ #include "multiif.h" #include "system_win32.h" - /* The last #include file should be: */ +/* The last #include file should be: */ #include "curl_memory.h" #include "memdebug.h" @@ -179,25 +170,25 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn) long i = ssl_version; switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; - break; + case CURL_SSLVERSION_MAX_NONE: + case CURL_SSLVERSION_MAX_DEFAULT: + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; + break; } for(; i <= (ssl_version_max >> 16); ++i) { switch(i) { - case CURL_SSLVERSION_TLSv1_0: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_1: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_2: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "schannel: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; + case CURL_SSLVERSION_TLSv1_0: + schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_1: + schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_2: + schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_3: + failf(data, "schannel: TLS 1.3 is not yet supported"); + return CURLE_SSL_CONNECT_ERROR; } } return CURLE_OK; @@ -205,9 +196,9 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn) /*longest is 26, buffer is slightly bigger*/ #define LONGEST_ALG_ID 32 -#define CIPHEROPTION(X) \ -if(strcmp(#X, tmp) == 0) \ - return X +#define CIPHEROPTION(X) \ + if(strcmp(#X, tmp) == 0) \ + return X static int get_alg_id_by_name(char *name) @@ -283,11 +274,11 @@ get_alg_id_by_name(char *name) #ifdef CALG_HMAC CIPHEROPTION(CALG_HMAC); #endif -#if !defined(__W32API_MAJOR_VERSION) || \ - !defined(__W32API_MINOR_VERSION) || \ - defined(__MINGW64_VERSION_MAJOR) || \ - (__W32API_MAJOR_VERSION > 5) || \ - ((__W32API_MAJOR_VERSION == 5) && (__W32API_MINOR_VERSION > 0)) +#if !defined(__W32API_MAJOR_VERSION) || \ + !defined(__W32API_MINOR_VERSION) || \ + defined(__MINGW64_VERSION_MAJOR) || \ + (__W32API_MAJOR_VERSION > 5) || \ + ((__W32API_MAJOR_VERSION == 5) && (__W32API_MINOR_VERSION > 0)) /* CALG_TLS1PRF has a syntax error in MinGW's w32api up to version 5.0, see https://osdn.net/projects/mingw/ticket/38391 */ CIPHEROPTION(CALG_TLS1PRF); @@ -349,7 +340,7 @@ set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers) if(startCur) startCur++; } - schannel_cred->palgSupportedAlgs = algIds; + schannel_cred->palgSupportedAlgs = algIds; schannel_cred->cSupportedAlgs = algCount; return CURLE_OK; } @@ -434,8 +425,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) #endif TCHAR *host_name; CURLcode result; +#ifndef CURL_DISABLE_PROXY char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; +#else + char * const hostname = conn->host.name; +#endif DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n", @@ -443,20 +438,20 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) if(Curl_verify_windows_version(5, 1, PLATFORM_WINNT, VERSION_LESS_THAN_EQUAL)) { - /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and - algorithms that may not be supported by all servers. */ - infof(data, "schannel: Windows version is old and may not be able to " - "connect to some servers due to lack of SNI, algorithms, etc.\n"); + /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and + algorithms that may not be supported by all servers. */ + infof(data, "schannel: Windows version is old and may not be able to " + "connect to some servers due to lack of SNI, algorithms, etc.\n"); } #ifdef HAS_ALPN /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above. Also it doesn't seem to be supported for Wine, see curl bug #983. */ BACKEND->use_alpn = conn->bits.tls_enable_alpn && - !GetProcAddress(GetModuleHandleA("ntdll"), - "wine_get_version") && - Curl_verify_windows_version(6, 3, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL); + !GetProcAddress(GetModuleHandleA("ntdll"), + "wine_get_version") && + Curl_verify_windows_version(6, 3, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL); #else BACKEND->use_alpn = false; #endif @@ -530,8 +525,15 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) DEBUGF(infof(data, "schannel: disabled server certificate revocation " "checks\n")); } + else if(data->set.ssl.revoke_best_effort) { + schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN; + + DEBUGF(infof(data, "schannel: ignore revocation offline errors")); + } else { schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; + DEBUGF(infof(data, "schannel: checking server certificate revocation\n")); } @@ -558,12 +560,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: - { - result = set_ssl_version_min_max(&schannel_cred, conn); - if(result != CURLE_OK) - return result; - break; - } + { + result = set_ssl_version_min_max(&schannel_cred, conn); + if(result != CURLE_OK) + return result; + break; + } case CURL_SSLVERSION_SSLv3: schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT; break; @@ -586,68 +588,186 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) #ifdef HAS_CLIENT_CERT_PATH /* client certificate */ - if(data->set.ssl.cert) { - DWORD cert_store_name; - TCHAR *cert_store_path; - TCHAR *cert_thumbprint_str; + if(data->set.ssl.cert || data->set.ssl.cert_blob) { + DWORD cert_store_name = 0; + TCHAR *cert_store_path = NULL; + TCHAR *cert_thumbprint_str = NULL; CRYPT_HASH_BLOB cert_thumbprint; BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; - HCERTSTORE cert_store; + HCERTSTORE cert_store = NULL; + FILE *fInCert = NULL; + void *certdata = NULL; + size_t certsize = 0; + bool blob = data->set.ssl.cert_blob != NULL; + TCHAR *cert_path = NULL; + if(blob) { + certdata = data->set.ssl.cert_blob->data; + certsize = data->set.ssl.cert_blob->len; + } + else { + cert_path = curlx_convert_UTF8_to_tchar(data->set.ssl.cert); + if(!cert_path) + return CURLE_OUT_OF_MEMORY; - TCHAR *cert_path = Curl_convert_UTF8_to_tchar(data->set.ssl.cert); - if(!cert_path) - return CURLE_OUT_OF_MEMORY; + result = get_cert_location(cert_path, &cert_store_name, + &cert_store_path, &cert_thumbprint_str); - result = get_cert_location(cert_path, &cert_store_name, - &cert_store_path, &cert_thumbprint_str); - if(result != CURLE_OK) { - failf(data, "schannel: Failed to get certificate location for %s", - cert_path); - Curl_unicodefree(cert_path); - return result; + if(result && (data->set.ssl.cert[0]!='\0')) + fInCert = fopen(data->set.ssl.cert, "rb"); + + if(result && !fInCert) { + failf(data, "schannel: Failed to get certificate location" + " or file for %s", + data->set.ssl.cert); + curlx_unicodefree(cert_path); + return result; + } } - cert_store = - CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, - (HCRYPTPROV)NULL, - CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name, - cert_store_path); - if(!cert_store) { - failf(data, "schannel: Failed to open cert store %x %s, " - "last error is %x", - cert_store_name, cert_store_path, GetLastError()); - free(cert_store_path); - Curl_unicodefree(cert_path); + if((fInCert || blob) && (data->set.ssl.cert_type) && + (!strcasecompare(data->set.ssl.cert_type, "P12"))) { + failf(data, "schannel: certificate format compatibility error " + " for %s", + blob ? "(memory blob)" : data->set.ssl.cert); + curlx_unicodefree(cert_path); return CURLE_SSL_CERTPROBLEM; } - free(cert_store_path); - cert_thumbprint.pbData = cert_thumbprint_data; - cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; + if(fInCert || blob) { + /* Reading a .P12 or .pfx file, like the example at bottom of + https://social.msdn.microsoft.com/Forums/windowsdesktop/ + en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5 + */ + CRYPT_DATA_BLOB datablob; + WCHAR* pszPassword; + size_t pwd_len = 0; + int str_w_len = 0; + const char *cert_showfilename_error = blob ? + "(memory blob)" : data->set.ssl.cert; + curlx_unicodefree(cert_path); + if(fInCert) { + long cert_tell = 0; + bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; + if(continue_reading) + cert_tell = ftell(fInCert); + if(cert_tell < 0) + continue_reading = FALSE; + else + certsize = (size_t)cert_tell; + if(continue_reading) + continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; + if(continue_reading) + certdata = malloc(certsize + 1); + if((!certdata) || + ((int) fread(certdata, certsize, 1, fInCert) != 1)) + continue_reading = FALSE; + fclose(fInCert); + if(!continue_reading) { + failf(data, "schannel: Failed to read cert file %s", + data->set.ssl.cert); + free(certdata); + return CURLE_SSL_CERTPROBLEM; + } + } - if(!CryptStringToBinary(cert_thumbprint_str, CERT_THUMBPRINT_STR_LEN, - CRYPT_STRING_HEX, - cert_thumbprint_data, &cert_thumbprint.cbData, - NULL, NULL)) { - Curl_unicodefree(cert_path); - return CURLE_SSL_CERTPROBLEM; - } + /* Convert key-pair data to the in-memory certificate store */ + datablob.pbData = (BYTE*)certdata; + datablob.cbData = (DWORD)certsize; + + if(data->set.ssl.key_passwd != NULL) + pwd_len = strlen(data->set.ssl.key_passwd); + pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1)); + if(pszPassword) { + if(pwd_len > 0) + str_w_len = MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + data->set.ssl.key_passwd, (int)pwd_len, + pszPassword, (int)(pwd_len + 1)); + + if((str_w_len >= 0) && (str_w_len <= (int)pwd_len)) + pszPassword[str_w_len] = 0; + else + pszPassword[0] = 0; + + cert_store = PFXImportCertStore(&datablob, pszPassword, 0); + free(pszPassword); + } + if(!blob) + free(certdata); + if(cert_store == NULL) { + DWORD errorcode = GetLastError(); + if(errorcode == ERROR_INVALID_PASSWORD) + failf(data, "schannel: Failed to import cert file %s, " + "password is bad", + cert_showfilename_error); + else + failf(data, "schannel: Failed to import cert file %s, " + "last error is 0x%x", + cert_showfilename_error, errorcode); + return CURLE_SSL_CERTPROBLEM; + } - client_certs[0] = CertFindCertificateInStore( - cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - CERT_FIND_HASH, &cert_thumbprint, NULL); + client_certs[0] = CertFindCertificateInStore( + cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + CERT_FIND_ANY, NULL, NULL); - Curl_unicodefree(cert_path); + if(client_certs[0] == NULL) { + failf(data, "schannel: Failed to get certificate from file %s" + ", last error is 0x%x", + cert_showfilename_error, GetLastError()); + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } - if(client_certs[0]) { schannel_cred.cCreds = 1; schannel_cred.paCred = client_certs; } else { - /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ - return CURLE_SSL_CERTPROBLEM; - } + cert_store = + CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, + (HCRYPTPROV)NULL, + CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name, + cert_store_path); + if(!cert_store) { + failf(data, "schannel: Failed to open cert store %x %s, " + "last error is 0x%x", + cert_store_name, cert_store_path, GetLastError()); + free(cert_store_path); + curlx_unicodefree(cert_path); + return CURLE_SSL_CERTPROBLEM; + } + free(cert_store_path); + + cert_thumbprint.pbData = cert_thumbprint_data; + cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; + + if(!CryptStringToBinary(cert_thumbprint_str, + CERT_THUMBPRINT_STR_LEN, + CRYPT_STRING_HEX, + cert_thumbprint_data, + &cert_thumbprint.cbData, + NULL, NULL)) { + curlx_unicodefree(cert_path); + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } + + client_certs[0] = CertFindCertificateInStore( + cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + CERT_FIND_HASH, &cert_thumbprint, NULL); + + curlx_unicodefree(cert_path); + if(client_certs[0]) { + schannel_cred.cCreds = 1; + schannel_cred.paCred = client_certs; + } + else { + /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } + } CertCloseStore(cert_store, 0); } #else @@ -671,7 +791,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) BACKEND->cred->refcount = 1; /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx - */ + */ sspi_status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, @@ -688,15 +808,15 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); Curl_safefree(BACKEND->cred); switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - return CURLE_OUT_OF_MEMORY; - case SEC_E_NO_CREDENTIALS: - case SEC_E_SECPKG_NOT_FOUND: - case SEC_E_NOT_OWNER: - case SEC_E_UNKNOWN_CREDENTIALS: - case SEC_E_INTERNAL_ERROR: - default: - return CURLE_SSL_CONNECT_ERROR; + case SEC_E_INSUFFICIENT_MEMORY: + return CURLE_OUT_OF_MEMORY; + case SEC_E_NO_CREDENTIALS: + case SEC_E_SECPKG_NOT_FOUND: + case SEC_E_NOT_OWNER: + case SEC_E_UNKNOWN_CREDENTIALS: + case SEC_E_INTERNAL_ERROR: + default: + return CURLE_SSL_CONNECT_ERROR; } } } @@ -780,7 +900,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) return CURLE_OUT_OF_MEMORY; } - host_name = Curl_convert_UTF8_to_tchar(hostname); + host_name = curlx_convert_UTF8_to_tchar(hostname); if(!host_name) return CURLE_OUT_OF_MEMORY; @@ -797,35 +917,35 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) 0, &BACKEND->ctxt->ctxt_handle, &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp); - Curl_unicodefree(host_name); + curlx_unicodefree(host_name); if(sspi_status != SEC_I_CONTINUE_NEEDED) { char buffer[STRERROR_LEN]; Curl_safefree(BACKEND->ctxt); switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - failf(data, "schannel: initial InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_OUT_OF_MEMORY; - case SEC_E_WRONG_PRINCIPAL: - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - /* - case SEC_E_INVALID_HANDLE: - case SEC_E_INVALID_TOKEN: - case SEC_E_LOGON_DENIED: - case SEC_E_TARGET_UNKNOWN: - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - case SEC_E_INTERNAL_ERROR: - case SEC_E_NO_CREDENTIALS: - case SEC_E_UNSUPPORTED_FUNCTION: - case SEC_E_APPLICATION_PROTOCOL_MISMATCH: - */ - default: - failf(data, "schannel: initial InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_SSL_CONNECT_ERROR; + case SEC_E_INSUFFICIENT_MEMORY: + failf(data, "schannel: initial InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_OUT_OF_MEMORY; + case SEC_E_WRONG_PRINCIPAL: + failf(data, "schannel: SNI or certificate check failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_PEER_FAILED_VERIFICATION; + /* + case SEC_E_INVALID_HANDLE: + case SEC_E_INVALID_TOKEN: + case SEC_E_LOGON_DENIED: + case SEC_E_TARGET_UNKNOWN: + case SEC_E_NO_AUTHENTICATING_AUTHORITY: + case SEC_E_INTERNAL_ERROR: + case SEC_E_NO_CREDENTIALS: + case SEC_E_UNSUPPORTED_FUNCTION: + case SEC_E_APPLICATION_PROTOCOL_MISMATCH: + */ + default: + failf(data, "schannel: initial InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_SSL_CONNECT_ERROR; } } @@ -871,8 +991,12 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; bool doread; +#ifndef CURL_DISABLE_PROXY char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; +#else + char * const hostname = conn->host.name; +#endif const char *pubkey_ptr; doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; @@ -980,18 +1104,18 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) memcpy(inbuf[0].pvBuffer, BACKEND->encdata_buffer, BACKEND->encdata_offset); - host_name = Curl_convert_UTF8_to_tchar(hostname); + host_name = curlx_convert_UTF8_to_tchar(hostname); if(!host_name) return CURLE_OUT_OF_MEMORY; /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx - */ + */ sspi_status = s_pSecFn->InitializeSecurityContext( &BACKEND->cred->cred_handle, &BACKEND->ctxt->ctxt_handle, host_name, BACKEND->req_flags, 0, 0, &inbuf_desc, 0, NULL, &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp); - Curl_unicodefree(host_name); + curlx_unicodefree(host_name); /* free buffer for received handshake data */ Curl_safefree(inbuf[0].pvBuffer); @@ -1046,29 +1170,29 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) else { char buffer[STRERROR_LEN]; switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_OUT_OF_MEMORY; - case SEC_E_WRONG_PRINCIPAL: - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - /* - case SEC_E_INVALID_HANDLE: - case SEC_E_INVALID_TOKEN: - case SEC_E_LOGON_DENIED: - case SEC_E_TARGET_UNKNOWN: - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - case SEC_E_INTERNAL_ERROR: - case SEC_E_NO_CREDENTIALS: - case SEC_E_UNSUPPORTED_FUNCTION: - case SEC_E_APPLICATION_PROTOCOL_MISMATCH: - */ - default: - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_SSL_CONNECT_ERROR; + case SEC_E_INSUFFICIENT_MEMORY: + failf(data, "schannel: next InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_OUT_OF_MEMORY; + case SEC_E_WRONG_PRINCIPAL: + failf(data, "schannel: SNI or certificate check failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_PEER_FAILED_VERIFICATION; + /* + case SEC_E_INVALID_HANDLE: + case SEC_E_INVALID_TOKEN: + case SEC_E_LOGON_DENIED: + case SEC_E_TARGET_UNKNOWN: + case SEC_E_NO_AUTHENTICATING_AUTHORITY: + case SEC_E_INTERNAL_ERROR: + case SEC_E_NO_CREDENTIALS: + case SEC_E_UNSUPPORTED_FUNCTION: + case SEC_E_APPLICATION_PROTOCOL_MISMATCH: + */ + default: + failf(data, "schannel: next InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_SSL_CONNECT_ERROR; } } @@ -1237,8 +1361,10 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) #ifdef HAS_ALPN if(BACKEND->use_alpn) { - sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, - SECPKG_ATTR_APPLICATION_PROTOCOL, &alpn_result); + sspi_status = + s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + SECPKG_ATTR_APPLICATION_PROTOCOL, + &alpn_result); if(sspi_status != SEC_E_OK) { failf(data, "schannel: failed to retrieve ALPN result"); @@ -1249,21 +1375,21 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) SecApplicationProtocolNegotiationStatus_Success) { infof(data, "schannel: ALPN, server accepted to use %.*s\n", - alpn_result.ProtocolIdSize, alpn_result.ProtocolId); + alpn_result.ProtocolIdSize, alpn_result.ProtocolId); #ifdef USE_NGHTTP2 if(alpn_result.ProtocolIdSize == NGHTTP2_PROTO_VERSION_ID_LEN && !memcmp(NGHTTP2_PROTO_VERSION_ID, alpn_result.ProtocolId, - NGHTTP2_PROTO_VERSION_ID_LEN)) { + NGHTTP2_PROTO_VERSION_ID_LEN)) { conn->negnpn = CURL_HTTP_VERSION_2; } else #endif - if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId, - ALPN_HTTP_1_1_LENGTH)) { - conn->negnpn = CURL_HTTP_VERSION_1_1; - } + if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH && + !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId, + ALPN_HTTP_1_1_LENGTH)) { + conn->negnpn = CURL_HTTP_VERSION_1_1; + } } else infof(data, "ALPN, server did not agree to a protocol\n"); @@ -1310,8 +1436,10 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) if(data->set.ssl.certinfo) { int certs_count = 0; - sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, &ccert_context); + sspi_status = + s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &ccert_context); if((sspi_status != SEC_E_OK) || (ccert_context == NULL)) { failf(data, "schannel: failed to retrieve remote cert context"); @@ -1394,7 +1522,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex, connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking ? 0 : (time_t)timeout_ms); + nonblocking ? 0 : timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO); @@ -1543,14 +1671,10 @@ schannel_send(struct connectdata *conn, int sockindex, /* send entire message or fail */ while(len > (size_t)written) { - ssize_t this_write; - timediff_t timeleft; + ssize_t this_write = 0; int what; - - this_write = 0; - - timeleft = Curl_timeleft(conn->data, NULL, FALSE); - if(timeleft < 0) { + timediff_t timeout_ms = Curl_timeleft(conn->data, NULL, FALSE); + if(timeout_ms < 0) { /* we already got the timeout */ failf(conn->data, "schannel: timed out sending data " "(bytes sent: %zd)", written); @@ -1558,8 +1682,9 @@ schannel_send(struct connectdata *conn, int sockindex, written = -1; break; } - - what = SOCKET_WRITABLE(conn->sock[sockindex], timeleft); + else if(!timeout_ms) + timeout_ms = TIMEDIFF_T_MAX; + what = SOCKET_WRITABLE(conn->sock[sockindex], timeout_ms); if(what < 0) { /* fatal error */ failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -1653,8 +1778,8 @@ schannel_recv(struct connectdata *conn, int sockindex, } else if(!len) { /* It's debatable what to return when !len. Regardless we can't return - immediately because there may be data to decrypt (in the case we want to - decrypt all encrypted cached data) so handle !len later in cleanup. + immediately because there may be data to decrypt (in the case we want to + decrypt all encrypted cached data) so handle !len later in cleanup. */ ; /* do nothing */ } @@ -1664,7 +1789,7 @@ schannel_recv(struct connectdata *conn, int sockindex, if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || BACKEND->encdata_length < min_encdata_length) { reallocated_length = BACKEND->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; + CURL_SCHANNEL_BUFFER_FREE_SIZE; if(reallocated_length < min_encdata_length) { reallocated_length = min_encdata_length; } @@ -1732,7 +1857,7 @@ schannel_recv(struct connectdata *conn, int sockindex, InitSecBufferDesc(&inbuf_desc, inbuf, 4); /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx - */ + */ sspi_status = s_pSecFn->DecryptMessage(&BACKEND->ctxt->ctxt_handle, &inbuf_desc, 0, NULL); @@ -1748,7 +1873,7 @@ schannel_recv(struct connectdata *conn, int sockindex, /* increase buffer in order to fit the received amount of data */ size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? - inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; + inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; if(BACKEND->decdata_length - BACKEND->decdata_offset < size || BACKEND->decdata_length < len) { /* increase internal decrypted data buffer */ @@ -1818,7 +1943,7 @@ schannel_recv(struct connectdata *conn, int sockindex, if(BACKEND->encdata_offset) { *err = CURLE_RECV_ERROR; infof(data, "schannel: can't renogotiate, " - "encrypted data available\n"); + "encrypted data available\n"); goto cleanup; } /* begin renegotiation */ @@ -1873,17 +1998,20 @@ schannel_recv(struct connectdata *conn, int sockindex, "schannel: decrypted data buffer: offset %zu length %zu\n", BACKEND->decdata_offset, BACKEND->decdata_length)); -cleanup: + cleanup: /* Warning- there is no guarantee the encdata state is valid at this point */ DEBUGF(infof(data, "schannel: schannel_recv cleanup\n")); /* Error if the connection has closed without a close_notify. - Behavior here is a matter of debate. We don't want to be vulnerable to a - truncation attack however there's some browser precedent for ignoring the - close_notify for compatibility reasons. - Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't - return close_notify. In that case if the connection was closed we assume it - was graceful (close_notify) since there doesn't seem to be a way to tell. + + The behavior here is a matter of debate. We don't want to be vulnerable + to a truncation attack however there's some browser precedent for + ignoring the close_notify for compatibility reasons. + + Additionally, Windows 2000 (v5.0) is a special case since it seems it + doesn't return close_notify. In that case if the connection was closed we + assume it was graceful (close_notify) since there doesn't seem to be a + way to tell. */ if(len && !BACKEND->decdata_offset && BACKEND->recv_connection_closed && !BACKEND->recv_sspi_close_notify) { @@ -1900,7 +2028,7 @@ cleanup: /* Any error other than CURLE_AGAIN is an unrecoverable error. */ if(*err && *err != CURLE_AGAIN) - BACKEND->recv_unrecoverable_err = *err; + BACKEND->recv_unrecoverable_err = *err; size = len < BACKEND->decdata_offset ? len : BACKEND->decdata_offset; if(size) { @@ -1917,10 +2045,11 @@ cleanup: } if(!*err && !BACKEND->recv_connection_closed) - *err = CURLE_AGAIN; + *err = CURLE_AGAIN; - /* It's debatable what to return when !len. We could return whatever error we - got from decryption but instead we override here so the return is consistent. + /* It's debatable what to return when !len. We could return whatever error + we got from decryption but instead we override here so the return is + consistent. */ if(!len) *err = CURLE_OK; @@ -1986,8 +2115,12 @@ static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) */ struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; +#ifndef CURL_DISABLE_PROXY char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; +#else + char * const hostname = conn->host.name; +#endif DEBUGASSERT(data); @@ -2016,7 +2149,7 @@ static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); } - host_name = Curl_convert_UTF8_to_tchar(hostname); + host_name = curlx_convert_UTF8_to_tchar(hostname); if(!host_name) return CURLE_OUT_OF_MEMORY; @@ -2038,7 +2171,7 @@ static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp); - Curl_unicodefree(host_name); + curlx_unicodefree(host_name); if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { /* send close message which is in output buffer */ @@ -2147,8 +2280,8 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, SECURITY_STATUS sspi_status; const char *x509_der; DWORD x509_der_len; - curl_X509certificate x509_parsed; - curl_asn1Element *pubkey; + struct Curl_X509certificate x509_parsed; + struct Curl_asn1Element *pubkey; sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, @@ -2164,7 +2297,7 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (pCertContextServer->cbCertEncoded > 0))) + (pCertContextServer->cbCertEncoded > 0))) break; x509_der = (const char *)pCertContextServer->pbCertEncoded; @@ -2213,7 +2346,7 @@ static void Curl_schannel_checksum(const unsigned char *input, memset(checksum, 0, checksumlen); if(!CryptAcquireContext(&hProv, NULL, NULL, provType, - CRYPT_VERIFYCONTEXT)) + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) return; /* failed */ do { @@ -2255,9 +2388,9 @@ static CURLcode Curl_schannel_md5sum(unsigned char *input, } static CURLcode Curl_schannel_sha256sum(const unsigned char *input, - size_t inputlen, - unsigned char *sha256sum, - size_t sha256len) + size_t inputlen, + unsigned char *sha256sum, + size_t sha256len) { Curl_schannel_checksum(input, inputlen, sha256sum, sha256len, PROV_RSA_AES, CALG_SHA_256); |