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 close() throws IOException
148 {
149
150
151 _closing=true;
152 long end=System.currentTimeMillis()+((SocketChannel)_channel).socket().getSoTimeout();
153 try
154 {
155 if (isBufferingOutput())
156 {
157 flush();
158 while (isOpen() && isBufferingOutput() && System.currentTimeMillis()<end)
159 {
160 Thread.sleep(100);
161 flush();
162 }
163 }
164
165 _engine.closeOutbound();
166
167 loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()) && System.currentTimeMillis()<end)
168 {
169 if (isBufferingOutput())
170 {
171 flush();
172 while (isOpen() && isBufferingOutput() && System.currentTimeMillis()<end)
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 }
255 }
256
257
258
259
260
261
262 public int fill(Buffer buffer) throws IOException
263 {
264 ByteBuffer bbuf=extractInputBuffer(buffer);
265 int size=buffer.length();
266 HandshakeStatus initialStatus = _engine.getHandshakeStatus();
267 synchronized (bbuf)
268 {
269 try
270 {
271 unwrap(bbuf);
272
273 int wraps=0;
274 loop: while (true)
275 {
276 if (isBufferingOutput())
277 {
278 flush();
279 if (isBufferingOutput())
280 break loop;
281 }
282
283
284 switch(_engine.getHandshakeStatus())
285 {
286 case FINISHED:
287 case NOT_HANDSHAKING:
288 if (_closing)
289 return -1;
290 break loop;
291
292 case NEED_UNWRAP:
293 checkRenegotiate();
294 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
295 {
296 break loop;
297 }
298 break;
299
300 case NEED_TASK:
301 {
302 Runnable task;
303 while ((task=_engine.getDelegatedTask())!=null)
304 {
305 task.run();
306 }
307
308 if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
309 _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && wraps==0)
310 {
311
312
313
314
315 return -1;
316 }
317 break;
318 }
319
320 case NEED_WRAP:
321 {
322 checkRenegotiate();
323 wraps++;
324 synchronized(_outBuffer)
325 {
326 try
327 {
328 _outNIOBuffer.compact();
329 int put=_outNIOBuffer.putIndex();
330 _outBuffer.position();
331 _result=null;
332 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
333 switch(_result.getStatus())
334 {
335 case BUFFER_OVERFLOW:
336 case BUFFER_UNDERFLOW:
337 Log.warn("wrap {}",_result);
338 case CLOSED:
339 _closing=true;
340 }
341
342 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
343 }
344 finally
345 {
346 _outBuffer.position(0);
347 }
348 }
349
350 flush();
351
352 break;
353 }
354 }
355 }
356 }
357 catch(SSLException e)
358 {
359 Log.warn(e.toString());
360 Log.debug(e);
361 throw e;
362 }
363 finally
364 {
365 buffer.setPutIndex(bbuf.position());
366 bbuf.position(0);
367 }
368
369 int filled=buffer.length()-size;
370 if (filled>0)
371 _handshook=true;
372 return filled;
373 }
374
375 }
376
377
378 public int flush(Buffer buffer) throws IOException
379 {
380 return flush(buffer,null,null);
381 }
382
383
384
385
386
387 public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
388 {
389 int consumed=0;
390 int available=header.length();
391 if (buffer!=null)
392 available+=buffer.length();
393
394 int tries=0;
395 loop: while (true)
396 {
397 if (_outNIOBuffer.length()>0)
398 {
399 flush();
400 if (isBufferingOutput())
401 break loop;
402 }
403
404
405 switch(_engine.getHandshakeStatus())
406 {
407 case FINISHED:
408 case NOT_HANDSHAKING:
409 if (_closing || available==0)
410 {
411 if (consumed==0)
412 consumed= -1;
413 break loop;
414 }
415
416 int c;
417 if (header!=null && header.length()>0)
418 {
419 if (buffer!=null && buffer.length()>0)
420 c=wrap(header,buffer);
421 else
422 c=wrap(header);
423 }
424 else
425 c=wrap(buffer);
426
427 if (c>0)
428 {
429 _handshook=true;
430 consumed+=c;
431 available-=c;
432 }
433 else if (c<0)
434 {
435 if (consumed==0)
436 consumed=-1;
437 break loop;
438 }
439
440 break;
441
442 case NEED_UNWRAP:
443 checkRenegotiate();
444 Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
445 try
446 {
447 ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
448 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
449 {
450 break loop;
451 }
452 }
453 finally
454 {
455 _buffers.returnBuffer(buf);
456 }
457
458 break;
459
460 case NEED_TASK:
461 {
462 Runnable task;
463 while ((task=_engine.getDelegatedTask())!=null)
464 {
465 task.run();
466 }
467 break;
468 }
469
470 case NEED_WRAP:
471 {
472 checkRenegotiate();
473 synchronized(_outBuffer)
474 {
475 try
476 {
477 _outNIOBuffer.compact();
478 int put=_outNIOBuffer.putIndex();
479 _outBuffer.position();
480 _result=null;
481 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
482 switch(_result.getStatus())
483 {
484 case BUFFER_OVERFLOW:
485 case BUFFER_UNDERFLOW:
486 Log.warn("wrap {}",_result);
487 case CLOSED:
488 _closing=true;
489 }
490 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
491 }
492 finally
493 {
494 _outBuffer.position(0);
495 }
496 }
497
498 flush();
499 if (isBufferingOutput())
500 break loop;
501
502 break;
503 }
504 }
505 }
506
507 return consumed;
508 }
509
510
511 public void flush() throws IOException
512 {
513 int len=_outNIOBuffer.length();
514 if (len>0)
515 {
516 int flushed=super.flush(_outNIOBuffer);
517 len=_outNIOBuffer.length();
518
519 if (len>0)
520 {
521 Thread.yield();
522 flushed=super.flush(_outNIOBuffer);
523 len=_outNIOBuffer.length();
524 }
525 }
526 }
527
528
529 private void checkRenegotiate() throws IOException
530 {
531 if (_handshook && !_allowRenegotiate && _channel!=null && _channel.isOpen())
532 {
533 Log.warn("SSL renegotiate denied: "+_channel);
534 close();
535 }
536 }
537
538
539 private ByteBuffer extractInputBuffer(Buffer buffer)
540 {
541 assert buffer instanceof NIOBuffer;
542 NIOBuffer nbuf=(NIOBuffer)buffer;
543 ByteBuffer bbuf=nbuf.getByteBuffer();
544 bbuf.position(buffer.putIndex());
545 return bbuf;
546 }
547
548
549
550
551
552 private boolean unwrap(ByteBuffer buffer) throws IOException
553 {
554 if (_inNIOBuffer.hasContent())
555 _inNIOBuffer.compact();
556 else
557 _inNIOBuffer.clear();
558
559 int total_filled=0;
560
561 while (_inNIOBuffer.space()>0 && super.isOpen())
562 {
563 try
564 {
565 int filled=super.fill(_inNIOBuffer);
566 if (filled<=0)
567 break;
568 total_filled+=filled;
569 }
570 catch(IOException e)
571 {
572 if (_inNIOBuffer.length()==0)
573 {
574 _outNIOBuffer.clear();
575 throw e;
576 }
577 break;
578 }
579 }
580
581 if (total_filled==0 && _inNIOBuffer.length()==0)
582 {
583 if(!isOpen())
584 {
585 _outNIOBuffer.clear();
586 throw new EofException();
587 }
588 return false;
589 }
590
591 try
592 {
593 _inBuffer.position(_inNIOBuffer.getIndex());
594 _inBuffer.limit(_inNIOBuffer.putIndex());
595 _result=null;
596 _result=_engine.unwrap(_inBuffer,buffer);
597 _inNIOBuffer.skip(_result.bytesConsumed());
598 }
599 finally
600 {
601 _inBuffer.position(0);
602 _inBuffer.limit(_inBuffer.capacity());
603 }
604
605 switch(_result.getStatus())
606 {
607 case BUFFER_OVERFLOW:
608 throw new IllegalStateException(_result.toString());
609
610 case BUFFER_UNDERFLOW:
611 if (Log.isDebugEnabled())
612 Log.debug("unwrap {}",_result);
613 if(!isOpen())
614 {
615 _inNIOBuffer.clear();
616 _outNIOBuffer.clear();
617 throw new EofException();
618 }
619 return (total_filled > 0);
620
621 case CLOSED:
622 _closing=true;
623
624 case OK:
625 boolean progress=total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
626 return progress;
627 default:
628 Log.warn("unwrap "+_result);
629 throw new IOException(_result.toString());
630 }
631 }
632
633
634 private ByteBuffer extractOutputBuffer(Buffer buffer,int n)
635 {
636 if (buffer.buffer() instanceof NIOBuffer)
637 return ((NIOBuffer)buffer.buffer()).getByteBuffer();
638
639 return ByteBuffer.wrap(buffer.array());
640 }
641
642
643 private int wrap(Buffer header, Buffer buffer) throws IOException
644 {
645 _gather[0]=extractOutputBuffer(header,0);
646 synchronized(_gather[0])
647 {
648 _gather[0].position(header.getIndex());
649 _gather[0].limit(header.putIndex());
650
651 _gather[1]=extractOutputBuffer(buffer,1);
652
653 synchronized(_gather[1])
654 {
655 _gather[1].position(buffer.getIndex());
656 _gather[1].limit(buffer.putIndex());
657
658 synchronized(_outBuffer)
659 {
660 int consumed=0;
661 try
662 {
663 _outNIOBuffer.clear();
664 _outBuffer.position(0);
665 _outBuffer.limit(_outBuffer.capacity());
666
667 _result=null;
668 _result=_engine.wrap(_gather,_outBuffer);
669 _outNIOBuffer.setGetIndex(0);
670 _outNIOBuffer.setPutIndex(_result.bytesProduced());
671 consumed=_result.bytesConsumed();
672 }
673 finally
674 {
675 _outBuffer.position(0);
676
677 if (consumed>0 && header!=null)
678 {
679 int len=consumed<header.length()?consumed:header.length();
680 header.skip(len);
681 consumed-=len;
682 _gather[0].position(0);
683 _gather[0].limit(_gather[0].capacity());
684 }
685 if (consumed>0 && buffer!=null)
686 {
687 int len=consumed<buffer.length()?consumed:buffer.length();
688 buffer.skip(len);
689 consumed-=len;
690 _gather[1].position(0);
691 _gather[1].limit(_gather[1].capacity());
692 }
693 assert consumed==0;
694 }
695 }
696 }
697 }
698
699
700 switch(_result.getStatus())
701 {
702 case BUFFER_OVERFLOW:
703 case BUFFER_UNDERFLOW:
704 Log.warn("wrap {}",_result);
705
706 case OK:
707 return _result.bytesConsumed();
708 case CLOSED:
709 _closing=true;
710 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
711
712 default:
713 Log.warn("wrap "+_result);
714 throw new IOException(_result.toString());
715 }
716 }
717
718
719 private int wrap(Buffer buffer) throws IOException
720 {
721 _gather[0]=extractOutputBuffer(buffer,0);
722 synchronized(_gather[0])
723 {
724 _gather[0].position(buffer.getIndex());
725 _gather[0].limit(buffer.putIndex());
726
727 int consumed=0;
728 synchronized(_outBuffer)
729 {
730 try
731 {
732 _outNIOBuffer.clear();
733 _outBuffer.position(0);
734 _outBuffer.limit(_outBuffer.capacity());
735 _result=null;
736 _result=_engine.wrap(_gather[0],_outBuffer);
737 _outNIOBuffer.setGetIndex(0);
738 _outNIOBuffer.setPutIndex(_result.bytesProduced());
739 consumed=_result.bytesConsumed();
740 }
741 finally
742 {
743 _outBuffer.position(0);
744
745 if (consumed>0 && buffer!=null)
746 {
747 int len=consumed<buffer.length()?consumed:buffer.length();
748 buffer.skip(len);
749 consumed-=len;
750 _gather[0].position(0);
751 _gather[0].limit(_gather[0].capacity());
752 }
753 assert consumed==0;
754 }
755 }
756 }
757 switch(_result.getStatus())
758 {
759 case BUFFER_OVERFLOW:
760 case BUFFER_UNDERFLOW:
761 Log.warn("wrap {}",_result);
762
763 case OK:
764 return _result.bytesConsumed();
765 case CLOSED:
766 _closing=true;
767 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
768
769 default:
770 Log.warn("wrap "+_result);
771 throw new IOException(_result.toString());
772 }
773 }
774
775
776 public boolean isBufferingInput()
777 {
778 return _inNIOBuffer.hasContent();
779 }
780
781
782 public boolean isBufferingOutput()
783 {
784 return _outNIOBuffer.hasContent();
785 }
786
787
788 public boolean isBufferred()
789 {
790 return true;
791 }
792
793
794 public SSLEngine getSSLEngine()
795 {
796 return _engine;
797 }
798
799
800 public String toString()
801 {
802 return super.toString()+","+_engine.getHandshakeStatus()+", in/out="+_inNIOBuffer.length()+"/"+_outNIOBuffer.length()+" "+_result;
803 }
804 }