1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.filter.ssl;
21
22 import java.net.InetSocketAddress;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import javax.net.ssl.SSLContext;
27 import javax.net.ssl.SSLEngine;
28 import javax.net.ssl.SSLException;
29 import javax.net.ssl.SSLHandshakeException;
30 import javax.net.ssl.SSLSession;
31
32 import org.apache.mina.core.buffer.IoBuffer;
33 import org.apache.mina.core.filterchain.IoFilter;
34 import org.apache.mina.core.filterchain.IoFilterAdapter;
35 import org.apache.mina.core.filterchain.IoFilterChain;
36 import org.apache.mina.core.future.DefaultWriteFuture;
37 import org.apache.mina.core.future.IoFuture;
38 import org.apache.mina.core.future.IoFutureListener;
39 import org.apache.mina.core.future.WriteFuture;
40 import org.apache.mina.core.service.IoAcceptor;
41 import org.apache.mina.core.service.IoHandler;
42 import org.apache.mina.core.session.AttributeKey;
43 import org.apache.mina.core.session.IoSession;
44 import org.apache.mina.core.write.DefaultWriteRequest;
45 import org.apache.mina.core.write.WriteRequest;
46 import org.apache.mina.core.write.WriteToClosedSessionException;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
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
80
81
82
83
84
85
86
87
88
89 public class SslFilter extends IoFilterAdapter {
90
91 private static final Logger LOGGER = LoggerFactory.getLogger(SslFilter.class);
92
93
94
95
96
97 public static final AttributeKeyteKey.html#AttributeKey">AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
98
99
100
101
102
103
104
105
106
107
108
109 public static final AttributeKeyttributeKey">AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
110
111
112
113
114
115
116
117
118
119 public static final AttributeKey.html#AttributeKey">AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 public static final AttributeKeyeKey.html#AttributeKey">AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
135
136
137 private static final AttributeKeyteKey.html#AttributeKey">AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
138
139 private static final AttributeKeyteKey.html#AttributeKey">AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
140
141
142 final SSLContext sslContext;
143
144
145 private final boolean autoStart;
146
147
148 public static final boolean START_HANDSHAKE = true;
149
150
151 public static final boolean CLIENT_HANDSHAKE = false;
152
153 private boolean client;
154
155 private boolean needClientAuth;
156
157 private boolean wantClientAuth;
158
159 private String[] enabledCipherSuites;
160
161 private String[] enabledProtocols;
162
163
164
165
166
167
168
169
170 public SslFilter(SSLContext sslContext) {
171 this(sslContext, START_HANDSHAKE);
172 }
173
174
175
176
177
178
179
180
181
182
183 public SslFilter(SSLContext sslContext, boolean autoStart) {
184 if (sslContext == null) {
185 throw new IllegalArgumentException("sslContext");
186 }
187
188 this.sslContext = sslContext;
189 this.autoStart = autoStart;
190 }
191
192
193
194
195
196
197
198 public SSLSession getSslSession(IoSession session) {
199 return (SSLSession) session.getAttribute(SSL_SESSION);
200 }
201
202
203
204
205
206
207
208
209
210
211 public boolean startSsl(IoSession session) throws SSLException {
212 SslHandler sslHandler = getSslSessionHandler(session);
213 boolean started;
214
215 try {
216 synchronized (sslHandler) {
217 if (sslHandler.isOutboundDone()) {
218 NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
219 sslHandler.destroy();
220 sslHandler.init();
221 sslHandler.handshake(nextFilter);
222 started = true;
223 } else {
224 started = false;
225 }
226 sslHandler.flushFilterWrite();
227 }
228 sslHandler.flushMessageReceived();
229 } catch (SSLException se) {
230 sslHandler.release();
231 throw se;
232 }
233
234 return started;
235 }
236
237
238
239
240
241
242 String getSessionInfo(IoSession session) {
243 StringBuilder sb = new StringBuilder();
244
245 if (session.getService() instanceof IoAcceptor) {
246 sb.append("Session Server");
247
248 } else {
249 sb.append("Session Client");
250 }
251
252 sb.append('[').append(session.getId()).append(']');
253
254 SslHandler/../../org/apache/mina/filter/ssl/SslHandler.html#SslHandler">SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
255
256 if (sslHandler == null) {
257 sb.append("(no sslEngine)");
258 } else if (isSslStarted(session)) {
259 if (sslHandler.isHandshakeComplete()) {
260 sb.append("(SSL)");
261 } else {
262 sb.append("(ssl...)");
263 }
264 }
265
266 return sb.toString();
267 }
268
269
270
271
272
273
274
275
276
277 public boolean isSslStarted(IoSession session) {
278 SslHandler/../../org/apache/mina/filter/ssl/SslHandler.html#SslHandler">SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
279
280 if (sslHandler == null) {
281 return false;
282 }
283
284 synchronized (sslHandler) {
285 return !sslHandler.isOutboundDone();
286 }
287 }
288
289
290
291
292
293
294
295
296 public boolean isSecured(IoSession session) {
297 SslHandler/../../org/apache/mina/filter/ssl/SslHandler.html#SslHandler">SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
298
299 if (sslHandler == null) {
300 return false;
301 }
302
303 synchronized (sslHandler) {
304 return !sslHandler.isOutboundDone() && sslHandler.isHandshakeComplete();
305 }
306 }
307
308
309
310
311
312
313
314
315
316
317 public WriteFuture stopSsl(IoSession session) throws SSLException {
318 SslHandler sslHandler = getSslSessionHandler(session);
319 NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
320 WriteFuture future;
321
322 try {
323 synchronized (sslHandler) {
324 future = initiateClosure(nextFilter, session);
325 sslHandler.flushFilterWrite();
326 }
327 } catch (SSLException se) {
328 sslHandler.release();
329 throw se;
330 }
331
332 return future;
333 }
334
335
336
337
338
339 public boolean isUseClientMode() {
340 return client;
341 }
342
343
344
345
346
347
348 public void setUseClientMode(boolean clientMode) {
349 this.client = clientMode;
350 }
351
352
353
354
355
356 public boolean isNeedClientAuth() {
357 return needClientAuth;
358 }
359
360
361
362
363
364
365
366 public void setNeedClientAuth(boolean needClientAuth) {
367 this.needClientAuth = needClientAuth;
368 }
369
370
371
372
373
374 public boolean isWantClientAuth() {
375 return wantClientAuth;
376 }
377
378
379
380
381
382
383
384 public void setWantClientAuth(boolean wantClientAuth) {
385 this.wantClientAuth = wantClientAuth;
386 }
387
388
389
390
391
392 public String[] getEnabledCipherSuites() {
393 return enabledCipherSuites;
394 }
395
396
397
398
399
400
401
402 public void setEnabledCipherSuites(String[] cipherSuites) {
403 this.enabledCipherSuites = cipherSuites;
404 }
405
406
407
408
409
410 public String[] getEnabledProtocols() {
411 return enabledProtocols;
412 }
413
414
415
416
417
418
419
420 public void setEnabledProtocols(String[] protocols) {
421 this.enabledProtocols = protocols;
422 }
423
424
425
426
427
428
429
430
431
432
433 @Override
434 public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
435
436 if (parent.contains(SslFilter.class)) {
437 String msg = "Only one SSL filter is permitted in a chain.";
438 LOGGER.error(msg);
439 throw new IllegalStateException(msg);
440 }
441
442 if (LOGGER.isDebugEnabled()) {
443 LOGGER.debug("Adding the SSL Filter {} to the chain", name);
444 }
445
446 IoSession session = parent.getSession();
447 session.setAttribute(NEXT_FILTER, nextFilter);
448
449
450 SslHandlerdler.html#SslHandler">SslHandler sslHandler = new SslHandler(this, session);
451
452
453 if ((enabledCipherSuites == null) || (enabledCipherSuites.length == 0)) {
454 enabledCipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites();
455 }
456
457 sslHandler.init();
458
459 session.setAttribute(SSL_HANDLER, sslHandler);
460 }
461
462 @Override
463 public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
464 if (autoStart == START_HANDSHAKE) {
465 initiateHandshake(nextFilter, parent.getSession());
466 }
467 }
468
469 @Override
470 public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
471 IoSession session = parent.getSession();
472 stopSsl(session);
473 session.removeAttribute(NEXT_FILTER);
474 session.removeAttribute(SSL_HANDLER);
475 }
476
477
478 @Override
479 public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
480 SslHandler sslHandler = getSslSessionHandler(session);
481
482 try {
483 synchronized (sslHandler) {
484
485 sslHandler.destroy();
486 }
487 } finally {
488
489 nextFilter.sessionClosed(session);
490 }
491 }
492
493 @Override
494 public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
495 if (LOGGER.isDebugEnabled()) {
496 LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
497 }
498
499 SslHandler sslHandler = getSslSessionHandler(session);
500
501 synchronized (sslHandler) {
502 if (!isSslStarted(session) && sslHandler.isInboundDone()) {
503
504
505
506 sslHandler.scheduleMessageReceived(nextFilter, message);
507 } else {
508 IoBuffer buf = (IoBuffer) message;
509
510 try {
511 if (sslHandler.isOutboundDone()) {
512 sslHandler.destroy();
513 throw new SSLException("Outbound done");
514 }
515
516
517 sslHandler.messageReceived(nextFilter, buf.buf());
518
519
520 handleSslData(nextFilter, sslHandler);
521
522 if (sslHandler.isInboundDone()) {
523 if (sslHandler.isOutboundDone()) {
524 sslHandler.destroy();
525 } else {
526 initiateClosure(nextFilter, session);
527 }
528
529 if (buf.hasRemaining()) {
530
531 sslHandler.scheduleMessageReceived(nextFilter, buf);
532 }
533 }
534 } catch (SSLException ssle) {
535 if (!sslHandler.isHandshakeComplete()) {
536 SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
537 newSsle.initCause(ssle);
538 ssle = newSsle;
539
540
541 session.closeNow();
542 } else {
543
544 sslHandler.release();
545 }
546
547 throw ssle;
548 }
549 }
550 }
551
552 sslHandler.flushMessageReceived();
553 }
554
555 @Override
556 public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
557 if (writeRequest instanceof EncryptedWriteRequest) {
558 EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
559 nextFilter.messageSent(session, wrappedRequest.getParentRequest());
560 } else {
561
562 }
563 }
564
565 @Override
566 public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
567
568 if (cause instanceof WriteToClosedSessionException) {
569
570
571 WriteToClosedSessionException/apache/mina/core/write/WriteToClosedSessionException.html#WriteToClosedSessionException">WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
572 List<WriteRequest> failedRequests = e.getRequests();
573 boolean containsCloseNotify = false;
574
575 for (WriteRequest r : failedRequests) {
576 if (isCloseNotify(r.getMessage())) {
577 containsCloseNotify = true;
578 break;
579 }
580 }
581
582 if (containsCloseNotify) {
583 if (failedRequests.size() == 1) {
584
585 return;
586 }
587
588 List<WriteRequest> newFailedRequests = new ArrayList<>(failedRequests.size() - 1);
589
590 for (WriteRequest r : failedRequests) {
591 if (!isCloseNotify(r.getMessage())) {
592 newFailedRequests.add(r);
593 }
594 }
595
596 if (newFailedRequests.isEmpty()) {
597
598 return;
599 }
600
601 cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
602 }
603 }
604
605 nextFilter.exceptionCaught(session, cause);
606 }
607
608 private boolean isCloseNotify(Object message) {
609 if (!(message instanceof IoBuffer)) {
610 return false;
611 }
612
613 IoBuffer"../../../../../org/apache/mina/core/buffer/IoBuffer.html#IoBuffer">IoBuffer buf = (IoBuffer) message;
614 int offset = buf.position();
615
616 return (buf.get(offset + 0) == 0x15)
617 && (buf.get(offset + 1) == 0x03)
618 && ((buf.get(offset + 2) == 0x00)
619 || (buf.get(offset + 2) == 0x01)
620 || (buf.get(offset + 2) == 0x02)
621 || (buf.get(offset + 2) == 0x03))
622 && (buf.get(offset + 3) == 0x00);
623 }
624
625 @Override
626 public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
627 if (LOGGER.isDebugEnabled()) {
628 LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
629 }
630
631 boolean needsFlush = true;
632 SslHandler sslHandler = getSslSessionHandler(session);
633
634 try {
635 synchronized (sslHandler) {
636 if (!isSslStarted(session)) {
637 sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
638 }
639
640 else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
641
642 session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
643 sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
644 } else {
645
646 IoBuffer"../../../../../org/apache/mina/core/buffer/IoBuffer.html#IoBuffer">IoBuffer buf = (IoBuffer) writeRequest.getMessage();
647
648 if (sslHandler.isWritingEncryptedData()) {
649
650 sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
651 } else if (sslHandler.isHandshakeComplete()) {
652
653 sslHandler.encrypt(buf.buf());
654 IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer();
655 writeRequest.setMessage( encryptedBuffer );
656 sslHandler.scheduleFilterWrite(nextFilter, new EncryptedWriteRequest(writeRequest,
657 encryptedBuffer));
658 } else {
659 if (session.isConnected()) {
660
661 sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
662 }
663
664 needsFlush = false;
665 }
666 }
667 if (needsFlush) {
668 sslHandler.flushFilterWrite();
669 }
670 }
671 } catch (SSLException se) {
672 sslHandler.release();
673 throw se;
674 }
675 }
676
677 @Override
678 public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
679 SslHandler/../../org/apache/mina/filter/ssl/SslHandler.html#SslHandler">SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
680
681 if (sslHandler == null) {
682
683
684 nextFilter.filterClose(session);
685 return;
686 }
687
688 WriteFuture future = null;
689
690 try {
691 synchronized (sslHandler) {
692 if (isSslStarted(session)) {
693 future = initiateClosure(nextFilter, session);
694 future.addListener(new IoFutureListener<IoFuture>() {
695 @Override
696 public void operationComplete(IoFuture future) {
697 nextFilter.filterClose(session);
698 }
699 });
700 }
701 sslHandler.flushFilterWrite();
702 }
703 } catch (SSLException se) {
704 sslHandler.release();
705 throw se;
706 } finally {
707 if (future == null) {
708 nextFilter.filterClose(session);
709 }
710 }
711 }
712
713
714
715
716
717
718
719
720 public void initiateHandshake(IoSession session) throws SSLException {
721 IoFilterChain filterChain = session.getFilterChain();
722
723 if (filterChain == null) {
724 throw new SSLException("No filter chain");
725 }
726
727 IoFilter.NextFilter nextFilter = filterChain.getNextFilter(SslFilter.class);
728
729 if (nextFilter == null) {
730 throw new SSLException("No SSL next filter in the chain");
731 }
732
733 initiateHandshake(nextFilter, session);
734 }
735
736 private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
737 if (LOGGER.isDebugEnabled()) {
738 LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
739 }
740
741 SslHandler sslHandler = getSslSessionHandler(session);
742
743 try {
744 synchronized (sslHandler) {
745 sslHandler.handshake(nextFilter);
746 sslHandler.flushFilterWrite();
747 }
748 sslHandler.flushMessageReceived();
749 } catch (SSLException se) {
750 sslHandler.release();
751 throw se;
752 }
753 }
754
755 private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
756 SslHandler sslHandler = getSslSessionHandler(session);
757 WriteFuture future = null;
758
759
760 try {
761 synchronized(sslHandler) {
762 if (!sslHandler.closeOutbound()) {
763 return DefaultWriteFuture.newNotWrittenFuture(session, new IllegalStateException(
764 "SSL session is shut down already."));
765 }
766
767
768 future = sslHandler.writeNetBuffer(nextFilter);
769
770 if (future == null) {
771 future = DefaultWriteFuture.newWrittenFuture(session);
772 }
773
774 if (sslHandler.isInboundDone()) {
775 sslHandler.destroy();
776 }
777 }
778
779
780 session.getFilterChain().fireEvent(SslEvent.UNSECURED);
781 } catch (SSLException se) {
782 sslHandler.release();
783 throw se;
784 }
785
786 return future;
787 }
788
789
790 private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException {
791 if (LOGGER.isDebugEnabled()) {
792 LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession()));
793 }
794
795
796 if (sslHandler.isHandshakeComplete()) {
797 sslHandler.flushPreHandshakeEvents();
798 }
799
800
801 sslHandler.writeNetBuffer(nextFilter);
802
803
804 handleAppDataRead(nextFilter, sslHandler);
805 }
806
807 private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) {
808
809 IoBuffer readBuffer = sslHandler.fetchAppBuffer();
810
811 if (readBuffer.hasRemaining()) {
812 sslHandler.scheduleMessageReceived(nextFilter, readBuffer);
813 }
814 }
815
816 private SslHandler getSslSessionHandler(IoSession session) {
817 SslHandler/../../org/apache/mina/filter/ssl/SslHandler.html#SslHandler">SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
818
819 if (sslHandler == null) {
820 throw new IllegalStateException();
821 }
822
823 synchronized(sslHandler) {
824 if (sslHandler.getSslFilter() != this) {
825 throw new IllegalArgumentException("Not managed by this filter.");
826 }
827 }
828
829 return sslHandler;
830 }
831
832
833
834
835
836
837
838 public static class SslFilterMessage {
839 private final String name;
840
841 private SslFilterMessage(String name) {
842 this.name = name;
843 }
844
845 @Override
846 public String toString() {
847 return name;
848 }
849 }
850
851
852
853
854
855
856
857 static class EncryptedWriteRequest extends DefaultWriteRequest {
858
859 private final IoBuffer encryptedMessage;
860
861
862 private WriteRequest parentRequest;
863
864
865
866
867
868
869 private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
870 super(encryptedMessage);
871 parentRequest = writeRequest;
872 this.encryptedMessage = encryptedMessage;
873 }
874
875
876
877
878 @Override
879 public Object getMessage() {
880 return encryptedMessage;
881 }
882
883
884
885
886 public WriteRequest getParentRequest() {
887 return parentRequest;
888 }
889
890
891
892
893 @Override
894 public WriteFuture getFuture() {
895 return parentRequest.getFuture();
896 }
897 }
898 }