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