schannel: not supported with UWP, drop redundant code

Schannel is not supported by UWP. SSPI is also required by Schannel in
curl, and SSPI also isn't supported by UWP.

mingw-w64 is able to create such build regardless (my guess: due to API
parts not accurately marked as UWP-only), but the binary is unlikely
to work. With MSVC the failure happens at build-time.

Ref: https://learn.microsoft.com/windows/win32/api/sspi/nf-sspi-initsecurityinterfacea#requirements
Ref: https://learn.microsoft.com/windows/win32/secauthn/initializesecuritycontext--schannel#requirements

Drop all UWP-related logic, including two related feature checks, that
can now be permanently enabled.

Also:
- build: show fatal error for Schannel in UWP mode.
- build: do not allow enabling SSPI in UWP mode.
- drop undocumented option `DISABLE_SCHANNEL_CLIENT_CERT`. Added without
  mention in an unrelated commit. The PR text says to save size. On x64
  this is 0.3%, or 4KB out of 1.3MB. The tiny gain doesn't justify
  an extra build variant. Ref: 8beff43559
- move `MPROTO_SCHANNEL_CERT_SHARE_KEY` closer to its use.
- replace commented block with `#if 0`.

Reviewed-by: Jay Satiro
Follow-up to cd0ec4784c #17089
Closes #18116
This commit is contained in:
Viktor Szakats 2025-07-31 07:44:35 +02:00
parent b5c245045e
commit 923db3515d
No known key found for this signature in database
GPG Key ID: B5ABD165E2AEF201
7 changed files with 65 additions and 152 deletions

View File

@ -213,7 +213,7 @@ jobs:
- { build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: '' , config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_GNUTLS=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON', install: 'mingw-w64-clang-x86_64-gnutls mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh', type: 'Debug', name: 'gnutls libssh' }
- { build: 'cmake' , sys: 'clangarm64', env: 'clang-aarch64', tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_CURLDEBUG=ON', install: 'mingw-w64-clang-aarch64-libssh2', type: 'Release', name: 'schannel R TrackMemory', image: 'windows-11-arm' }
- { build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON', install: 'mingw-w64-clang-x86_64-openssl mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh2', type: 'Release', name: 'openssl', chkprefill: '_chkprefill' }
- { build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON', install: 'mingw-w64-ucrt-x86_64-libssh2', type: 'Release', test: 'uwp', name: 'schannel' }
- { build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_OPENSSL=ON', install: 'mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-libssh2', type: 'Release', test: 'uwp', name: 'schannel' }
# { build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun', config: '--without-debug --with-schannel --enable-shared', install: 'mingw-w64-ucrt-x86_64-libssh2', type: 'Release', test: 'uwp', name: 'schannel' }
- { build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', install: 'mingw-w64-x86_64-libssh2', type: 'Debug', cflags: '-DCURL_SCHANNEL_DEV_DEBUG', name: 'schannel dev debug', image: 'windows-2025' }
- { build: 'cmake' , sys: 'mingw32' , env: 'i686' , tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON', install: 'mingw-w64-i686-libssh2', type: 'Release', name: 'schannel R' }

View File

@ -726,6 +726,9 @@ elseif(_enabled_ssl_options_count EQUAL 0)
endif()
if(CURL_USE_SCHANNEL)
if(WINDOWS_STORE)
message(FATAL_ERROR "UWP does not support Schannel.")
endif()
set(_ssl_enabled ON)
set(USE_SCHANNEL ON) # Windows native SSL/TLS support
set(USE_WINDOWS_SSPI ON) # CURL_USE_SCHANNEL requires CURL_WINDOWS_SSPI
@ -734,7 +737,7 @@ if(CURL_USE_SCHANNEL)
set(_valid_default_ssl_backend TRUE)
endif()
endif()
if(CURL_WINDOWS_SSPI)
if(CURL_WINDOWS_SSPI AND NOT WINDOWS_STORE)
set(USE_WINDOWS_SSPI ON)
endif()

View File

@ -4351,44 +4351,46 @@ AS_HELP_STRING([--disable-verbose],[Disable verbose strings]),
AC_MSG_RESULT(yes)
)
dnl ************************************************************
dnl enable SSPI support
dnl
AC_MSG_CHECKING([whether to enable SSPI support (Windows native builds only)])
AC_ARG_ENABLE(sspi,
AS_HELP_STRING([--enable-sspi],[Enable SSPI])
AS_HELP_STRING([--disable-sspi],[Disable SSPI]),
[ case "$enableval" in
yes)
if test "$curl_cv_native_windows" = "yes"; then
AC_MSG_RESULT(yes)
AC_DEFINE(USE_WINDOWS_SSPI, 1, [to enable SSPI support])
USE_WINDOWS_SSPI=1
curl_sspi_msg="enabled"
else
AC_MSG_RESULT(no)
AC_MSG_WARN([--enable-sspi Ignored. Only supported on native Windows builds.])
fi
;;
*)
if test "x$SCHANNEL_ENABLED" = "x1"; then
# --with-schannel implies --enable-sspi
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
fi
;;
esac ],
if test "x$SCHANNEL_ENABLED" = "x1"; then
# --with-schannel implies --enable-sspi
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
fi
)
if test "$curl_cv_winuwp" != 'yes'; then
dnl ************************************************************
dnl enable SSPI support
dnl
AC_MSG_CHECKING([whether to enable SSPI support (Windows native builds only)])
AC_ARG_ENABLE(sspi,
AS_HELP_STRING([--enable-sspi],[Enable SSPI])
AS_HELP_STRING([--disable-sspi],[Disable SSPI]),
[ case "$enableval" in
yes)
if test "$curl_cv_native_windows" = "yes"; then
AC_MSG_RESULT(yes)
AC_DEFINE(USE_WINDOWS_SSPI, 1, [to enable SSPI support])
USE_WINDOWS_SSPI=1
curl_sspi_msg="enabled"
else
AC_MSG_RESULT(no)
AC_MSG_WARN([--enable-sspi Ignored. Only supported on native Windows builds.])
fi
;;
*)
if test "x$SCHANNEL_ENABLED" = "x1"; then
# --with-schannel implies --enable-sspi
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
fi
;;
esac ],
if test "x$SCHANNEL_ENABLED" = "x1"; then
# --with-schannel implies --enable-sspi
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
fi
)
if test "x$USE_WINDOWS_SSPI" = "x1"; then
LIBS="-lsecur32 $LIBS"
if test "x$USE_WINDOWS_SSPI" = "x1"; then
LIBS="-lsecur32 $LIBS"
fi
fi
dnl ************************************************************

View File

@ -130,6 +130,9 @@
#define CERT_FIND_HAS_PRIVATE_KEY (21 << CERT_COMPARE_SHIFT)
#endif
/* key to use at `multi->proto_hash` */
#define MPROTO_SCHANNEL_CERT_SHARE_KEY "tls:schannel:cert:share"
/* ALPN requires version 8.1 of the Windows SDK, which was
shipped with Visual Studio 2013, aka _MSC_VER 1800:
https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
@ -377,8 +380,7 @@ set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers,
return CURLE_OK;
}
#ifdef HAS_CLIENT_CERT_PATH
#ifndef UNDER_CE
/* Function allocates memory for store_path only if CURLE_OK is returned */
static CURLcode
get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
@ -444,10 +446,8 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
#ifdef HAS_CLIENT_CERT_PATH
PCCERT_CONTEXT client_certs[1] = { NULL };
HCERTSTORE client_cert_store = NULL;
#endif
SECURITY_STATUS sspi_status = SEC_E_OK;
CURLcode result;
@ -461,11 +461,9 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
if(conn_config->verifypeer) {
#ifdef HAS_MANUAL_VERIFY_API
if(backend->use_manual_cred_validation)
flags = SCH_CRED_MANUAL_CRED_VALIDATION;
else
#endif
flags = SCH_CRED_AUTO_CRED_VALIDATION;
if(ssl_config->no_revoke) {
@ -533,7 +531,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
return CURLE_SSL_CONNECT_ERROR;
}
#ifdef HAS_CLIENT_CERT_PATH
#ifndef UNDER_CE
/* client certificate */
if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
DWORD cert_store_name = 0;
@ -737,11 +735,6 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
}
client_cert_store = cert_store;
}
#else
if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
failf(data, "schannel: client cert support not built in");
return CURLE_NOT_BUILT_IN;
}
#endif
/* allocate memory for the reusable credential handle */
@ -750,23 +743,19 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
if(!backend->cred) {
failf(data, "schannel: unable to allocate memory");
#ifdef HAS_CLIENT_CERT_PATH
if(client_certs[0])
CertFreeCertificateContext(client_certs[0]);
if(client_cert_store)
CertCloseStore(client_cert_store, 0);
#endif
return CURLE_OUT_OF_MEMORY;
}
backend->cred->refcount = 1;
#ifdef HAS_CLIENT_CERT_PATH
/* Since we did not persist the key, we need to extend the store's
* lifetime until the end of the connection
*/
backend->cred->client_cert_store = client_cert_store;
#endif
/* We support TLS 1.3 starting in Windows 10 version 1809 (OS build 17763) as
long as the user did not set a legacy algorithm list
@ -792,12 +781,10 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
credentials.pTlsParameters->grbitDisabledProtocols =
(DWORD)~enabled_protocols;
#ifdef HAS_CLIENT_CERT_PATH
if(client_certs[0]) {
credentials.cCreds = 1;
credentials.paCred = client_certs;
}
#endif
sspi_status =
Curl_pSecFn->AcquireCredentialsHandle(NULL,
@ -833,12 +820,10 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
schannel_cred.dwFlags = flags | SCH_USE_STRONG_CRYPTO;
}
#ifdef HAS_CLIENT_CERT_PATH
if(client_certs[0]) {
schannel_cred.cCreds = 1;
schannel_cred.paCred = client_certs;
}
#endif
sspi_status =
Curl_pSecFn->AcquireCredentialsHandle(NULL,
@ -849,10 +834,8 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
&backend->cred->time_stamp);
}
#ifdef HAS_CLIENT_CERT_PATH
if(client_certs[0])
CertFreeCertificateContext(client_certs[0]);
#endif
if(sspi_status != SEC_E_OK) {
char buffer[STRERROR_LEN];
@ -916,15 +899,10 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
#endif
#ifdef UNDER_CE
#ifdef HAS_MANUAL_VERIFY_API
/* certificate validation on Windows CE does not seem to work right; we will
* do it following a more manual process. */
backend->use_manual_cred_validation = TRUE;
#else
#error "compiler too old to support Windows CE requisite manual cert verify"
#endif
#else
#ifdef HAS_MANUAL_VERIFY_API
if(conn_config->CAfile || conn_config->ca_info_blob) {
if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT,
VERSION_GREATER_THAN_EQUAL)) {
@ -938,12 +916,6 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
else
backend->use_manual_cred_validation = FALSE;
#else
if(conn_config->CAfile || conn_config->ca_info_blob) {
failf(data, "schannel: CA cert support not built in");
return CURLE_NOT_BUILT_IN;
}
#endif
#endif
backend->cred = NULL;
@ -1087,17 +1059,17 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
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:
*/
#if 0
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:
#endif
default:
failf(data, "schannel: initial InitializeSecurityContext failed: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
@ -1407,12 +1379,10 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
}
}
#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 will not verify it. */
@ -1507,12 +1477,10 @@ static void schannel_session_free(void *sessionid)
if(cred->refcount == 0) {
Curl_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
curlx_unicodefree(cred->sni_hostname);
#ifdef HAS_CLIENT_CERT_PATH
if(cred->client_cert_store) {
CertCloseStore(cred->client_cert_store, 0);
cred->client_cert_store = NULL;
}
#endif
Curl_safefree(cred);
}
}
@ -2359,26 +2327,16 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data)
static int schannel_init(void)
{
#if defined(HAS_ALPN_SCHANNEL) && !defined(UNDER_CE)
bool wine = FALSE;
bool wine_has_alpn = FALSE;
#ifndef CURL_WINDOWS_UWP
typedef const char *(APIENTRY *WINE_GET_VERSION_FN)(void);
/* GetModuleHandle() not available for UWP.
Assume no WINE because WINE has no UWP support. */
WINE_GET_VERSION_FN p_wine_get_version =
CURLX_FUNCTION_CAST(WINE_GET_VERSION_FN,
(GetProcAddress(GetModuleHandleA("ntdll"),
"wine_get_version")));
wine = !!p_wine_get_version;
if(wine) {
if(p_wine_get_version) { /* WINE detected */
const char *wine_version = p_wine_get_version(); /* e.g. "6.0.2" */
/* Assume ALPN support with WINE 6.0 or upper */
wine_has_alpn = wine_version && atoi(wine_version) >= 6;
s_win_has_alpn = wine_version && atoi(wine_version) >= 6;
}
#endif
if(wine)
s_win_has_alpn = wine_has_alpn;
else {
/* ALPN is supported on Windows 8.1 / Server 2012 R2 and above. */
s_win_has_alpn = curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
@ -2483,13 +2441,6 @@ static void schannel_checksum(const unsigned char *input,
DWORD provType,
const unsigned int algId)
{
#ifdef CURL_WINDOWS_UWP
(void)input;
(void)inputlen;
(void)provType;
(void)algId;
memset(checksum, 0, checksumlen);
#else
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
DWORD cbHashSize = 0;
@ -2535,7 +2486,6 @@ static void schannel_checksum(const unsigned char *input,
if(hProv)
CryptReleaseContext(hProv, 0);
#endif
}
static CURLcode schannel_sha256sum(const unsigned char *input,
@ -2705,12 +2655,8 @@ const struct Curl_ssl Curl_ssl_schannel = {
{ CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */
SSLSUPP_CERTINFO |
#ifdef HAS_MANUAL_VERIFY_API
SSLSUPP_CAINFO_BLOB |
#endif
#ifndef CURL_WINDOWS_UWP
SSLSUPP_PINNEDPUBKEY |
#endif
SSLSUPP_CA_CACHE |
SSLSUPP_HTTPS_PROXY |
SSLSUPP_CIPHER_LIST,

View File

@ -30,16 +30,6 @@
#include "vtls.h"
#ifndef CURL_WINDOWS_UWP
#define HAS_MANUAL_VERIFY_API
#endif
/* These two macros are missing from mingw-w64 in UWP mode as of v13 */
#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) && \
!defined(DISABLE_SCHANNEL_CLIENT_CERT)
#define HAS_CLIENT_CERT_PATH
#endif
#if defined(_MSC_VER) && (_MSC_VER <= 1600)
/* Workaround for warning:
'type cast' : conversion from 'int' to 'LPCSTR' of greater size */
@ -111,9 +101,7 @@ struct Curl_schannel_cred {
CredHandle cred_handle;
TimeStamp time_stamp;
TCHAR *sni_hostname;
#ifdef HAS_CLIENT_CERT_PATH
HCERTSTORE client_cert_store;
#endif
int refcount;
};
@ -139,16 +127,11 @@ struct schannel_ssl_backend_data {
BIT(recv_connection_closed); /* true if connection closed, regardless how */
BIT(recv_renegotiating); /* true if recv is doing renegotiation */
BIT(use_alpn); /* true if ALPN is used for this connection */
#ifdef HAS_MANUAL_VERIFY_API
BIT(use_manual_cred_validation); /* true if manual cred validation is used */
#endif
BIT(sent_shutdown);
BIT(encdata_is_incomplete);
};
/* key to use at `multi->proto_hash` */
#define MPROTO_SCHANNEL_CERT_SHARE_KEY "tls:schannel:cert:share"
struct schannel_cert_share {
unsigned char CAinfo_blob_digest[CURL_SHA256_DIGEST_LENGTH];
size_t CAinfo_blob_size; /* CA info blob size */

View File

@ -56,8 +56,6 @@
#define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend)
#ifdef HAS_MANUAL_VERIFY_API
#ifdef __MINGW32CE__
#define CERT_QUERY_OBJECT_BLOB 0x00000002
#define CERT_QUERY_CONTENT_CERT 1
@ -370,11 +368,7 @@ cleanup:
return result;
}
#endif
#endif /* HAS_MANUAL_VERIFY_API */
#ifndef UNDER_CE
/*
* Returns the number of characters necessary to populate all the host_names.
* If host_names is not NULL, populate it with all the hostnames. Each string
@ -390,14 +384,6 @@ static DWORD cert_get_name_string(struct Curl_easy *data,
BOOL Win8_compat)
{
DWORD actual_length = 0;
#ifdef CURL_WINDOWS_UWP
(void)data;
(void)cert_context;
(void)host_names;
(void)length;
(void)alt_name_info;
(void)Win8_compat;
#else
BOOL compute_content = FALSE;
LPTSTR current_pos = NULL;
DWORD i;
@ -471,7 +457,6 @@ static DWORD cert_get_name_string(struct Curl_easy *data,
/* Last string has double null-terminator. */
*current_pos = '\0';
}
#endif
return actual_length;
}
@ -510,12 +495,6 @@ static bool get_alt_name_info(struct Curl_easy *data,
LPDWORD alt_name_info_size)
{
bool result = FALSE;
#ifdef CURL_WINDOWS_UWP
(void)data;
(void)ctx;
(void)alt_name_info;
(void)alt_name_info_size;
#else
PCERT_INFO cert_info = NULL;
PCERT_EXTENSION extension = NULL;
CRYPT_DECODE_PARA decode_para = { sizeof(CRYPT_DECODE_PARA), NULL, NULL };
@ -553,7 +532,6 @@ static bool get_alt_name_info(struct Curl_easy *data,
return result;
}
result = TRUE;
#endif
return result;
}
#endif /* !UNDER_CE */
@ -767,7 +745,6 @@ cleanup:
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)
@ -979,5 +956,4 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
return result;
}
#endif /* HAS_MANUAL_VERIFY_API */
#endif /* USE_SCHANNEL */

View File

@ -28,6 +28,9 @@ if test "x$OPT_SCHANNEL" != xno; then
ssl_msg=
if test "x$OPT_SCHANNEL" != "xno" &&
test "x$curl_cv_native_windows" = "xyes"; then
if test "$curl_cv_winuwp" = 'yes'; then
AC_MSG_ERROR([UWP does not support Schannel.])
fi
AC_MSG_RESULT(yes)
AC_DEFINE(USE_SCHANNEL, 1, [to enable Windows native SSL/TLS support])
ssl_msg="Schannel"