diff options
Diffstat (limited to 'lib/vtls/schannel.c')
-rw-r--r-- | lib/vtls/schannel.c | 232 |
1 files changed, 183 insertions, 49 deletions
diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 410a5c4ec..45c337371 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -439,6 +439,12 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, return CURLE_OK; } #endif + +static bool algo(const char *check, char *namep, size_t nlen) +{ + return (strlen(check) == nlen) && !strncmp(check, namep, nlen); +} + static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -660,7 +666,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, cert_showfilename_error); else failf(data, "schannel: Failed to import cert file %s, " - "last error is 0x%x", + "last error is 0x%lx", cert_showfilename_error, errorcode); return CURLE_SSL_CERTPROBLEM; } @@ -671,7 +677,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, if(!client_certs[0]) { failf(data, "schannel: Failed to get certificate from file %s" - ", last error is 0x%x", + ", last error is 0x%lx", cert_showfilename_error, GetLastError()); CertCloseStore(cert_store, 0); return CURLE_SSL_CERTPROBLEM; @@ -684,10 +690,15 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, 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()); + char *path_utf8 = + curlx_convert_tchar_to_UTF8(cert_store_path); + failf(data, "schannel: Failed to open cert store %lx %s, " + "last error is 0x%lx", + cert_store_name, + (path_utf8 ? path_utf8 : "(unknown)"), + GetLastError()); free(cert_store_path); + curlx_unicodefree(path_utf8); curlx_unicodefree(cert_path); return CURLE_SSL_CERTPROBLEM; } @@ -790,9 +801,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, char *startCur = ciphers13; int algCount = 0; - char tmp[LONGEST_ALG_ID] = { 0 }; char *nameEnd; - size_t n; disable_aes_gcm_sha384 = TRUE; disable_aes_gcm_sha256 = TRUE; @@ -801,40 +810,34 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, disable_aes_ccm_sha256 = TRUE; while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) { + size_t n; + char *namep; nameEnd = strchr(startCur, ':'); n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur); + namep = startCur; - /* reject too-long cipher names */ - if(n > (LONGEST_ALG_ID - 1)) { - failf(data, "schannel: Cipher name too long, not checked"); - return CURLE_SSL_CIPHER; - } - - strncpy(tmp, startCur, n); - tmp[n] = 0; - - if(disable_aes_gcm_sha384 - && !strcmp("TLS_AES_256_GCM_SHA384", tmp)) { + if(disable_aes_gcm_sha384 && + algo("TLS_AES_256_GCM_SHA384", namep, n)) { disable_aes_gcm_sha384 = FALSE; } else if(disable_aes_gcm_sha256 - && !strcmp("TLS_AES_128_GCM_SHA256", tmp)) { + && algo("TLS_AES_128_GCM_SHA256", namep, n)) { disable_aes_gcm_sha256 = FALSE; } else if(disable_chacha_poly - && !strcmp("TLS_CHACHA20_POLY1305_SHA256", tmp)) { + && algo("TLS_CHACHA20_POLY1305_SHA256", namep, n)) { disable_chacha_poly = FALSE; } else if(disable_aes_ccm_8_sha256 - && !strcmp("TLS_AES_128_CCM_8_SHA256", tmp)) { + && algo("TLS_AES_128_CCM_8_SHA256", namep, n)) { disable_aes_ccm_8_sha256 = FALSE; } else if(disable_aes_ccm_sha256 - && !strcmp("TLS_AES_128_CCM_SHA256", tmp)) { + && algo("TLS_AES_128_CCM_SHA256", namep, n)) { disable_aes_ccm_sha256 = FALSE; } else { - failf(data, "schannel: Unknown TLS 1.3 cipher: %s", tmp); + failf(data, "schannel: Unknown TLS 1.3 cipher: %.*s", (int)n, namep); return CURLE_SSL_CIPHER; } @@ -1063,17 +1066,12 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #endif SECURITY_STATUS sspi_status = SEC_E_OK; struct Curl_schannel_cred *old_cred = NULL; - struct in_addr addr; -#ifdef ENABLE_IPV6 - struct in6_addr addr6; -#endif CURLcode result; - const char *hostname = connssl->hostname; DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", - hostname, connssl->port)); + connssl->peer.hostname, connssl->port)); if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, VERSION_LESS_THAN_EQUAL)) { @@ -1154,22 +1152,14 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* A hostname associated with the credential is needed by InitializeSecurityContext for SNI and other reasons. */ - snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } + snihost = connssl->peer.sni? connssl->peer.sni : connssl->peer.hostname; backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); if(!backend->cred->sni_hostname) return CURLE_OUT_OF_MEMORY; } /* Warn if SNI is disabled due to use of an IP address */ - if(Curl_inet_pton(AF_INET, hostname, &addr) -#ifdef ENABLE_IPV6 - || Curl_inet_pton(AF_INET6, hostname, &addr6) -#endif - ) { + if(connssl->peer.is_ip_address) { infof(data, "schannel: using IP address, SNI is not supported by OS."); } @@ -1208,9 +1198,8 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) cur += proto.len; *list_len = curlx_uitous(cur - list_start_index); - *extension_len = *list_len + - (unsigned short)sizeof(unsigned int) + - (unsigned short)sizeof(unsigned short); + *extension_len = (unsigned int)(*list_len + + sizeof(unsigned int) + sizeof(unsigned short)); InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur); InitSecBufferDesc(&inbuf_desc, &inbuf, 1); @@ -1346,7 +1335,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", - connssl->hostname, connssl->port)); + connssl->peer.hostname, connssl->port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1700,7 +1689,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", - connssl->hostname, connssl->port)); + connssl->peer.hostname, connssl->port)); if(!backend->cred) return CURLE_SSL_CONNECT_ERROR; @@ -2345,10 +2334,10 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, else { #ifndef CURL_DISABLE_VERBOSE_STRINGS char buffer[STRERROR_LEN]; -#endif - *err = CURLE_RECV_ERROR; infof(data, "schannel: failed to read data from server: %s", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); +#endif + *err = CURLE_RECV_ERROR; goto cleanup; } } @@ -2498,7 +2487,7 @@ static int schannel_shutdown(struct Curl_cfilter *cf, if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", - connssl->hostname, connssl->port); + connssl->peer.hostname, connssl->port); } if(backend->cred && backend->ctxt) { @@ -2754,6 +2743,151 @@ static void *schannel_get_internals(struct ssl_connect_data *connssl, return &backend->ctxt->ctxt_handle; } +HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + struct schannel_multi_ssl_backend_data *mbackend; + const struct ssl_general_config *cfg = &data->set.general_ssl; + timediff_t timeout_ms; + timediff_t elapsed_ms; + struct curltime now; + unsigned char info_blob_digest[CURL_SHA256_DIGEST_LENGTH]; + + DEBUGASSERT(multi); + + if(!multi || !multi->ssl_backend_data) { + return NULL; + } + + mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data; + if(!mbackend->cert_store) { + return NULL; + } + + /* zero ca_cache_timeout completely disables caching */ + if(!cfg->ca_cache_timeout) { + return NULL; + } + + /* check for cache timeout by using the cached_x509_store_expired timediff + calculation pattern from openssl.c. + negative timeout means retain forever. */ + timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + if(timeout_ms >= 0) { + now = Curl_now(); + elapsed_ms = Curl_timediff(now, mbackend->time); + if(elapsed_ms >= timeout_ms) { + return NULL; + } + } + + if(ca_info_blob) { + if(!mbackend->CAinfo_blob_digest) { + return NULL; + } + if(mbackend->CAinfo_blob_size != ca_info_blob->len) { + return NULL; + } + schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH); + if(memcmp(mbackend->CAinfo_blob_digest, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH)) { + return NULL; + } + } + else { + if(!conn_config->CAfile || !mbackend->CAfile || + strcmp(mbackend->CAfile, conn_config->CAfile)) { + return NULL; + } + } + + return mbackend->cert_store; +} + +bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + HCERTSTORE cert_store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + struct schannel_multi_ssl_backend_data *mbackend; + unsigned char *CAinfo_blob_digest = NULL; + size_t CAinfo_blob_size = 0; + char *CAfile = NULL; + + DEBUGASSERT(multi); + + if(!multi) { + return false; + } + + if(!multi->ssl_backend_data) { + multi->ssl_backend_data = + calloc(1, sizeof(struct schannel_multi_ssl_backend_data)); + if(!multi->ssl_backend_data) { + return false; + } + } + + mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data; + + + if(ca_info_blob) { + CAinfo_blob_digest = malloc(CURL_SHA256_DIGEST_LENGTH); + if(!CAinfo_blob_digest) { + return false; + } + schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + CAinfo_blob_digest, + CURL_SHA256_DIGEST_LENGTH); + CAinfo_blob_size = ca_info_blob->len; + } + else { + if(conn_config->CAfile) { + CAfile = strdup(conn_config->CAfile); + if(!CAfile) { + return false; + } + } + } + + /* free old cache data */ + if(mbackend->cert_store) { + CertCloseStore(mbackend->cert_store, 0); + } + free(mbackend->CAinfo_blob_digest); + free(mbackend->CAfile); + + mbackend->time = Curl_now(); + mbackend->cert_store = cert_store; + mbackend->CAinfo_blob_digest = CAinfo_blob_digest; + mbackend->CAinfo_blob_size = CAinfo_blob_size; + mbackend->CAfile = CAfile; + return true; +} + +static void schannel_free_multi_ssl_backend_data( + struct multi_ssl_backend_data *msbd) +{ + struct schannel_multi_ssl_backend_data *mbackend = + (struct schannel_multi_ssl_backend_data*)msbd; + if(mbackend->cert_store) { + CertCloseStore(mbackend->cert_store, 0); + } + free(mbackend->CAinfo_blob_digest); + free(mbackend->CAfile); + free(mbackend); +} + const struct Curl_ssl Curl_ssl_schannel = { { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */ @@ -2777,7 +2911,7 @@ const struct Curl_ssl Curl_ssl_schannel = { Curl_none_cert_status_request, /* cert_status_request */ schannel_connect, /* connect */ schannel_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ schannel_get_internals, /* get_internals */ schannel_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -2789,7 +2923,7 @@ const struct Curl_ssl Curl_ssl_schannel = { schannel_sha256sum, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ + schannel_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */ schannel_recv, /* recv decrypted data */ schannel_send, /* send data to encrypt */ }; |