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