33
44using System ;
55using System . Collections . Generic ;
6+ using System . Diagnostics ;
67using System . Net . Security ;
78using System . Security . Authentication ;
9+ using System . Security . Cryptography . X509Certificates ;
10+ using Microsoft . AspNetCore . Connections ;
811using Microsoft . AspNetCore . Server . Kestrel . Https ;
912using Microsoft . AspNetCore . Server . Kestrel . Https . Internal ;
1013using Microsoft . Extensions . Logging ;
@@ -17,9 +20,13 @@ internal class SniOptionsSelector
1720 private const string wildcardPrefix = "*." ;
1821
1922 private readonly string _endpointName ;
20- private readonly Dictionary < string , SslServerAuthenticationOptions > _fullNameOptions = new Dictionary < string , SslServerAuthenticationOptions > ( StringComparer . OrdinalIgnoreCase ) ;
21- private readonly List < ( string , SslServerAuthenticationOptions ) > _wildcardPrefixOptions = new List < ( string , SslServerAuthenticationOptions ) > ( ) ;
22- private readonly SslServerAuthenticationOptions _wildcardHostOptions = null ;
23+
24+ private readonly Func < ConnectionContext , string , X509Certificate2 > _fallbackServerCertificateSelector ;
25+ private readonly Action < ConnectionContext , SslServerAuthenticationOptions > _onAuthenticateCallback ;
26+
27+ private readonly Dictionary < string , SniOptions > _fullNameOptions = new Dictionary < string , SniOptions > ( StringComparer . OrdinalIgnoreCase ) ;
28+ private readonly List < ( string , SniOptions ) > _wildcardPrefixOptions = new List < ( string , SniOptions ) > ( ) ;
29+ private readonly SniOptions _wildcardHostOptions = null ;
2330
2431 public SniOptionsSelector (
2532 KestrelConfigurationLoader configLoader ,
@@ -30,24 +37,30 @@ public SniOptionsSelector(
3037 {
3138 _endpointName = endpointConfig . Name ;
3239
40+ _fallbackServerCertificateSelector = fallbackOptions . ServerCertificateSelector ;
41+ _onAuthenticateCallback = fallbackOptions . OnAuthenticate ;
42+
3343 foreach ( var ( name , sniConfig ) in endpointConfig . SNI )
3444 {
35- var sslServerOptions = new SslServerAuthenticationOptions ( ) ;
36-
37- sslServerOptions . ServerCertificate = configLoader . LoadCertificate ( sniConfig . Certificate , endpointConfig . Name ) ;
38-
39- if ( sslServerOptions . ServerCertificate is null &&
40- fallbackOptions . ServerCertificate is null &&
41- fallbackOptions . ServerCertificateSelector is null )
45+ var sslServerOptions = new SslServerAuthenticationOptions
4246 {
43- throw new InvalidOperationException ( CoreStrings . NoCertSpecifiedNoDevelopmentCertificateFound ) ;
44- }
47+ ServerCertificate = configLoader . LoadCertificate ( sniConfig . Certificate , endpointConfig . Name ) ,
48+ EnabledSslProtocols = sniConfig . SslProtocols ?? fallbackOptions . SslProtocols ,
49+ } ;
4550
46- sslServerOptions . EnabledSslProtocols = sniConfig . SslProtocols ?? fallbackOptions . SslProtocols ;
51+ if ( sslServerOptions . ServerCertificate is null )
52+ {
53+ if ( fallbackOptions . ServerCertificate is null && fallbackOptions . ServerCertificateSelector is null )
54+ {
55+ throw new InvalidOperationException ( CoreStrings . NoCertSpecifiedNoDevelopmentCertificateFound ) ;
56+ }
4757
48- var httpProtocols = sniConfig . Protocols ?? fallbackHttpProtocols ;
49- httpProtocols = HttpsConnectionMiddleware . ValidateAndNormalizeHttpProtocols ( httpProtocols , logger ) ;
50- HttpsConnectionMiddleware . ConfigureAlpn ( sslServerOptions , httpProtocols ) ;
58+ if ( fallbackOptions . ServerCertificateSelector is null )
59+ {
60+ // Cache the fallback ServerCertificate since there's no fallback ServerCertificateSelector taking precedence.
61+ sslServerOptions . ServerCertificate = fallbackOptions . ServerCertificate ;
62+ }
63+ }
5164
5265 var clientCertificateMode = sniConfig . ClientCertificateMode ?? fallbackOptions . ClientCertificateMode ;
5366
@@ -59,24 +72,34 @@ fallbackOptions.ServerCertificate is null &&
5972 clientCertificateMode , fallbackOptions . ClientCertificateValidation , certificate , chain , sslPolicyErrors ) ;
6073 }
6174
75+ var httpProtocols = sniConfig . Protocols ?? fallbackHttpProtocols ;
76+ httpProtocols = HttpsConnectionMiddleware . ValidateAndNormalizeHttpProtocols ( httpProtocols , logger ) ;
77+ HttpsConnectionMiddleware . ConfigureAlpn ( sslServerOptions , httpProtocols ) ;
78+
79+ var sniOptions = new SniOptions
80+ {
81+ SslOptions = sslServerOptions ,
82+ HttpProtocols = httpProtocols ,
83+ } ;
84+
6285 if ( name . Equals ( wildcardHost , StringComparison . Ordinal ) )
6386 {
64- _wildcardHostOptions = sslServerOptions ;
87+ _wildcardHostOptions = sniOptions ;
6588 }
6689 else if ( name . StartsWith ( wildcardPrefix , StringComparison . Ordinal ) )
6790 {
68- _wildcardPrefixOptions . Add ( ( name , sslServerOptions ) ) ;
91+ _wildcardPrefixOptions . Add ( ( name , sniOptions ) ) ;
6992 }
7093 else
7194 {
72- _fullNameOptions [ name ] = sslServerOptions ;
95+ _fullNameOptions [ name ] = sniOptions ;
7396 }
7497 }
7598 }
7699
77- public SslServerAuthenticationOptions GetOptions ( string serverName )
100+ public SniOptions GetOptions ( ConnectionContext connection , string serverName )
78101 {
79- SslServerAuthenticationOptions options = null ;
102+ SniOptions options = null ;
80103
81104 if ( ! string . IsNullOrEmpty ( serverName ) )
82105 {
@@ -116,6 +139,25 @@ public SslServerAuthenticationOptions GetOptions(string serverName)
116139 }
117140 }
118141
142+ if ( options . SslOptions . ServerCertificate is null )
143+ {
144+ Debug . Assert ( _fallbackServerCertificateSelector != null ,
145+ "The cached SniOptions ServerCertificate can only be null if there's a fallback certificate selector." ) ;
146+
147+ // If a ServerCertificateSelector passed into HttpsConnectionMiddleware via HttpsConnectionAdapterOptions doesn't return a cert,
148+ // HttpsConnectionMiddleware doesn't fallback to the ServerCertificate, so we don't do that here either.
149+ options = options . Clone ( ) ;
150+ options . SslOptions . ServerCertificate = _fallbackServerCertificateSelector ( connection , serverName ) ;
151+ }
152+
153+ if ( _onAuthenticateCallback != null )
154+ {
155+ options = options . Clone ( ) ;
156+
157+ // From doc comments: "This is called after all of the other settings have already been applied."
158+ _onAuthenticateCallback ( connection , options . SslOptions ) ;
159+ }
160+
119161 return options ;
120162 }
121163 }
0 commit comments