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