Esempio n. 1
0
    /// <summary>
    /// Configure Kestrel to use HTTPS. This does not use default certificates or other defaults specified via config or
    /// <see cref="KestrelServerOptions.ConfigureHttpsDefaults(Action{HttpsConnectionAdapterOptions})"/>.
    /// </summary>
    /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
    /// <param name="callbackOptions">Options for a per connection callback.</param>
    /// <returns>The <see cref="ListenOptions"/>.</returns>
    public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandshakeCallbackOptions callbackOptions)
    {
        if (callbackOptions is null)
        {
            throw new ArgumentNullException(nameof(callbackOptions));
        }

        if (callbackOptions.OnConnection is null)
        {
            throw new ArgumentException($"{nameof(TlsHandshakeCallbackOptions.OnConnection)} must not be null.");
        }

        var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance;

        listenOptions.IsTls = true;
        listenOptions.HttpsCallbackOptions = callbackOptions;

        listenOptions.Use(next =>
        {
            // Set the list of protocols from listen options.
            // Set it inside Use delegate so Protocols and UseHttps can be called out of order.
            callbackOptions.HttpProtocols = listenOptions.Protocols;

            var middleware = new HttpsConnectionMiddleware(next, callbackOptions, loggerFactory);
            return(middleware.OnConnectionAsync);
        });

        return(listenOptions);
    }
Esempio n. 2
0
        /// <summary>
        /// Configure Kestrel to use HTTPS. This does not use default certificates or other defaults specified via config or
        /// <see cref="KestrelServerOptions.ConfigureHttpsDefaults(Action{HttpsConnectionAdapterOptions})"/>.
        /// </summary>
        /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
        /// <param name="callbackOptions">Options for a per connection callback.</param>
        /// <returns>The <see cref="ListenOptions"/>.</returns>
        public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandshakeCallbackOptions callbackOptions)
        {
            if (callbackOptions is null)
            {
                throw new ArgumentNullException(nameof(callbackOptions));
            }

            if (callbackOptions.OnConnection is null)
            {
                throw new ArgumentException($"{nameof(TlsHandshakeCallbackOptions.OnConnection)} must not be null.");
            }

            if (listenOptions.Protocols.HasFlag(HttpProtocols.Http3))
            {
                throw new NotSupportedException($"{nameof(UseHttps)} with {nameof(TlsHandshakeCallbackOptions)} is not supported with HTTP/3.");
            }

            var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance;

            listenOptions.IsTls = true;
            listenOptions.Use(next =>
            {
                // Set the list of protocols from listen options
                callbackOptions.HttpProtocols = listenOptions.Protocols;
                var middleware = new HttpsConnectionMiddleware(next, callbackOptions, loggerFactory);
                return(middleware.OnConnectionAsync);
            });

            return(listenOptions);
        }
Esempio n. 3
0
        /// <summary>
        /// Configure Kestrel to use HTTPS.
        /// </summary>
        /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
        /// <param name="httpsOptionsCallback">Callback to configure HTTPS options.</param>
        /// <param name="state">State for the <paramref name="httpsOptionsCallback"/>.</param>
        /// <param name="handshakeTimeout">Specifies the maximum amount of time allowed for the TLS/SSL handshake. This must be positive and finite.</param>
        /// <returns>The <see cref="ListenOptions"/>.</returns>
        internal static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsOptionsCallback httpsOptionsCallback, object state, TimeSpan handshakeTimeout)
        {
            var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance;

            listenOptions.IsTls = true;
            listenOptions.Use(next =>
            {
                var middleware = new HttpsConnectionMiddleware(next, httpsOptionsCallback, state, handshakeTimeout, loggerFactory);
                return(middleware.OnConnectionAsync);
            });

            return(listenOptions);
        }
        /// <summary>
        /// Configure Kestrel to use HTTPS.
        /// </summary>
        /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
        /// <param name="httpsOptions">Options to configure HTTPS.</param>
        /// <returns>The <see cref="ListenOptions"/>.</returns>
        public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions)
        {
            var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance;

            listenOptions.IsTls = true;
            listenOptions.Use(next => {
                // Set the list of protocols from listen options
                httpsOptions.HttpProtocols = listenOptions.Protocols;
                var middleware             = new HttpsConnectionMiddleware(next, httpsOptions, loggerFactory);
                return(middleware.OnConnectionAsync);
            });

            return(listenOptions);
        }
Esempio n. 5
0
    public async Task <EndPoint> BindAsync(EndPoint endPoint, MultiplexedConnectionDelegate multiplexedConnectionDelegate, ListenOptions listenOptions, CancellationToken cancellationToken)
    {
        if (_multiplexedTransportFactory is null)
        {
            throw new InvalidOperationException($"Cannot bind with {nameof(MultiplexedConnectionDelegate)} no {nameof(IMultiplexedConnectionListenerFactory)} is registered.");
        }

        var features = new FeatureCollection();

        // HttpsOptions or HttpsCallbackOptions should always be set in production, but it's not set for InMemory tests.
        // The QUIC transport will check if TlsConnectionCallbackOptions is missing.
        if (listenOptions.HttpsOptions != null)
        {
            var sslServerAuthenticationOptions = HttpsConnectionMiddleware.CreateHttp3Options(listenOptions.HttpsOptions);
            features.Set(new TlsConnectionCallbackOptions
            {
                ApplicationProtocols = sslServerAuthenticationOptions.ApplicationProtocols ?? new List <SslApplicationProtocol> {
                    SslApplicationProtocol.Http3
                },
                OnConnection      = (context, cancellationToken) => ValueTask.FromResult(sslServerAuthenticationOptions),
                OnConnectionState = null,
            });
        }
        else if (listenOptions.HttpsCallbackOptions != null)
        {
            features.Set(new TlsConnectionCallbackOptions
            {
                ApplicationProtocols = new List <SslApplicationProtocol> {
                    SslApplicationProtocol.Http3
                },
                OnConnection = (context, cancellationToken) =>
                {
                    return(listenOptions.HttpsCallbackOptions.OnConnection(new TlsHandshakeCallbackContext
                    {
                        ClientHelloInfo = context.ClientHelloInfo,
                        CancellationToken = cancellationToken,
                        State = context.State,
                        Connection = new ConnectionContextAdapter(context.Connection),
                    }));
                },
                OnConnectionState = listenOptions.HttpsCallbackOptions.OnConnectionState,
            });
        }

        var transport = await _multiplexedTransportFactory.BindAsync(endPoint, features, cancellationToken).ConfigureAwait(false);

        StartAcceptLoop(new GenericMultiplexedConnectionListener(transport), c => multiplexedConnectionDelegate(c), listenOptions.EndpointConfig);
        return(transport.EndPoint);
    }
Esempio n. 6
0
        public async Task <EndPoint> BindAsync(EndPoint endPoint, MultiplexedConnectionDelegate multiplexedConnectionDelegate, ListenOptions listenOptions, CancellationToken cancellationToken)
        {
            if (_multiplexedTransportFactory is null)
            {
                throw new InvalidOperationException($"Cannot bind with {nameof(MultiplexedConnectionDelegate)} no {nameof(IMultiplexedConnectionListenerFactory)} is registered.");
            }

            var features = new FeatureCollection();

            // This should always be set in production, but it's not set for InMemory tests.
            // The transport will check if the feature is missing.
            if (listenOptions.HttpsOptions != null)
            {
                features.Set(HttpsConnectionMiddleware.CreateHttp3Options(listenOptions.HttpsOptions));
            }

            var transport = await _multiplexedTransportFactory.BindAsync(endPoint, features, cancellationToken).ConfigureAwait(false);

            StartAcceptLoop(new GenericMultiplexedConnectionListener(transport), c => multiplexedConnectionDelegate(c), listenOptions.EndpointConfig);
            return(transport.EndPoint);
        }
Esempio n. 7
0
        public SniOptionsSelector(
            string endpointName,
            Dictionary <string, SniConfig> sniDictionary,
            ICertificateConfigLoader certifcateConfigLoader,
            HttpsConnectionAdapterOptions fallbackHttpsOptions,
            HttpProtocols fallbackHttpProtocols,
            ILogger <HttpsConnectionMiddleware> logger)
        {
            _endpointName = endpointName;

            _fallbackServerCertificateSelector = fallbackHttpsOptions.ServerCertificateSelector;
            _onAuthenticateCallback            = fallbackHttpsOptions.OnAuthenticate;

            foreach (var(name, sniConfig) in sniDictionary)
            {
                var sslOptions = new SslServerAuthenticationOptions
                {
                    ServerCertificate              = certifcateConfigLoader.LoadCertificate(sniConfig.Certificate, $"{endpointName}:Sni:{name}"),
                    EnabledSslProtocols            = sniConfig.SslProtocols ?? fallbackHttpsOptions.SslProtocols,
                    CertificateRevocationCheckMode = fallbackHttpsOptions.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                };

                if (sslOptions.ServerCertificate is null)
                {
                    if (fallbackHttpsOptions.ServerCertificate is null && _fallbackServerCertificateSelector is null)
                    {
                        throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
                    }

                    if (_fallbackServerCertificateSelector is null)
                    {
                        // Cache the fallback ServerCertificate since there's no fallback ServerCertificateSelector taking precedence.
                        sslOptions.ServerCertificate = fallbackHttpsOptions.ServerCertificate;
                    }
                }

                if (sslOptions.ServerCertificate != null)
                {
                    // This might be do blocking IO but it'll resolve the certificate chain up front before any connections are
                    // made to the server
                    sslOptions.ServerCertificateContext = SslStreamCertificateContext.Create((X509Certificate2)sslOptions.ServerCertificate, additionalCertificates: null);
                }

                if (!certifcateConfigLoader.IsTestMock && sslOptions.ServerCertificate is X509Certificate2 cert2)
                {
                    HttpsConnectionMiddleware.EnsureCertificateIsAllowedForServerAuth(cert2);
                }

                var clientCertificateMode = sniConfig.ClientCertificateMode ?? fallbackHttpsOptions.ClientCertificateMode;

                if (clientCertificateMode != ClientCertificateMode.NoCertificate)
                {
                    sslOptions.ClientCertificateRequired = clientCertificateMode == ClientCertificateMode.AllowCertificate ||
                                                           clientCertificateMode == ClientCertificateMode.RequireCertificate;
                    sslOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
                                                                     HttpsConnectionMiddleware.RemoteCertificateValidationCallback(
                        clientCertificateMode, fallbackHttpsOptions.ClientCertificateValidation, certificate, chain, sslPolicyErrors);
                }

                var httpProtocols = sniConfig.Protocols ?? fallbackHttpProtocols;
                httpProtocols = HttpsConnectionMiddleware.ValidateAndNormalizeHttpProtocols(httpProtocols, logger);
                HttpsConnectionMiddleware.ConfigureAlpn(sslOptions, httpProtocols);

                var sniOptions = new SniOptions(sslOptions, httpProtocols, clientCertificateMode);

                if (name.Equals(WildcardHost, StringComparison.Ordinal))
                {
                    _wildcardOptions = sniOptions;
                }
                else if (name.StartsWith(WildcardPrefix, StringComparison.Ordinal))
                {
                    // Only slice off 1 character, the `*`. We want to match the leading `.` also.
                    _wildcardPrefixOptions.Add(name.Substring(1), sniOptions);
                }
                else
                {
                    _exactNameOptions.Add(name, sniOptions);
                }
            }
        }
Esempio n. 8
0
        public (SslServerAuthenticationOptions, ClientCertificateMode) GetOptions(ConnectionContext connection, string serverName)
        {
            SniOptions?sniOptions = null;

            if (!string.IsNullOrEmpty(serverName) && !_exactNameOptions.TryGetValue(serverName, out sniOptions))
            {
                foreach (var(suffix, options) in _wildcardPrefixOptions)
                {
                    if (serverName.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
                    {
                        sniOptions = options;
                        break;
                    }
                }
            }

            // Fully wildcarded ("*") options can be used even when given an empty server name.
            sniOptions ??= _wildcardOptions;

            if (sniOptions is null)
            {
                if (serverName is null)
                {
                    // There was no ALPN
                    throw new AuthenticationException(CoreStrings.FormatSniNotConfiguredToAllowNoServerName(_endpointName));
                }
                else
                {
                    throw new AuthenticationException(CoreStrings.FormatSniNotConfiguredForServerName(serverName, _endpointName));
                }
            }

            connection.Features.Set(new HttpProtocolsFeature(sniOptions.HttpProtocols));

            var sslOptions = sniOptions.SslOptions;

            if (sslOptions.ServerCertificate is null)
            {
                Debug.Assert(_fallbackServerCertificateSelector != null,
                             "The cached SniOptions ServerCertificate can only be null if there's a fallback certificate selector.");

                // If a ServerCertificateSelector doesn't return a cert, HttpsConnectionMiddleware doesn't fallback to the ServerCertificate.
                sslOptions = CloneSslOptions(sslOptions);
                var fallbackCertificate = _fallbackServerCertificateSelector(connection, serverName);

                if (fallbackCertificate != null)
                {
                    HttpsConnectionMiddleware.EnsureCertificateIsAllowedForServerAuth(fallbackCertificate);
                }

                sslOptions.ServerCertificate = fallbackCertificate;
            }

            if (_onAuthenticateCallback != null)
            {
                // From doc comments: "This is called after all of the other settings have already been applied."
                sslOptions = CloneSslOptions(sslOptions);
                _onAuthenticateCallback(connection, sslOptions);
            }

            return(sslOptions, sniOptions.ClientCertificateMode);
        }