From 303416080c447dae8729e1ffbe78a366f1f88a71 Mon Sep 17 00:00:00 2001 From: Thibaut Le Guilly Date: Tue, 9 Oct 2018 17:10:22 +0900 Subject: [PATCH 1/8] Provide access to verify_peer_callback from C# --- src/csharp/Grpc.Core/ChannelCredentials.cs | 69 +++++++++++++++++-- .../Internal/ChannelCredentialsSafeHandle.cs | 12 +++- .../Internal/NativeMethods.Generated.cs | 6 +- src/csharp/Grpc.Core/VerifyPeerContext.cs | 30 ++++++++ .../SslCredentialsTest.cs | 43 +++++++++++- src/csharp/ext/grpc_csharp_ext.c | 41 +++++++++-- .../Grpc.Core/Internal/native_methods.include | 2 +- 7 files changed, 184 insertions(+), 19 deletions(-) create mode 100644 src/csharp/Grpc.Core/VerifyPeerContext.cs diff --git a/src/csharp/Grpc.Core/ChannelCredentials.cs b/src/csharp/Grpc.Core/ChannelCredentials.cs index 3ce32f31b76..103a594bb06 100644 --- a/src/csharp/Grpc.Core/ChannelCredentials.cs +++ b/src/csharp/Grpc.Core/ChannelCredentials.cs @@ -18,9 +18,11 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Threading.Tasks; using Grpc.Core.Internal; +using Grpc.Core.Logging; using Grpc.Core.Utils; namespace Grpc.Core @@ -104,20 +106,36 @@ namespace Grpc.Core } } + /// + /// Callback invoked with the expected targetHost and the peer's certificate. + /// If false is returned by this callback then it is treated as a + /// verification failure. Invocation of the callback is blocking, so any + /// implementation should be light-weight. + /// + /// The associated with the callback + /// true if verification succeeded, false otherwise. + /// Note: experimental API that can change or be removed without any prior notice. + public delegate bool VerifyPeerCallback(VerifyPeerContext context); + /// /// Client-side SSL credentials. /// public sealed class SslCredentials : ChannelCredentials { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); + readonly string rootCertificates; readonly KeyCertificatePair keyCertificatePair; + readonly VerifyPeerCallback verifyPeerCallback; + readonly VerifyPeerCallbackInternal verifyPeerCallbackInternal; + readonly GCHandle gcHandle; /// /// Creates client-side SSL credentials loaded from /// disk file pointed to by the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable. /// If that fails, gets the roots certificates from a well known place on disk. /// - public SslCredentials() : this(null, null) + public SslCredentials() : this(null, null, null) { } @@ -125,19 +143,37 @@ namespace Grpc.Core /// Creates client-side SSL credentials from /// a string containing PEM encoded root certificates. /// - public SslCredentials(string rootCertificates) : this(rootCertificates, null) + public SslCredentials(string rootCertificates) : this(rootCertificates, null, null) { } - + + /// + /// Creates client-side SSL credentials. + /// + /// string containing PEM encoded server root certificates. + /// a key certificate pair. + public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair) : + this(rootCertificates, keyCertificatePair, null) + { + } + /// /// Creates client-side SSL credentials. /// /// string containing PEM encoded server root certificates. /// a key certificate pair. - public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair) + /// a callback to verify peer's target name and certificate. + /// Note: experimental API that can change or be removed without any prior notice. + public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair, VerifyPeerCallback verifyPeerCallback) { this.rootCertificates = rootCertificates; this.keyCertificatePair = keyCertificatePair; + if (verifyPeerCallback != null) + { + this.verifyPeerCallback = verifyPeerCallback; + this.verifyPeerCallbackInternal = this.VerifyPeerCallbackHandler; + gcHandle = GCHandle.Alloc(verifyPeerCallbackInternal); + } } /// @@ -171,7 +207,30 @@ namespace Grpc.Core internal override ChannelCredentialsSafeHandle CreateNativeCredentials() { - return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair); + return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair, this.verifyPeerCallbackInternal); + } + + private int VerifyPeerCallbackHandler(IntPtr host, IntPtr pem, IntPtr userData, bool isDestroy) + { + if (isDestroy) + { + this.gcHandle.Free(); + return 0; + } + + try + { + var context = new VerifyPeerContext(Marshal.PtrToStringAnsi(host), Marshal.PtrToStringAnsi(pem)); + + return this.verifyPeerCallback(context) ? 0 : 1; + } + catch (Exception e) + { + // eat the exception, we must not throw when inside callback from native code. + Logger.Error(e, "Exception occurred while invoking verify peer callback handler."); + // Return validation failure in case of exception. + return 1; + } } } diff --git a/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs index 11b5d2cc3f6..5e336673153 100644 --- a/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs @@ -20,6 +20,12 @@ using System.Threading.Tasks; namespace Grpc.Core.Internal { + internal delegate int VerifyPeerCallbackInternal( + IntPtr targetHost, + IntPtr targetPem, + IntPtr userData, + bool isDestroy); + /// /// grpc_channel_credentials from grpc/grpc_security.h /// @@ -38,15 +44,15 @@ namespace Grpc.Core.Internal return creds; } - public static ChannelCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, KeyCertificatePair keyCertPair) + public static ChannelCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, KeyCertificatePair keyCertPair, VerifyPeerCallbackInternal verifyPeerCallback) { if (keyCertPair != null) { - return Native.grpcsharp_ssl_credentials_create(pemRootCerts, keyCertPair.CertificateChain, keyCertPair.PrivateKey); + return Native.grpcsharp_ssl_credentials_create(pemRootCerts, keyCertPair.CertificateChain, keyCertPair.PrivateKey, verifyPeerCallback); } else { - return Native.grpcsharp_ssl_credentials_create(pemRootCerts, null, null); + return Native.grpcsharp_ssl_credentials_create(pemRootCerts, null, null, verifyPeerCallback); } } diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs index b7b9a12d8a3..e09b9813f79 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs @@ -482,7 +482,7 @@ namespace Grpc.Core.Internal public delegate void grpcsharp_channel_args_set_integer_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, int value); public delegate void grpcsharp_channel_args_destroy_delegate(IntPtr args); public delegate void grpcsharp_override_default_ssl_roots_delegate(string pemRootCerts); - public delegate ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create_delegate(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey); + public delegate ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create_delegate(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, VerifyPeerCallbackInternal verifyPeerCallback); public delegate ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create_delegate(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds); public delegate void grpcsharp_channel_credentials_release_delegate(IntPtr credentials); public delegate ChannelSafeHandle grpcsharp_insecure_channel_create_delegate(string target, ChannelArgsSafeHandle channelArgs); @@ -676,7 +676,7 @@ namespace Grpc.Core.Internal public static extern void grpcsharp_override_default_ssl_roots(string pemRootCerts); [DllImport(ImportName)] - public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey); + public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, VerifyPeerCallbackInternal verifyPeerCallback); [DllImport(ImportName)] public static extern ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds); @@ -972,7 +972,7 @@ namespace Grpc.Core.Internal public static extern void grpcsharp_override_default_ssl_roots(string pemRootCerts); [DllImport(ImportName)] - public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey); + public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, VerifyPeerCallbackInternal verifyPeerCallback); [DllImport(ImportName)] public static extern ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds); diff --git a/src/csharp/Grpc.Core/VerifyPeerContext.cs b/src/csharp/Grpc.Core/VerifyPeerContext.cs new file mode 100644 index 00000000000..b48984b093c --- /dev/null +++ b/src/csharp/Grpc.Core/VerifyPeerContext.cs @@ -0,0 +1,30 @@ +namespace Grpc.Core +{ + /// + /// Verification context for VerifyPeerCallback. + /// Note: experimental API that can change or be removed without any prior notice. + /// + public class VerifyPeerContext + { + /// + /// Initializes a new instance of the class. + /// + /// string containing the host name of the peer. + /// string containing PEM encoded certificate of the peer. + internal VerifyPeerContext(string targetHost, string targetPem) + { + this.TargetHost = targetHost; + this.TargetPem = targetPem; + } + + /// + /// String containing the host name of the peer. + /// + public string TargetHost { get; } + + /// + /// string containing PEM encoded certificate of the peer. + /// + public string TargetPem { get; } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs index b3c47c2d8d3..818ac6d1adc 100644 --- a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs @@ -44,17 +44,23 @@ namespace Grpc.IntegrationTesting string rootCert; KeyCertificatePair keyCertPair; + string certChain; + List options; + bool isHostEqual; + bool isPemEqual; public void InitClientAndServer(bool clientAddKeyCertPair, SslClientCertificateRequestType clientCertRequestType) { rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath); + certChain = File.ReadAllText(TestCredentials.ServerCertChainPath); + certChain = certChain.Replace("\r", string.Empty); keyCertPair = new KeyCertificatePair( - File.ReadAllText(TestCredentials.ServerCertChainPath), + certChain, File.ReadAllText(TestCredentials.ServerPrivateKeyPath)); var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, clientCertRequestType); - var clientCredentials = clientAddKeyCertPair ? new SslCredentials(rootCert, keyCertPair) : new SslCredentials(rootCert); + var clientCredentials = clientAddKeyCertPair ? new SslCredentials(rootCert, keyCertPair, context => this.VerifyPeerCallback(context, true)) : new SslCredentials(rootCert); // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755 server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) @@ -64,7 +70,7 @@ namespace Grpc.IntegrationTesting }; server.Start(); - var options = new List + options = new List { new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) }; @@ -210,6 +216,37 @@ namespace Grpc.IntegrationTesting Assert.AreEqual(12345, response.AggregatedPayloadSize); } + [Test] + public void VerifyPeerCallbackTest() + { + InitClientAndServer(true, SslClientCertificateRequestType.RequestAndRequireAndVerify); + + // Force GC collection to verify that the VerifyPeerCallback is not collected. If + // it gets collected, this test will hang. + GC.Collect(); + + client.UnaryCall(new SimpleRequest { ResponseSize = 10 }); + Assert.IsTrue(isHostEqual); + Assert.IsTrue(isPemEqual); + } + + [Test] + public void VerifyPeerCallbackFailTest() + { + InitClientAndServer(true, SslClientCertificateRequestType.RequestAndRequireAndVerify); + var clientCredentials = new SslCredentials(rootCert, keyCertPair, context => this.VerifyPeerCallback(context, false)); + var failingChannel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options); + var failingClient = new TestService.TestServiceClient(failingChannel); + Assert.Throws(() => failingClient.UnaryCall(new SimpleRequest { ResponseSize = 10 })); + } + + private bool VerifyPeerCallback(VerifyPeerContext context, bool returnValue) + { + isHostEqual = TestCredentials.DefaultHostOverride == context.TargetHost; + isPemEqual = certChain == context.TargetPem; + return returnValue; + } + private class SslCredentialsTestServiceImpl : TestService.TestServiceBase { public override Task UnaryCall(SimpleRequest request, ServerCallContext context) diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index dc690a6a608..5f80d3417a5 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -927,20 +927,53 @@ grpcsharp_override_default_ssl_roots(const char* pem_root_certs) { grpc_set_ssl_roots_override_callback(override_ssl_roots_handler); } +typedef int(GPR_CALLTYPE* grpcsharp_verify_peer_func)(const char* target_host, + const char* target_pem, + void* userdata, + int32_t isDestroy); + +static void grpcsharp_verify_peer_destroy_handler(void* userdata) { + grpcsharp_verify_peer_func callback = + (grpcsharp_verify_peer_func)(intptr_t)userdata; + callback(NULL, NULL, NULL, 1); +} + +static int grpcsharp_verify_peer_handler(const char* target_host, + const char* target_pem, + void* userdata) { + grpcsharp_verify_peer_func callback = + (grpcsharp_verify_peer_func)(intptr_t)userdata; + return callback(target_host, target_pem, NULL, 0); +} + + GPR_EXPORT grpc_channel_credentials* GPR_CALLTYPE grpcsharp_ssl_credentials_create(const char* pem_root_certs, const char* key_cert_pair_cert_chain, - const char* key_cert_pair_private_key) { + const char* key_cert_pair_private_key, + grpcsharp_verify_peer_func verify_peer_func) { grpc_ssl_pem_key_cert_pair key_cert_pair; + verify_peer_options verify_options; + verify_peer_options* p_verify_options = NULL; + if (verify_peer_func != NULL) { + verify_options.verify_peer_callback_userdata = + (void*)(intptr_t)verify_peer_func; + verify_options.verify_peer_destruct = + grpcsharp_verify_peer_destroy_handler; + verify_options.verify_peer_callback = grpcsharp_verify_peer_handler; + p_verify_options = &verify_options; + } + if (key_cert_pair_cert_chain || key_cert_pair_private_key) { key_cert_pair.cert_chain = key_cert_pair_cert_chain; key_cert_pair.private_key = key_cert_pair_private_key; - return grpc_ssl_credentials_create(pem_root_certs, &key_cert_pair, NULL, - NULL); + return grpc_ssl_credentials_create(pem_root_certs, &key_cert_pair, + p_verify_options, NULL); } else { GPR_ASSERT(!key_cert_pair_cert_chain); GPR_ASSERT(!key_cert_pair_private_key); - return grpc_ssl_credentials_create(pem_root_certs, NULL, NULL, NULL); + return grpc_ssl_credentials_create(pem_root_certs, NULL, p_verify_options, + NULL); } } diff --git a/templates/src/csharp/Grpc.Core/Internal/native_methods.include b/templates/src/csharp/Grpc.Core/Internal/native_methods.include index b7a8e285488..4cb71730529 100644 --- a/templates/src/csharp/Grpc.Core/Internal/native_methods.include +++ b/templates/src/csharp/Grpc.Core/Internal/native_methods.include @@ -44,7 +44,7 @@ native_method_signatures = [ 'void grpcsharp_channel_args_set_integer(ChannelArgsSafeHandle args, UIntPtr index, string key, int value)', 'void grpcsharp_channel_args_destroy(IntPtr args)', 'void grpcsharp_override_default_ssl_roots(string pemRootCerts)', - 'ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey)', + 'ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, VerifyPeerCallbackInternal verifyPeerCallback)', 'ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds)', 'void grpcsharp_channel_credentials_release(IntPtr credentials)', 'ChannelSafeHandle grpcsharp_insecure_channel_create(string target, ChannelArgsSafeHandle channelArgs)', From f1199912e85e8cb7cc03c3b2c1751797cd474316 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Sat, 30 Mar 2019 22:30:18 -0700 Subject: [PATCH 2/8] fix typo in log message --- src/csharp/Grpc.Core/Internal/NativeCallbackDispatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csharp/Grpc.Core/Internal/NativeCallbackDispatcher.cs b/src/csharp/Grpc.Core/Internal/NativeCallbackDispatcher.cs index d5146e816e2..0777d7933e6 100644 --- a/src/csharp/Grpc.Core/Internal/NativeCallbackDispatcher.cs +++ b/src/csharp/Grpc.Core/Internal/NativeCallbackDispatcher.cs @@ -63,7 +63,7 @@ namespace Grpc.Core.Internal catch (Exception e) { // eat the exception, we must not throw when inside callback from native code. - Logger.Error(e, "Caught exception inside callback from native callback."); + Logger.Error(e, "Caught exception inside callback from native code."); return 0; } } From c835fedd4d42d7799a9a0c89b284db9c9a66a886 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Thu, 21 Mar 2019 12:07:08 -0700 Subject: [PATCH 3/8] Modify verifyPeerCallback logic to use NativeCallbackDispatcher --- src/csharp/Grpc.Core/ChannelCredentials.cs | 61 +++++++----- .../Internal/ChannelCredentialsSafeHandle.cs | 12 +-- .../Internal/NativeMethods.Generated.cs | 6 +- .../SslCredentialsTest.cs | 92 ++++++++++--------- src/csharp/ext/grpc_csharp_ext.c | 53 +++++------ .../Grpc.Core/Internal/native_methods.include | 2 +- 6 files changed, 121 insertions(+), 105 deletions(-) diff --git a/src/csharp/Grpc.Core/ChannelCredentials.cs b/src/csharp/Grpc.Core/ChannelCredentials.cs index 103a594bb06..2ecd3875e84 100644 --- a/src/csharp/Grpc.Core/ChannelCredentials.cs +++ b/src/csharp/Grpc.Core/ChannelCredentials.cs @@ -127,8 +127,6 @@ namespace Grpc.Core readonly string rootCertificates; readonly KeyCertificatePair keyCertificatePair; readonly VerifyPeerCallback verifyPeerCallback; - readonly VerifyPeerCallbackInternal verifyPeerCallbackInternal; - readonly GCHandle gcHandle; /// /// Creates client-side SSL credentials loaded from @@ -168,12 +166,7 @@ namespace Grpc.Core { this.rootCertificates = rootCertificates; this.keyCertificatePair = keyCertificatePair; - if (verifyPeerCallback != null) - { - this.verifyPeerCallback = verifyPeerCallback; - this.verifyPeerCallbackInternal = this.VerifyPeerCallbackHandler; - gcHandle = GCHandle.Alloc(verifyPeerCallbackInternal); - } + this.verifyPeerCallback = verifyPeerCallback; } /// @@ -207,29 +200,53 @@ namespace Grpc.Core internal override ChannelCredentialsSafeHandle CreateNativeCredentials() { - return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair, this.verifyPeerCallbackInternal); + IntPtr verifyPeerCallbackTag = IntPtr.Zero; + if (verifyPeerCallback != null) + { + verifyPeerCallbackTag = new VerifyPeerCallbackRegistration(verifyPeerCallback).CallbackRegistration.Tag; + } + return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair, verifyPeerCallbackTag); } - private int VerifyPeerCallbackHandler(IntPtr host, IntPtr pem, IntPtr userData, bool isDestroy) + private class VerifyPeerCallbackRegistration { - if (isDestroy) + readonly VerifyPeerCallback verifyPeerCallback; + readonly NativeCallbackRegistration callbackRegistration; + + public VerifyPeerCallbackRegistration(VerifyPeerCallback verifyPeerCallback) { - this.gcHandle.Free(); - return 0; + this.verifyPeerCallback = verifyPeerCallback; + this.callbackRegistration = NativeCallbackDispatcher.RegisterCallback(HandleUniversalCallback); } - try - { - var context = new VerifyPeerContext(Marshal.PtrToStringAnsi(host), Marshal.PtrToStringAnsi(pem)); + public NativeCallbackRegistration CallbackRegistration => callbackRegistration; - return this.verifyPeerCallback(context) ? 0 : 1; + private int HandleUniversalCallback(IntPtr arg0, IntPtr arg1, IntPtr arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5) + { + return VerifyPeerCallbackHandler(arg0, arg1, arg2 != IntPtr.Zero); } - catch (Exception e) + + private int VerifyPeerCallbackHandler(IntPtr host, IntPtr pem, bool isDestroy) { - // eat the exception, we must not throw when inside callback from native code. - Logger.Error(e, "Exception occurred while invoking verify peer callback handler."); - // Return validation failure in case of exception. - return 1; + if (isDestroy) + { + this.callbackRegistration.Dispose(); + return 0; + } + + try + { + var context = new VerifyPeerContext(Marshal.PtrToStringAnsi(host), Marshal.PtrToStringAnsi(pem)); + + return this.verifyPeerCallback(context) ? 0 : 1; + } + catch (Exception e) + { + // eat the exception, we must not throw when inside callback from native code. + Logger.Error(e, "Exception occurred while invoking verify peer callback handler."); + // Return validation failure in case of exception. + return 1; + } } } } diff --git a/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs index 5e336673153..d4f50344b23 100644 --- a/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs @@ -20,12 +20,6 @@ using System.Threading.Tasks; namespace Grpc.Core.Internal { - internal delegate int VerifyPeerCallbackInternal( - IntPtr targetHost, - IntPtr targetPem, - IntPtr userData, - bool isDestroy); - /// /// grpc_channel_credentials from grpc/grpc_security.h /// @@ -44,15 +38,15 @@ namespace Grpc.Core.Internal return creds; } - public static ChannelCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, KeyCertificatePair keyCertPair, VerifyPeerCallbackInternal verifyPeerCallback) + public static ChannelCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, KeyCertificatePair keyCertPair, IntPtr verifyPeerCallbackTag) { if (keyCertPair != null) { - return Native.grpcsharp_ssl_credentials_create(pemRootCerts, keyCertPair.CertificateChain, keyCertPair.PrivateKey, verifyPeerCallback); + return Native.grpcsharp_ssl_credentials_create(pemRootCerts, keyCertPair.CertificateChain, keyCertPair.PrivateKey, verifyPeerCallbackTag); } else { - return Native.grpcsharp_ssl_credentials_create(pemRootCerts, null, null, verifyPeerCallback); + return Native.grpcsharp_ssl_credentials_create(pemRootCerts, null, null, verifyPeerCallbackTag); } } diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs index e09b9813f79..a1387aff562 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs @@ -482,7 +482,7 @@ namespace Grpc.Core.Internal public delegate void grpcsharp_channel_args_set_integer_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, int value); public delegate void grpcsharp_channel_args_destroy_delegate(IntPtr args); public delegate void grpcsharp_override_default_ssl_roots_delegate(string pemRootCerts); - public delegate ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create_delegate(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, VerifyPeerCallbackInternal verifyPeerCallback); + public delegate ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create_delegate(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, IntPtr verifyPeerCallbackTag); public delegate ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create_delegate(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds); public delegate void grpcsharp_channel_credentials_release_delegate(IntPtr credentials); public delegate ChannelSafeHandle grpcsharp_insecure_channel_create_delegate(string target, ChannelArgsSafeHandle channelArgs); @@ -676,7 +676,7 @@ namespace Grpc.Core.Internal public static extern void grpcsharp_override_default_ssl_roots(string pemRootCerts); [DllImport(ImportName)] - public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, VerifyPeerCallbackInternal verifyPeerCallback); + public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, IntPtr verifyPeerCallbackTag); [DllImport(ImportName)] public static extern ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds); @@ -972,7 +972,7 @@ namespace Grpc.Core.Internal public static extern void grpcsharp_override_default_ssl_roots(string pemRootCerts); [DllImport(ImportName)] - public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, VerifyPeerCallbackInternal verifyPeerCallback); + public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, IntPtr verifyPeerCallbackTag); [DllImport(ImportName)] public static extern ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds); diff --git a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs index 818ac6d1adc..4c1189320fa 100644 --- a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs @@ -44,23 +44,18 @@ namespace Grpc.IntegrationTesting string rootCert; KeyCertificatePair keyCertPair; - string certChain; - List options; - bool isHostEqual; - bool isPemEqual; public void InitClientAndServer(bool clientAddKeyCertPair, - SslClientCertificateRequestType clientCertRequestType) + SslClientCertificateRequestType clientCertRequestType, + VerifyPeerCallback verifyPeerCallback = null) { rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath); - certChain = File.ReadAllText(TestCredentials.ServerCertChainPath); - certChain = certChain.Replace("\r", string.Empty); keyCertPair = new KeyCertificatePair( - certChain, + File.ReadAllText(TestCredentials.ServerCertChainPath), File.ReadAllText(TestCredentials.ServerPrivateKeyPath)); var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, clientCertRequestType); - var clientCredentials = clientAddKeyCertPair ? new SslCredentials(rootCert, keyCertPair, context => this.VerifyPeerCallback(context, true)) : new SslCredentials(rootCert); + var clientCredentials = new SslCredentials(rootCert, clientAddKeyCertPair ? keyCertPair : null, verifyPeerCallback); // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755 server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) @@ -70,7 +65,7 @@ namespace Grpc.IntegrationTesting }; server.Start(); - options = new List + var options = new List { new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) }; @@ -194,6 +189,52 @@ namespace Grpc.IntegrationTesting Assert.Throws(typeof(ArgumentNullException), () => new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestAndRequireAndVerify)); } + [Test] + public async Task VerifyPeerCallback_Accepted() + { + string targetNameFromCallback = null; + string peerPemFromCallback = null; + InitClientAndServer( + clientAddKeyCertPair: false, + clientCertRequestType: SslClientCertificateRequestType.DontRequest, + verifyPeerCallback: (ctx) => + { + targetNameFromCallback = ctx.TargetName; + peerPemFromCallback = ctx.PeerPem; + return true; + }); + await CheckAccepted(expectPeerAuthenticated: false); + Assert.AreEqual(TestCredentials.DefaultHostOverride, targetNameFromCallback); + var expectedServerPem = File.ReadAllText(TestCredentials.ServerCertChainPath).Replace("\r", ""); + Assert.AreEqual(expectedServerPem, peerPemFromCallback); + } + + [Test] + public void VerifyPeerCallback_CallbackThrows_Rejected() + { + InitClientAndServer( + clientAddKeyCertPair: false, + clientCertRequestType: SslClientCertificateRequestType.DontRequest, + verifyPeerCallback: (ctx) => + { + throw new Exception("VerifyPeerCallback has thrown on purpose."); + }); + CheckRejected(); + } + + [Test] + public void VerifyPeerCallback_Rejected() + { + InitClientAndServer( + clientAddKeyCertPair: false, + clientCertRequestType: SslClientCertificateRequestType.DontRequest, + verifyPeerCallback: (ctx) => + { + return false; + }); + CheckRejected(); + } + private async Task CheckAccepted(bool expectPeerAuthenticated) { var call = client.UnaryCallAsync(new SimpleRequest { ResponseSize = 10 }); @@ -216,37 +257,6 @@ namespace Grpc.IntegrationTesting Assert.AreEqual(12345, response.AggregatedPayloadSize); } - [Test] - public void VerifyPeerCallbackTest() - { - InitClientAndServer(true, SslClientCertificateRequestType.RequestAndRequireAndVerify); - - // Force GC collection to verify that the VerifyPeerCallback is not collected. If - // it gets collected, this test will hang. - GC.Collect(); - - client.UnaryCall(new SimpleRequest { ResponseSize = 10 }); - Assert.IsTrue(isHostEqual); - Assert.IsTrue(isPemEqual); - } - - [Test] - public void VerifyPeerCallbackFailTest() - { - InitClientAndServer(true, SslClientCertificateRequestType.RequestAndRequireAndVerify); - var clientCredentials = new SslCredentials(rootCert, keyCertPair, context => this.VerifyPeerCallback(context, false)); - var failingChannel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options); - var failingClient = new TestService.TestServiceClient(failingChannel); - Assert.Throws(() => failingClient.UnaryCall(new SimpleRequest { ResponseSize = 10 })); - } - - private bool VerifyPeerCallback(VerifyPeerContext context, bool returnValue) - { - isHostEqual = TestCredentials.DefaultHostOverride == context.TargetHost; - isPemEqual = certChain == context.TargetPem; - return returnValue; - } - private class SslCredentialsTestServiceImpl : TestService.TestServiceBase { public override Task UnaryCall(SimpleRequest request, ServerCallContext context) diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 5f80d3417a5..4323d40b6d3 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -901,6 +901,21 @@ grpcsharp_server_request_call(grpc_server* server, grpc_completion_queue* cq, &(ctx->request_metadata), cq, cq, ctx); } +/* Native callback dispatcher */ + +typedef int(GPR_CALLTYPE* grpcsharp_native_callback_dispatcher_func)( + void* tag, void* arg0, void* arg1, void* arg2, void* arg3, void* arg4, + void* arg5); + +static grpcsharp_native_callback_dispatcher_func native_callback_dispatcher = + NULL; + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_native_callback_dispatcher_init( + grpcsharp_native_callback_dispatcher_func func) { + GPR_ASSERT(func); + native_callback_dispatcher = func; +} + /* Security */ static char* default_pem_root_certs = NULL; @@ -927,23 +942,18 @@ grpcsharp_override_default_ssl_roots(const char* pem_root_certs) { grpc_set_ssl_roots_override_callback(override_ssl_roots_handler); } -typedef int(GPR_CALLTYPE* grpcsharp_verify_peer_func)(const char* target_host, - const char* target_pem, - void* userdata, - int32_t isDestroy); - static void grpcsharp_verify_peer_destroy_handler(void* userdata) { - grpcsharp_verify_peer_func callback = - (grpcsharp_verify_peer_func)(intptr_t)userdata; - callback(NULL, NULL, NULL, 1); + native_callback_dispatcher(userdata, NULL, + NULL, (void*)1, NULL, + NULL, NULL); } static int grpcsharp_verify_peer_handler(const char* target_host, const char* target_pem, void* userdata) { - grpcsharp_verify_peer_func callback = - (grpcsharp_verify_peer_func)(intptr_t)userdata; - return callback(target_host, target_pem, NULL, 0); + return native_callback_dispatcher(userdata, (void*)target_host, + (void*)target_pem, (void*)0, NULL, + NULL, NULL); } @@ -951,13 +961,13 @@ GPR_EXPORT grpc_channel_credentials* GPR_CALLTYPE grpcsharp_ssl_credentials_create(const char* pem_root_certs, const char* key_cert_pair_cert_chain, const char* key_cert_pair_private_key, - grpcsharp_verify_peer_func verify_peer_func) { + void* verify_peer_callback_tag) { grpc_ssl_pem_key_cert_pair key_cert_pair; verify_peer_options verify_options; verify_peer_options* p_verify_options = NULL; - if (verify_peer_func != NULL) { + if (verify_peer_callback_tag != NULL) { verify_options.verify_peer_callback_userdata = - (void*)(intptr_t)verify_peer_func; + verify_peer_callback_tag; verify_options.verify_peer_destruct = grpcsharp_verify_peer_destroy_handler; verify_options.verify_peer_callback = grpcsharp_verify_peer_handler; @@ -1043,21 +1053,6 @@ grpcsharp_composite_call_credentials_create(grpc_call_credentials* creds1, return grpc_composite_call_credentials_create(creds1, creds2, NULL); } -/* Native callback dispatcher */ - -typedef int(GPR_CALLTYPE* grpcsharp_native_callback_dispatcher_func)( - void* tag, void* arg0, void* arg1, void* arg2, void* arg3, void* arg4, - void* arg5); - -static grpcsharp_native_callback_dispatcher_func native_callback_dispatcher = - NULL; - -GPR_EXPORT void GPR_CALLTYPE grpcsharp_native_callback_dispatcher_init( - grpcsharp_native_callback_dispatcher_func func) { - GPR_ASSERT(func); - native_callback_dispatcher = func; -} - /* Metadata credentials plugin */ GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin( diff --git a/templates/src/csharp/Grpc.Core/Internal/native_methods.include b/templates/src/csharp/Grpc.Core/Internal/native_methods.include index 4cb71730529..e8ec4c87b06 100644 --- a/templates/src/csharp/Grpc.Core/Internal/native_methods.include +++ b/templates/src/csharp/Grpc.Core/Internal/native_methods.include @@ -44,7 +44,7 @@ native_method_signatures = [ 'void grpcsharp_channel_args_set_integer(ChannelArgsSafeHandle args, UIntPtr index, string key, int value)', 'void grpcsharp_channel_args_destroy(IntPtr args)', 'void grpcsharp_override_default_ssl_roots(string pemRootCerts)', - 'ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, VerifyPeerCallbackInternal verifyPeerCallback)', + 'ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, IntPtr verifyPeerCallbackTag)', 'ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds)', 'void grpcsharp_channel_credentials_release(IntPtr credentials)', 'ChannelSafeHandle grpcsharp_insecure_channel_create(string target, ChannelArgsSafeHandle channelArgs)', From 046cd3533b85425631bf3e3be43da8fb77e5d8b3 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Sun, 31 Mar 2019 19:34:50 -0700 Subject: [PATCH 4/8] add license --- src/csharp/Grpc.Core/VerifyPeerContext.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/csharp/Grpc.Core/VerifyPeerContext.cs b/src/csharp/Grpc.Core/VerifyPeerContext.cs index b48984b093c..52a5fabf7c8 100644 --- a/src/csharp/Grpc.Core/VerifyPeerContext.cs +++ b/src/csharp/Grpc.Core/VerifyPeerContext.cs @@ -1,3 +1,21 @@ +#region Copyright notice and license + +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + namespace Grpc.Core { /// From d297ede96a5cb453e32d40ec22015e67e214942a Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Sun, 31 Mar 2019 19:47:11 -0700 Subject: [PATCH 5/8] honor C core's naming of verify_peer_callback arguments --- src/csharp/Grpc.Core/ChannelCredentials.cs | 4 ++-- src/csharp/Grpc.Core/VerifyPeerContext.cs | 18 +++++++++--------- src/csharp/ext/grpc_csharp_ext.c | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/csharp/Grpc.Core/ChannelCredentials.cs b/src/csharp/Grpc.Core/ChannelCredentials.cs index 2ecd3875e84..fbea40d0a4b 100644 --- a/src/csharp/Grpc.Core/ChannelCredentials.cs +++ b/src/csharp/Grpc.Core/ChannelCredentials.cs @@ -226,7 +226,7 @@ namespace Grpc.Core return VerifyPeerCallbackHandler(arg0, arg1, arg2 != IntPtr.Zero); } - private int VerifyPeerCallbackHandler(IntPtr host, IntPtr pem, bool isDestroy) + private int VerifyPeerCallbackHandler(IntPtr targetName, IntPtr peerPem, bool isDestroy) { if (isDestroy) { @@ -236,7 +236,7 @@ namespace Grpc.Core try { - var context = new VerifyPeerContext(Marshal.PtrToStringAnsi(host), Marshal.PtrToStringAnsi(pem)); + var context = new VerifyPeerContext(Marshal.PtrToStringAnsi(targetName), Marshal.PtrToStringAnsi(peerPem)); return this.verifyPeerCallback(context) ? 0 : 1; } diff --git a/src/csharp/Grpc.Core/VerifyPeerContext.cs b/src/csharp/Grpc.Core/VerifyPeerContext.cs index 52a5fabf7c8..b1dc60f8e27 100644 --- a/src/csharp/Grpc.Core/VerifyPeerContext.cs +++ b/src/csharp/Grpc.Core/VerifyPeerContext.cs @@ -27,22 +27,22 @@ namespace Grpc.Core /// /// Initializes a new instance of the class. /// - /// string containing the host name of the peer. - /// string containing PEM encoded certificate of the peer. - internal VerifyPeerContext(string targetHost, string targetPem) + /// The target name of the peer. + /// The PEM encoded certificate of the peer. + internal VerifyPeerContext(string targetName, string peerPem) { - this.TargetHost = targetHost; - this.TargetPem = targetPem; + this.TargetName = targetName; + this.PeerPem = peerPem; } /// - /// String containing the host name of the peer. + /// The target name of the peer. /// - public string TargetHost { get; } + public string TargetName { get; } /// - /// string containing PEM encoded certificate of the peer. + /// The PEM encoded certificate of the peer. /// - public string TargetPem { get; } + public string PeerPem { get; } } } diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 4323d40b6d3..b3bb7c668ca 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -948,11 +948,11 @@ static void grpcsharp_verify_peer_destroy_handler(void* userdata) { NULL, NULL); } -static int grpcsharp_verify_peer_handler(const char* target_host, - const char* target_pem, +static int grpcsharp_verify_peer_handler(const char* target_name, + const char* peer_pem, void* userdata) { - return native_callback_dispatcher(userdata, (void*)target_host, - (void*)target_pem, (void*)0, NULL, + return native_callback_dispatcher(userdata, (void*)target_name, + (void*)peer_pem, (void*)0, NULL, NULL, NULL); } From c02aec32554a44b2d8180cd461ec1617ba0b113e Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 1 Apr 2019 00:13:54 -0400 Subject: [PATCH 6/8] clang format code --- src/csharp/ext/grpc_csharp_ext.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index b3bb7c668ca..061a5edc4ab 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -943,20 +943,16 @@ grpcsharp_override_default_ssl_roots(const char* pem_root_certs) { } static void grpcsharp_verify_peer_destroy_handler(void* userdata) { - native_callback_dispatcher(userdata, NULL, - NULL, (void*)1, NULL, - NULL, NULL); + native_callback_dispatcher(userdata, NULL, NULL, (void*)1, NULL, NULL, NULL); } static int grpcsharp_verify_peer_handler(const char* target_name, - const char* peer_pem, - void* userdata) { + const char* peer_pem, void* userdata) { return native_callback_dispatcher(userdata, (void*)target_name, - (void*)peer_pem, (void*)0, NULL, - NULL, NULL); + (void*)peer_pem, (void*)0, NULL, NULL, + NULL); } - GPR_EXPORT grpc_channel_credentials* GPR_CALLTYPE grpcsharp_ssl_credentials_create(const char* pem_root_certs, const char* key_cert_pair_cert_chain, @@ -966,10 +962,8 @@ grpcsharp_ssl_credentials_create(const char* pem_root_certs, verify_peer_options verify_options; verify_peer_options* p_verify_options = NULL; if (verify_peer_callback_tag != NULL) { - verify_options.verify_peer_callback_userdata = - verify_peer_callback_tag; - verify_options.verify_peer_destruct = - grpcsharp_verify_peer_destroy_handler; + verify_options.verify_peer_callback_userdata = verify_peer_callback_tag; + verify_options.verify_peer_destruct = grpcsharp_verify_peer_destroy_handler; verify_options.verify_peer_callback = grpcsharp_verify_peer_handler; p_verify_options = &verify_options; } From cc44a729db8bec1f991be19f49fa86b681b5f49b Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 1 Apr 2019 20:42:02 -0400 Subject: [PATCH 7/8] improve doc comment --- src/csharp/Grpc.Core/ChannelCredentials.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/csharp/Grpc.Core/ChannelCredentials.cs b/src/csharp/Grpc.Core/ChannelCredentials.cs index fbea40d0a4b..dcbe9f3599b 100644 --- a/src/csharp/Grpc.Core/ChannelCredentials.cs +++ b/src/csharp/Grpc.Core/ChannelCredentials.cs @@ -109,8 +109,12 @@ namespace Grpc.Core /// /// Callback invoked with the expected targetHost and the peer's certificate. /// If false is returned by this callback then it is treated as a - /// verification failure. Invocation of the callback is blocking, so any + /// verification failure and the attempted connection will fail. + /// Invocation of the callback is blocking, so any /// implementation should be light-weight. + /// Note that the callback can potentially be invoked multiple times, + /// concurrently from different threads (e.g. when multiple connections + /// are being created for the same credentials). /// /// The associated with the callback /// true if verification succeeded, false otherwise. From c9974ab6c9090bb25e41f80a2f0fb8a12beb9f14 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 1 Apr 2019 20:55:50 -0400 Subject: [PATCH 8/8] refactor grpcsharp_ssl_credentials_create --- src/csharp/ext/grpc_csharp_ext.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 061a5edc4ab..91d3957dbf0 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -960,25 +960,29 @@ grpcsharp_ssl_credentials_create(const char* pem_root_certs, void* verify_peer_callback_tag) { grpc_ssl_pem_key_cert_pair key_cert_pair; verify_peer_options verify_options; - verify_peer_options* p_verify_options = NULL; - if (verify_peer_callback_tag != NULL) { - verify_options.verify_peer_callback_userdata = verify_peer_callback_tag; - verify_options.verify_peer_destruct = grpcsharp_verify_peer_destroy_handler; - verify_options.verify_peer_callback = grpcsharp_verify_peer_handler; - p_verify_options = &verify_options; - } + grpc_ssl_pem_key_cert_pair* key_cert_pair_ptr = NULL; + verify_peer_options* verify_options_ptr = NULL; if (key_cert_pair_cert_chain || key_cert_pair_private_key) { + memset(&key_cert_pair, 0, sizeof(key_cert_pair)); key_cert_pair.cert_chain = key_cert_pair_cert_chain; key_cert_pair.private_key = key_cert_pair_private_key; - return grpc_ssl_credentials_create(pem_root_certs, &key_cert_pair, - p_verify_options, NULL); + key_cert_pair_ptr = &key_cert_pair; } else { GPR_ASSERT(!key_cert_pair_cert_chain); GPR_ASSERT(!key_cert_pair_private_key); - return grpc_ssl_credentials_create(pem_root_certs, NULL, p_verify_options, - NULL); } + + if (verify_peer_callback_tag != NULL) { + memset(&verify_options, 0, sizeof(verify_peer_options)); + verify_options.verify_peer_callback_userdata = verify_peer_callback_tag; + verify_options.verify_peer_destruct = grpcsharp_verify_peer_destroy_handler; + verify_options.verify_peer_callback = grpcsharp_verify_peer_handler; + verify_options_ptr = &verify_options; + } + + return grpc_ssl_credentials_create(pem_root_certs, key_cert_pair_ptr, + verify_options_ptr, NULL); } GPR_EXPORT void GPR_CALLTYPE