This commit is contained in:
Greg Wilkins 2017-04-12 14:54:18 +10:00
parent daafc8fed1
commit 23a9c6c1be
9 changed files with 224 additions and 40 deletions

View File

@ -83,6 +83,7 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
{
SslConnection sslConnection = (SslConnection)connection;
sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
sslConnection.setRenegotiationLimit(sslContextFactory.getRenegotiationLimit());
ContainerLifeCycle connector = (ContainerLifeCycle)context.get(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY);
connector.getBeans(SslHandshakeListener.class).forEach(sslConnection::addHandshakeListener);
}

View File

@ -90,6 +90,7 @@ public class SslConnection extends AbstractConnection
private final boolean _encryptedDirectBuffers = true;
private final boolean _decryptedDirectBuffers = false;
private boolean _renegotiationAllowed;
private int _renegotiationLimit = -1;
private boolean _closedOutbound;
private final Runnable _runCompletWrite = new Runnable()
{
@ -170,7 +171,26 @@ public class SslConnection extends AbstractConnection
public void setRenegotiationAllowed(boolean renegotiationAllowed)
{
this._renegotiationAllowed = renegotiationAllowed;
_renegotiationAllowed = renegotiationAllowed;
}
/**
* @return The number of renegotions allowed for this connection. When the limit
* is 0 renegotiation will be denied. If the limit is <0 then no limit is applied.
*/
public int getRenegotiationLimit()
{
return _renegotiationLimit;
}
/**
* @param renegotiationLimit The number of renegotions allowed for this connection.
* When the limit is 0 renegotiation will be denied. If the limit is <0 then no limit is applied.
* Default -1.
*/
public void setRenegotiationLimit(int renegotiationLimit)
{
_renegotiationLimit = renegotiationLimit;
}
@Override
@ -323,7 +343,7 @@ public class SslConnection extends AbstractConnection
synchronized (DecryptedEndPoint.this)
{
if (LOG.isDebugEnabled())
LOG.debug("{} write failed", SslConnection.this, x);
LOG.debug("write failed {}", SslConnection.this, x);
BufferUtil.clear(_encryptedOutput);
releaseEncryptedOutputBuffer();
@ -567,8 +587,8 @@ public class SslConnection extends AbstractConnection
}
if (LOG.isDebugEnabled())
{
LOG.debug("{} net={} unwrap {}", SslConnection.this, net_filled, unwrapResult.toString().replace('\n',' '));
LOG.debug("{} filled {}",SslConnection.this,BufferUtil.toHexSummary(buffer));
LOG.debug("net={} unwrap {} {}", net_filled, unwrapResult.toString().replace('\n',' '), SslConnection.this);
LOG.debug("filled {} {}",BufferUtil.toHexSummary(buffer), SslConnection.this);
}
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
@ -626,25 +646,13 @@ public class SslConnection extends AbstractConnection
case BUFFER_UNDERFLOW:
case OK:
{
if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken)
{
_handshaken = true;
if (LOG.isDebugEnabled())
LOG.debug("{} {} handshake succeeded {}/{}", SslConnection.this,
_sslEngine.getUseClientMode() ? "client" : "resumed server",
_sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite());
notifyHandshakeSucceeded(_sslEngine);
}
if (unwrapHandshakeStatus == HandshakeStatus.FINISHED)
handshakeFinished();
// Check whether renegotiation is allowed
if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
{
if (LOG.isDebugEnabled())
LOG.debug("{} renegotiation denied", SslConnection.this);
closeInbound();
// Check whether re-negotiation is allowed
if (!allowRenegotiate(handshakeStatus))
return -1;
}
// If bytes were produced, don't bother with the handshake status;
// pass the decrypted data to the application, which will perform
// another call to fill() or flush().
@ -762,6 +770,51 @@ public class SslConnection extends AbstractConnection
}
}
private void handshakeFinished()
{
if (_handshaken)
{
if (LOG.isDebugEnabled())
LOG.debug("Renegotiated {}", SslConnection.this);
if (_renegotiationLimit>0)
_renegotiationLimit--;
}
else
{
_handshaken = true;
if (LOG.isDebugEnabled())
LOG.debug("{} handshake succeeded {}/{} {}",
_sslEngine.getUseClientMode() ? "client" : "resumed server",
_sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite(),
SslConnection.this);
notifyHandshakeSucceeded(_sslEngine);
}
}
private boolean allowRenegotiate(HandshakeStatus handshakeStatus)
{
if (!_handshaken || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING)
return true;
if (!isRenegotiationAllowed())
{
if (LOG.isDebugEnabled())
LOG.debug("Renegotiation denied {}", SslConnection.this);
closeInbound();
return false;
}
if (_renegotiationLimit==0)
{
if (LOG.isDebugEnabled())
LOG.debug("Renegotiation limit exceeded {}", SslConnection.this);
closeInbound();
return false;
}
return true;
}
private void closeInbound()
{
try
@ -787,7 +840,7 @@ public class SslConnection extends AbstractConnection
if (LOG.isDebugEnabled())
{
for (ByteBuffer b : appOuts)
LOG.debug("{} flush {}", SslConnection.this, BufferUtil.toHexSummary(b));
LOG.debug("flush {} {}", BufferUtil.toHexSummary(b), SslConnection.this);
}
try
@ -822,7 +875,7 @@ public class SslConnection extends AbstractConnection
BufferUtil.flipToFlush(_encryptedOutput, pos);
}
if (LOG.isDebugEnabled())
LOG.debug("{} wrap {}", SslConnection.this, wrapResult.toString().replace('\n',' '));
LOG.debug("wrap {} {}", wrapResult.toString().replace('\n',' '), SslConnection.this);
Status wrapResultStatus = wrapResult.getStatus();
@ -863,29 +916,20 @@ public class SslConnection extends AbstractConnection
default:
{
if (LOG.isDebugEnabled())
LOG.debug("{} wrap {} {}", SslConnection.this, wrapResultStatus, BufferUtil.toHexSummary(_encryptedOutput));
LOG.debug("wrap {} {} {}", wrapResultStatus, BufferUtil.toHexSummary(_encryptedOutput), SslConnection.this);
if (wrapResult.getHandshakeStatus() == HandshakeStatus.FINISHED && !_handshaken)
{
_handshaken = true;
if (LOG.isDebugEnabled())
LOG.debug("{} {} handshake succeeded {}/{}", SslConnection.this,
_sslEngine.getUseClientMode() ? "resumed client" : "server",
_sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite());
notifyHandshakeSucceeded(_sslEngine);
}
if (wrapResult.getHandshakeStatus() == HandshakeStatus.FINISHED)
handshakeFinished();
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
// Check whether renegotiation is allowed
if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
// Check whether re-negotiation is allowed
if (!allowRenegotiate(handshakeStatus))
{
if (LOG.isDebugEnabled())
LOG.debug("{} renegotiation denied", SslConnection.this);
getEndPoint().shutdownOutput();
return allConsumed;
}
// if we have net bytes, let's try to flush them
if (BufferUtil.hasContent(_encryptedOutput))
if (!getEndPoint().flush(_encryptedOutput))
@ -975,7 +1019,7 @@ public class SslConnection extends AbstractConnection
boolean ishut = isInputShutdown();
boolean oshut = isOutputShutdown();
if (LOG.isDebugEnabled())
LOG.debug("{} shutdownOutput: oshut={}, ishut={}", SslConnection.this, oshut, ishut);
LOG.debug("shutdownOutput: oshut={}, ishut={} {}", oshut, ishut, SslConnection.this);
if (oshut)
return;

View File

@ -77,6 +77,7 @@ public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
engine.setUseClientMode(false);
SslConnection sslConnection = new SslConnection(__byteBufferPool, _threadPool, endpoint, engine);
sslConnection.setRenegotiationAllowed(__sslCtxFactory.isRenegotiationAllowed());
sslConnection.setRenegotiationLimit(__sslCtxFactory.getRenegotiationLimit());
Connection appConnection = super.newConnection(channel,sslConnection.getDecryptedEndPoint());
sslConnection.getDecryptedEndPoint().setConnection(appConnection);
return sslConnection;

View File

@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.io.ssl.SslConnection;
@ -80,6 +81,7 @@ public class SslConnectionTest
engine.setUseClientMode(false);
SslConnection sslConnection = new SslConnection(__byteBufferPool, getExecutor(), endpoint, engine);
sslConnection.setRenegotiationAllowed(__sslCtxFactory.isRenegotiationAllowed());
sslConnection.setRenegotiationLimit(__sslCtxFactory.getRenegotiationLimit());
Connection appConnection = new TestConnection(sslConnection.getDecryptedEndPoint());
sslConnection.getDecryptedEndPoint().setConnection(appConnection);
return sslConnection;
@ -149,6 +151,8 @@ public class SslConnectionTest
_threadPool.start();
_scheduler.start();
_manager.start();
__sslCtxFactory.setRenegotiationAllowed(true);
__sslCtxFactory.setRenegotiationLimit(-1);
}
@ -248,7 +252,7 @@ public class SslConnectionTest
}
}
}
protected Socket newClient() throws IOException
protected SSLSocket newClient() throws IOException
{
SSLSocket socket = __sslCtxFactory.newSslSocket();
socket.connect(_connector.socket().getLocalSocketAddress());
@ -280,6 +284,112 @@ public class SslConnectionTest
client.close();
}
@Test
public void testRenegotiate() throws Exception
{
SSLSocket client = newClient();
client.setSoTimeout(60000);
SocketChannel server = _connector.accept();
server.configureBlocking(false);
_manager.accept(server);
client.getOutputStream().write("Hello".getBytes(StandardCharsets.UTF_8));
byte[] buffer = new byte[1024];
int len=client.getInputStream().read(buffer);
Assert.assertEquals(5, len);
Assert.assertEquals("Hello",new String(buffer,0,len,StandardCharsets.UTF_8));
client.startHandshake();
client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8));
len=client.getInputStream().read(buffer);
Assert.assertEquals(5, len);
Assert.assertEquals("World",new String(buffer,0,len,StandardCharsets.UTF_8));
client.close();
}
@Test
public void testRenegotiateNotAllowed() throws Exception
{
__sslCtxFactory.setRenegotiationAllowed(false);
SSLSocket client = newClient();
client.setSoTimeout(60000);
SocketChannel server = _connector.accept();
server.configureBlocking(false);
_manager.accept(server);
client.getOutputStream().write("Hello".getBytes(StandardCharsets.UTF_8));
byte[] buffer = new byte[1024];
int len=client.getInputStream().read(buffer);
Assert.assertEquals(5, len);
Assert.assertEquals("Hello",new String(buffer,0,len,StandardCharsets.UTF_8));
client.startHandshake();
client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8));
try
{
client.getInputStream().read(buffer);
Assert.fail();
}
catch(SSLException e)
{
// expected
}
}
@Test
public void testRenegotiateLimit() throws Exception
{
__sslCtxFactory.setRenegotiationAllowed(true);
__sslCtxFactory.setRenegotiationLimit(2);
SSLSocket client = newClient();
client.setSoTimeout(60000);
SocketChannel server = _connector.accept();
server.configureBlocking(false);
_manager.accept(server);
client.getOutputStream().write("Good".getBytes(StandardCharsets.UTF_8));
byte[] buffer = new byte[1024];
int len=client.getInputStream().read(buffer);
Assert.assertEquals(4, len);
Assert.assertEquals("Good",new String(buffer,0,len,StandardCharsets.UTF_8));
client.startHandshake();
client.getOutputStream().write("Bye".getBytes(StandardCharsets.UTF_8));
len=client.getInputStream().read(buffer);
Assert.assertEquals(3, len);
Assert.assertEquals("Bye",new String(buffer,0,len,StandardCharsets.UTF_8));
client.startHandshake();
client.getOutputStream().write("Cruel".getBytes(StandardCharsets.UTF_8));
len=client.getInputStream().read(buffer);
Assert.assertEquals(5, len);
Assert.assertEquals("Cruel",new String(buffer,0,len,StandardCharsets.UTF_8));
client.startHandshake();
client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8));
try
{
client.getInputStream().read(buffer);
Assert.fail();
}
catch(SSLException e)
{
// expected
}
}
@Test
public void testWriteOnConnect() throws Exception

View File

@ -26,4 +26,6 @@
<Set name="useCipherSuitesOrder"><Property name="jetty.sslContext.useCipherSuitesOrder" default="true"/></Set>
<Set name="sslSessionCacheSize"><Property name="jetty.sslContext.sslSessionCacheSize" default="-1"/></Set>
<Set name="sslSessionTimeout"><Property name="jetty.sslContext.sslSessionTimeout" default="-1"/></Set>
<Set name="RenegotiationAllowed"><Property name="jetty.sslContext.renegotiationAllowed" default="true"/></Set>
<Set name="RenegotiationLimit"><Property name="jetty.sslContext.renegotiationLimit" default="5"/></Set>
</Configure>

View File

@ -95,3 +95,7 @@ https://raw.githubusercontent.com/eclipse/jetty.project/master/jetty-server/src/
## Set the timeout (in seconds) of the SslSession cache timeout
# jetty.sslContext.sslSessionTimeout=-1
## Allow SSL renegotiation
# jetty.sslContext.renegotiationAllowed=true
# jetty.sslContext.renegotiationLimit=5

View File

@ -87,6 +87,7 @@ public class SslConnectionFactory extends AbstractConnectionFactory
SslConnection sslConnection = newSslConnection(connector, endPoint, engine);
sslConnection.setRenegotiationAllowed(_sslContextFactory.isRenegotiationAllowed());
sslConnection.setRenegotiationLimit(_sslContextFactory.getRenegotiationLimit());
configure(sslConnection, connector, endPoint);
ConnectionFactory next = connector.getConnectionFactory(_nextProtocol);

View File

@ -166,6 +166,7 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
private String _endpointIdentificationAlgorithm = null;
private boolean _trustAll;
private boolean _renegotiationAllowed = true;
private int _renegotiationLimit = 5;
private Factory _factory;
/**
@ -930,6 +931,25 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
_renegotiationAllowed = renegotiationAllowed;
}
/**
* @return The number of renegotions allowed for this connection. When the limit
* is 0 renegotiation will be denied. If the limit is <0 then no limit is applied.
*/
public int getRenegotiationLimit()
{
return _renegotiationLimit;
}
/**
* @param renegotiationLimit The number of renegotions allowed for this connection.
* When the limit is 0 renegotiation will be denied. If the limit is <0 then no limit is applied.
* Default 5.
*/
public void setRenegotiationLimit(int renegotiationLimit)
{
_renegotiationLimit = renegotiationLimit;
}
/**
* @return Path to file that contains Certificate Revocation List
*/

View File

@ -86,6 +86,7 @@ public class WebSocketClientSelectorManager extends SelectorManager
SSLEngine engine = newSSLEngine(sslContextFactory,channel);
SslConnection sslConnection = new SslConnection(bufferPool,getExecutor(),endPoint,engine);
sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
sslConnection.setRenegotiationLimit(sslContextFactory.getRenegotiationLimit());
EndPoint sslEndPoint = sslConnection.getDecryptedEndPoint();
Connection connection = newUpgradeConnection(channel,sslEndPoint,connectPromise);