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