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;
21
22 import javax.net.ssl.SSLContext;
23 import javax.net.ssl.SSLEngine;
24 import javax.net.ssl.SSLException;
25 import javax.net.ssl.SSLHandshakeException;
26 import javax.net.ssl.SSLSession;
27
28 import org.apache.mina.common.ByteBuffer;
29 import org.apache.mina.common.ByteBufferProxy;
30 import org.apache.mina.common.IoFilterAdapter;
31 import org.apache.mina.common.IoFilterChain;
32 import org.apache.mina.common.IoFuture;
33 import org.apache.mina.common.IoFutureListener;
34 import org.apache.mina.common.IoHandler;
35 import org.apache.mina.common.IoSession;
36 import org.apache.mina.common.WriteFuture;
37 import org.apache.mina.common.support.DefaultWriteFuture;
38 import org.apache.mina.filter.support.SSLHandler;
39 import org.apache.mina.util.SessionLog;
40
41
42
43
44
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 public class SSLFilter extends IoFilterAdapter {
81
82
83
84
85 public static final String SSL_SESSION = SSLFilter.class.getName()
86 + ".SSLSession";
87
88
89
90
91
92
93
94
95
96
97
98 public static final String DISABLE_ENCRYPTION_ONCE = SSLFilter.class
99 .getName()
100 + ".DisableEncryptionOnce";
101
102
103
104
105
106
107
108
109
110 public static final String USE_NOTIFICATION = SSLFilter.class.getName()
111 + ".UseNotification";
112
113
114
115
116
117
118 public static final SSLFilterMessage SESSION_SECURED = new SSLFilterMessage(
119 "SESSION_SECURED");
120
121
122
123
124
125
126 public static final SSLFilterMessage SESSION_UNSECURED = new SSLFilterMessage(
127 "SESSION_UNSECURED");
128
129 private static final String NEXT_FILTER = SSLFilter.class.getName()
130 + ".NextFilter";
131
132 private static final String SSL_HANDLER = SSLFilter.class.getName()
133 + ".SSLHandler";
134
135
136 private SSLContext sslContext;
137
138 private boolean client;
139
140 private boolean needClientAuth;
141
142 private boolean wantClientAuth;
143
144 private String[] enabledCipherSuites;
145
146 private String[] enabledProtocols;
147
148
149
150
151 public SSLFilter(SSLContext sslContext) {
152 if (sslContext == null) {
153 throw new NullPointerException("sslContext");
154 }
155
156 this.sslContext = sslContext;
157 }
158
159
160
161
162
163
164 public SSLSession getSSLSession(IoSession session) {
165 return (SSLSession) session.getAttribute(SSL_SESSION);
166 }
167
168
169
170
171
172
173
174
175
176 public boolean startSSL(IoSession session) throws SSLException {
177 SSLHandler handler = getSSLSessionHandler(session);
178 boolean started;
179 synchronized (handler) {
180 if (handler.isOutboundDone()) {
181 NextFilter nextFilter = (NextFilter) session
182 .getAttribute(NEXT_FILTER);
183 handler.destroy();
184 handler.init();
185 handler.handshake(nextFilter);
186 started = true;
187 } else {
188 started = false;
189 }
190 }
191
192 handler.flushScheduledEvents();
193 return started;
194 }
195
196
197
198
199
200
201
202 public boolean isSSLStarted(IoSession session) {
203 SSLHandler handler = getSSLSessionHandler0(session);
204 if (handler == null) {
205 return false;
206 }
207
208 synchronized (handler) {
209 return !handler.isOutboundDone();
210 }
211 }
212
213
214
215
216
217
218
219
220
221 public WriteFuture stopSSL(IoSession session) throws SSLException {
222 SSLHandler handler = getSSLSessionHandler(session);
223 NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
224 WriteFuture future;
225 synchronized (handler) {
226 future = initiateClosure(nextFilter, session);
227 }
228
229 handler.flushScheduledEvents();
230
231 return future;
232 }
233
234
235
236
237
238 public boolean isUseClientMode() {
239 return client;
240 }
241
242
243
244
245 public void setUseClientMode(boolean clientMode) {
246 this.client = clientMode;
247 }
248
249
250
251
252
253 public boolean isNeedClientAuth() {
254 return needClientAuth;
255 }
256
257
258
259
260
261 public void setNeedClientAuth(boolean needClientAuth) {
262 this.needClientAuth = needClientAuth;
263 }
264
265
266
267
268
269 public boolean isWantClientAuth() {
270 return wantClientAuth;
271 }
272
273
274
275
276
277 public void setWantClientAuth(boolean wantClientAuth) {
278 this.wantClientAuth = wantClientAuth;
279 }
280
281
282
283
284
285
286
287 public String[] getEnabledCipherSuites() {
288 return enabledCipherSuites;
289 }
290
291
292
293
294
295
296
297 public void setEnabledCipherSuites(String[] cipherSuites) {
298 this.enabledCipherSuites = cipherSuites;
299 }
300
301
302
303
304
305
306
307 public String[] getEnabledProtocols() {
308 return enabledProtocols;
309 }
310
311
312
313
314
315
316
317 public void setEnabledProtocols(String[] protocols) {
318 this.enabledProtocols = protocols;
319 }
320
321 public void onPreAdd(IoFilterChain parent, String name,
322 NextFilter nextFilter) throws SSLException {
323 if (parent.contains(SSLFilter.class)) {
324 throw new IllegalStateException(
325 "A filter chain cannot contain more than one SSLFilter.");
326 }
327
328 IoSession session = parent.getSession();
329 session.setAttribute(NEXT_FILTER, nextFilter);
330
331
332 SSLHandler handler = new SSLHandler(this, sslContext, session);
333 session.setAttribute(SSL_HANDLER, handler);
334 }
335
336 public void onPostAdd(IoFilterChain parent, String name,
337 NextFilter nextFilter) throws SSLException {
338 SSLHandler handler = getSSLSessionHandler(parent.getSession());
339 synchronized (handler) {
340 handler.handshake(nextFilter);
341 }
342 handler.flushScheduledEvents();
343 }
344
345 public void onPreRemove(IoFilterChain parent, String name,
346 NextFilter nextFilter) throws SSLException {
347 IoSession session = parent.getSession();
348 stopSSL(session);
349 session.removeAttribute(NEXT_FILTER);
350 session.removeAttribute(SSL_HANDLER);
351 }
352
353
354 public void sessionClosed(NextFilter nextFilter, IoSession session)
355 throws SSLException {
356 SSLHandler handler = getSSLSessionHandler(session);
357 try {
358 synchronized (handler) {
359 if (isSSLStarted(session)) {
360 if (SessionLog.isDebugEnabled(session)) {
361 SessionLog.debug(session, " Closed: "
362 + getSSLSessionHandler(session));
363 }
364 }
365
366
367 handler.destroy();
368 }
369
370 handler.flushScheduledEvents();
371 } finally {
372
373 nextFilter.sessionClosed(session);
374 }
375 }
376
377 public void messageReceived(NextFilter nextFilter, IoSession session,
378 Object message) throws SSLException {
379 SSLHandler handler = getSSLSessionHandler(session);
380 synchronized (handler) {
381 if (!isSSLStarted(session) && handler.isInboundDone()) {
382 handler.scheduleMessageReceived(nextFilter, message);
383 } else {
384 ByteBuffer buf = (ByteBuffer) message;
385 if (SessionLog.isDebugEnabled(session)) {
386 SessionLog.debug(session, " Data Read: " + handler + " ("
387 + buf + ')');
388 }
389
390 try {
391
392 handler.messageReceived(nextFilter, buf.buf());
393
394
395 handleSSLData(nextFilter, handler);
396
397 if (handler.isInboundDone()) {
398 if (handler.isOutboundDone()) {
399 if (SessionLog.isDebugEnabled(session)) {
400 SessionLog.debug(session,
401 " SSL Session closed.");
402 }
403
404 handler.destroy();
405 } else {
406 initiateClosure(nextFilter, session);
407 }
408
409 if (buf.hasRemaining()) {
410
411 handler.scheduleMessageReceived(nextFilter, buf);
412 }
413 }
414 } catch (SSLException ssle) {
415 if (!handler.isHandshakeComplete()) {
416 SSLException newSSLE = new SSLHandshakeException(
417 "SSL handshake failed.");
418 newSSLE.initCause(ssle);
419 ssle = newSSLE;
420 }
421
422 throw ssle;
423 }
424 }
425 }
426
427 handler.flushScheduledEvents();
428 }
429
430 public void messageSent(NextFilter nextFilter, IoSession session,
431 Object message) {
432 if (message instanceof EncryptedBuffer) {
433 EncryptedBuffer buf = (EncryptedBuffer) message;
434 buf.release();
435 nextFilter.messageSent(session, buf.originalBuffer);
436 } else {
437
438 }
439 }
440
441 public void filterWrite(NextFilter nextFilter, IoSession session,
442 WriteRequest writeRequest) throws SSLException {
443 boolean needsFlush = true;
444 SSLHandler handler = getSSLSessionHandler(session);
445 synchronized (handler) {
446 if (!isSSLStarted(session)) {
447 handler.scheduleFilterWrite(nextFilter,
448 writeRequest);
449 }
450
451 else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
452
453 session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
454 handler.scheduleFilterWrite(nextFilter,
455 writeRequest);
456 } else {
457
458 ByteBuffer buf = (ByteBuffer) writeRequest.getMessage();
459
460 if (SessionLog.isDebugEnabled(session)) {
461 SessionLog.debug(session, " Filtered Write: " + handler);
462 }
463
464 if (handler.isWritingEncryptedData()) {
465
466 if (SessionLog.isDebugEnabled(session)) {
467 SessionLog.debug(session, " already encrypted: "
468 + buf);
469 }
470 handler.scheduleFilterWrite(nextFilter,
471 writeRequest);
472 } else if (handler.isHandshakeComplete()) {
473
474 if (SessionLog.isDebugEnabled(session)) {
475 SessionLog.debug(session, " encrypt: " + buf);
476 }
477
478 int pos = buf.position();
479 handler.encrypt(buf.buf());
480 buf.position(pos);
481 ByteBuffer encryptedBuffer = new EncryptedBuffer(SSLHandler
482 .copy(handler.getOutNetBuffer()), buf);
483
484 if (SessionLog.isDebugEnabled(session)) {
485 SessionLog.debug(session, " encrypted buf: "
486 + encryptedBuffer);
487 }
488 handler.scheduleFilterWrite(nextFilter,
489 new WriteRequest(encryptedBuffer, writeRequest
490 .getFuture()));
491 } else {
492 if (!session.isConnected()) {
493 if (SessionLog.isDebugEnabled(session)) {
494 SessionLog.debug(session,
495 " Write request on closed session.");
496 }
497 } else {
498 if (SessionLog.isDebugEnabled(session)) {
499 SessionLog
500 .debug(session,
501 " Handshaking is not complete yet. Buffering write request.");
502 }
503 handler.schedulePreHandshakeWriteRequest(nextFilter,
504 writeRequest);
505 }
506 needsFlush = false;
507 }
508 }
509 }
510
511 if (needsFlush) {
512 handler.flushScheduledEvents();
513 }
514 }
515
516 public void filterClose(final NextFilter nextFilter, final IoSession session)
517 throws SSLException {
518 SSLHandler handler = getSSLSessionHandler0(session);
519 if (handler == null) {
520
521
522 nextFilter.filterClose(session);
523 return;
524 }
525
526 WriteFuture future = null;
527 try {
528 synchronized (handler) {
529 if (isSSLStarted(session)) {
530 future = initiateClosure(nextFilter, session);
531 future.addListener(new IoFutureListener() {
532 public void operationComplete(IoFuture future) {
533 nextFilter.filterClose(session);
534 }
535 });
536 }
537 }
538
539 handler.flushScheduledEvents();
540 } finally {
541 if (future == null) {
542 nextFilter.filterClose(session);
543 }
544 }
545 }
546
547 private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session)
548 throws SSLException {
549 SSLHandler handler = getSSLSessionHandler(session);
550
551 if (!handler.closeOutbound()) {
552 return DefaultWriteFuture.newNotWrittenFuture(session);
553 }
554
555
556 WriteFuture future = handler.writeNetBuffer(nextFilter);
557
558 if (handler.isInboundDone()) {
559 handler.destroy();
560 }
561
562 if (session.containsAttribute(USE_NOTIFICATION)) {
563 handler.scheduleMessageReceived(nextFilter, SESSION_UNSECURED);
564 }
565
566 return future;
567 }
568
569
570
571 private void handleSSLData(NextFilter nextFilter, SSLHandler handler)
572 throws SSLException {
573
574 if (handler.isHandshakeComplete()) {
575 handler.flushPreHandshakeEvents();
576 }
577
578
579 handler.writeNetBuffer(nextFilter);
580
581
582 handleAppDataRead(nextFilter, handler);
583 }
584
585 private void handleAppDataRead(NextFilter nextFilter, SSLHandler handler) {
586 IoSession session = handler.getSession();
587 handler.getAppBuffer().flip();
588 if (!handler.getAppBuffer().hasRemaining()) {
589 handler.getAppBuffer().clear();
590 return;
591 }
592
593 if (SessionLog.isDebugEnabled(session)) {
594 SessionLog.debug(session, " appBuffer: " + handler.getAppBuffer());
595 }
596
597
598 ByteBuffer readBuffer = SSLHandler.copy(handler.getAppBuffer());
599 handler.getAppBuffer().clear();
600 if (SessionLog.isDebugEnabled(session)) {
601 SessionLog.debug(session, " app data read: " + readBuffer + " ("
602 + readBuffer.getHexDump() + ')');
603 }
604
605 handler.scheduleMessageReceived(nextFilter, readBuffer);
606 }
607
608 private SSLHandler getSSLSessionHandler(IoSession session) {
609 SSLHandler handler = getSSLSessionHandler0(session);
610 if (handler == null) {
611 throw new IllegalStateException();
612 }
613 if (handler.getParent() != this) {
614 throw new IllegalArgumentException("Not managed by this filter.");
615 }
616 return handler;
617 }
618
619 private SSLHandler getSSLSessionHandler0(IoSession session) {
620 return (SSLHandler) session.getAttribute(SSL_HANDLER);
621 }
622
623
624
625
626
627
628
629
630 public static class SSLFilterMessage {
631 private final String name;
632
633 private SSLFilterMessage(String name) {
634 this.name = name;
635 }
636
637 public String toString() {
638 return name;
639 }
640 }
641
642 private static class EncryptedBuffer extends ByteBufferProxy {
643 private final ByteBuffer originalBuffer;
644
645 private EncryptedBuffer(ByteBuffer buf, ByteBuffer originalBuffer) {
646 super(buf);
647 this.originalBuffer = originalBuffer;
648 }
649 }
650 }