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.support;
21
22 import java.nio.ByteBuffer;
23 import java.util.LinkedList;
24 import java.util.Queue;
25 import java.util.concurrent.ConcurrentLinkedQueue;
26
27 import javax.net.ssl.SSLContext;
28 import javax.net.ssl.SSLEngine;
29 import javax.net.ssl.SSLEngineResult;
30 import javax.net.ssl.SSLException;
31 import javax.net.ssl.SSLHandshakeException;
32 import javax.net.ssl.SSLSession;
33
34 import org.apache.mina.common.IoSession;
35 import org.apache.mina.common.WriteFuture;
36 import org.apache.mina.common.IoFilter.NextFilter;
37 import org.apache.mina.common.IoFilter.WriteRequest;
38 import org.apache.mina.common.support.DefaultWriteFuture;
39 import org.apache.mina.filter.SSLFilter;
40 import org.apache.mina.util.SessionLog;
41
42
43
44
45
46
47
48
49
50
51
52
53 public class SSLHandler {
54 private final SSLFilter parent;
55
56 private final SSLContext ctx;
57
58 private final IoSession session;
59
60 private final Queue<Event> preHandshakeEventQueue = new LinkedList<Event>();
61
62 private final Queue<Event> filterWriteEventQueue = new ConcurrentLinkedQueue<Event>();
63
64 private final Queue<Event> messageReceivedEventQueue = new ConcurrentLinkedQueue<Event>();
65
66 private SSLEngine sslEngine;
67
68
69
70
71 private ByteBuffer inNetBuffer;
72
73
74
75
76 private ByteBuffer outNetBuffer;
77
78
79
80
81 private ByteBuffer appBuffer;
82
83
84
85
86 private final ByteBuffer hsBB = ByteBuffer.allocate(0);
87
88
89
90
91 private SSLEngineResult.HandshakeStatus handshakeStatus;
92
93 private boolean initialHandshakeComplete;
94
95
96
97
98 private boolean handshakeComplete;
99
100 private boolean writingEncryptedData;
101
102
103
104
105
106
107
108 public SSLHandler(SSLFilter parent, SSLContext sslc, IoSession session)
109 throws SSLException {
110 this.parent = parent;
111 this.session = session;
112 this.ctx = sslc;
113 init();
114 }
115
116 public void init() throws SSLException {
117 if (sslEngine != null) {
118 return;
119 }
120
121 sslEngine = ctx.createSSLEngine();
122 sslEngine.setUseClientMode(parent.isUseClientMode());
123
124 if (parent.isWantClientAuth()) {
125 sslEngine.setWantClientAuth(true);
126 }
127
128 if (parent.isNeedClientAuth()) {
129 sslEngine.setNeedClientAuth(true);
130 }
131
132 if (parent.getEnabledCipherSuites() != null) {
133 sslEngine.setEnabledCipherSuites(parent.getEnabledCipherSuites());
134 }
135
136 if (parent.getEnabledProtocols() != null) {
137 sslEngine.setEnabledProtocols(parent.getEnabledProtocols());
138 }
139
140 sslEngine.beginHandshake();
141 handshakeStatus = sslEngine.getHandshakeStatus();
142 handshakeComplete = false;
143 initialHandshakeComplete = false;
144
145 SSLByteBufferPool.initiate(sslEngine);
146
147 appBuffer = SSLByteBufferPool.getApplicationBuffer();
148
149 inNetBuffer = SSLByteBufferPool.getPacketBuffer();
150 outNetBuffer = SSLByteBufferPool.getPacketBuffer();
151 outNetBuffer.position(0);
152 outNetBuffer.limit(0);
153
154 writingEncryptedData = false;
155 }
156
157
158
159
160 public void destroy() {
161 if (sslEngine == null) {
162 return;
163 }
164
165
166 try {
167 sslEngine.closeInbound();
168 } catch (SSLException e) {
169 SessionLog.debug(session,
170 "Unexpected exception from SSLEngine.closeInbound().", e);
171 }
172
173 try {
174 do {
175 outNetBuffer.clear();
176 } while (sslEngine.wrap(hsBB, outNetBuffer).bytesProduced() > 0);
177 } catch (SSLException e) {
178 SessionLog.debug(session,
179 "Unexpected exception from SSLEngine.wrap().", e);
180 }
181 sslEngine.closeOutbound();
182 sslEngine = null;
183
184 SSLByteBufferPool.release(appBuffer);
185 SSLByteBufferPool.release(inNetBuffer);
186 SSLByteBufferPool.release(outNetBuffer);
187 preHandshakeEventQueue.clear();
188 }
189
190 public SSLFilter getParent() {
191 return parent;
192 }
193
194 public IoSession getSession() {
195 return session;
196 }
197
198
199
200
201 public boolean isWritingEncryptedData() {
202 return writingEncryptedData;
203 }
204
205
206
207
208 public boolean isHandshakeComplete() {
209 return handshakeComplete;
210 }
211
212 public boolean isInboundDone() {
213 return sslEngine == null || sslEngine.isInboundDone();
214 }
215
216 public boolean isOutboundDone() {
217 return sslEngine == null || sslEngine.isOutboundDone();
218 }
219
220
221
222
223 public boolean needToCompleteHandshake() {
224 return (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone());
225 }
226
227 public void schedulePreHandshakeWriteRequest(NextFilter nextFilter,
228 WriteRequest writeRequest) {
229 preHandshakeEventQueue.offer(new Event(EventType.FILTER_WRITE,
230 nextFilter, writeRequest));
231 }
232
233 public void flushPreHandshakeEvents() throws SSLException {
234 Event scheduledWrite;
235
236 while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
237 if (SessionLog.isDebugEnabled(session)) {
238 SessionLog.debug(session, " Flushing buffered write request: "
239 + scheduledWrite.data);
240 }
241 parent.filterWrite(scheduledWrite.nextFilter, session,
242 (WriteRequest) scheduledWrite.data);
243 }
244 }
245
246 public void scheduleFilterWrite(NextFilter nextFilter,
247 WriteRequest writeRequest) {
248 filterWriteEventQueue.offer(new Event(EventType.FILTER_WRITE,
249 nextFilter, writeRequest));
250 }
251
252 public void scheduleMessageReceived(NextFilter nextFilter,
253 Object message) {
254 messageReceivedEventQueue.offer(new Event(EventType.RECEIVED, nextFilter,
255 message));
256 }
257
258 public void flushScheduledEvents() {
259
260 if (Thread.holdsLock(this)) {
261 return;
262 }
263
264 Event e;
265
266
267
268 synchronized (this) {
269 while ((e = filterWriteEventQueue.poll()) != null) {
270 e.nextFilter.filterWrite(session, (WriteRequest) e.data);
271 }
272 }
273
274 while ((e = messageReceivedEventQueue.poll()) != null) {
275 e.nextFilter.messageReceived(session, e.data);
276 }
277 }
278
279
280
281
282
283
284
285
286
287 public void messageReceived(NextFilter nextFilter, ByteBuffer buf)
288 throws SSLException {
289 if (buf.limit() > inNetBuffer.remaining()) {
290
291 inNetBuffer = SSLByteBufferPool.expandBuffer(inNetBuffer,
292 inNetBuffer.capacity() + (buf.limit() * 2));
293
294 appBuffer = SSLByteBufferPool.expandBuffer(appBuffer, inNetBuffer
295 .capacity() * 2);
296 if (SessionLog.isDebugEnabled(session)) {
297 SessionLog.debug(session, " expanded inNetBuffer:"
298 + inNetBuffer);
299 SessionLog.debug(session, " expanded appBuffer:" + appBuffer);
300 }
301 }
302
303
304 inNetBuffer.put(buf);
305 if (!handshakeComplete) {
306 handshake(nextFilter);
307 } else {
308 decrypt(nextFilter);
309 }
310
311 if (isInboundDone()) {
312
313 buf.position(buf.position() - inNetBuffer.position());
314 inNetBuffer.clear();
315 }
316 }
317
318
319
320
321
322
323 public ByteBuffer getAppBuffer() {
324 return appBuffer;
325 }
326
327
328
329
330
331
332 public ByteBuffer getOutNetBuffer() {
333 return outNetBuffer;
334 }
335
336
337
338
339
340
341
342 public void encrypt(ByteBuffer src) throws SSLException {
343 if (!handshakeComplete) {
344 throw new IllegalStateException();
345 }
346
347
348
349 outNetBuffer.clear();
350
351
352 while (src.hasRemaining()) {
353
354 if (src.remaining() > ((outNetBuffer.capacity() - outNetBuffer
355 .position()) / 2)) {
356
357
358
359 outNetBuffer = SSLByteBufferPool.expandBuffer(outNetBuffer, src
360 .capacity() * 2);
361 if (SessionLog.isDebugEnabled(session)) {
362 SessionLog.debug(session, " expanded outNetBuffer:"
363 + outNetBuffer);
364 }
365 }
366
367 SSLEngineResult result = sslEngine.wrap(src, outNetBuffer);
368 if (SessionLog.isDebugEnabled(session)) {
369 SessionLog.debug(session, " Wrap res:" + result);
370 }
371
372 if (result.getStatus() == SSLEngineResult.Status.OK) {
373 if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
374 doTasks();
375 }
376 } else {
377 throw new SSLException("SSLEngine error during encrypt: "
378 + result.getStatus() + " src: " + src
379 + "outNetBuffer: " + outNetBuffer);
380 }
381 }
382
383 outNetBuffer.flip();
384 }
385
386
387
388
389
390
391
392
393
394 public boolean closeOutbound() throws SSLException {
395 if (sslEngine == null || sslEngine.isOutboundDone()) {
396 return false;
397 }
398
399 sslEngine.closeOutbound();
400
401
402
403 outNetBuffer.clear();
404 SSLEngineResult result = sslEngine.wrap(hsBB, outNetBuffer);
405 if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
406 throw new SSLException("Improper close state: " + result);
407 }
408 outNetBuffer.flip();
409 return true;
410 }
411
412
413
414
415
416
417 private void decrypt(NextFilter nextFilter) throws SSLException {
418
419 if (!handshakeComplete) {
420 throw new IllegalStateException();
421 }
422
423 unwrap(nextFilter);
424 }
425
426
427
428
429
430 private void checkStatus(SSLEngineResult res)
431 throws SSLException {
432
433 SSLEngineResult.Status status = res.getStatus();
434
435
436
437
438
439
440
441
442
443 if (status != SSLEngineResult.Status.OK
444 && status != SSLEngineResult.Status.CLOSED
445 && status != SSLEngineResult.Status.BUFFER_UNDERFLOW) {
446 throw new SSLException("SSLEngine error during decrypt: " + status
447 + " inNetBuffer: " + inNetBuffer + "appBuffer: "
448 + appBuffer);
449 }
450 }
451
452
453
454
455 public void handshake(NextFilter nextFilter) throws SSLException {
456 if (SessionLog.isDebugEnabled(session)) {
457 SessionLog.debug(session, " doHandshake()");
458 }
459
460 for (;;) {
461 if (handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED) {
462 session.setAttribute(SSLFilter.SSL_SESSION, sslEngine
463 .getSession());
464 if (SessionLog.isDebugEnabled(session)) {
465 SSLSession sslSession = sslEngine.getSession();
466 SessionLog.debug(session,
467 " handshakeStatus=FINISHED");
468 SessionLog.debug(session, " sslSession CipherSuite used "
469 + sslSession.getCipherSuite());
470 }
471 handshakeComplete = true;
472 if (!initialHandshakeComplete
473 && session.containsAttribute(SSLFilter.USE_NOTIFICATION)) {
474
475
476 initialHandshakeComplete = true;
477 scheduleMessageReceived(nextFilter,
478 SSLFilter.SESSION_SECURED);
479 }
480 break;
481 } else if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
482 if (SessionLog.isDebugEnabled(session)) {
483 SessionLog.debug(session,
484 " handshakeStatus=NEED_TASK");
485 }
486 handshakeStatus = doTasks();
487 } else if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
488
489 if (SessionLog.isDebugEnabled(session)) {
490 SessionLog.debug(session,
491 " handshakeStatus=NEED_UNWRAP");
492 }
493 SSLEngineResult.Status status = unwrapHandshake(nextFilter);
494 if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
495 || isInboundDone()) {
496
497 break;
498 }
499 } else if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
500 if (SessionLog.isDebugEnabled(session)) {
501 SessionLog.debug(session,
502 " handshakeStatus=NEED_WRAP");
503 }
504
505
506 if (outNetBuffer.hasRemaining()) {
507 if (SessionLog.isDebugEnabled(session)) {
508 SessionLog
509 .debug(session, " Still data in out buffer!");
510 }
511 break;
512 }
513 outNetBuffer.clear();
514 SSLEngineResult result = sslEngine.wrap(hsBB, outNetBuffer);
515 if (SessionLog.isDebugEnabled(session)) {
516 SessionLog.debug(session, " Wrap res:" + result);
517 }
518
519 outNetBuffer.flip();
520 handshakeStatus = result.getHandshakeStatus();
521 writeNetBuffer(nextFilter);
522 } else {
523 throw new IllegalStateException("Invalid Handshaking State"
524 + handshakeStatus);
525 }
526 }
527 }
528
529 public WriteFuture writeNetBuffer(NextFilter nextFilter)
530 throws SSLException {
531
532 if (!getOutNetBuffer().hasRemaining()) {
533
534 return DefaultWriteFuture.newNotWrittenFuture(session);
535 }
536
537
538
539 writingEncryptedData = true;
540
541
542 WriteFuture writeFuture = null;
543
544 try {
545 if (SessionLog.isDebugEnabled(session)) {
546 SessionLog.debug(session, " write outNetBuffer: "
547 + getOutNetBuffer());
548 }
549 org.apache.mina.common.ByteBuffer writeBuffer = copy(getOutNetBuffer());
550 if (SessionLog.isDebugEnabled(session)) {
551 SessionLog.debug(session, " session write: " + writeBuffer);
552 }
553
554
555 writeFuture = new DefaultWriteFuture(session);
556 parent.filterWrite(nextFilter, session, new WriteRequest(
557 writeBuffer, writeFuture));
558
559
560 while (needToCompleteHandshake()) {
561 try {
562 handshake(nextFilter);
563 } catch (SSLException ssle) {
564 SSLException newSSLE = new SSLHandshakeException(
565 "SSL handshake failed.");
566 newSSLE.initCause(ssle);
567 throw newSSLE;
568 }
569 if (getOutNetBuffer().hasRemaining()) {
570 if (SessionLog.isDebugEnabled(session)) {
571 SessionLog.debug(session, " write outNetBuffer2: "
572 + getOutNetBuffer());
573 }
574 org.apache.mina.common.ByteBuffer writeBuffer2 = copy(getOutNetBuffer());
575 writeFuture = new DefaultWriteFuture(session);
576 parent.filterWrite(nextFilter, session, new WriteRequest(
577 writeBuffer2, writeFuture));
578 }
579 }
580 } finally {
581 writingEncryptedData = false;
582 }
583
584 return writeFuture;
585 }
586
587 private void unwrap(NextFilter nextFilter) throws SSLException {
588 if (SessionLog.isDebugEnabled(session)) {
589 SessionLog.debug(session, " unwrap()");
590 }
591
592
593 inNetBuffer.flip();
594
595 SSLEngineResult res = unwrap0();
596
597
598 inNetBuffer.compact();
599
600 checkStatus(res);
601
602 renegotiateIfNeeded(nextFilter, res);
603 }
604
605 private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
606 if (SessionLog.isDebugEnabled(session)) {
607 SessionLog.debug(session, " unwrapHandshake()");
608 }
609
610
611 inNetBuffer.flip();
612
613 SSLEngineResult res = unwrap0();
614 handshakeStatus = res.getHandshakeStatus();
615
616 checkStatus(res);
617
618
619
620 if (handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED
621 && res.getStatus() == SSLEngineResult.Status.OK
622 && inNetBuffer.hasRemaining()) {
623 res = unwrap0();
624
625
626 inNetBuffer.compact();
627
628 renegotiateIfNeeded(nextFilter, res);
629 } else {
630
631 inNetBuffer.compact();
632 }
633
634 return res.getStatus();
635 }
636
637 private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res)
638 throws SSLException {
639 if (res.getStatus() != SSLEngineResult.Status.CLOSED
640 && res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW
641 && res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
642
643 SessionLog.debug(session, " Renegotiating...");
644 handshakeComplete = false;
645 handshakeStatus = res.getHandshakeStatus();
646 handshake(nextFilter);
647 }
648 }
649
650 private SSLEngineResult unwrap0() throws SSLException {
651 SSLEngineResult res;
652 do {
653 if (SessionLog.isDebugEnabled(session)) {
654 SessionLog.debug(session, " inNetBuffer: " + inNetBuffer);
655 SessionLog.debug(session, " appBuffer: " + appBuffer);
656 }
657 res = sslEngine.unwrap(inNetBuffer, appBuffer);
658 if (SessionLog.isDebugEnabled(session)) {
659 SessionLog.debug(session, " Unwrap res:" + res);
660 }
661 } while (res.getStatus() == SSLEngineResult.Status.OK
662 && (handshakeComplete && res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING
663 || res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP));
664
665 return res;
666 }
667
668
669
670
671 private SSLEngineResult.HandshakeStatus doTasks() {
672 if (SessionLog.isDebugEnabled(session)) {
673 SessionLog.debug(session, " doTasks()");
674 }
675
676
677
678
679
680 Runnable runnable;
681 while ((runnable = sslEngine.getDelegatedTask()) != null) {
682 if (SessionLog.isDebugEnabled(session)) {
683 SessionLog.debug(session, " doTask: " + runnable);
684 }
685 runnable.run();
686 }
687 if (SessionLog.isDebugEnabled(session)) {
688 SessionLog.debug(session, " doTasks(): "
689 + sslEngine.getHandshakeStatus());
690 }
691 return sslEngine.getHandshakeStatus();
692 }
693
694
695
696
697
698
699
700
701 public static org.apache.mina.common.ByteBuffer copy(java.nio.ByteBuffer src) {
702 org.apache.mina.common.ByteBuffer copy = org.apache.mina.common.ByteBuffer
703 .allocate(src.remaining());
704 copy.put(src);
705 copy.flip();
706 return copy;
707 }
708
709 private static class EventType {
710 public static final EventType RECEIVED = new EventType("RECEIVED");
711
712 public static final EventType FILTER_WRITE = new EventType(
713 "FILTER_WRITE");
714
715 private final String value;
716
717 private EventType(String value) {
718 this.value = value;
719 }
720
721 public String toString() {
722 return value;
723 }
724 }
725
726 private static class Event {
727 private final EventType type;
728
729 private final NextFilter nextFilter;
730
731 private final Object data;
732
733 Event(EventType type, NextFilter nextFilter, Object data) {
734 this.type = type;
735 this.nextFilter = nextFilter;
736 this.data = data;
737 }
738
739 public Object getData() {
740 return data;
741 }
742
743 public NextFilter getNextFilter() {
744 return nextFilter;
745 }
746
747 public EventType getType() {
748 return type;
749 }
750 }
751 }