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