77using System . Net ;
88using System . Net . Security ;
99using System . Security . Cryptography . X509Certificates ;
10+ using System . Text ;
11+ using Microsoft . Data . Common ;
1012using System . Threading ;
1113using System . Threading . Tasks ;
1214using Microsoft . Data . ProviderBase ;
@@ -150,31 +152,72 @@ internal static bool ValidateSslServerCertificate(string targetServerName, X509C
150152 return true ;
151153 }
152154
153- if ( ( policyErrors & SslPolicyErrors . RemoteCertificateNameMismatch ) != 0 )
155+ // If we get to this point then there is a ssl policy flag.
156+ StringBuilder messageBuilder = new ( ) ;
157+ if ( policyErrors . HasFlag ( SslPolicyErrors . RemoteCertificateChainErrors ) )
154158 {
159+ SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "targetServerName {0}, SslPolicyError {1}, SSL Policy certificate chain has errors." , args0 : targetServerName , args1 : policyErrors ) ;
160+
161+ // get the chain status from the certificate
162+ X509Certificate2 cert2 = cert as X509Certificate2 ;
163+ X509Chain chain = new ( ) ;
164+ chain . ChainPolicy . RevocationMode = X509RevocationMode . Offline ;
165+ StringBuilder chainStatusInformation = new ( ) ;
166+ bool chainIsValid = chain . Build ( cert2 ) ;
167+ Debug . Assert ( ! chainIsValid , "RemoteCertificateChainError flag is detected, but certificate chain is valid." ) ;
168+ if ( ! chainIsValid )
169+ {
170+ foreach ( X509ChainStatus chainStatus in chain . ChainStatus )
171+ {
172+ chainStatusInformation . Append ( $ "{ chainStatus . StatusInformation } , [Status: { chainStatus . Status } ]") ;
173+ chainStatusInformation . AppendLine ( ) ;
174+ }
175+ }
176+ SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "targetServerName {0}, SslPolicyError {1}, SSL Policy certificate chain has errors. ChainStatus {2}" , args0 : targetServerName , args1 : policyErrors , args2 : chainStatusInformation ) ;
177+ messageBuilder . AppendFormat ( Strings . SQL_RemoteCertificateChainErrors , chainStatusInformation ) ;
178+ messageBuilder . AppendLine ( ) ;
179+ }
180+
181+ if ( policyErrors . HasFlag ( SslPolicyErrors . RemoteCertificateNotAvailable ) )
182+ {
183+ SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "targetServerName {0}, SSL Policy invalidated certificate." , args0 : targetServerName ) ;
184+ messageBuilder . AppendLine ( Strings . SQL_RemoteCertificateNotAvailable ) ;
185+ }
186+
187+ if ( policyErrors . HasFlag ( SslPolicyErrors . RemoteCertificateNameMismatch ) )
188+ {
189+ #if NET7_0_OR_GREATER
190+ X509Certificate2 cert2 = cert as X509Certificate2 ;
191+ if ( ! cert2 . MatchesHostname ( targetServerName ) )
192+ {
193+ SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "targetServerName {0}, Target Server name or HNIC does not match the Subject/SAN in Certificate." , args0 : targetServerName ) ;
194+ messageBuilder . AppendLine ( Strings . SQL_RemoteCertificateNameMismatch ) ;
195+ }
196+ #else
197+ // To Do: include certificate SAN (Subject Alternative Name) check.
155198 string certServerName = cert . Subject . Substring ( cert . Subject . IndexOf ( '=' ) + 1 ) ;
156199
157200 // Verify that target server name matches subject in the certificate
158201 if ( targetServerName . Length > certServerName . Length )
159202 {
160203 SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "targetServerName {0}, Target Server name is of greater length than Subject in Certificate." , args0 : targetServerName ) ;
161- return false ;
204+ messageBuilder . AppendLine ( Strings . SQL_RemoteCertificateNameMismatch ) ;
162205 }
163206 else if ( targetServerName . Length == certServerName . Length )
164207 {
165208 // Both strings have the same length, so targetServerName must be a FQDN
166209 if ( ! targetServerName . Equals ( certServerName , StringComparison . OrdinalIgnoreCase ) )
167210 {
168211 SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "targetServerName {0}, Target Server name does not match Subject in Certificate." , args0 : targetServerName ) ;
169- return false ;
212+ messageBuilder . AppendLine ( Strings . SQL_RemoteCertificateNameMismatch ) ;
170213 }
171214 }
172215 else
173216 {
174217 if ( string . Compare ( targetServerName , 0 , certServerName , 0 , targetServerName . Length , StringComparison . OrdinalIgnoreCase ) != 0 )
175218 {
176219 SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "targetServerName {0}, Target Server name does not match Subject in Certificate." , args0 : targetServerName ) ;
177- return false ;
220+ messageBuilder . AppendLine ( Strings . SQL_RemoteCertificateNameMismatch ) ;
178221 }
179222
180223 // Server name matches cert name for its whole length, so ensure that the
@@ -184,17 +227,18 @@ internal static bool ValidateSslServerCertificate(string targetServerName, X509C
184227 if ( certServerName [ targetServerName . Length ] != '.' )
185228 {
186229 SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "targetServerName {0}, Target Server name does not match Subject in Certificate." , args0 : targetServerName ) ;
187- return false ;
230+ messageBuilder . AppendLine ( Strings . SQL_RemoteCertificateNameMismatch ) ;
188231 }
189232 }
233+ #endif
190234 }
191- else
235+
236+ if ( messageBuilder . Length > 0 )
192237 {
193- // Fail all other SslPolicy cases besides RemoteCertificateNameMismatch
194- SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "targetServerName {0}, SslPolicyError {1}, SSL Policy invalidated certificate." , args0 : targetServerName , args1 : policyErrors ) ;
195- return false ;
238+ throw ADP . SSLCertificateAuthenticationException ( messageBuilder . ToString ( ) ) ;
196239 }
197- SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . INFO , "targetServerName {0}, Client certificate validated successfully." , args0 : targetServerName ) ;
240+
241+ SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . INFO , " Remote certificate with subject: {0}, validated successfully." , args0 : cert . Subject ) ;
198242 return true ;
199243 }
200244 }
@@ -218,26 +262,67 @@ internal static bool ValidateSslServerCertificate(X509Certificate clientCert, X5
218262 return true ;
219263 }
220264
221- if ( ( policyErrors & SslPolicyErrors . RemoteCertificateNameMismatch ) != 0 )
265+ StringBuilder messageBuilder = new ( ) ;
266+ if ( policyErrors . HasFlag ( SslPolicyErrors . RemoteCertificateNotAvailable ) )
222267 {
268+ SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "serverCert {0}, SSL Server certificate not validated as PolicyErrors set to RemoteCertificateNotAvailable." , args0 : clientCert . Subject ) ;
269+ messageBuilder . AppendLine ( Strings . SQL_RemoteCertificateNotAvailable ) ;
270+ }
271+
272+ if ( policyErrors . HasFlag ( SslPolicyErrors . RemoteCertificateChainErrors ) )
273+ {
274+ // get the chain status from the server certificate
275+ X509Certificate2 cert2 = serverCert as X509Certificate2 ;
276+ X509Chain chain = new ( ) ;
277+ chain . ChainPolicy . RevocationMode = X509RevocationMode . Offline ;
278+ StringBuilder chainStatusInformation = new ( ) ;
279+ bool chainIsValid = chain . Build ( cert2 ) ;
280+ Debug . Assert ( ! chainIsValid , "RemoteCertificateChainError flag is detected, but certificate chain is valid." ) ;
281+ if ( ! chainIsValid )
282+ {
283+ foreach ( X509ChainStatus chainStatus in chain . ChainStatus )
284+ {
285+ chainStatusInformation . Append ( $ "{ chainStatus . StatusInformation } , [Status: { chainStatus . Status } ]") ;
286+ chainStatusInformation . AppendLine ( ) ;
287+ }
288+ }
289+ SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "certificate subject from server is {0}, and does not match with the certificate provided client." , args0 : cert2 . SubjectName . Name ) ;
290+ messageBuilder . AppendFormat ( Strings . SQL_RemoteCertificateChainErrors , chainStatusInformation ) ;
291+ messageBuilder . AppendLine ( ) ;
292+ }
293+
294+ if ( policyErrors . HasFlag ( SslPolicyErrors . RemoteCertificateNameMismatch ) )
295+ {
296+ #if NET7_0_OR_GREATER
297+ X509Certificate2 s_cert = serverCert as X509Certificate2 ;
298+ X509Certificate2 c_cert = clientCert as X509Certificate2 ;
299+
300+ if ( ! s_cert . MatchesHostname ( c_cert . SubjectName . Name ) )
301+ {
302+ SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "certificate from server does not match with the certificate provided client." , args0 : s_cert . Subject ) ;
303+ messageBuilder . AppendLine ( Strings . SQL_RemoteCertificateNameMismatch ) ;
304+ }
305+ #else
223306 // Verify that subject name matches
224307 if ( serverCert . Subject != clientCert . Subject )
225308 {
226309 SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "certificate subject from server is {0}, and does not match with the certificate provided client." , args0 : serverCert . Subject ) ;
227- return false ;
310+ messageBuilder . AppendLine ( Strings . SQL_RemoteCertificateNameMismatch ) ;
228311 }
312+
229313 if ( ! serverCert . Equals ( clientCert ) )
230314 {
231315 SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "certificate from server does not match with the certificate provided client." , args0 : serverCert . Subject ) ;
232- return false ;
316+ messageBuilder . AppendLine ( Strings . SQL_RemoteCertificateNameMismatch ) ;
233317 }
318+ #endif
234319 }
235- else
320+
321+ if ( messageBuilder . Length > 0 )
236322 {
237- // Fail all other SslPolicy cases besides RemoteCertificateNameMismatch
238- SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . ERR , "certificate subject: {0}, SslPolicyError {1}, SSL Policy invalidated certificate." , args0 : clientCert . Subject , args1 : policyErrors ) ;
239- return false ;
323+ throw ADP . SSLCertificateAuthenticationException ( messageBuilder . ToString ( ) ) ;
240324 }
325+
241326 SqlClientEventSource . Log . TrySNITraceEvent ( nameof ( SNICommon ) , EventType . INFO , "certificate subject {0}, Client certificate validated successfully." , args0 : clientCert . Subject ) ;
242327 return true ;
243328 }
0 commit comments