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