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