-
Notifications
You must be signed in to change notification settings - Fork 427
Added CCC and close notify capabilities #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking "Sign up for GitHub", you agree to our terms of service and privacy statement. We'll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Open
Added CCC and close notify capabilities #49
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -137,7 +137,7 @@ public class SslFilter extends IoFilterAdapter { | |
| /** An attribute containing the next filter */ | ||
| private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter"); | ||
|
|
||
| private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler"); | ||
| static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler"); | ||
|
|
||
| /** The SslContext used */ | ||
| /* No qualifier */final SSLContext sslContext; | ||
|
|
@@ -335,6 +335,58 @@ public WriteFuture stopSsl(IoSession session) throws SSLException { | |
| return future; | ||
| } | ||
|
|
||
| /** | ||
| * Marks the handler as CCC being enabled | ||
| * | ||
| * @param session | ||
| * the {@link IoSession} to initiate TLS closure | ||
| * | ||
| * @throws IllegalArgumentException | ||
| * if this filter is not managing the specified session | ||
| */ | ||
| public void enableCCC(IoSession session) { | ||
| SslHandler handler = getSslSessionHandler(session); | ||
| handler.setCCCEnabled(true); | ||
| } | ||
|
|
||
| /** | ||
| * Stops the SSL filter handling of messages, but does not send a | ||
| * CLOSE_NOTIFY to the client. This basically disables the filter and | ||
| * proceeds with passing through the messages assumed to be plain text. This | ||
| * is added to support clients that do not wish to honor the close_notify | ||
| * negotiation. | ||
| * | ||
| * @param session | ||
| * the {@link IoSession} to initiate TLS closure | ||
| * @throws IllegalArgumentException | ||
| * if this filter is not managing the specified session | ||
| */ | ||
| public void stopSslWithoutCloseNotify(IoSession session) { | ||
| SslHandler handler = getSslSessionHandler(session); | ||
| handler.setDisabled(true); | ||
| } | ||
|
|
||
| /** | ||
| * Similar to the isSslStarted() method, however this will additionally | ||
| * check to see if the handler is disabled, which can happen when ssl is | ||
| * disabled without sending a CLOSE_NOTIFY | ||
| * | ||
| * @param session | ||
| * the SSL session to check | ||
| * @return whether or not ssl is currently active | ||
| */ | ||
| public boolean isSslActive(IoSession session) { | ||
| SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER); | ||
|
|
||
| if (handler == null) { | ||
| return false; | ||
| } | ||
|
|
||
| synchronized (handler) { | ||
| return !handler.isOutboundDone() && !handler.isDisabled(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
* @return true if the engine is set to use client mode |
||
| * when handshaking. | ||
|
|
@@ -520,62 +572,63 @@ public void messageReceived(NextFilter nextFilter, IoSession session, Object mes | |
| SslHandler sslHandler = getSslSessionHandler(session); | ||
| AtomicBoolean canPushMessage = new AtomicBoolean( false ); | ||
|
|
||
| // The SslHandler instance is *guaranteed* to nit be null here | ||
| // The SslHandler instance is *guaranteed* to not be null here | ||
| synchronized (sslHandler.getCccLock()) { | ||
| synchronized (sslHandler) { | ||
| if ((sslHandler.isOutboundDone() && sslHandler.isInboundDone()) || sslHandler.isDisabled()) { | ||
| // We aren't handshaking here. Let's push the message to the next filter | ||
|
|
||
| synchronized (sslHandler) { | ||
| if (sslHandler.isOutboundDone() && sslHandler.isInboundDone()) { | ||
| // We aren't handshaking here. Let's push the message to the next filter | ||
|
|
||
| // Note: we can push the message to the queue immediately, | ||
| // but don't do so in the synchronized block. We use a protected | ||
| // flag to do so. | ||
| canPushMessage.set( true ); | ||
| } else { | ||
| canPushMessage.set( false ); | ||
| IoBuffer buf = (IoBuffer) message; | ||
|
|
||
| try { | ||
| if (sslHandler.isOutboundDone()) { | ||
| sslHandler.destroy(); | ||
| throw new SSLException("Outbound done"); | ||
| } | ||
|
|
||
| // forward read encrypted data to SSL handler | ||
| sslHandler.messageReceived(nextFilter, buf.buf()); | ||
|
|
||
| // Handle data to be forwarded to application or written to net | ||
| handleSslData(nextFilter, sslHandler); | ||
|
|
||
| if (sslHandler.isInboundDone()) { | ||
| if (sslHandler.isOutboundDone()) { | ||
| // Note: we can push the message to the queue immediately, | ||
| // but don't do so in the synchronized block. We use a protected | ||
| // flag to do so. | ||
| canPushMessage.set( true ); | ||
| } else { | ||
| canPushMessage.set( false ); | ||
| IoBuffer buf = (IoBuffer) message; | ||
|
|
||
| try { | ||
| if (sslHandler.isOutboundDone() && !sslHandler.isCCCEnabled()) { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This entire 'if' block was originally commented out in our fork, citing issues with CCC and close notify. To not break backwards compatibility, I added the addition check for CCC enabled. |
||
| sslHandler.destroy(); | ||
| } else { | ||
| initiateClosure(nextFilter, session); | ||
| throw new SSLException("Outbound done"); | ||
| } | ||
|
|
||
| // forward read encrypted data to SSL handler | ||
| sslHandler.messageReceived(nextFilter, buf.buf()); | ||
|
|
||
| // Handle data to be forwarded to application or written to net | ||
| handleSslData(nextFilter, sslHandler); | ||
|
|
||
| if (sslHandler.isInboundDone()) { | ||
| if (sslHandler.isOutboundDone()) { | ||
| sslHandler.destroy(); | ||
| } else { | ||
| initiateClosure(nextFilter, session); | ||
| } | ||
|
|
||
| if (buf.hasRemaining()) { | ||
| // Forward the data received after closure. | ||
| sslHandler.scheduleMessageReceived(nextFilter, buf); | ||
| } | ||
| } | ||
|
|
||
| if (buf.hasRemaining()) { | ||
| // Forward the data received after closure. | ||
| sslHandler.scheduleMessageReceived(nextFilter, buf); | ||
| } catch (SSLException ssle) { | ||
| if (!sslHandler.isHandshakeComplete()) { | ||
| SSLException newSsle = new SSLHandshakeException("SSL handshake failed."); | ||
| newSsle.initCause(ssle); | ||
| ssle = newSsle; | ||
|
|
||
| // Close the session immediately, the handshake has failed | ||
| session.closeNow(); | ||
| } else { | ||
| // Free the SSL Handler buffers | ||
| sslHandler.release(); | ||
| } | ||
|
|
||
| throw ssle; | ||
| } | ||
| } catch (SSLException ssle) { | ||
| if (!sslHandler.isHandshakeComplete()) { | ||
| SSLException newSsle = new SSLHandshakeException("SSL handshake failed."); | ||
| newSsle.initCause(ssle); | ||
| ssle = newSsle; | ||
|
|
||
| // Close the session immediately, the handshake has failed | ||
| session.closeNow(); | ||
| } else { | ||
| // Free the SSL Handler buffers | ||
| sslHandler.release(); | ||
| } | ||
|
|
||
| throw ssle; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (canPushMessage.get()) { | ||
| nextFilter.messageReceived(session, message); | ||
| } else { | ||
|
|
@@ -672,6 +725,9 @@ else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) { | |
| // Remove the marker attribute because it is temporary. | ||
| session.removeAttribute(DISABLE_ENCRYPTION_ONCE); | ||
| sslHandler.scheduleFilterWrite(nextFilter, writeRequest); | ||
| } | ||
| else if(sslHandler.isDisabled()) { | ||
| sslHandler.scheduleFilterWrite(nextFilter,writeRequest); | ||
| } else { | ||
| // Otherwise, encrypt the buffer. | ||
| IoBuffer buf = (IoBuffer) writeRequest.getMessage(); | ||
|
|
@@ -722,12 +778,7 @@ public void filterClose(final NextFilter nextFilter, final IoSession session) th | |
| synchronized (sslHandler) { | ||
| if (isSslStarted(session)) { | ||
| future = initiateClosure(nextFilter, session); | ||
|
future.addListener(new IoFutureListener |
||
| @Override | ||
| public void operationComplete(IoFuture future) { | ||
| nextFilter.filterClose(session); | ||
| } | ||
| }); | ||
| future.addListener(future1 -> nextFilter.filterClose(session)); | ||
| } | ||
| sslHandler.flushFilterWrite(); | ||
| } | ||
|
|
@@ -890,7 +941,7 @@ public String toString() { | |
| private final IoBuffer encryptedMessage; | ||
|
|
||
| // The original message | ||
| private WriteRequest parentRequest; | ||
| private final WriteRequest parentRequest; | ||
|
|
||
| /** | ||
| * Create a new instance of an EncryptedWriteRequest | ||
|
|
@@ -926,4 +977,17 @@ public WriteFuture getFuture() { | |
| return parentRequest.getFuture(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns the ccc lock from the underlying ssl handler for synchronizing | ||
| * ccc logic when proper ssl termination is bypassed | ||
| * | ||
| * @param session | ||
| * the iosession | ||
| * @return the object to lock on | ||
| */ | ||
| public Object getCccLock(IoSession session) { | ||
| SslHandler handler = getSslSessionHandler(session); | ||
| return handler.getCccLock(); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,15 +118,31 @@ class SslHandler { | |
| * for data being produced during the handshake). */ | ||
| private boolean writingEncryptedData; | ||
|
|
||
| /** | ||
| * Whether or not this ssl handler is disabled | ||
| */ | ||
| private boolean disabled = false; | ||
|
|
||
| /** | ||
| * The lock used for CCC (clear command channel) negotiation | ||
| */ | ||
| private final Object cccLock; | ||
|
|
||
| /** | ||
| * Whether or not CCC is enabled | ||
| */ | ||
| private boolean cccEnabled = false; | ||
|
|
||
| /** | ||
| * Create a new SSL Handler, and initialize it. | ||
| * | ||
| * @param sslContext | ||
| * @throws SSLException | ||
| * @param sslFilter the {@code SslFilter} | ||
| * @param session the {@code IoSession} implementation | ||
| */ | ||
| /* no qualifier */SslHandler(SslFilter sslFilter, IoSession session) { | ||
| this.sslFilter = sslFilter; | ||
| this.session = session; | ||
| this.cccLock = new Object(); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -196,6 +212,8 @@ class SslHandler { | |
| // set the flags accordingly | ||
| firstSSLNegociation = true; | ||
| handshakeComplete = false; | ||
| disabled = false; | ||
| cccEnabled = false; | ||
|
|
||
| if (LOGGER.isDebugEnabled()) { | ||
| LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session)); | ||
|
|
@@ -509,6 +527,53 @@ class SslHandler { | |
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * Sets whether or not this handler has CCC enabled | ||
| * | ||
| * @param cccEnabled | ||
| * whether or not this handler is has CCC enabled | ||
| */ | ||
| void setCCCEnabled(boolean cccEnabled) { | ||
| this.cccEnabled = cccEnabled; | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether or not this handler has CCC enabled | ||
| * | ||
| * @return whether or not this handler has CCC enabled | ||
| */ | ||
| boolean isCCCEnabled() { | ||
| return cccEnabled; | ||
| } | ||
|
|
||
| /** | ||
| * Sets whether or not this handler is disabled | ||
| * | ||
| * @param disabled | ||
| * whether or not this handler is disabled | ||
| */ | ||
| void setDisabled(boolean disabled) { | ||
| this.disabled = disabled; | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether or not this handler is disabled | ||
| * | ||
| * @return whether or not this handler is disabled | ||
| */ | ||
| boolean isDisabled() { | ||
| return disabled; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the CCC lock | ||
| * | ||
| * @return the CCC lock object | ||
| */ | ||
| Object getCccLock() { | ||
| return cccLock; | ||
| } | ||
|
|
||
| /** | ||
| * @param res | ||
| * @throws SSLException | ||
|
|
@@ -530,28 +595,31 @@ private void checkStatus(SSLEngineResult res) throws SSLException { | |
| throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer | ||
| + "appBuffer: " + appBuffer); | ||
| case CLOSED: | ||
| Exception exception =new RuntimeIoException("SSL/TLS close_notify received"); | ||
|
|
||
| // Empty the Ssl queue | ||
| for (IoFilterEvent event:filterWriteEventQueue) { | ||
| EncryptedWriteRequest writeRequest = (EncryptedWriteRequest)event.getParameter(); | ||
| WriteFuture writeFuture = writeRequest.getParentRequest().getFuture(); | ||
| writeFuture.setException(exception); | ||
| writeFuture.notifyAll(); | ||
| } | ||
|
|
||
| // Empty the session queue | ||
| WriteRequestQueue queue = session.getWriteRequestQueue(); | ||
| WriteRequest request = null; | ||
|
|
||
| while ((request = queue.poll(session)) != null) { | ||
| WriteFuture writeFuture = request.getFuture(); | ||
| writeFuture.setException(exception); | ||
| writeFuture.notifyAll(); | ||
| } | ||
|
|
||
| // We *must* shutdown session | ||
| session.closeNow(); | ||
| // Adding this if check so that we don't close the connection completely if we are using CCC | ||
| if (!cccEnabled) { | ||
| Exception exception =new RuntimeIoException("SSL/TLS close_notify received"); | ||
|
|
||
| // Empty the Ssl queue | ||
| for (IoFilterEvent event:filterWriteEventQueue) { | ||
| EncryptedWriteRequest writeRequest = (EncryptedWriteRequest)event.getParameter(); | ||
| WriteFuture writeFuture = writeRequest.getParentRequest().getFuture(); | ||
| writeFuture.setException(exception); | ||
| writeFuture.notifyAll(); | ||
| } | ||
|
|
||
| // Empty the session queue | ||
| WriteRequestQueue queue = session.getWriteRequestQueue(); | ||
| WriteRequest request = null; | ||
|
|
||
| while ((request = queue.poll(session)) != null) { | ||
| WriteFuture writeFuture = request.getFuture(); | ||
| writeFuture.setException(exception); | ||
| writeFuture.notifyAll(); | ||
| } | ||
|
|
||
| // We *must* shutdown session | ||
| session.closeNow(); | ||
| } | ||
| break; | ||
| default: | ||
| break; | ||
|
|
@@ -588,7 +656,10 @@ private void checkStatus(SSLEngineResult res) throws SSLException { | |
| } | ||
|
|
||
| if (inNetBuffer != null && inNetBuffer.hasRemaining()) { | ||
| LOGGER.debug("pos: " + inNetBuffer.position() + ", lim: " + inNetBuffer.limit() + ", cap: " + inNetBuffer.capacity()); | ||
| LOGGER.debug("pos: {}, lim: {}, cap: {}", | ||
| inNetBuffer.position(), | ||
| inNetBuffer.limit(), | ||
| inNetBuffer.capacity()); | ||
| inNetBuffer.flip(); | ||
| SSLEngineResult res = unwrap(); | ||
|
|
||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.