OpenSslEngine wrap may not consume all data

Motivation:
If the OpenSslEngine has bytes pending in the non-application buffer and also generates wrapped data during the handshake then the handshake data will be missed. This will lead to a handshake stall and eventually timeout. This can occur if the non-application buffer becomes full due to a large certificate/hello message.

Modification:
- ReferenceCountedOpenSslEngine should not assume if no data is flushed from the non-application buffer that no data will be produced by the handshake.

Result:
New unit tests with larger certificate chains don't fail.
This commit is contained in:
Scott Mitchell 2017-02-14 17:24:03 -08:00 committed by Norman Maurer
parent c521c72178
commit d60e37cb3d

View File

@ -483,39 +483,37 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
// Flush any data that may have been written implicitly during the handshake by OpenSSL.
bytesProduced = SSL.bioFlushByteBuffer(networkBIO);
// Check to see if the engine wrote data into the network BIO.
if (bytesProduced > 0) {
if (bytesProduced > 0 && handshakeException != null) {
// We produced / consumed some data during the handshake, signal back to the caller.
// If there is a handshake exception and we have produced data, we should send the data before
// we allow handshake() to throw the handshake exception.
if (handshakeException == null) {
status = handshake();
if (renegotiationPending && status == FINISHED) {
// If renegotiationPending is true that means when we attempted to start renegotiation
// the BIO buffer didn't have enough space to hold the HelloRequest which prompts the
// client to initiate a renegotiation. At this point the HelloRequest has been written
// so we can actually start the handshake process.
renegotiationPending = false;
SSL.setState(ssl, SSL.SSL_ST_ACCEPT);
handshakeState = HandshakeState.STARTED_EXPLICITLY;
status = handshake();
}
// Handshake may have generated more data, for example if the internal SSL buffer is small
// we may have freed up space by flushing above.
bytesProduced = bioLengthBefore - SSL.bioLengthByteBuffer(networkBIO);
// It's important we call this before wrapStatus() as wrapStatus() may shutdown the engine.
return newResult(mayFinishHandshake(status != FINISHED ?
getHandshakeStatus(SSL.bioLengthNonApplication(networkBIO)) : FINISHED),
0, bytesProduced);
}
return newResult(NEED_WRAP, 0, bytesProduced);
}
status = handshake();
if (renegotiationPending && status == FINISHED) {
// If renegotiationPending is true that means when we attempted to start renegotiation
// the BIO buffer didn't have enough space to hold the HelloRequest which prompts the
// client to initiate a renegotiation. At this point the HelloRequest has been written
// so we can actually start the handshake process.
renegotiationPending = false;
SSL.setState(ssl, SSL.SSL_ST_ACCEPT);
handshakeState = HandshakeState.STARTED_EXPLICITLY;
status = handshake();
}
// Handshake may have generated more data, for example if the internal SSL buffer is small
// we may have freed up space by flushing above.
bytesProduced = bioLengthBefore - SSL.bioLengthByteBuffer(networkBIO);
if (bytesProduced > 0) {
// It's important we call this before wrapStatus() as wrapStatus() may shutdown the engine.
return newResult(mayFinishHandshake(status != FINISHED ?
getHandshakeStatus(SSL.bioLengthNonApplication(networkBIO)) : FINISHED),
0, bytesProduced);
}
if (status == NEED_UNWRAP) {
// Signal if the outbound is done or not.
return isOutboundDone() ? NEED_UNWRAP_CLOSED : NEED_UNWRAP_OK;