|  | @@ -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
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    /// <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. Invocation of the callback is blocking, so any
 | 
	
		
			
				|  |  | +    /// implementation should be light-weight.
 | 
	
		
			
				|  |  | +    /// </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;
 | 
	
		
			
				|  |  | +        readonly VerifyPeerCallbackInternal verifyPeerCallbackInternal;
 | 
	
		
			
				|  |  | +        readonly GCHandle gcHandle;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <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)
 | 
	
		
			
				|  |  | +        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.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  | -        public SslCredentials(string rootCertificates) : this(rootCertificates, null)
 | 
	
		
			
				|  |  | +        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>
 | 
	
		
			
				|  |  | -        public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair)
 | 
	
		
			
				|  |  | +        /// <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;
 | 
	
		
			
				|  |  | +            if (verifyPeerCallback != null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                this.verifyPeerCallback = verifyPeerCallback;
 | 
	
		
			
				|  |  | +                this.verifyPeerCallbackInternal = this.VerifyPeerCallbackHandler;
 | 
	
		
			
				|  |  | +                gcHandle = GCHandle.Alloc(verifyPeerCallbackInternal);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
	
		
			
				|  | @@ -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;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 |