private async Task InnerOnConnectionAsync(ConnectionContext context)
        {
            bool certificateRequired;
            var  feature = new Core.Internal.TlsConnectionFeature();

            context.Features.Set <ITlsConnectionFeature>(feature);
            context.Features.Set <ITlsHandshakeFeature>(feature);

            var memoryPool = context.Features.Get <IMemoryPoolFeature>()?.MemoryPool;

            var inputPipeOptions = new StreamPipeReaderOptions
                                   (
                pool: memoryPool,
                bufferSize: memoryPool.GetMinimumSegmentSize(),
                minimumReadSize: memoryPool.GetMinimumAllocSize(),
                leaveOpen: true
                                   );

            var outputPipeOptions = new StreamPipeWriterOptions
                                    (
                pool: memoryPool,
                leaveOpen: true
                                    );

            SslDuplexPipe sslDuplexPipe = null;

            if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
            {
                sslDuplexPipe       = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions);
                certificateRequired = false;
            }
            else
            {
                sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions, s => new SslStream(s,
                                                                                                                             leaveInnerStreamOpen: false,
                                                                                                                             userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
                {
                    if (certificate == null)
                    {
                        return(_options.ClientCertificateMode != ClientCertificateMode.RequireCertificate);
                    }

                    if (_options.ClientCertificateValidation == null)
                    {
                        if (sslPolicyErrors != SslPolicyErrors.None)
                        {
                            return(false);
                        }
                    }

                    var certificate2 = ConvertToX509Certificate2(certificate);
                    if (certificate2 == null)
                    {
                        return(false);
                    }

                    if (_options.ClientCertificateValidation != null)
                    {
                        if (!_options.ClientCertificateValidation(certificate2, chain, sslPolicyErrors))
                        {
                            return(false);
                        }
                    }

                    return(true);
                }));

                certificateRequired = true;
            }

            var sslStream = sslDuplexPipe.Stream;

            using (var cancellationTokeSource = new CancellationTokenSource(_options.HandshakeTimeout))
                using (cancellationTokeSource.Token.UnsafeRegister(state => ((ConnectionContext)state).Abort(), context))
                {
                    try
                    {
                        // Adapt to the SslStream signature
                        ServerCertificateSelectionCallback selector = null;
                        if (_serverCertificateSelector != null)
                        {
                            selector = (sender, name) =>
                            {
                                context.Features.Set(sslStream);
                                var cert = _serverCertificateSelector(context, name);
                                if (cert != null)
                                {
                                    EnsureCertificateIsAllowedForServerAuth(cert);
                                }
                                return(cert);
                            };
                        }

                        var sslOptions = new SslServerAuthenticationOptions
                        {
                            ServerCertificate = _serverCertificate,
                            ServerCertificateSelectionCallback = selector,
                            ClientCertificateRequired          = certificateRequired,
                            EnabledSslProtocols            = _options.SslProtocols,
                            CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                            ApplicationProtocols           = new List <SslApplicationProtocol>()
                        };

                        // This is order sensitive
                        if ((_options.HttpProtocols & HttpProtocols.Http2) != 0)
                        {
                            sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2);
                            // https://tools.ietf.org/html/rfc7540#section-9.2.1
                            sslOptions.AllowRenegotiation = false;
                        }

                        if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
                        {
                            sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11);
                        }

                        _options.OnAuthenticate?.Invoke(context, sslOptions);

                        await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
                    }
                    catch (OperationCanceledException)
                    {
                        _logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut);
                        await sslStream.DisposeAsync();

                        return;
                    }
                    catch (Exception ex) when(ex is IOException || ex is AuthenticationException)
                    {
                        _logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
                        await sslStream.DisposeAsync();

                        return;
                    }
                }

            feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
            context.Features.Set <ITlsApplicationProtocolFeature>(feature);
            feature.ClientCertificate    = ConvertToX509Certificate2(sslStream.RemoteCertificate);
            feature.CipherAlgorithm      = sslStream.CipherAlgorithm;
            feature.CipherStrength       = sslStream.CipherStrength;
            feature.HashAlgorithm        = sslStream.HashAlgorithm;
            feature.HashStrength         = sslStream.HashStrength;
            feature.KeyExchangeAlgorithm = sslStream.KeyExchangeAlgorithm;
            feature.KeyExchangeStrength  = sslStream.KeyExchangeStrength;
            feature.Protocol             = sslStream.SslProtocol;

            var originalTransport = context.Transport;

            try
            {
                context.Transport = sslDuplexPipe;

                // Disposing the stream will dispose the sslDuplexPipe
                await using (sslStream)
                    await using (sslDuplexPipe)
                    {
                        await _next(context);

                        // Dispose the inner stream (SslDuplexPipe) before disposing the SslStream
                        // as the duplex pipe can hit an ODE as it still may be writing.
                    }
            }
            finally
            {
                // Restore the original so that it gets closed appropriately
                context.Transport = originalTransport;
            }
        }
        private Task DoOptionsBasedHandshakeAsync(ConnectionContext context, SslStream sslStream, Core.Internal.TlsConnectionFeature feature, CancellationToken cancellationToken)
        {
            // Adapt to the SslStream signature
            ServerCertificateSelectionCallback selector = null;

            if (_serverCertificateSelector != null)
            {
                selector = (sender, name) =>
                {
                    feature.HostName = name;
                    context.Features.Set(sslStream);
                    var cert = _serverCertificateSelector(context, name);
                    if (cert != null)
                    {
                        EnsureCertificateIsAllowedForServerAuth(cert);
                    }
                    return(cert);
                };
            }

            var sslOptions = new SslServerAuthenticationOptions
            {
                ServerCertificate                  = _serverCertificate,
                ServerCertificateContext           = _serverCertificateContext,
                ServerCertificateSelectionCallback = selector,
                ClientCertificateRequired          = _options.ClientCertificateMode != ClientCertificateMode.NoCertificate,
                EnabledSslProtocols                = _options.SslProtocols,
                CertificateRevocationCheckMode     = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
            };

            ConfigureAlpn(sslOptions, _options.HttpProtocols);

            _options.OnAuthenticate?.Invoke(context, sslOptions);

            KestrelEventSource.Log.TlsHandshakeStart(context, sslOptions);

            return(sslStream.AuthenticateAsServerAsync(sslOptions, cancellationToken));
        }
Ejemplo n.º 3
0
        private async Task <IAdaptedConnection> InnerOnConnectionAsync(ConnectionAdapterContext context)
        {
            SslStream sslStream;
            bool      certificateRequired;
            var       feature = new Core.Internal.TlsConnectionFeature();

            context.Features.Set <ITlsConnectionFeature>(feature);
            context.Features.Set <ITlsHandshakeFeature>(feature);

            if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
            {
                sslStream           = new SslStream(context.ConnectionStream);
                certificateRequired = false;
            }
            else
            {
                sslStream = new SslStream(context.ConnectionStream,
                                          leaveInnerStreamOpen: false,
                                          userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
                {
                    if (certificate == null)
                    {
                        return(_options.ClientCertificateMode != ClientCertificateMode.RequireCertificate);
                    }

                    if (_options.ClientCertificateValidation == null)
                    {
                        if (sslPolicyErrors != SslPolicyErrors.None)
                        {
                            return(false);
                        }
                    }

                    var certificate2 = ConvertToX509Certificate2(certificate);
                    if (certificate2 == null)
                    {
                        return(false);
                    }

                    if (_options.ClientCertificateValidation != null)
                    {
                        if (!_options.ClientCertificateValidation(certificate2, chain, sslPolicyErrors))
                        {
                            return(false);
                        }
                    }

                    return(true);
                });

                certificateRequired = true;
            }

            var timeoutFeature = context.Features.Get <IConnectionTimeoutFeature>();

            timeoutFeature.SetTimeout(_options.HandshakeTimeout);

            _options.OnHandshakeStarted?.Invoke();

            try
            {
                // Adapt to the SslStream signature
                ServerCertificateSelectionCallback selector = null;
                if (_serverCertificateSelector != null)
                {
                    selector = (sender, name) =>
                    {
                        context.Features.Set(sslStream);
                        var cert = _serverCertificateSelector(context.ConnectionContext, name);
                        if (cert != null)
                        {
                            EnsureCertificateIsAllowedForServerAuth(cert);
                        }
                        return(cert);
                    };
                }

                var sslOptions = new SslServerAuthenticationOptions
                {
                    ServerCertificate = _serverCertificate,
                    ServerCertificateSelectionCallback = selector,
                    ClientCertificateRequired          = certificateRequired,
                    EnabledSslProtocols            = _options.SslProtocols,
                    CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                    ApplicationProtocols           = new List <SslApplicationProtocol>()
                };

                // This is order sensitive
                if ((_options.HttpProtocols & HttpProtocols.Http2) != 0)
                {
                    sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2);
                    // https://tools.ietf.org/html/rfc7540#section-9.2.1
                    sslOptions.AllowRenegotiation = false;
                }

                if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
                {
                    sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11);
                }

                await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
            }
            catch (OperationCanceledException)
            {
                _logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut);
                sslStream.Dispose();
                return(_closedAdaptedConnection);
            }
            catch (Exception ex) when(ex is IOException || ex is AuthenticationException)
            {
                _logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
                sslStream.Dispose();
                return(_closedAdaptedConnection);
            }
            finally
            {
                timeoutFeature.CancelTimeout();
            }

            feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
            context.Features.Set <ITlsApplicationProtocolFeature>(feature);
            feature.ClientCertificate    = ConvertToX509Certificate2(sslStream.RemoteCertificate);
            feature.CipherAlgorithm      = sslStream.CipherAlgorithm;
            feature.CipherStrength       = sslStream.CipherStrength;
            feature.HashAlgorithm        = sslStream.HashAlgorithm;
            feature.HashStrength         = sslStream.HashStrength;
            feature.KeyExchangeAlgorithm = sslStream.KeyExchangeAlgorithm;
            feature.KeyExchangeStrength  = sslStream.KeyExchangeStrength;
            feature.Protocol             = sslStream.SslProtocol;

            return(new HttpsAdaptedConnection(sslStream));
        }
        public async Task OnConnectionAsync(ConnectionContext context)
        {
            await Task.Yield();

            if (context.Features.Get <ITlsConnectionFeature>() != null)
            {
                await _next(context);

                return;
            }

            var feature = new Core.Internal.TlsConnectionFeature();

            context.Features.Set <ITlsConnectionFeature>(feature);
            context.Features.Set <ITlsHandshakeFeature>(feature);

            var sslDuplexPipe = CreateSslDuplexPipe(context.Transport, context.Features.Get <IMemoryPoolFeature>()?.MemoryPool);
            var sslStream     = sslDuplexPipe.Stream;

            try
            {
                using var cancellationTokenSource = new CancellationTokenSource(_handshakeTimeout);
                if (_httpsOptionsCallback is null)
                {
                    await DoOptionsBasedHandshakeAsync(context, sslStream, feature, cancellationTokenSource.Token);
                }
                else
                {
                    var state = (this, context, feature);
                    await sslStream.AuthenticateAsServerAsync(ServerOptionsCallback, state, cancellationTokenSource.Token);
                }
            }
            catch (OperationCanceledException)
            {
                KestrelEventSource.Log.TlsHandshakeFailed(context.ConnectionId);
                KestrelEventSource.Log.TlsHandshakeStop(context, null);

                _logger.AuthenticationTimedOut();
                await sslStream.DisposeAsync();

                return;
            }
            catch (IOException ex)
            {
                KestrelEventSource.Log.TlsHandshakeFailed(context.ConnectionId);
                KestrelEventSource.Log.TlsHandshakeStop(context, null);

                _logger.AuthenticationFailed(ex);
                await sslStream.DisposeAsync();

                return;
            }
            catch (AuthenticationException ex)
            {
                KestrelEventSource.Log.TlsHandshakeFailed(context.ConnectionId);
                KestrelEventSource.Log.TlsHandshakeStop(context, null);

                _logger.AuthenticationFailed(ex);

                await sslStream.DisposeAsync();

                return;
            }

            feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
            context.Features.Set <ITlsApplicationProtocolFeature>(feature);

            feature.ClientCertificate    = ConvertToX509Certificate2(sslStream.RemoteCertificate);
            feature.CipherAlgorithm      = sslStream.CipherAlgorithm;
            feature.CipherStrength       = sslStream.CipherStrength;
            feature.HashAlgorithm        = sslStream.HashAlgorithm;
            feature.HashStrength         = sslStream.HashStrength;
            feature.KeyExchangeAlgorithm = sslStream.KeyExchangeAlgorithm;
            feature.KeyExchangeStrength  = sslStream.KeyExchangeStrength;
            feature.Protocol             = sslStream.SslProtocol;

            KestrelEventSource.Log.TlsHandshakeStop(context, feature);

            _logger.HttpsConnectionEstablished(context.ConnectionId, sslStream.SslProtocol);

            var originalTransport = context.Transport;

            try
            {
                context.Transport = sslDuplexPipe;

                // Disposing the stream will dispose the sslDuplexPipe
                await using (sslStream)
                    await using (sslDuplexPipe)
                    {
                        await _next(context);

                        // Dispose the inner stream (SslDuplexPipe) before disposing the SslStream
                        // as the duplex pipe can hit an ODE as it still may be writing.
                    }
            }
            finally
            {
                // Restore the original so that it gets closed appropriately
                context.Transport = originalTransport;
            }
        }
Ejemplo n.º 5
0
        private async Task InnerOnConnectionAsync(ConnectionContext context)
        {
            SslStream sslStream;
            bool      certificateRequired;
            var       feature = new Core.Internal.TlsConnectionFeature();

            context.Features.Set <ITlsConnectionFeature>(feature);
            context.Features.Set <ITlsHandshakeFeature>(feature);

            // TODO: Handle the cases where this can be null
            var memoryPoolFeature = context.Features.Get <IMemoryPoolFeature>();

            var inputPipeOptions = new PipeOptions
                                   (
                pool: memoryPoolFeature.MemoryPool,
                readerScheduler: _options.Scheduler,
                writerScheduler: PipeScheduler.Inline,
                pauseWriterThreshold: _options.MaxInputBufferSize ?? 0,
                resumeWriterThreshold: _options.MaxInputBufferSize / 2 ?? 0,
                useSynchronizationContext: false,
                minimumSegmentSize: memoryPoolFeature.MemoryPool.GetMinimumSegmentSize()
                                   );

            var outputPipeOptions = new PipeOptions
                                    (
                pool: memoryPoolFeature.MemoryPool,
                readerScheduler: PipeScheduler.Inline,
                writerScheduler: PipeScheduler.Inline,
                pauseWriterThreshold: _options.MaxOutputBufferSize ?? 0,
                resumeWriterThreshold: _options.MaxOutputBufferSize / 2 ?? 0,
                useSynchronizationContext: false,
                minimumSegmentSize: memoryPoolFeature.MemoryPool.GetMinimumSegmentSize()
                                    );

            // TODO: eventually make SslDuplexStream : Stream, IDuplexPipe to avoid RawStream allocation and pipe allocations
            var adaptedPipeline = new AdaptedPipeline(context.Transport, new Pipe(inputPipeOptions), new Pipe(outputPipeOptions), _logger, memoryPoolFeature.MemoryPool.GetMinimumAllocSize());
            var transportStream = adaptedPipeline.TransportStream;

            if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
            {
                sslStream           = new SslStream(transportStream);
                certificateRequired = false;
            }
            else
            {
                sslStream = new SslStream(transportStream,
                                          leaveInnerStreamOpen: false,
                                          userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
                {
                    if (certificate == null)
                    {
                        return(_options.ClientCertificateMode != ClientCertificateMode.RequireCertificate);
                    }

                    if (_options.ClientCertificateValidation == null)
                    {
                        if (sslPolicyErrors != SslPolicyErrors.None)
                        {
                            return(false);
                        }
                    }

                    var certificate2 = ConvertToX509Certificate2(certificate);
                    if (certificate2 == null)
                    {
                        return(false);
                    }

                    if (_options.ClientCertificateValidation != null)
                    {
                        if (!_options.ClientCertificateValidation(certificate2, chain, sslPolicyErrors))
                        {
                            return(false);
                        }
                    }

                    return(true);
                });

                certificateRequired = true;
            }

            using (var cancellationTokeSource = new CancellationTokenSource(_options.HandshakeTimeout))
                using (cancellationTokeSource.Token.UnsafeRegister(state => ((ConnectionContext)state).Abort(), context))
                {
                    try
                    {
                        // Adapt to the SslStream signature
                        ServerCertificateSelectionCallback selector = null;
                        if (_serverCertificateSelector != null)
                        {
                            selector = (sender, name) =>
                            {
                                context.Features.Set(sslStream);
                                var cert = _serverCertificateSelector(context, name);
                                if (cert != null)
                                {
                                    EnsureCertificateIsAllowedForServerAuth(cert);
                                }
                                return(cert);
                            };
                        }

                        var sslOptions = new SslServerAuthenticationOptions
                        {
                            ServerCertificate = _serverCertificate,
                            ServerCertificateSelectionCallback = selector,
                            ClientCertificateRequired          = certificateRequired,
                            EnabledSslProtocols            = _options.SslProtocols,
                            CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                            ApplicationProtocols           = new List <SslApplicationProtocol>()
                        };

                        // This is order sensitive
                        if ((_options.HttpProtocols & HttpProtocols.Http2) != 0)
                        {
                            sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2);
                            // https://tools.ietf.org/html/rfc7540#section-9.2.1
                            sslOptions.AllowRenegotiation = false;
                        }

                        if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
                        {
                            sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11);
                        }

                        _options.OnAuthenticate?.Invoke(context, sslOptions);

                        await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
                    }
                    catch (OperationCanceledException)
                    {
                        _logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut);
                        sslStream.Dispose();
                        return;
                    }
                    catch (Exception ex) when(ex is IOException || ex is AuthenticationException)
                    {
                        _logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
                        sslStream.Dispose();
                        return;
                    }
                }

            feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
            context.Features.Set <ITlsApplicationProtocolFeature>(feature);
            feature.ClientCertificate    = ConvertToX509Certificate2(sslStream.RemoteCertificate);
            feature.CipherAlgorithm      = sslStream.CipherAlgorithm;
            feature.CipherStrength       = sslStream.CipherStrength;
            feature.HashAlgorithm        = sslStream.HashAlgorithm;
            feature.HashStrength         = sslStream.HashStrength;
            feature.KeyExchangeAlgorithm = sslStream.KeyExchangeAlgorithm;
            feature.KeyExchangeStrength  = sslStream.KeyExchangeStrength;
            feature.Protocol             = sslStream.SslProtocol;

            var original = context.Transport;

            try
            {
                context.Transport = adaptedPipeline;

                using (sslStream)
                {
                    try
                    {
                        adaptedPipeline.RunAsync(sslStream);

                        await _next(context);
                    }
                    finally
                    {
                        await adaptedPipeline.CompleteAsync();
                    }
                }
            }
            finally
            {
                // Restore the original so that it gets closed appropriately
                context.Transport = original;
            }
        }