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