001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.filter.ssl; 021 022import java.net.InetSocketAddress; 023import java.util.ArrayList; 024import java.util.List; 025 026import javax.net.ssl.SSLContext; 027import javax.net.ssl.SSLEngine; 028import javax.net.ssl.SSLException; 029import javax.net.ssl.SSLHandshakeException; 030import javax.net.ssl.SSLSession; 031 032import org.apache.mina.core.buffer.IoBuffer; 033import org.apache.mina.core.filterchain.IoFilterAdapter; 034import org.apache.mina.core.filterchain.IoFilterChain; 035import org.apache.mina.core.future.DefaultWriteFuture; 036import org.apache.mina.core.future.IoFuture; 037import org.apache.mina.core.future.IoFutureListener; 038import org.apache.mina.core.future.WriteFuture; 039import org.apache.mina.core.service.IoAcceptor; 040import org.apache.mina.core.service.IoHandler; 041import org.apache.mina.core.session.AttributeKey; 042import org.apache.mina.core.session.IoSession; 043import org.apache.mina.core.write.WriteRequest; 044import org.apache.mina.core.write.WriteRequestWrapper; 045import org.apache.mina.core.write.WriteToClosedSessionException; 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048 049/** 050 * An SSL filter that encrypts and decrypts the data exchanged in the session. 051 * Adding this filter triggers SSL handshake procedure immediately by sending 052 * a SSL 'hello' message, so you don't need to call 053 * {@link #startSsl(IoSession)} manually unless you are implementing StartTLS 054 * (see below). If you don't want the handshake procedure to start 055 * immediately, please specify {@code false} as {@code autoStart} parameter in 056 * the constructor. 057 * <p> 058 * This filter uses an {@link SSLEngine} which was introduced in Java 5, so 059 * Java version 5 or above is mandatory to use this filter. And please note that 060 * this filter only works for TCP/IP connections. 061 * 062 * <h2>Implementing StartTLS</h2> 063 * <p> 064 * You can use {@link #DISABLE_ENCRYPTION_ONCE} attribute to implement StartTLS: 065 * <pre> 066 * public void messageReceived(IoSession session, Object message) { 067 * if (message instanceof MyStartTLSRequest) { 068 * // Insert SSLFilter to get ready for handshaking 069 * session.getFilterChain().addFirst(sslFilter); 070 * 071 * // Disable encryption temporarilly. 072 * // This attribute will be removed by SSLFilter 073 * // inside the Session.write() call below. 074 * session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE); 075 * 076 * // Write StartTLSResponse which won't be encrypted. 077 * session.write(new MyStartTLSResponse(OK)); 078 * 079 * // Now DISABLE_ENCRYPTION_ONCE attribute is cleared. 080 * assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null; 081 * } 082 * } 083 * </pre> 084 * 085 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 086 * @org.apache.xbean.XBean 087 */ 088public class SslFilter extends IoFilterAdapter { 089 /** The logger */ 090 private static final Logger LOGGER = LoggerFactory.getLogger(SslFilter.class); 091 092 /** 093 * A session attribute key that stores underlying {@link SSLSession} 094 * for each session. 095 */ 096 public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session"); 097 098 /** 099 * A session attribute key that makes next one write request bypass 100 * this filter (not encrypting the data). This is a marker attribute, 101 * which means that you can put whatever as its value. ({@link Boolean#TRUE} 102 * is preferred.) The attribute is automatically removed from the session 103 * attribute map as soon as {@link IoSession#write(Object)} is invoked, 104 * and therefore should be put again if you want to make more messages 105 * bypass this filter. This is especially useful when you implement 106 * StartTLS. 107 */ 108 public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce"); 109 110 /** 111 * A session attribute key that makes this filter to emit a 112 * {@link IoHandler#messageReceived(IoSession, Object)} event with a 113 * special message ({@link #SESSION_SECURED} or {@link #SESSION_UNSECURED}). 114 * This is a marker attribute, which means that you can put whatever as its 115 * value. ({@link Boolean#TRUE} is preferred.) By default, this filter 116 * doesn't emit any events related with SSL session flow control. 117 */ 118 public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification"); 119 120 /** 121 * A session attribute key that should be set to an {@link InetSocketAddress}. 122 * Setting this attribute causes 123 * {@link SSLContext#createSSLEngine(String, int)} to be called passing the 124 * hostname and port of the {@link InetSocketAddress} to get an 125 * {@link SSLEngine} instance. If not set {@link SSLContext#createSSLEngine()} 126 * will be called. 127 * <br> 128 * Using this feature {@link SSLSession} objects may be cached and reused 129 * when in client mode. 130 * 131 * @see SSLContext#createSSLEngine(String, int) 132 */ 133 public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress"); 134 135 /** 136 * A special message object which is emitted with a {@link IoHandler#messageReceived(IoSession, Object)} 137 * event when the session is secured and its {@link #USE_NOTIFICATION} 138 * attribute is set. 139 */ 140 public static final SslFilterMessage SESSION_SECURED = new SslFilterMessage("SESSION_SECURED"); 141 142 /** 143 * A special message object which is emitted with a {@link IoHandler#messageReceived(IoSession, Object)} 144 * event when the session is not secure anymore and its {@link #USE_NOTIFICATION} 145 * attribute is set. 146 */ 147 public static final SslFilterMessage SESSION_UNSECURED = new SslFilterMessage("SESSION_UNSECURED"); 148 149 private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter"); 150 151 private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler"); 152 153 /** The SslContext used */ 154 /* No qualifier */final SSLContext sslContext; 155 156 /** A flag used to tell the filter to start the handshake immediately */ 157 private final boolean autoStart; 158 159 /** A flag used to determinate if the handshake should start immediately */ 160 private static final boolean START_HANDSHAKE = true; 161 162 private boolean client; 163 164 private boolean needClientAuth; 165 166 private boolean wantClientAuth; 167 168 private String[] enabledCipherSuites; 169 170 private String[] enabledProtocols; 171 172 /** 173 * Creates a new SSL filter using the specified {@link SSLContext}. 174 * The handshake will start immediately. 175 * 176 * @param sslContext The SSLContext to use 177 */ 178 public SslFilter(SSLContext sslContext) { 179 this(sslContext, START_HANDSHAKE); 180 } 181 182 /** 183 * Creates a new SSL filter using the specified {@link SSLContext}. 184 * If the <tt>autostart</tt> flag is set to <tt>true</tt>, the 185 * handshake will start immediately. 186 * 187 * @param sslContext The SSLContext to use 188 * @param autoStart The flag used to tell the filter to start the handshake immediately 189 */ 190 public SslFilter(SSLContext sslContext, boolean autoStart) { 191 if (sslContext == null) { 192 throw new IllegalArgumentException("sslContext"); 193 } 194 195 this.sslContext = sslContext; 196 this.autoStart = autoStart; 197 } 198 199 /** 200 * Returns the underlying {@link SSLSession} for the specified session. 201 * 202 * @param session The current session 203 * @return <tt>null</tt> if no {@link SSLSession} is initialized yet. 204 */ 205 public SSLSession getSslSession(IoSession session) { 206 return (SSLSession) session.getAttribute(SSL_SESSION); 207 } 208 209 /** 210 * (Re)starts SSL session for the specified <tt>session</tt> if not started yet. 211 * Please note that SSL session is automatically started by default, and therefore 212 * you don't need to call this method unless you've used TLS closure. 213 * 214 * @param session The session that will be switched to SSL mode 215 * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if already started. 216 * @throws SSLException if failed to start the SSL session 217 */ 218 public boolean startSsl(IoSession session) throws SSLException { 219 SslHandler sslHandler = getSslSessionHandler(session); 220 boolean started; 221 222 try { 223 synchronized (sslHandler) { 224 if (sslHandler.isOutboundDone()) { 225 NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER); 226 sslHandler.destroy(); 227 sslHandler.init(); 228 sslHandler.handshake(nextFilter); 229 started = true; 230 } else { 231 started = false; 232 } 233 } 234 235 sslHandler.flushScheduledEvents(); 236 } catch (SSLException se) { 237 sslHandler.release(); 238 throw se; 239 } 240 241 return started; 242 } 243 244 /** 245 * An extended toString() method for sessions. If the SSL handshake 246 * is not yet completed, we will print (ssl) in small caps. Once it's 247 * completed, we will use SSL capitalized. 248 */ 249 /* no qualifier */String getSessionInfo(IoSession session) { 250 StringBuilder sb = new StringBuilder(); 251 252 if (session.getService() instanceof IoAcceptor) { 253 sb.append("Session Server"); 254 255 } else { 256 sb.append("Session Client"); 257 } 258 259 sb.append('[').append(session.getId()).append(']'); 260 261 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER); 262 263 if (sslHandler == null) { 264 sb.append("(no sslEngine)"); 265 } else if (isSslStarted(session)) { 266 if (sslHandler.isHandshakeComplete()) { 267 sb.append("(SSL)"); 268 } else { 269 sb.append("(ssl...)"); 270 } 271 } 272 273 return sb.toString(); 274 } 275 276 /** 277 * @return <tt>true</tt> if and only if the specified <tt>session</tt> is 278 * encrypted/decrypted over SSL/TLS currently. This method will start 279 * to return <tt>false</tt> after TLS <tt>close_notify</tt> message 280 * is sent and any messages written after then is not going to get encrypted. 281 * 282 * @param session the session we want to check 283 */ 284 public boolean isSslStarted(IoSession session) { 285 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER); 286 287 if (sslHandler == null) { 288 return false; 289 } 290 291 synchronized (sslHandler) { 292 return !sslHandler.isOutboundDone(); 293 } 294 } 295 296 /** 297 * Stops the SSL session by sending TLS <tt>close_notify</tt> message to 298 * initiate TLS closure. 299 * 300 * @param session the {@link IoSession} to initiate TLS closure 301 * @return The Future for the initiated closure 302 * @throws SSLException if failed to initiate TLS closure 303 */ 304 public WriteFuture stopSsl(IoSession session) throws SSLException { 305 SslHandler sslHandler = getSslSessionHandler(session); 306 NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER); 307 WriteFuture future; 308 309 try { 310 synchronized (sslHandler) { 311 future = initiateClosure(nextFilter, session); 312 } 313 314 sslHandler.flushScheduledEvents(); 315 } catch (SSLException se) { 316 sslHandler.release(); 317 throw se; 318 } 319 320 return future; 321 } 322 323 /** 324 * @return <tt>true</tt> if the engine is set to use client mode 325 * when handshaking. 326 */ 327 public boolean isUseClientMode() { 328 return client; 329 } 330 331 /** 332 * Configures the engine to use client (or server) mode when handshaking. 333 * 334 * @param clientMode <tt>true</tt> when we are in client mode, <tt>false</tt> when in server mode 335 */ 336 public void setUseClientMode(boolean clientMode) { 337 this.client = clientMode; 338 } 339 340 /** 341 * @return <tt>true</tt> if the engine will <em>require</em> client authentication. 342 * This option is only useful to engines in the server mode. 343 */ 344 public boolean isNeedClientAuth() { 345 return needClientAuth; 346 } 347 348 /** 349 * Configures the engine to <em>require</em> client authentication. 350 * This option is only useful for engines in the server mode. 351 * 352 * @param needClientAuth A flag set when we need to authenticate the client 353 */ 354 public void setNeedClientAuth(boolean needClientAuth) { 355 this.needClientAuth = needClientAuth; 356 } 357 358 /** 359 * @return <tt>true</tt> if the engine will <em>request</em> client authentication. 360 * This option is only useful to engines in the server mode. 361 */ 362 public boolean isWantClientAuth() { 363 return wantClientAuth; 364 } 365 366 /** 367 * Configures the engine to <em>request</em> client authentication. 368 * This option is only useful for engines in the server mode. 369 * 370 * @param wantClientAuth A flag set when we want to check the client authentication 371 */ 372 public void setWantClientAuth(boolean wantClientAuth) { 373 this.wantClientAuth = wantClientAuth; 374 } 375 376 /** 377 * @return the list of cipher suites to be enabled when {@link SSLEngine} 378 * is initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.' 379 */ 380 public String[] getEnabledCipherSuites() { 381 return enabledCipherSuites; 382 } 383 384 /** 385 * Sets the list of cipher suites to be enabled when {@link SSLEngine} 386 * is initialized. 387 * 388 * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.' 389 */ 390 public void setEnabledCipherSuites(String[] cipherSuites) { 391 this.enabledCipherSuites = cipherSuites; 392 } 393 394 /** 395 * @return the list of protocols to be enabled when {@link SSLEngine} 396 * is initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.' 397 */ 398 public String[] getEnabledProtocols() { 399 return enabledProtocols; 400 } 401 402 /** 403 * Sets the list of protocols to be enabled when {@link SSLEngine} 404 * is initialized. 405 * 406 * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.' 407 */ 408 public void setEnabledProtocols(String[] protocols) { 409 this.enabledProtocols = protocols; 410 } 411 412 /** 413 * Executed just before the filter is added into the chain, we do : 414 * <ul> 415 * <li>check that we don't have a SSL filter already present 416 * <li>we update the next filter 417 * <li>we create the SSL handler helper class 418 * <li>and we store it into the session's Attributes 419 * </ul> 420 */ 421 @Override 422 public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException { 423 // Check that we don't have a SSL filter already present in the chain 424 if (parent.contains(SslFilter.class)) { 425 String msg = "Only one SSL filter is permitted in a chain."; 426 LOGGER.error(msg); 427 throw new IllegalStateException(msg); 428 } 429 430 LOGGER.debug("Adding the SSL Filter {} to the chain", name); 431 432 IoSession session = parent.getSession(); 433 session.setAttribute(NEXT_FILTER, nextFilter); 434 435 // Create a SSL handler and start handshake. 436 SslHandler sslHandler = new SslHandler(this, session); 437 sslHandler.init(); 438 439 // Adding the supported ciphers in the SSLHandler 440 // In Java 6, we should call sslContext.getSupportedSSLParameters() 441 // instead 442 String[] ciphers = sslContext.getServerSocketFactory().getSupportedCipherSuites(); 443 setEnabledCipherSuites(ciphers); 444 session.setAttribute(SSL_HANDLER, sslHandler); 445 } 446 447 @Override 448 public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException { 449 if (autoStart == START_HANDSHAKE) { 450 initiateHandshake(nextFilter, parent.getSession()); 451 } 452 } 453 454 @Override 455 public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException { 456 IoSession session = parent.getSession(); 457 stopSsl(session); 458 session.removeAttribute(NEXT_FILTER); 459 session.removeAttribute(SSL_HANDLER); 460 } 461 462 // IoFilter impl. 463 @Override 464 public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException { 465 SslHandler sslHandler = getSslSessionHandler(session); 466 467 try { 468 synchronized (sslHandler) { 469 // release resources 470 sslHandler.destroy(); 471 } 472 473 sslHandler.flushScheduledEvents(); 474 } finally { 475 // notify closed session 476 nextFilter.sessionClosed(session); 477 } 478 } 479 480 @Override 481 public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException { 482 if (LOGGER.isDebugEnabled()) { 483 LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message); 484 } 485 486 SslHandler sslHandler = getSslSessionHandler(session); 487 488 synchronized (sslHandler) { 489 if (!isSslStarted(session) && sslHandler.isInboundDone()) { 490 // The SSL session must be established first before we 491 // can push data to the application. Store the incoming 492 // data into a queue for a later processing 493 sslHandler.scheduleMessageReceived(nextFilter, message); 494 } else { 495 IoBuffer buf = (IoBuffer) message; 496 497 try { 498 // forward read encrypted data to SSL handler 499 sslHandler.messageReceived(nextFilter, buf.buf()); 500 501 // Handle data to be forwarded to application or written to net 502 handleSslData(nextFilter, sslHandler); 503 504 if (sslHandler.isInboundDone()) { 505 if (sslHandler.isOutboundDone()) { 506 sslHandler.destroy(); 507 } else { 508 initiateClosure(nextFilter, session); 509 } 510 511 if (buf.hasRemaining()) { 512 // Forward the data received after closure. 513 sslHandler.scheduleMessageReceived(nextFilter, buf); 514 } 515 } 516 } catch (SSLException ssle) { 517 if (!sslHandler.isHandshakeComplete()) { 518 SSLException newSsle = new SSLHandshakeException("SSL handshake failed."); 519 newSsle.initCause(ssle); 520 ssle = newSsle; 521 } else { 522 // Free the SSL Handler buffers 523 sslHandler.release(); 524 } 525 526 throw ssle; 527 } 528 } 529 } 530 531 sslHandler.flushScheduledEvents(); 532 } 533 534 @Override 535 public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) { 536 if (writeRequest instanceof EncryptedWriteRequest) { 537 EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest; 538 nextFilter.messageSent(session, wrappedRequest.getParentRequest()); 539 } else { 540 // ignore extra buffers used for handshaking 541 } 542 } 543 544 @Override 545 public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception { 546 547 if (cause instanceof WriteToClosedSessionException) { 548 // Filter out SSL close notify, which is likely to fail to flush 549 // due to disconnection. 550 WriteToClosedSessionException e = (WriteToClosedSessionException) cause; 551 List<WriteRequest> failedRequests = e.getRequests(); 552 boolean containsCloseNotify = false; 553 554 for (WriteRequest r : failedRequests) { 555 if (isCloseNotify(r.getMessage())) { 556 containsCloseNotify = true; 557 break; 558 } 559 } 560 561 if (containsCloseNotify) { 562 if (failedRequests.size() == 1) { 563 // close notify is the only failed request; bail out. 564 return; 565 } 566 567 List<WriteRequest> newFailedRequests = new ArrayList<WriteRequest>(failedRequests.size() - 1); 568 569 for (WriteRequest r : failedRequests) { 570 if (!isCloseNotify(r.getMessage())) { 571 newFailedRequests.add(r); 572 } 573 } 574 575 if (newFailedRequests.isEmpty()) { 576 // the failedRequests were full with close notify; bail out. 577 return; 578 } 579 580 cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause()); 581 } 582 } 583 584 nextFilter.exceptionCaught(session, cause); 585 } 586 587 private boolean isCloseNotify(Object message) { 588 if (!(message instanceof IoBuffer)) { 589 return false; 590 } 591 592 IoBuffer buf = (IoBuffer) message; 593 int offset = buf.position(); 594 595 return (buf.get(offset + 0) == 0x15) /* Alert */ 596 && (buf.get(offset + 1) == 0x03) /* TLS/SSL */ 597 && ((buf.get(offset + 2) == 0x00) /* SSL 3.0 */ 598 || (buf.get(offset + 2) == 0x01) /* TLS 1.0 */ 599 || (buf.get(offset + 2) == 0x02) /* TLS 1.1 */ 600 || (buf.get(offset + 2) == 0x03)) /* TLS 1.2 */ 601 && (buf.get(offset + 3) == 0x00); /* close_notify */ 602 } 603 604 @Override 605 public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException { 606 if (LOGGER.isDebugEnabled()) { 607 LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest); 608 } 609 610 boolean needsFlush = true; 611 SslHandler sslHandler = getSslSessionHandler(session); 612 613 try { 614 synchronized (sslHandler) { 615 if (!isSslStarted(session)) { 616 sslHandler.scheduleFilterWrite(nextFilter, writeRequest); 617 } 618 // Don't encrypt the data if encryption is disabled. 619 else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) { 620 // Remove the marker attribute because it is temporary. 621 session.removeAttribute(DISABLE_ENCRYPTION_ONCE); 622 sslHandler.scheduleFilterWrite(nextFilter, writeRequest); 623 } else { 624 // Otherwise, encrypt the buffer. 625 IoBuffer buf = (IoBuffer) writeRequest.getMessage(); 626 627 if (sslHandler.isWritingEncryptedData()) { 628 // data already encrypted; simply return buffer 629 sslHandler.scheduleFilterWrite(nextFilter, writeRequest); 630 } else if (sslHandler.isHandshakeComplete()) { 631 // SSL encrypt 632 int pos = buf.position(); 633 sslHandler.encrypt(buf.buf()); 634 buf.position(pos); 635 IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer(); 636 sslHandler.scheduleFilterWrite(nextFilter, new EncryptedWriteRequest(writeRequest, 637 encryptedBuffer)); 638 } else { 639 if (session.isConnected()) { 640 // Handshake not complete yet. 641 sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest); 642 } 643 644 needsFlush = false; 645 } 646 } 647 } 648 649 if (needsFlush) { 650 sslHandler.flushScheduledEvents(); 651 } 652 } catch (SSLException se) { 653 sslHandler.release(); 654 throw se; 655 } 656 } 657 658 @Override 659 public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException { 660 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER); 661 662 if (sslHandler == null) { 663 // The connection might already have closed, or 664 // SSL might have not started yet. 665 nextFilter.filterClose(session); 666 return; 667 } 668 669 WriteFuture future = null; 670 671 try { 672 synchronized (sslHandler) { 673 if (isSslStarted(session)) { 674 future = initiateClosure(nextFilter, session); 675 future.addListener(new IoFutureListener<IoFuture>() { 676 public void operationComplete(IoFuture future) { 677 nextFilter.filterClose(session); 678 } 679 }); 680 } 681 } 682 683 sslHandler.flushScheduledEvents(); 684 } catch (SSLException se) { 685 sslHandler.release(); 686 throw se; 687 } finally { 688 if (future == null) { 689 nextFilter.filterClose(session); 690 } 691 } 692 } 693 694 private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException { 695 LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session)); 696 SslHandler sslHandler = getSslSessionHandler(session); 697 698 try { 699 synchronized (sslHandler) { 700 sslHandler.handshake(nextFilter); 701 } 702 703 sslHandler.flushScheduledEvents(); 704 } catch (SSLException se) { 705 sslHandler.release(); 706 throw se; 707 } 708 } 709 710 private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException { 711 SslHandler sslHandler = getSslSessionHandler(session); 712 WriteFuture future = null; 713 714 // if already shut down 715 try { 716 if (!sslHandler.closeOutbound()) { 717 return DefaultWriteFuture.newNotWrittenFuture(session, new IllegalStateException( 718 "SSL session is shut down already.")); 719 } 720 721 // there might be data to write out here? 722 future = sslHandler.writeNetBuffer(nextFilter); 723 724 if (future == null) { 725 future = DefaultWriteFuture.newWrittenFuture(session); 726 } 727 728 if (sslHandler.isInboundDone()) { 729 sslHandler.destroy(); 730 } 731 732 if (session.containsAttribute(USE_NOTIFICATION)) { 733 sslHandler.scheduleMessageReceived(nextFilter, SESSION_UNSECURED); 734 } 735 } catch (SSLException se) { 736 sslHandler.release(); 737 throw se; 738 } 739 740 return future; 741 } 742 743 // Utilities 744 private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException { 745 if (LOGGER.isDebugEnabled()) { 746 LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession())); 747 } 748 749 // Flush any buffered write requests occurred before handshaking. 750 if (sslHandler.isHandshakeComplete()) { 751 sslHandler.flushPreHandshakeEvents(); 752 } 753 754 // Write encrypted data to be written (if any) 755 sslHandler.writeNetBuffer(nextFilter); 756 757 // handle app. data read (if any) 758 handleAppDataRead(nextFilter, sslHandler); 759 } 760 761 private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) { 762 // forward read app data 763 IoBuffer readBuffer = sslHandler.fetchAppBuffer(); 764 765 if (readBuffer.hasRemaining()) { 766 sslHandler.scheduleMessageReceived(nextFilter, readBuffer); 767 } 768 } 769 770 private SslHandler getSslSessionHandler(IoSession session) { 771 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER); 772 773 if (sslHandler == null) { 774 throw new IllegalStateException(); 775 } 776 777 if (sslHandler.getSslFilter() != this) { 778 throw new IllegalArgumentException("Not managed by this filter."); 779 } 780 781 return sslHandler; 782 } 783 784 /** 785 * A message that is sent from {@link SslFilter} when the connection became 786 * secure or is not secure anymore. 787 * 788 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 789 */ 790 public static class SslFilterMessage { 791 private final String name; 792 793 private SslFilterMessage(String name) { 794 this.name = name; 795 } 796 797 @Override 798 public String toString() { 799 return name; 800 } 801 } 802 803 private static class EncryptedWriteRequest extends WriteRequestWrapper { 804 private final IoBuffer encryptedMessage; 805 806 private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) { 807 super(writeRequest); 808 this.encryptedMessage = encryptedMessage; 809 } 810 811 @Override 812 public Object getMessage() { 813 return encryptedMessage; 814 } 815 } 816}