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