[#5976] Ensure we only consume as much data on wrap(...) as we can handle.
Motiviation: We need to ensure we only consume as much da as we can maximal put in one ssl record to not produce a BUFFER_OVERFLOW when calling wrap(...). Modification: - Limit the amount of data that we consume based on the maximal plain text size that can be put in one ssl record - Add testcase to verify the fix - Tighten up testcases to ensure the amount of produced and consumed data in SslEngineResult matches the buffers. If not the tests will fail now. Result: Correct and conform behavior of OpenSslEngine.wrap(...) and better test coverage during handshaking in general.
This commit is contained in:
parent
c4e96d010e
commit
c8575c42d0
@ -379,10 +379,9 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
*
|
*
|
||||||
* Calling this function with src.remaining == 0 is undefined.
|
* Calling this function with src.remaining == 0 is undefined.
|
||||||
*/
|
*/
|
||||||
private int writePlaintextData(final ByteBuffer src) {
|
private int writePlaintextData(final ByteBuffer src, int len) {
|
||||||
final int pos = src.position();
|
final int pos = src.position();
|
||||||
final int limit = src.limit();
|
final int limit = src.limit();
|
||||||
final int len = Math.min(limit - pos, MAX_PLAINTEXT_LENGTH);
|
|
||||||
final int sslWrote;
|
final int sslWrote;
|
||||||
|
|
||||||
if (src.isDirect()) {
|
if (src.isDirect()) {
|
||||||
@ -614,7 +613,8 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
int bytesProduced = 0;
|
int bytesProduced = 0;
|
||||||
int bytesConsumed = 0;
|
int bytesConsumed = 0;
|
||||||
int endOffset = offset + length;
|
int endOffset = offset + length;
|
||||||
for (int i = offset; i < endOffset; ++i) {
|
|
||||||
|
loop: for (int i = offset; i < endOffset; ++i) {
|
||||||
final ByteBuffer src = srcs[i];
|
final ByteBuffer src = srcs[i];
|
||||||
if (src == null) {
|
if (src == null) {
|
||||||
throw new IllegalArgumentException("srcs[" + i + "] is null");
|
throw new IllegalArgumentException("srcs[" + i + "] is null");
|
||||||
@ -622,7 +622,9 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
while (src.hasRemaining()) {
|
while (src.hasRemaining()) {
|
||||||
final SSLEngineResult pendingNetResult;
|
final SSLEngineResult pendingNetResult;
|
||||||
// Write plaintext application data to the SSL engine
|
// Write plaintext application data to the SSL engine
|
||||||
int result = writePlaintextData(src);
|
int result = writePlaintextData(
|
||||||
|
src, Math.min(src.remaining(), MAX_PLAINTEXT_LENGTH - bytesConsumed));
|
||||||
|
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
bytesConsumed += result;
|
bytesConsumed += result;
|
||||||
|
|
||||||
@ -633,6 +635,11 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
}
|
}
|
||||||
bytesProduced = pendingNetResult.bytesProduced();
|
bytesProduced = pendingNetResult.bytesProduced();
|
||||||
}
|
}
|
||||||
|
if (bytesConsumed == MAX_PLAINTEXT_LENGTH) {
|
||||||
|
// If we consumed the maximum amount of bytes for the plaintext length break out of the
|
||||||
|
// loop and start to fill the dst buffer.
|
||||||
|
break loop;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
int sslError = SSL.getError(ssl, result);
|
int sslError = SSL.getError(ssl, result);
|
||||||
switch (sslError) {
|
switch (sslError) {
|
||||||
|
@ -694,25 +694,71 @@ public abstract class SSLEngineTest {
|
|||||||
SSLEngineResult clientResult;
|
SSLEngineResult clientResult;
|
||||||
SSLEngineResult serverResult;
|
SSLEngineResult serverResult;
|
||||||
|
|
||||||
|
boolean clientHandshakeFinished = false;
|
||||||
|
boolean serverHandshakeFinished = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
int cTOsPos = cTOs.position();
|
||||||
|
int sTOcPos = sTOc.position();
|
||||||
|
|
||||||
clientResult = clientEngine.wrap(empty, cTOs);
|
clientResult = clientEngine.wrap(empty, cTOs);
|
||||||
runDelegatedTasks(clientResult, clientEngine);
|
runDelegatedTasks(clientResult, clientEngine);
|
||||||
serverResult = serverEngine.wrap(empty, sTOc);
|
serverResult = serverEngine.wrap(empty, sTOc);
|
||||||
runDelegatedTasks(serverResult, serverEngine);
|
runDelegatedTasks(serverResult, serverEngine);
|
||||||
|
|
||||||
|
// Verify that the consumed and produced number match what is in the buffers now.
|
||||||
|
assertEquals(empty.remaining(), clientResult.bytesConsumed());
|
||||||
|
assertEquals(empty.remaining(), serverResult.bytesConsumed());
|
||||||
|
assertEquals(cTOs.position() - cTOsPos, clientResult.bytesProduced());
|
||||||
|
assertEquals(sTOc.position() - sTOcPos, serverResult.bytesProduced());
|
||||||
|
|
||||||
cTOs.flip();
|
cTOs.flip();
|
||||||
sTOc.flip();
|
sTOc.flip();
|
||||||
|
|
||||||
|
// Verify that we only had one SSLEngineResult.HandshakeStatus.FINISHED
|
||||||
|
if (isHandshakeFinished(clientResult)) {
|
||||||
|
assertFalse(clientHandshakeFinished);
|
||||||
|
clientHandshakeFinished = true;
|
||||||
|
}
|
||||||
|
if (isHandshakeFinished(serverResult)) {
|
||||||
|
assertFalse(serverHandshakeFinished);
|
||||||
|
serverHandshakeFinished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cTOsPos = cTOs.position();
|
||||||
|
sTOcPos = sTOc.position();
|
||||||
|
|
||||||
|
int clientAppReadBufferPos = clientAppReadBuffer.position();
|
||||||
|
int serverAppReadBufferPos = serverAppReadBuffer.position();
|
||||||
|
|
||||||
clientResult = clientEngine.unwrap(sTOc, clientAppReadBuffer);
|
clientResult = clientEngine.unwrap(sTOc, clientAppReadBuffer);
|
||||||
runDelegatedTasks(clientResult, clientEngine);
|
runDelegatedTasks(clientResult, clientEngine);
|
||||||
serverResult = serverEngine.unwrap(cTOs, serverAppReadBuffer);
|
serverResult = serverEngine.unwrap(cTOs, serverAppReadBuffer);
|
||||||
runDelegatedTasks(serverResult, serverEngine);
|
runDelegatedTasks(serverResult, serverEngine);
|
||||||
|
|
||||||
|
// Verify that the consumed and produced number match what is in the buffers now.
|
||||||
|
assertEquals(sTOc.position() - sTOcPos, clientResult.bytesConsumed());
|
||||||
|
assertEquals(cTOs.position() - cTOsPos, serverResult.bytesConsumed());
|
||||||
|
assertEquals(clientAppReadBuffer.position() - clientAppReadBufferPos, clientResult.bytesProduced());
|
||||||
|
assertEquals(serverAppReadBuffer.position() - serverAppReadBufferPos, serverResult.bytesProduced());
|
||||||
|
|
||||||
cTOs.compact();
|
cTOs.compact();
|
||||||
sTOc.compact();
|
sTOc.compact();
|
||||||
} while (isHandshaking(clientResult) || isHandshaking(serverResult));
|
|
||||||
|
// Verify that we only had one SSLEngineResult.HandshakeStatus.FINISHED
|
||||||
|
if (isHandshakeFinished(clientResult)) {
|
||||||
|
assertFalse(clientHandshakeFinished);
|
||||||
|
clientHandshakeFinished = true;
|
||||||
|
}
|
||||||
|
if (isHandshakeFinished(serverResult)) {
|
||||||
|
assertFalse(serverHandshakeFinished);
|
||||||
|
serverHandshakeFinished = true;
|
||||||
|
}
|
||||||
|
} while (!clientHandshakeFinished || !serverHandshakeFinished);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isHandshaking(SSLEngineResult result) {
|
private static boolean isHandshakeFinished(SSLEngineResult result) {
|
||||||
return result.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING &&
|
return result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED;
|
||||||
result.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.FINISHED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) {
|
private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) {
|
||||||
@ -994,4 +1040,46 @@ public abstract class SSLEngineTest {
|
|||||||
cleanupServerSslEngine(server);
|
cleanupServerSslEngine(server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPacketBufferSizeLimit() throws Exception {
|
||||||
|
SelfSignedCertificate cert = new SelfSignedCertificate();
|
||||||
|
|
||||||
|
clientSslCtx = SslContextBuilder
|
||||||
|
.forClient()
|
||||||
|
.trustManager(cert.cert())
|
||||||
|
.sslProvider(sslClientProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
||||||
|
|
||||||
|
serverSslCtx = SslContextBuilder
|
||||||
|
.forServer(cert.certificate(), cert.privateKey())
|
||||||
|
.sslProvider(sslServerProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Allocate an buffer that is bigger then the max plain record size.
|
||||||
|
ByteBuffer plainServerOut = ByteBuffer.allocate(server.getSession().getApplicationBufferSize() * 2);
|
||||||
|
|
||||||
|
handshake(client, server);
|
||||||
|
|
||||||
|
// Fill the whole buffer and flip it.
|
||||||
|
plainServerOut.position(plainServerOut.capacity());
|
||||||
|
plainServerOut.flip();
|
||||||
|
|
||||||
|
ByteBuffer encryptedServerToClient = ByteBuffer.allocate(server.getSession().getPacketBufferSize());
|
||||||
|
|
||||||
|
int encryptedServerToClientPos = encryptedServerToClient.position();
|
||||||
|
int plainServerOutPos = plainServerOut.position();
|
||||||
|
SSLEngineResult result = server.wrap(plainServerOut, encryptedServerToClient);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(plainServerOut.position() - plainServerOutPos, result.bytesConsumed());
|
||||||
|
assertEquals(encryptedServerToClient.position() - encryptedServerToClientPos, result.bytesProduced());
|
||||||
|
} finally {
|
||||||
|
cleanupClientSslEngine(client);
|
||||||
|
cleanupServerSslEngine(server);
|
||||||
|
cert.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user