1 #region Copyright notice and license
2 
3 // Copyright 2015 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.Threading.Tasks;
22 
23 using Grpc.Core.Internal;
24 using Grpc.Core.Utils;
25 
26 namespace Grpc.Core
27 {
28     /// <summary>
29     /// Client-side channel credentials. Used for creation of a secure channel.
30     /// </summary>
31     public abstract class ChannelCredentials
32     {
33         static readonly ChannelCredentials InsecureInstance = new InsecureCredentialsImpl();
34         readonly Lazy<ChannelCredentialsSafeHandle> cachedNativeCredentials;
35 
36         /// <summary>
37         /// Creates a new instance of channel credentials
38         /// </summary>
ChannelCredentials()39         public ChannelCredentials()
40         {
41             // Native credentials object need to be kept alive once initialized for subchannel sharing to work correctly
42             // with secure connections. See https://github.com/grpc/grpc/issues/15207.
43             // We rely on finalizer to clean up the native portion of ChannelCredentialsSafeHandle after the ChannelCredentials
44             // instance becomes unused.
45             this.cachedNativeCredentials = new Lazy<ChannelCredentialsSafeHandle>(() => CreateNativeCredentials());
46         }
47 
48         /// <summary>
49         /// Returns instance of credentials that provides no security and
50         /// will result in creating an unsecure channel with no encryption whatsoever.
51         /// </summary>
52         public static ChannelCredentials Insecure
53         {
54             get
55             {
56                 return InsecureInstance;
57             }
58         }
59 
60         /// <summary>
61         /// Creates a new instance of <c>ChannelCredentials</c> class by composing
62         /// given channel credentials with call credentials.
63         /// </summary>
64         /// <param name="channelCredentials">Channel credentials.</param>
65         /// <param name="callCredentials">Call credentials.</param>
66         /// <returns>The new composite <c>ChannelCredentials</c></returns>
Create(ChannelCredentials channelCredentials, CallCredentials callCredentials)67         public static ChannelCredentials Create(ChannelCredentials channelCredentials, CallCredentials callCredentials)
68         {
69             return new CompositeChannelCredentials(channelCredentials, callCredentials);
70         }
71 
72         /// <summary>
73         /// Gets native object for the credentials, creating one if it already doesn't exist. May return null if insecure channel
74         /// should be created. Caller must not call <c>Dispose()</c> on the returned native credentials as their lifetime
75         /// is managed by this class (and instances of native credentials are cached).
76         /// </summary>
77         /// <returns>The native credentials.</returns>
GetNativeCredentials()78         internal ChannelCredentialsSafeHandle GetNativeCredentials()
79         {
80             return cachedNativeCredentials.Value;
81         }
82 
83         /// <summary>
84         /// Creates a new native object for the credentials. May return null if insecure channel
85         /// should be created. For internal use only, use <see cref="GetNativeCredentials"/> instead.
86         /// </summary>
87         /// <returns>The native credentials.</returns>
CreateNativeCredentials()88         internal abstract ChannelCredentialsSafeHandle CreateNativeCredentials();
89 
90         /// <summary>
91         /// Returns <c>true</c> if this credential type allows being composed by <c>CompositeCredentials</c>.
92         /// </summary>
93         internal virtual bool IsComposable
94         {
95             get { return false; }
96         }
97 
98         private sealed class InsecureCredentialsImpl : ChannelCredentials
99         {
CreateNativeCredentials()100             internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
101             {
102                 return null;
103             }
104         }
105     }
106 
107     /// <summary>
108     /// Client-side SSL credentials.
109     /// </summary>
110     public sealed class SslCredentials : ChannelCredentials
111     {
112         readonly string rootCertificates;
113         readonly KeyCertificatePair keyCertificatePair;
114 
115         /// <summary>
116         /// Creates client-side SSL credentials loaded from
117         /// disk file pointed to by the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable.
118         /// If that fails, gets the roots certificates from a well known place on disk.
119         /// </summary>
SslCredentials()120         public SslCredentials() : this(null, null)
121         {
122         }
123 
124         /// <summary>
125         /// Creates client-side SSL credentials from
126         /// a string containing PEM encoded root certificates.
127         /// </summary>
SslCredentials(string rootCertificates)128         public SslCredentials(string rootCertificates) : this(rootCertificates, null)
129         {
130         }
131 
132         /// <summary>
133         /// Creates client-side SSL credentials.
134         /// </summary>
135         /// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>
136         /// <param name="keyCertificatePair">a key certificate pair.</param>
SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair)137         public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair)
138         {
139             this.rootCertificates = rootCertificates;
140             this.keyCertificatePair = keyCertificatePair;
141         }
142 
143         /// <summary>
144         /// PEM encoding of the server root certificates.
145         /// </summary>
146         public string RootCertificates
147         {
148             get
149             {
150                 return this.rootCertificates;
151             }
152         }
153 
154         /// <summary>
155         /// Client side key and certificate pair.
156         /// If null, client will not use key and certificate pair.
157         /// </summary>
158         public KeyCertificatePair KeyCertificatePair
159         {
160             get
161             {
162                 return this.keyCertificatePair;
163             }
164         }
165 
166         // Composing composite makes no sense.
167         internal override bool IsComposable
168         {
169             get { return true; }
170         }
171 
CreateNativeCredentials()172         internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
173         {
174             return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair);
175         }
176     }
177 
178     /// <summary>
179     /// Credentials that allow composing one <see cref="ChannelCredentials"/> object and
180     /// one or more <see cref="CallCredentials"/> objects into a single <see cref="ChannelCredentials"/>.
181     /// </summary>
182     internal sealed class CompositeChannelCredentials : ChannelCredentials
183     {
184         readonly ChannelCredentials channelCredentials;
185         readonly CallCredentials callCredentials;
186 
187         /// <summary>
188         /// Initializes a new instance of <c>CompositeChannelCredentials</c> class.
189         /// The resulting credentials object will be composite of all the credentials specified as parameters.
190         /// </summary>
191         /// <param name="channelCredentials">channelCredentials to compose</param>
192         /// <param name="callCredentials">channelCredentials to compose</param>
CompositeChannelCredentials(ChannelCredentials channelCredentials, CallCredentials callCredentials)193         public CompositeChannelCredentials(ChannelCredentials channelCredentials, CallCredentials callCredentials)
194         {
195             this.channelCredentials = GrpcPreconditions.CheckNotNull(channelCredentials);
196             this.callCredentials = GrpcPreconditions.CheckNotNull(callCredentials);
197             GrpcPreconditions.CheckArgument(channelCredentials.IsComposable, "Supplied channel credentials do not allow composition.");
198         }
199 
CreateNativeCredentials()200         internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
201         {
202             using (var callCreds = callCredentials.ToNativeCredentials())
203             {
204                 var nativeComposite = ChannelCredentialsSafeHandle.CreateComposite(channelCredentials.GetNativeCredentials(), callCreds);
205                 if (nativeComposite.IsInvalid)
206                 {
207                     throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
208                 }
209                 return nativeComposite;
210             }
211         }
212     }
213 }
214