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