View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.filter.ssl;
21  
22  import java.net.InetSocketAddress;
23  import java.nio.ByteBuffer;
24  import java.util.Queue;
25  import java.util.concurrent.ConcurrentLinkedQueue;
26  import java.util.concurrent.atomic.AtomicInteger;
27  import java.util.concurrent.locks.ReentrantLock;
28  
29  import javax.net.ssl.SSLEngine;
30  import javax.net.ssl.SSLEngineResult;
31  import javax.net.ssl.SSLEngineResult.HandshakeStatus;
32  import javax.net.ssl.SSLEngineResult.Status;
33  import javax.net.ssl.SSLException;
34  import javax.net.ssl.SSLHandshakeException;
35  
36  import org.apache.mina.core.RuntimeIoException;
37  import org.apache.mina.core.buffer.IoBuffer;
38  import org.apache.mina.core.filterchain.IoFilter.NextFilter;
39  import org.apache.mina.core.filterchain.IoFilterEvent;
40  import org.apache.mina.core.future.DefaultWriteFuture;
41  import org.apache.mina.core.future.WriteFuture;
42  import org.apache.mina.core.session.IoEventType;
43  import org.apache.mina.core.session.IoSession;
44  import org.apache.mina.core.write.DefaultWriteRequest;
45  import org.apache.mina.core.write.WriteRequest;
46  import org.apache.mina.filter.ssl.SslFilter.EncryptedWriteRequest;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  /**
51   * A helper class using the SSLEngine API to decrypt/encrypt data.
52   * <p/>
53   * Each connection has a SSLEngine that is used through the lifetime of the connection.
54   * We allocate buffers for use as the outbound and inbound network buffers.
55   * These buffers handle all of the intermediary data for the SSL connection. To make things easy,
56   * we'll require outNetBuffer be completely flushed before trying to wrap any more data.
57   * <p/>
58   * This class is not to be used by any client, it's closely associated with the SSL Filter.
59   * None of its methods are public as they should not be used by any other class but from
60   * the SslFilter class, in the same package
61   *
62   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
63   */
64  /** No qualifier*/
65  class SslHandler {
66      /** A logger for this class */
67      private static final Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
68  
69      /** The SSL Filter which has created this handler */
70      private final SslFilter sslFilter;
71  
72      /** The current session */
73      private final IoSession session;
74  
75      private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<>();
76  
77      private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<>();
78  
79      /** A queue used to stack all the incoming data until the SSL session is established */
80      private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<>();
81  
82      private SSLEngine sslEngine;
83  
84      /**
85       * Encrypted data from the net
86       */
87      private IoBuffer inNetBuffer;
88  
89      /**
90       * Encrypted data to be written to the net
91       */
92      private IoBuffer outNetBuffer;
93  
94      /**
95       * Application cleartext data to be read by application
96       */
97      private IoBuffer appBuffer;
98  
99      /**
100      * Empty buffer used during initial handshake and close operations
101      */
102     private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
103 
104     private SSLEngineResult.HandshakeStatus handshakeStatus;
105 
106     /**
107      * A flag set to true when the first SSL handshake has been completed
108      * This is used to avoid sending a notification to the application handler
109      * when we switch to a SECURE or UNSECURE session.
110      */
111     private boolean firstSSLNegociation;
112 
113     /** A flag set to true when a SSL Handshake has been completed */
114     private boolean handshakeComplete;
115 
116     /** A flag used to indicate to the SslFilter that the buffer
117      * it will write is already encrypted (this will be the case
118      * for data being produced during the handshake). */
119     private boolean writingEncryptedData;
120 
121     /** A lock to protect the SSL flush of events */
122     private ReentrantLock sslLock = new ReentrantLock();
123     
124     /** A counter of schedules events */
125     private final AtomicInteger scheduledEvents = new AtomicInteger(0);
126 
127     /**
128      * Create a new SSL Handler, and initialize it.
129      *
130      * @param sslContext
131      * @throws SSLException
132      */
133     /* no qualifier */SslHandler(SslFilter sslFilter, IoSession session) {
134         this.sslFilter = sslFilter;
135         this.session = session;
136     }
137 
138     /**
139      * Initialize the SSL handshake.
140      *
141      * @throws SSLException If the underlying SSLEngine handshake initialization failed
142      */
143     /* no qualifier */void init() throws SSLException {
144         if (sslEngine != null) {
145             // We already have a SSL engine created, no need to create a new one
146             return;
147         }
148 
149         if (LOGGER.isDebugEnabled()) {
150             LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
151         }
152 
153         InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
154 
155         // Create the SSL engine here
156         if (peer == null) {
157             sslEngine = sslFilter.sslContext.createSSLEngine();
158         } else {
159             sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
160         }
161 
162         // Initialize the engine in client mode if necessary
163         sslEngine.setUseClientMode(sslFilter.isUseClientMode());
164 
165         // Initialize the different SslEngine modes
166         if (!sslEngine.getUseClientMode()) {
167             // Those parameters are only valid when in server mode
168             if (sslFilter.isWantClientAuth()) {
169                 sslEngine.setWantClientAuth(true);
170             }
171 
172             if (sslFilter.isNeedClientAuth()) {
173                 sslEngine.setNeedClientAuth(true);
174             }
175         }
176 
177         // Set the cipher suite to use by this SslEngine instance
178         if (sslFilter.getEnabledCipherSuites() != null) {
179             sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
180         }
181 
182         // Set the list of enabled protocols
183         if (sslFilter.getEnabledProtocols() != null) {
184             sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
185         }
186 
187         // TODO : we may not need to call this method...
188         // However, if we don't call it here, the tests are failing. Why?
189         handshakeStatus = sslEngine.getHandshakeStatus();
190 
191         // Default value
192         writingEncryptedData = false;
193 
194         // We haven't yet started a SSL negotiation
195         // set the flags accordingly
196         firstSSLNegociation = true;
197         handshakeComplete = false;
198 
199         if (LOGGER.isDebugEnabled()) {
200             LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
201         }
202     }
203     
204 
205     /**
206      * Release allocated buffers.
207      */
208     /* no qualifier */void destroy() {
209         if (sslEngine == null) {
210             return;
211         }
212 
213         // Close inbound and flush all remaining data if available.
214         try {
215             sslEngine.closeInbound();
216         } catch (SSLException e) {
217             if (LOGGER.isDebugEnabled()) {
218                 LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
219             }
220         }
221 
222         if (outNetBuffer != null) {
223             outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
224         } else {
225             createOutNetBuffer(0);
226         }
227         try {
228             do {
229                 outNetBuffer.clear();
230             } while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
231         } catch (SSLException e) {
232             // Ignore.
233         } finally {
234             outNetBuffer.free();
235             outNetBuffer = null;
236         }
237 
238         sslEngine.closeOutbound();
239         sslEngine = null;
240 
241         preHandshakeEventQueue.clear();
242     }
243 
244     /**
245      * @return The SSL filter which has created this handler
246      */
247     /* no qualifier */SslFilter getSslFilter() {
248         return sslFilter;
249     }
250 
251     /* no qualifier */IoSession getSession() {
252         return session;
253     }
254 
255     /**
256      * Check if we are writing encrypted data.
257      */
258     /* no qualifier */boolean isWritingEncryptedData() {
259         return writingEncryptedData;
260     }
261 
262     /**
263      * Check if handshake is completed.
264      */
265     /* no qualifier */boolean isHandshakeComplete() {
266         return handshakeComplete;
267     }
268 
269     /**
270      * Check if handshake is on going.
271      */
272     /* no qualifier */boolean notHandshaking() {
273         return handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING;
274     }
275 
276     /* no qualifier */boolean isInboundDone() {
277         return sslEngine == null || sslEngine.isInboundDone();
278     }
279 
280     /* no qualifier */boolean isOutboundDone() {
281         return sslEngine == null || sslEngine.isOutboundDone();
282     }
283 
284     /**
285      * Check if there is any need to complete handshake.
286      */
287     /* no qualifier */boolean needToCompleteHandshake() {
288         return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
289     }
290 
291     /* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
292         preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
293     }
294 
295     /* no qualifier */void flushPreHandshakeEvents() throws SSLException {
296         IoFilterEvent scheduledWrite;
297 
298         while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
299             sslFilter
300             .filterWrite(scheduledWrite.getNextFilter(), session, (WriteRequest) scheduledWrite.getParameter());
301         }
302     }
303 
304     /* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
305         filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
306     }
307 
308     /**
309      * Push the newly received data into a queue, waiting for the SSL session
310      * to be fully established
311      *
312      * @param nextFilter The next filter to call
313      * @param message The incoming data
314      */
315     /* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
316         messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
317     }
318 
319     /* no qualifier */void flushScheduledEvents() {
320         scheduledEvents.incrementAndGet();
321 
322         // Fire events only when the lock is available for this handler.
323         if (sslLock.tryLock()) {
324             IoFilterEvent event;
325             
326             try {
327                 do {
328                     // We need synchronization here inevitably because filterWrite can be
329                     // called simultaneously and cause 'bad record MAC' integrity error.
330                     while ((event = filterWriteEventQueue.poll()) != null) {
331                         NextFilter nextFilter = event.getNextFilter();
332                         nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
333                     }
334             
335                     while ((event = messageReceivedEventQueue.poll()) != null) {
336                         NextFilter nextFilter = event.getNextFilter();
337                         nextFilter.messageReceived(session, event.getParameter());
338                     }
339                 } while (scheduledEvents.decrementAndGet() > 0);
340             } finally {
341                 sslLock.unlock();
342             }
343         }
344     }
345 
346     /**
347      * Call when data are read from net. It will perform the initial hanshake or decrypt
348      * the data if SSL has been initialiaed.
349      * 
350      * @param buf buffer to decrypt
351      * @param nextFilter Next filter in chain
352      * @throws SSLException on errors
353      */
354     /* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
355         if (LOGGER.isDebugEnabled()) {
356             LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
357         }
358 
359         // append buf to inNetBuffer
360         if (inNetBuffer == null) {
361             inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
362         }
363 
364         inNetBuffer.put(buf);
365 
366         if (!handshakeComplete) {
367             handshake(nextFilter);
368         } else {
369             // Prepare the net data for reading.
370             inNetBuffer.flip();
371 
372             if (!inNetBuffer.hasRemaining()) {
373                 return;
374             }
375 
376             SSLEngineResult res = unwrap();
377 
378             // prepare to be written again
379             if (inNetBuffer.hasRemaining()) {
380                 inNetBuffer.compact();
381             } else {
382                 inNetBuffer.free();
383                 inNetBuffer = null;
384             }
385 
386             checkStatus(res);
387 
388             renegotiateIfNeeded(nextFilter, res);
389         }
390 
391         if (isInboundDone()) {
392             // Rewind the MINA buffer if not all data is processed and inbound
393             // is finished.
394             int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
395             buf.position(buf.position() - inNetBufferPosition);
396 
397             if (inNetBuffer != null) {
398                 inNetBuffer.free();
399                 inNetBuffer = null;
400             }
401         }
402     }
403 
404     /**
405      * Get decrypted application data.
406      * 
407      * @return buffer with data
408      */
409     /* no qualifier */IoBuffer fetchAppBuffer() {
410         if (appBuffer == null) {
411             return IoBuffer.allocate(0);
412         } else {
413             IoBuffer newAppBuffer = appBuffer.flip();
414             appBuffer = null;
415 
416             return newAppBuffer.shrink();
417         }
418     }
419 
420     /**
421      * Get encrypted data to be sent.
422      * 
423      * @return buffer with data
424      */
425     /* no qualifier */IoBuffer fetchOutNetBuffer() {
426         IoBuffer answer = outNetBuffer;
427         
428         if (answer == null) {
429             return emptyBuffer;
430         }
431 
432         outNetBuffer = null;
433         
434         return answer.shrink();
435     }
436 
437     /**
438      * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
439      * 
440      * @param src
441      *            data to encrypt
442      * @throws SSLException
443      *             on errors
444      */
445     /* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
446         if (!handshakeComplete) {
447             throw new IllegalStateException();
448         }
449 
450         if (!src.hasRemaining()) {
451             if (outNetBuffer == null) {
452                 outNetBuffer = emptyBuffer;
453             }
454             return;
455         }
456 
457         createOutNetBuffer(src.remaining());
458 
459         // Loop until there is no more data in src
460         while (src.hasRemaining()) {
461 
462             SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
463             
464             if (result.getStatus() == SSLEngineResult.Status.OK) {
465                 if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
466                     doTasks();
467                 }
468             } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
469                 outNetBuffer.capacity(outNetBuffer.capacity() << 1);
470                 outNetBuffer.limit(outNetBuffer.capacity());
471             } else {
472                 throw new SSLException("SSLEngine error during encrypt: " + result.getStatus() + " src: " + src
473                         + "outNetBuffer: " + outNetBuffer);
474             }
475         }
476 
477         outNetBuffer.flip();
478     }
479 
480     /**
481      * Start SSL shutdown process.
482      * 
483      * @return <tt>true</tt> if shutdown process is started. <tt>false</tt> if
484      *         shutdown process is already finished.
485      * @throws SSLException
486      *             on errors
487      */
488     /* no qualifier */boolean closeOutbound() throws SSLException {
489         if (sslEngine == null || sslEngine.isOutboundDone()) {
490             return false;
491         }
492 
493         sslEngine.closeOutbound();
494 
495         createOutNetBuffer(0);
496         SSLEngineResult result;
497 
498         for (;;) {
499             result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
500             if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
501                 outNetBuffer.capacity(outNetBuffer.capacity() << 1);
502                 outNetBuffer.limit(outNetBuffer.capacity());
503             } else {
504                 break;
505             }
506         }
507 
508         if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
509             throw new SSLException("Improper close state: " + result);
510         }
511 
512         outNetBuffer.flip();
513 
514         return true;
515     }
516 
517     /**
518      * @param res
519      * @throws SSLException
520      */
521     private void checkStatus(SSLEngineResult res) throws SSLException {
522 
523         SSLEngineResult.Status status = res.getStatus();
524 
525         /*
526          * The status may be:
527          * OK - Normal operation
528          * OVERFLOW - Should never happen since the application buffer is sized to hold the maximum
529          * packet size.
530          * UNDERFLOW - Need to read more data from the socket. It's normal.
531          * CLOSED - The other peer closed the socket. Also normal.
532          */
533         switch (status) {
534             case BUFFER_OVERFLOW:
535                 throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
536                     + "appBuffer: " + appBuffer);
537             case CLOSED:
538                 Exception exception =new RuntimeIoException("SSL/TLS close_notify received");
539                 
540                 // Empty the Ssl queue
541                 for (IoFilterEvent event:filterWriteEventQueue) {
542                     EncryptedWriteRequest writeRequest = (EncryptedWriteRequest)event.getParameter();
543                     WriteFuture writeFuture = writeRequest.getParentRequest().getFuture();
544                     writeFuture.setException(exception);
545                     writeFuture.notifyAll();
546                 }
547                 
548                 // Empty the session queue
549                 while (!session.getWriteRequestQueue().isEmpty(session)) {
550                     WriteRequest writeRequest = session.getWriteRequestQueue().poll( session );
551                     WriteFuture writeFuture = writeRequest.getFuture();
552                     writeFuture.setException(exception);
553                     writeFuture.notifyAll();
554                 }
555                 
556                 // We *must* shutdown session
557                 session.closeNow();
558                 break;
559             default: 
560                 break;
561         }
562     }
563 
564     /**
565      * Perform any handshaking processing.
566      */
567     /* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
568         for (;;) {
569             switch (handshakeStatus) {
570             case FINISHED:
571                 if (LOGGER.isDebugEnabled()) {
572                     LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
573                 }
574 
575                 session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
576                 handshakeComplete = true;
577 
578                 // Send the SECURE message only if it's the first SSL handshake
579                 if (firstSSLNegociation) {
580                     firstSSLNegociation = false;
581                     
582                     nextFilter.event(session, SslEvent.SECURED);
583                 }
584 
585                 if (LOGGER.isDebugEnabled()) {
586                     if (!isOutboundDone()) {
587                         LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
588                     } else {
589                         LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
590                     }
591                 }
592 
593                 return;
594 
595             case NEED_TASK:
596                 if (LOGGER.isDebugEnabled()) {
597                     LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
598                 }
599 
600                 handshakeStatus = doTasks();
601                 break;
602 
603             case NEED_UNWRAP:
604                 if (LOGGER.isDebugEnabled()) {
605                     LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
606                 }
607                 // we need more data read
608                 SSLEngineResult.Status status = unwrapHandshake(nextFilter);
609 
610                 if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
611                         && handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
612                     // We need more data or the session is closed
613                     return;
614                 }
615 
616                 break;
617 
618             case NEED_WRAP:
619             case NOT_HANDSHAKING:
620                 if (LOGGER.isDebugEnabled()) {
621                     LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
622                 }
623 
624                 // First make sure that the out buffer is completely empty.
625                 // Since we cannot call wrap with data left on the buffer
626                 if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
627                     return;
628                 }
629 
630                 SSLEngineResult result;
631                 createOutNetBuffer(0);
632 
633                 result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
634 
635                 while ( result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW ) {
636                     outNetBuffer.capacity(outNetBuffer.capacity() << 1);
637                     outNetBuffer.limit(outNetBuffer.capacity());
638 
639                     result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
640                 }
641 
642                 outNetBuffer.flip();
643                 handshakeStatus = result.getHandshakeStatus();
644                 writeNetBuffer(nextFilter);
645                 break;
646 
647             default:
648                 String msg = "Invalid Handshaking State" + handshakeStatus
649                 + " while processing the Handshake for session " + session.getId();
650                 LOGGER.error(msg);
651                 throw new IllegalStateException(msg);
652             }
653         }
654     }
655 
656     private void createOutNetBuffer(int expectedRemaining) {
657         // SSLEngine requires us to allocate unnecessarily big buffer
658         // even for small data. *Shrug*
659         int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
660 
661         if (outNetBuffer != null) {
662             outNetBuffer.capacity(capacity);
663         } else {
664             outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
665         }
666     }
667 
668     /* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
669         // Check if any net data needed to be writen
670         if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
671             // no; bail out
672             return null;
673         }
674 
675         // set flag that we are writing encrypted data
676         // (used in SSLFilter.filterWrite())
677         writingEncryptedData = true;
678 
679         // write net data
680         WriteFuture writeFuture = null;
681 
682         try {
683             IoBuffer writeBuffer = fetchOutNetBuffer();
684             writeFuture = new DefaultWriteFuture(session);
685             sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
686 
687             // loop while more writes required to complete handshake
688             while (needToCompleteHandshake()) {
689                 try {
690                     handshake(nextFilter);
691                 } catch (SSLException ssle) {
692                     SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
693                     newSsle.initCause(ssle);
694                     throw newSsle;
695                 }
696 
697                 IoBuffer currentOutNetBuffer = fetchOutNetBuffer();
698                 
699                 if (currentOutNetBuffer != null && currentOutNetBuffer.hasRemaining()) {
700                     writeFuture = new DefaultWriteFuture(session);
701                     sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(currentOutNetBuffer, writeFuture));
702                 }
703             }
704         } finally {
705             writingEncryptedData = false;
706         }
707 
708         return writeFuture;
709     }
710 
711     private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
712         // Prepare the net data for reading.
713         if (inNetBuffer != null) {
714             inNetBuffer.flip();
715         }
716 
717         if ((inNetBuffer == null) || !inNetBuffer.hasRemaining()) {
718             // Need more data.
719             return SSLEngineResult.Status.BUFFER_UNDERFLOW;
720         }
721 
722         SSLEngineResult res = unwrap();
723         handshakeStatus = res.getHandshakeStatus();
724 
725         checkStatus(res);
726 
727         // If handshake finished, no data was produced, and the status is still
728         // ok, try to unwrap more
729         if ((handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
730                 && (res.getStatus() == SSLEngineResult.Status.OK)
731                 && inNetBuffer.hasRemaining()) {
732             res = unwrap();
733 
734             // prepare to be written again
735             if (inNetBuffer.hasRemaining()) {
736                 inNetBuffer.compact();
737             } else {
738                 inNetBuffer.free();
739                 inNetBuffer = null;
740             }
741 
742             renegotiateIfNeeded(nextFilter, res);
743         } else {
744             // prepare to be written again
745             if (inNetBuffer.hasRemaining()) {
746                 inNetBuffer.compact();
747             } else {
748                 inNetBuffer.free();
749                 inNetBuffer = null;
750             }
751         }
752 
753         return res.getStatus();
754     }
755 
756     private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
757         if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
758                 && (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
759                 && (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
760             // Renegotiation required.
761             handshakeComplete = false;
762             handshakeStatus = res.getHandshakeStatus();
763             handshake(nextFilter);
764         }
765     }
766 
767     /**
768      * Decrypt the incoming buffer and move the decrypted data to an
769      * application buffer.
770      */
771     private SSLEngineResult unwrap() throws SSLException {
772         // We first have to create the application buffer if it does not exist
773         if (appBuffer == null) {
774             appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
775         } else {
776             // We already have one, just add the new data into it
777             appBuffer.expand(inNetBuffer.remaining());
778         }
779 
780         SSLEngineResult res;
781         Status status;
782         HandshakeStatus localHandshakeStatus;
783 
784         do {
785             // Decode the incoming data
786             res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
787             status = res.getStatus();
788 
789             // We can be processing the Handshake
790             localHandshakeStatus = res.getHandshakeStatus();
791 
792             if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
793                 // We have to grow the target buffer, it's too small.
794                 // Then we can call the unwrap method again
795                 int newCapacity = sslEngine.getSession().getApplicationBufferSize();
796                 
797                 if (appBuffer.remaining() >= newCapacity) {
798                     // The buffer is already larger than the max buffer size suggested by the SSL engine.
799                     // Raising it any more will not make sense and it will end up in an endless loop. Throwing an error is safer
800                     throw new SSLException("SSL buffer overflow");
801                 }
802 
803                 appBuffer.expand(newCapacity);
804                 continue;
805             }
806         } while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
807                 && ((localHandshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) || 
808                         (localHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
809 
810         return res;
811     }
812 
813     /**
814      * Do all the outstanding handshake tasks in the current Thread.
815      */
816     private SSLEngineResult.HandshakeStatus doTasks() {
817         /*
818          * We could run this in a separate thread, but I don't see the need for
819          * this when used from SSLFilter. Use thread filters in MINA instead?
820          */
821         Runnable runnable;
822         while ((runnable = sslEngine.getDelegatedTask()) != null) {
823             // TODO : we may have to use a thread pool here to improve the
824             // performances
825             runnable.run();
826         }
827         return sslEngine.getHandshakeStatus();
828     }
829 
830     /**
831      * Creates a new MINA buffer that is a deep copy of the remaining bytes in
832      * the given buffer (between index buf.position() and buf.limit())
833      * 
834      * @param src
835      *            the buffer to copy
836      * @return the new buffer, ready to read from
837      */
838     /* no qualifier */static IoBuffer copy(ByteBuffer src) {
839         IoBuffer copy = IoBuffer.allocate(src.remaining());
840         copy.put(src);
841         copy.flip();
842         return copy;
843     }
844 
845     /**
846      * {@inheritDoc}
847      */
848     @Override
849     public String toString() {
850         StringBuilder sb = new StringBuilder();
851 
852         sb.append("SSLStatus <");
853 
854         if (handshakeComplete) {
855             sb.append("SSL established");
856         } else {
857             sb.append("Processing Handshake").append("; ");
858             sb.append("Status : ").append(handshakeStatus).append("; ");
859         }
860 
861         sb.append(", ");
862         sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
863         sb.append(">");
864 
865         return sb.toString();
866     }
867 
868     /**
869      * Free the allocated buffers
870      */
871     /* no qualifier */void release() {
872         if (inNetBuffer != null) {
873             inNetBuffer.free();
874             inNetBuffer = null;
875         }
876 
877         if (outNetBuffer != null) {
878             outNetBuffer.free();
879             outNetBuffer = null;
880         }
881     }
882 }