DefaultChannelCredentialsConfigurator.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #region Copyright notice and license
  2. // Copyright 2019 The gRPC Authors
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #endregion
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Runtime.InteropServices;
  19. using System.Runtime.CompilerServices;
  20. using Grpc.Core.Utils;
  21. using Grpc.Core.Logging;
  22. namespace Grpc.Core.Internal
  23. {
  24. /// <summary>
  25. /// Creates native call credential objects from instances of <c>ChannelCredentials</c>.
  26. /// </summary>
  27. internal class DefaultChannelCredentialsConfigurator : ChannelCredentialsConfiguratorBase
  28. {
  29. static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<DefaultCallCredentialsConfigurator>();
  30. // Native credentials object need to be kept alive once initialized for subchannel sharing to work correctly
  31. // with secure connections. See https://github.com/grpc/grpc/issues/15207.
  32. // We rely on finalizer to clean up the native portion of ChannelCredentialsSafeHandle after the ChannelCredentials
  33. // instance becomes unused.
  34. static readonly ConditionalWeakTable<ChannelCredentials, Lazy<ChannelCredentialsSafeHandle>> CachedNativeCredentials = new ConditionalWeakTable<ChannelCredentials, Lazy<ChannelCredentialsSafeHandle>>();
  35. static readonly object StaticLock = new object();
  36. bool configured;
  37. ChannelCredentialsSafeHandle nativeCredentials;
  38. public ChannelCredentialsSafeHandle NativeCredentials => nativeCredentials;
  39. public override void SetInsecureCredentials(object state)
  40. {
  41. GrpcPreconditions.CheckState(!configured);
  42. // null corresponds to insecure credentials.
  43. configured = true;
  44. nativeCredentials = null;
  45. }
  46. public override void SetSslCredentials(object state, string rootCertificates, KeyCertificatePair keyCertificatePair, VerifyPeerCallback verifyPeerCallback)
  47. {
  48. GrpcPreconditions.CheckState(!configured);
  49. configured = true;
  50. nativeCredentials = GetOrCreateNativeCredentials((ChannelCredentials) state,
  51. () => CreateNativeSslCredentials(rootCertificates, keyCertificatePair, verifyPeerCallback));
  52. }
  53. public override void SetCompositeCredentials(object state, ChannelCredentials channelCredentials, CallCredentials callCredentials)
  54. {
  55. GrpcPreconditions.CheckState(!configured);
  56. configured = true;
  57. nativeCredentials = GetOrCreateNativeCredentials((ChannelCredentials) state,
  58. () => CreateNativeCompositeCredentials(channelCredentials, callCredentials));
  59. }
  60. private ChannelCredentialsSafeHandle CreateNativeSslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair, VerifyPeerCallback verifyPeerCallback)
  61. {
  62. IntPtr verifyPeerCallbackTag = IntPtr.Zero;
  63. if (verifyPeerCallback != null)
  64. {
  65. verifyPeerCallbackTag = new VerifyPeerCallbackRegistration(verifyPeerCallback).CallbackRegistration.Tag;
  66. }
  67. return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair, verifyPeerCallbackTag);
  68. }
  69. private ChannelCredentialsSafeHandle CreateNativeCompositeCredentials(ChannelCredentials channelCredentials, CallCredentials callCredentials)
  70. {
  71. using (var callCreds = callCredentials.ToNativeCredentials())
  72. {
  73. var nativeComposite = ChannelCredentialsSafeHandle.CreateComposite(channelCredentials.ToNativeCredentials(), callCreds);
  74. if (nativeComposite.IsInvalid)
  75. {
  76. throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
  77. }
  78. return nativeComposite;
  79. }
  80. }
  81. private ChannelCredentialsSafeHandle GetOrCreateNativeCredentials(ChannelCredentials key, Func<ChannelCredentialsSafeHandle> nativeCredentialsFactory)
  82. {
  83. Lazy<ChannelCredentialsSafeHandle> lazyValue;
  84. lock (StaticLock) {
  85. if (!CachedNativeCredentials.TryGetValue(key, out lazyValue))
  86. {
  87. lazyValue = new Lazy<ChannelCredentialsSafeHandle>(nativeCredentialsFactory);
  88. CachedNativeCredentials.Add(key, lazyValue);
  89. }
  90. }
  91. return lazyValue.Value;
  92. }
  93. private class VerifyPeerCallbackRegistration
  94. {
  95. readonly VerifyPeerCallback verifyPeerCallback;
  96. readonly NativeCallbackRegistration callbackRegistration;
  97. public VerifyPeerCallbackRegistration(VerifyPeerCallback verifyPeerCallback)
  98. {
  99. this.verifyPeerCallback = verifyPeerCallback;
  100. this.callbackRegistration = NativeCallbackDispatcher.RegisterCallback(HandleUniversalCallback);
  101. }
  102. public NativeCallbackRegistration CallbackRegistration => callbackRegistration;
  103. private int HandleUniversalCallback(IntPtr arg0, IntPtr arg1, IntPtr arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5)
  104. {
  105. return VerifyPeerCallbackHandler(arg0, arg1, arg2 != IntPtr.Zero);
  106. }
  107. private int VerifyPeerCallbackHandler(IntPtr targetName, IntPtr peerPem, bool isDestroy)
  108. {
  109. if (isDestroy)
  110. {
  111. this.callbackRegistration.Dispose();
  112. return 0;
  113. }
  114. try
  115. {
  116. var context = new VerifyPeerContext(Marshal.PtrToStringAnsi(targetName), Marshal.PtrToStringAnsi(peerPem));
  117. return this.verifyPeerCallback(context) ? 0 : 1;
  118. }
  119. catch (Exception e)
  120. {
  121. // eat the exception, we must not throw when inside callback from native code.
  122. Logger.Error(e, "Exception occurred while invoking verify peer callback handler.");
  123. // Return validation failure in case of exception.
  124. return 1;
  125. }
  126. }
  127. }
  128. }
  129. internal static class ChannelCredentialsExtensions
  130. {
  131. /// <summary>
  132. /// Creates native object for the credentials.
  133. /// </summary>
  134. /// <returns>The native credentials.</returns>
  135. public static ChannelCredentialsSafeHandle ToNativeCredentials(this ChannelCredentials credentials)
  136. {
  137. var configurator = new DefaultChannelCredentialsConfigurator();
  138. credentials.InternalPopulateConfiguration(configurator, credentials);
  139. return configurator.NativeCredentials;
  140. }
  141. }
  142. }