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..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(¶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_cipher_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) {