From: Hans Leidekker hans@codeweavers.com
--- include/minschannel.h | 44 +++++++++++++++++++++++++++++++++++++++++++ include/schannel.h | 39 ++++++++++++++++++++++---------------- 2 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 include/minschannel.h
diff --git a/include/minschannel.h b/include/minschannel.h new file mode 100644 index 00000000000..c30ae85922b --- /dev/null +++ b/include/minschannel.h @@ -0,0 +1,44 @@ +/* + * Copyright 2022 Hans Leidekker for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_MINSCHANNEL_H__ +#define __WINE_MINSCHANNEL_H__ + +#define SECPKG_ATTR_ISSUER_LIST 0x50 +#define SECPKG_ATTR_REMOTE_CRED 0x51 +#define SECPKG_ATTR_LOCAL_CRED 0x52 +#define SECPKG_ATTR_REMOTE_CERT_CONTEXT 0x53 +#define SECPKG_ATTR_LOCAL_CERT_CONTEXT 0x54 +#define SECPKG_ATTR_ROOT_STORE 0x55 +#define SECPKG_ATTR_SUPPORTED_ALGS 0x56 +#define SECPKG_ATTR_CIPHER_STRENGTHS 0x57 +#define SECPKG_ATTR_SUPPORTED_PROTOCOLS 0x58 +#define SECPKG_ATTR_ISSUER_LIST_EX 0x59 +#define SECPKG_ATTR_CONNECTION_INFO 0x5a +#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b +#define SECPKG_ATTR_MAPPED_CRED_ATTR 0x5c +#define SECPKG_ATTR_SESSION_INFO 0x5d +#define SECPKG_ATTR_APP_DATA 0x5e +#define SECPKG_ATTR_REMOTE_CERTIFICATES 0x5f +#define SECPKG_ATTR_CLIENT_CERT_POLICY 0x60 +#define SECPKG_ATTR_CC_POLICY_RESULT 0x61 +#define SECPKG_ATTR_USE_NCRYPT 0x62 +#define SECPKG_ATTR_LOCAL_CERT_INFO 0x63 +#define SECPKG_ATTR_CIPHER_INFO 0x64 + +#endif /* __WINE_MINSCHANNEL_H__ */ diff --git a/include/schannel.h b/include/schannel.h index 556ec1882c9..3aa85832fd3 100644 --- a/include/schannel.h +++ b/include/schannel.h @@ -18,6 +18,7 @@ #ifndef __WINE_SCHANNEL_H__ #define __WINE_SCHANNEL_H__
+#include <minschannel.h> #include <wincrypt.h>
/* Package names */ @@ -179,22 +180,6 @@ static const WCHAR SCHANNEL_NAME_W[] = { 'S','c','h','a','n','n','e','l',0 }; #define SCH_CRED_IGNORE_NO_REVOCATION_CHECK 2048 #define SCH_CRED_IGNORE_REVOCATION_OFFLINE 4096
-#define SECPKG_ATTR_ISSUER_LIST 0x50 -#define SECPKG_ATTR_REMOTE_CRED 0x51 -#define SECPKG_ATTR_LOCAL_CRED 0x52 -#define SECPKG_ATTR_REMOTE_CERT_CONTEXT 0x53 -#define SECPKG_ATTR_LOCAL_CERT_CONTEXT 0x54 -#define SECPKG_ATTR_ROOT_STORE 0x55 -#define SECPKG_ATTR_SUPPORTED_ALGS 0x56 -#define SECPKG_ATTR_CIPHER_STRENGTHS 0x57 -#define SECPKG_ATTR_SUPPORTED_PROTOCOLS 0x58 -#define SECPKG_ATTR_ISSUER_LIST_EX 0x59 -#define SECPKG_ATTR_CONNECTION_INFO 0x5a -#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b -#define SECPKG_ATTR_MAPPED_CRED_ATTR 0x5c -#define SECPKG_ATTR_SESSION_INFO 0x5d -#define SECPKG_ATTR_APP_DATA 0x5e - #define UNISP_RPC_ID 14
struct _HMAPPER; @@ -299,4 +284,26 @@ typedef struct _SecPkgContext_ConnectionInfo DWORD dwExchStrength; } SecPkgContext_ConnectionInfo, *PSecPkgContext_ConnectionInfo;
+#define SECPKGCONTEXT_CIPHERINFO_V1 1 +#define SZ_ALG_MAX_SIZE 64 + +typedef struct _SecPkgContext_CipherInfo +{ + DWORD dwVersion; + DWORD dwProtocol; + DWORD dwCipherSuite; + DWORD dwBaseCipherSuite; + WCHAR szCipherSuite[SZ_ALG_MAX_SIZE]; + WCHAR szCipher[SZ_ALG_MAX_SIZE]; + DWORD dwCipherLen; + DWORD dwCipherBlockLen; + WCHAR szHash[SZ_ALG_MAX_SIZE]; + DWORD dwHashLen; + WCHAR szExchange[SZ_ALG_MAX_SIZE]; + DWORD dwMinExchangeLen; + DWORD dwMaxExchangeLen; + WCHAR szCertificate[SZ_ALG_MAX_SIZE]; + DWORD dwKeyType; +} SecPkgContext_CipherInfo, *PSecPkgContext_CipherInfo; + #endif /* __WINE_SCHANNEL_H__ */
From: Hans Leidekker hans@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53180 --- dlls/secur32/schannel.c | 8 ++ dlls/secur32/schannel_gnutls.c | 222 +++++++++++++++++++++++++++++++++ dlls/secur32/secur32_priv.h | 7 ++ dlls/secur32/tests/schannel.c | 29 +++++ 4 files changed, 266 insertions(+)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 5b4fb196aca..7dc9bd1d21a 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -1244,6 +1244,12 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW( struct get_application_protocol_params params = { ctx->session, protocol }; return GNUTLS_CALL( get_application_protocol, ¶ms ); } + case SECPKG_ATTR_CIPHER_INFO: + { + SecPkgContext_CipherInfo *info = buffer; + struct get_cipher_info_params params = { ctx->session, info }; + return GNUTLS_CALL( get_cipher_info, ¶ms ); + }
default: FIXME("Unhandled attribute %#lx\n", attribute); @@ -1282,6 +1288,8 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesA( return schan_QueryContextAttributesW(context_handle, attribute, buffer); case SECPKG_ATTR_APPLICATION_PROTOCOL: return schan_QueryContextAttributesW(context_handle, attribute, buffer); + case SECPKG_ATTR_CIPHER_INFO: + return schan_QueryContextAttributesW(context_handle, attribute, buffer);
default: FIXME("Unhandled attribute %#lx\n", attribute); diff --git a/dlls/secur32/schannel_gnutls.c b/dlls/secur32/schannel_gnutls.c index ad319518a7a..39031a46086 100644 --- a/dlls/secur32/schannel_gnutls.c +++ b/dlls/secur32/schannel_gnutls.c @@ -698,6 +698,211 @@ static NTSTATUS schan_get_connection_info( void *args ) return SEC_E_OK; }
+static DWORD get_protocol_version( gnutls_session_t session ) +{ + gnutls_protocol_t proto = pgnutls_protocol_get_version( session ); + + switch (proto) + { + case GNUTLS_SSL3: return 0x300; + case GNUTLS_TLS1_0: return 0x301; + case GNUTLS_TLS1_1: return 0x302; + case GNUTLS_TLS1_2: return 0x303; + case GNUTLS_DTLS1_0: return 0x201; + case GNUTLS_DTLS1_2: return 0x202; + default: + FIXME( "unknown protocol %u\n", proto ); + return 0; + } +} + +static const WCHAR *get_cipher_str( gnutls_session_t session ) +{ + static const WCHAR aesW[] = {'A','E','S',0}; + static const WCHAR unknownW[] = {'<','u','n','k','n','o','w','n','>',0}; + gnutls_cipher_algorithm_t cipher = pgnutls_cipher_get( session ); + + switch (cipher) + { + case GNUTLS_CIPHER_AES_128_CBC: + case GNUTLS_CIPHER_AES_192_CBC: + case GNUTLS_CIPHER_AES_256_CBC: + case GNUTLS_CIPHER_AES_128_GCM: + case GNUTLS_CIPHER_AES_256_GCM: + case GNUTLS_CIPHER_AES_128_CCM: + case GNUTLS_CIPHER_AES_256_CCM: + return aesW; + default: + FIXME( "unknown cipher %u\n", cipher ); + return unknownW; + } +} + +static DWORD get_cipher_len( gnutls_session_t session ) +{ + gnutls_cipher_algorithm_t cipher = pgnutls_cipher_get( session ); + + switch (cipher) + { + case GNUTLS_CIPHER_AES_128_CBC: + case GNUTLS_CIPHER_AES_128_GCM: + case GNUTLS_CIPHER_AES_128_CCM: + return 128; + case GNUTLS_CIPHER_AES_192_CBC: + return 192; + case GNUTLS_CIPHER_AES_256_CBC: + case GNUTLS_CIPHER_AES_256_GCM: + case GNUTLS_CIPHER_AES_256_CCM: + return 256; + default: + FIXME( "unknown cipher %u\n", cipher ); + return 0; + } +} + +static DWORD get_cipher_block_len( gnutls_session_t session ) +{ + gnutls_cipher_algorithm_t cipher = pgnutls_cipher_get( session ); + return pgnutls_cipher_get_block_size( cipher ); +} + +static const WCHAR *get_hash_str( gnutls_session_t session, BOOL full ) +{ + static const WCHAR shaW[] = {'S','H','A',0}; + static const WCHAR sha1W[] = {'S','H','A','1',0}; + static const WCHAR sha224W[] = {'S','H','A','2','2','4',0}; + static const WCHAR sha256W[] = {'S','H','A','2','5','6',0}; + static const WCHAR sha384W[] = {'S','H','A','3','8','4',0}; + static const WCHAR sha512W[] = {'S','H','A','5','1','2',0}; + static const WCHAR unknownW[] = {'<','u','n','k','n','o','w','n','>',0}; + gnutls_mac_algorithm_t mac = pgnutls_mac_get( session ); + + switch (mac) + { + case GNUTLS_MAC_SHA1: return full ? sha1W : shaW; + case GNUTLS_MAC_SHA224: return sha224W; + case GNUTLS_MAC_SHA256: return sha256W; + case GNUTLS_MAC_SHA384: return sha384W; + case GNUTLS_MAC_SHA512: return sha512W; + default: + FIXME( "unknown mac %u\n", mac ); + return unknownW; + } +} + +static DWORD get_hash_len( gnutls_session_t session ) +{ + gnutls_mac_algorithm_t mac = pgnutls_mac_get( session ); + return pgnutls_mac_get_key_size( mac ) * 8; +} + +static const WCHAR *get_exchange_str( gnutls_session_t session, BOOL full ) +{ + static const WCHAR ecdhW[] = {'E','C','D','H',0}; + static const WCHAR ecdheW[] = {'E','C','D','H','E',0}; + static const WCHAR unknownW[] = {'<','u','n','k','n','o','w','n','>',0}; + gnutls_kx_algorithm_t kx = pgnutls_kx_get( session ); + + switch (kx) + { + case GNUTLS_KX_ECDHE_RSA: + case GNUTLS_KX_ECDHE_ECDSA: + return full ? ecdheW : ecdhW; + default: + FIXME( "unknown kx %u\n", kx ); + return unknownW; + } +} + +static const WCHAR *get_certificate_str( gnutls_session_t session ) +{ + static const WCHAR rsaW[] = {'R','S','A',0}; + static const WCHAR ecdsaW[] = {'E','C','D','S','A',0}; + static const WCHAR unknownW[] = {'<','u','n','k','n','o','w','n','>',0}; + gnutls_kx_algorithm_t kx = pgnutls_kx_get( session ); + + switch (kx) + { + case GNUTLS_KX_RSA: + case GNUTLS_KX_RSA_EXPORT: + case GNUTLS_KX_DHE_RSA: + case GNUTLS_KX_ECDHE_RSA: return rsaW; + case GNUTLS_KX_ECDHE_ECDSA: return ecdsaW; + default: + FIXME( "unknown kx %u\n", kx ); + return unknownW; + } +} + +static const WCHAR *get_chaining_mode_str( gnutls_session_t session ) +{ + static const WCHAR cbcW[] = {'C','B','C',0}; + static const WCHAR ccmW[] = {'C','C','M',0}; + static const WCHAR gcmW[] = {'G','C','M',0}; + static const WCHAR unknownW[] = {'<','u','n','k','n','o','w','n','>',0}; + gnutls_cipher_algorithm_t cipher = pgnutls_cipher_get( session ); + + switch (cipher) + { + case GNUTLS_CIPHER_AES_128_CBC: + case GNUTLS_CIPHER_AES_192_CBC: + case GNUTLS_CIPHER_AES_256_CBC: + return cbcW; + case GNUTLS_CIPHER_AES_128_GCM: + case GNUTLS_CIPHER_AES_256_GCM: + return gcmW; + case GNUTLS_CIPHER_AES_128_CCM: + case GNUTLS_CIPHER_AES_256_CCM: + return ccmW; + default: + FIXME( "unknown cipher %u\n", cipher ); + return unknownW; + } +} + +static NTSTATUS schan_get_cipher_info( void *args ) +{ + const WCHAR tlsW[] = {'T','L','S','_',0}; + const WCHAR underscoreW[] = {'_',0}; + const WCHAR widthW[] = {'_','W','I','T','H','_',0}; + const struct get_cipher_info_params *params = args; + gnutls_session_t session = session_from_handle( params->session ); + SecPkgContext_CipherInfo *info = params->info; + char buf[11]; + WCHAR *ptr; + int len; + + info->dwProtocol = get_protocol_version( session ); + info->dwCipherSuite = 0; /* FIXME */ + info->dwBaseCipherSuite = 0; /* FIXME */ + wcscpy( info->szCipher, get_cipher_str( session ) ); + info->dwCipherLen = get_cipher_len( session ); + info->dwCipherBlockLen = get_cipher_block_len( session ); + wcscpy( info->szHash, get_hash_str( session, TRUE ) ); + info->dwHashLen = get_hash_len( session ); + wcscpy( info->szExchange, get_exchange_str( session, FALSE ) ); + info->dwMinExchangeLen = 0; + info->dwMaxExchangeLen = 65536; + wcscpy( info->szCertificate, get_certificate_str( session ) ); + info->dwKeyType = 0; /* FIXME */ + + wcscpy( info->szCipherSuite, tlsW ); + wcscat( info->szCipherSuite, get_exchange_str( session, TRUE ) ); + wcscat( info->szCipherSuite, underscoreW ); + wcscat( info->szCipherSuite, info->szCertificate ); + wcscat( info->szCipherSuite, widthW ); + wcscat( info->szCipherSuite, info->szCipher ); + wcscat( info->szCipherSuite, underscoreW ); + len = sprintf( buf, "%u", (unsigned int)info->dwCipherLen ) + 1; + ptr = info->szCipherSuite + wcslen( info->szCipherSuite ); + ntdll_umbstowcs( buf, len, ptr, len ); + wcscat( info->szCipherSuite, underscoreW ); + wcscat( info->szCipherSuite, get_chaining_mode_str( session ) ); + wcscat( info->szCipherSuite, underscoreW ); + wcscat( info->szCipherSuite, get_hash_str( session, FALSE ) ); + return SEC_E_OK; +} + static NTSTATUS schan_get_unique_channel_binding( void *args ) { const struct get_unique_channel_binding_params *params = args; @@ -1271,6 +1476,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = schan_dispose_session, schan_free_certificate_credentials, schan_get_application_protocol, + schan_get_cipher_info, schan_get_connection_info, schan_get_enabled_protocols, schan_get_key_signature_algorithm, @@ -1386,6 +1592,21 @@ static NTSTATUS wow64_schan_get_connection_info( void *args ) return schan_get_connection_info(¶ms); }
+static NTSTATUS wow64_schan_get_cipher_info( void *args ) +{ + struct + { + schan_session session; + PTR32 info; + } const *params32 = args; + struct get_cipher_info_params params = + { + params32->session, + ULongToPtr(params32->info), + }; + return schan_get_connection_info(¶ms); +} + static NTSTATUS wow64_schan_get_session_peer_certificate( void *args ) { struct @@ -1582,6 +1803,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = schan_dispose_session, wow64_schan_free_certificate_credentials, wow64_schan_get_application_protocol, + wow64_schan_get_cipher_info, wow64_schan_get_connection_info, schan_get_enabled_protocols, schan_get_key_signature_algorithm, diff --git a/dlls/secur32/secur32_priv.h b/dlls/secur32/secur32_priv.h index 5753ed47ffa..d1321b7d6fd 100644 --- a/dlls/secur32/secur32_priv.h +++ b/dlls/secur32/secur32_priv.h @@ -126,6 +126,12 @@ struct get_connection_info_params SecPkgContext_ConnectionInfo *info; };
+struct get_cipher_info_params +{ + schan_session session; + SecPkgContext_CipherInfo *info; +}; + struct get_session_peer_certificate_params { schan_session session; @@ -206,6 +212,7 @@ enum schan_funcs unix_dispose_session, unix_free_certificate_credentials, unix_get_application_protocol, + unix_get_cipher_info, unix_get_connection_info, unix_get_enabled_protocols, unix_get_key_signature_algorithm, diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 314b43a2607..c68474e9eae 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1038,6 +1038,7 @@ static void test_communication(void) CRYPT_DATA_BLOB pfx; HCERTSTORE store; SecPkgContext_NegotiationInfoA info; + SecPkgContext_CipherInfo cipher; SecBufferDesc buffers[2]; SecBuffer *buf; unsigned buf_size = 8192; @@ -1291,6 +1292,34 @@ static void test_communication(void) ok(conn_info.dwHashStrength >= 128, "conn_info.dwHashStrength = %ld\n", conn_info.dwHashStrength); }
+ memset(&cipher, 0, sizeof(cipher)); + cipher.dwVersion = SECPKGCONTEXT_CIPHERINFO_V1; + status = pQueryContextAttributesA(&context, SECPKG_ATTR_CIPHER_INFO, &cipher); + ok(status == SEC_E_OK || broken(status == SEC_E_UNSUPPORTED_FUNCTION) /* < vista */, "got %08lx\n", status); + if (status == SEC_E_OK) + { + ok(cipher.dwProtocol == 0x301, "got %lx\n", cipher.dwProtocol); + todo_wine ok(cipher.dwCipherSuite == 0xc014, "got %lx\n", cipher.dwCipherSuite); + todo_wine ok(cipher.dwBaseCipherSuite == 0xc014, "got %lx\n", cipher.dwBaseCipherSuite); + ok(!wcscmp(cipher.szCipherSuite, L"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA") || + !wcscmp(cipher.szCipherSuite, L"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P256"), /* < win10 */ + "got %s\n", wine_dbgstr_w(cipher.szCipherSuite)); + ok(!wcscmp(cipher.szCipher, L"AES"), "got %s\n", wine_dbgstr_w(cipher.szCipher)); + ok(cipher.dwCipherLen == 256, "got %lu\n", cipher.dwCipherLen); + ok(cipher.dwCipherBlockLen == 16, "got %lu\n", cipher.dwCipherBlockLen); + ok(!wcscmp(cipher.szHash, L"SHA1"), "got %s\n", wine_dbgstr_w(cipher.szHash)); + ok(cipher.dwHashLen == 160, "got %lu\n", cipher.dwHashLen); + ok(!wcscmp(cipher.szExchange, L"ECDH") || !wcscmp(cipher.szExchange, L"ECDH_P256"), /* < win10 */ + "got %s\n", wine_dbgstr_w(cipher.szExchange)); + ok(cipher.dwMinExchangeLen == 0 || cipher.dwMinExchangeLen == 256, /* < win10 */ + "got %lu\n", cipher.dwMinExchangeLen); + ok(cipher.dwMaxExchangeLen == 65536 || cipher.dwMaxExchangeLen == 256, /* < win10 */ + "got %lu\n", cipher.dwMaxExchangeLen); + ok(!wcscmp(cipher.szCertificate, L"RSA"), "got %s\n", wine_dbgstr_w(cipher.szCertificate)); + todo_wine ok(cipher.dwKeyType == 0x1d || cipher.dwKeyType == 0x17, /* < win10 */ + "got %#lx\n", cipher.dwKeyType); + } + status = pQueryContextAttributesA(&context, SECPKG_ATTR_KEY_INFO, &key_info); ok(status == SEC_E_OK, "QueryContextAttributesW(SECPKG_ATTR_KEY_INFO) failed: %08lx\n", status); if(status == SEC_E_OK) {
Hans Leidekker wine@gitlab.winehq.org wrote:
@@ -1386,6 +1592,21 @@ static NTSTATUS wow64_schan_get_connection_info( void *args ) return schan_get_connection_info(¶ms); }
+static NTSTATUS wow64_schan_get_cipher_info( void *args ) +{
- struct
- {
schan_session session;
PTR32 info;
- } const *params32 = args;
- struct get_cipher_info_params params =
- {
params32->session,
ULongToPtr(params32->info),
- };
- return schan_get_connection_info(¶ms);
+}
Probably it should forward to schan_get_cipher_info().
On Tue, 2022-06-21 at 13:16 +0300, Dmitry Timoshkov wrote:
Hans Leidekker wine@gitlab.winehq.org wrote:
+static NTSTATUS wow64_schan_get_cipher_info( void *args ) +{
- struct
- {
schan_session session;
PTR32 info;
- } const *params32 = args;
- struct get_cipher_info_params params =
- {
params32->session,
ULongToPtr(params32->info),
- };
- return schan_get_connection_info(¶ms);
+}
Probably it should forward to schan_get_cipher_info().
Thanks, I pushed a fix.