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 failedCallback(new Callback()
305 {
306 @Override
307 public void succeeded()
308 {
309 }
310
311 @Override
312 public void failed(Throwable x)
313 {
314 if (filler_failed)
315 getFillInterest().onFail(x);
316 getWriteFlusher().onFail(x);
317 }
318
319 },x);
320 }
321 };
322
323 public DecryptedEndPoint()
324 {
325 super(null,getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress());
326 setIdleTimeout(getEndPoint().getIdleTimeout());
327 }
328
329 @Override
330 protected FillInterest getFillInterest()
331 {
332 return super.getFillInterest();
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 flush = false;
356 synchronized (DecryptedEndPoint.this)
357 {
358 if (DEBUG)
359 LOG.debug("onIncompleteFlush {}", getEndPoint());
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 SslConnection.this.fillInterested();
373 }
374 else
375 {
376 flush = true;
377 }
378 }
379 if (flush)
380 {
381
382 if (isOutputShutdown())
383 {
384
385 getWriteFlusher().onClose();
386 }
387
388 else
389 {
390
391 getWriteFlusher().completeWrite();
392 }
393 }
394 }
395
396 @Override
397 protected boolean needsFill() throws IOException
398 {
399
400
401
402
403
404 synchronized (DecryptedEndPoint.this)
405 {
406
407 if (BufferUtil.hasContent(_decryptedInput))
408 return true;
409
410
411 if (BufferUtil.isEmpty(_encryptedInput) || _underFlown)
412 {
413
414
415
416 if (_fillRequiresFlushToProgress)
417 {
418
419
420
421 if (BufferUtil.hasContent(_encryptedOutput))
422 {
423
424 _cannotAcceptMoreAppDataToFlush = true;
425 getEndPoint().write(_writeCallback, _encryptedOutput);
426 }
427 else
428 {
429
430
431 _fillRequiresFlushToProgress = false;
432 return true;
433 }
434 }
435 else
436 {
437
438
439 SslConnection.this.fillInterested();
440 }
441
442 return false;
443 }
444 else
445 {
446
447 return true;
448 }
449 }
450 }
451
452 @Override
453 public void setConnection(Connection connection)
454 {
455 if (connection instanceof AbstractConnection)
456 {
457 AbstractConnection a = (AbstractConnection)connection;
458 if (a.getInputBufferSize()<_sslEngine.getSession().getApplicationBufferSize())
459 a.setInputBufferSize(_sslEngine.getSession().getApplicationBufferSize());
460 }
461 super.setConnection(connection);
462 }
463
464 public SslConnection getSslConnection()
465 {
466 return SslConnection.this;
467 }
468
469 @Override
470 public synchronized int fill(ByteBuffer buffer) throws IOException
471 {
472 if (DEBUG)
473 LOG.debug("{} fill enter", SslConnection.this);
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 if (DEBUG)
501 LOG.debug("{} filled {} encrypted bytes", SslConnection.this, net_filled);
502
503 decryption: while (true)
504 {
505
506
507 int pos = BufferUtil.flipToFill(app_in);
508 SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
509 BufferUtil.flipToFlush(app_in, pos);
510 if (DEBUG)
511 LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
512
513 HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
514 HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
515 Status unwrapResultStatus = unwrapResult.getStatus();
516
517 _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW;
518
519 if (_underFlown)
520 {
521 if (net_filled < 0)
522 closeInbound();
523 if (net_filled <= 0)
524 return net_filled;
525 }
526
527 switch (unwrapResultStatus)
528 {
529 case CLOSED:
530 {
531 switch (handshakeStatus)
532 {
533 case NOT_HANDSHAKING:
534 {
535
536 return -1;
537 }
538 case NEED_TASK:
539 {
540 _sslEngine.getDelegatedTask().run();
541 continue;
542 }
543 case NEED_WRAP:
544 {
545
546
547
548 return -1;
549 }
550 default:
551 {
552 throw new IllegalStateException();
553 }
554 }
555 }
556 case BUFFER_UNDERFLOW:
557 case OK:
558 {
559 if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken)
560 {
561 _handshaken = true;
562 if (DEBUG)
563 LOG.debug("{} {} handshake completed", SslConnection.this,
564 _sslEngine.getUseClientMode() ? "client-side" : "resumed session server-side");
565 }
566
567
568 if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
569 {
570 if (DEBUG)
571 LOG.debug("{} renegotiation denied", SslConnection.this);
572 closeInbound();
573 return -1;
574 }
575
576
577
578
579 if (unwrapResult.bytesProduced() > 0)
580 {
581 if (app_in == buffer)
582 return unwrapResult.bytesProduced();
583 return BufferUtil.append(buffer,_decryptedInput);
584 }
585
586 switch (handshakeStatus)
587 {
588 case NOT_HANDSHAKING:
589 {
590 if (_underFlown)
591 break decryption;
592 continue;
593 }
594 case NEED_TASK:
595 {
596 _sslEngine.getDelegatedTask().run();
597 continue;
598 }
599 case NEED_WRAP:
600 {
601
602
603 if (buffer == __FLUSH_CALLED_FILL)
604 return 0;
605
606 _fillRequiresFlushToProgress = true;
607 flush(__FILL_CALLED_FLUSH);
608 if (BufferUtil.isEmpty(_encryptedOutput))
609 {
610
611 _fillRequiresFlushToProgress = false;
612 continue;
613 }
614 else
615 {
616
617
618 return 0;
619 }
620 }
621 case NEED_UNWRAP:
622 {
623 if (_underFlown)
624 break decryption;
625 continue;
626 }
627 default:
628 {
629 throw new IllegalStateException();
630 }
631 }
632 }
633 default:
634 {
635 throw new IllegalStateException();
636 }
637 }
638 }
639 }
640 }
641 catch (Exception e)
642 {
643 getEndPoint().close();
644 throw e;
645 }
646 finally
647 {
648
649 if (_flushRequiresFillToProgress)
650 {
651 _flushRequiresFillToProgress = false;
652 getExecutor().execute(_runCompletWrite);
653 }
654
655 if (_encryptedInput != null && !_encryptedInput.hasRemaining())
656 {
657 _bufferPool.release(_encryptedInput);
658 _encryptedInput = null;
659 }
660 if (_decryptedInput != null && !_decryptedInput.hasRemaining())
661 {
662 _bufferPool.release(_decryptedInput);
663 _decryptedInput = null;
664 }
665 if (DEBUG)
666 LOG.debug("{} fill exit", SslConnection.this);
667 }
668 }
669
670 private void closeInbound()
671 {
672 try
673 {
674 _sslEngine.closeInbound();
675 }
676 catch (SSLException x)
677 {
678 LOG.ignore(x);
679 }
680 }
681
682 @Override
683 public synchronized boolean flush(ByteBuffer... appOuts) throws IOException
684 {
685
686
687
688
689
690
691
692 if (DEBUG)
693 LOG.debug("{} flush enter {}", SslConnection.this, Arrays.toString(appOuts));
694 int consumed=0;
695 try
696 {
697 if (_cannotAcceptMoreAppDataToFlush)
698 {
699 if (_sslEngine.isOutboundDone())
700 throw new EofException(new ClosedChannelException());
701 return false;
702 }
703
704
705 if (_encryptedOutput == null)
706 _encryptedOutput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
707
708 while (true)
709 {
710
711 BufferUtil.compact(_encryptedOutput);
712 int pos = BufferUtil.flipToFill(_encryptedOutput);
713 SSLEngineResult wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput);
714 if (DEBUG)
715 LOG.debug("{} wrap {}", SslConnection.this, wrapResult);
716 BufferUtil.flipToFlush(_encryptedOutput, pos);
717 if (wrapResult.bytesConsumed()>0)
718 consumed+=wrapResult.bytesConsumed();
719
720 boolean allConsumed=true;
721
722 for (ByteBuffer b : appOuts)
723 {
724 if (BufferUtil.isEmpty(b))
725 BufferUtil.clear(b);
726 else
727 allConsumed=false;
728 }
729
730 Status wrapResultStatus = wrapResult.getStatus();
731
732
733 switch (wrapResultStatus)
734 {
735 case CLOSED:
736
737 if (BufferUtil.hasContent(_encryptedOutput))
738 {
739 _cannotAcceptMoreAppDataToFlush = true;
740 getEndPoint().flush(_encryptedOutput);
741 getEndPoint().shutdownOutput();
742
743
744
745
746 if (BufferUtil.hasContent(_encryptedOutput))
747 return false;
748 }
749
750 else
751 {
752 getEndPoint().shutdownOutput();
753 }
754 return allConsumed;
755
756 case BUFFER_UNDERFLOW:
757 throw new IllegalStateException();
758
759 default:
760 if (DEBUG)
761 LOG.debug("{} {} {}", this, wrapResultStatus, BufferUtil.toDetailString(_encryptedOutput));
762
763 if (wrapResult.getHandshakeStatus() == HandshakeStatus.FINISHED && !_handshaken)
764 {
765 _handshaken = true;
766 if (DEBUG)
767 LOG.debug("{} {} handshake completed", SslConnection.this, "server-side");
768 }
769
770 HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
771
772
773 if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
774 {
775 if (DEBUG)
776 LOG.debug("{} renegotiation denied", SslConnection.this);
777 shutdownOutput();
778 return allConsumed;
779 }
780
781
782 if (BufferUtil.hasContent(_encryptedOutput))
783 getEndPoint().flush(_encryptedOutput);
784
785
786 switch (handshakeStatus)
787 {
788 case NOT_HANDSHAKING:
789
790 return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
791
792 case NEED_TASK:
793
794 _sslEngine.getDelegatedTask().run();
795 continue;
796
797 case NEED_WRAP:
798
799 continue;
800
801 case NEED_UNWRAP:
802
803
804 if (appOuts[0]!=__FILL_CALLED_FLUSH && !getFillInterest().isInterested())
805 {
806
807 _flushRequiresFillToProgress = true;
808 fill(__FLUSH_CALLED_FILL);
809
810 if (handshakeStatus == HandshakeStatus.NEED_WRAP)
811 continue;
812 }
813 return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
814
815 case FINISHED:
816 throw new IllegalStateException();
817 }
818 }
819 }
820 }
821 catch (Exception e)
822 {
823 getEndPoint().close();
824 throw e;
825 }
826 finally
827 {
828 if (DEBUG)
829 LOG.debug("{} flush exit, consumed {}", SslConnection.this, consumed);
830 releaseEncryptedOutputBuffer();
831 }
832 }
833
834 private void releaseEncryptedOutputBuffer()
835 {
836 if (!Thread.holdsLock(DecryptedEndPoint.this))
837 throw new IllegalStateException();
838 if (_encryptedOutput != null && !_encryptedOutput.hasRemaining())
839 {
840 _bufferPool.release(_encryptedOutput);
841 _encryptedOutput = null;
842 }
843 }
844
845 @Override
846 public void shutdownOutput()
847 {
848 boolean ishut = isInputShutdown();
849 boolean oshut = isOutputShutdown();
850 if (DEBUG)
851 LOG.debug("{} shutdownOutput: oshut={}, ishut={}", SslConnection.this, oshut, ishut);
852 if (ishut)
853 {
854
855
856
857
858 getEndPoint().close();
859 }
860 else if (!oshut)
861 {
862 try
863 {
864 _sslEngine.closeOutbound();
865 flush(BufferUtil.EMPTY_BUFFER);
866 SslConnection.this.fillInterested();
867 }
868 catch (Exception e)
869 {
870 LOG.ignore(e);
871 getEndPoint().close();
872 }
873 }
874 }
875
876 @Override
877 public boolean isOutputShutdown()
878 {
879 return _sslEngine.isOutboundDone() || getEndPoint().isOutputShutdown();
880 }
881
882 @Override
883 public void close()
884 {
885 super.close();
886
887 shutdownOutput();
888 getEndPoint().close();
889 }
890
891 @Override
892 public boolean isOpen()
893 {
894 return getEndPoint().isOpen();
895 }
896
897 @Override
898 public Object getTransport()
899 {
900 return getEndPoint();
901 }
902
903 @Override
904 public boolean isInputShutdown()
905 {
906 return _sslEngine.isInboundDone();
907 }
908
909 @Override
910 public String toString()
911 {
912 return super.toString()+"->"+getEndPoint().toString();
913 }
914 }
915 }