• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #20254

29 Apr 2026 05:08PM UTC coverage: 88.827% (+0.01%) from 88.814%
#20254

push

github

web-flow
xds: Trust Manager fix for when SAN validation against SNI sent doesn't apply (#12775)

Fixes a bug in propagation of `autoSniSanValidationDoesNotApply` (from
PR #12422). It added an argument `autoSniSanValidationDoesNotApply` to
`SslContextProviderSupplier.updateSslContext` that sets it on the
`DynamicSslContextProvider` but because `UpstreamTlsContext` equals
wasn't implemented, it was getting replaced by a new instance and the
flag getting lost. This issue was identified when fixing an incorrect
merge caused error in `CertProviderClientSslContextProvider` that
recreated the trust manager without consideration to
`autoSniSanValidationDoesNotApply`. It ought to have caused failure in
the test
`XdsSecurityClientServerTest.tlsClientServer_autoSniValidation_noSniApplicable_usesMatcherFromCmnVdnCtx`
but it wasn't, because even though `autoSniSanValidationDoesNotApply`
was false due to not getting the propagated true value, SAN matcher
fallback was still happening because there was no server SNI sent.

With the new changes, in addition to fixing the equals method, by moving
the decision about autoSniSanValidationDoesNotApply to
TlsContextManagerImpl.findOrCreateClientSslContextProvider I have
eliminated the need to have a deferred setting of this decision via
DynamicSslContextProvider.setAutoSniSanValidationDoesNotApply called
from SslContextProviderSupplier.updateSslContext.

Summary of Changes:

   1. Enhanced `UpstreamTlsContext` (EnvoyServerProtoData.java):
* Modified Caching Behavior: Implemented full equals() and hashCode()
overrides for `UpstreamTlsContext`. Previously, it relied on the base
class which only compared the commonTlsContext, causing different SNI or
auto-validation settings to incorrectly share the same cache entry.
* Normalization: Updated constructors to normalize the sni field to an
empty string ("") if null. This prevents equality mismatches between
context objects created from different sources (e.g., test helpers vs.
Envoy proto... (continued)

36141 of 40687 relevant lines covered (88.83%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.36
/../xds/src/main/java/io/grpc/xds/internal/security/SslContextProviderSupplier.java
1
/*
2
 * Copyright 2020 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.xds.internal.security;
18

19
import static com.google.common.base.Preconditions.checkNotNull;
20

21
import com.google.common.annotations.VisibleForTesting;
22
import com.google.common.base.MoreObjects;
23
import io.grpc.xds.EnvoyServerProtoData.BaseTlsContext;
24
import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext;
25
import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext;
26
import io.grpc.xds.TlsContextManager;
27
import io.netty.handler.ssl.SslContext;
28
import java.util.AbstractMap;
29
import java.util.Objects;
30
import javax.net.ssl.X509TrustManager;
31

32
/**
33
 * Enables Client or server side to initialize this object with the received {@link BaseTlsContext}
34
 * and communicate it to the consumer i.e. {@link SecurityProtocolNegotiators}
35
 * to lazily evaluate the {@link SslContextProvider}. The supplier prevents credentials leakage in
36
 * cases where the user is not using xDS credentials but the client/server contains a non-default
37
 * {@link BaseTlsContext}.
38
 */
39
public final class SslContextProviderSupplier implements Closeable {
40

41
  private final BaseTlsContext tlsContext;
42
  private final TlsContextManager tlsContextManager;
43
  private SslContextProvider sslContextProvider;
44
  private boolean shutdown;
45

46
  public SslContextProviderSupplier(
47
      BaseTlsContext tlsContext, TlsContextManager tlsContextManager) {
1✔
48
    this.tlsContext = checkNotNull(tlsContext, "tlsContext");
1✔
49
    this.tlsContextManager = checkNotNull(tlsContextManager, "tlsContextManager");
1✔
50
  }
1✔
51

52
  public BaseTlsContext getTlsContext() {
53
    return tlsContext;
1✔
54
  }
55

56
  /** Updates SslContext via the passed callback. */
57
  public synchronized void updateSslContext(
58
      final SslContextProvider.Callback callback, boolean autoSniSanValidationDoesNotApply) {
59
    checkNotNull(callback, "callback");
1✔
60
    try {
61
      if (!shutdown) {
1✔
62
        if (sslContextProvider == null) {
1✔
63
          sslContextProvider = getSslContextProvider(autoSniSanValidationDoesNotApply);
1✔
64
        }
65
      }
66
      // we want to increment the ref-count so call findOrCreate again...
67
      final SslContextProvider toRelease = getSslContextProvider(autoSniSanValidationDoesNotApply);
1✔
68
      toRelease.addCallback(
1✔
69
          new SslContextProvider.Callback(callback.getExecutor()) {
1✔
70

71
            @Override
72
            public void updateSslContextAndExtendedX509TrustManager(
73
                AbstractMap.SimpleImmutableEntry<SslContext, X509TrustManager> sslContextAndTm) {
74
              callback.updateSslContextAndExtendedX509TrustManager(sslContextAndTm);
1✔
75
              releaseSslContextProvider(toRelease);
1✔
76
            }
1✔
77

78
            @Override
79
            public void onException(Throwable throwable) {
80
              callback.onException(throwable);
1✔
81
              releaseSslContextProvider(toRelease);
1✔
82
            }
1✔
83
          });
84
    } catch (final Throwable throwable) {
×
85
      callback.getExecutor().execute(new Runnable() {
×
86
        @Override
87
        public void run() {
88
          callback.onException(throwable);
×
89
        }
×
90
      });
91
    }
1✔
92
  }
1✔
93

94
  private void releaseSslContextProvider(SslContextProvider toRelease) {
95
    if (tlsContext instanceof UpstreamTlsContext) {
1✔
96
      tlsContextManager.releaseClientSslContextProvider(toRelease);
1✔
97
    } else {
98
      tlsContextManager.releaseServerSslContextProvider(toRelease);
1✔
99
    }
100
  }
1✔
101

102
  private SslContextProvider getSslContextProvider(boolean autoSniSanValidationDoesNotApply) {
103
    if (tlsContext instanceof UpstreamTlsContext) {
1✔
104
      UpstreamTlsContext upstreamTlsContext = (UpstreamTlsContext) tlsContext;
1✔
105
      if (autoSniSanValidationDoesNotApply && upstreamTlsContext.getAutoSniSanValidation()) {
1✔
106
        upstreamTlsContext = new UpstreamTlsContext(
1✔
107
            upstreamTlsContext.getCommonTlsContext(),
1✔
108
            upstreamTlsContext.getSni(),
1✔
109
            upstreamTlsContext.getAutoHostSni(),
1✔
110
            false);
111
      }
112
      return tlsContextManager.findOrCreateClientSslContextProvider(upstreamTlsContext);
1✔
113
    }
114
    return tlsContextManager.findOrCreateServerSslContextProvider(
1✔
115
        (DownstreamTlsContext) tlsContext);
116
  }
117

118
  @VisibleForTesting public boolean isShutdown() {
119
    return shutdown;
1✔
120
  }
121

122
  /** Called by consumer when tlsContext changes. */
123
  @Override
124
  public synchronized void close() {
125
    if (sslContextProvider != null) {
1✔
126
      if (tlsContext instanceof UpstreamTlsContext) {
1✔
127
        tlsContextManager.releaseClientSslContextProvider(sslContextProvider);
1✔
128
      } else {
129
        tlsContextManager.releaseServerSslContextProvider(sslContextProvider);
1✔
130
      }
131
    }
132
    sslContextProvider = null;
1✔
133
    shutdown = true;
1✔
134
  }
1✔
135

136
  @Override
137
  public boolean equals(Object o) {
138
    if (this == o) {
×
139
      return true;
×
140
    }
141
    if (o == null || getClass() != o.getClass()) {
×
142
      return false;
×
143
    }
144
    SslContextProviderSupplier that = (SslContextProviderSupplier) o;
×
145
    return Objects.equals(tlsContext, that.tlsContext)
×
146
        && Objects.equals(tlsContextManager, that.tlsContextManager);
×
147
  }
148

149
  @Override
150
  public int hashCode() {
151
    return Objects.hash(tlsContext, tlsContextManager);
1✔
152
  }
153

154
  @Override
155
  public String toString() {
156
    return MoreObjects.toStringHelper(this)
1✔
157
        .add("tlsContext", tlsContext)
1✔
158
        .add("tlsContextManager", tlsContextManager)
1✔
159
        .add("sslContextProvider", sslContextProvider)
1✔
160
        .add("shutdown", shutdown)
1✔
161
        .toString();
1✔
162
  }
163
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc