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