Skip to content

Commit 8fa5a78

Browse files
committed
TrustManager to server name from SslEngine instead of from ProtocolNegotiator.
1 parent 107cbd8 commit 8fa5a78

File tree

5 files changed

+448
-927
lines changed

5 files changed

+448
-927
lines changed

xds/src/main/java/io/grpc/xds/internal/security/certprovider/CertProviderClientSslContextProvider.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ final class CertProviderClientSslContextProvider extends CertProviderSslContextP
6565
sslContextBuilder = sslContextBuilder.trustManager(
6666
new XdsTrustManagerFactory(
6767
savedSpiffeTrustMap,
68-
certificateValidationContext, sniForSanMatching));
68+
certificateValidationContext, ((UpstreamTlsContext) tlsContext).getAutoSniSanValidation()));
6969
} else if (savedTrustedRoots != null) {
7070
sslContextBuilder = sslContextBuilder.trustManager(
7171
new XdsTrustManagerFactory(
7272
savedTrustedRoots.toArray(new X509Certificate[0]),
73-
certificateValidationContext, sniForSanMatching));
73+
certificateValidationContext, ((UpstreamTlsContext) tlsContext).getAutoSniSanValidation()));
7474
} else {
7575
// Should be impossible because of the check in CertProviderClientSslContextProviderFactory
7676
throw new IllegalStateException("There must be trusted roots or a SPIFFE trust map");
@@ -79,12 +79,12 @@ final class CertProviderClientSslContextProvider extends CertProviderSslContextP
7979
if (savedSpiffeTrustMap != null) {
8080
trustManagerFactory = new XdsTrustManagerFactory(
8181
savedSpiffeTrustMap,
82-
certificateValidationContext, sniForSanMatching);
82+
certificateValidationContext, ((UpstreamTlsContext) tlsContext).getAutoSniSanValidation());
8383
sslContextBuilder = sslContextBuilder.trustManager(trustManagerFactory);
8484
} else {
8585
trustManagerFactory = new XdsTrustManagerFactory(
8686
savedTrustedRoots.toArray(new X509Certificate[0]),
87-
certificateValidationContext, sniForSanMatching);
87+
certificateValidationContext, ((UpstreamTlsContext) tlsContext).getAutoSniSanValidation());
8888
sslContextBuilder = sslContextBuilder.trustManager(trustManagerFactory);
8989
}
9090
if (isMtls()) {

xds/src/main/java/io/grpc/xds/internal/security/trust/XdsTrustManagerFactory.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,26 @@ public XdsTrustManagerFactory(CertificateValidationContext certificateValidation
5959
getTrustedCaFromCertContext(certificateValidationContext),
6060
certificateValidationContext,
6161
false,
62-
null);
62+
false);
6363
}
6464

6565
public XdsTrustManagerFactory(
6666
X509Certificate[] certs, CertificateValidationContext staticCertificateValidationContext,
67-
String sniForSanMatching) throws CertStoreException {
68-
this(certs, staticCertificateValidationContext, true, sniForSanMatching);
67+
boolean autoSniSanValidation) throws CertStoreException {
68+
this(certs, staticCertificateValidationContext, true, autoSniSanValidation);
6969
}
7070

7171
public XdsTrustManagerFactory(Map<String, List<X509Certificate>> spiffeTrustMap,
72-
CertificateValidationContext staticCertificateValidationContext, String sniForSanMatching)
72+
CertificateValidationContext staticCertificateValidationContext, boolean autoSniSanValidation)
7373
throws CertStoreException {
74-
this(spiffeTrustMap, staticCertificateValidationContext, true, sniForSanMatching);
74+
this(spiffeTrustMap, staticCertificateValidationContext, true, autoSniSanValidation);
7575
}
7676

7777
private XdsTrustManagerFactory(
7878
X509Certificate[] certs,
7979
CertificateValidationContext certificateValidationContext,
8080
boolean validationContextIsStatic,
81-
String sniForSanMatching)
81+
boolean autoSniSanValidation)
8282
throws CertStoreException {
8383
if (validationContextIsStatic) {
8484
checkArgument(
@@ -87,21 +87,21 @@ private XdsTrustManagerFactory(
8787
"only static certificateValidationContext expected");
8888
}
8989
xdsX509TrustManager = createX509TrustManager(
90-
certs, certificateValidationContext, sniForSanMatching);
90+
certs, certificateValidationContext, autoSniSanValidation);
9191
}
9292

9393
private XdsTrustManagerFactory(
9494
Map<String, List<X509Certificate>> spiffeTrustMap,
9595
CertificateValidationContext certificateValidationContext,
9696
boolean validationContextIsStatic,
97-
String sniForSanMatching)
97+
boolean autoSniSanValidation)
9898
throws CertStoreException {
9999
if (validationContextIsStatic) {
100100
checkArgument(
101101
certificateValidationContext == null || !certificateValidationContext.hasTrustedCa(),
102102
"only static certificateValidationContext expected");
103103
xdsX509TrustManager = createX509TrustManager(
104-
spiffeTrustMap, certificateValidationContext, sniForSanMatching);
104+
spiffeTrustMap, certificateValidationContext, autoSniSanValidation);
105105
}
106106
}
107107

@@ -128,23 +128,24 @@ private static X509Certificate[] getTrustedCaFromCertContext(
128128

129129
@VisibleForTesting
130130
static XdsX509TrustManager createX509TrustManager(
131-
X509Certificate[] certs, CertificateValidationContext certContext, String sniForSanMatching)
131+
X509Certificate[] certs, CertificateValidationContext certContext,
132+
boolean autoSniSanValidation)
132133
throws CertStoreException {
133-
return new XdsX509TrustManager(certContext, createTrustManager(certs), sniForSanMatching);
134+
return new XdsX509TrustManager(certContext, createTrustManager(certs), autoSniSanValidation);
134135
}
135136

136137
@VisibleForTesting
137138
static XdsX509TrustManager createX509TrustManager(
138139
Map<String, List<X509Certificate>> spiffeTrustMapFile,
139-
CertificateValidationContext certContext, String sniForSanMatching)
140+
CertificateValidationContext certContext, boolean autoSniSanValidation)
140141
throws CertStoreException {
141142
checkNotNull(spiffeTrustMapFile, "spiffeTrustMapFile");
142143
Map<String, X509ExtendedTrustManager> delegates = new HashMap<>();
143144
for (Map.Entry<String, List<X509Certificate>> entry:spiffeTrustMapFile.entrySet()) {
144145
delegates.put(entry.getKey(), createTrustManager(
145146
entry.getValue().toArray(new X509Certificate[0])));
146147
}
147-
return new XdsX509TrustManager(certContext, delegates, sniForSanMatching);
148+
return new XdsX509TrustManager(certContext, delegates, autoSniSanValidation);
148149
}
149150

150151
private static X509ExtendedTrustManager createTrustManager(X509Certificate[] certs)

xds/src/main/java/io/grpc/xds/internal/security/trust/XdsX509TrustManager.java

Lines changed: 100 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import com.google.common.annotations.VisibleForTesting;
2222
import com.google.common.base.Optional;
2323
import com.google.common.base.Strings;
24-
import com.google.common.collect.ImmutableList;
2524
import com.google.common.collect.ImmutableMap;
2625
import com.google.re2j.Pattern;
2726
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext;
@@ -32,6 +31,7 @@
3231
import java.security.cert.CertificateException;
3332
import java.security.cert.CertificateParsingException;
3433
import java.security.cert.X509Certificate;
34+
import java.util.ArrayList;
3535
import java.util.Arrays;
3636
import java.util.Collection;
3737
import java.util.HashSet;
@@ -40,6 +40,8 @@
4040
import java.util.Map;
4141
import java.util.Set;
4242
import javax.annotation.Nullable;
43+
import javax.net.ssl.SNIHostName;
44+
import javax.net.ssl.SNIServerName;
4345
import javax.net.ssl.SSLEngine;
4446
import javax.net.ssl.SSLParameters;
4547
import 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

Comments
 (0)