View Javadoc
1   /*
2    * Copyright 2016 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.ssl;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.handler.ssl.util.LazyX509Certificate;
21  import io.netty.internal.tcnative.AsyncSSLPrivateKeyMethod;
22  import io.netty.internal.tcnative.CertificateCompressionAlgo;
23  import io.netty.internal.tcnative.CertificateVerifier;
24  import io.netty.internal.tcnative.ResultCallback;
25  import io.netty.internal.tcnative.SSL;
26  import io.netty.internal.tcnative.SSLContext;
27  import io.netty.internal.tcnative.SSLPrivateKeyMethod;
28  import io.netty.util.AbstractReferenceCounted;
29  import io.netty.util.ReferenceCounted;
30  import io.netty.util.ResourceLeakDetector;
31  import io.netty.util.ResourceLeakDetectorFactory;
32  import io.netty.util.ResourceLeakTracker;
33  import io.netty.util.concurrent.Future;
34  import io.netty.util.concurrent.FutureListener;
35  import io.netty.util.concurrent.ImmediateExecutor;
36  import io.netty.util.internal.EmptyArrays;
37  import io.netty.util.internal.PlatformDependent;
38  import io.netty.util.internal.StringUtil;
39  import io.netty.util.internal.SystemPropertyUtil;
40  import io.netty.util.internal.UnstableApi;
41  import io.netty.util.internal.logging.InternalLogger;
42  import io.netty.util.internal.logging.InternalLoggerFactory;
43  
44  import java.security.PrivateKey;
45  import java.security.SignatureException;
46  import java.security.cert.CertPathValidatorException;
47  import java.security.cert.Certificate;
48  import java.security.cert.CertificateExpiredException;
49  import java.security.cert.CertificateNotYetValidException;
50  import java.security.cert.CertificateRevokedException;
51  import java.security.cert.X509Certificate;
52  import java.util.ArrayList;
53  import java.util.Collections;
54  import java.util.LinkedHashSet;
55  import java.util.List;
56  import java.util.Map;
57  import java.util.concurrent.Executor;
58  import java.util.concurrent.locks.Lock;
59  import java.util.concurrent.locks.ReadWriteLock;
60  import java.util.concurrent.locks.ReentrantReadWriteLock;
61  import javax.net.ssl.KeyManager;
62  import javax.net.ssl.KeyManagerFactory;
63  import javax.net.ssl.SSLEngine;
64  import javax.net.ssl.SSLException;
65  import javax.net.ssl.SSLHandshakeException;
66  import javax.net.ssl.TrustManager;
67  import javax.net.ssl.X509ExtendedTrustManager;
68  import javax.net.ssl.X509KeyManager;
69  import javax.net.ssl.X509TrustManager;
70  
71  import static io.netty.handler.ssl.OpenSsl.DEFAULT_CIPHERS;
72  import static io.netty.handler.ssl.OpenSsl.availableJavaCipherSuites;
73  import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
74  import static io.netty.util.internal.ObjectUtil.checkNotNull;
75  import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
76  
77  /**
78   * An implementation of {@link SslContext} which works with libraries that support the
79   * <a href="https://www.openssl.org/">OpenSsl</a> C library API.
80   * <p>Instances of this class must be {@link #release() released} or else native memory will leak!
81   *
82   * <p>Instances of this class <strong>must not</strong> be released before any {@link ReferenceCountedOpenSslEngine}
83   * which depends upon the instance of this class is released. Otherwise if any method of
84   * {@link ReferenceCountedOpenSslEngine} is called which uses this class's JNI resources the JVM may crash.
85   */
86  public abstract class ReferenceCountedOpenSslContext extends SslContext implements ReferenceCounted {
87      private static final InternalLogger logger =
88              InternalLoggerFactory.getInstance(ReferenceCountedOpenSslContext.class);
89  
90      private static final int DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE = Math.max(1,
91              SystemPropertyUtil.getInt("io.netty.handler.ssl.openssl.bioNonApplicationBufferSize",
92                      2048));
93      // Let's use tasks by default but still allow the user to disable it via system property just in case.
94      static final boolean USE_TASKS =
95              SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.useTasks", true);
96      private static final Integer DH_KEY_LENGTH;
97      private static final ResourceLeakDetector<ReferenceCountedOpenSslContext> leakDetector =
98              ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslContext.class);
99  
100     // TODO: Maybe make configurable ?
101     protected static final int VERIFY_DEPTH = 10;
102 
103     static final boolean CLIENT_ENABLE_SESSION_TICKET =
104             SystemPropertyUtil.getBoolean("jdk.tls.client.enableSessionTicketExtension", false);
105 
106     static final boolean CLIENT_ENABLE_SESSION_TICKET_TLSV13 =
107             SystemPropertyUtil.getBoolean("jdk.tls.client.enableSessionTicketExtension", true);
108 
109     static final boolean SERVER_ENABLE_SESSION_TICKET =
110             SystemPropertyUtil.getBoolean("jdk.tls.server.enableSessionTicketExtension", false);
111 
112      static final boolean SERVER_ENABLE_SESSION_TICKET_TLSV13 =
113             SystemPropertyUtil.getBoolean("jdk.tls.server.enableSessionTicketExtension", true);
114 
115     static final boolean SERVER_ENABLE_SESSION_CACHE =
116             SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.sessionCacheServer", true);
117     static final boolean CLIENT_ENABLE_SESSION_CACHE =
118             SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.sessionCacheClient", true);
119 
120     /**
121      * The OpenSSL SSL_CTX object.
122      *
123      * <strong>{@link #ctxLock} must be hold while using ctx!</strong>
124      */
125     protected long ctx;
126     private final List<String> unmodifiableCiphers;
127     private final OpenSslApplicationProtocolNegotiator apn;
128     private final int mode;
129 
130     // Reference Counting
131     private final ResourceLeakTracker<ReferenceCountedOpenSslContext> leak;
132     private final AbstractReferenceCounted refCnt = new AbstractReferenceCounted() {
133         @Override
134         public ReferenceCounted touch(Object hint) {
135             if (leak != null) {
136                 leak.record(hint);
137             }
138 
139             return ReferenceCountedOpenSslContext.this;
140         }
141 
142         @Override
143         protected void deallocate() {
144             destroy();
145             if (leak != null) {
146                 boolean closed = leak.close(ReferenceCountedOpenSslContext.this);
147                 assert closed;
148             }
149         }
150     };
151 
152     final Certificate[] keyCertChain;
153     final ClientAuth clientAuth;
154     final String[] protocols;
155     final String endpointIdentificationAlgorithm;
156     final boolean hasTLSv13Cipher;
157 
158     final boolean enableOcsp;
159     final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap();
160     final ReadWriteLock ctxLock = new ReentrantReadWriteLock();
161 
162     private volatile int bioNonApplicationBufferSize = DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE;
163 
164     @SuppressWarnings("deprecation")
165     static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR =
166             new OpenSslApplicationProtocolNegotiator() {
167                 @Override
168                 public ApplicationProtocolConfig.Protocol protocol() {
169                     return ApplicationProtocolConfig.Protocol.NONE;
170                 }
171 
172                 @Override
173                 public List<String> protocols() {
174                     return Collections.emptyList();
175                 }
176 
177                 @Override
178                 public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() {
179                     return ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL;
180                 }
181 
182                 @Override
183                 public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
184                     return ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT;
185                 }
186             };
187 
188     static {
189         Integer dhLen = null;
190 
191         try {
192             String dhKeySize = SystemPropertyUtil.get("jdk.tls.ephemeralDHKeySize");
193             if (dhKeySize != null) {
194                 try {
195                     dhLen = Integer.valueOf(dhKeySize);
196                 } catch (NumberFormatException e) {
197                     logger.debug("ReferenceCountedOpenSslContext supports -Djdk.tls.ephemeralDHKeySize={int}, but got: "
198                             + dhKeySize);
199                 }
200             }
201         } catch (Throwable ignore) {
202             // ignore
203         }
204         DH_KEY_LENGTH = dhLen;
205     }
206 
207     final boolean tlsFalseStart;
208 
209     ReferenceCountedOpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
210                                    OpenSslApplicationProtocolNegotiator apn, int mode, Certificate[] keyCertChain,
211                                    ClientAuth clientAuth, String[] protocols, boolean startTls,
212                                    String endpointIdentificationAlgorithm, boolean enableOcsp,
213                                    boolean leakDetection, ResumptionController resumptionController,
214                                    Map.Entry<SslContextOption<?>, Object>... ctxOptions)
215             throws SSLException {
216         super(startTls, resumptionController);
217 
218         OpenSsl.ensureAvailability();
219 
220         if (enableOcsp && !OpenSsl.isOcspSupported()) {
221             throw new IllegalStateException("OCSP is not supported.");
222         }
223 
224         if (mode != SSL.SSL_MODE_SERVER && mode != SSL.SSL_MODE_CLIENT) {
225             throw new IllegalArgumentException("mode most be either SSL.SSL_MODE_SERVER or SSL.SSL_MODE_CLIENT");
226         }
227 
228         boolean tlsFalseStart = false;
229         boolean useTasks = USE_TASKS;
230         OpenSslPrivateKeyMethod privateKeyMethod = null;
231         OpenSslAsyncPrivateKeyMethod asyncPrivateKeyMethod = null;
232         OpenSslCertificateCompressionConfig certCompressionConfig = null;
233         Integer maxCertificateList = null;
234 
235         if (ctxOptions != null) {
236             for (Map.Entry<SslContextOption<?>, Object> ctxOpt : ctxOptions) {
237                 SslContextOption<?> option = ctxOpt.getKey();
238 
239                 if (option == OpenSslContextOption.TLS_FALSE_START) {
240                     tlsFalseStart = (Boolean) ctxOpt.getValue();
241                 } else if (option == OpenSslContextOption.USE_TASKS) {
242                     useTasks = (Boolean) ctxOpt.getValue();
243                 } else if (option == OpenSslContextOption.PRIVATE_KEY_METHOD) {
244                     privateKeyMethod = (OpenSslPrivateKeyMethod) ctxOpt.getValue();
245                 } else if (option == OpenSslContextOption.ASYNC_PRIVATE_KEY_METHOD) {
246                     asyncPrivateKeyMethod = (OpenSslAsyncPrivateKeyMethod) ctxOpt.getValue();
247                 } else if (option == OpenSslContextOption.CERTIFICATE_COMPRESSION_ALGORITHMS) {
248                     certCompressionConfig = (OpenSslCertificateCompressionConfig) ctxOpt.getValue();
249                 } else if (option == OpenSslContextOption.MAX_CERTIFICATE_LIST_BYTES) {
250                     maxCertificateList = (Integer) ctxOpt.getValue();
251                 } else {
252                     logger.debug("Skipping unsupported " + SslContextOption.class.getSimpleName()
253                             + ": " + ctxOpt.getKey());
254                 }
255             }
256         }
257         if (privateKeyMethod != null && asyncPrivateKeyMethod != null) {
258             throw new IllegalArgumentException("You can either only use "
259                     + OpenSslAsyncPrivateKeyMethod.class.getSimpleName() + " or "
260                     + OpenSslPrivateKeyMethod.class.getSimpleName());
261         }
262 
263         this.tlsFalseStart = tlsFalseStart;
264 
265         leak = leakDetection ? leakDetector.track(this) : null;
266         this.mode = mode;
267         this.clientAuth = isServer() ? checkNotNull(clientAuth, "clientAuth") : ClientAuth.NONE;
268         this.protocols = protocols == null ? OpenSsl.defaultProtocols(mode == SSL.SSL_MODE_CLIENT) : protocols;
269         this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
270         this.enableOcsp = enableOcsp;
271 
272         this.keyCertChain = keyCertChain == null ? null : keyCertChain.clone();
273 
274         String[] suites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
275                 ciphers, DEFAULT_CIPHERS, availableJavaCipherSuites());
276         // Filter out duplicates.
277         LinkedHashSet<String> suitesSet = new LinkedHashSet<String>(suites.length);
278         Collections.addAll(suitesSet, suites);
279         unmodifiableCiphers = new ArrayList<String>(suitesSet);
280 
281         this.apn = checkNotNull(apn, "apn");
282 
283         // Create a new SSL_CTX and configure it.
284         boolean success = false;
285         try {
286             boolean tlsv13Supported = OpenSsl.isTlsv13Supported();
287             boolean anyTlsv13Ciphers = false;
288             try {
289                 int protocolOpts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 |
290                         SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2;
291                 if (tlsv13Supported) {
292                     protocolOpts |= SSL.SSL_PROTOCOL_TLSV1_3;
293                 }
294                 ctx = SSLContext.make(protocolOpts, mode);
295             } catch (Exception e) {
296                 throw new SSLException("failed to create an SSL_CTX", e);
297             }
298 
299             StringBuilder cipherBuilder = new StringBuilder();
300             StringBuilder cipherTLSv13Builder = new StringBuilder();
301 
302             /* List the ciphers that are permitted to negotiate. */
303             try {
304                 if (unmodifiableCiphers.isEmpty()) {
305                     // Set non TLSv1.3 ciphers.
306                     SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, false);
307                     if (tlsv13Supported) {
308                         // Set TLSv1.3 ciphers.
309                         SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, true);
310                     }
311                 } else {
312                     CipherSuiteConverter.convertToCipherStrings(
313                             unmodifiableCiphers, cipherBuilder, cipherTLSv13Builder, OpenSsl.isBoringSSL());
314 
315                     // Set non TLSv1.3 ciphers.
316                     SSLContext.setCipherSuite(ctx, cipherBuilder.toString(), false);
317                     if (tlsv13Supported) {
318                         // Set TLSv1.3 ciphers.
319                         String tlsv13Ciphers = OpenSsl.checkTls13Ciphers(logger, cipherTLSv13Builder.toString());
320                         SSLContext.setCipherSuite(ctx, tlsv13Ciphers, true);
321                         if (!tlsv13Ciphers.isEmpty()) {
322                             anyTlsv13Ciphers = true;
323                         }
324                     }
325                 }
326             } catch (SSLException e) {
327                 throw e;
328             } catch (Exception e) {
329                 throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e);
330             }
331 
332             int options = SSLContext.getOptions(ctx) |
333                     SSL.SSL_OP_NO_SSLv2 |
334                     SSL.SSL_OP_NO_SSLv3 |
335                     // Disable TLSv1 and TLSv1.1 by default as these are not considered secure anymore
336                     // and the JDK is doing the same:
337                     // https://www.oracle.com/java/technologies/javase/8u291-relnotes.html
338                     SSL.SSL_OP_NO_TLSv1 |
339                     SSL.SSL_OP_NO_TLSv1_1 |
340 
341                     SSL.SSL_OP_CIPHER_SERVER_PREFERENCE |
342 
343                     // We do not support compression at the moment so we should explicitly disable it.
344                     SSL.SSL_OP_NO_COMPRESSION |
345 
346                     // Disable ticket support by default to be more inline with SSLEngineImpl of the JDK.
347                     // This also let SSLSession.getId() work the same way for the JDK implementation and the
348                     // OpenSSLEngine. If tickets are supported SSLSession.getId() will only return an ID on the
349                     // server-side if it could make use of tickets.
350                     SSL.SSL_OP_NO_TICKET;
351 
352             if (cipherBuilder.length() == 0) {
353                 // No ciphers that are compatible with SSLv2 / SSLv3 / TLSv1 / TLSv1.1 / TLSv1.2
354                 options |= SSL.SSL_OP_NO_SSLv2 | SSL.SSL_OP_NO_SSLv3 | SSL.SSL_OP_NO_TLSv1
355                         | SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2;
356             }
357 
358             if (!tlsv13Supported) {
359                 // Explicit disable TLSv1.3
360                 // See:
361                 //  - https://github.com/netty/netty/issues/12968
362                 options |= SSL.SSL_OP_NO_TLSv1_3;
363             }
364 
365             hasTLSv13Cipher = anyTlsv13Ciphers;
366             SSLContext.setOptions(ctx, options);
367 
368             // We need to enable SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER as the memory address may change between
369             // calling OpenSSLEngine.wrap(...).
370             // See https://github.com/netty/netty-tcnative/issues/100
371             SSLContext.setMode(ctx, SSLContext.getMode(ctx) | SSL.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
372 
373             if (DH_KEY_LENGTH != null) {
374                 SSLContext.setTmpDHLength(ctx, DH_KEY_LENGTH);
375             }
376 
377             List<String> nextProtoList = apn.protocols();
378             /* Set next protocols for next protocol negotiation extension, if specified */
379             if (!nextProtoList.isEmpty()) {
380                 String[] appProtocols = nextProtoList.toArray(EmptyArrays.EMPTY_STRINGS);
381                 int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior());
382 
383                 switch (apn.protocol()) {
384                     case NPN:
385                         SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior);
386                         break;
387                     case ALPN:
388                         SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior);
389                         break;
390                     case NPN_AND_ALPN:
391                         SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior);
392                         SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior);
393                         break;
394                     default:
395                         throw new Error();
396                 }
397             }
398 
399             if (enableOcsp) {
400                 SSLContext.enableOcsp(ctx, isClient());
401             }
402 
403             SSLContext.setUseTasks(ctx, useTasks);
404             if (privateKeyMethod != null) {
405                 SSLContext.setPrivateKeyMethod(ctx, new PrivateKeyMethod(engineMap, privateKeyMethod));
406             }
407             if (asyncPrivateKeyMethod != null) {
408                 SSLContext.setPrivateKeyMethod(ctx, new AsyncPrivateKeyMethod(engineMap, asyncPrivateKeyMethod));
409             }
410             if (certCompressionConfig != null) {
411                 for (OpenSslCertificateCompressionConfig.AlgorithmConfig configPair : certCompressionConfig) {
412                     final CertificateCompressionAlgo algo = new CompressionAlgorithm(engineMap, configPair.algorithm());
413                     switch (configPair.mode()) {
414                         case Decompress:
415                             SSLContext.addCertificateCompressionAlgorithm(
416                                     ctx, SSL.SSL_CERT_COMPRESSION_DIRECTION_DECOMPRESS, algo);
417                             break;
418                         case Compress:
419                             SSLContext.addCertificateCompressionAlgorithm(
420                                     ctx, SSL.SSL_CERT_COMPRESSION_DIRECTION_COMPRESS, algo);
421                             break;
422                         case Both:
423                             SSLContext.addCertificateCompressionAlgorithm(
424                                     ctx, SSL.SSL_CERT_COMPRESSION_DIRECTION_BOTH, algo);
425                             break;
426                         default:
427                             throw new IllegalStateException();
428                     }
429                 }
430             }
431             if (maxCertificateList != null) {
432                 SSLContext.setMaxCertList(ctx, maxCertificateList);
433             }
434             // Set the curves.
435             SSLContext.setCurvesList(ctx, OpenSsl.NAMED_GROUPS);
436             success = true;
437         } finally {
438             if (!success) {
439                 release();
440             }
441         }
442     }
443 
444     private static int opensslSelectorFailureBehavior(ApplicationProtocolConfig.SelectorFailureBehavior behavior) {
445         switch (behavior) {
446             case NO_ADVERTISE:
447                 return SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE;
448             case CHOOSE_MY_LAST_PROTOCOL:
449                 return SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL;
450             default:
451                 throw new Error();
452         }
453     }
454 
455     @Override
456     public final List<String> cipherSuites() {
457         return unmodifiableCiphers;
458     }
459 
460     @Override
461     public ApplicationProtocolNegotiator applicationProtocolNegotiator() {
462         return apn;
463     }
464 
465     @Override
466     public final boolean isClient() {
467         return mode == SSL.SSL_MODE_CLIENT;
468     }
469 
470     @Override
471     public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
472         return newEngine0(alloc, peerHost, peerPort, true);
473     }
474 
475     @Override
476     protected final SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) {
477         return new SslHandler(newEngine0(alloc, null, -1, false), startTls, ImmediateExecutor.INSTANCE,
478                 resumptionController);
479     }
480 
481     @Override
482     protected final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) {
483         return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), startTls, ImmediateExecutor.INSTANCE,
484                 resumptionController);
485     }
486 
487     @Override
488     protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls, Executor executor) {
489         return new SslHandler(newEngine0(alloc, null, -1, false), startTls, executor, resumptionController);
490     }
491 
492     @Override
493     protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort,
494                                     boolean startTls, Executor executor) {
495         return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), false, executor, resumptionController);
496     }
497 
498     SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode) {
499         return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode, true,
500                 endpointIdentificationAlgorithm);
501     }
502 
503     /**
504      * Returns a new server-side {@link SSLEngine} with the current configuration.
505      */
506     @Override
507     public final SSLEngine newEngine(ByteBufAllocator alloc) {
508         return newEngine(alloc, null, -1);
509     }
510 
511     /**
512      * Returns the pointer to the {@code SSL_CTX} object for this {@link ReferenceCountedOpenSslContext}.
513      * Be aware that it is freed as soon as the {@link #finalize()}  method is called.
514      * At this point {@code 0} will be returned.
515      *
516      * @deprecated this method is considered unsafe as the returned pointer may be released later. Dont use it!
517      */
518     @Deprecated
519     public final long context() {
520         return sslCtxPointer();
521     }
522 
523     /**
524      * Returns the stats of this context.
525      *
526      * @deprecated use {@link #sessionContext#stats()}
527      */
528     @Deprecated
529     public final OpenSslSessionStats stats() {
530         return sessionContext().stats();
531     }
532 
533     /**
534      * {@deprecated Renegotiation is not supported}
535      * Specify if remote initiated renegotiation is supported or not. If not supported and the remote side tries
536      * to initiate a renegotiation a {@link SSLHandshakeException} will be thrown during decoding.
537      */
538     @Deprecated
539     public void setRejectRemoteInitiatedRenegotiation(boolean rejectRemoteInitiatedRenegotiation) {
540         if (!rejectRemoteInitiatedRenegotiation) {
541             throw new UnsupportedOperationException("Renegotiation is not supported");
542         }
543     }
544 
545     /**
546      * {@deprecated Renegotiation is not supported}
547      * @return {@code true} because renegotiation is not supported.
548      */
549     @Deprecated
550     public boolean getRejectRemoteInitiatedRenegotiation() {
551         return true;
552     }
553 
554     /**
555      * Set the size of the buffer used by the BIO for non-application based writes
556      * (e.g. handshake, renegotiation, etc...).
557      */
558     public void setBioNonApplicationBufferSize(int bioNonApplicationBufferSize) {
559         this.bioNonApplicationBufferSize =
560                 checkPositiveOrZero(bioNonApplicationBufferSize, "bioNonApplicationBufferSize");
561     }
562 
563     /**
564      * Returns the size of the buffer used by the BIO for non-application based writes
565      */
566     public int getBioNonApplicationBufferSize() {
567         return bioNonApplicationBufferSize;
568     }
569 
570     /**
571      * Sets the SSL session ticket keys of this context.
572      *
573      * @deprecated use {@link OpenSslSessionContext#setTicketKeys(byte[])}
574      */
575     @Deprecated
576     public final void setTicketKeys(byte[] keys) {
577         sessionContext().setTicketKeys(keys);
578     }
579 
580     @Override
581     public abstract OpenSslSessionContext sessionContext();
582 
583     /**
584      * Returns the pointer to the {@code SSL_CTX} object for this {@link ReferenceCountedOpenSslContext}.
585      * Be aware that it is freed as soon as the {@link #release()} method is called.
586      * At this point {@code 0} will be returned.
587      *
588      * @deprecated this method is considered unsafe as the returned pointer may be released later. Dont use it!
589      */
590     @Deprecated
591     public final long sslCtxPointer() {
592         Lock readerLock = ctxLock.readLock();
593         readerLock.lock();
594         try {
595             return SSLContext.getSslCtx(ctx);
596         } finally {
597             readerLock.unlock();
598         }
599     }
600 
601     /**
602      * Set the {@link OpenSslPrivateKeyMethod} to use. This allows to offload private-key operations
603      * if needed.
604      *
605      * This method is currently only supported when {@code BoringSSL} is used.
606      *
607      * @param        method method to use.
608      * @deprecated   use {@link SslContextBuilder#option(SslContextOption, Object)} with
609      *              {@link OpenSslContextOption#PRIVATE_KEY_METHOD}.
610      */
611     @Deprecated
612     @UnstableApi
613     public final void setPrivateKeyMethod(OpenSslPrivateKeyMethod method) {
614         checkNotNull(method, "method");
615         Lock writerLock = ctxLock.writeLock();
616         writerLock.lock();
617         try {
618             SSLContext.setPrivateKeyMethod(ctx, new PrivateKeyMethod(engineMap, method));
619         } finally {
620             writerLock.unlock();
621         }
622     }
623 
624     /**
625      * @deprecated   use {@link SslContextBuilder#option(SslContextOption, Object)} with
626      *              {@link OpenSslContextOption#USE_TASKS}.
627      */
628     @Deprecated
629     public final void setUseTasks(boolean useTasks) {
630         Lock writerLock = ctxLock.writeLock();
631         writerLock.lock();
632         try {
633             SSLContext.setUseTasks(ctx, useTasks);
634         } finally {
635             writerLock.unlock();
636         }
637     }
638 
639     // IMPORTANT: This method must only be called from either the constructor or the finalizer as a user MUST never
640     //            get access to an OpenSslSessionContext after this method was called to prevent the user from
641     //            producing a segfault.
642     private void destroy() {
643         Lock writerLock = ctxLock.writeLock();
644         writerLock.lock();
645         try {
646             if (ctx != 0) {
647                 if (enableOcsp) {
648                     SSLContext.disableOcsp(ctx);
649                 }
650 
651                 SSLContext.free(ctx);
652                 ctx = 0;
653 
654                 OpenSslSessionContext context = sessionContext();
655                 if (context != null) {
656                     context.destroy();
657                 }
658             }
659         } finally {
660             writerLock.unlock();
661         }
662     }
663 
664     protected static X509Certificate[] certificates(byte[][] chain) {
665         X509Certificate[] peerCerts = new X509Certificate[chain.length];
666         for (int i = 0; i < peerCerts.length; i++) {
667             peerCerts[i] = new LazyX509Certificate(chain[i]);
668         }
669         return peerCerts;
670     }
671 
672     /**
673      * @deprecated This method is kept for API backwards compatibility.
674      */
675     @Deprecated
676     protected static X509TrustManager chooseTrustManager(TrustManager[] managers) {
677         return chooseTrustManager(managers, null);
678     }
679 
680     static X509TrustManager chooseTrustManager(TrustManager[] managers,
681                                                          ResumptionController resumptionController) {
682         for (TrustManager m : managers) {
683             if (m instanceof X509TrustManager) {
684                 X509TrustManager tm = (X509TrustManager) m;
685                 if (resumptionController != null) {
686                     tm = (X509TrustManager) resumptionController.wrapIfNeeded(tm);
687                 }
688                 tm = OpenSslX509TrustManagerWrapper.wrapIfNeeded((X509TrustManager) m);
689                 if (useExtendedTrustManager(tm)) {
690                     // Wrap the TrustManager to provide a better exception message for users to debug hostname
691                     // validation failures.
692                     tm = new EnhancingX509ExtendedTrustManager(tm);
693                 }
694                 return tm;
695             }
696         }
697         throw new IllegalStateException("no X509TrustManager found");
698     }
699 
700     protected static X509KeyManager chooseX509KeyManager(KeyManager[] kms) {
701         for (KeyManager km : kms) {
702             if (km instanceof X509KeyManager) {
703                 return (X509KeyManager) km;
704             }
705         }
706         throw new IllegalStateException("no X509KeyManager found");
707     }
708 
709     /**
710      * Translate a {@link ApplicationProtocolConfig} object to a
711      * {@link OpenSslApplicationProtocolNegotiator} object.
712      *
713      * @param config The configuration which defines the translation
714      * @return The results of the translation
715      */
716     @SuppressWarnings("deprecation")
717     static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config) {
718         if (config == null) {
719             return NONE_PROTOCOL_NEGOTIATOR;
720         }
721 
722         switch (config.protocol()) {
723             case NONE:
724                 return NONE_PROTOCOL_NEGOTIATOR;
725             case ALPN:
726             case NPN:
727             case NPN_AND_ALPN:
728                 switch (config.selectedListenerFailureBehavior()) {
729                     case CHOOSE_MY_LAST_PROTOCOL:
730                     case ACCEPT:
731                         switch (config.selectorFailureBehavior()) {
732                             case CHOOSE_MY_LAST_PROTOCOL:
733                             case NO_ADVERTISE:
734                                 return new OpenSslDefaultApplicationProtocolNegotiator(
735                                         config);
736                             default:
737                                 throw new UnsupportedOperationException(
738                                         new StringBuilder("OpenSSL provider does not support ")
739                                                 .append(config.selectorFailureBehavior())
740                                                 .append(" behavior").toString());
741                         }
742                     default:
743                         throw new UnsupportedOperationException(
744                                 new StringBuilder("OpenSSL provider does not support ")
745                                         .append(config.selectedListenerFailureBehavior())
746                                         .append(" behavior").toString());
747                 }
748             default:
749                 throw new Error();
750         }
751     }
752 
753     static boolean useExtendedTrustManager(X509TrustManager trustManager) {
754         return trustManager instanceof X509ExtendedTrustManager;
755     }
756 
757     @Override
758     public final int refCnt() {
759         return refCnt.refCnt();
760     }
761 
762     @Override
763     public final ReferenceCounted retain() {
764         refCnt.retain();
765         return this;
766     }
767 
768     @Override
769     public final ReferenceCounted retain(int increment) {
770         refCnt.retain(increment);
771         return this;
772     }
773 
774     @Override
775     public final ReferenceCounted touch() {
776         refCnt.touch();
777         return this;
778     }
779 
780     @Override
781     public final ReferenceCounted touch(Object hint) {
782         refCnt.touch(hint);
783         return this;
784     }
785 
786     @Override
787     public final boolean release() {
788         return refCnt.release();
789     }
790 
791     @Override
792     public final boolean release(int decrement) {
793         return refCnt.release(decrement);
794     }
795 
796     abstract static class AbstractCertificateVerifier extends CertificateVerifier {
797         private final OpenSslEngineMap engineMap;
798 
799         AbstractCertificateVerifier(OpenSslEngineMap engineMap) {
800             this.engineMap = engineMap;
801         }
802 
803         @Override
804         public final int verify(long ssl, byte[][] chain, String auth) {
805             final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
806             if (engine == null) {
807                 // May be null if it was destroyed in the meantime.
808                 return CertificateVerifier.X509_V_ERR_UNSPECIFIED;
809             }
810             X509Certificate[] peerCerts = certificates(chain);
811             try {
812                 verify(engine, peerCerts, auth);
813                 return CertificateVerifier.X509_V_OK;
814             } catch (Throwable cause) {
815                 logger.debug("verification of certificate failed", cause);
816                 engine.initHandshakeException(cause);
817 
818                 // Try to extract the correct error code that should be used.
819                 if (cause instanceof OpenSslCertificateException) {
820                     // This will never return a negative error code as its validated when constructing the
821                     // OpenSslCertificateException.
822                     return ((OpenSslCertificateException) cause).errorCode();
823                 }
824                 if (cause instanceof CertificateExpiredException) {
825                     return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED;
826                 }
827                 if (cause instanceof CertificateNotYetValidException) {
828                     return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID;
829                 }
830                 return translateToError(cause);
831             }
832         }
833 
834         private static int translateToError(Throwable cause) {
835             if (cause instanceof CertificateRevokedException) {
836                 return CertificateVerifier.X509_V_ERR_CERT_REVOKED;
837             }
838 
839             // The X509TrustManagerImpl uses a Validator which wraps a CertPathValidatorException into
840             // an CertificateException. So we need to handle the wrapped CertPathValidatorException to be
841             // able to send the correct alert.
842             Throwable wrapped = cause.getCause();
843             while (wrapped != null) {
844                 if (wrapped instanceof CertPathValidatorException) {
845                     CertPathValidatorException ex = (CertPathValidatorException) wrapped;
846                     CertPathValidatorException.Reason reason = ex.getReason();
847                     if (reason == CertPathValidatorException.BasicReason.EXPIRED) {
848                         return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED;
849                     }
850                     if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) {
851                         return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID;
852                     }
853                     if (reason == CertPathValidatorException.BasicReason.REVOKED) {
854                         return CertificateVerifier.X509_V_ERR_CERT_REVOKED;
855                     }
856                 }
857                 wrapped = wrapped.getCause();
858             }
859             return CertificateVerifier.X509_V_ERR_UNSPECIFIED;
860         }
861 
862         abstract void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts,
863                              String auth) throws Exception;
864     }
865 
866     private static final class DefaultOpenSslEngineMap implements OpenSslEngineMap {
867         private final Map<Long, ReferenceCountedOpenSslEngine> engines = PlatformDependent.newConcurrentHashMap();
868 
869         @Override
870         public ReferenceCountedOpenSslEngine remove(long ssl) {
871             return engines.remove(ssl);
872         }
873 
874         @Override
875         public void add(ReferenceCountedOpenSslEngine engine) {
876             engines.put(engine.sslPointer(), engine);
877         }
878 
879         @Override
880         public ReferenceCountedOpenSslEngine get(long ssl) {
881             return engines.get(ssl);
882         }
883     }
884 
885     static void setKeyMaterial(long ctx, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword)
886             throws SSLException {
887          /* Load the certificate file and private key. */
888         long keyBio = 0;
889         long keyCertChainBio = 0;
890         long keyCertChainBio2 = 0;
891         PemEncoded encoded = null;
892         try {
893             // Only encode one time
894             encoded = PemX509Certificate.toPEM(ByteBufAllocator.DEFAULT, true, keyCertChain);
895             keyCertChainBio = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
896             keyCertChainBio2 = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
897 
898             if (key != null) {
899                 keyBio = toBIO(ByteBufAllocator.DEFAULT, key);
900             }
901 
902             SSLContext.setCertificateBio(
903                     ctx, keyCertChainBio, keyBio,
904                     keyPassword == null ? StringUtil.EMPTY_STRING : keyPassword);
905             // We may have more then one cert in the chain so add all of them now.
906             SSLContext.setCertificateChainBio(ctx, keyCertChainBio2, true);
907         } catch (SSLException e) {
908             throw e;
909         } catch (Exception e) {
910             throw new SSLException("failed to set certificate and key", e);
911         } finally {
912             freeBio(keyBio);
913             freeBio(keyCertChainBio);
914             freeBio(keyCertChainBio2);
915             if (encoded != null) {
916                 encoded.release();
917             }
918         }
919     }
920 
921     static void freeBio(long bio) {
922         if (bio != 0) {
923             SSL.freeBIO(bio);
924         }
925     }
926 
927     /**
928      * Return the pointer to a <a href="https://www.openssl.org/docs/crypto/BIO_get_mem_ptr.html">in-memory BIO</a>
929      * or {@code 0} if the {@code key} is {@code null}. The BIO contains the content of the {@code key}.
930      */
931     static long toBIO(ByteBufAllocator allocator, PrivateKey key) throws Exception {
932         if (key == null) {
933             return 0;
934         }
935 
936         PemEncoded pem = PemPrivateKey.toPEM(allocator, true, key);
937         try {
938             return toBIO(allocator, pem.retain());
939         } finally {
940             pem.release();
941         }
942     }
943 
944     /**
945      * Return the pointer to a <a href="https://www.openssl.org/docs/crypto/BIO_get_mem_ptr.html">in-memory BIO</a>
946      * or {@code 0} if the {@code certChain} is {@code null}. The BIO contains the content of the {@code certChain}.
947      */
948     static long toBIO(ByteBufAllocator allocator, X509Certificate... certChain) throws Exception {
949         if (certChain == null) {
950             return 0;
951         }
952 
953         checkNonEmpty(certChain, "certChain");
954 
955         PemEncoded pem = PemX509Certificate.toPEM(allocator, true, certChain);
956         try {
957             return toBIO(allocator, pem.retain());
958         } finally {
959             pem.release();
960         }
961     }
962 
963     static long toBIO(ByteBufAllocator allocator, PemEncoded pem) throws Exception {
964         try {
965             // We can turn direct buffers straight into BIOs. No need to
966             // make a yet another copy.
967             ByteBuf content = pem.content();
968 
969             if (content.isDirect()) {
970                 return newBIO(content.retainedSlice());
971             }
972 
973             ByteBuf buffer = allocator.directBuffer(content.readableBytes());
974             try {
975                 buffer.writeBytes(content, content.readerIndex(), content.readableBytes());
976                 return newBIO(buffer.retainedSlice());
977             } finally {
978                 try {
979                     // If the contents of the ByteBuf is sensitive (e.g. a PrivateKey) we
980                     // need to zero out the bytes of the copy before we're releasing it.
981                     if (pem.isSensitive()) {
982                         SslUtils.zeroout(buffer);
983                     }
984                 } finally {
985                     buffer.release();
986                 }
987             }
988         } finally {
989             pem.release();
990         }
991     }
992 
993     private static long newBIO(ByteBuf buffer) throws Exception {
994         try {
995             long bio = SSL.newMemBIO();
996             int readable = buffer.readableBytes();
997             if (SSL.bioWrite(bio, OpenSsl.memoryAddress(buffer) + buffer.readerIndex(), readable) != readable) {
998                 SSL.freeBIO(bio);
999                 throw new IllegalStateException("Could not write data to memory BIO");
1000             }
1001             return bio;
1002         } finally {
1003             buffer.release();
1004         }
1005     }
1006 
1007     /**
1008      * Returns the {@link OpenSslKeyMaterialProvider} that should be used for OpenSSL. Depending on the given
1009      * {@link KeyManagerFactory} this may cache the {@link OpenSslKeyMaterial} for better performance if it can
1010      * ensure that the same material is always returned for the same alias.
1011      */
1012     static OpenSslKeyMaterialProvider providerFor(KeyManagerFactory factory, String password) {
1013         if (factory instanceof OpenSslX509KeyManagerFactory) {
1014             return ((OpenSslX509KeyManagerFactory) factory).newProvider();
1015         }
1016 
1017         if (factory instanceof OpenSslCachingX509KeyManagerFactory) {
1018             // The user explicit used OpenSslCachingX509KeyManagerFactory which signals us that its fine to cache.
1019             return ((OpenSslCachingX509KeyManagerFactory) factory).newProvider(password);
1020         }
1021         // We can not be sure if the material may change at runtime so we will not cache it.
1022         return new OpenSslKeyMaterialProvider(chooseX509KeyManager(factory.getKeyManagers()), password);
1023     }
1024 
1025     private static ReferenceCountedOpenSslEngine retrieveEngine(OpenSslEngineMap engineMap, long ssl)
1026             throws SSLException {
1027         ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
1028         if (engine == null) {
1029             throw new SSLException("Could not find a " +
1030                     StringUtil.simpleClassName(ReferenceCountedOpenSslEngine.class) + " for sslPointer " + ssl);
1031         }
1032         return engine;
1033     }
1034 
1035     private static final class PrivateKeyMethod implements SSLPrivateKeyMethod {
1036 
1037         private final OpenSslEngineMap engineMap;
1038         private final OpenSslPrivateKeyMethod keyMethod;
1039         PrivateKeyMethod(OpenSslEngineMap engineMap, OpenSslPrivateKeyMethod keyMethod) {
1040             this.engineMap = engineMap;
1041             this.keyMethod = keyMethod;
1042         }
1043 
1044         @Override
1045         public byte[] sign(long ssl, int signatureAlgorithm, byte[] digest) throws Exception {
1046             ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1047             try {
1048                 return verifyResult(keyMethod.sign(engine, signatureAlgorithm, digest));
1049             } catch (Exception e) {
1050                 engine.initHandshakeException(e);
1051                 throw e;
1052             }
1053         }
1054 
1055         @Override
1056         public byte[] decrypt(long ssl, byte[] input) throws Exception {
1057             ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1058             try {
1059                 return verifyResult(keyMethod.decrypt(engine, input));
1060             } catch (Exception e) {
1061                 engine.initHandshakeException(e);
1062                 throw e;
1063             }
1064         }
1065     }
1066 
1067     private static final class AsyncPrivateKeyMethod implements AsyncSSLPrivateKeyMethod {
1068 
1069         private final OpenSslEngineMap engineMap;
1070         private final OpenSslAsyncPrivateKeyMethod keyMethod;
1071 
1072         AsyncPrivateKeyMethod(OpenSslEngineMap engineMap, OpenSslAsyncPrivateKeyMethod keyMethod) {
1073             this.engineMap = engineMap;
1074             this.keyMethod = keyMethod;
1075         }
1076 
1077         @Override
1078         public void sign(long ssl, int signatureAlgorithm, byte[] bytes, ResultCallback<byte[]> resultCallback) {
1079             try {
1080                 ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1081                 keyMethod.sign(engine, signatureAlgorithm, bytes)
1082                         .addListener(new ResultCallbackListener(engine, ssl, resultCallback));
1083             } catch (SSLException e) {
1084                 resultCallback.onError(ssl, e);
1085             }
1086         }
1087 
1088         @Override
1089         public void decrypt(long ssl, byte[] bytes, ResultCallback<byte[]> resultCallback) {
1090             try {
1091                 ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1092                 keyMethod.decrypt(engine, bytes)
1093                         .addListener(new ResultCallbackListener(engine, ssl, resultCallback));
1094             } catch (SSLException e) {
1095                 resultCallback.onError(ssl, e);
1096             }
1097         }
1098 
1099         private static final class ResultCallbackListener implements FutureListener<byte[]> {
1100             private final ReferenceCountedOpenSslEngine engine;
1101             private final long ssl;
1102             private final ResultCallback<byte[]> resultCallback;
1103 
1104             ResultCallbackListener(ReferenceCountedOpenSslEngine engine, long ssl,
1105                                    ResultCallback<byte[]> resultCallback) {
1106                 this.engine = engine;
1107                 this.ssl = ssl;
1108                 this.resultCallback = resultCallback;
1109             }
1110 
1111             @Override
1112             public void operationComplete(Future<byte[]> future) {
1113                 Throwable cause = future.cause();
1114                 if (cause == null) {
1115                     try {
1116                         byte[] result = verifyResult(future.getNow());
1117                         resultCallback.onSuccess(ssl, result);
1118                         return;
1119                     } catch (SignatureException e) {
1120                         cause = e;
1121                         engine.initHandshakeException(e);
1122                     }
1123                 }
1124                 resultCallback.onError(ssl, cause);
1125             }
1126         }
1127     }
1128 
1129     private static byte[] verifyResult(byte[] result) throws SignatureException {
1130         if (result == null) {
1131             throw new SignatureException();
1132         }
1133         return result;
1134     }
1135 
1136     private static final class CompressionAlgorithm implements CertificateCompressionAlgo {
1137         private final OpenSslEngineMap engineMap;
1138         private final OpenSslCertificateCompressionAlgorithm compressionAlgorithm;
1139 
1140         CompressionAlgorithm(OpenSslEngineMap engineMap, OpenSslCertificateCompressionAlgorithm compressionAlgorithm) {
1141             this.engineMap = engineMap;
1142             this.compressionAlgorithm = compressionAlgorithm;
1143         }
1144 
1145         @Override
1146         public byte[] compress(long ssl, byte[] bytes) throws Exception {
1147             ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1148             return compressionAlgorithm.compress(engine, bytes);
1149         }
1150 
1151         @Override
1152         public byte[] decompress(long ssl, int len, byte[] bytes) throws Exception {
1153             ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1154             return compressionAlgorithm.decompress(engine, len, bytes);
1155         }
1156 
1157         @Override
1158         public int algorithmId() {
1159             return compressionAlgorithm.algorithmId();
1160         }
1161     }
1162 }