[PATCH v2 2/2] secur32: Implement SECPKG_ATTR_CIPHER_INFO.

Hans Leidekker wine at gitlab.winehq.org
Tue Jun 21 05:47:14 CDT 2022


From: Hans Leidekker <hans at 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, &params );
         }
+        case SECPKG_ATTR_CIPHER_INFO:
+        {
+            SecPkgContext_CipherInfo *info = buffer;
+            struct get_cipher_info_params params = { ctx->session, info };
+            return GNUTLS_CALL( get_cipher_info, &params );
+        }
 
         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..58e787b7600 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(&params);
 }
 
+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_cipher_info(&params);
+}
+
 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) {
-- 
GitLab

https://gitlab.winehq.org/wine/wine/-/merge_requests/291



More information about the wine-devel mailing list