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