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