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