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