1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.jetty.security;
16
17 import java.io.IOException;
18 import java.nio.ByteBuffer;
19 import java.nio.channels.SelectionKey;
20 import java.nio.channels.SocketChannel;
21
22 import javax.net.ssl.SSLEngine;
23 import javax.net.ssl.SSLEngineResult;
24 import javax.net.ssl.SSLException;
25 import javax.net.ssl.SSLSession;
26 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
27
28 import org.mortbay.io.Buffer;
29 import org.mortbay.io.Buffers;
30 import org.mortbay.io.nio.NIOBuffer;
31 import org.mortbay.io.nio.SelectChannelEndPoint;
32 import org.mortbay.io.nio.SelectorManager;
33 import org.mortbay.jetty.EofException;
34 import org.mortbay.jetty.nio.SelectChannelConnector;
35 import org.mortbay.log.Log;
36
37
38
39
40
41
42
43
44 public class SslHttpChannelEndPoint extends SelectChannelConnector.ConnectorEndPoint implements Runnable
45 {
46 private static final ByteBuffer[] __NO_BUFFERS={};
47
48 private Buffers _buffers;
49
50 private SSLEngine _engine;
51 private ByteBuffer _inBuffer;
52 private NIOBuffer _inNIOBuffer;
53 private ByteBuffer _outBuffer;
54 private NIOBuffer _outNIOBuffer;
55
56 private NIOBuffer[] _reuseBuffer=new NIOBuffer[2];
57 private ByteBuffer[] _gather=new ByteBuffer[2];
58
59 private boolean _closing=false;
60 private SSLEngineResult _result;
61
62 private boolean _handshook=false;
63 private boolean _allowRenegotiate=false;
64
65
66
67 protected SSLSession _session;
68
69
70 public SslHttpChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine)
71 throws SSLException, IOException
72 {
73 super(channel,selectSet,key);
74 _buffers=buffers;
75
76
77 _engine=engine;
78 _session=engine.getSession();
79
80
81 _outNIOBuffer=(NIOBuffer)buffers.getBuffer(_session.getPacketBufferSize());
82 _outBuffer=_outNIOBuffer.getByteBuffer();
83 _inNIOBuffer=(NIOBuffer)buffers.getBuffer(_session.getPacketBufferSize());
84 _inBuffer=_inNIOBuffer.getByteBuffer();
85 }
86
87
88
89
90
91
92 public boolean isAllowRenegotiate()
93 {
94 return _allowRenegotiate;
95 }
96
97
98
99
100
101
102
103
104
105 public void setAllowRenegotiate(boolean allowRenegotiate)
106 {
107 _allowRenegotiate = allowRenegotiate;
108 }
109
110
111
112 public void dump()
113 {
114 System.err.println(_result);
115
116
117 }
118
119
120
121
122
123 protected void idleExpired()
124 {
125 try
126 {
127 _selectSet.getManager().dispatch(new Runnable()
128 {
129 public void run()
130 {
131 doIdleExpired();
132 }
133 });
134 }
135 catch(Exception e)
136 {
137 Log.ignore(e);
138 }
139 }
140
141
142 protected void doIdleExpired()
143 {
144 super.idleExpired();
145 }
146
147
148 public void close() throws IOException
149 {
150
151
152 _closing=true;
153 try
154 {
155 if (isBufferingOutput())
156 {
157 flush();
158 while (isOpen() && isBufferingOutput())
159 {
160 Thread.sleep(100);
161 flush();
162 }
163 }
164
165 _engine.closeOutbound();
166
167 loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()))
168 {
169 if (isBufferingOutput())
170 {
171 flush();
172 while (isOpen() && isBufferingOutput())
173 {
174 Thread.sleep(100);
175 flush();
176 }
177 }
178
179
180 switch(_engine.getHandshakeStatus())
181 {
182 case FINISHED:
183 case NOT_HANDSHAKING:
184 _handshook=true;
185 break loop;
186
187 case NEED_UNWRAP:
188 Buffer buffer =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
189 try
190 {
191 ByteBuffer bbuffer = ((NIOBuffer)buffer).getByteBuffer();
192 if (!unwrap(bbuffer) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
193 {
194 break loop;
195 }
196 }
197 catch(SSLException e)
198 {
199 Log.ignore(e);
200 }
201 finally
202 {
203 _buffers.returnBuffer(buffer);
204 }
205 break;
206
207 case NEED_TASK:
208 {
209 Runnable task;
210 while ((task=_engine.getDelegatedTask())!=null)
211 {
212 task.run();
213 }
214 break;
215 }
216
217 case NEED_WRAP:
218 {
219 try
220 {
221 _outNIOBuffer.compact();
222 int put=_outNIOBuffer.putIndex();
223 _outBuffer.position(put);
224 _result=null;
225 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
226 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
227 }
228 finally
229 {
230 _outBuffer.position(0);
231 }
232
233 break;
234 }
235 }
236 }
237 }
238 catch(IOException e)
239 {
240 Log.ignore(e);
241 }
242 catch (InterruptedException e)
243 {
244 Log.ignore(e);
245 }
246 finally
247 {
248 super.close();
249
250 if (_inNIOBuffer!=null)
251 _buffers.returnBuffer(_inNIOBuffer);
252 if (_outNIOBuffer!=null)
253 _buffers.returnBuffer(_outNIOBuffer);
254 if (_reuseBuffer[0]!=null)
255 _buffers.returnBuffer(_reuseBuffer[0]);
256 if (_reuseBuffer[1]!=null)
257 _buffers.returnBuffer(_reuseBuffer[1]);
258 }
259 }
260
261
262
263
264
265
266 public int fill(Buffer buffer) throws IOException
267 {
268 ByteBuffer bbuf=extractInputBuffer(buffer);
269 int size=buffer.length();
270 HandshakeStatus initialStatus = _engine.getHandshakeStatus();
271 synchronized (bbuf)
272 {
273 try
274 {
275 unwrap(bbuf);
276
277 int wraps=0;
278 loop: while (true)
279 {
280 if (isBufferingOutput())
281 {
282 flush();
283 if (isBufferingOutput())
284 break loop;
285 }
286
287
288 switch(_engine.getHandshakeStatus())
289 {
290 case FINISHED:
291 case NOT_HANDSHAKING:
292 if (_closing)
293 return -1;
294 break loop;
295
296 case NEED_UNWRAP:
297 checkRenegotiate();
298 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
299 {
300 break loop;
301 }
302 break;
303
304 case NEED_TASK:
305 {
306 Runnable task;
307 while ((task=_engine.getDelegatedTask())!=null)
308 {
309 task.run();
310 }
311
312 if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
313 _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && wraps==0)
314 {
315
316
317
318
319 return -1;
320 }
321 break;
322 }
323
324 case NEED_WRAP:
325 {
326 checkRenegotiate();
327 wraps++;
328 synchronized(_outBuffer)
329 {
330 try
331 {
332 _outNIOBuffer.compact();
333 int put=_outNIOBuffer.putIndex();
334 _outBuffer.position();
335 _result=null;
336 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
337 switch(_result.getStatus())
338 {
339 case BUFFER_OVERFLOW:
340 case BUFFER_UNDERFLOW:
341 Log.warn("wrap {}",_result);
342 case CLOSED:
343 _closing=true;
344 }
345
346 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
347 }
348 finally
349 {
350 _outBuffer.position(0);
351 }
352 }
353
354 flush();
355
356 break;
357 }
358 }
359 }
360 }
361 catch(SSLException e)
362 {
363 Log.warn(e.toString());
364 Log.debug(e);
365 throw e;
366 }
367 finally
368 {
369 buffer.setPutIndex(bbuf.position());
370 bbuf.position(0);
371 }
372
373 int filled=buffer.length()-size;
374 if (filled>0)
375 _handshook=true;
376 return filled;
377 }
378
379 }
380
381
382 public int flush(Buffer buffer) throws IOException
383 {
384 return flush(buffer,null,null);
385 }
386
387
388
389
390
391 public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
392 {
393 int consumed=0;
394 int available=header.length();
395 if (buffer!=null)
396 available+=buffer.length();
397
398 int tries=0;
399 loop: while (true)
400 {
401 if (_outNIOBuffer.length()>0)
402 {
403 flush();
404 if (isBufferingOutput())
405 break loop;
406 }
407
408
409 switch(_engine.getHandshakeStatus())
410 {
411 case FINISHED:
412 case NOT_HANDSHAKING:
413 if (_closing || available==0)
414 {
415 if (consumed==0)
416 consumed= -1;
417 break loop;
418 }
419
420 int c;
421 if (header!=null && header.length()>0)
422 {
423 if (buffer!=null && buffer.length()>0)
424 c=wrap(header,buffer);
425 else
426 c=wrap(header);
427 }
428 else
429 c=wrap(buffer);
430
431 if (c>0)
432 {
433 _handshook=true;
434 consumed+=c;
435 available-=c;
436 }
437 else if (c<0)
438 {
439 if (consumed==0)
440 consumed=-1;
441 break loop;
442 }
443
444 break;
445
446 case NEED_UNWRAP:
447 checkRenegotiate();
448 Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
449 try
450 {
451 ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
452 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
453 {
454 break loop;
455 }
456 }
457 finally
458 {
459 _buffers.returnBuffer(buf);
460 }
461
462 break;
463
464 case NEED_TASK:
465 {
466 Runnable task;
467 while ((task=_engine.getDelegatedTask())!=null)
468 {
469 task.run();
470 }
471 break;
472 }
473
474 case NEED_WRAP:
475 {
476 checkRenegotiate();
477 synchronized(_outBuffer)
478 {
479 try
480 {
481 _outNIOBuffer.compact();
482 int put=_outNIOBuffer.putIndex();
483 _outBuffer.position();
484 _result=null;
485 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
486 switch(_result.getStatus())
487 {
488 case BUFFER_OVERFLOW:
489 case BUFFER_UNDERFLOW:
490 Log.warn("wrap {}",_result);
491 case CLOSED:
492 _closing=true;
493 }
494 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
495 }
496 finally
497 {
498 _outBuffer.position(0);
499 }
500 }
501
502 flush();
503 if (isBufferingOutput())
504 break loop;
505
506 break;
507 }
508 }
509 }
510
511 return consumed;
512 }
513
514
515 public void flush() throws IOException
516 {
517 int len=_outNIOBuffer.length();
518 if (len>0)
519 {
520 int flushed=super.flush(_outNIOBuffer);
521 len=_outNIOBuffer.length();
522
523 if (len>0)
524 {
525 Thread.yield();
526 flushed=super.flush(_outNIOBuffer);
527 len=_outNIOBuffer.length();
528 }
529 }
530 }
531
532
533 private void checkRenegotiate() throws IOException
534 {
535 if (_handshook && !_allowRenegotiate && _channel!=null && _channel.isOpen())
536 {
537 Log.warn("SSL renegotiate denied: "+_channel);
538 close();
539 }
540 }
541
542
543 private ByteBuffer extractInputBuffer(Buffer buffer)
544 {
545 assert buffer instanceof NIOBuffer;
546 NIOBuffer nbuf=(NIOBuffer)buffer;
547 ByteBuffer bbuf=nbuf.getByteBuffer();
548 bbuf.position(buffer.putIndex());
549 return bbuf;
550 }
551
552
553
554
555
556 private boolean unwrap(ByteBuffer buffer) throws IOException
557 {
558 if (_inNIOBuffer.hasContent())
559 _inNIOBuffer.compact();
560 else
561 _inNIOBuffer.clear();
562
563 int total_filled=0;
564
565 while (_inNIOBuffer.space()>0 && super.isOpen())
566 {
567 try
568 {
569 int filled=super.fill(_inNIOBuffer);
570 if (filled<=0)
571 break;
572 total_filled+=filled;
573 }
574 catch(IOException e)
575 {
576 if (_inNIOBuffer.length()==0)
577 {
578 _outNIOBuffer.clear();
579 throw e;
580 }
581 break;
582 }
583 }
584
585 if (total_filled==0 && _inNIOBuffer.length()==0)
586 {
587 if(!isOpen())
588 {
589 _outNIOBuffer.clear();
590 throw new EofException();
591 }
592 return false;
593 }
594
595 try
596 {
597 _inBuffer.position(_inNIOBuffer.getIndex());
598 _inBuffer.limit(_inNIOBuffer.putIndex());
599 _result=null;
600 _result=_engine.unwrap(_inBuffer,buffer);
601 _inNIOBuffer.skip(_result.bytesConsumed());
602 }
603 finally
604 {
605 _inBuffer.position(0);
606 _inBuffer.limit(_inBuffer.capacity());
607 }
608
609 switch(_result.getStatus())
610 {
611 case BUFFER_OVERFLOW:
612 throw new IllegalStateException(_result.toString());
613
614 case BUFFER_UNDERFLOW:
615 if (Log.isDebugEnabled())
616 Log.debug("unwrap {}",_result);
617 if(!isOpen())
618 {
619 _inNIOBuffer.clear();
620 _outNIOBuffer.clear();
621 throw new EofException();
622 }
623 return (total_filled > 0);
624
625 case CLOSED:
626 _closing=true;
627
628 case OK:
629 boolean progress=total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
630 return progress;
631 default:
632 Log.warn("unwrap "+_result);
633 throw new IOException(_result.toString());
634 }
635 }
636
637
638
639 private ByteBuffer extractOutputBuffer(Buffer buffer,int n)
640 {
641 NIOBuffer nBuf=null;
642
643 if (buffer.buffer() instanceof NIOBuffer)
644 {
645 nBuf=(NIOBuffer)buffer.buffer();
646 return nBuf.getByteBuffer();
647 }
648 else
649 {
650 if (_reuseBuffer[n]==null)
651 _reuseBuffer[n] = (NIOBuffer)_buffers.getBuffer(_session.getApplicationBufferSize());
652 NIOBuffer buf = _reuseBuffer[n];
653 buf.clear();
654 buf.put(buffer);
655 return buf.getByteBuffer();
656 }
657 }
658
659
660 private int wrap(Buffer header, Buffer buffer) throws IOException
661 {
662 _gather[0]=extractOutputBuffer(header,0);
663 synchronized(_gather[0])
664 {
665 _gather[0].position(header.getIndex());
666 _gather[0].limit(header.putIndex());
667
668 _gather[1]=extractOutputBuffer(buffer,1);
669
670 synchronized(_gather[1])
671 {
672 _gather[1].position(buffer.getIndex());
673 _gather[1].limit(buffer.putIndex());
674
675 synchronized(_outBuffer)
676 {
677 int consumed=0;
678 try
679 {
680 _outNIOBuffer.clear();
681 _outBuffer.position(0);
682 _outBuffer.limit(_outBuffer.capacity());
683
684 _result=null;
685 _result=_engine.wrap(_gather,_outBuffer);
686 _outNIOBuffer.setGetIndex(0);
687 _outNIOBuffer.setPutIndex(_result.bytesProduced());
688 consumed=_result.bytesConsumed();
689 }
690 finally
691 {
692 _outBuffer.position(0);
693
694 if (consumed>0 && header!=null)
695 {
696 int len=consumed<header.length()?consumed:header.length();
697 header.skip(len);
698 consumed-=len;
699 _gather[0].position(0);
700 _gather[0].limit(_gather[0].capacity());
701 }
702 if (consumed>0 && buffer!=null)
703 {
704 int len=consumed<buffer.length()?consumed:buffer.length();
705 buffer.skip(len);
706 consumed-=len;
707 _gather[1].position(0);
708 _gather[1].limit(_gather[1].capacity());
709 }
710 assert consumed==0;
711 }
712 }
713 }
714 }
715
716
717 switch(_result.getStatus())
718 {
719 case BUFFER_OVERFLOW:
720 case BUFFER_UNDERFLOW:
721 Log.warn("wrap {}",_result);
722
723 case OK:
724 return _result.bytesConsumed();
725 case CLOSED:
726 _closing=true;
727 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
728
729 default:
730 Log.warn("wrap "+_result);
731 throw new IOException(_result.toString());
732 }
733 }
734
735
736 private int wrap(Buffer buffer) throws IOException
737 {
738 _gather[0]=extractOutputBuffer(buffer,0);
739 synchronized(_gather[0])
740 {
741 _gather[0].position(buffer.getIndex());
742 _gather[0].limit(buffer.putIndex());
743
744 int consumed=0;
745 synchronized(_outBuffer)
746 {
747 try
748 {
749 _outNIOBuffer.clear();
750 _outBuffer.position(0);
751 _outBuffer.limit(_outBuffer.capacity());
752 _result=null;
753 _result=_engine.wrap(_gather[0],_outBuffer);
754 _outNIOBuffer.setGetIndex(0);
755 _outNIOBuffer.setPutIndex(_result.bytesProduced());
756 consumed=_result.bytesConsumed();
757 }
758 finally
759 {
760 _outBuffer.position(0);
761
762 if (consumed>0 && buffer!=null)
763 {
764 int len=consumed<buffer.length()?consumed:buffer.length();
765 buffer.skip(len);
766 consumed-=len;
767 _gather[0].position(0);
768 _gather[0].limit(_gather[0].capacity());
769 }
770 assert consumed==0;
771 }
772 }
773 }
774 switch(_result.getStatus())
775 {
776 case BUFFER_OVERFLOW:
777 case BUFFER_UNDERFLOW:
778 Log.warn("wrap {}",_result);
779
780 case OK:
781 return _result.bytesConsumed();
782 case CLOSED:
783 _closing=true;
784 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
785
786 default:
787 Log.warn("wrap "+_result);
788 throw new IOException(_result.toString());
789 }
790 }
791
792
793 public boolean isBufferingInput()
794 {
795 return _inNIOBuffer.hasContent();
796 }
797
798
799 public boolean isBufferingOutput()
800 {
801 return _outNIOBuffer.hasContent();
802 }
803
804
805 public boolean isBufferred()
806 {
807 return true;
808 }
809
810
811 public SSLEngine getSSLEngine()
812 {
813 return _engine;
814 }
815
816
817 public String toString()
818 {
819 return super.toString()+","+_engine.getHandshakeStatus()+", in/out="+_inNIOBuffer.length()+"/"+_outNIOBuffer.length()+" "+_result;
820 }
821 }