diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index c5f4305bcebbaa..d180aa98538f1f 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -21,6 +21,10 @@ internal static partial class Interop { internal static partial class OpenSsl { +#if DEBUG + private static readonly string? s_keyLogFile = Environment.GetEnvironmentVariable("SSLKEYLOGFILE"); + private static readonly FileStream? s_fileStream = s_keyLogFile != null ? File.Open(s_keyLogFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite) : null; +#endif private const string DisableTlsResumeCtxSwitch = "System.Net.Security.DisableTlsResume"; private const string DisableTlsResumeEnvironmentVariable = "DOTNET_SYSTEM_NET_SECURITY_DISABLETLSRESUME"; private const string TlsCacheSizeCtxName = "System.Net.Security.TlsCacheSize"; @@ -236,6 +240,12 @@ internal static unsafe SafeSslContextHandle AllocateSslContext(SslAuthentication Ssl.SslCtxSetDefaultOcspCallback(sslCtx); } } +#if DEBUG + if (s_fileStream != null) + { + Ssl.SslCtxSetKeylogCallback(sslCtx, &KeyLogCallback); + } +#endif } catch { @@ -783,6 +793,23 @@ private static unsafe void RemoveSessionCallback(IntPtr ctx, IntPtr session) ctxHandle.RemoveSession(name); } +#if DEBUG + [UnmanagedCallersOnly] + private static unsafe void KeyLogCallback(IntPtr ssl, char* line) + { + Debug.Assert(s_fileStream != null); + ReadOnlySpan data = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)line); + if (data.Length > 0) + { + lock (s_fileStream) + { + s_fileStream.Write(data); + s_fileStream.WriteByte((byte)'\n'); + } + } + } +#endif + private static int BioRead(SafeBioHandle bio, byte[] buffer, int count) { Debug.Assert(buffer != null); diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtx.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtx.cs index aae292fe35c64b..5baa776d9d5a8a 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtx.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtx.cs @@ -33,6 +33,9 @@ internal static partial class Ssl [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslCtxSetAlpnSelectCb")] internal static unsafe partial void SslCtxSetAlpnSelectCb(SafeSslContextHandle ctx, delegate* unmanaged callback, IntPtr arg); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslCtxSetKeylogCallback")] + internal static unsafe partial void SslCtxSetKeylogCallback(SafeSslContextHandle ctx, delegate* unmanaged callback); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslCtxSetCaching")] internal static unsafe partial int SslCtxSetCaching(SafeSslContextHandle ctx, int mode, int cacheSize, int contextIdLength, Span contextId, delegate* unmanaged neewSessionCallback, delegate* unmanaged removeSessionCallback); diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs index 3a19656f63acb7..4c45abe7acd925 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs @@ -15,7 +15,7 @@ namespace System.Net.Quic; internal sealed class MsQuicTlsSecret : IDisposable { private static readonly string? s_keyLogFile = Environment.GetEnvironmentVariable("SSLKEYLOGFILE"); - private static readonly FileStream? s_fileStream = s_keyLogFile != null ? File.Open(s_keyLogFile, FileMode.Append, FileAccess.Write) : null; + private static readonly FileStream? s_fileStream = s_keyLogFile != null ? File.Open(s_keyLogFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite) : null; private unsafe QUIC_TLS_SECRETS* _tlsSecrets; diff --git a/src/native/libs/System.Security.Cryptography.Native/apibridge.c b/src/native/libs/System.Security.Cryptography.Native/apibridge.c index b130edf3574e01..bad65ee2b50211 100644 --- a/src/native/libs/System.Security.Cryptography.Native/apibridge.c +++ b/src/native/libs/System.Security.Cryptography.Native/apibridge.c @@ -809,6 +809,12 @@ void local_SSL_set_security_level(SSL* ssl, int32_t level) (void)level; } +void local_SSL_CTX_set_keylog_callback(SSL_CTX *ctx, SSL_CTX_keylog_cb_func cb) +{ + (void)ctx; + (void)cb; +} + int local_BIO_up_ref(BIO *bio) { if (!bio) diff --git a/src/native/libs/System.Security.Cryptography.Native/apibridge.h b/src/native/libs/System.Security.Cryptography.Native/apibridge.h index 397159da810bec..93cff0d2fd11ed 100644 --- a/src/native/libs/System.Security.Cryptography.Native/apibridge.h +++ b/src/native/libs/System.Security.Cryptography.Native/apibridge.h @@ -59,3 +59,5 @@ const X509_ALGOR* local_X509_get0_tbs_sigalg(const X509* x509); X509_PUBKEY* local_X509_get_X509_PUBKEY(const X509* x509); int32_t local_X509_get_version(const X509* x509); int32_t local_X509_up_ref(X509* x509); +typedef void (*SSL_CTX_keylog_cb_func)(const SSL *ssl, const char *line); +void local_SSL_CTX_set_keylog_callback(SSL_CTX *ctx, SSL_CTX_keylog_cb_func cb); diff --git a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c index 9df3a271103230..f69959f36a4b51 100644 --- a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c @@ -302,6 +302,7 @@ static const Entry s_cryptoNative[] = DllImportEntry(CryptoNative_SslCtxSetCiphers) DllImportEntry(CryptoNative_SslCtxSetDefaultOcspCallback) DllImportEntry(CryptoNative_SslCtxSetEncryptionPolicy) + DllImportEntry(CryptoNative_SslCtxSetKeylogCallback) DllImportEntry(CryptoNative_SetCiphers) DllImportEntry(CryptoNative_SslCreate) DllImportEntry(CryptoNative_SslCtxCheckPrivateKey) diff --git a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h index 8b0188440cd0a5..9abc53338796e0 100644 --- a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h +++ b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h @@ -509,6 +509,7 @@ int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t len); LIGHTUP_FUNCTION(SSL_CTX_set_ciphersuites) \ REQUIRED_FUNCTION(SSL_CTX_set_client_cert_cb) \ REQUIRED_FUNCTION(SSL_CTX_set_ex_data) \ + FALLBACK_FUNCTION(SSL_CTX_set_keylog_callback) \ REQUIRED_FUNCTION(SSL_CTX_set_quiet_shutdown) \ FALLBACK_FUNCTION(SSL_CTX_set_options) \ FALLBACK_FUNCTION(SSL_CTX_set_security_level) \ @@ -1005,6 +1006,7 @@ FOR_ALL_OPENSSL_FUNCTIONS #define SSL_CTX_set_client_cert_cb SSL_CTX_set_client_cert_cb_ptr #define SSL_CTX_set_ex_data SSL_CTX_set_ex_data_ptr #define SSL_CTX_set_options SSL_CTX_set_options_ptr +#define SSL_CTX_set_keylog_callback SSL_CTX_set_keylog_callback_ptr #define SSL_CTX_set_quiet_shutdown SSL_CTX_set_quiet_shutdown_ptr #define SSL_CTX_set_security_level SSL_CTX_set_security_level_ptr #define SSL_CTX_set_session_id_context SSL_CTX_set_session_id_context_ptr @@ -1268,6 +1270,7 @@ FOR_ALL_OPENSSL_FUNCTIONS #define X509_get_X509_PUBKEY local_X509_get_X509_PUBKEY #define X509_get_version local_X509_get_version #define X509_up_ref local_X509_up_ref +#define SSL_CTX_set_keylog_callback local_SSL_CTX_set_keylog_callback #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_0_2_RTM diff --git a/src/native/libs/System.Security.Cryptography.Native/osslcompat_111.h b/src/native/libs/System.Security.Cryptography.Native/osslcompat_111.h index ccb8ddfb22368b..2581e7f8150682 100644 --- a/src/native/libs/System.Security.Cryptography.Native/osslcompat_111.h +++ b/src/native/libs/System.Security.Cryptography.Native/osslcompat_111.h @@ -86,6 +86,7 @@ int X509_set1_notBefore(X509* x509, const ASN1_TIME*); int32_t X509_up_ref(X509* x509); const char *SSL_SESSION_get0_hostname(const SSL_SESSION *s); int SSL_SESSION_set1_hostname(SSL_SESSION *s, const char *hostname); +void SSL_CTX_set_keylog_callback(SSL_CTX *ctx, SSL_CTX_keylog_cb_func cb); #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_0_2_RTM int32_t X509_check_host(X509* x509, const char* name, size_t namelen, unsigned int flags, char** peername); diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c index 5ccd3b4888775d..29d478555ce130 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c @@ -1005,6 +1005,13 @@ void CryptoNative_SslSetClientCertCallback(SSL* ssl, int set) SSL_set_cert_cb(ssl, set ? client_certificate_cb : NULL, NULL); } +void CryptoNative_SslCtxSetKeylogCallback(SSL_CTX* ctx, SslCtxSetKeylogCallback cb) +{ + // void shim functions don't lead to exceptions, so skip the unconditional error clearing. + + SSL_CTX_set_keylog_callback(ctx, cb); +} + void CryptoNative_SslSetPostHandshakeAuth(SSL* ssl, int32_t val) { #if defined NEED_OPENSSL_1_1 || defined NEED_OPENSSL_3_0 diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h index 738511d31a1d96..3c63564cc4e59f 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h +++ b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h @@ -124,6 +124,9 @@ typedef int32_t (*SslCtxNewSessionCallback)(SSL* ssl, SSL_SESSION* session); // the function pointer used for new session typedef void (*SslCtxRemoveSessionCallback)(SSL_CTX* ctx, SSL_SESSION* session); +// the function pointer for keylog +typedef void (*SslCtxSetKeylogCallback)(const SSL* ssl, const char *line); + /* Ensures that libssl is correctly initialized and ready to use. */ @@ -164,6 +167,11 @@ Sets session caching. 0 is disabled. */ PALEXPORT int CryptoNative_SslCtxSetCaching(SSL_CTX* ctx, int mode, int cacheSize, int contextIdLength, uint8_t* contextId, SslCtxNewSessionCallback newSessionCb, SslCtxRemoveSessionCallback removeSessionCb); +/* +Sets callback to log TLS session keys +*/ +PALEXPORT void CryptoNative_SslCtxSetKeylogCallback(SSL_CTX* ctx, SslCtxSetKeylogCallback callback); + /* Returns name associated with given ssl session. OpenSSL holds reference to it and it must not be freed.