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