2.4 Secure Transport will not import PKCS#12 client certificates without a password
2.5 Client cert handling with Issuer DN differs between backends
2.7 Client cert (MTLS) issues with Schannel
- 2.8 Schannel disable CURLOPT_SSL_VERIFYPEER and verify hostname
2.11 Schannel TLS 1.2 handshake bug in old Windows versions
2.12 FTPS with Schannel times out file list operation
2.13 CURLOPT_CERTINFO results in CURLE_OUT_OF_MEMORY with Schannel
See https://github.com/curl/curl/issues/3145
-2.8 Schannel disable CURLOPT_SSL_VERIFYPEER and verify hostname
-
- This seems to be a limitation in the underlying Schannel API.
-
- https://github.com/curl/curl/issues/3284
-
2.11 Schannel TLS 1.2 handshake bug in old Windows versions
In old versions of Windows such as 7 and 8.1 the Schannel TLS 1.2 handshake
SCH_CREDENTIALS credentials = { 0 };
TLS_PARAMETERS tls_parameters = { 0 };
- CRYPTO_SETTINGS crypto_settings[4] = { 0 };
- UNICODE_STRING blocked_ccm_modes[1] = { 0 };
- UNICODE_STRING blocked_gcm_modes[1] = { 0 };
+ CRYPTO_SETTINGS crypto_settings[4] = { { 0 } };
+ UNICODE_STRING blocked_ccm_modes[1] = { { 0 } };
+ UNICODE_STRING blocked_gcm_modes[1] = { { 0 } };
int crypto_settings_idx = 0;
#ifdef HAS_MANUAL_VERIFY_API
if(conn_config->verifypeer && backend->use_manual_cred_validation) {
+ /* Certificate verification also verifies the hostname if verifyhost */
return Curl_verify_certificate(cf, data);
}
#endif
+ /* Verify the hostname manually when certificate verification is disabled,
+ because in that case Schannel won't verify it. */
+ if(!conn_config->verifypeer && conn_config->verifyhost)
+ return Curl_verify_host(cf, data);
+
return CURLE_OK;
}
#define HAS_CLIENT_CERT_PATH
#endif
+#ifndef CRYPT_DECODE_NOCOPY_FLAG
+#define CRYPT_DECODE_NOCOPY_FLAG 0x1
+#endif
+
+#ifndef CRYPT_DECODE_ALLOC_FLAG
+#define CRYPT_DECODE_ALLOC_FLAG 0x8000
+#endif
+
+#ifndef CERT_ALT_NAME_DNS_NAME
+#define CERT_ALT_NAME_DNS_NAME 3
+#endif
+
+#ifndef CERT_ALT_NAME_IP_ADDRESS
+#define CERT_ALT_NAME_IP_ADDRESS 8
+#endif
+
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+/* Original mingw is missing CERT structs or they're disabled.
+ Refer to w32api-5.0.2-mingw32-dev\include\wincrypt.h. */
+
+/* !checksrc! disable TYPEDEFSTRUCT 4 */
+typedef struct _CERT_OTHER_NAME {
+ LPSTR pszObjId;
+ CRYPT_OBJID_BLOB Value;
+} CERT_OTHER_NAME, *PCERT_OTHER_NAME;
+
+typedef struct _CERT_ALT_NAME_ENTRY {
+ DWORD dwAltNameChoice;
+ union {
+ PCERT_OTHER_NAME pOtherName;
+ LPWSTR pwszRfc822Name;
+ LPWSTR pwszDNSName;
+ CERT_NAME_BLOB DirectoryName;
+ LPWSTR pwszURL;
+ CRYPT_DATA_BLOB IPAddress;
+ LPSTR pszRegisteredID;
+ };
+} CERT_ALT_NAME_ENTRY, *PCERT_ALT_NAME_ENTRY;
+
+typedef struct _CERT_ALT_NAME_INFO {
+ DWORD cAltEntry;
+ PCERT_ALT_NAME_ENTRY rgAltEntry;
+} CERT_ALT_NAME_INFO, *PCERT_ALT_NAME_INFO;
+
+typedef struct _CRYPT_DECODE_PARA {
+ DWORD cbSize;
+ PFN_CRYPT_ALLOC pfnAlloc;
+ PFN_CRYPT_FREE pfnFree;
+} CRYPT_DECODE_PARA, *PCRYPT_DECODE_PARA;
+#endif
+
#ifndef SCH_CREDENTIALS_VERSION
#define SCH_CREDENTIALS_VERSION 0x00000005
#include "schannel.h"
#include "schannel_int.h"
-#ifdef HAS_MANUAL_VERIFY_API
-
#include "vtls.h"
#include "vtls_int.h"
#include "sendf.h"
#define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend)
+
+#ifdef HAS_MANUAL_VERIFY_API
+
#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */
#define BEGIN_CERT "-----BEGIN CERTIFICATE-----"
#define END_CERT "\n-----END CERTIFICATE-----"
return result;
}
+#endif /* HAS_MANUAL_VERIFY_API */
+
/*
* Returns the number of characters necessary to populate all the host_names.
* If host_names is not NULL, populate it with all the host names. Each string
LPTSTR current_pos = NULL;
DWORD i;
+#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG
/* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */
if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT,
VERSION_GREATER_THAN_EQUAL)) {
-#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG
/* CertGetNameString will provide the 8-bit character string without
* any decoding */
DWORD name_flags =
host_names,
length);
return actual_length;
-#endif
}
+#endif
compute_content = host_names != NULL && length != 0;
return actual_length;
}
-static CURLcode verify_host(struct Curl_easy *data,
- CERT_CONTEXT *pCertContextServer,
- const char *conn_hostname)
+/* Verify the server's hostname */
+CURLcode Curl_verify_host(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
+ struct ssl_connect_data *connssl = cf->ctx;
+ SECURITY_STATUS sspi_status;
CURLcode result = CURLE_PEER_FAILED_VERIFICATION;
+ CERT_CONTEXT *pCertContextServer = NULL;
TCHAR *cert_hostname_buff = NULL;
size_t cert_hostname_buff_index = 0;
+ const char *conn_hostname = connssl->hostname;
size_t hostlen = strlen(conn_hostname);
DWORD len = 0;
DWORD actual_len = 0;
+ sspi_status =
+ s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &pCertContextServer);
+
+ if((sspi_status != SEC_E_OK) || !pCertContextServer) {
+ char buffer[STRERROR_LEN];
+ failf(data, "schannel: Failed to read remote certificate context: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ goto cleanup;
+ }
+
/* Determine the size of the string needed for the cert hostname */
len = cert_get_name_string(data, pCertContextServer, NULL, 0);
if(len == 0) {
goto cleanup;
}
- /* If HAVE_CERT_NAME_SEARCH_ALL_NAMES is available, the output
- * will contain all DNS names, where each name is null-terminated
- * and the last DNS name is double null-terminated. Due to this
- * encoding, use the length of the buffer to iterate over all names.
+ /* cert_hostname_buff contains all DNS names, where each name is
+ * null-terminated and the last DNS name is double null-terminated. Due to
+ * this encoding, use the length of the buffer to iterate over all names.
*/
result = CURLE_PEER_FAILED_VERIFICATION;
while(cert_hostname_buff_index < len &&
cleanup:
Curl_safefree(cert_hostname_buff);
+ if(pCertContextServer)
+ CertFreeCertificateContext(pCertContextServer);
+
return result;
}
+
+#ifdef HAS_MANUAL_VERIFY_API
+/* Verify the server's certificate and hostname */
CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
if(result == CURLE_OK) {
if(conn_config->verifyhost) {
- result = verify_host(data, pCertContextServer, connssl->hostname);
+ result = Curl_verify_host(cf, data);
}
}