• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.collect.ImmutableList;
22 import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext;
23 import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext;
24 import io.grpc.Internal;
25 import io.grpc.Status;
26 import io.grpc.xds.EnvoyServerProtoData.BaseTlsContext;
27 import io.netty.handler.ssl.ApplicationProtocolConfig;
28 import io.netty.handler.ssl.SslContext;
29 import io.netty.handler.ssl.SslContextBuilder;
30 import java.io.IOException;
31 import java.security.cert.CertStoreException;
32 import java.security.cert.CertificateException;
33 import java.util.ArrayList;
34 import java.util.List;
35 import javax.annotation.Nullable;
36 
37 /** Base class for dynamic {@link SslContextProvider}s. */
38 @Internal
39 public abstract class DynamicSslContextProvider extends SslContextProvider {
40 
41   protected final List<Callback> pendingCallbacks = new ArrayList<>();
42   @Nullable protected final CertificateValidationContext staticCertificateValidationContext;
43   @Nullable protected SslContext sslContext;
44 
DynamicSslContextProvider( BaseTlsContext tlsContext, CertificateValidationContext staticCertValidationContext)45   protected DynamicSslContextProvider(
46       BaseTlsContext tlsContext, CertificateValidationContext staticCertValidationContext) {
47     super(tlsContext);
48     this.staticCertificateValidationContext = staticCertValidationContext;
49   }
50 
51   @Nullable
getSslContext()52   public SslContext getSslContext() {
53     return sslContext;
54   }
55 
generateCertificateValidationContext()56   protected abstract CertificateValidationContext generateCertificateValidationContext();
57 
58   /** Gets a server or client side SslContextBuilder. */
getSslContextBuilder( CertificateValidationContext certificateValidationContext)59   protected abstract SslContextBuilder getSslContextBuilder(
60       CertificateValidationContext certificateValidationContext)
61       throws CertificateException, IOException, CertStoreException;
62 
63   // this gets called only when requested secrets are ready...
updateSslContext()64   protected final void updateSslContext() {
65     try {
66       CertificateValidationContext localCertValidationContext =
67           generateCertificateValidationContext();
68       SslContextBuilder sslContextBuilder = getSslContextBuilder(localCertValidationContext);
69       CommonTlsContext commonTlsContext = getCommonTlsContext();
70       if (commonTlsContext != null && commonTlsContext.getAlpnProtocolsCount() > 0) {
71         List<String> alpnList = commonTlsContext.getAlpnProtocolsList();
72         ApplicationProtocolConfig apn =
73             new ApplicationProtocolConfig(
74                 ApplicationProtocolConfig.Protocol.ALPN,
75                 ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
76                 ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
77                 alpnList);
78         sslContextBuilder.applicationProtocolConfig(apn);
79       }
80       List<Callback> pendingCallbacksCopy;
81       SslContext sslContextCopy;
82       synchronized (pendingCallbacks) {
83         sslContext = sslContextBuilder.build();
84         sslContextCopy = sslContext;
85         pendingCallbacksCopy = clonePendingCallbacksAndClear();
86       }
87       makePendingCallbacks(sslContextCopy, pendingCallbacksCopy);
88     } catch (Exception e) {
89       onError(Status.fromThrowable(e));
90       throw new RuntimeException(e);
91     }
92   }
93 
callPerformCallback( Callback callback, final SslContext sslContextCopy)94   protected final void callPerformCallback(
95           Callback callback, final SslContext sslContextCopy) {
96     performCallback(
97         new SslContextGetter() {
98           @Override
99           public SslContext get() {
100             return sslContextCopy;
101           }
102         },
103         callback
104     );
105   }
106 
107   @Override
addCallback(Callback callback)108   public final void addCallback(Callback callback) {
109     checkNotNull(callback, "callback");
110     // if there is a computed sslContext just send it
111     SslContext sslContextCopy = null;
112     synchronized (pendingCallbacks) {
113       if (sslContext != null) {
114         sslContextCopy = sslContext;
115       } else {
116         pendingCallbacks.add(callback);
117       }
118     }
119     if (sslContextCopy != null) {
120       callPerformCallback(callback, sslContextCopy);
121     }
122   }
123 
makePendingCallbacks( SslContext sslContextCopy, List<Callback> pendingCallbacksCopy)124   private final void makePendingCallbacks(
125       SslContext sslContextCopy, List<Callback> pendingCallbacksCopy) {
126     for (Callback callback : pendingCallbacksCopy) {
127       callPerformCallback(callback, sslContextCopy);
128     }
129   }
130 
131   /** Propagates error to all the callback receivers. */
onError(Status error)132   public final void onError(Status error) {
133     for (Callback callback : clonePendingCallbacksAndClear()) {
134       callback.onException(error.asException());
135     }
136   }
137 
clonePendingCallbacksAndClear()138   private List<Callback> clonePendingCallbacksAndClear() {
139     synchronized (pendingCallbacks) {
140       List<Callback> copy = ImmutableList.copyOf(pendingCallbacks);
141       pendingCallbacks.clear();
142       return copy;
143     }
144   }
145 }
146