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