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.IoHandler;
40 import org.apache.mina.core.session.AttributeKey;
41 import org.apache.mina.core.session.IoSession;
42 import org.apache.mina.core.write.WriteRequest;
43 import org.apache.mina.core.write.WriteRequestWrapper;
44 import org.apache.mina.core.write.WriteToClosedSessionException;
45
46
47
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 public class SslFilter extends IoFilterAdapter {
87
88
89
90
91 public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
92
93
94
95
96
97
98
99
100
101
102
103 public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
104
105
106
107
108
109
110
111
112
113 public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
129
130
131
132
133
134
135 public static final SslFilterMessage SESSION_SECURED = new SslFilterMessage(
136 "SESSION_SECURED");
137
138
139
140
141
142
143 public static final SslFilterMessage SESSION_UNSECURED = new SslFilterMessage(
144 "SESSION_UNSECURED");
145
146 private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
147 private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
148
149
150 private final SSLContext sslContext;
151
152 private final boolean autoStart;
153
154 private boolean client;
155
156 private boolean needClientAuth;
157
158 private boolean wantClientAuth;
159
160 private String[] enabledCipherSuites;
161
162 private String[] enabledProtocols;
163
164
165
166
167 public SslFilter(SSLContext sslContext) {
168 this(sslContext, true);
169 }
170
171
172
173
174 public SslFilter(SSLContext sslContext, boolean autoStart) {
175 if (sslContext == null) {
176 throw new NullPointerException("sslContext");
177 }
178
179 this.sslContext = sslContext;
180 this.autoStart = autoStart;
181 }
182
183
184
185
186
187
188 public SSLSession getSslSession(IoSession session) {
189 return (SSLSession) session.getAttribute(SSL_SESSION);
190 }
191
192
193
194
195
196
197
198
199
200 public boolean startSsl(IoSession session) throws SSLException {
201 SslHandler handler = getSslSessionHandler(session);
202 boolean started;
203 synchronized (handler) {
204 if (handler.isOutboundDone()) {
205 NextFilter nextFilter = (NextFilter) session
206 .getAttribute(NEXT_FILTER);
207 handler.destroy();
208 handler.init();
209 handler.handshake(nextFilter);
210 started = true;
211 } else {
212 started = false;
213 }
214 }
215
216 handler.flushScheduledEvents();
217 return started;
218 }
219
220
221
222
223
224
225
226 public boolean isSslStarted(IoSession session) {
227 SslHandler handler = getSslSessionHandler0(session);
228 if (handler == null) {
229 return false;
230 }
231
232 synchronized (handler) {
233 return !handler.isOutboundDone();
234 }
235 }
236
237
238
239
240
241
242
243
244
245 public WriteFuture stopSsl(IoSession session) throws SSLException {
246 SslHandler handler = getSslSessionHandler(session);
247 NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
248 WriteFuture future;
249 synchronized (handler) {
250 future = initiateClosure(nextFilter, session);
251 }
252
253 handler.flushScheduledEvents();
254
255 return future;
256 }
257
258
259
260
261
262 public boolean isUseClientMode() {
263 return client;
264 }
265
266
267
268
269 public void setUseClientMode(boolean clientMode) {
270 this.client = clientMode;
271 }
272
273
274
275
276
277 public boolean isNeedClientAuth() {
278 return needClientAuth;
279 }
280
281
282
283
284
285 public void setNeedClientAuth(boolean needClientAuth) {
286 this.needClientAuth = needClientAuth;
287 }
288
289
290
291
292
293 public boolean isWantClientAuth() {
294 return wantClientAuth;
295 }
296
297
298
299
300
301 public void setWantClientAuth(boolean wantClientAuth) {
302 this.wantClientAuth = wantClientAuth;
303 }
304
305
306
307
308
309
310
311 public String[] getEnabledCipherSuites() {
312 return enabledCipherSuites;
313 }
314
315
316
317
318
319
320
321 public void setEnabledCipherSuites(String[] cipherSuites) {
322 this.enabledCipherSuites = cipherSuites;
323 }
324
325
326
327
328
329
330
331 public String[] getEnabledProtocols() {
332 return enabledProtocols;
333 }
334
335
336
337
338
339
340
341 public void setEnabledProtocols(String[] protocols) {
342 this.enabledProtocols = protocols;
343 }
344
345 @Override
346 public void onPreAdd(IoFilterChain parent, String name,
347 NextFilter nextFilter) throws SSLException {
348 if (parent.contains(SslFilter.class)) {
349 throw new IllegalStateException(
350 "Only one " + SslFilter.class.getName() + " is permitted.");
351 }
352
353 IoSession session = parent.getSession();
354 session.setAttribute(NEXT_FILTER, nextFilter);
355
356
357 SslHandler handler = new SslHandler(this, sslContext, session);
358 session.setAttribute(SSL_HANDLER, handler);
359 }
360
361 @Override
362 public void onPostAdd(IoFilterChain parent, String name,
363 NextFilter nextFilter) throws SSLException {
364 if (autoStart) {
365 initiateHandshake(nextFilter, parent.getSession());
366 }
367 }
368
369 @Override
370 public void onPreRemove(IoFilterChain parent, String name,
371 NextFilter nextFilter) throws SSLException {
372 IoSession session = parent.getSession();
373 stopSsl(session);
374 session.removeAttribute(NEXT_FILTER);
375 session.removeAttribute(SSL_HANDLER);
376 }
377
378
379 @Override
380 public void sessionClosed(NextFilter nextFilter, IoSession session)
381 throws SSLException {
382 SslHandler handler = getSslSessionHandler(session);
383 try {
384 synchronized (handler) {
385
386 handler.destroy();
387 }
388
389 handler.flushScheduledEvents();
390 } finally {
391
392 nextFilter.sessionClosed(session);
393 }
394 }
395
396 @Override
397 public void messageReceived(NextFilter nextFilter, IoSession session,
398 Object message) throws SSLException {
399 SslHandler handler = getSslSessionHandler(session);
400 synchronized (handler) {
401 if (!isSslStarted(session) && handler.isInboundDone()) {
402 handler.scheduleMessageReceived(nextFilter, message);
403 } else {
404 IoBuffer buf = (IoBuffer) message;
405 try {
406
407 handler.messageReceived(nextFilter, buf.buf());
408
409
410 handleSslData(nextFilter, handler);
411
412 if (handler.isInboundDone()) {
413 if (handler.isOutboundDone()) {
414 handler.destroy();
415 } else {
416 initiateClosure(nextFilter, session);
417 }
418
419 if (buf.hasRemaining()) {
420
421 handler.scheduleMessageReceived(nextFilter, buf);
422 }
423 }
424 } catch (SSLException ssle) {
425 if (!handler.isHandshakeComplete()) {
426 SSLException newSsle = new SSLHandshakeException(
427 "SSL handshake failed.");
428 newSsle.initCause(ssle);
429 ssle = newSsle;
430 }
431
432 throw ssle;
433 }
434 }
435 }
436
437 handler.flushScheduledEvents();
438 }
439
440 @Override
441 public void messageSent(NextFilter nextFilter, IoSession session,
442 WriteRequest writeRequest) {
443 if (writeRequest instanceof EncryptedWriteRequest) {
444 EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
445 nextFilter.messageSent(session, wrappedRequest.getParentRequest());
446 } else {
447
448 }
449 }
450
451 @Override
452 public void exceptionCaught(NextFilter nextFilter, IoSession session,
453 Throwable cause) throws Exception {
454
455 if (cause instanceof WriteToClosedSessionException) {
456
457
458 WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
459 List<WriteRequest> failedRequests = e.getRequests();
460 boolean containsCloseNotify = false;
461 for (WriteRequest r: failedRequests) {
462 if (isCloseNotify(r.getMessage())) {
463 containsCloseNotify = true;
464 break;
465 }
466 }
467
468 if (containsCloseNotify) {
469 if (failedRequests.size() == 1) {
470
471 return;
472 }
473
474 List<WriteRequest> newFailedRequests =
475 new ArrayList<WriteRequest>(failedRequests.size() - 1);
476 for (WriteRequest r: failedRequests) {
477 if (!isCloseNotify(r.getMessage())) {
478 newFailedRequests.add(r);
479 }
480 }
481
482 if (newFailedRequests.isEmpty()) {
483
484 return;
485 }
486
487 cause = new WriteToClosedSessionException(
488 newFailedRequests, cause.getMessage(), cause.getCause());
489 }
490 }
491
492 nextFilter.exceptionCaught(session, cause);
493 }
494
495 private boolean isCloseNotify(Object message) {
496 if (!(message instanceof IoBuffer)) {
497 return false;
498 }
499
500 IoBuffer buf = (IoBuffer) message;
501 int offset = buf.position();
502 return buf.remaining() == 23 &&
503 buf.get(offset + 0) == 0x15 && buf.get(offset + 1) == 0x03 &&
504 buf.get(offset + 2) == 0x01 && buf.get(offset + 3) == 0x00 &&
505 buf.get(offset + 4) == 0x12;
506 }
507
508 @Override
509 public void filterWrite(NextFilter nextFilter, IoSession session,
510 WriteRequest writeRequest) throws SSLException {
511 boolean needsFlush = true;
512 SslHandler handler = getSslSessionHandler(session);
513 synchronized (handler) {
514 if (!isSslStarted(session)) {
515 handler.scheduleFilterWrite(nextFilter,
516 writeRequest);
517 }
518
519 else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
520
521 session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
522 handler.scheduleFilterWrite(nextFilter,
523 writeRequest);
524 } else {
525
526 IoBuffer buf = (IoBuffer) writeRequest.getMessage();
527
528 if (handler.isWritingEncryptedData()) {
529
530 handler.scheduleFilterWrite(nextFilter, writeRequest);
531 } else if (handler.isHandshakeComplete()) {
532
533 int pos = buf.position();
534 handler.encrypt(buf.buf());
535 buf.position(pos);
536 IoBuffer encryptedBuffer = handler.fetchOutNetBuffer();
537 handler.scheduleFilterWrite(
538 nextFilter,
539 new EncryptedWriteRequest(
540 writeRequest, encryptedBuffer));
541 } else {
542 if (session.isConnected()) {
543
544 handler.schedulePreHandshakeWriteRequest(nextFilter,
545 writeRequest);
546 }
547 needsFlush = false;
548 }
549 }
550 }
551
552 if (needsFlush) {
553 handler.flushScheduledEvents();
554 }
555 }
556
557 @Override
558 public void filterClose(final NextFilter nextFilter, final IoSession session)
559 throws SSLException {
560 SslHandler handler = getSslSessionHandler0(session);
561 if (handler == null) {
562
563
564 nextFilter.filterClose(session);
565 return;
566 }
567
568 WriteFuture future = null;
569 try {
570 synchronized (handler) {
571 if (isSslStarted(session)) {
572 future = initiateClosure(nextFilter, session);
573 future.addListener(new IoFutureListener<IoFuture>() {
574 public void operationComplete(IoFuture future) {
575 nextFilter.filterClose(session);
576 }
577 });
578 }
579 }
580
581 handler.flushScheduledEvents();
582 } finally {
583 if (future == null) {
584 nextFilter.filterClose(session);
585 }
586 }
587 }
588
589 private void initiateHandshake(NextFilter nextFilter, IoSession session)
590 throws SSLException {
591 SslHandler handler = getSslSessionHandler(session);
592 synchronized (handler) {
593 handler.handshake(nextFilter);
594 }
595 handler.flushScheduledEvents();
596 }
597
598 private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session)
599 throws SSLException {
600 SslHandler handler = getSslSessionHandler(session);
601
602 if (!handler.closeOutbound()) {
603 return DefaultWriteFuture.newNotWrittenFuture(
604 session, new IllegalStateException("SSL session is shut down already."));
605 }
606
607
608 WriteFuture future = handler.writeNetBuffer(nextFilter);
609 if (future == null) {
610 future = DefaultWriteFuture.newWrittenFuture(session);
611 }
612
613 if (handler.isInboundDone()) {
614 handler.destroy();
615 }
616
617 if (session.containsAttribute(USE_NOTIFICATION)) {
618 handler.scheduleMessageReceived(nextFilter, SESSION_UNSECURED);
619 }
620
621 return future;
622 }
623
624
625
626 private void handleSslData(NextFilter nextFilter, SslHandler handler)
627 throws SSLException {
628
629 if (handler.isHandshakeComplete()) {
630 handler.flushPreHandshakeEvents();
631 }
632
633
634 handler.writeNetBuffer(nextFilter);
635
636
637 handleAppDataRead(nextFilter, handler);
638 }
639
640 private void handleAppDataRead(NextFilter nextFilter, SslHandler handler) {
641
642 IoBuffer readBuffer = handler.fetchAppBuffer();
643 if (readBuffer.hasRemaining()) {
644 handler.scheduleMessageReceived(nextFilter, readBuffer);
645 }
646 }
647
648 private SslHandler getSslSessionHandler(IoSession session) {
649 SslHandler handler = getSslSessionHandler0(session);
650 if (handler == null) {
651 throw new IllegalStateException();
652 }
653 if (handler.getParent() != this) {
654 throw new IllegalArgumentException("Not managed by this filter.");
655 }
656 return handler;
657 }
658
659 private SslHandler getSslSessionHandler0(IoSession session) {
660 return (SslHandler) session.getAttribute(SSL_HANDLER);
661 }
662
663
664
665
666
667
668
669
670 public static class SslFilterMessage {
671 private final String name;
672
673 private SslFilterMessage(String name) {
674 this.name = name;
675 }
676
677 @Override
678 public String toString() {
679 return name;
680 }
681 }
682
683 private static class EncryptedWriteRequest extends WriteRequestWrapper {
684 private final IoBuffer encryptedMessage;
685
686 private EncryptedWriteRequest(WriteRequest writeRequest,
687 IoBuffer encryptedMessage) {
688 super(writeRequest);
689 this.encryptedMessage = encryptedMessage;
690 }
691
692 @Override
693 public Object getMessage() {
694 return encryptedMessage;
695 }
696 }
697 }