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.IoFilterAdapter;
34 import org.apache.mina.core.filterchain.IoFilterChain;
35 import org.apache.mina.core.future.DefaultWriteFuture;
36 import org.apache.mina.core.future.IoFuture;
37 import org.apache.mina.core.future.IoFutureListener;
38 import org.apache.mina.core.future.WriteFuture;
39 import org.apache.mina.core.service.IoAcceptor;
40 import org.apache.mina.core.service.IoHandler;
41 import org.apache.mina.core.session.AttributeKey;
42 import org.apache.mina.core.session.IoSession;
43 import org.apache.mina.core.write.WriteRequest;
44 import org.apache.mina.core.write.WriteRequestWrapper;
45 import org.apache.mina.core.write.WriteToClosedSessionException;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
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
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 AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
98
99
100
101
102
103
104
105
106
107
108
109 public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
110
111
112
113
114
115
116
117
118
119 public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
134
135
136
137
138
139
140 public static final SslFilterMessage SESSION_SECURED = new SslFilterMessage("SESSION_SECURED");
141
142
143
144
145
146
147 public static final SslFilterMessage SESSION_UNSECURED = new SslFilterMessage("SESSION_UNSECURED");
148
149 private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
150
151 private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
152
153
154 final SSLContext sslContext;
155
156
157 private final boolean autoStart;
158
159
160 private static final boolean START_HANDSHAKE = true;
161
162 private boolean client;
163
164 private boolean needClientAuth;
165
166 private boolean wantClientAuth;
167
168 private String[] enabledCipherSuites;
169
170 private String[] enabledProtocols;
171
172
173
174
175
176 public SslFilter(SSLContext sslContext) {
177 this(sslContext, START_HANDSHAKE);
178 }
179
180
181
182
183
184
185 public SslFilter(SSLContext sslContext, boolean autoStart) {
186 if (sslContext == null) {
187 throw new IllegalArgumentException("sslContext");
188 }
189
190 this.sslContext = sslContext;
191 this.autoStart = autoStart;
192 }
193
194
195
196
197
198
199 public SSLSession getSslSession(IoSession session) {
200 return (SSLSession) session.getAttribute(SSL_SESSION);
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 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 public boolean isSslStarted(IoSession session) {
276 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
277
278 if (sslHandler == null) {
279 return false;
280 }
281
282 synchronized (sslHandler) {
283 return !sslHandler.isOutboundDone();
284 }
285 }
286
287
288
289
290
291
292
293
294
295 public WriteFuture stopSsl(IoSession session) throws SSLException {
296 SslHandler sslHandler = getSslSessionHandler(session);
297 NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
298 WriteFuture future;
299
300 try {
301 synchronized (sslHandler) {
302 future = initiateClosure(nextFilter, session);
303 }
304
305 sslHandler.flushScheduledEvents();
306 } catch (SSLException se) {
307 sslHandler.release();
308 throw se;
309 }
310
311 return future;
312 }
313
314
315
316
317
318 public boolean isUseClientMode() {
319 return client;
320 }
321
322
323
324
325 public void setUseClientMode(boolean clientMode) {
326 this.client = clientMode;
327 }
328
329
330
331
332
333 public boolean isNeedClientAuth() {
334 return needClientAuth;
335 }
336
337
338
339
340
341 public void setNeedClientAuth(boolean needClientAuth) {
342 this.needClientAuth = needClientAuth;
343 }
344
345
346
347
348
349 public boolean isWantClientAuth() {
350 return wantClientAuth;
351 }
352
353
354
355
356
357 public void setWantClientAuth(boolean wantClientAuth) {
358 this.wantClientAuth = wantClientAuth;
359 }
360
361
362
363
364
365
366
367 public String[] getEnabledCipherSuites() {
368 return enabledCipherSuites;
369 }
370
371
372
373
374
375
376
377 public void setEnabledCipherSuites(String[] cipherSuites) {
378 this.enabledCipherSuites = cipherSuites;
379 }
380
381
382
383
384
385
386
387 public String[] getEnabledProtocols() {
388 return enabledProtocols;
389 }
390
391
392
393
394
395
396
397 public void setEnabledProtocols(String[] protocols) {
398 this.enabledProtocols = protocols;
399 }
400
401
402
403
404
405
406
407
408
409
410 @Override
411 public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
412
413 if (parent.contains(SslFilter.class)) {
414 String msg = "Only one SSL filter is permitted in a chain.";
415 LOGGER.error(msg);
416 throw new IllegalStateException(msg);
417 }
418
419 LOGGER.debug("Adding the SSL Filter {} to the chain", name);
420
421 IoSession session = parent.getSession();
422 session.setAttribute(NEXT_FILTER, nextFilter);
423
424
425 SslHandler sslHandler = new SslHandler(this, session);
426 sslHandler.init();
427
428
429
430
431 String[] ciphers = sslContext.getServerSocketFactory().getSupportedCipherSuites();
432 setEnabledCipherSuites(ciphers);
433 session.setAttribute(SSL_HANDLER, sslHandler);
434 }
435
436 @Override
437 public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
438 if (autoStart == START_HANDSHAKE) {
439 initiateHandshake(nextFilter, parent.getSession());
440 }
441 }
442
443 @Override
444 public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
445 IoSession session = parent.getSession();
446 stopSsl(session);
447 session.removeAttribute(NEXT_FILTER);
448 session.removeAttribute(SSL_HANDLER);
449 }
450
451
452 @Override
453 public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
454 SslHandler sslHandler = getSslSessionHandler(session);
455
456 try {
457 synchronized (sslHandler) {
458
459 sslHandler.destroy();
460 }
461
462 sslHandler.flushScheduledEvents();
463 } finally {
464
465 nextFilter.sessionClosed(session);
466 }
467 }
468
469 @Override
470 public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
471 if (LOGGER.isDebugEnabled()) {
472 LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
473 }
474
475 SslHandler sslHandler = getSslSessionHandler(session);
476
477 synchronized (sslHandler) {
478 if (!isSslStarted(session) && sslHandler.isInboundDone()) {
479
480
481
482 sslHandler.scheduleMessageReceived(nextFilter, message);
483 } else {
484 IoBuffer buf = (IoBuffer) message;
485
486 try {
487
488 sslHandler.messageReceived(nextFilter, buf.buf());
489
490
491 handleSslData(nextFilter, sslHandler);
492
493 if (sslHandler.isInboundDone()) {
494 if (sslHandler.isOutboundDone()) {
495 sslHandler.destroy();
496 } else {
497 initiateClosure(nextFilter, session);
498 }
499
500 if (buf.hasRemaining()) {
501
502 sslHandler.scheduleMessageReceived(nextFilter, buf);
503 }
504 }
505 } catch (SSLException ssle) {
506 if (!sslHandler.isHandshakeComplete()) {
507 SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
508 newSsle.initCause(ssle);
509 ssle = newSsle;
510 } else {
511
512 sslHandler.release();
513 }
514
515 throw ssle;
516 }
517 }
518 }
519
520 sslHandler.flushScheduledEvents();
521 }
522
523 @Override
524 public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
525 if (writeRequest instanceof EncryptedWriteRequest) {
526 EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
527 nextFilter.messageSent(session, wrappedRequest.getParentRequest());
528 } else {
529
530 }
531 }
532
533 @Override
534 public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
535
536 if (cause instanceof WriteToClosedSessionException) {
537
538
539 WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
540 List<WriteRequest> failedRequests = e.getRequests();
541 boolean containsCloseNotify = false;
542
543 for (WriteRequest r : failedRequests) {
544 if (isCloseNotify(r.getMessage())) {
545 containsCloseNotify = true;
546 break;
547 }
548 }
549
550 if (containsCloseNotify) {
551 if (failedRequests.size() == 1) {
552
553 return;
554 }
555
556 List<WriteRequest> newFailedRequests = new ArrayList<WriteRequest>(failedRequests.size() - 1);
557
558 for (WriteRequest r : failedRequests) {
559 if (!isCloseNotify(r.getMessage())) {
560 newFailedRequests.add(r);
561 }
562 }
563
564 if (newFailedRequests.isEmpty()) {
565
566 return;
567 }
568
569 cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
570 }
571 }
572
573 nextFilter.exceptionCaught(session, cause);
574 }
575
576 private boolean isCloseNotify(Object message) {
577 if (!(message instanceof IoBuffer)) {
578 return false;
579 }
580
581 IoBuffer buf = (IoBuffer) message;
582 int offset = buf.position();
583
584 return (buf.get(offset + 0) == 0x15)
585 && (buf.get(offset + 1) == 0x03)
586 && ((buf.get(offset + 2) == 0x00)
587 || (buf.get(offset + 2) == 0x01)
588 || (buf.get(offset + 2) == 0x02)
589 || (buf.get(offset + 2) == 0x03))
590 && (buf.get(offset + 3) == 0x00);
591 }
592
593 @Override
594 public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
595 if (LOGGER.isDebugEnabled()) {
596 LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
597 }
598
599 boolean needsFlush = true;
600 SslHandler sslHandler = getSslSessionHandler(session);
601
602 try {
603 synchronized (sslHandler) {
604 if (!isSslStarted(session)) {
605 sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
606 }
607
608 else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
609
610 session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
611 sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
612 } else {
613
614 IoBuffer buf = (IoBuffer) writeRequest.getMessage();
615
616 if (sslHandler.isWritingEncryptedData()) {
617
618 sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
619 } else if (sslHandler.isHandshakeComplete()) {
620
621 int pos = buf.position();
622 sslHandler.encrypt(buf.buf());
623 buf.position(pos);
624 IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer();
625 sslHandler.scheduleFilterWrite(nextFilter, new EncryptedWriteRequest(writeRequest,
626 encryptedBuffer));
627 } else {
628 if (session.isConnected()) {
629
630 sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
631 }
632
633 needsFlush = false;
634 }
635 }
636 }
637
638 if (needsFlush) {
639 sslHandler.flushScheduledEvents();
640 }
641 } catch (SSLException se) {
642 sslHandler.release();
643 throw se;
644 }
645 }
646
647 @Override
648 public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
649 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
650
651 if (sslHandler == null) {
652
653
654 nextFilter.filterClose(session);
655 return;
656 }
657
658 WriteFuture future = null;
659
660 try {
661 synchronized (sslHandler) {
662 if (isSslStarted(session)) {
663 future = initiateClosure(nextFilter, session);
664 future.addListener(new IoFutureListener<IoFuture>() {
665 public void operationComplete(IoFuture future) {
666 nextFilter.filterClose(session);
667 }
668 });
669 }
670 }
671
672 sslHandler.flushScheduledEvents();
673 } catch (SSLException se) {
674 sslHandler.release();
675 throw se;
676 } finally {
677 if (future == null) {
678 nextFilter.filterClose(session);
679 }
680 }
681 }
682
683 private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
684 LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
685 SslHandler sslHandler = getSslSessionHandler(session);
686
687 try {
688 synchronized (sslHandler) {
689 sslHandler.handshake(nextFilter);
690 }
691
692 sslHandler.flushScheduledEvents();
693 } catch (SSLException se) {
694 sslHandler.release();
695 throw se;
696 }
697 }
698
699 private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
700 SslHandler sslHandler = getSslSessionHandler(session);
701 WriteFuture future = null;
702
703
704 try {
705 if (!sslHandler.closeOutbound()) {
706 return DefaultWriteFuture.newNotWrittenFuture(session, new IllegalStateException(
707 "SSL session is shut down already."));
708 }
709
710
711 future = sslHandler.writeNetBuffer(nextFilter);
712
713 if (future == null) {
714 future = DefaultWriteFuture.newWrittenFuture(session);
715 }
716
717 if (sslHandler.isInboundDone()) {
718 sslHandler.destroy();
719 }
720
721 if (session.containsAttribute(USE_NOTIFICATION)) {
722 sslHandler.scheduleMessageReceived(nextFilter, SESSION_UNSECURED);
723 }
724 } catch (SSLException se) {
725 sslHandler.release();
726 throw se;
727 }
728
729 return future;
730 }
731
732
733 private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException {
734 if (LOGGER.isDebugEnabled()) {
735 LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession()));
736 }
737
738
739 if (sslHandler.isHandshakeComplete()) {
740 sslHandler.flushPreHandshakeEvents();
741 }
742
743
744 sslHandler.writeNetBuffer(nextFilter);
745
746
747 handleAppDataRead(nextFilter, sslHandler);
748 }
749
750 private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) {
751
752 IoBuffer readBuffer = sslHandler.fetchAppBuffer();
753
754 if (readBuffer.hasRemaining()) {
755 sslHandler.scheduleMessageReceived(nextFilter, readBuffer);
756 }
757 }
758
759 private SslHandler getSslSessionHandler(IoSession session) {
760 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
761
762 if (sslHandler == null) {
763 throw new IllegalStateException();
764 }
765
766 if (sslHandler.getSslFilter() != this) {
767 throw new IllegalArgumentException("Not managed by this filter.");
768 }
769
770 return sslHandler;
771 }
772
773
774
775
776
777
778
779 public static class SslFilterMessage {
780 private final String name;
781
782 private SslFilterMessage(String name) {
783 this.name = name;
784 }
785
786 @Override
787 public String toString() {
788 return name;
789 }
790 }
791
792 private static class EncryptedWriteRequest extends WriteRequestWrapper {
793 private final IoBuffer encryptedMessage;
794
795 private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
796 super(writeRequest);
797 this.encryptedMessage = encryptedMessage;
798 }
799
800 @Override
801 public Object getMessage() {
802 return encryptedMessage;
803 }
804 }
805 }