| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 | #region Copyright notice and license// Copyright 2015 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.#endregionusing 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{    /// <summary>    /// Client-side channel credentials. Used for creation of a secure channel.    /// </summary>    public abstract class ChannelCredentials    {        static readonly ChannelCredentials InsecureInstance = new InsecureCredentialsImpl();        readonly Lazy<ChannelCredentialsSafeHandle> cachedNativeCredentials;        /// <summary>        /// Creates a new instance of channel credentials        /// </summary>        public ChannelCredentials()        {            // Native credentials object need to be kept alive once initialized for subchannel sharing to work correctly            // with secure connections. See https://github.com/grpc/grpc/issues/15207.            // We rely on finalizer to clean up the native portion of ChannelCredentialsSafeHandle after the ChannelCredentials            // instance becomes unused.            this.cachedNativeCredentials = new Lazy<ChannelCredentialsSafeHandle>(() => CreateNativeCredentials());        }        /// <summary>        /// Returns instance of credentials that provides no security and         /// will result in creating an unsecure channel with no encryption whatsoever.        /// </summary>        public static ChannelCredentials Insecure        {            get            {                return InsecureInstance;            }        }        /// <summary>        /// Creates a new instance of <c>ChannelCredentials</c> class by composing        /// given channel credentials with call credentials.        /// </summary>        /// <param name="channelCredentials">Channel credentials.</param>        /// <param name="callCredentials">Call credentials.</param>        /// <returns>The new composite <c>ChannelCredentials</c></returns>        public static ChannelCredentials Create(ChannelCredentials channelCredentials, CallCredentials callCredentials)        {            return new CompositeChannelCredentials(channelCredentials, callCredentials);        }        /// <summary>        /// Gets native object for the credentials, creating one if it already doesn't exist. May return null if insecure channel        /// should be created. Caller must not call <c>Dispose()</c> on the returned native credentials as their lifetime        /// is managed by this class (and instances of native credentials are cached).        /// </summary>        /// <returns>The native credentials.</returns>        internal ChannelCredentialsSafeHandle GetNativeCredentials()        {            return cachedNativeCredentials.Value;        }        /// <summary>        /// Creates a new native object for the credentials. May return null if insecure channel        /// should be created. For internal use only, use <see cref="GetNativeCredentials"/> instead.        /// </summary>        /// <returns>The native credentials.</returns>        internal abstract ChannelCredentialsSafeHandle CreateNativeCredentials();        /// <summary>        /// Returns <c>true</c> if this credential type allows being composed by <c>CompositeCredentials</c>.        /// </summary>        internal virtual bool IsComposable        {            get { return false; }        }        private sealed class InsecureCredentialsImpl : ChannelCredentials        {            internal override ChannelCredentialsSafeHandle CreateNativeCredentials()            {                return null;            }        }    }    /// <summary>    /// 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 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).    /// </summary>    /// <param name="context">The <see cref="T:Grpc.Core.VerifyPeerContext"/> associated with the callback</param>    /// <returns>true if verification succeeded, false otherwise.</returns>    /// Note: experimental API that can change or be removed without any prior notice.    public delegate bool VerifyPeerCallback(VerifyPeerContext context);    /// <summary>    /// Client-side SSL credentials.    /// </summary>    public sealed class SslCredentials : ChannelCredentials    {        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<SslCredentials>();        readonly string rootCertificates;        readonly KeyCertificatePair keyCertificatePair;        readonly VerifyPeerCallback verifyPeerCallback;        /// <summary>        /// 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.        /// </summary>        public SslCredentials() : this(null, null, null)        {        }        /// <summary>        /// Creates client-side SSL credentials from        /// a string containing PEM encoded root certificates.        /// </summary>        public SslCredentials(string rootCertificates) : this(rootCertificates, null, null)        {        }        /// <summary>        /// Creates client-side SSL credentials.        /// </summary>        /// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>        /// <param name="keyCertificatePair">a key certificate pair.</param>        public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair) :            this(rootCertificates, keyCertificatePair, null)        {        }        /// <summary>        /// Creates client-side SSL credentials.        /// </summary>        /// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>        /// <param name="keyCertificatePair">a key certificate pair.</param>        /// <param name="verifyPeerCallback">a callback to verify peer's target name and certificate.</param>        /// 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;            this.verifyPeerCallback = verifyPeerCallback;        }        /// <summary>        /// PEM encoding of the server root certificates.        /// </summary>        public string RootCertificates        {            get            {                return this.rootCertificates;            }        }        /// <summary>        /// Client side key and certificate pair.        /// If null, client will not use key and certificate pair.        /// </summary>        public KeyCertificatePair KeyCertificatePair        {            get            {                return this.keyCertificatePair;            }        }        // Composing composite makes no sense.        internal override bool IsComposable        {            get { return true; }        }        internal override ChannelCredentialsSafeHandle CreateNativeCredentials()        {            IntPtr verifyPeerCallbackTag = IntPtr.Zero;            if (verifyPeerCallback != null)            {                verifyPeerCallbackTag = new VerifyPeerCallbackRegistration(verifyPeerCallback).CallbackRegistration.Tag;            }            return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair, verifyPeerCallbackTag);        }        private class VerifyPeerCallbackRegistration        {            readonly VerifyPeerCallback verifyPeerCallback;            readonly NativeCallbackRegistration callbackRegistration;            public VerifyPeerCallbackRegistration(VerifyPeerCallback verifyPeerCallback)            {                this.verifyPeerCallback = verifyPeerCallback;                this.callbackRegistration = NativeCallbackDispatcher.RegisterCallback(HandleUniversalCallback);            }            public NativeCallbackRegistration CallbackRegistration => callbackRegistration;            private int HandleUniversalCallback(IntPtr arg0, IntPtr arg1, IntPtr arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5)            {                return VerifyPeerCallbackHandler(arg0, arg1, arg2 != IntPtr.Zero);            }            private int VerifyPeerCallbackHandler(IntPtr targetName, IntPtr peerPem, bool isDestroy)            {                if (isDestroy)                {                    this.callbackRegistration.Dispose();                    return 0;                }                try                {                    var context = new VerifyPeerContext(Marshal.PtrToStringAnsi(targetName), Marshal.PtrToStringAnsi(peerPem));                    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;                }            }        }    }    /// <summary>    /// Credentials that allow composing one <see cref="ChannelCredentials"/> object and     /// one or more <see cref="CallCredentials"/> objects into a single <see cref="ChannelCredentials"/>.    /// </summary>    internal sealed class CompositeChannelCredentials : ChannelCredentials    {        readonly ChannelCredentials channelCredentials;        readonly CallCredentials callCredentials;        /// <summary>        /// Initializes a new instance of <c>CompositeChannelCredentials</c> class.        /// The resulting credentials object will be composite of all the credentials specified as parameters.        /// </summary>        /// <param name="channelCredentials">channelCredentials to compose</param>        /// <param name="callCredentials">channelCredentials to compose</param>        public CompositeChannelCredentials(ChannelCredentials channelCredentials, CallCredentials callCredentials)        {            this.channelCredentials = GrpcPreconditions.CheckNotNull(channelCredentials);            this.callCredentials = GrpcPreconditions.CheckNotNull(callCredentials);            GrpcPreconditions.CheckArgument(channelCredentials.IsComposable, "Supplied channel credentials do not allow composition.");        }        internal override ChannelCredentialsSafeHandle CreateNativeCredentials()        {            using (var callCreds = callCredentials.ToNativeCredentials())            {                var nativeComposite = ChannelCredentialsSafeHandle.CreateComposite(channelCredentials.GetNativeCredentials(), callCreds);                if (nativeComposite.IsInvalid)                {                    throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");                }                return nativeComposite;            }        }    }}
 |