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