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