1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.io.ssl;
20
21 import java.io.IOException;
22 import java.nio.ByteBuffer;
23 import java.nio.channels.ClosedChannelException;
24 import java.util.concurrent.Executor;
25
26 import javax.net.ssl.SSLEngine;
27 import javax.net.ssl.SSLEngineResult;
28 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
29 import javax.net.ssl.SSLEngineResult.Status;
30 import javax.net.ssl.SSLException;
31
32 import org.eclipse.jetty.io.AbstractConnection;
33 import org.eclipse.jetty.io.AbstractEndPoint;
34 import org.eclipse.jetty.io.ByteBufferPool;
35 import org.eclipse.jetty.io.Connection;
36 import org.eclipse.jetty.io.EndPoint;
37 import org.eclipse.jetty.io.EofException;
38 import org.eclipse.jetty.io.RuntimeIOException;
39 import org.eclipse.jetty.io.SelectChannelEndPoint;
40 import org.eclipse.jetty.io.WriteFlusher;
41 import org.eclipse.jetty.util.BufferUtil;
42 import org.eclipse.jetty.util.Callback;
43 import org.eclipse.jetty.util.log.Log;
44 import org.eclipse.jetty.util.log.Logger;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 public class SslConnection extends AbstractConnection
78 {
79 private static final Logger LOG = Log.getLogger(SslConnection.class);
80 private static final boolean DEBUG = LOG.isDebugEnabled();
81 private static final ByteBuffer __FILL_CALLED_FLUSH= BufferUtil.allocate(0);
82 private static final ByteBuffer __FLUSH_CALLED_FILL= BufferUtil.allocate(0);
83 private final ByteBufferPool _bufferPool;
84 private final SSLEngine _sslEngine;
85 private final DecryptedEndPoint _decryptedEndPoint;
86 private ByteBuffer _decryptedInput;
87 private ByteBuffer _encryptedInput;
88 private ByteBuffer _encryptedOutput;
89 private final boolean _encryptedDirectBuffers = true;
90 private final boolean _decryptedDirectBuffers = false;
91 private boolean _renegotiationAllowed;
92 private final Runnable _runCompletWrite = new Runnable()
93 {
94 @Override
95 public void run()
96 {
97 _decryptedEndPoint.getWriteFlusher().completeWrite();
98 }
99 };
100 private final Runnable _runFillable = new Runnable()
101 {
102 @Override
103 public void run()
104 {
105 _decryptedEndPoint.getFillInterest().fillable();
106 }
107 };
108
109 public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine)
110 {
111
112
113 super(endPoint, executor);
114 this._bufferPool = byteBufferPool;
115 this._sslEngine = sslEngine;
116 this._decryptedEndPoint = newDecryptedEndPoint();
117 }
118
119 protected DecryptedEndPoint newDecryptedEndPoint()
120 {
121 return new DecryptedEndPoint();
122 }
123
124 public SSLEngine getSSLEngine()
125 {
126 return _sslEngine;
127 }
128
129 public DecryptedEndPoint getDecryptedEndPoint()
130 {
131 return _decryptedEndPoint;
132 }
133
134 public boolean isRenegotiationAllowed()
135 {
136 return _renegotiationAllowed;
137 }
138
139 public void setRenegotiationAllowed(boolean renegotiationAllowed)
140 {
141 this._renegotiationAllowed = renegotiationAllowed;
142 }
143
144 @Override
145 public void onOpen()
146 {
147 try
148 {
149
150 _sslEngine.beginHandshake();
151 super.onOpen();
152 getDecryptedEndPoint().getConnection().onOpen();
153 }
154 catch (SSLException x)
155 {
156 getEndPoint().close();
157 throw new RuntimeIOException(x);
158 }
159 }
160
161 @Override
162 public void onClose()
163 {
164 _decryptedEndPoint.getConnection().onClose();
165 super.onClose();
166 }
167
168 @Override
169 public void close()
170 {
171 getDecryptedEndPoint().getConnection().close();
172 }
173
174 @Override
175 public void onFillable()
176 {
177
178
179
180
181
182
183 if (DEBUG)
184 LOG.debug("onFillable enter {}", _decryptedEndPoint);
185
186
187 if (_decryptedEndPoint.isInputShutdown())
188 _decryptedEndPoint.close();
189
190
191
192 _decryptedEndPoint.getFillInterest().fillable();
193
194
195 synchronized(_decryptedEndPoint)
196 {
197 if (_decryptedEndPoint._flushRequiresFillToProgress)
198 {
199 _decryptedEndPoint._flushRequiresFillToProgress = false;
200 _runCompletWrite.run();
201 }
202 }
203
204 if (DEBUG)
205 LOG.debug("onFillable exit {}", _decryptedEndPoint);
206 }
207
208 @Override
209 public void onFillInterestedFailed(Throwable cause)
210 {
211
212
213
214
215
216 _decryptedEndPoint.getFillInterest().onFail(cause);
217
218 boolean failFlusher = false;
219 synchronized(_decryptedEndPoint)
220 {
221 if (_decryptedEndPoint._flushRequiresFillToProgress)
222 {
223 _decryptedEndPoint._flushRequiresFillToProgress = false;
224 failFlusher = true;
225 }
226 }
227 if (failFlusher)
228 _decryptedEndPoint.getWriteFlusher().onFail(cause);
229 }
230
231 @Override
232 public String toString()
233 {
234 ByteBuffer b = _encryptedInput;
235 int ei=b==null?-1:b.remaining();
236 b = _encryptedOutput;
237 int eo=b==null?-1:b.remaining();
238 b = _decryptedInput;
239 int di=b==null?-1:b.remaining();
240
241 return String.format("SslConnection@%x{%s,eio=%d/%d,di=%d} -> %s",
242 hashCode(),
243 _sslEngine.getHandshakeStatus(),
244 ei,eo,di,
245 _decryptedEndPoint.getConnection());
246 }
247
248 public class DecryptedEndPoint extends AbstractEndPoint
249 {
250 private boolean _fillRequiresFlushToProgress;
251 private boolean _flushRequiresFillToProgress;
252 private boolean _cannotAcceptMoreAppDataToFlush;
253 private boolean _handshaken;
254 private boolean _underFlown;
255
256 private final Callback _writeCallback = new Callback()
257 {
258 @Override
259 public void succeeded()
260 {
261
262
263
264 boolean fillable = false;
265 synchronized (DecryptedEndPoint.this)
266 {
267 if (DEBUG)
268 LOG.debug("write.complete {}", SslConnection.this.getEndPoint());
269
270 releaseEncryptedOutputBuffer();
271
272 _cannotAcceptMoreAppDataToFlush = false;
273
274 if (_fillRequiresFlushToProgress)
275 {
276 _fillRequiresFlushToProgress = false;
277 fillable = true;
278 }
279 }
280 if (fillable)
281 getFillInterest().fillable();
282 _runCompletWrite.run();
283 }
284
285 @Override
286 public void failed(final Throwable x)
287 {
288
289
290
291 boolean fail_filler = false;
292 synchronized (DecryptedEndPoint.this)
293 {
294 if (DEBUG)
295 LOG.debug("{} write.failed", SslConnection.this, x);
296 BufferUtil.clear(_encryptedOutput);
297 releaseEncryptedOutputBuffer();
298
299 _cannotAcceptMoreAppDataToFlush = false;
300
301 if (_fillRequiresFlushToProgress)
302 {
303 _fillRequiresFlushToProgress = false;
304 fail_filler = true;
305 }
306 }
307
308 final boolean filler_failed=fail_filler;
309
310 failedCallback(new Callback()
311 {
312 @Override
313 public void succeeded()
314 {
315 }
316
317 @Override
318 public void failed(Throwable x)
319 {
320 if (filler_failed)
321 getFillInterest().onFail(x);
322 getWriteFlusher().onFail(x);
323 }
324
325 },x);
326 }
327 };
328
329 public DecryptedEndPoint()
330 {
331 super(null,getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress());
332 setIdleTimeout(getEndPoint().getIdleTimeout());
333 }
334
335 @Override
336 public void setIdleTimeout(long idleTimeout)
337 {
338 super.setIdleTimeout(idleTimeout);
339 getEndPoint().setIdleTimeout(idleTimeout);
340 }
341
342 @Override
343 protected WriteFlusher getWriteFlusher()
344 {
345 return super.getWriteFlusher();
346 }
347
348 @Override
349 protected void onIncompleteFlush()
350 {
351
352
353
354
355 boolean try_again = false;
356 synchronized (DecryptedEndPoint.this)
357 {
358 if (DEBUG)
359 LOG.debug("onIncompleteFlush {}", SslConnection.this);
360
361 if (BufferUtil.hasContent(_encryptedOutput))
362 {
363
364 _cannotAcceptMoreAppDataToFlush = true;
365 getEndPoint().write(_writeCallback, _encryptedOutput);
366 }
367
368 else if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP)
369 {
370
371 _flushRequiresFillToProgress = true;
372
373 ensureFillInterested();
374 }
375 else
376 {
377
378
379
380
381
382 try_again = true;
383 }
384 }
385
386
387 if (try_again)
388 {
389
390 if (isOutputShutdown())
391 {
392
393 getWriteFlusher().onClose();
394 }
395
396 else
397 {
398
399
400 getExecutor().execute(_runCompletWrite);
401 }
402 }
403 }
404
405 @Override
406 protected void needsFillInterest() throws IOException
407 {
408
409
410
411
412
413 synchronized (DecryptedEndPoint.this)
414 {
415
416 boolean fillable = (BufferUtil.hasContent(_decryptedInput))
417
418 || BufferUtil.hasContent(_encryptedInput) && !_underFlown;
419
420
421 if (!fillable)
422 {
423
424
425
426 if (_fillRequiresFlushToProgress)
427 {
428
429
430
431 if (BufferUtil.hasContent(_encryptedOutput))
432 {
433
434 _cannotAcceptMoreAppDataToFlush = true;
435 getEndPoint().write(_writeCallback, _encryptedOutput);
436 }
437 else
438 {
439
440
441 _fillRequiresFlushToProgress = false;
442 fillable=true;
443 }
444 }
445 }
446
447 if (fillable)
448 getExecutor().execute(_runFillable);
449 else
450 ensureFillInterested();
451 }
452 }
453
454 @Override
455 public void setConnection(Connection connection)
456 {
457 if (connection instanceof AbstractConnection)
458 {
459 AbstractConnection a = (AbstractConnection)connection;
460 if (a.getInputBufferSize()<_sslEngine.getSession().getApplicationBufferSize())
461 a.setInputBufferSize(_sslEngine.getSession().getApplicationBufferSize());
462 }
463 super.setConnection(connection);
464 }
465
466 public SslConnection getSslConnection()
467 {
468 return SslConnection.this;
469 }
470
471 @Override
472 public synchronized int fill(ByteBuffer buffer) throws IOException
473 {
474 try
475 {
476
477 if (BufferUtil.hasContent(_decryptedInput))
478 return BufferUtil.append(buffer,_decryptedInput);
479
480
481 if (_encryptedInput == null)
482 _encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
483 else
484 BufferUtil.compact(_encryptedInput);
485
486
487 ByteBuffer app_in;
488 if (BufferUtil.space(buffer) > _sslEngine.getSession().getApplicationBufferSize())
489 app_in = buffer;
490 else if (_decryptedInput == null)
491 app_in = _decryptedInput = _bufferPool.acquire(_sslEngine.getSession().getApplicationBufferSize(), _decryptedDirectBuffers);
492 else
493 app_in = _decryptedInput;
494
495
496 while (true)
497 {
498
499 int net_filled = getEndPoint().fill(_encryptedInput);
500
501 decryption: while (true)
502 {
503
504
505 int pos = BufferUtil.flipToFill(app_in);
506 SSLEngineResult unwrapResult;
507 try
508 {
509 unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
510 }
511 finally
512 {
513 BufferUtil.flipToFlush(app_in, pos);
514 }
515 if (DEBUG)
516 {
517 LOG.debug("{} net={} unwrap {}", SslConnection.this, net_filled, unwrapResult.toString().replace('\n',' '));
518 LOG.debug("{} filled {}",SslConnection.this,BufferUtil.toHexSummary(buffer));
519 }
520
521 HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
522 HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
523 Status unwrapResultStatus = unwrapResult.getStatus();
524
525
526
527 _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW || unwrapResultStatus == Status.OK && unwrapResult.bytesConsumed()==0 && unwrapResult.bytesProduced()==0;
528
529 if (_underFlown)
530 {
531 if (net_filled < 0)
532 closeInbound();
533 if (net_filled <= 0)
534 return net_filled;
535 }
536
537 switch (unwrapResultStatus)
538 {
539 case CLOSED:
540 {
541 switch (handshakeStatus)
542 {
543 case NOT_HANDSHAKING:
544 {
545
546 return -1;
547 }
548 case NEED_TASK:
549 {
550 _sslEngine.getDelegatedTask().run();
551 continue;
552 }
553 case NEED_WRAP:
554 {
555
556
557
558 return -1;
559 }
560 case NEED_UNWRAP:
561 {
562
563
564 return -1;
565 }
566 default:
567 {
568 throw new IllegalStateException();
569 }
570 }
571 }
572 case BUFFER_UNDERFLOW:
573 case OK:
574 {
575 if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken)
576 {
577 _handshaken = true;
578 if (DEBUG)
579 LOG.debug("{} {} handshook {}/{}", SslConnection.this,
580 _sslEngine.getUseClientMode() ? "client" : "resumed server",
581 _sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite());
582 }
583
584
585 if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
586 {
587 if (DEBUG)
588 LOG.debug("{} renegotiation denied", SslConnection.this);
589 closeInbound();
590 return -1;
591 }
592
593
594
595
596 if (unwrapResult.bytesProduced() > 0)
597 {
598 if (app_in == buffer)
599 return unwrapResult.bytesProduced();
600 return BufferUtil.append(buffer,_decryptedInput);
601 }
602
603 switch (handshakeStatus)
604 {
605 case NOT_HANDSHAKING:
606 {
607 if (_underFlown)
608 break decryption;
609 continue;
610 }
611 case NEED_TASK:
612 {
613 _sslEngine.getDelegatedTask().run();
614 continue;
615 }
616 case NEED_WRAP:
617 {
618
619
620 if (buffer == __FLUSH_CALLED_FILL)
621 return 0;
622
623 _fillRequiresFlushToProgress = true;
624 flush(__FILL_CALLED_FLUSH);
625 if (BufferUtil.isEmpty(_encryptedOutput))
626 {
627
628 _fillRequiresFlushToProgress = false;
629 continue;
630 }
631 else
632 {
633
634
635 return 0;
636 }
637 }
638 case NEED_UNWRAP:
639 {
640 if (_underFlown)
641 break decryption;
642 continue;
643 }
644 default:
645 {
646 throw new IllegalStateException();
647 }
648 }
649 }
650 default:
651 {
652 throw new IllegalStateException();
653 }
654 }
655 }
656 }
657 }
658 catch (IllegalStateException e)
659 {
660
661 LOG.debug(e);
662 getEndPoint().close();
663 throw new EofException(e);
664 }
665 catch (Exception e)
666 {
667 getEndPoint().close();
668 throw e;
669 }
670 finally
671 {
672
673 if (_flushRequiresFillToProgress)
674 {
675 _flushRequiresFillToProgress = false;
676 getExecutor().execute(_runCompletWrite);
677 }
678
679 if (_encryptedInput != null && !_encryptedInput.hasRemaining())
680 {
681 _bufferPool.release(_encryptedInput);
682 _encryptedInput = null;
683 }
684 if (_decryptedInput != null && !_decryptedInput.hasRemaining())
685 {
686 _bufferPool.release(_decryptedInput);
687 _decryptedInput = null;
688 }
689 }
690 }
691
692 private void closeInbound()
693 {
694 try
695 {
696 _sslEngine.closeInbound();
697 }
698 catch (SSLException x)
699 {
700 LOG.ignore(x);
701 }
702 }
703
704 @Override
705 public synchronized boolean flush(ByteBuffer... appOuts) throws IOException
706 {
707
708
709
710
711
712
713
714 if (DEBUG)
715 {
716 for (ByteBuffer b : appOuts)
717 LOG.debug("{} flush {}", SslConnection.this, BufferUtil.toHexSummary(b));
718 }
719
720 try
721 {
722 if (_cannotAcceptMoreAppDataToFlush)
723 {
724 if (_sslEngine.isOutboundDone())
725 throw new EofException(new ClosedChannelException());
726 return false;
727 }
728
729
730 if (_encryptedOutput == null)
731 _encryptedOutput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
732
733 while (true)
734 {
735
736 BufferUtil.compact(_encryptedOutput);
737 int pos = BufferUtil.flipToFill(_encryptedOutput);
738 SSLEngineResult wrapResult;
739 try
740 {
741 wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput);
742 }
743 finally
744 {
745 BufferUtil.flipToFlush(_encryptedOutput, pos);
746 }
747 if (DEBUG)
748 LOG.debug("{} wrap {}", SslConnection.this, wrapResult.toString().replace('\n',' '));
749
750 Status wrapResultStatus = wrapResult.getStatus();
751
752 boolean allConsumed=true;
753 for (ByteBuffer b : appOuts)
754 if (BufferUtil.hasContent(b))
755 allConsumed=false;
756
757
758 switch (wrapResultStatus)
759 {
760 case CLOSED:
761
762 if (BufferUtil.hasContent(_encryptedOutput))
763 {
764 _cannotAcceptMoreAppDataToFlush = true;
765 getEndPoint().flush(_encryptedOutput);
766 getEndPoint().shutdownOutput();
767
768
769
770
771 if (BufferUtil.hasContent(_encryptedOutput))
772 return false;
773 }
774
775 else
776 {
777 getEndPoint().shutdownOutput();
778 }
779 return allConsumed;
780
781 case BUFFER_UNDERFLOW:
782 throw new IllegalStateException();
783
784 default:
785 if (DEBUG)
786 LOG.debug("{} wrap {} {}", SslConnection.this, wrapResultStatus, BufferUtil.toHexSummary(_encryptedOutput));
787
788 if (wrapResult.getHandshakeStatus() == HandshakeStatus.FINISHED && !_handshaken)
789 {
790 _handshaken = true;
791 if (DEBUG)
792 LOG.debug("{} server handshook complete {}/{}", SslConnection.this, _sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite());
793 }
794
795 HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
796
797
798 if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
799 {
800 if (DEBUG)
801 LOG.debug("{} renegotiation denied", SslConnection.this);
802 getEndPoint().shutdownOutput();
803 return allConsumed;
804 }
805
806
807 if (BufferUtil.hasContent(_encryptedOutput))
808 if (!getEndPoint().flush(_encryptedOutput))
809 getEndPoint().flush(_encryptedOutput);
810
811
812 switch (handshakeStatus)
813 {
814 case NOT_HANDSHAKING:
815
816
817
818 if (!allConsumed && wrapResult.getHandshakeStatus()==HandshakeStatus.FINISHED && BufferUtil.isEmpty(_encryptedOutput))
819 continue;
820
821
822 return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
823
824 case NEED_TASK:
825
826 _sslEngine.getDelegatedTask().run();
827 continue;
828
829 case NEED_WRAP:
830
831 continue;
832
833 case NEED_UNWRAP:
834
835
836 if (appOuts[0]!=__FILL_CALLED_FLUSH && !getFillInterest().isInterested())
837 {
838
839 _flushRequiresFillToProgress = true;
840 fill(__FLUSH_CALLED_FILL);
841
842 if (handshakeStatus == HandshakeStatus.NEED_WRAP)
843 continue;
844 }
845 return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
846
847 case FINISHED:
848 throw new IllegalStateException();
849 }
850 }
851 }
852 }
853 finally
854 {
855 releaseEncryptedOutputBuffer();
856 }
857 }
858
859 private void releaseEncryptedOutputBuffer()
860 {
861 if (!Thread.holdsLock(DecryptedEndPoint.this))
862 throw new IllegalStateException();
863 if (_encryptedOutput != null && !_encryptedOutput.hasRemaining())
864 {
865 _bufferPool.release(_encryptedOutput);
866 _encryptedOutput = null;
867 }
868 }
869
870 @Override
871 public void shutdownOutput()
872 {
873 boolean ishut = isInputShutdown();
874 boolean oshut = isOutputShutdown();
875 if (DEBUG)
876 LOG.debug("{} shutdownOutput: oshut={}, ishut={}", SslConnection.this, oshut, ishut);
877 if (ishut)
878 {
879
880
881
882
883 getEndPoint().close();
884 }
885 else if (!oshut)
886 {
887 try
888 {
889 synchronized (this)
890 {
891 _sslEngine.closeOutbound();
892 flush(BufferUtil.EMPTY_BUFFER);
893 ensureFillInterested();
894 }
895 }
896 catch (Exception e)
897 {
898 LOG.ignore(e);
899 getEndPoint().close();
900 }
901 }
902 }
903
904 private void ensureFillInterested()
905 {
906 if (!SslConnection.this.isFillInterested())
907 SslConnection.this.fillInterested();
908 }
909
910 @Override
911 public boolean isOutputShutdown()
912 {
913 return _sslEngine.isOutboundDone() || getEndPoint().isOutputShutdown();
914 }
915
916 @Override
917 public void close()
918 {
919
920 shutdownOutput();
921 getEndPoint().close();
922 super.close();
923 }
924
925 @Override
926 public boolean isOpen()
927 {
928 return getEndPoint().isOpen();
929 }
930
931 @Override
932 public Object getTransport()
933 {
934 return getEndPoint();
935 }
936
937 @Override
938 public boolean isInputShutdown()
939 {
940 return _sslEngine.isInboundDone();
941 }
942
943 @Override
944 public String toString()
945 {
946 return super.toString()+"->"+getEndPoint().toString();
947 }
948 }
949 }