aboutsummaryrefslogtreecommitdiff
path: root/lib/vtls/schannel.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vtls/schannel.c')
-rw-r--r--lib/vtls/schannel.c232
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 */
};