Example #1
0
        internal SniEndPointResolver(IDnsResolver dns, SniOptions sniOptions, IRandom rand)
        {
            _dns        = dns;
            _sniOptions = sniOptions;
            _random     = rand;

            if (sniOptions.IsIp)
            {
                _endPoint = new IPEndPoint(sniOptions.Ip, sniOptions.Port);
            }
        }
Example #2
0
        internal SniEndPointResolver(IDnsResolver dns, SniOptions sniOptions, IRandom rand)
        {
            _dns        = dns ?? throw new ArgumentNullException(nameof(dns));
            _sniOptions = sniOptions ?? throw new ArgumentNullException(nameof(sniOptions));
            _random     = rand ?? throw new ArgumentNullException(nameof(rand));

            if (sniOptions.IsIp)
            {
                _endPoint = new IPEndPoint(sniOptions.Ip, sniOptions.Port);
            }
        }
Example #3
0
 public SniEndPointResolver(IDnsResolver dns, SniOptions sniOptions) : this(dns, sniOptions, new DefaultRandom())
 {
 }
Example #4
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);
                }
            }
        }
Example #5
0
        public SslServerAuthenticationOptions 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);
        }