2121import com .google .common .annotations .VisibleForTesting ;
2222import com .google .common .base .Optional ;
2323import com .google .common .base .Strings ;
24- import com .google .common .collect .ImmutableList ;
2524import com .google .common .collect .ImmutableMap ;
2625import com .google .re2j .Pattern ;
2726import io .envoyproxy .envoy .extensions .transport_sockets .tls .v3 .CertificateValidationContext ;
3231import java .security .cert .CertificateException ;
3332import java .security .cert .CertificateParsingException ;
3433import java .security .cert .X509Certificate ;
34+ import java .util .ArrayList ;
3535import java .util .Arrays ;
3636import java .util .Collection ;
3737import java .util .HashSet ;
4040import java .util .Map ;
4141import java .util .Set ;
4242import javax .annotation .Nullable ;
43+ import javax .net .ssl .SNIHostName ;
44+ import javax .net .ssl .SNIServerName ;
4345import javax .net .ssl .SSLEngine ;
4446import javax .net .ssl .SSLParameters ;
4547import javax .net .ssl .SSLSocket ;
@@ -61,30 +63,24 @@ final class XdsX509TrustManager extends X509ExtendedTrustManager implements X509
6163 private final X509ExtendedTrustManager delegate ;
6264 private final Map <String , X509ExtendedTrustManager > spiffeTrustMapDelegates ;
6365 private final CertificateValidationContext certContext ;
64- private final String sniForSanMatching ;
66+ private final boolean autoSniSanValidation ;
6567
6668 XdsX509TrustManager (@ Nullable CertificateValidationContext certContext ,
67- X509ExtendedTrustManager delegate ) {
68- this (certContext , delegate , null );
69- }
70-
71- XdsX509TrustManager (@ Nullable CertificateValidationContext certContext ,
72- X509ExtendedTrustManager delegate , @ Nullable String sniForSanMatching ) {
69+ X509ExtendedTrustManager delegate , boolean autoSniSanValidation ) {
7370 checkNotNull (delegate , "delegate" );
7471 this .certContext = certContext ;
7572 this .delegate = delegate ;
7673 this .spiffeTrustMapDelegates = null ;
77- this .sniForSanMatching = sniForSanMatching ;
74+ this .autoSniSanValidation = autoSniSanValidation ;
7875 }
7976
8077 XdsX509TrustManager (@ Nullable CertificateValidationContext certContext ,
81- Map <String , X509ExtendedTrustManager > spiffeTrustMapDelegates ,
82- @ Nullable String sniForSanMatching ) {
78+ Map <String , X509ExtendedTrustManager > spiffeTrustMapDelegates , boolean autoSniSanValidation ) {
8379 checkNotNull (spiffeTrustMapDelegates , "spiffeTrustMapDelegates" );
8480 this .spiffeTrustMapDelegates = ImmutableMap .copyOf (spiffeTrustMapDelegates );
8581 this .certContext = certContext ;
8682 this .delegate = null ;
87- this .sniForSanMatching = sniForSanMatching ;
83+ this .autoSniSanValidation = autoSniSanValidation ;
8884 }
8985
9086 private static boolean verifyDnsNameInPattern (
@@ -114,7 +110,7 @@ private static boolean verifyDnsNameInPattern(
114110 }
115111
116112 private static boolean verifyDnsNameSafeRegex (
117- String altNameFromCert , RegexMatcher sanToVerifySafeRegex ) {
113+ String altNameFromCert , RegexMatcher sanToVerifySafeRegex ) {
118114 Pattern safeRegExMatch = Pattern .compile (sanToVerifySafeRegex .getRegex ());
119115 return safeRegExMatch .matches (altNameFromCert );
120116 }
@@ -126,37 +122,40 @@ private static boolean verifyDnsNamePrefix(
126122 }
127123 return ignoreCase
128124 ? altNameFromCert .toLowerCase (Locale .ROOT ).startsWith (
129- sanToVerifyPrefix .toLowerCase (Locale .ROOT ))
125+ sanToVerifyPrefix .toLowerCase (Locale .ROOT ))
130126 : altNameFromCert .startsWith (sanToVerifyPrefix );
131127 }
132128
133129 private static boolean verifyDnsNameSuffix (
134- String altNameFromCert , String sanToVerifySuffix , boolean ignoreCase ) {
130+ String altNameFromCert , String sanToVerifySuffix , boolean ignoreCase ) {
135131 if (Strings .isNullOrEmpty (sanToVerifySuffix )) {
136132 return false ;
137133 }
138134 return ignoreCase
139- ? altNameFromCert .toLowerCase (Locale .ROOT ).endsWith (
140- sanToVerifySuffix .toLowerCase (Locale .ROOT ))
141- : altNameFromCert .endsWith (sanToVerifySuffix );
135+ ? altNameFromCert .toLowerCase (Locale .ROOT ).endsWith (
136+ sanToVerifySuffix .toLowerCase (Locale .ROOT ))
137+ : altNameFromCert .endsWith (sanToVerifySuffix );
142138 }
143139
144140 private static boolean verifyDnsNameContains (
145- String altNameFromCert , String sanToVerifySubstring , boolean ignoreCase ) {
141+ String altNameFromCert , String sanToVerifySubstring , boolean ignoreCase ) {
146142 if (Strings .isNullOrEmpty (sanToVerifySubstring )) {
147143 return false ;
148144 }
149145 return ignoreCase
150- ? altNameFromCert .toLowerCase (Locale .ROOT ).contains (
151- sanToVerifySubstring .toLowerCase (Locale .ROOT ))
152- : altNameFromCert .contains (sanToVerifySubstring );
146+ ? altNameFromCert .toLowerCase (Locale .ROOT ).contains (
147+ sanToVerifySubstring .toLowerCase (Locale .ROOT ))
148+ : altNameFromCert .contains (sanToVerifySubstring );
153149 }
154150
155151 private static boolean verifyDnsNameExact (
156152 String altNameFromCert , String sanToVerifyExact , boolean ignoreCase ) {
157153 if (Strings .isNullOrEmpty (sanToVerifyExact )) {
158154 return false ;
159155 }
156+ if (sanToVerifyExact .contains ("*" )) {
157+ return verifyDnsNameWildcard (altNameFromCert , sanToVerifyExact , ignoreCase );
158+ }
160159 return ignoreCase
161160 ? sanToVerifyExact .equalsIgnoreCase (altNameFromCert )
162161 : sanToVerifyExact .equals (altNameFromCert );
@@ -213,15 +212,11 @@ private static void verifySubjectAltNameInLeaf(
213212 * This is called from various check*Trusted methods.
214213 */
215214 @ VisibleForTesting
216- void verifySubjectAltNameInChain (X509Certificate [] peerCertChain ) throws CertificateException {
215+ void verifySubjectAltNameInChain (X509Certificate [] peerCertChain ,
216+ List <StringMatcher > verifyList ) throws CertificateException {
217217 if (certContext == null ) {
218218 return ;
219219 }
220- @ SuppressWarnings ("deprecation" ) // gRFC A29 predates match_typed_subject_alt_names
221- List <StringMatcher > verifyList =
222- CertificateUtils .isXdsSniEnabled && !Strings .isNullOrEmpty (sniForSanMatching )
223- ? ImmutableList .of (StringMatcher .newBuilder ().setExact (sniForSanMatching ).build ())
224- : certContext .getMatchSubjectAltNamesList ();
225220 if (verifyList .isEmpty ()) {
226221 return ;
227222 }
@@ -236,14 +231,14 @@ void verifySubjectAltNameInChain(X509Certificate[] peerCertChain) throws Certifi
236231 public void checkClientTrusted (X509Certificate [] chain , String authType , Socket socket )
237232 throws CertificateException {
238233 chooseDelegate (chain ).checkClientTrusted (chain , authType , socket );
239- verifySubjectAltNameInChain (chain );
234+ verifySubjectAltNameInChain (chain , new ArrayList <>() );
240235 }
241236
242237 @ Override
243238 public void checkClientTrusted (X509Certificate [] chain , String authType , SSLEngine sslEngine )
244239 throws CertificateException {
245240 chooseDelegate (chain ).checkClientTrusted (chain , authType , sslEngine );
246- verifySubjectAltNameInChain (chain );
241+ verifySubjectAltNameInChain (chain , new ArrayList <>() );
247242 }
248243
249244 @ Override
@@ -254,39 +249,66 @@ public void checkClientTrusted(X509Certificate[] chain, String authType)
254249 }
255250
256251 @ Override
252+ @ SuppressWarnings ("deprecation" ) // gRFC A29 predates match_typed_subject_alt_names
257253 public void checkServerTrusted (X509Certificate [] chain , String authType , Socket socket )
258254 throws CertificateException {
255+ List <StringMatcher > sniMatchers = null ;
259256 if (socket instanceof SSLSocket ) {
260257 SSLSocket sslSocket = (SSLSocket ) socket ;
261258 SSLParameters sslParams = sslSocket .getSSLParameters ();
262259 if (sslParams != null ) {
263260 sslParams .setEndpointIdentificationAlgorithm ("" );
264261 sslSocket .setSSLParameters (sslParams );
265262 }
263+ sniMatchers = getAutoSniSanMatchers (sslParams );
264+ }
265+ if (sniMatchers .isEmpty ()) {
266+ sniMatchers = certContext .getMatchSubjectAltNamesList ();
266267 }
267268 chooseDelegate (chain ).checkServerTrusted (chain , authType , socket );
268- verifySubjectAltNameInChain (chain );
269+ verifySubjectAltNameInChain (chain , sniMatchers );
269270 }
270271
271272 @ Override
272273 public void checkServerTrusted (X509Certificate [] chain , String authType , SSLEngine sslEngine )
273274 throws CertificateException {
275+ List <StringMatcher > sniMatchers = null ;
274276 SSLParameters sslParams = sslEngine .getSSLParameters ();
275277 if (sslParams != null ) {
276278 sslParams .setEndpointIdentificationAlgorithm ("" );
277279 sslEngine .setSSLParameters (sslParams );
280+ sniMatchers = getAutoSniSanMatchers (sslParams );
281+ }
282+ if (sniMatchers .isEmpty ()) {
283+ sniMatchers = certContext .getMatchSubjectAltNamesList ();
278284 }
279285 chooseDelegate (chain ).checkServerTrusted (chain , authType , sslEngine );
280- verifySubjectAltNameInChain (chain );
286+ verifySubjectAltNameInChain (chain , sniMatchers );
281287 }
282288
283289 @ Override
284290 public void checkServerTrusted (X509Certificate [] chain , String authType )
285291 throws CertificateException {
286292 chooseDelegate (chain ).checkServerTrusted (chain , authType );
287- verifySubjectAltNameInChain (chain );
293+ verifySubjectAltNameInChain (chain , new ArrayList <>() );
288294 }
289295
296+ private List <StringMatcher > getAutoSniSanMatchers (SSLParameters sslParams ) {
297+ List <StringMatcher > sniNamesToMatch = new ArrayList <>();
298+ if (CertificateUtils .isXdsSniEnabled ) {
299+ List <SNIServerName > serverNames = sslParams .getServerNames ();
300+ if (serverNames != null ) {
301+ for (SNIServerName serverName : serverNames ) {
302+ if (serverName instanceof SNIHostName ) {
303+ SNIHostName sniHostName = (SNIHostName ) serverName ;
304+ String hostName = sniHostName .getAsciiName ();
305+ sniNamesToMatch .add (StringMatcher .newBuilder ().setExact (hostName ).build ());
306+ }
307+ }
308+ }
309+ }
310+ return sniNamesToMatch ;
311+ }
290312 private X509ExtendedTrustManager chooseDelegate (X509Certificate [] chain )
291313 throws CertificateException {
292314 if (spiffeTrustMapDelegates != null ) {
@@ -316,4 +338,49 @@ public X509Certificate[] getAcceptedIssuers() {
316338 }
317339 return delegate .getAcceptedIssuers ();
318340 }
341+
342+ private static boolean verifyDnsNameWildcard (
343+ String altNameFromCert , String sanToVerify , boolean ignoreCase ) {
344+ String [] splitPattern = splitAtFirstDelimiter (ignoreCase
345+ ? sanToVerify .toLowerCase (Locale .ROOT ) : sanToVerify );
346+ String [] splitDnsName = splitAtFirstDelimiter (ignoreCase
347+ ? altNameFromCert .toLowerCase (Locale .ROOT ) : altNameFromCert );
348+ if (splitPattern == null || splitDnsName == null ) {
349+ return false ;
350+ }
351+ if (splitDnsName [0 ].startsWith ("xn--" )) {
352+ return false ;
353+ }
354+ if (splitPattern [0 ].contains ("*" )
355+ && !splitPattern [1 ].contains ("*" )
356+ && !splitPattern [0 ].startsWith ("xn--" )) {
357+ return splitDnsName [1 ].equals (splitPattern [1 ])
358+ && labelWildcardMatch (splitDnsName [0 ], splitPattern [0 ]);
359+ }
360+ return false ;
361+ }
362+
363+ private static boolean labelWildcardMatch (String dnsLabel , String pattern ) {
364+ final char glob = '*' ;
365+ // Check the special case of a single * pattern, as it's common.
366+ if (pattern .equals ("*" )) {
367+ return !dnsLabel .isEmpty ();
368+ }
369+ int globIndex = pattern .indexOf (glob );
370+ if (pattern .indexOf (glob , globIndex + 1 ) == -1 ) {
371+ return dnsLabel .length () >= pattern .length () - 1
372+ && dnsLabel .startsWith (pattern .substring (0 , globIndex ))
373+ && dnsLabel .endsWith (pattern .substring (globIndex + 1 ));
374+ }
375+ return false ;
376+ }
377+
378+ @ Nullable
379+ private static String [] splitAtFirstDelimiter (String s ) {
380+ int index = s .indexOf ('.' );
381+ if (index == -1 ) {
382+ return null ;
383+ }
384+ return new String []{s .substring (0 , index ), s .substring (index + 1 )};
385+ }
319386}
0 commit comments