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 if (autoStart == START_HANDSHAKE) {
420 initiateHandshake(nextFilter, parent.getSession());
421 }
422 }
423
424 @Override
425 public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
426 IoSession session = parent.getSession();
427 stopSsl(session);
428 session.removeAttribute(NEXT_FILTER);
429 session.removeAttribute(SSL_HANDLER);
430 }
431
432
433 @Override
434 public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
435 SslHandler handler = getSslSessionHandler(session);
436 try {
437 synchronized (handler) {
438
439 handler.destroy();
440 }
441
442 handler.flushScheduledEvents();
443 } finally {
444
445 nextFilter.sessionClosed(session);
446 }
447 }
448
449 @Override
450 public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
451 if (LOGGER.isDebugEnabled()) {
452 LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
453 }
454
455 SslHandler handler = getSslSessionHandler(session);
456
457 synchronized (handler) {
458 if (!isSslStarted(session) && handler.isInboundDone()) {
459
460
461
462 handler.scheduleMessageReceived(nextFilter, message);
463 } else {
464 IoBuffer buf = (IoBuffer) message;
465
466 try {
467
468 handler.messageReceived(nextFilter, buf.buf());
469
470
471 handleSslData(nextFilter, handler);
472
473 if (handler.isInboundDone()) {
474 if (handler.isOutboundDone()) {
475 handler.destroy();
476 } else {
477 initiateClosure(nextFilter, session);
478 }
479
480 if (buf.hasRemaining()) {
481
482 handler.scheduleMessageReceived(nextFilter, buf);
483 }
484 }
485 } catch (SSLException ssle) {
486 if (!handler.isHandshakeComplete()) {
487 SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
488 newSsle.initCause(ssle);
489 ssle = newSsle;
490 }
491
492 throw ssle;
493 }
494 }
495 }
496
497 handler.flushScheduledEvents();
498 }
499
500 @Override
501 public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
502 if (writeRequest instanceof EncryptedWriteRequest) {
503 EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
504 nextFilter.messageSent(session, wrappedRequest.getParentRequest());
505 } else {
506
507 }
508 }
509
510 @Override
511 public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
512
513 if (cause instanceof WriteToClosedSessionException) {
514
515
516 WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
517 List<WriteRequest> failedRequests = e.getRequests();
518 boolean containsCloseNotify = false;
519 for (WriteRequest r : failedRequests) {
520 if (isCloseNotify(r.getMessage())) {
521 containsCloseNotify = true;
522 break;
523 }
524 }
525
526 if (containsCloseNotify) {
527 if (failedRequests.size() == 1) {
528
529 return;
530 }
531
532 List<WriteRequest> newFailedRequests = new ArrayList<WriteRequest>(failedRequests.size() - 1);
533 for (WriteRequest r : failedRequests) {
534 if (!isCloseNotify(r.getMessage())) {
535 newFailedRequests.add(r);
536 }
537 }
538
539 if (newFailedRequests.isEmpty()) {
540
541 return;
542 }
543
544 cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
545 }
546 }
547
548 nextFilter.exceptionCaught(session, cause);
549 }
550
551 private boolean isCloseNotify(Object message) {
552 if (!(message instanceof IoBuffer)) {
553 return false;
554 }
555
556 IoBuffer buf = (IoBuffer) message;
557 int offset = buf.position();
558 return (buf.get(offset + 0) == 0x15)
559 && (buf.get(offset + 1) == 0x03)
560 && ((buf.get(offset + 2) == 0x00)
561 || (buf.get(offset + 2) == 0x01)
562 || (buf.get(offset + 2) == 0x02)
563 || (buf.get(offset + 2) == 0x03))
564 && (buf.get(offset + 3) == 0x00);
565 }
566
567 @Override
568 public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
569 if (LOGGER.isDebugEnabled()) {
570 LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
571 }
572
573 boolean needsFlush = true;
574 SslHandler handler = getSslSessionHandler(session);
575 synchronized (handler) {
576 if (!isSslStarted(session)) {
577 handler.scheduleFilterWrite(nextFilter, writeRequest);
578 }
579
580 else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
581
582 session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
583 handler.scheduleFilterWrite(nextFilter, writeRequest);
584 } else {
585
586 IoBuffer buf = (IoBuffer) writeRequest.getMessage();
587
588 if (handler.isWritingEncryptedData()) {
589
590 handler.scheduleFilterWrite(nextFilter, writeRequest);
591 } else if (handler.isHandshakeComplete()) {
592
593 int pos = buf.position();
594 handler.encrypt(buf.buf());
595 buf.position(pos);
596 IoBuffer encryptedBuffer = handler.fetchOutNetBuffer();
597 handler.scheduleFilterWrite(nextFilter, new EncryptedWriteRequest(writeRequest, encryptedBuffer));
598 } else {
599 if (session.isConnected()) {
600
601 handler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
602 }
603 needsFlush = false;
604 }
605 }
606 }
607
608 if (needsFlush) {
609 handler.flushScheduledEvents();
610 }
611 }
612
613 @Override
614 public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
615 SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER);
616 if (handler == null) {
617
618
619 nextFilter.filterClose(session);
620 return;
621 }
622
623 WriteFuture future = null;
624 try {
625 synchronized (handler) {
626 if (isSslStarted(session)) {
627 future = initiateClosure(nextFilter, session);
628 future.addListener(new IoFutureListener<IoFuture>() {
629 public void operationComplete(IoFuture future) {
630 nextFilter.filterClose(session);
631 }
632 });
633 }
634 }
635
636 handler.flushScheduledEvents();
637 } finally {
638 if (future == null) {
639 nextFilter.filterClose(session);
640 }
641 }
642 }
643
644 private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
645 LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
646 SslHandler handler = getSslSessionHandler(session);
647
648 synchronized (handler) {
649 handler.handshake(nextFilter);
650 }
651
652 handler.flushScheduledEvents();
653 }
654
655 private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
656 SslHandler handler = getSslSessionHandler(session);
657
658
659 if (!handler.closeOutbound()) {
660 return DefaultWriteFuture.newNotWrittenFuture(session, new IllegalStateException(
661 "SSL session is shut down already."));
662 }
663
664
665 WriteFuture future = handler.writeNetBuffer(nextFilter);
666
667 if (future == null) {
668 future = DefaultWriteFuture.newWrittenFuture(session);
669 }
670
671 if (handler.isInboundDone()) {
672 handler.destroy();
673 }
674
675 if (session.containsAttribute(USE_NOTIFICATION)) {
676 handler.scheduleMessageReceived(nextFilter, SESSION_UNSECURED);
677 }
678
679 return future;
680 }
681
682
683 private void handleSslData(NextFilter nextFilter, SslHandler handler) throws SSLException {
684 if (LOGGER.isDebugEnabled()) {
685 LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(handler.getSession()));
686 }
687
688
689 if (handler.isHandshakeComplete()) {
690 handler.flushPreHandshakeEvents();
691 }
692
693
694 handler.writeNetBuffer(nextFilter);
695
696
697 handleAppDataRead(nextFilter, handler);
698 }
699
700 private void handleAppDataRead(NextFilter nextFilter, SslHandler handler) {
701
702 IoBuffer readBuffer = handler.fetchAppBuffer();
703
704 if (readBuffer.hasRemaining()) {
705 handler.scheduleMessageReceived(nextFilter, readBuffer);
706 }
707 }
708
709 private SslHandler getSslSessionHandler(IoSession session) {
710 SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER);
711
712 if (handler == null) {
713 throw new IllegalStateException();
714 }
715
716 if (handler.getSslFilter() != this) {
717 throw new IllegalArgumentException("Not managed by this filter.");
718 }
719
720 return handler;
721 }
722
723
724
725
726
727
728
729 public static class SslFilterMessage {
730 private final String name;
731
732 private SslFilterMessage(String name) {
733 this.name = name;
734 }
735
736 @Override
737 public String toString() {
738 return name;
739 }
740 }
741
742 private static class EncryptedWriteRequest extends WriteRequestWrapper {
743 private final IoBuffer encryptedMessage;
744
745 private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
746 super(writeRequest);
747 this.encryptedMessage = encryptedMessage;
748 }
749
750 @Override
751 public Object getMessage() {
752 return encryptedMessage;
753 }
754 }
755 }