View Javadoc
1   /*
2    * Copyright 2014 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    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, 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.buffer.Unpooled;
21  import io.netty.util.internal.EmptyArrays;
22  import io.netty.util.internal.PlatformDependent;
23  import io.netty.util.internal.logging.InternalLogger;
24  import io.netty.util.internal.logging.InternalLoggerFactory;
25  import org.apache.tomcat.jni.Buffer;
26  import org.apache.tomcat.jni.SSL;
27  
28  import javax.net.ssl.SSLEngine;
29  import javax.net.ssl.SSLEngineResult;
30  import javax.net.ssl.SSLException;
31  import javax.net.ssl.SSLPeerUnverifiedException;
32  import javax.net.ssl.SSLSession;
33  import javax.net.ssl.SSLSessionBindingEvent;
34  import javax.net.ssl.SSLSessionBindingListener;
35  import javax.net.ssl.SSLSessionContext;
36  import javax.security.cert.CertificateException;
37  import javax.security.cert.X509Certificate;
38  import java.nio.ByteBuffer;
39  import java.nio.ReadOnlyBufferException;
40  import java.security.Principal;
41  import java.security.cert.Certificate;
42  import java.util.ArrayList;
43  import java.util.Arrays;
44  import java.util.HashMap;
45  import java.util.HashSet;
46  import java.util.List;
47  import java.util.Map;
48  import java.util.Set;
49  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
50  import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
51  
52  import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
53  import static javax.net.ssl.SSLEngineResult.Status.*;
54  
55  /**
56   * Implements a {@link SSLEngine} using
57   * <a href="https://www.openssl.org/docs/crypto/BIO_s_bio.html#EXAMPLE">OpenSSL BIO abstractions</a>.
58   */
59  public final class OpenSslEngine extends SSLEngine {
60  
61      private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslEngine.class);
62  
63      private static final Certificate[] EMPTY_CERTIFICATES = new Certificate[0];
64      private static final SSLException ENGINE_CLOSED = new SSLException("engine closed");
65      private static final SSLException RENEGOTIATION_UNSUPPORTED = new SSLException("renegotiation unsupported");
66      private static final SSLException ENCRYPTED_PACKET_OVERSIZED = new SSLException("encrypted packet oversized");
67      static {
68          ENGINE_CLOSED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
69          RENEGOTIATION_UNSUPPORTED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
70          ENCRYPTED_PACKET_OVERSIZED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
71  
72          AtomicIntegerFieldUpdater<OpenSslEngine> destroyedUpdater =
73                  PlatformDependent.newAtomicIntegerFieldUpdater(OpenSslEngine.class, "destroyed");
74          if (destroyedUpdater == null) {
75              destroyedUpdater = AtomicIntegerFieldUpdater.newUpdater(OpenSslEngine.class, "destroyed");
76          }
77          DESTROYED_UPDATER = destroyedUpdater;
78          AtomicReferenceFieldUpdater<OpenSslEngine, SSLSession> sessionUpdater =
79                  PlatformDependent.newAtomicReferenceFieldUpdater(OpenSslEngine.class, "session");
80          if (sessionUpdater == null) {
81              sessionUpdater = AtomicReferenceFieldUpdater.newUpdater(OpenSslEngine.class, SSLSession.class, "session");
82          }
83          SESSION_UPDATER = sessionUpdater;
84      }
85  
86      private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
87      private static final int MAX_COMPRESSED_LENGTH = MAX_PLAINTEXT_LENGTH + 1024;
88      private static final int MAX_CIPHERTEXT_LENGTH = MAX_COMPRESSED_LENGTH + 1024;
89  
90      // Protocols
91      private static final String PROTOCOL_SSL_V2_HELLO = "SSLv2Hello";
92      private static final String PROTOCOL_SSL_V2 = "SSLv2";
93      private static final String PROTOCOL_SSL_V3 = "SSLv3";
94      private static final String PROTOCOL_TLS_V1 = "TLSv1";
95      private static final String PROTOCOL_TLS_V1_1 = "TLSv1.1";
96      private static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
97  
98      private static final String[] SUPPORTED_PROTOCOLS = {
99              PROTOCOL_SSL_V2_HELLO,
100             PROTOCOL_SSL_V2,
101             PROTOCOL_SSL_V3,
102             PROTOCOL_TLS_V1,
103             PROTOCOL_TLS_V1_1,
104             PROTOCOL_TLS_V1_2
105     };
106     private static final Set<String> SUPPORTED_PROTOCOLS_SET = new HashSet<String>(Arrays.asList(SUPPORTED_PROTOCOLS));
107 
108     // Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC (20) + Padding (256)
109     static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
110 
111     static final int MAX_ENCRYPTION_OVERHEAD_LENGTH = MAX_ENCRYPTED_PACKET_LENGTH - MAX_PLAINTEXT_LENGTH;
112 
113     enum ClientAuthMode {
114         NONE,
115         OPTIONAL,
116         REQUIRE,
117     }
118 
119     private static final AtomicIntegerFieldUpdater<OpenSslEngine> DESTROYED_UPDATER;
120     private static final AtomicReferenceFieldUpdater<OpenSslEngine, SSLSession> SESSION_UPDATER;
121 
122     private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
123 
124     private static final long EMPTY_ADDR = Buffer.address(Unpooled.EMPTY_BUFFER.nioBuffer());
125 
126     // OpenSSL state
127     private long ssl;
128     private long networkBIO;
129 
130     /**
131      * 0 - not accepted, 1 - accepted implicitly via wrap()/unwrap(), 2 - accepted explicitly via beginHandshake() call
132      */
133     private int accepted;
134     private boolean handshakeFinished;
135     private boolean receivedShutdown;
136     @SuppressWarnings("UnusedDeclaration")
137     private volatile int destroyed;
138 
139     // Use an invalid cipherSuite until the handshake is completed
140     // See http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html#getSession()
141     private volatile String cipher;
142     private volatile String applicationProtocol;
143 
144     // We store this outside of the SslSession so we not need to create an instance during verifyCertificates(...)
145     private volatile Certificate[] peerCerts;
146     private volatile ClientAuthMode clientAuth = ClientAuthMode.NONE;
147 
148     // SSL Engine status variables
149     private boolean isInboundDone;
150     private boolean isOutboundDone;
151     private boolean engineClosed;
152 
153     private final boolean clientMode;
154     private final ByteBufAllocator alloc;
155     private final String fallbackApplicationProtocol;
156     private final OpenSslSessionContext sessionContext;
157 
158     @SuppressWarnings("unused")
159     private volatile SSLSession session;
160 
161     /**
162      * Creates a new instance
163      *
164      * @param sslCtx an OpenSSL {@code SSL_CTX} object
165      * @param alloc the {@link ByteBufAllocator} that will be used by this engine
166      */
167     @Deprecated
168     public OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol) {
169         this(sslCtx, alloc, fallbackApplicationProtocol, false, null);
170     }
171 
172     /**
173      * Creates a new instance
174      *
175      * @param sslCtx an OpenSSL {@code SSL_CTX} object
176      * @param alloc the {@link ByteBufAllocator} that will be used by this engine
177      * @param clientMode {@code true} if this is used for clients, {@code false} otherwise
178      * @param sessionContext the {@link OpenSslSessionContext} this {@link SSLEngine} belongs to.
179      */
180     OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol,
181                   boolean clientMode, OpenSslSessionContext sessionContext) {
182         OpenSsl.ensureAvailability();
183         if (sslCtx == 0) {
184             throw new NullPointerException("sslContext");
185         }
186         if (alloc == null) {
187             throw new NullPointerException("alloc");
188         }
189 
190         this.alloc = alloc;
191         ssl = SSL.newSSL(sslCtx, !clientMode);
192         networkBIO = SSL.makeNetworkBIO(ssl);
193         this.fallbackApplicationProtocol = fallbackApplicationProtocol;
194         this.clientMode = clientMode;
195         this.sessionContext = sessionContext;
196     }
197 
198     /**
199      * Destroys this engine.
200      */
201     public synchronized void shutdown() {
202         if (DESTROYED_UPDATER.compareAndSet(this, 0, 1)) {
203             SSL.freeSSL(ssl);
204             SSL.freeBIO(networkBIO);
205             ssl = networkBIO = 0;
206 
207             // internal errors can cause shutdown without marking the engine closed
208             isInboundDone = isOutboundDone = engineClosed = true;
209         }
210     }
211 
212     /**
213      * Write plaintext data to the OpenSSL internal BIO
214      *
215      * Calling this function with src.remaining == 0 is undefined.
216      */
217     private int writePlaintextData(final ByteBuffer src) {
218         final int pos = src.position();
219         final int limit = src.limit();
220         final int len = Math.min(limit - pos, MAX_PLAINTEXT_LENGTH);
221         final int sslWrote;
222 
223         if (src.isDirect()) {
224             final long addr = Buffer.address(src) + pos;
225             sslWrote = SSL.writeToSSL(ssl, addr, len);
226             if (sslWrote > 0) {
227                 src.position(pos + sslWrote);
228                 return sslWrote;
229             }
230         } else {
231             ByteBuf buf = alloc.directBuffer(len);
232             try {
233                 final long addr = memoryAddress(buf);
234 
235                 src.limit(pos + len);
236 
237                 buf.setBytes(0, src);
238                 src.limit(limit);
239 
240                 sslWrote = SSL.writeToSSL(ssl, addr, len);
241                 if (sslWrote > 0) {
242                     src.position(pos + sslWrote);
243                     return sslWrote;
244                 } else {
245                     src.position(pos);
246                 }
247             } finally {
248                 buf.release();
249             }
250         }
251 
252         throw new IllegalStateException("SSL.writeToSSL() returned a non-positive value: " + sslWrote);
253     }
254 
255     /**
256      * Write encrypted data to the OpenSSL network BIO.
257      */
258     private int writeEncryptedData(final ByteBuffer src) {
259         final int pos = src.position();
260         final int len = src.remaining();
261         if (src.isDirect()) {
262             final long addr = Buffer.address(src) + pos;
263             final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
264             if (netWrote >= 0) {
265                 src.position(pos + netWrote);
266                 return netWrote;
267             }
268         } else {
269             final ByteBuf buf = alloc.directBuffer(len);
270             try {
271                 final long addr = memoryAddress(buf);
272 
273                 buf.setBytes(0, src);
274 
275                 final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
276                 if (netWrote >= 0) {
277                     src.position(pos + netWrote);
278                     return netWrote;
279                 } else {
280                     src.position(pos);
281                 }
282             } finally {
283                 buf.release();
284             }
285         }
286 
287         return -1;
288     }
289 
290     /**
291      * Read plaintext data from the OpenSSL internal BIO
292      */
293     private int readPlaintextData(final ByteBuffer dst) {
294         if (dst.isDirect()) {
295             final int pos = dst.position();
296             final long addr = Buffer.address(dst) + pos;
297             final int len = dst.limit() - pos;
298             final int sslRead = SSL.readFromSSL(ssl, addr, len);
299             if (sslRead > 0) {
300                 dst.position(pos + sslRead);
301                 return sslRead;
302             }
303         } else {
304             final int pos = dst.position();
305             final int limit = dst.limit();
306             final int len = Math.min(MAX_ENCRYPTED_PACKET_LENGTH, limit - pos);
307             final ByteBuf buf = alloc.directBuffer(len);
308             try {
309                 final long addr = memoryAddress(buf);
310 
311                 final int sslRead = SSL.readFromSSL(ssl, addr, len);
312                 if (sslRead > 0) {
313                     dst.limit(pos + sslRead);
314                     buf.getBytes(0, dst);
315                     dst.limit(limit);
316                     return sslRead;
317                 }
318             } finally {
319                 buf.release();
320             }
321         }
322 
323         return 0;
324     }
325 
326     /**
327      * Read encrypted data from the OpenSSL network BIO
328      */
329     private int readEncryptedData(final ByteBuffer dst, final int pending) {
330         if (dst.isDirect() && dst.remaining() >= pending) {
331             final int pos = dst.position();
332             final long addr = Buffer.address(dst) + pos;
333             final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
334             if (bioRead > 0) {
335                 dst.position(pos + bioRead);
336                 return bioRead;
337             }
338         } else {
339             final ByteBuf buf = alloc.directBuffer(pending);
340             try {
341                 final long addr = memoryAddress(buf);
342 
343                 final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
344                 if (bioRead > 0) {
345                     int oldLimit = dst.limit();
346                     dst.limit(dst.position() + bioRead);
347                     buf.getBytes(0, dst);
348                     dst.limit(oldLimit);
349                     return bioRead;
350                 }
351             } finally {
352                 buf.release();
353             }
354         }
355 
356         return 0;
357     }
358 
359     @Override
360     public synchronized SSLEngineResult wrap(
361             final ByteBuffer[] srcs, final int offset, final int length, final ByteBuffer dst) throws SSLException {
362 
363         // Check to make sure the engine has not been closed
364         if (destroyed != 0) {
365             return new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0);
366         }
367 
368         // Throw required runtime exceptions
369         if (srcs == null) {
370             throw new IllegalArgumentException("srcs is null");
371         }
372         if (dst == null) {
373             throw new IllegalArgumentException("dst is null");
374         }
375 
376         if (offset >= srcs.length || offset + length > srcs.length) {
377             throw new IndexOutOfBoundsException(
378                     "offset: " + offset + ", length: " + length +
379                             " (expected: offset <= offset + length <= srcs.length (" + srcs.length + "))");
380         }
381 
382         if (dst.isReadOnly()) {
383             throw new ReadOnlyBufferException();
384         }
385 
386         // Prepare OpenSSL to work in server mode and receive handshake
387         if (accepted == 0) {
388             beginHandshakeImplicitly();
389         }
390 
391         // In handshake or close_notify stages, check if call to wrap was made
392         // without regard to the handshake status.
393         SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
394 
395         if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_UNWRAP) {
396             return new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, 0, 0);
397         }
398 
399         int bytesProduced = 0;
400         int pendingNet;
401 
402         // Check for pending data in the network BIO
403         pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
404         if (pendingNet > 0) {
405             // Do we have enough room in dst to write encrypted data?
406             int capacity = dst.remaining();
407             if (capacity < pendingNet) {
408                 return new SSLEngineResult(BUFFER_OVERFLOW, handshakeStatus, 0, bytesProduced);
409             }
410 
411             // Write the pending data from the network BIO into the dst buffer
412             try {
413                 bytesProduced += readEncryptedData(dst, pendingNet);
414             } catch (Exception e) {
415                 throw new SSLException(e);
416             }
417 
418             // If isOuboundDone is set, then the data from the network BIO
419             // was the close_notify message -- we are not required to wait
420             // for the receipt the peer's close_notify message -- shutdown.
421             if (isOutboundDone) {
422                 shutdown();
423             }
424 
425             return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), 0, bytesProduced);
426         }
427 
428         // There was no pending data in the network BIO -- encrypt any application data
429         int bytesConsumed = 0;
430         int endOffset = offset + length;
431         for (int i = offset; i < endOffset; ++ i) {
432             final ByteBuffer src = srcs[i];
433             if (src == null) {
434                 throw new IllegalArgumentException("srcs[" + i + "] is null");
435             }
436             while (src.hasRemaining()) {
437 
438                 // Write plaintext application data to the SSL engine
439                 try {
440                     bytesConsumed += writePlaintextData(src);
441                 } catch (Exception e) {
442                     throw new SSLException(e);
443                 }
444 
445                 // Check to see if the engine wrote data into the network BIO
446                 pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
447                 if (pendingNet > 0) {
448                     // Do we have enough room in dst to write encrypted data?
449                     int capacity = dst.remaining();
450                     if (capacity < pendingNet) {
451                         return new SSLEngineResult(
452                                 BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, bytesProduced);
453                     }
454 
455                     // Write the pending data from the network BIO into the dst buffer
456                     try {
457                         bytesProduced += readEncryptedData(dst, pendingNet);
458                     } catch (Exception e) {
459                         throw new SSLException(e);
460                     }
461 
462                     return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
463                 }
464             }
465         }
466 
467         return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
468     }
469 
470     public synchronized SSLEngineResult unwrap(
471             final ByteBuffer[] srcs, int srcsOffset, final int srcsLength,
472             final ByteBuffer[] dsts, final int dstsOffset, final int dstsLength) throws SSLException {
473 
474         // Check to make sure the engine has not been closed
475         if (destroyed != 0) {
476             return new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0);
477         }
478 
479         // Throw requried runtime exceptions
480         if (srcs == null) {
481             throw new NullPointerException("srcs");
482         }
483         if (srcsOffset >= srcs.length
484                 || srcsOffset + srcsLength > srcs.length) {
485             throw new IndexOutOfBoundsException(
486                     "offset: " + srcsOffset + ", length: " + srcsLength +
487                     " (expected: offset <= offset + length <= srcs.length (" + srcs.length + "))");
488         }
489         if (dsts == null) {
490             throw new IllegalArgumentException("dsts is null");
491         }
492         if (dstsOffset >= dsts.length || dstsOffset + dstsLength > dsts.length) {
493             throw new IndexOutOfBoundsException(
494                     "offset: " + dstsOffset + ", length: " + dstsLength +
495                     " (expected: offset <= offset + length <= dsts.length (" + dsts.length + "))");
496         }
497         int capacity = 0;
498         final int endOffset = dstsOffset + dstsLength;
499         for (int i = dstsOffset; i < endOffset; i ++) {
500             ByteBuffer dst = dsts[i];
501             if (dst == null) {
502                 throw new IllegalArgumentException("dsts[" + i + "] is null");
503             }
504             if (dst.isReadOnly()) {
505                 throw new ReadOnlyBufferException();
506             }
507             capacity += dst.remaining();
508         }
509 
510         // Prepare OpenSSL to work in server mode and receive handshake
511         if (accepted == 0) {
512             beginHandshakeImplicitly();
513         }
514 
515         // In handshake or close_notify stages, check if call to unwrap was made
516         // without regard to the handshake status.
517         SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
518         if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_WRAP) {
519             return new SSLEngineResult(getEngineStatus(), NEED_WRAP, 0, 0);
520         }
521 
522         final int srcsEndOffset = srcsOffset + srcsLength;
523         int len = 0;
524         for (int i = srcsOffset; i < srcsEndOffset; i++) {
525             ByteBuffer src = srcs[i];
526             if (src == null) {
527                 throw new IllegalArgumentException("srcs[" + i + "] is null");
528             }
529             len += src.remaining();
530         }
531 
532         // protect against protocol overflow attack vector
533         if (len > MAX_ENCRYPTED_PACKET_LENGTH) {
534             isInboundDone = true;
535             isOutboundDone = true;
536             engineClosed = true;
537             shutdown();
538             throw ENCRYPTED_PACKET_OVERSIZED;
539         }
540 
541         // Write encrypted data to network BIO
542         int bytesConsumed = -1;
543         try {
544             while (srcsOffset < srcsEndOffset) {
545                 ByteBuffer src = srcs[srcsOffset];
546                 int remaining = src.remaining();
547                 int written = writeEncryptedData(src);
548                 if (written >= 0) {
549                     if (bytesConsumed == -1) {
550                         bytesConsumed = written;
551                     } else {
552                         bytesConsumed += written;
553                     }
554                     if (written == remaining) {
555                         srcsOffset ++;
556                     } else if (written == 0) {
557                         break;
558                     }
559                 } else {
560                     break;
561                 }
562             }
563         } catch (Exception e) {
564             throw new SSLException(e);
565         }
566         if (bytesConsumed >= 0) {
567             int lastPrimingReadResult = SSL.readFromSSL(ssl, EMPTY_ADDR, 0); // priming read
568 
569             // check if SSL_read returned <= 0. In this case we need to check the error and see if it was something
570             // fatal.
571             if (lastPrimingReadResult <= 0) {
572                 // Check for OpenSSL errors caused by the priming read
573                 long error = SSL.getLastErrorNumber();
574                 if (OpenSsl.isError(error)) {
575                     String err = SSL.getErrorString(error);
576                     if (logger.isDebugEnabled()) {
577                         logger.debug(
578                                 "SSL_read failed: primingReadResult: " + lastPrimingReadResult +
579                                         "; OpenSSL error: '" + err + '\'');
580                     }
581 
582                     // There was an internal error -- shutdown
583                     shutdown();
584                     throw new SSLException(err);
585                 }
586             }
587         } else {
588             // Reset to 0 as -1 is used to signal that nothing was written and no priming read needs to be done
589             bytesConsumed = 0;
590         }
591 
592         // There won't be any application data until we're done handshaking
593         //
594         // We first check handshakeFinished to eliminate the overhead of extra JNI call if possible.
595         int pendingApp = (handshakeFinished || SSL.isInInit(ssl) == 0) ? SSL.pendingReadableBytesInSSL(ssl) : 0;
596         int bytesProduced = 0;
597 
598         if (pendingApp > 0) {
599             // Do we have enough room in dsts to write decrypted data?
600             if (capacity < pendingApp) {
601                 return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, 0);
602             }
603 
604             // Write decrypted data to dsts buffers
605             int idx = dstsOffset;
606             while (idx < endOffset) {
607                 ByteBuffer dst = dsts[idx];
608                 if (!dst.hasRemaining()) {
609                     idx ++;
610                     continue;
611                 }
612 
613                 if (pendingApp <= 0) {
614                     break;
615                 }
616 
617                 int bytesRead;
618                 try {
619                     bytesRead = readPlaintextData(dst);
620                 } catch (Exception e) {
621                     throw new SSLException(e);
622                 }
623 
624                 if (bytesRead == 0) {
625                     break;
626                 }
627 
628                 bytesProduced += bytesRead;
629                 pendingApp -= bytesRead;
630 
631                 if (!dst.hasRemaining()) {
632                     idx ++;
633                 }
634             }
635         }
636 
637         // Check to see if we received a close_notify message from the peer
638         if (!receivedShutdown && (SSL.getShutdown(ssl) & SSL.SSL_RECEIVED_SHUTDOWN) == SSL.SSL_RECEIVED_SHUTDOWN) {
639             receivedShutdown = true;
640             closeOutbound();
641             closeInbound();
642         }
643 
644         return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
645     }
646 
647     public SSLEngineResult unwrap(final ByteBuffer[] srcs, final ByteBuffer[] dsts) throws SSLException {
648         return unwrap(srcs, 0, srcs.length, dsts, 0, dsts.length);
649     }
650 
651     @Override
652     public SSLEngineResult unwrap(
653             final ByteBuffer src, final ByteBuffer[] dsts, final int offset, final int length) throws SSLException {
654         return unwrap(new ByteBuffer[] { src }, 0, 1, dsts, offset, length);
655     }
656 
657     @Override
658     public Runnable getDelegatedTask() {
659         // Currently, we do not delegate SSL computation tasks
660         // TODO: in the future, possibly create tasks to do encrypt / decrypt async
661 
662         return null;
663     }
664 
665     @Override
666     public synchronized void closeInbound() throws SSLException {
667         if (isInboundDone) {
668             return;
669         }
670 
671         isInboundDone = true;
672         engineClosed = true;
673 
674         shutdown();
675 
676         if (accepted != 0 && !receivedShutdown) {
677             throw new SSLException(
678                     "Inbound closed before receiving peer's close_notify: possible truncation attack?");
679         }
680     }
681 
682     @Override
683     public synchronized boolean isInboundDone() {
684         return isInboundDone || engineClosed;
685     }
686 
687     @Override
688     public synchronized void closeOutbound() {
689         if (isOutboundDone) {
690             return;
691         }
692 
693         isOutboundDone = true;
694         engineClosed = true;
695 
696         if (accepted != 0 && destroyed == 0) {
697             int mode = SSL.getShutdown(ssl);
698             if ((mode & SSL.SSL_SENT_SHUTDOWN) != SSL.SSL_SENT_SHUTDOWN) {
699                 SSL.shutdownSSL(ssl);
700             }
701         } else {
702             // engine closing before initial handshake
703             shutdown();
704         }
705     }
706 
707     @Override
708     public synchronized boolean isOutboundDone() {
709         return isOutboundDone;
710     }
711 
712     @Override
713     public String[] getSupportedCipherSuites() {
714         Set<String> availableCipherSuites = OpenSsl.availableCipherSuites();
715         return availableCipherSuites.toArray(new String[availableCipherSuites.size()]);
716     }
717 
718     @Override
719     public String[] getEnabledCipherSuites() {
720         String[] enabled = SSL.getCiphers(ssl);
721         if (enabled == null) {
722             return EmptyArrays.EMPTY_STRINGS;
723         } else {
724             for (int i = 0; i < enabled.length; i++) {
725                 String mapped = toJavaCipherSuite(enabled[i]);
726                 if (mapped != null) {
727                     enabled[i] = mapped;
728                 }
729             }
730             return enabled;
731         }
732     }
733 
734     @Override
735     public void setEnabledCipherSuites(String[] cipherSuites) {
736         if (cipherSuites == null) {
737             throw new NullPointerException("cipherSuites");
738         }
739 
740         final StringBuilder buf = new StringBuilder();
741         for (String c: cipherSuites) {
742             if (c == null) {
743                 break;
744             }
745 
746             String converted = CipherSuiteConverter.toOpenSsl(c);
747             if (converted == null) {
748                 converted = c;
749             }
750 
751             if (!OpenSsl.isCipherSuiteAvailable(converted)) {
752                 throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')');
753             }
754 
755             buf.append(converted);
756             buf.append(':');
757         }
758 
759         if (buf.length() == 0) {
760             throw new IllegalArgumentException("empty cipher suites");
761         }
762         buf.setLength(buf.length() - 1);
763 
764         final String cipherSuiteSpec = buf.toString();
765         try {
766             SSL.setCipherSuites(ssl, cipherSuiteSpec);
767         } catch (Exception e) {
768             throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec, e);
769         }
770     }
771 
772     @Override
773     public String[] getSupportedProtocols() {
774         return SUPPORTED_PROTOCOLS.clone();
775     }
776 
777     @Override
778     public String[] getEnabledProtocols() {
779         List<String> enabled = new ArrayList<String>();
780         // Seems like there is no way to explict disable SSLv2Hello in openssl so it is always enabled
781         enabled.add(PROTOCOL_SSL_V2_HELLO);
782         int opts = SSL.getOptions(ssl);
783         if ((opts & SSL.SSL_OP_NO_TLSv1) == 0) {
784             enabled.add(PROTOCOL_TLS_V1);
785         }
786         if ((opts & SSL.SSL_OP_NO_TLSv1_1) == 0) {
787             enabled.add(PROTOCOL_TLS_V1_1);
788         }
789         if ((opts & SSL.SSL_OP_NO_TLSv1_2) == 0) {
790             enabled.add(PROTOCOL_TLS_V1_2);
791         }
792         if ((opts & SSL.SSL_OP_NO_SSLv2) == 0) {
793             enabled.add(PROTOCOL_SSL_V2);
794         }
795         if ((opts & SSL.SSL_OP_NO_SSLv3) == 0) {
796             enabled.add(PROTOCOL_SSL_V3);
797         }
798         int size = enabled.size();
799         if (size == 0) {
800             return EmptyArrays.EMPTY_STRINGS;
801         } else {
802             return enabled.toArray(new String[size]);
803         }
804     }
805 
806     @Override
807     public void setEnabledProtocols(String[] protocols) {
808         if (protocols == null) {
809             // This is correct from the API docs
810             throw new IllegalArgumentException();
811         }
812         boolean sslv2 = false;
813         boolean sslv3 = false;
814         boolean tlsv1 = false;
815         boolean tlsv1_1 = false;
816         boolean tlsv1_2 = false;
817         for (String p: protocols) {
818             if (!SUPPORTED_PROTOCOLS_SET.contains(p)) {
819                 throw new IllegalArgumentException("Protocol " + p + " is not supported.");
820             }
821             if (p.equals(PROTOCOL_SSL_V2)) {
822                 sslv2 = true;
823             } else if (p.equals(PROTOCOL_SSL_V3)) {
824                 sslv3 = true;
825             } else if (p.equals(PROTOCOL_TLS_V1)) {
826                 tlsv1 = true;
827             } else if (p.equals(PROTOCOL_TLS_V1_1)) {
828                 tlsv1_1 = true;
829             } else if (p.equals(PROTOCOL_TLS_V1_2)) {
830                 tlsv1_2 = true;
831             }
832         }
833         // Enable all and then disable what we not want
834         SSL.setOptions(ssl, SSL.SSL_OP_ALL);
835 
836         if (!sslv2) {
837             SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv2);
838         }
839         if (!sslv3) {
840             SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv3);
841         }
842         if (!tlsv1) {
843             SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1);
844         }
845         if (!tlsv1_1) {
846             SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_1);
847         }
848         if (!tlsv1_2) {
849             SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_2);
850         }
851     }
852 
853     private Certificate[] initPeerCertChain() throws SSLPeerUnverifiedException {
854         byte[][] chain = SSL.getPeerCertChain(ssl);
855         byte[] clientCert;
856         if (!clientMode) {
857             // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer certificate.
858             // We use SSL_get_peer_certificate to get it in this case and add it to our array later.
859             //
860             // See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
861             clientCert = SSL.getPeerCertificate(ssl);
862         } else {
863             clientCert = null;
864         }
865 
866         if (chain == null && clientCert == null) {
867             throw new SSLPeerUnverifiedException("peer not verified");
868         }
869         int len = 0;
870         if (chain != null) {
871             len += chain.length;
872         }
873 
874         int i = 0;
875         Certificate[] peerCerts;
876         if (clientCert != null) {
877             len++;
878             peerCerts = new Certificate[len];
879             peerCerts[i++] = new OpenSslX509Certificate(clientCert);
880         } else {
881             peerCerts = new Certificate[len];
882         }
883         if (chain != null) {
884             int a = 0;
885             for (; i < peerCerts.length; i++) {
886                 peerCerts[i] = new OpenSslX509Certificate(chain[a++]);
887             }
888         }
889         return peerCerts;
890     }
891 
892     @Override
893     public SSLSession getSession() {
894         // A other methods on SSLEngine are thread-safe we also need to make this thread-safe...
895         SSLSession session = this.session;
896         if (session == null) {
897             session = new SSLSession() {
898                 // SSLSession implementation seems to not need to be thread-safe so no need for volatile etc.
899                 private X509Certificate[] x509PeerCerts;
900 
901                 // lazy init for memory reasons
902                 private Map<String, Object> values;
903 
904                 @Override
905                 public byte[] getId() {
906                     // We don't cache that to keep memory usage to a minimum.
907                     byte[] id = SSL.getSessionId(ssl);
908                     if (id == null) {
909                         // The id should never be null, if it was null then the SESSION itself was not valid.
910                         throw new IllegalStateException("SSL session ID not available");
911                     }
912                     return id;
913                 }
914 
915                 @Override
916                 public SSLSessionContext getSessionContext() {
917                     return sessionContext;
918                 }
919 
920                 @Override
921                 public long getCreationTime() {
922                     // We need ot multiple by 1000 as openssl uses seconds and we need milli-seconds.
923                     return SSL.getTime(ssl) * 1000L;
924                 }
925 
926                 @Override
927                 public long getLastAccessedTime() {
928                     // TODO: Add proper implementation
929                     return getCreationTime();
930                 }
931 
932                 @Override
933                 public void invalidate() {
934                     // NOOP
935                 }
936 
937                 @Override
938                 public boolean isValid() {
939                     return false;
940                 }
941 
942                 @Override
943                 public void putValue(String name, Object value) {
944                     if (name == null) {
945                         throw new NullPointerException("name");
946                     }
947                     if (value == null) {
948                         throw new NullPointerException("value");
949                     }
950                     Map<String, Object> values = this.values;
951                     if (values == null) {
952                         // Use size of 2 to keep the memory overhead small
953                         values = this.values = new HashMap<String, Object>(2);
954                     }
955                     Object old = values.put(name, value);
956                     if (value instanceof SSLSessionBindingListener) {
957                         ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
958                     }
959                     notifyUnbound(old, name);
960                 }
961 
962                 @Override
963                 public Object getValue(String name) {
964                     if (name == null) {
965                         throw new NullPointerException("name");
966                     }
967                     if (values == null) {
968                         return null;
969                     }
970                     return values.get(name);
971                 }
972 
973                 @Override
974                 public void removeValue(String name) {
975                     if (name == null) {
976                         throw new NullPointerException("name");
977                     }
978                     Map<String, Object> values = this.values;
979                     if (values == null) {
980                         return;
981                     }
982                     Object old = values.remove(name);
983                     notifyUnbound(old, name);
984                 }
985 
986                 @Override
987                 public String[] getValueNames() {
988                     Map<String, Object> values = this.values;
989                     if (values == null || values.isEmpty()) {
990                         return EmptyArrays.EMPTY_STRINGS;
991                     }
992                     return values.keySet().toArray(new String[values.size()]);
993                 }
994 
995                 private void notifyUnbound(Object value, String name) {
996                     if (value instanceof SSLSessionBindingListener) {
997                         ((SSLSessionBindingListener) value).valueUnbound(new SSLSessionBindingEvent(this, name));
998                     }
999                 }
1000 
1001                 @Override
1002                 public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
1003                     // these are lazy created to reduce memory overhead
1004                     Certificate[] c = peerCerts;
1005                     if (c == null) {
1006                         if (SSL.isInInit(ssl) != 0) {
1007                             throw new SSLPeerUnverifiedException("peer not verified");
1008                         }
1009                         c = peerCerts = initPeerCertChain();
1010                     }
1011                     return c;
1012                 }
1013 
1014                 @Override
1015                 public Certificate[] getLocalCertificates() {
1016                     // TODO: Find out how to get these
1017                     return EMPTY_CERTIFICATES;
1018                 }
1019 
1020                 @Override
1021                 public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
1022                     // these are lazy created to reduce memory overhead
1023                     X509Certificate[] c = x509PeerCerts;
1024                     if (c == null) {
1025                         if (SSL.isInInit(ssl) != 0) {
1026                             throw new SSLPeerUnverifiedException("peer not verified");
1027                         }
1028                         byte[][] chain = SSL.getPeerCertChain(ssl);
1029                         if (chain == null) {
1030                             throw new SSLPeerUnverifiedException("peer not verified");
1031                         }
1032                         X509Certificate[] peerCerts = new X509Certificate[chain.length];
1033                         for (int i = 0; i < peerCerts.length; i++) {
1034                             try {
1035                                 peerCerts[i] = X509Certificate.getInstance(chain[i]);
1036                             } catch (CertificateException e) {
1037                                 throw new IllegalStateException(e);
1038                             }
1039                         }
1040                         c = x509PeerCerts = peerCerts;
1041                     }
1042                     return c;
1043                 }
1044 
1045                 @Override
1046                 public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
1047                     Certificate[] peer = getPeerCertificates();
1048                     if (peer == null || peer.length == 0) {
1049                         return null;
1050                     }
1051                     return principal(peer);
1052                 }
1053 
1054                 @Override
1055                 public Principal getLocalPrincipal() {
1056                     Certificate[] local = getLocalCertificates();
1057                     if (local == null || local.length == 0) {
1058                         return null;
1059                     }
1060                     return principal(local);
1061                 }
1062 
1063                 private Principal principal(Certificate[] certs) {
1064                     return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal();
1065                 }
1066 
1067                 @Override
1068                 public String getCipherSuite() {
1069                     if (!handshakeFinished) {
1070                         return INVALID_CIPHER;
1071                     }
1072                     if (cipher == null) {
1073                         String c = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
1074                         if (c != null) {
1075                             cipher = c;
1076                         }
1077                     }
1078                     return cipher;
1079                 }
1080 
1081                 @Override
1082                 public String getProtocol() {
1083                     String applicationProtocol = OpenSslEngine.this.applicationProtocol;
1084                     if (applicationProtocol == null) {
1085                         applicationProtocol = SSL.getNextProtoNegotiated(ssl);
1086                         if (applicationProtocol == null) {
1087                             applicationProtocol = fallbackApplicationProtocol;
1088                         }
1089                         if (applicationProtocol != null) {
1090                             OpenSslEngine.this.applicationProtocol = applicationProtocol.replace(':', '_');
1091                         } else {
1092                             OpenSslEngine.this.applicationProtocol = applicationProtocol = "";
1093                         }
1094                     }
1095                     String version = SSL.getVersion(ssl);
1096                     if (applicationProtocol.isEmpty()) {
1097                         return version;
1098                     } else {
1099                         return version + ':' + applicationProtocol;
1100                     }
1101                 }
1102 
1103                 @Override
1104                 public String getPeerHost() {
1105                     return null;
1106                 }
1107 
1108                 @Override
1109                 public int getPeerPort() {
1110                     return 0;
1111                 }
1112 
1113                 @Override
1114                 public int getPacketBufferSize() {
1115                     return MAX_ENCRYPTED_PACKET_LENGTH;
1116                 }
1117 
1118                 @Override
1119                 public int getApplicationBufferSize() {
1120                     return MAX_PLAINTEXT_LENGTH;
1121                 }
1122             };
1123 
1124             if (!SESSION_UPDATER.compareAndSet(this, null, session)) {
1125                 // Was lazy created in the meantime so get the current reference.
1126                 session = this.session;
1127             }
1128         }
1129 
1130         return session;
1131     }
1132 
1133     @Override
1134     public synchronized void beginHandshake() throws SSLException {
1135         if (engineClosed || destroyed != 0) {
1136             throw ENGINE_CLOSED;
1137         }
1138         switch (accepted) {
1139             case 0:
1140                 handshake();
1141                 accepted = 2;
1142                 break;
1143             case 1:
1144                 // A user did not start handshake by calling this method by him/herself,
1145                 // but handshake has been started already by wrap() or unwrap() implicitly.
1146                 // Because it's the user's first time to call this method, it is unfair to
1147                 // raise an exception.  From the user's standpoint, he or she never asked
1148                 // for renegotiation.
1149 
1150                 accepted = 2; // Next time this method is invoked by the user, we should raise an exception.
1151                 break;
1152             case 2:
1153                 throw RENEGOTIATION_UNSUPPORTED;
1154             default:
1155                 throw new Error();
1156         }
1157     }
1158 
1159     private void beginHandshakeImplicitly() throws SSLException {
1160         if (engineClosed || destroyed != 0) {
1161             throw ENGINE_CLOSED;
1162         }
1163 
1164         if (accepted == 0) {
1165             handshake();
1166             accepted = 1;
1167         }
1168     }
1169 
1170     private void handshake() throws SSLException {
1171         int code = SSL.doHandshake(ssl);
1172         if (code <= 0) {
1173             // Check for OpenSSL errors caused by the handshake
1174             long error = SSL.getLastErrorNumber();
1175             if (OpenSsl.isError(error)) {
1176                 String err = SSL.getErrorString(error);
1177                 if (logger.isDebugEnabled()) {
1178                     logger.debug(
1179                             "SSL_do_handshake failed: OpenSSL error: '" + err + '\'');
1180                 }
1181 
1182                 // There was an internal error -- shutdown
1183                 shutdown();
1184                 throw new SSLException(err);
1185             }
1186         } else {
1187             // if SSL_do_handshake returns > 0 it means the handshake was finished. This means we can update
1188             // handshakeFinished directly and so eliminate uncessary calls to SSL.isInInit(...)
1189             handshakeFinished = true;
1190         }
1191     }
1192 
1193     private static long memoryAddress(ByteBuf buf) {
1194         if (buf.hasMemoryAddress()) {
1195             return buf.memoryAddress();
1196         } else {
1197             return Buffer.address(buf.nioBuffer());
1198         }
1199     }
1200 
1201     private SSLEngineResult.Status getEngineStatus() {
1202         return engineClosed? CLOSED : OK;
1203     }
1204 
1205     @Override
1206     public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
1207         if (accepted == 0 || destroyed != 0) {
1208             return NOT_HANDSHAKING;
1209         }
1210 
1211         // Check if we are in the initial handshake phase
1212         if (!handshakeFinished) {
1213             // There is pending data in the network BIO -- call wrap
1214             if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
1215                 return NEED_WRAP;
1216             }
1217 
1218             // No pending data to be sent to the peer
1219             // Check to see if we have finished handshaking
1220             if (SSL.isInInit(ssl) == 0) {
1221                 handshakeFinished = true;
1222                 return FINISHED;
1223             }
1224 
1225             // No pending data and still handshaking
1226             // Must be waiting on the peer to send more data
1227             return NEED_UNWRAP;
1228         }
1229 
1230         // Check if we are in the shutdown phase
1231         if (engineClosed) {
1232             // Waiting to send the close_notify message
1233             if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
1234                 return NEED_WRAP;
1235             }
1236 
1237             // Must be waiting to receive the close_notify message
1238             return NEED_UNWRAP;
1239         }
1240 
1241         return NOT_HANDSHAKING;
1242     }
1243 
1244     /**
1245      * Converts the specified OpenSSL cipher suite to the Java cipher suite.
1246      */
1247     private String toJavaCipherSuite(String openSslCipherSuite) {
1248         if (openSslCipherSuite == null) {
1249             return null;
1250         }
1251 
1252         String prefix = toJavaCipherSuitePrefix(SSL.getVersion(ssl));
1253         return CipherSuiteConverter.toJava(openSslCipherSuite, prefix);
1254     }
1255 
1256     /**
1257      * Converts the protocol version string returned by {@link SSL#getVersion(long)} to protocol family string.
1258      */
1259     private static String toJavaCipherSuitePrefix(String protocolVersion) {
1260         final char c;
1261         if (protocolVersion == null || protocolVersion.length() == 0) {
1262             c = 0;
1263         } else {
1264             c = protocolVersion.charAt(0);
1265         }
1266 
1267         switch (c) {
1268         case 'T':
1269             return "TLS";
1270         case 'S':
1271             return "SSL";
1272         default:
1273             return "UNKNOWN";
1274         }
1275     }
1276 
1277     @Override
1278     public void setUseClientMode(boolean clientMode) {
1279         if (clientMode != this.clientMode) {
1280             throw new UnsupportedOperationException();
1281         }
1282     }
1283 
1284     @Override
1285     public boolean getUseClientMode() {
1286         return clientMode;
1287     }
1288 
1289     @Override
1290     public void setNeedClientAuth(boolean b) {
1291         setClientAuth(b ? ClientAuthMode.REQUIRE : ClientAuthMode.NONE);
1292     }
1293 
1294     @Override
1295     public boolean getNeedClientAuth() {
1296         return clientAuth == ClientAuthMode.REQUIRE;
1297     }
1298 
1299     @Override
1300     public void setWantClientAuth(boolean b) {
1301         setClientAuth(b ? ClientAuthMode.OPTIONAL : ClientAuthMode.NONE);
1302     }
1303 
1304     @Override
1305     public boolean getWantClientAuth() {
1306         return clientAuth == ClientAuthMode.OPTIONAL;
1307     }
1308 
1309     private void setClientAuth(ClientAuthMode mode) {
1310         if (clientMode) {
1311             return;
1312         }
1313         synchronized (this) {
1314             if (clientAuth == mode) {
1315                 // No need to issue any JNI calls if the mode is the same
1316                 return;
1317             }
1318             switch (mode) {
1319                 case NONE:
1320                     SSL.setVerify(ssl, SSL.SSL_CVERIFY_NONE, OpenSslContext.VERIFY_DEPTH);
1321                     break;
1322                 case REQUIRE:
1323                     SSL.setVerify(ssl, SSL.SSL_CVERIFY_REQUIRE, OpenSslContext.VERIFY_DEPTH);
1324                     break;
1325                 case OPTIONAL:
1326                     SSL.setVerify(ssl, SSL.SSL_CVERIFY_OPTIONAL, OpenSslContext.VERIFY_DEPTH);
1327                     break;
1328             }
1329             clientAuth = mode;
1330         }
1331     }
1332 
1333     @Override
1334     public void setEnableSessionCreation(boolean b) {
1335         if (b) {
1336             throw new UnsupportedOperationException();
1337         }
1338     }
1339 
1340     @Override
1341     public boolean getEnableSessionCreation() {
1342         return false;
1343     }
1344 
1345     @Override
1346     @SuppressWarnings("FinalizeDeclaration")
1347     protected void finalize() throws Throwable {
1348         super.finalize();
1349         // Call shutdown as the user may have created the OpenSslEngine and not used it at all.
1350         shutdown();
1351     }
1352 }