1 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 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 71 private ByteBuffer inNetBuffer; 72 73 76 private ByteBuffer outNetBuffer; 77 78 81 private ByteBuffer appBuffer; 82 83 86 private final ByteBuffer hsBB = ByteBuffer.allocate(0); 87 88 91 private SSLEngineResult.HandshakeStatus initialHandshakeStatus; 92 93 96 private boolean initialHandshakeComplete; 97 98 private boolean writingEncryptedData; 99 100 106 public SSLHandler(SSLFilter parent, SSLContext sslc, IoSession session) 107 throws SSLException { 108 this.parent = parent; 109 this.session = session; 110 this.ctx = sslc; 111 init(); 112 } 113 114 public void init() throws SSLException { 115 if (sslEngine != null) { 116 return; 117 } 118 119 sslEngine = ctx.createSSLEngine(); 120 sslEngine.setUseClientMode(parent.isUseClientMode()); 121 122 if (parent.isWantClientAuth()) { 123 sslEngine.setWantClientAuth(true); 124 } 125 126 if (parent.isNeedClientAuth()) { 127 sslEngine.setNeedClientAuth(true); 128 } 129 130 if (parent.getEnabledCipherSuites() != null) { 131 sslEngine.setEnabledCipherSuites(parent.getEnabledCipherSuites()); 132 } 133 134 if (parent.getEnabledProtocols() != null) { 135 sslEngine.setEnabledProtocols(parent.getEnabledProtocols()); 136 } 137 138 sslEngine.beginHandshake(); 139 initialHandshakeStatus = sslEngine.getHandshakeStatus(); initialHandshakeComplete = false; 141 142 SSLByteBufferPool.initiate(sslEngine); 143 144 appBuffer = SSLByteBufferPool.getApplicationBuffer(); 145 146 inNetBuffer = SSLByteBufferPool.getPacketBuffer(); 147 outNetBuffer = SSLByteBufferPool.getPacketBuffer(); 148 outNetBuffer.position(0); 149 outNetBuffer.limit(0); 150 151 writingEncryptedData = false; 152 } 153 154 157 public void destroy() { 158 if (sslEngine == null) { 159 return; 160 } 161 162 try { 164 sslEngine.closeInbound(); 165 } catch (SSLException e) { 166 SessionLog.debug(session, 167 "Unexpected exception from SSLEngine.closeInbound().", e); 168 } 169 170 try { 171 do { 172 outNetBuffer.clear(); 173 } while (sslEngine.wrap(hsBB, outNetBuffer).bytesProduced() > 0); 174 } catch (SSLException e) { 175 SessionLog.debug(session, 176 "Unexpected exception from SSLEngine.wrap().", e); 177 } 178 sslEngine.closeOutbound(); 179 sslEngine = null; 180 181 SSLByteBufferPool.release(appBuffer); 182 SSLByteBufferPool.release(inNetBuffer); 183 SSLByteBufferPool.release(outNetBuffer); 184 preHandshakeEventQueue.clear(); 185 } 187 188 public SSLFilter getParent() { 189 return parent; 190 } 191 192 public IoSession getSession() { 193 return session; 194 } 195 196 199 public boolean isWritingEncryptedData() { 200 return writingEncryptedData; 201 } 202 203 206 public boolean isInitialHandshakeComplete() { 207 return initialHandshakeComplete; 208 } 209 210 public boolean isInboundDone() { 211 return sslEngine == null || sslEngine.isInboundDone(); 212 } 213 214 public boolean isOutboundDone() { 215 return sslEngine == null || sslEngine.isOutboundDone(); 216 } 217 218 221 public boolean needToCompleteInitialHandshake() { 222 return (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone()); 223 } 224 225 public void schedulePreHandshakeWriteRequest(NextFilter nextFilter, 226 WriteRequest writeRequest) { 227 preHandshakeEventQueue.offer(new Event(EventType.FILTER_WRITE, 228 nextFilter, writeRequest)); 229 } 230 231 public void flushPreHandshakeEvents() throws SSLException { 232 Event scheduledWrite; 233 234 while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) { 235 if (SessionLog.isDebugEnabled(session)) { 236 SessionLog.debug(session, " Flushing buffered write request: " 237 + scheduledWrite.data); 238 } 239 parent.filterWrite(scheduledWrite.nextFilter, session, 240 (WriteRequest) scheduledWrite.data); 241 } 242 } 243 244 public void scheduleFilterWrite(NextFilter nextFilter, 245 WriteRequest writeRequest) { 246 filterWriteEventQueue.offer(new Event(EventType.FILTER_WRITE, 247 nextFilter, writeRequest)); 248 } 249 250 public void scheduleMessageReceived(NextFilter nextFilter, 251 Object message) { 252 messageReceivedEventQueue.offer(new Event(EventType.RECEIVED, nextFilter, 253 message)); 254 } 255 256 public void flushScheduledEvents() { 257 if (Thread.holdsLock(this)) { 259 return; 260 } 261 262 Event e; 263 264 synchronized (this) { 267 while ((e = filterWriteEventQueue.poll()) != null) { 268 e.nextFilter.filterWrite(session, (WriteRequest) e.data); 269 } 270 } 271 272 while ((e = messageReceivedEventQueue.poll()) != null) { 273 e.nextFilter.messageReceived(session, e.data); 274 } 275 } 276 277 285 public void messageReceived(NextFilter nextFilter, ByteBuffer buf) 286 throws SSLException { 287 if (buf.limit() > inNetBuffer.remaining()) { 288 inNetBuffer = SSLByteBufferPool.expandBuffer(inNetBuffer, 290 inNetBuffer.capacity() + (buf.limit() * 2)); 291 appBuffer = SSLByteBufferPool.expandBuffer(appBuffer, inNetBuffer 293 .capacity() * 2); 294 appBuffer.position(0); 295 appBuffer.limit(0); 296 if (SessionLog.isDebugEnabled(session)) { 297 SessionLog.debug(session, " expanded inNetBuffer:" 298 + inNetBuffer); 299 SessionLog.debug(session, " expanded appBuffer:" + appBuffer); 300 } 301 } 302 303 inNetBuffer.put(buf); 305 if (!initialHandshakeComplete) { 306 handshake(nextFilter); 307 } else { 308 decrypt(); 309 } 310 311 if (isInboundDone()) { 312 buf.position(buf.position() - inNetBuffer.position()); 314 inNetBuffer.clear(); 315 } 316 } 317 318 323 public ByteBuffer getAppBuffer() { 324 return appBuffer; 325 } 326 327 332 public ByteBuffer getOutNetBuffer() { 333 return outNetBuffer; 334 } 335 336 342 public void encrypt(ByteBuffer src) throws SSLException { 343 if (!initialHandshakeComplete) { 344 throw new IllegalStateException (); 345 } 346 347 outNetBuffer.clear(); 350 351 while (src.hasRemaining()) { 353 354 if (src.remaining() > ((outNetBuffer.capacity() - outNetBuffer 355 .position()) / 2)) { 356 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 394 public boolean closeOutbound() throws SSLException { 395 if (sslEngine == null || sslEngine.isOutboundDone()) { 396 return false; 397 } 398 399 sslEngine.closeOutbound(); 400 401 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 417 private void decrypt() throws SSLException { 418 419 if (!initialHandshakeComplete) { 420 throw new IllegalStateException (); 421 } 422 423 if (appBuffer.hasRemaining()) { 424 if (SessionLog.isDebugEnabled(session)) { 425 SessionLog.debug(session, " Error: appBuffer not empty!"); 426 } 427 throw new IllegalStateException (); 429 } 430 431 unwrap(); 432 } 433 434 438 private SSLEngineResult.Status checkStatus(SSLEngineResult.Status status) 439 throws SSLException { 440 if (status != SSLEngineResult.Status.OK 441 && status != SSLEngineResult.Status.CLOSED 442 && status != SSLEngineResult.Status.BUFFER_UNDERFLOW) { 443 throw new SSLException("SSLEngine error during decrypt: " + status 444 + " inNetBuffer: " + inNetBuffer + "appBuffer: " 445 + appBuffer); 446 } 447 448 return status; 449 } 450 451 454 public void handshake(NextFilter nextFilter) throws SSLException { 455 if (SessionLog.isDebugEnabled(session)) { 456 SessionLog.debug(session, " doHandshake()"); 457 } 458 459 while (!initialHandshakeComplete) { 460 if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED) { 461 session.setAttribute(SSLFilter.SSL_SESSION, sslEngine 462 .getSession()); 463 if (SessionLog.isDebugEnabled(session)) { 464 SSLSession sslSession = sslEngine.getSession(); 465 SessionLog.debug(session, 466 " initialHandshakeStatus=FINISHED"); 467 SessionLog.debug(session, " sslSession CipherSuite used " 468 + sslSession.getCipherSuite()); 469 } 470 initialHandshakeComplete = true; 471 if (session.containsAttribute(SSLFilter.USE_NOTIFICATION)) { 472 scheduleMessageReceived(nextFilter, 473 SSLFilter.SESSION_SECURED); 474 } 475 break; 476 } else if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) { 477 if (SessionLog.isDebugEnabled(session)) { 478 SessionLog.debug(session, 479 " initialHandshakeStatus=NEED_TASK"); 480 } 481 initialHandshakeStatus = doTasks(); 482 } else if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) { 483 if (SessionLog.isDebugEnabled(session)) { 485 SessionLog.debug(session, 486 " initialHandshakeStatus=NEED_UNWRAP"); 487 } 488 SSLEngineResult.Status status = unwrapHandshake(); 489 if ((initialHandshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED && status == SSLEngineResult.Status.BUFFER_UNDERFLOW) 490 || isInboundDone()) { 491 break; 493 } 494 } else if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) { 495 if (SessionLog.isDebugEnabled(session)) { 496 SessionLog.debug(session, 497 " initialHandshakeStatus=NEED_WRAP"); 498 } 499 if (outNetBuffer.hasRemaining()) { 502 if (SessionLog.isDebugEnabled(session)) { 503 SessionLog 504 .debug(session, " Still data in out buffer!"); 505 } 506 break; 507 } 508 outNetBuffer.clear(); 509 SSLEngineResult result = sslEngine.wrap(hsBB, outNetBuffer); 510 if (SessionLog.isDebugEnabled(session)) { 511 SessionLog.debug(session, " Wrap res:" + result); 512 } 513 514 outNetBuffer.flip(); 515 initialHandshakeStatus = result.getHandshakeStatus(); 516 writeNetBuffer(nextFilter); 517 } else { 518 throw new IllegalStateException ("Invalid Handshaking State" 519 + initialHandshakeStatus); 520 } 521 } 522 } 523 524 public WriteFuture writeNetBuffer(NextFilter nextFilter) 525 throws SSLException { 526 if (!getOutNetBuffer().hasRemaining()) { 528 return DefaultWriteFuture.newNotWrittenFuture(session); 530 } 531 532 writingEncryptedData = true; 535 536 WriteFuture writeFuture = null; 538 539 try { 540 if (SessionLog.isDebugEnabled(session)) { 541 SessionLog.debug(session, " write outNetBuffer: " 542 + getOutNetBuffer()); 543 } 544 org.apache.mina.common.ByteBuffer writeBuffer = copy(getOutNetBuffer()); 545 if (SessionLog.isDebugEnabled(session)) { 546 SessionLog.debug(session, " session write: " + writeBuffer); 547 } 548 550 writeFuture = new DefaultWriteFuture(session); 551 parent.filterWrite(nextFilter, session, new WriteRequest( 552 writeBuffer, writeFuture)); 553 554 while (needToCompleteInitialHandshake()) { 556 try { 557 handshake(nextFilter); 558 } catch (SSLException ssle) { 559 SSLException newSSLE = new SSLHandshakeException( 560 "Initial SSL handshake failed."); 561 newSSLE.initCause(ssle); 562 throw newSSLE; 563 } 564 if (getOutNetBuffer().hasRemaining()) { 565 if (SessionLog.isDebugEnabled(session)) { 566 SessionLog.debug(session, " write outNetBuffer2: " 567 + getOutNetBuffer()); 568 } 569 org.apache.mina.common.ByteBuffer writeBuffer2 = copy(getOutNetBuffer()); 570 writeFuture = new DefaultWriteFuture(session); 571 parent.filterWrite(nextFilter, session, new WriteRequest( 572 writeBuffer2, writeFuture)); 573 } 574 } 575 } finally { 576 writingEncryptedData = false; 577 } 578 579 return writeFuture; 580 } 581 582 private SSLEngineResult.Status unwrap() throws SSLException { 583 if (SessionLog.isDebugEnabled(session)) { 584 SessionLog.debug(session, " unwrap()"); 585 } 586 appBuffer.clear(); 588 589 inNetBuffer.flip(); 591 592 SSLEngineResult res; 593 do { 594 if (SessionLog.isDebugEnabled(session)) { 595 SessionLog.debug(session, " inNetBuffer: " + inNetBuffer); 596 SessionLog.debug(session, " appBuffer: " + appBuffer); 597 } 598 res = sslEngine.unwrap(inNetBuffer, appBuffer); 599 if (SessionLog.isDebugEnabled(session)) { 600 SessionLog.debug(session, " Unwrap res:" + res); 601 } 602 } while (res.getStatus() == SSLEngineResult.Status.OK); 603 604 inNetBuffer.compact(); 606 appBuffer.flip(); 608 609 617 return checkStatus(res.getStatus()); 618 } 619 620 private SSLEngineResult.Status unwrapHandshake() throws SSLException { 621 if (SessionLog.isDebugEnabled(session)) { 622 SessionLog.debug(session, " unwrapHandshake()"); 623 } 624 appBuffer.clear(); 626 627 inNetBuffer.flip(); 629 630 SSLEngineResult res; 631 do { 632 if (SessionLog.isDebugEnabled(session)) { 633 SessionLog.debug(session, " inNetBuffer: " + inNetBuffer); 634 SessionLog.debug(session, " appBuffer: " + appBuffer); 635 } 636 res = sslEngine.unwrap(inNetBuffer, appBuffer); 637 if (SessionLog.isDebugEnabled(session)) { 638 SessionLog.debug(session, " Unwrap res:" + res); 639 } 640 641 } while (res.getStatus() == SSLEngineResult.Status.OK 642 && res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP); 643 644 initialHandshakeStatus = res.getHandshakeStatus(); 645 646 if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED 649 && appBuffer.position() == 0 650 && res.getStatus() == SSLEngineResult.Status.OK 651 && inNetBuffer.hasRemaining()) { 652 do { 653 if (SessionLog.isDebugEnabled(session)) { 654 SessionLog.debug(session, " extra handshake unwrap"); 655 SessionLog.debug(session, " inNetBuffer: " + inNetBuffer); 656 SessionLog.debug(session, " appBuffer: " + appBuffer); 657 } 658 res = sslEngine.unwrap(inNetBuffer, appBuffer); 659 if (SessionLog.isDebugEnabled(session)) { 660 SessionLog.debug(session, " Unwrap res:" + res); 661 } 662 } while (res.getStatus() == SSLEngineResult.Status.OK); 663 } 664 665 inNetBuffer.compact(); 667 668 appBuffer.flip(); 670 671 679 return checkStatus(res.getStatus()); 681 } 682 683 686 private SSLEngineResult.HandshakeStatus doTasks() { 687 if (SessionLog.isDebugEnabled(session)) { 688 SessionLog.debug(session, " doTasks()"); 689 } 690 691 695 Runnable runnable; 696 while ((runnable = sslEngine.getDelegatedTask()) != null) { 697 if (SessionLog.isDebugEnabled(session)) { 698 SessionLog.debug(session, " doTask: " + runnable); 699 } 700 runnable.run(); 701 } 702 if (SessionLog.isDebugEnabled(session)) { 703 SessionLog.debug(session, " doTasks(): " 704 + sslEngine.getHandshakeStatus()); 705 } 706 return sslEngine.getHandshakeStatus(); 707 } 708 709 716 public static org.apache.mina.common.ByteBuffer copy(java.nio.ByteBuffer src) { 717 org.apache.mina.common.ByteBuffer copy = org.apache.mina.common.ByteBuffer 718 .allocate(src.remaining()); 719 copy.put(src); 720 copy.flip(); 721 return copy; 722 } 723 724 private static class EventType { 725 public static final EventType RECEIVED = new EventType("RECEIVED"); 726 727 public static final EventType FILTER_WRITE = new EventType( 728 "FILTER_WRITE"); 729 730 private final String value; 731 732 private EventType(String value) { 733 this.value = value; 734 } 735 736 public String toString() { 737 return value; 738 } 739 } 740 741 private static class Event { 742 private final EventType type; 743 744 private final NextFilter nextFilter; 745 746 private final Object data; 747 748 Event(EventType type, NextFilter nextFilter, Object data) { 749 this.type = type; 750 this.nextFilter = nextFilter; 751 this.data = data; 752 } 753 754 public Object getData() { 755 return data; 756 } 757 758 public NextFilter getNextFilter() { 759 return nextFilter; 760 } 761 762 public EventType getType() { 763 return type; 764 } 765 } 766 } 767 | Popular Tags |