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