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 }
227
228 sslHandler.flushScheduledEvents();
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 }
326
327 sslHandler.flushScheduledEvents();
328 } catch (SSLException se) {
329 sslHandler.release();
330 throw se;
331 }
332
333 return future;
334 }
335
336
337
338
339
340 public boolean isUseClientMode() {
341 return client;
342 }
343
344
345
346
347
348
349 public void setUseClientMode(boolean clientMode) {
350 this.client = clientMode;
351 }
352
353
354
355
356
357 public boolean isNeedClientAuth() {
358 return needClientAuth;
359 }
360
361
362
363
364
365
366
367 public void setNeedClientAuth(boolean needClientAuth) {
368 this.needClientAuth = needClientAuth;
369 }
370
371
372
373
374
375 public boolean isWantClientAuth() {
376 return wantClientAuth;
377 }
378
379
380
381
382
383
384
385 public void setWantClientAuth(boolean wantClientAuth) {
386 this.wantClientAuth = wantClientAuth;
387 }
388
389
390
391
392
393 public String[] getEnabledCipherSuites() {
394 return enabledCipherSuites;
395 }
396
397
398
399
400
401
402
403 public void setEnabledCipherSuites(String[] cipherSuites) {
404 this.enabledCipherSuites = cipherSuites;
405 }
406
407
408
409
410
411 public String[] getEnabledProtocols() {
412 return enabledProtocols;
413 }
414
415
416
417
418
419
420
421 public void setEnabledProtocols(String[] protocols) {
422 this.enabledProtocols = protocols;
423 }
424
425
426
427
428
429
430
431
432
433
434 @Override
435 public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
436
437 if (parent.contains(SslFilter.class)) {
438 String msg = "Only one SSL filter is permitted in a chain.";
439 LOGGER.error(msg);
440 throw new IllegalStateException(msg);
441 }
442
443 if (LOGGER.isDebugEnabled()) {
444 LOGGER.debug("Adding the SSL Filter {} to the chain", name);
445 }
446
447 IoSession session = parent.getSession();
448 session.setAttribute(NEXT_FILTER, nextFilter);
449
450
451 SslHandlerdler.html#SslHandler">SslHandler sslHandler = new SslHandler(this, session);
452
453
454 if ((enabledCipherSuites == null) || (enabledCipherSuites.length == 0)) {
455 enabledCipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites();
456 }
457
458 sslHandler.init();
459
460 session.setAttribute(SSL_HANDLER, sslHandler);
461 }
462
463 @Override
464 public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
465 if (autoStart == START_HANDSHAKE) {
466 initiateHandshake(nextFilter, parent.getSession());
467 }
468 }
469
470 @Override
471 public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
472 IoSession session = parent.getSession();
473 stopSsl(session);
474 session.removeAttribute(NEXT_FILTER);
475 session.removeAttribute(SSL_HANDLER);
476 }
477
478
479 @Override
480 public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
481 SslHandler sslHandler = getSslSessionHandler(session);
482
483 try {
484 synchronized (sslHandler) {
485
486 sslHandler.destroy();
487 }
488 } finally {
489
490 nextFilter.sessionClosed(session);
491 }
492 }
493
494 @Override
495 public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
496 if (LOGGER.isDebugEnabled()) {
497 LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
498 }
499
500 SslHandler sslHandler = getSslSessionHandler(session);
501
502 synchronized (sslHandler) {
503 if (!isSslStarted(session) && sslHandler.isInboundDone()) {
504
505
506
507 sslHandler.scheduleMessageReceived(nextFilter, message);
508 } else {
509 IoBuffer"../../../../../org/apache/mina/core/buffer/IoBuffer.html#IoBuffer">IoBuffer buf = (IoBuffer) message;
510
511 try {
512 if (sslHandler.isOutboundDone()) {
513 sslHandler.destroy();
514 throw new SSLException("Outbound done");
515 }
516
517
518 sslHandler.messageReceived(nextFilter, buf.buf());
519
520
521 handleSslData(nextFilter, sslHandler);
522
523 if (sslHandler.isInboundDone()) {
524 if (sslHandler.isOutboundDone()) {
525 sslHandler.destroy();
526 } else {
527 initiateClosure(nextFilter, session);
528 }
529
530 if (buf.hasRemaining()) {
531
532 sslHandler.scheduleMessageReceived(nextFilter, buf);
533 }
534 }
535 } catch (SSLException ssle) {
536 if (!sslHandler.isHandshakeComplete()) {
537 SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
538 newSsle.initCause(ssle);
539 ssle = newSsle;
540
541
542 session.closeNow();
543 } else {
544
545 sslHandler.release();
546 }
547
548 throw ssle;
549 }
550 }
551 }
552
553 sslHandler.flushScheduledEvents();
554 }
555
556 @Override
557 public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
558 if (writeRequest instanceof EncryptedWriteRequest) {
559 EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
560 nextFilter.messageSent(session, wrappedRequest.getParentRequest());
561 } else {
562
563 }
564 }
565
566 @Override
567 public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
568
569 if (cause instanceof WriteToClosedSessionException) {
570
571
572 WriteToClosedSessionException/apache/mina/core/write/WriteToClosedSessionException.html#WriteToClosedSessionException">WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
573 List<WriteRequest> failedRequests = e.getRequests();
574 boolean containsCloseNotify = false;
575
576 for (WriteRequest r : failedRequests) {
577 if (isCloseNotify(r.getMessage())) {
578 containsCloseNotify = true;
579 break;
580 }
581 }
582
583 if (containsCloseNotify) {
584 if (failedRequests.size() == 1) {
585
586 return;
587 }
588
589 List<WriteRequest> newFailedRequests = new ArrayList<>(failedRequests.size() - 1);
590
591 for (WriteRequest r : failedRequests) {
592 if (!isCloseNotify(r.getMessage())) {
593 newFailedRequests.add(r);
594 }
595 }
596
597 if (newFailedRequests.isEmpty()) {
598
599 return;
600 }
601
602 cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
603 }
604 }
605
606 nextFilter.exceptionCaught(session, cause);
607 }
608
609 private boolean isCloseNotify(Object message) {
610 if (!(message instanceof IoBuffer)) {
611 return false;
612 }
613
614 IoBuffer"../../../../../org/apache/mina/core/buffer/IoBuffer.html#IoBuffer">IoBuffer buf = (IoBuffer) message;
615 int offset = buf.position();
616
617 return (buf.get(offset + 0) == 0x15)
618 && (buf.get(offset + 1) == 0x03)
619 && ((buf.get(offset + 2) == 0x00)
620 || (buf.get(offset + 2) == 0x01)
621 || (buf.get(offset + 2) == 0x02)
622 || (buf.get(offset + 2) == 0x03))
623 && (buf.get(offset + 3) == 0x00);
624 }
625
626 @Override
627 public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
628 if (LOGGER.isDebugEnabled()) {
629 LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
630 }
631
632 boolean needsFlush = true;
633 SslHandler sslHandler = getSslSessionHandler(session);
634
635 try {
636 synchronized (sslHandler) {
637 if (!isSslStarted(session)) {
638 sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
639 }
640
641 else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
642
643 session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
644 sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
645 } else {
646
647 IoBuffer"../../../../../org/apache/mina/core/buffer/IoBuffer.html#IoBuffer">IoBuffer buf = (IoBuffer) writeRequest.getMessage();
648
649 if (sslHandler.isWritingEncryptedData()) {
650
651 sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
652 } else if (sslHandler.isHandshakeComplete()) {
653
654 sslHandler.encrypt(buf.buf());
655 IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer();
656 writeRequest.setMessage( encryptedBuffer );
657 sslHandler.scheduleFilterWrite(nextFilter, new EncryptedWriteRequest(writeRequest,
658 encryptedBuffer));
659 } else {
660 if (session.isConnected()) {
661
662 sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
663 }
664
665 needsFlush = false;
666 }
667 }
668 }
669
670 if (needsFlush) {
671 sslHandler.flushScheduledEvents();
672 }
673 } catch (SSLException se) {
674 sslHandler.release();
675 throw se;
676 }
677 }
678
679 @Override
680 public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
681 SslHandler/../../org/apache/mina/filter/ssl/SslHandler.html#SslHandler">SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
682
683 if (sslHandler == null) {
684
685
686 nextFilter.filterClose(session);
687 return;
688 }
689
690 WriteFuture future = null;
691
692 try {
693 synchronized (sslHandler) {
694 if (isSslStarted(session)) {
695 future = initiateClosure(nextFilter, session);
696 future.addListener(new IoFutureListener<IoFuture>() {
697 @Override
698 public void operationComplete(IoFuture future) {
699 nextFilter.filterClose(session);
700 }
701 });
702 }
703 }
704
705 sslHandler.flushScheduledEvents();
706 } catch (SSLException se) {
707 sslHandler.release();
708 throw se;
709 } finally {
710 if (future == null) {
711 nextFilter.filterClose(session);
712 }
713 }
714 }
715
716
717
718
719
720
721
722
723 public void initiateHandshake(IoSession session) throws SSLException {
724 IoFilterChain filterChain = session.getFilterChain();
725
726 if (filterChain == null) {
727 throw new SSLException("No filter chain");
728 }
729
730 IoFilter.NextFilter nextFilter = filterChain.getNextFilter(SslFilter.class);
731
732 if (nextFilter == null) {
733 throw new SSLException("No SSL next filter in the chain");
734 }
735
736 initiateHandshake(nextFilter, session);
737 }
738
739 private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
740 if (LOGGER.isDebugEnabled()) {
741 LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
742 }
743
744 SslHandler sslHandler = getSslSessionHandler(session);
745
746 try {
747 synchronized (sslHandler) {
748 sslHandler.handshake(nextFilter);
749 }
750
751 sslHandler.flushScheduledEvents();
752 } catch (SSLException se) {
753 sslHandler.release();
754 throw se;
755 }
756 }
757
758 private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
759 SslHandler sslHandler = getSslSessionHandler(session);
760 WriteFuture future = null;
761
762
763 try {
764 synchronized(sslHandler) {
765 if (!sslHandler.closeOutbound()) {
766 return DefaultWriteFuture.newNotWrittenFuture(session, new IllegalStateException(
767 "SSL session is shut down already."));
768 }
769
770
771 future = sslHandler.writeNetBuffer(nextFilter);
772
773 if (future == null) {
774 future = DefaultWriteFuture.newWrittenFuture(session);
775 }
776
777 if (sslHandler.isInboundDone()) {
778 sslHandler.destroy();
779 }
780 }
781
782
783 session.getFilterChain().fireEvent(SslEvent.UNSECURED);
784 } catch (SSLException se) {
785 sslHandler.release();
786 throw se;
787 }
788
789 return future;
790 }
791
792
793 private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException {
794 if (LOGGER.isDebugEnabled()) {
795 LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession()));
796 }
797
798
799 if (sslHandler.isHandshakeComplete()) {
800 sslHandler.flushPreHandshakeEvents();
801 }
802
803
804 sslHandler.writeNetBuffer(nextFilter);
805
806
807 handleAppDataRead(nextFilter, sslHandler);
808 }
809
810 private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) {
811
812 IoBuffer readBuffer = sslHandler.fetchAppBuffer();
813
814 if (readBuffer.hasRemaining()) {
815 sslHandler.scheduleMessageReceived(nextFilter, readBuffer);
816 }
817 }
818
819 private SslHandler getSslSessionHandler(IoSession session) {
820 SslHandler/../../org/apache/mina/filter/ssl/SslHandler.html#SslHandler">SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
821
822 if (sslHandler == null) {
823 throw new IllegalStateException();
824 }
825
826 synchronized(sslHandler) {
827 if (sslHandler.getSslFilter() != this) {
828 throw new IllegalArgumentException("Not managed by this filter.");
829 }
830 }
831
832 return sslHandler;
833 }
834
835
836
837
838
839
840
841 public static class SslFilterMessage {
842 private final String name;
843
844 private SslFilterMessage(String name) {
845 this.name = name;
846 }
847
848 @Override
849 public String toString() {
850 return name;
851 }
852 }
853
854
855
856
857
858
859
860 static class EncryptedWriteRequest extends DefaultWriteRequest {
861
862 private final IoBuffer encryptedMessage;
863
864
865 private WriteRequest parentRequest;
866
867
868
869
870
871
872 private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
873 super(encryptedMessage);
874 parentRequest = writeRequest;
875 this.encryptedMessage = encryptedMessage;
876 }
877
878
879
880
881 @Override
882 public Object getMessage() {
883 return encryptedMessage;
884 }
885
886
887
888
889 public WriteRequest getParentRequest() {
890 return parentRequest;
891 }
892 }
893 }