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