1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.io.nio;
20
21import java.io.IOException;
22import java.nio.ByteBuffer;
23import java.util.concurrent.atomic.AtomicBoolean;
24import javax.net.ssl.SSLEngine;
25import javax.net.ssl.SSLEngineResult;
26import javax.net.ssl.SSLEngineResult.HandshakeStatus;
27import javax.net.ssl.SSLException;
28import javax.net.ssl.SSLSession;
29
30import org.eclipse.jetty.io.AbstractConnection;
31import org.eclipse.jetty.io.AsyncEndPoint;
32import org.eclipse.jetty.io.Buffer;
33import org.eclipse.jetty.io.Connection;
34import org.eclipse.jetty.io.EndPoint;
35import org.eclipse.jetty.util.log.Log;
36import org.eclipse.jetty.util.log.Logger;
37import org.eclipse.jetty.util.thread.Timeout.Task;
38
39/* ------------------------------------------------------------ */
40/** SSL Connection.
41 * An AysyncConnection that acts as an interceptor between and EndPoint and another
42 * Connection, that implements TLS encryption using an {@link SSLEngine}.
43 * <p>
44 * The connector uses an {@link AsyncEndPoint} (like {@link SelectChannelEndPoint}) as
45 * it's source/sink of encrypted data.   It then provides {@link #getSslEndPoint()} to
46 * expose a source/sink of unencrypted data to another connection (eg HttpConnection).
47 */
48public class SslConnection extends AbstractConnection implements AsyncConnection
49{
50    private final Logger _logger = Log.getLogger("org.eclipse.jetty.io.nio.ssl");
51
52    private static final NIOBuffer __ZERO_BUFFER=new IndirectNIOBuffer(0);
53
54    private static final ThreadLocal<SslBuffers> __buffers = new ThreadLocal<SslBuffers>();
55    private final SSLEngine _engine;
56    private final SSLSession _session;
57    private AsyncConnection _connection;
58    private final SslEndPoint _sslEndPoint;
59    private int _allocations;
60    private SslBuffers _buffers;
61    private NIOBuffer _inbound;
62    private NIOBuffer _unwrapBuf;
63    private NIOBuffer _outbound;
64    private AsyncEndPoint _aEndp;
65    private boolean _allowRenegotiate=true;
66    private boolean _handshook;
67    private boolean _ishut;
68    private boolean _oshut;
69    private final AtomicBoolean _progressed = new AtomicBoolean();
70
71    /* ------------------------------------------------------------ */
72    /* this is a half baked buffer pool
73     */
74    private static class SslBuffers
75    {
76        final NIOBuffer _in;
77        final NIOBuffer _out;
78        final NIOBuffer _unwrap;
79
80        SslBuffers(int packetSize, int appSize)
81        {
82            _in=new IndirectNIOBuffer(packetSize);
83            _out=new IndirectNIOBuffer(packetSize);
84            _unwrap=new IndirectNIOBuffer(appSize);
85        }
86    }
87
88    /* ------------------------------------------------------------ */
89    public SslConnection(SSLEngine engine,EndPoint endp)
90    {
91        this(engine,endp,System.currentTimeMillis());
92    }
93
94    /* ------------------------------------------------------------ */
95    public SslConnection(SSLEngine engine,EndPoint endp, long timeStamp)
96    {
97        super(endp,timeStamp);
98        _engine=engine;
99        _session=_engine.getSession();
100        _aEndp=(AsyncEndPoint)endp;
101        _sslEndPoint = newSslEndPoint();
102    }
103
104    /* ------------------------------------------------------------ */
105    protected SslEndPoint newSslEndPoint()
106    {
107        return new SslEndPoint();
108    }
109
110    /* ------------------------------------------------------------ */
111    /**
112     * @return True if SSL re-negotiation is allowed (default false)
113     */
114    public boolean isAllowRenegotiate()
115    {
116        return _allowRenegotiate;
117    }
118
119    /* ------------------------------------------------------------ */
120    /**
121     * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
122     * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
123     * does not have CVE-2009-3555 fixed, then re-negotiation should
124     * not be allowed.  CVE-2009-3555 was fixed in Sun java 1.6 with a ban
125     * of renegotiates in u19 and with RFC5746 in u22.
126     *
127     * @param allowRenegotiate
128     *            true if re-negotiation is allowed (default false)
129     */
130    public void setAllowRenegotiate(boolean allowRenegotiate)
131    {
132        _allowRenegotiate = allowRenegotiate;
133    }
134
135    /* ------------------------------------------------------------ */
136    private void allocateBuffers()
137    {
138        synchronized (this)
139        {
140            if (_allocations++==0)
141            {
142                if (_buffers==null)
143                {
144                    _buffers=__buffers.get();
145                    if (_buffers==null)
146                        _buffers=new SslBuffers(_session.getPacketBufferSize()*2,_session.getApplicationBufferSize()*2);
147                    _inbound=_buffers._in;
148                    _outbound=_buffers._out;
149                    _unwrapBuf=_buffers._unwrap;
150                    __buffers.set(null);
151                }
152            }
153        }
154    }
155
156    /* ------------------------------------------------------------ */
157    private void releaseBuffers()
158    {
159        synchronized (this)
160        {
161            if (--_allocations==0)
162            {
163                if (_buffers!=null &&
164                    _inbound.length()==0 &&
165                    _outbound.length()==0 &&
166                    _unwrapBuf.length()==0)
167                {
168                    _inbound=null;
169                    _outbound=null;
170                    _unwrapBuf=null;
171                    __buffers.set(_buffers);
172                    _buffers=null;
173                }
174            }
175        }
176    }
177
178    /* ------------------------------------------------------------ */
179    public Connection handle() throws IOException
180    {
181        try
182        {
183            allocateBuffers();
184
185            boolean progress=true;
186
187            while (progress)
188            {
189                progress=false;
190
191                // If we are handshook let the delegate connection
192                if (_engine.getHandshakeStatus()!=HandshakeStatus.NOT_HANDSHAKING)
193                    progress=process(null,null);
194
195                // handle the delegate connection
196                AsyncConnection next = (AsyncConnection)_connection.handle();
197                if (next!=_connection && next!=null)
198                {
199                    _connection=next;
200                    progress=true;
201                }
202
203                _logger.debug("{} handle {} progress={}", _session, this, progress);
204            }
205        }
206        finally
207        {
208            releaseBuffers();
209
210            if (!_ishut && _sslEndPoint.isInputShutdown() && _sslEndPoint.isOpen())
211            {
212                _ishut=true;
213                try
214                {
215                    _connection.onInputShutdown();
216                }
217                catch(Throwable x)
218                {
219                    _logger.warn("onInputShutdown failed", x);
220                    try{_sslEndPoint.close();}
221                    catch(IOException e2){
222                        _logger.ignore(e2);}
223                }
224            }
225        }
226
227        return this;
228    }
229
230    /* ------------------------------------------------------------ */
231    public boolean isIdle()
232    {
233        return false;
234    }
235
236    /* ------------------------------------------------------------ */
237    public boolean isSuspended()
238    {
239        return false;
240    }
241
242    /* ------------------------------------------------------------ */
243    public void onClose()
244    {
245        Connection connection = _sslEndPoint.getConnection();
246        if (connection != null && connection != this)
247            connection.onClose();
248    }
249
250    /* ------------------------------------------------------------ */
251    @Override
252    public void onIdleExpired(long idleForMs)
253    {
254        try
255        {
256            _logger.debug("onIdleExpired {}ms on {}",idleForMs,this);
257            if (_endp.isOutputShutdown())
258                _sslEndPoint.close();
259            else
260                _sslEndPoint.shutdownOutput();
261        }
262        catch (IOException e)
263        {
264            _logger.warn(e);
265            super.onIdleExpired(idleForMs);
266        }
267    }
268
269    /* ------------------------------------------------------------ */
270    public void onInputShutdown() throws IOException
271    {
272
273    }
274
275    /* ------------------------------------------------------------ */
276    private synchronized boolean process(Buffer toFill, Buffer toFlush) throws IOException
277    {
278        boolean some_progress=false;
279        try
280        {
281            // We need buffers to progress
282            allocateBuffers();
283
284            // if we don't have a buffer to put received data into
285            if (toFill==null)
286            {
287                // use the unwrapbuffer to hold received data.
288                _unwrapBuf.compact();
289                toFill=_unwrapBuf;
290            }
291            // Else if the fill buffer is too small for the SSL session
292            else if (toFill.capacity()<_session.getApplicationBufferSize())
293            {
294                // fill to the temporary unwrapBuffer
295                boolean progress=process(null,toFlush);
296
297                // if we received any data,
298                if (_unwrapBuf!=null && _unwrapBuf.hasContent())
299                {
300                    // transfer from temp buffer to fill buffer
301                    _unwrapBuf.skip(toFill.put(_unwrapBuf));
302                    return true;
303                }
304                else
305                    // return progress from recursive call
306                    return progress;
307            }
308            // Else if there is some temporary data
309            else if (_unwrapBuf!=null && _unwrapBuf.hasContent())
310            {
311                // transfer from temp buffer to fill buffer
312                _unwrapBuf.skip(toFill.put(_unwrapBuf));
313                return true;
314            }
315
316            // If we are here, we have a buffer ready into which we can put some read data.
317
318            // If we have no data to flush, flush the empty buffer
319            if (toFlush==null)
320                toFlush=__ZERO_BUFFER;
321
322            // While we are making progress processing SSL engine
323            boolean progress=true;
324            while (progress)
325            {
326                progress=false;
327
328                // Do any real IO
329                int filled=0,flushed=0;
330                try
331                {
332                    // Read any available data
333                    if (_inbound.space()>0 && (filled=_endp.fill(_inbound))>0)
334                        progress = true;
335
336                    // flush any output data
337                    if (_outbound.hasContent() && (flushed=_endp.flush(_outbound))>0)
338                        progress = true;
339                }
340                catch (IOException e)
341                {
342                    _endp.close();
343                    throw e;
344                }
345                finally
346                {
347                    _logger.debug("{} {} {} filled={}/{} flushed={}/{}",_session,this,_engine.getHandshakeStatus(),filled,_inbound.length(),flushed,_outbound.length());
348                }
349
350                // handle the current hand share status
351                switch(_engine.getHandshakeStatus())
352                {
353                    case FINISHED:
354                        throw new IllegalStateException();
355
356                    case NOT_HANDSHAKING:
357                    {
358                        // Try unwrapping some application data
359                        if (toFill.space()>0 && _inbound.hasContent() && unwrap(toFill))
360                            progress=true;
361
362                        // Try wrapping some application data
363                        if (toFlush.hasContent() && _outbound.space()>0 && wrap(toFlush))
364                            progress=true;
365                    }
366                    break;
367
368                    case NEED_TASK:
369                    {
370                        // A task needs to be run, so run it!
371                        Runnable task;
372                        while ((task=_engine.getDelegatedTask())!=null)
373                        {
374                            progress=true;
375                            task.run();
376                        }
377
378                    }
379                    break;
380
381                    case NEED_WRAP:
382                    {
383                        // The SSL needs to send some handshake data to the other side
384                        if (_handshook && !_allowRenegotiate)
385                            _endp.close();
386                        else if (wrap(toFlush))
387                            progress=true;
388                    }
389                    break;
390
391                    case NEED_UNWRAP:
392                    {
393                        // The SSL needs to receive some handshake data from the other side
394                        if (_handshook && !_allowRenegotiate)
395                            _endp.close();
396                        else if (!_inbound.hasContent()&&filled==-1)
397                        {
398                            // No more input coming
399                            _endp.shutdownInput();
400                        }
401                        else if (unwrap(toFill))
402                            progress=true;
403                    }
404                    break;
405                }
406
407                // pass on ishut/oshut state
408                if (_endp.isOpen() && _endp.isInputShutdown() && !_inbound.hasContent())
409                    closeInbound();
410
411                if (_endp.isOpen() && _engine.isOutboundDone() && !_outbound.hasContent())
412                    _endp.shutdownOutput();
413
414                // remember if any progress has been made
415                some_progress|=progress;
416            }
417
418            // If we are reading into the temp buffer and it has some content, then we should be dispatched.
419            if (toFill==_unwrapBuf && _unwrapBuf.hasContent() && !_connection.isSuspended())
420                _aEndp.dispatch();
421        }
422        finally
423        {
424            releaseBuffers();
425            if (some_progress)
426                _progressed.set(true);
427        }
428        return some_progress;
429    }
430
431    private void closeInbound()
432    {
433        try
434        {
435            _engine.closeInbound();
436        }
437        catch (SSLException x)
438        {
439            _logger.debug(x);
440        }
441    }
442
443    private synchronized boolean wrap(final Buffer buffer) throws IOException
444    {
445        ByteBuffer bbuf=extractByteBuffer(buffer);
446        final SSLEngineResult result;
447
448        synchronized(bbuf)
449        {
450            _outbound.compact();
451            ByteBuffer out_buffer=_outbound.getByteBuffer();
452            synchronized(out_buffer)
453            {
454                try
455                {
456                    bbuf.position(buffer.getIndex());
457                    bbuf.limit(buffer.putIndex());
458                    out_buffer.position(_outbound.putIndex());
459                    out_buffer.limit(out_buffer.capacity());
460                    result=_engine.wrap(bbuf,out_buffer);
461                    if (_logger.isDebugEnabled())
462                        _logger.debug("{} wrap {} {} consumed={} produced={}",
463                            _session,
464                            result.getStatus(),
465                            result.getHandshakeStatus(),
466                            result.bytesConsumed(),
467                            result.bytesProduced());
468
469
470                    buffer.skip(result.bytesConsumed());
471                    _outbound.setPutIndex(_outbound.putIndex()+result.bytesProduced());
472                }
473                catch(SSLException e)
474                {
475                    _logger.debug(String.valueOf(_endp), e);
476                    _endp.close();
477                    throw e;
478                }
479                finally
480                {
481                    out_buffer.position(0);
482                    out_buffer.limit(out_buffer.capacity());
483                    bbuf.position(0);
484                    bbuf.limit(bbuf.capacity());
485                }
486            }
487        }
488
489        switch(result.getStatus())
490        {
491            case BUFFER_UNDERFLOW:
492                throw new IllegalStateException();
493
494            case BUFFER_OVERFLOW:
495                break;
496
497            case OK:
498                if (result.getHandshakeStatus()==HandshakeStatus.FINISHED)
499                    _handshook=true;
500                break;
501
502            case CLOSED:
503                _logger.debug("wrap CLOSE {} {}",this,result);
504                if (result.getHandshakeStatus()==HandshakeStatus.FINISHED)
505                    _endp.close();
506                break;
507
508            default:
509                _logger.debug("{} wrap default {}",_session,result);
510            throw new IOException(result.toString());
511        }
512
513        return result.bytesConsumed()>0 || result.bytesProduced()>0;
514    }
515
516    private synchronized boolean unwrap(final Buffer buffer) throws IOException
517    {
518        if (!_inbound.hasContent())
519            return false;
520
521        ByteBuffer bbuf=extractByteBuffer(buffer);
522        final SSLEngineResult result;
523
524        synchronized(bbuf)
525        {
526            ByteBuffer in_buffer=_inbound.getByteBuffer();
527            synchronized(in_buffer)
528            {
529                try
530                {
531                    bbuf.position(buffer.putIndex());
532                    bbuf.limit(buffer.capacity());
533                    in_buffer.position(_inbound.getIndex());
534                    in_buffer.limit(_inbound.putIndex());
535
536                    result=_engine.unwrap(in_buffer,bbuf);
537                    if (_logger.isDebugEnabled())
538                        _logger.debug("{} unwrap {} {} consumed={} produced={}",
539                            _session,
540                            result.getStatus(),
541                            result.getHandshakeStatus(),
542                            result.bytesConsumed(),
543                            result.bytesProduced());
544
545                    _inbound.skip(result.bytesConsumed());
546                    _inbound.compact();
547                    buffer.setPutIndex(buffer.putIndex()+result.bytesProduced());
548                }
549                catch(SSLException e)
550                {
551                    _logger.debug(String.valueOf(_endp), e);
552                    _endp.close();
553                    throw e;
554                }
555                finally
556                {
557                    in_buffer.position(0);
558                    in_buffer.limit(in_buffer.capacity());
559                    bbuf.position(0);
560                    bbuf.limit(bbuf.capacity());
561                }
562            }
563        }
564
565        switch(result.getStatus())
566        {
567            case BUFFER_UNDERFLOW:
568                if (_endp.isInputShutdown())
569                    _inbound.clear();
570                break;
571
572            case BUFFER_OVERFLOW:
573                if (_logger.isDebugEnabled()) _logger.debug("{} unwrap {} {}->{}",_session,result.getStatus(),_inbound.toDetailString(),buffer.toDetailString());
574                break;
575
576            case OK:
577                if (result.getHandshakeStatus()==HandshakeStatus.FINISHED)
578                    _handshook=true;
579                break;
580
581            case CLOSED:
582                _logger.debug("unwrap CLOSE {} {}",this,result);
583                if (result.getHandshakeStatus()==HandshakeStatus.FINISHED)
584                    _endp.close();
585                break;
586
587            default:
588                _logger.debug("{} wrap default {}",_session,result);
589            throw new IOException(result.toString());
590        }
591
592        //if (LOG.isDebugEnabled() && result.bytesProduced()>0)
593        //    LOG.debug("{} unwrapped '{}'",_session,buffer);
594
595        return result.bytesConsumed()>0 || result.bytesProduced()>0;
596    }
597
598
599    /* ------------------------------------------------------------ */
600    private ByteBuffer extractByteBuffer(Buffer buffer)
601    {
602        if (buffer.buffer() instanceof NIOBuffer)
603            return ((NIOBuffer)buffer.buffer()).getByteBuffer();
604        return ByteBuffer.wrap(buffer.array());
605    }
606
607    /* ------------------------------------------------------------ */
608    public AsyncEndPoint getSslEndPoint()
609    {
610        return _sslEndPoint;
611    }
612
613    /* ------------------------------------------------------------ */
614    public String toString()
615    {
616        return String.format("%s %s", super.toString(), _sslEndPoint);
617    }
618
619    /* ------------------------------------------------------------ */
620    /* ------------------------------------------------------------ */
621    public class SslEndPoint implements AsyncEndPoint
622    {
623        public SSLEngine getSslEngine()
624        {
625            return _engine;
626        }
627
628        public AsyncEndPoint getEndpoint()
629        {
630            return _aEndp;
631        }
632
633        public void shutdownOutput() throws IOException
634        {
635            synchronized (SslConnection.this)
636            {
637                _logger.debug("{} ssl endp.oshut {}",_session,this);
638                _engine.closeOutbound();
639                _oshut=true;
640            }
641            flush();
642        }
643
644        public boolean isOutputShutdown()
645        {
646            synchronized (SslConnection.this)
647            {
648                return _oshut||!isOpen()||_engine.isOutboundDone();
649            }
650        }
651
652        public void shutdownInput() throws IOException
653        {
654            _logger.debug("{} ssl endp.ishut!",_session);
655            // We do not do a closeInput here, as SSL does not support half close.
656            // isInputShutdown works it out itself from buffer state and underlying endpoint state.
657        }
658
659        public boolean isInputShutdown()
660        {
661            synchronized (SslConnection.this)
662            {
663                return _endp.isInputShutdown() &&
664                !(_unwrapBuf!=null&&_unwrapBuf.hasContent()) &&
665                !(_inbound!=null&&_inbound.hasContent());
666            }
667        }
668
669        public void close() throws IOException
670        {
671            _logger.debug("{} ssl endp.close",_session);
672            _endp.close();
673        }
674
675        public int fill(Buffer buffer) throws IOException
676        {
677            int size=buffer.length();
678            process(buffer, null);
679
680            int filled=buffer.length()-size;
681
682            if (filled==0 && isInputShutdown())
683                return -1;
684            return filled;
685        }
686
687        public int flush(Buffer buffer) throws IOException
688        {
689            int size = buffer.length();
690            process(null, buffer);
691            return size-buffer.length();
692        }
693
694        public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
695        {
696            if (header!=null && header.hasContent())
697                return flush(header);
698            if (buffer!=null && buffer.hasContent())
699                return flush(buffer);
700            if (trailer!=null && trailer.hasContent())
701                return flush(trailer);
702            return 0;
703        }
704
705        public boolean blockReadable(long millisecs) throws IOException
706        {
707            long now = System.currentTimeMillis();
708            long end=millisecs>0?(now+millisecs):Long.MAX_VALUE;
709
710            while (now<end)
711            {
712                if (process(null,null))
713                    break;
714                _endp.blockReadable(end-now);
715                now = System.currentTimeMillis();
716            }
717
718            return now<end;
719        }
720
721        public boolean blockWritable(long millisecs) throws IOException
722        {
723            return _endp.blockWritable(millisecs);
724        }
725
726        public boolean isOpen()
727        {
728            return _endp.isOpen();
729        }
730
731        public Object getTransport()
732        {
733            return _endp;
734        }
735
736        public void flush() throws IOException
737        {
738            process(null, null);
739        }
740
741        public void dispatch()
742        {
743            _aEndp.dispatch();
744        }
745
746        public void asyncDispatch()
747        {
748            _aEndp.asyncDispatch();
749        }
750
751        public void scheduleWrite()
752        {
753            _aEndp.scheduleWrite();
754        }
755
756        public void onIdleExpired(long idleForMs)
757        {
758            _aEndp.onIdleExpired(idleForMs);
759        }
760
761        public void setCheckForIdle(boolean check)
762        {
763            _aEndp.setCheckForIdle(check);
764        }
765
766        public boolean isCheckForIdle()
767        {
768            return _aEndp.isCheckForIdle();
769        }
770
771        public void scheduleTimeout(Task task, long timeoutMs)
772        {
773            _aEndp.scheduleTimeout(task,timeoutMs);
774        }
775
776        public void cancelTimeout(Task task)
777        {
778            _aEndp.cancelTimeout(task);
779        }
780
781        public boolean isWritable()
782        {
783            return _aEndp.isWritable();
784        }
785
786        public boolean hasProgressed()
787        {
788            return _progressed.getAndSet(false);
789        }
790
791        public String getLocalAddr()
792        {
793            return _aEndp.getLocalAddr();
794        }
795
796        public String getLocalHost()
797        {
798            return _aEndp.getLocalHost();
799        }
800
801        public int getLocalPort()
802        {
803            return _aEndp.getLocalPort();
804        }
805
806        public String getRemoteAddr()
807        {
808            return _aEndp.getRemoteAddr();
809        }
810
811        public String getRemoteHost()
812        {
813            return _aEndp.getRemoteHost();
814        }
815
816        public int getRemotePort()
817        {
818            return _aEndp.getRemotePort();
819        }
820
821        public boolean isBlocking()
822        {
823            return false;
824        }
825
826        public int getMaxIdleTime()
827        {
828            return _aEndp.getMaxIdleTime();
829        }
830
831        public void setMaxIdleTime(int timeMs) throws IOException
832        {
833            _aEndp.setMaxIdleTime(timeMs);
834        }
835
836        public Connection getConnection()
837        {
838            return _connection;
839        }
840
841        public void setConnection(Connection connection)
842        {
843            _connection=(AsyncConnection)connection;
844        }
845
846        public String toString()
847        {
848            // Do NOT use synchronized (SslConnection.this)
849            // because it's very easy to deadlock when debugging is enabled.
850            // We do a best effort to print the right toString() and that's it.
851            Buffer inbound = _inbound;
852            Buffer outbound = _outbound;
853            Buffer unwrap = _unwrapBuf;
854            int i = inbound == null? -1 : inbound.length();
855            int o = outbound == null ? -1 : outbound.length();
856            int u = unwrap == null ? -1 : unwrap.length();
857            return String.format("SSL %s i/o/u=%d/%d/%d ishut=%b oshut=%b {%s}",
858                    _engine.getHandshakeStatus(),
859                    i, o, u,
860                    _ishut, _oshut,
861                    _connection);
862        }
863
864    }
865}
866