402666 - Improve handling of TLS exceptions due to raw socket close.

This commit is contained in:
Simone Bordet 2013-03-29 16:57:05 +01:00
parent b921ed13c0
commit b312fffd7e
3 changed files with 93 additions and 12 deletions

View File

@ -538,8 +538,10 @@ public class SslConnection extends AbstractConnection
case NOT_HANDSHAKING:
// we just didn't read anything.
if (net_filled < 0)
_sslEngine.closeInbound();
{
closeInbound();
return -1;
}
return 0;
case NEED_TASK:
@ -568,14 +570,8 @@ public class SslConnection extends AbstractConnection
// if we just filled some net data
if (net_filled < 0)
{
// If we call closeInbound() before having read the SSL close
// message an exception will be thrown (truncation attack).
// The TLS specification says that the sender of the SSL close
// message may just close and avoid to read the response.
// If that is the case, we avoid calling closeInbound() because
// will throw the truncation attack exception for nothing.
if (isOpen())
_sslEngine.closeInbound();
closeInbound();
return -1;
}
else if (net_filled > 0)
{
@ -625,6 +621,18 @@ public class SslConnection extends AbstractConnection
}
}
private void closeInbound()
{
try
{
_sslEngine.closeInbound();
}
catch (SSLException x)
{
LOG.ignore(x);
}
}
@Override
public synchronized boolean flush(ByteBuffer... appOuts) throws IOException
{

View File

@ -26,6 +26,7 @@ import java.nio.channels.SocketChannel;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.io.ssl.SslConnection;
@ -197,10 +198,28 @@ public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
Assert.assertEquals("HelloWorld",reply);
if (debug) System.err.println("Shutting down output");
client.socket().shutdownOutput();
filled=client.read(sslIn);
if (debug) System.err.println("in="+filled);
sslIn.flip();
try
{
// Since the client closed abruptly, the server is sending a close alert with a failure
engine.unwrap(sslIn, appIn);
Assert.fail();
}
catch (SSLException x)
{
// Expected
}
sslIn.clear();
filled=client.read(sslIn);
Assert.assertEquals(-1,filled);
Assert.assertFalse(server.isOpen());
}
@Test

View File

@ -39,7 +39,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
@ -866,7 +865,62 @@ public class SslBytesServerTest extends SslBytesTest
// Close the raw socket, this generates a truncation attack
proxy.flushToServer(null);
// Expect raw close from server
// Expect alert + raw close from server
record = proxy.readFromServer();
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500);
Assert.assertThat(sslFills.get(), Matchers.lessThan(20));
Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
Assert.assertThat(httpParses.get(), Matchers.lessThan(20));
client.close();
}
@Test
public void testRequestWithImmediateRawClose() throws Exception
{
final SSLSocket client = newClient();
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
client.startHandshake();
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
Future<Object> request = threadPool.submit(new Callable<Object>()
{
@Override
public Object call() throws Exception
{
OutputStream clientOutput = client.getOutputStream();
clientOutput.write(("" +
"GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n").getBytes("UTF-8"));
clientOutput.flush();
return null;
}
});
// Application data
TLSRecord record = proxy.readFromClient();
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToServer(record, 0);
// Close the raw socket, this generates a truncation attack
proxy.flushToServer(null);
Assert.assertNull(request.get(5, TimeUnit.SECONDS));
// Application data
record = proxy.readFromServer();
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToClient(record);
// Expect alert + raw close from server
record = proxy.readFromServer();
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);