Skip to content
This repository has been archived by the owner on Oct 15, 2019. It is now read-only.

Commit

Permalink
Add SessionAwareSslHandler
Browse files Browse the repository at this point in the history
This adds an SessionAwareSslHandler. Many components
might want to get access to an ssl session. The sslhandler
API is currently a bit racy, because calling it twice could
potentially cause renegotiation to trigger.

This changes it so that upstream filters will not get a
channelConnected until the SSL connection is established.

SessionAwareSslHandler propagates a message called sslsession
which upstream handlers can also register for.
  • Loading branch information
siyengar committed Aug 23, 2016
1 parent c0cf0c9 commit a5b5692
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 16 deletions.
Expand Up @@ -131,6 +131,8 @@ public ChannelPipeline getPipeline()
SslServerConfiguration serverConfiguration = sslConfiguration.get();
if (serverConfiguration != null) {
SslHandler handler = serverConfiguration.createHandler();
// This will delay channel connected upstream events until the channel is actually connected.
handler.setIssueHandshake(true);
if (serverConfiguration.allowPlaintext) {
cp.addFirst("ssl_plaintext", new SslPlaintextHandler(handler, "ssl"));
} else {
Expand Down
Expand Up @@ -47,7 +47,7 @@ public static JavaSslServerConfiguration.Builder newBuilder() {

protected SslHandlerFactory createSslHandlerFactory() {
try {
SslContext sslHandler =
SslContext sslContext =
SslContext.newServerContext(
SslProvider.JDK,
null,
Expand All @@ -61,7 +61,13 @@ protected SslHandlerFactory createSslHandlerFactory() {
return new SslHandlerFactory() {
@Override
public SslHandler newHandler() {
return sslHandler.newHandler();
SessionAwareSslHandler handler =
new SessionAwareSslHandler(
sslContext.newEngine(),
sslContext.bufferPool(),
JavaSslServerConfiguration.this);
handler.setCloseOnSSLException(true);
return handler;
}
};
}
Expand Down
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2012-2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.facebook.nifty.ssl;

import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.ssl.SslBufferPool;
import org.jboss.netty.handler.ssl.SslHandler;

import javax.net.ssl.SSLEngine;

/**
* An SslHandler which propagates session events to other handlers in the pipeline.
* This does not support the delayed handshake mode and will start the handshake as soon as the
* channel is connected.
*/
public class SessionAwareSslHandler extends SslHandler {

private final SslServerConfiguration sslServerConfiguration;

public SessionAwareSslHandler(SSLEngine engine, SslBufferPool pool, SslServerConfiguration configuration) {
super(engine, pool);
this.sslServerConfiguration = configuration;
}

@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
handshake().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
SslSession sslSession = sslServerConfiguration.getSession(getEngine());
Channels.fireChannelConnected(ctx, ctx.getPipeline().getChannel().getRemoteAddress());
Channels.fireMessageReceived(ctx, sslSession);
}
}
});
}
}
Expand Up @@ -30,6 +30,11 @@ public SslPlaintextHandler(SslHandler sslHandler, String sslHandlerName) {
this.sslHandlerName = sslHandlerName;
}

@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
// delay channel connects until we know what kind of transport we have.
}

@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
if (buffer.readableBytes() < 9) {
Expand All @@ -45,6 +50,7 @@ protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffe
}

ctx.getPipeline().remove(this);
Channels.fireChannelConnected(ctx, ctx.getPipeline().getChannel().getRemoteAddress());
Channels.fireMessageReceived(ctx, buffer, ctx.getPipeline().getChannel().getRemoteAddress());
return null;
}
Expand Down
Expand Up @@ -231,19 +231,15 @@ public NiftySecurityHandlers getSecurityHandlers(ThriftServerDef def, NettyServe
public ChannelHandler getAuthenticationHandler() {
return new SimpleChannelHandler() {
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelOpen(ctx, e);
SslHandler handler = (SslHandler) ctx.getPipeline().get("ssl");
handler.handshake().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
synchronized (TestNiftyOpenSslServer.this) {
sslSession[0] = configuration.getSession(handler.getEngine());
TestNiftyOpenSslServer.this.notify();
}
}
});
ctx.getPipeline().remove(this);
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if (!(e.getMessage() instanceof SslSession)) {
super.messageReceived(ctx, e);
return;
}
synchronized (TestNiftyOpenSslServer.this) {
sslSession[0] = (SslSession) e.getMessage();
TestNiftyOpenSslServer.this.notify();
}
}
};
}
Expand Down Expand Up @@ -301,6 +297,29 @@ public void testClientAuthenticatingServer() throws InterruptedException {
Assert.assertEquals(session[0].peerCert.getSubjectDN().toString(), "CN=RSA, OU=RSA, O=RSA, L=Default City, C=XX");
}

@Test
public void testClientAuthenticatingServerAllowPlaintext() throws InterruptedException {
SslServerConfiguration serverConfig = OpenSslServerConfiguration.newBuilder()
.certFile(new File(Plain.class.getResource("/rsa.crt").getFile()))
.keyFile(new File(Plain.class.getResource("/rsa.key").getFile()))
.allowPlaintext(true)
.sslVerification(OpenSslServerConfiguration.SSLVerification.VERIFY_REQUIRE)
.clientCAFile(new File(Plain.class.getResource("/rsa.crt").getFile()))
.build();

ThriftServerDefBuilder builder = getThriftServerDefBuilder(serverConfig, null);
SslSession[] session = addAuthentication(builder, serverConfig);
startServer(builder);
startClientWithCerts();
// Waits for max of 100ms for the server thread to process the cert
synchronized (this) {
if (session[0] == null) {
wait(100);
}
}
Assert.assertEquals(session[0].peerCert.getSubjectDN().toString(), "CN=RSA, OU=RSA, O=RSA, L=Default City, C=XX");
}

@Test(expectedExceptions = TTransportException.class)
public void testClientWithoutCerts() throws InterruptedException, TException {
SslServerConfiguration serverConfig = OpenSslServerConfiguration.newBuilder()
Expand Down
Expand Up @@ -283,7 +283,7 @@ public void setSessionCacheTimeout(long sessionTimeoutSeconds) {

@Override
public SslHandler newHandler() {
SslHandler handler = new SslHandler(newEngine(), bufferPool);
SessionAwareSslHandler handler = new SessionAwareSslHandler(newEngine(), bufferPool, sslServerConfiguration);
handler.setCloseOnSSLException(true);
return handler;
}
Expand Down

0 comments on commit a5b5692

Please sign in to comment.