View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
12  // ========================================================================
13  
14  package org.eclipse.jetty.io.nio;
15  
16  import java.io.IOException;
17  import java.nio.ByteBuffer;
18  import java.nio.channels.SelectionKey;
19  import java.nio.channels.SocketChannel;
20  import javax.net.ssl.SSLEngine;
21  import javax.net.ssl.SSLEngineResult;
22  import javax.net.ssl.SSLEngineResult.HandshakeStatus;
23  import javax.net.ssl.SSLException;
24  import javax.net.ssl.SSLSession;
25  
26  import org.eclipse.jetty.io.Buffer;
27  import org.eclipse.jetty.io.Buffers;
28  import org.eclipse.jetty.io.EofException;
29  import org.eclipse.jetty.util.log.Log;
30  import org.eclipse.jetty.util.log.Logger;
31  
32  /* ------------------------------------------------------------ */
33  /**
34   * SslSelectChannelEndPoint
35   * <p>
36   * A SelectChannelEndPoint that uses an {@link SSLEngine} to handle an
37   * SSL connection.
38   * <p>
39   * There is a named logger "org.eclipse.jetty.http.ssl"
40   * </p>
41   */
42  public class SslSelectChannelEndPoint extends SelectChannelEndPoint
43  {
44      public static final Logger LOG=Log.getLogger("org.eclipse.jetty.io.nio").getLogger("ssl");
45  
46      private static final Buffer __EMPTY_BUFFER=new DirectNIOBuffer(0);
47      private static final ByteBuffer __ZERO_BUFFER=ByteBuffer.allocate(0);
48  
49      private final Buffers _buffers;
50  
51      private final SSLEngine _engine;
52      private final SSLSession _session;
53      private volatile NIOBuffer _inNIOBuffer;
54      private volatile NIOBuffer _outNIOBuffer;
55  
56      private boolean _closing=false;
57      private SSLEngineResult _result;
58  
59      private volatile boolean _handshook=false;
60      private boolean _allowRenegotiate=true;
61  
62      private volatile boolean _debug = LOG.isDebugEnabled(); // snapshot debug status for optimizer
63  
64      /* ------------------------------------------------------------ */
65      public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine, int maxIdleTime)
66              throws IOException
67      {
68          super(channel,selectSet,key, maxIdleTime);
69          _buffers=buffers;
70  
71          // ssl
72          _engine=engine;
73          _session=engine.getSession();
74  
75          if (_debug) LOG.debug(_session+" channel="+channel);
76      }
77  
78      /* ------------------------------------------------------------ */
79      public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine)
80              throws IOException
81      {
82          super(channel,selectSet,key);
83          _buffers=buffers;
84  
85          // ssl
86          _engine=engine;
87          _session=engine.getSession();
88  
89          if (_debug) LOG.debug(_session+" channel="+channel);
90      }
91  
92  
93      /* ------------------------------------------------------------ */
94      private void needOutBuffer()
95      {
96          synchronized (this)
97          {
98              if (_outNIOBuffer==null)
99                  _outNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
100         }
101     }
102 
103     /* ------------------------------------------------------------ */
104     private void freeOutBuffer()
105     {
106         synchronized (this)
107         {
108             if (_outNIOBuffer!=null && _outNIOBuffer.length()==0)
109             {
110                 _buffers.returnBuffer(_outNIOBuffer);
111                 _outNIOBuffer=null;
112             }
113         }
114     }
115 
116     /* ------------------------------------------------------------ */
117     private void needInBuffer()
118     {
119         synchronized (this)
120         {
121             if(_inNIOBuffer==null)
122                 _inNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
123         }
124     }
125 
126     /* ------------------------------------------------------------ */
127     private void freeInBuffer()
128     {
129         synchronized (this)
130         {
131             if (_inNIOBuffer!=null && _inNIOBuffer.length()==0)
132             {
133                 _buffers.returnBuffer(_inNIOBuffer);
134                 _inNIOBuffer=null;
135             }
136         }
137     }
138 
139     /* ------------------------------------------------------------ */
140     /**
141      * @return True if the endpoint has produced/consumed bytes itself (non application data).
142      */
143     public boolean isProgressing()
144     {
145         SSLEngineResult result = _result;
146         _result=null;
147         return result!=null && (result.bytesConsumed()>0 || result.bytesProduced()>0);
148     }
149 
150     /* ------------------------------------------------------------ */
151     /**
152      * @return True if SSL re-negotiation is allowed (default false)
153      */
154     public boolean isAllowRenegotiate()
155     {
156         return _allowRenegotiate;
157     }
158 
159     /* ------------------------------------------------------------ */
160     /**
161      * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
162      * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
163      * does not have CVE-2009-3555 fixed, then re-negotiation should
164      * not be allowed.
165      * @param allowRenegotiate true if re-negotiation is allowed (default false)
166      */
167     public void setAllowRenegotiate(boolean allowRenegotiate)
168     {
169         _allowRenegotiate = allowRenegotiate;
170     }
171 
172 
173     /* ------------------------------------------------------------ */
174     @Override
175     public boolean isOutputShutdown()
176     {
177         return _engine!=null && _engine.isOutboundDone();
178     }
179 
180     /* ------------------------------------------------------------ */
181     @Override
182     public boolean isInputShutdown()
183     {
184         return _engine!=null && _engine.isInboundDone();
185     }
186 
187     /* ------------------------------------------------------------ */
188     @Override
189     public void shutdownOutput() throws IOException
190     {
191         LOG.debug("{} shutdownOutput",_session);
192         // All SSL closes should be graceful, as it is more secure.
193         // So normal SSL close can be used here.
194         close();
195     }
196 
197     /* ------------------------------------------------------------ */
198     private int process(ByteBuffer inBBuf, Buffer outBuf) throws IOException
199     {
200         if (_debug)
201             LOG.debug("{} process closing={} in={} out={}",_session,_closing,inBBuf,outBuf);
202 
203         // If there is no place to put incoming application data,
204         if (inBBuf==null)
205         {
206             // use ZERO buffer
207             inBBuf=__ZERO_BUFFER;
208         }
209 
210         int received=0;
211         int sent=0;
212 
213 
214         HandshakeStatus initialStatus = _engine.getHandshakeStatus();
215         boolean progress=true;
216 
217         while (progress)
218         {
219             progress=false;
220 
221             // flush output data
222             int len=_outNIOBuffer==null?0:_outNIOBuffer.length();
223 
224             // we must flush it, as the other end might be
225             // waiting for that outgoing data before sending
226             // more incoming data
227             flush();
228 
229             // If we have written some bytes, then progress has been made.
230             progress|=(_outNIOBuffer==null?0:_outNIOBuffer.length())<len;
231 
232             // handle the current hand share status
233             if (_debug) LOG.debug("status {} {}",_engine,_engine.getHandshakeStatus());
234             switch(_engine.getHandshakeStatus())
235             {
236                 case FINISHED:
237                     throw new IllegalStateException();
238 
239                 case NOT_HANDSHAKING:
240 
241                     // If closing, don't process application data
242                     if (_closing)
243                     {
244                         if (outBuf!=null && outBuf.hasContent())
245                         {
246                             LOG.debug("Write while closing");
247                             outBuf.clear();
248                         }
249                         break;
250                     }
251 
252                     // Try wrapping some application data
253                     if (outBuf!=null && outBuf.hasContent())
254                     {
255                         int c=wrap(outBuf);
256                         progress=c>0||_result.bytesProduced()>0||_result.bytesConsumed()>0;
257 
258                         if (c>0)
259                             sent+=c;
260                         else if (c<0 && sent==0)
261                             sent=-1;
262                     }
263 
264                     // Try unwrapping some application data
265                     if (inBBuf.remaining()>0 && _inNIOBuffer!=null && _inNIOBuffer.hasContent())
266                     {
267                         int space=inBBuf.remaining();
268                         progress|=unwrap(inBBuf);
269                         received+=space-inBBuf.remaining();
270                     }
271                     break;
272 
273 
274                 case NEED_TASK:
275                 {
276                     // A task needs to be run, so run it!
277                     Runnable task;
278                     while ((task=_engine.getDelegatedTask())!=null)
279                     {
280                         progress=true;
281                         task.run();
282                     }
283 
284                     // Detect SUN JVM Bug!!!
285                     if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
286                        _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && sent==0)
287                     {
288                         // This should be NEED_WRAP
289                         // The fix simply detects the signature of the bug and then close the connection (fail-fast) so that ff3 will delegate to using SSL instead of TLS.
290                         // This is a jvm bug on java1.6 where the SSLEngine expects more data from the initial handshake when the client(ff3-tls) already had given it.
291                         // See http://jira.codehaus.org/browse/JETTY-567 for more details
292                         if (_debug) LOG.warn("{} JETTY-567",_session);
293                         return -1;
294                     }
295                     break;
296                 }
297 
298                 case NEED_WRAP:
299                 {
300                     checkRenegotiate();
301 
302                     // The SSL needs to send some handshake data to the other side
303                     int c=0;
304                     if (outBuf!=null && outBuf.hasContent())
305                         c=wrap(outBuf);
306                     else
307                         c=wrap(__EMPTY_BUFFER);
308 
309                     progress=_result.bytesProduced()>0||_result.bytesConsumed()>0;
310                     if (c>0)
311                         sent+=c;
312                     else if (c<0 && sent==0)
313                         sent=-1;
314                     break;
315                 }
316 
317                 case NEED_UNWRAP:
318                 {
319                     checkRenegotiate();
320 
321                     // Need more data to be unwrapped so try another call to unwrap
322                     progress|=unwrap(inBBuf);
323                     if (_closing && inBBuf.hasRemaining())
324                         inBBuf.clear();
325                     break;
326                 }
327             }
328 
329             if (_debug) LOG.debug("{} progress {}",_session,progress);
330         }
331 
332         if (_debug) LOG.debug("{} received {} sent {}",_session,received,sent);
333 
334         freeInBuffer();
335         return (received<0||sent<0)?-1:(received+sent);
336     }
337 
338 
339 
340     /* ------------------------------------------------------------ */
341     @Override
342     public void close() throws IOException
343     {
344         // For safety we always force a close calling super
345         try
346         {
347             if (!_closing)
348             {
349                 _closing=true;
350                 LOG.debug("{} close",_session);
351                 _engine.closeOutbound();
352                 process(null,null);
353             }
354         }
355         catch (IOException e)
356         {
357             // We could not write the SSL close message because the
358             // socket was already closed, nothing more we can do.
359             LOG.ignore(e);
360         }
361         finally
362         {
363             super.close();
364         }
365     }
366 
367     /* ------------------------------------------------------------ */
368     /** Fill the buffer with unencrypted bytes.
369      * Called by a Http Parser when more data is
370      * needed to continue parsing a request or a response.
371      */
372     @Override
373     public int fill(Buffer buffer) throws IOException
374     {
375         _debug=LOG.isDebugEnabled();
376         LOG.debug("{} fill",_session);
377         // This end point only works on NIO buffer type (director
378         // or indirect), so extract the NIO buffer that is wrapped
379         // by the passed jetty Buffer.
380         ByteBuffer bbuf=((NIOBuffer)buffer).getByteBuffer();
381 
382 
383         // remember the original size of the unencrypted buffer
384         int size=buffer.length();
385 
386 
387         synchronized (bbuf)
388         {
389             bbuf.position(buffer.putIndex());
390             try
391             {
392                 // Call the SSLEngine unwrap method to process data in
393                 // the inBuffer.  If there is no data in the inBuffer, then
394                 // super.fill is called to read encrypted bytes.
395                 unwrap(bbuf);
396                 process(bbuf,null);
397             }
398             finally
399             {
400                 // reset the Buffers
401                 buffer.setPutIndex(bbuf.position());
402                 bbuf.position(0);
403             }
404         }
405         // return the number of unencrypted bytes filled.
406         int filled=buffer.length()-size;
407         if (filled==0 && (isInputShutdown() || !isOpen()))
408             return -1;
409 
410         return filled;
411     }
412 
413     /* ------------------------------------------------------------ */
414     @Override
415     public int flush(Buffer buffer) throws IOException
416     {
417         _debug=LOG.isDebugEnabled();
418         LOG.debug("{} flush1",_session);
419         return process(null,buffer);
420     }
421 
422 
423     /* ------------------------------------------------------------ */
424     /*
425      */
426     @Override
427     public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
428     {
429         _debug=LOG.isDebugEnabled();
430         LOG.debug("{} flush3",_session);
431 
432         int len=0;
433         int flushed=0;
434         if (header!=null && header.hasContent())
435         {
436             len=header.length();
437             flushed=flush(header);
438         }
439         if (flushed==len && buffer!=null && buffer.hasContent())
440         {
441             int f=flush(buffer);
442             if (f>=0)
443                 flushed+=f;
444             else if (flushed==0)
445                 flushed=-1;
446         }
447 
448         return flushed;
449     }
450 
451     /* ------------------------------------------------------------ */
452     @Override
453     public void flush() throws IOException
454     {
455         LOG.debug("{} flush",_session);
456         if (!isOpen())
457             throw new EofException();
458 
459         if (isBufferingOutput())
460         {
461             int flushed=super.flush(_outNIOBuffer);
462             if (_debug)
463                 LOG.debug("{} flushed={} left={}",_session,flushed,_outNIOBuffer.length());
464         }
465         else if (_engine.isOutboundDone() && super.isOpen())
466         {
467             if (_debug)
468                 LOG.debug("{} flush shutdownOutput",_session);
469             try
470             {
471                 super.shutdownOutput();
472             }
473             catch(IOException e)
474             {
475                 LOG.ignore(e);
476             }
477         }
478 
479         freeOutBuffer();
480     }
481 
482     /* ------------------------------------------------------------ */
483     private void checkRenegotiate() throws IOException
484     {
485         if (_handshook && !_allowRenegotiate && _channel!=null && _channel.isOpen())
486         {
487             LOG.warn("SSL renegotiate denied: {}",_channel);
488             super.close();
489         }
490     }
491 
492     /* ------------------------------------------------------------ */
493     /**
494      * @return true if progress is made
495      */
496     private boolean unwrap(ByteBuffer buffer) throws IOException
497     {
498         needInBuffer();
499         ByteBuffer in_buffer=_inNIOBuffer.getByteBuffer();
500 
501         _inNIOBuffer.compact();
502 
503         int total_filled=0;
504         boolean remoteClosed = false;
505 
506         LOG.debug("{} unwrap space={} open={}",_session,_inNIOBuffer.space(),super.isOpen());
507 
508         // loop filling as much encrypted data as we can into the buffer
509         while (_inNIOBuffer.space()>0 && super.isOpen())
510         {
511             int filled=super.fill(_inNIOBuffer);
512             if (_debug) LOG.debug("{} filled {}",_session,filled);
513             if (filled < 0)
514                 remoteClosed = true;
515             // break the loop if no progress is made (we have read everything there is to read)
516             if (filled<=0)
517                 break;
518             total_filled+=filled;
519         }
520 
521         // If we have no progress and no data
522         if (total_filled==0 && _inNIOBuffer.length()==0)
523         {
524             // Do we need to close?
525             if (isOpen() && remoteClosed)
526             {
527                 try
528                 {
529                     _engine.closeInbound();
530                 }
531                 catch (SSLException x)
532                 {
533                     // It may happen, for example, in case of truncation
534                     // attacks, we close so that we do not spin forever
535                     super.close();
536                 }
537             }
538 
539             if (!isOpen())
540                 throw new EofException();
541 
542             return false;
543         }
544 
545         // We have some in data, so try to unwrap it.
546         try
547         {
548             // inBuffer is the NIO buffer inside the _inNIOBuffer,
549             // so update its position and limit from the inNIOBuffer.
550             in_buffer.position(_inNIOBuffer.getIndex());
551             in_buffer.limit(_inNIOBuffer.putIndex());
552 
553             // Do the unwrap
554             _result=_engine.unwrap(in_buffer,buffer);
555             if (!_handshook && _result.getHandshakeStatus()==SSLEngineResult.HandshakeStatus.FINISHED)
556                 _handshook=true;
557             if (_debug) LOG.debug("{} unwrap {}",_session,_result);
558 
559             // skip the bytes consumed
560             _inNIOBuffer.skip(_result.bytesConsumed());
561         }
562         catch(SSLException e)
563         {
564             LOG.warn(getRemoteAddr() + ":" + getRemotePort() + " ",e);
565             super.close();
566             throw e;
567         }
568         finally
569         {
570             // reset the buffer so it can be managed by the _inNIOBuffer again.
571             in_buffer.position(0);
572             in_buffer.limit(in_buffer.capacity());
573         }
574 
575         // handle the unwrap results
576         switch(_result.getStatus())
577         {
578             case BUFFER_OVERFLOW:
579                 LOG.debug("{} unwrap overflow",_session);
580                 return false;
581 
582             case BUFFER_UNDERFLOW:
583                 // Not enough data,
584                 // If we are closed, we will never get more, so EOF
585                 // else return and we will be tried again
586                 // later when more data arriving causes another dispatch.
587                 if (LOG.isDebugEnabled()) LOG.debug("{} unwrap {}",_session,_result);
588                 if(!isOpen())
589                 {
590                     _inNIOBuffer.clear();
591                     if (_outNIOBuffer!=null)
592                         _outNIOBuffer.clear();
593                     throw new EofException();
594                 }
595                 return (total_filled > 0);
596 
597             case CLOSED:
598                 _closing=true;
599                 // return true is some bytes somewhere were moved about.
600                 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
601 
602             case OK:
603                 // return true is some bytes somewhere were moved about.
604                 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
605 
606             default:
607                 LOG.warn("{} unwrap default: {}",_session,_result);
608                 throw new IOException(_result.toString());
609         }
610     }
611 
612     /* ------------------------------------------------------------ */
613     private ByteBuffer extractOutputBuffer(Buffer buffer)
614     {
615         if (buffer.buffer() instanceof NIOBuffer)
616             return ((NIOBuffer)buffer.buffer()).getByteBuffer();
617 
618         return ByteBuffer.wrap(buffer.array());
619     }
620 
621     /* ------------------------------------------------------------ */
622     private int wrap(final Buffer buffer) throws IOException
623     {
624         ByteBuffer bbuf=extractOutputBuffer(buffer);
625         synchronized(bbuf)
626         {
627             int consumed=0;
628             needOutBuffer();
629             _outNIOBuffer.compact();
630             ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
631             synchronized(out_buffer)
632             {
633                 try
634                 {
635                     bbuf.position(buffer.getIndex());
636                     bbuf.limit(buffer.putIndex());
637                     out_buffer.position(_outNIOBuffer.putIndex());
638                     out_buffer.limit(out_buffer.capacity());
639                     _result=_engine.wrap(bbuf,out_buffer);
640                     if (_debug) LOG.debug("{} wrap {}",_session,_result);
641                     if (!_handshook && _result.getHandshakeStatus()==SSLEngineResult.HandshakeStatus.FINISHED)
642                         _handshook=true;
643                     _outNIOBuffer.setPutIndex(out_buffer.position());
644                     consumed=_result.bytesConsumed();
645                 }
646                 catch(SSLException e)
647                 {
648                     LOG.warn(getRemoteAddr()+":"+getRemotePort()+" ",e);
649                     if (getChannel().isOpen())
650                         getChannel().close();
651                     throw e;
652                 }
653                 finally
654                 {
655                     out_buffer.position(0);
656                     bbuf.position(0);
657                     bbuf.limit(bbuf.capacity());
658 
659                     if (consumed>0)
660                     {
661                         int len=consumed<buffer.length()?consumed:buffer.length();
662                         buffer.skip(len);
663                         consumed-=len;
664                     }
665                 }
666             }
667         }
668         switch(_result.getStatus())
669         {
670             case BUFFER_UNDERFLOW:
671                 throw new IllegalStateException();
672 
673             case BUFFER_OVERFLOW:
674                 LOG.debug("{} wrap {}",_session,_result);
675                 flush();
676                 return 0;
677 
678             case OK:
679                 return _result.bytesConsumed();
680             case CLOSED:
681                 _closing=true;
682                 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
683 
684             default:
685                 LOG.warn("{} wrap default {}",_session,_result);
686             throw new IOException(_result.toString());
687         }
688     }
689 
690     /* ------------------------------------------------------------ */
691     @Override
692     public boolean isBufferingInput()
693     {
694         final Buffer in = _inNIOBuffer;
695         return in!=null && in.hasContent();
696     }
697 
698     /* ------------------------------------------------------------ */
699     @Override
700     public boolean isBufferingOutput()
701     {
702         final NIOBuffer out = _outNIOBuffer;
703         return out!=null && out.hasContent();
704     }
705 
706     /* ------------------------------------------------------------ */
707     @Override
708     public boolean isBufferred()
709     {
710         return true;
711     }
712 
713     /* ------------------------------------------------------------ */
714     public SSLEngine getSSLEngine()
715     {
716         return _engine;
717     }
718 
719     /* ------------------------------------------------------------ */
720     @Override
721     public String toString()
722     {
723         final NIOBuffer i=_inNIOBuffer;
724         final NIOBuffer o=_outNIOBuffer;
725         return "SSL"+super.toString()+","+(_engine==null?"-":_engine.getHandshakeStatus())+", in/out="+
726         (i==null?0:i.length())+"/"+(o==null?0:o.length())+
727         " bi/o="+isBufferingInput()+"/"+isBufferingOutput()+
728         " "+_result;
729     }
730 }