use of javax.net.ssl.SSLEngineResult in project jersey by jersey.
the class SslFilter method close.
@Override
synchronized void close() {
if (state == State.NOT_STARTED) {
downstreamFilter.close();
return;
}
sslEngine.closeOutbound();
try {
LazyBuffer lazyBuffer = new LazyBuffer();
while (sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
ByteBuffer buffer = lazyBuffer.get();
SSLEngineResult result = sslEngine.wrap(emptyBuffer, buffer);
switch(result.getStatus()) {
case BUFFER_OVERFLOW:
{
lazyBuffer.resize();
break;
}
case BUFFER_UNDERFLOW:
{
/* This basically says that there is not enough data to create an SSL packet. Javadoc suggests that
BUFFER_UNDERFLOW can occur only after unwrap(), but to be 100% sure we handle all possible error
states: */
throw new IllegalStateException("SSL engine underflow while close operation \n" + getDebugState());
}
}
}
if (lazyBuffer.isAllocated()) {
ByteBuffer buffer = lazyBuffer.get();
buffer.flip();
writeQueue.write(buffer, new CompletionHandler<ByteBuffer>() {
@Override
public void completed(ByteBuffer result) {
downstreamFilter.close();
}
@Override
public void failed(Throwable throwable) {
downstreamFilter.close();
}
});
} else {
// make sure we close even if SSL had nothing to send
downstreamFilter.close();
}
} catch (Exception e) {
handleSslError(e);
}
}
use of javax.net.ssl.SSLEngineResult in project jersey by jersey.
the class SslFilter method handleWrite.
private void handleWrite(final ByteBuffer applicationData, final CompletionHandler<ByteBuffer> completionHandler) {
try {
// transport buffer always writes all data, so there are not leftovers in the networkOutputBuffer
networkOutputBuffer.clear();
SSLEngineResult result = sslEngine.wrap(applicationData, networkOutputBuffer);
switch(result.getStatus()) {
case BUFFER_OVERFLOW:
{
/* this means that the content of the ssl packet (max 16kB) did not fit into
networkOutputBuffer, we make sure to set networkOutputBuffer > max 16kB + SSL headers
when initializing this filter. This indicates a bug. */
throw new IllegalStateException("SSL packet does not fit into the network buffer: " + networkOutputBuffer + "\n" + getDebugState());
}
case BUFFER_UNDERFLOW:
{
/* This basically says that there is not enough data to create an SSL packet. Javadoc suggests that
BUFFER_UNDERFLOW can occur only after unwrap(), but to be 100% sure we handle all possible error states: */
throw new IllegalStateException("SSL engine underflow with the following application input: " + applicationData + "\n" + getDebugState());
}
case CLOSED:
{
state = State.CLOSED;
break;
}
case OK:
{
// check if we started re-handshaking
if (result.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
state = State.REHANDSHAKING;
}
networkOutputBuffer.flip();
// write only if something was written to the output buffer
if (networkOutputBuffer.hasRemaining()) {
writeQueue.write(networkOutputBuffer, new CompletionHandler<ByteBuffer>() {
@Override
public void completed(ByteBuffer result) {
handlePostWrite(applicationData, completionHandler);
}
@Override
public void failed(Throwable throwable) {
completionHandler.failed(throwable);
}
});
} else {
handlePostWrite(applicationData, completionHandler);
}
break;
}
}
} catch (SSLException e) {
handleSslError(e);
}
}
use of javax.net.ssl.SSLEngineResult in project netty by netty.
the class ReferenceCountedOpenSslEngine method wrap.
@Override
public final SSLEngineResult wrap(final ByteBuffer[] srcs, int offset, final int length, final ByteBuffer dst) throws SSLException {
// Throw required runtime exceptions
if (srcs == null) {
throw new IllegalArgumentException("srcs is null");
}
if (dst == null) {
throw new IllegalArgumentException("dst is null");
}
if (offset >= srcs.length || offset + length > srcs.length) {
throw new IndexOutOfBoundsException("offset: " + offset + ", length: " + length + " (expected: offset <= offset + length <= srcs.length (" + srcs.length + "))");
}
if (dst.isReadOnly()) {
throw new ReadOnlyBufferException();
}
synchronized (this) {
if (isOutboundDone()) {
// All drained in the outbound buffer
return isInboundDone() || isDestroyed() ? CLOSED_NOT_HANDSHAKING : NEED_UNWRAP_CLOSED;
}
int bytesProduced = 0;
ByteBuf bioReadCopyBuf = null;
try {
// Setup the BIO buffer so that we directly write the encryption results into dst.
if (dst.isDirect()) {
SSL.bioSetByteBuffer(networkBIO, Buffer.address(dst) + dst.position(), dst.remaining(), true);
} else {
bioReadCopyBuf = alloc.directBuffer(dst.remaining());
SSL.bioSetByteBuffer(networkBIO, memoryAddress(bioReadCopyBuf), bioReadCopyBuf.writableBytes(), true);
}
int bioLengthBefore = SSL.bioLengthByteBuffer(networkBIO);
// Explicit use outboundClosed as we want to drain any bytes that are still present.
if (outboundClosed) {
// There is something left to drain.
// See https://github.com/netty/netty/issues/6260
bytesProduced = SSL.bioFlushByteBuffer(networkBIO);
if (bytesProduced <= 0) {
return newResultMayFinishHandshake(NOT_HANDSHAKING, 0, 0);
}
// OpenSSL can give us.
if (!doSSLShutdown()) {
return newResultMayFinishHandshake(NOT_HANDSHAKING, 0, bytesProduced);
}
bytesProduced = bioLengthBefore - SSL.bioLengthByteBuffer(networkBIO);
return newResultMayFinishHandshake(NEED_WRAP, 0, bytesProduced);
}
// Flush any data that may be implicitly generated by OpenSSL (handshake, close, etc..).
SSLEngineResult.HandshakeStatus status = NOT_HANDSHAKING;
// Prepare OpenSSL to work in server mode and receive handshake
if (handshakeState != HandshakeState.FINISHED) {
if (handshakeState != HandshakeState.STARTED_EXPLICITLY) {
// Update accepted so we know we triggered the handshake via wrap
handshakeState = HandshakeState.STARTED_IMPLICITLY;
}
// Flush any data that may have been written implicitly during the handshake by OpenSSL.
bytesProduced = SSL.bioFlushByteBuffer(networkBIO);
if (bytesProduced > 0 && handshakeException != null) {
// we allow handshake() to throw the handshake exception.
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;
}
// still present.
if (outboundClosed) {
bytesProduced = SSL.bioFlushByteBuffer(networkBIO);
return newResultMayFinishHandshake(status, 0, bytesProduced);
}
}
int srcsLen = 0;
final int endOffset = offset + length;
for (int i = offset; i < endOffset; ++i) {
final ByteBuffer src = srcs[i];
if (src == null) {
throw new IllegalArgumentException("srcs[" + i + "] is null");
}
if (srcsLen == MAX_PLAINTEXT_LENGTH) {
continue;
}
srcsLen += src.remaining();
if (srcsLen > MAX_PLAINTEXT_LENGTH || srcsLen < 0) {
// If srcLen > MAX_PLAINTEXT_LENGTH or secLen < 0 just set it to MAX_PLAINTEXT_LENGTH.
// This also help us to guard against overflow.
// We not break out here as we still need to check for null entries in srcs[].
srcsLen = MAX_PLAINTEXT_LENGTH;
}
}
if (dst.remaining() < calculateOutNetBufSize(srcsLen, endOffset - offset)) {
// buffer.
return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0);
}
// There was no pending data in the network BIO -- encrypt any application data
int bytesConsumed = 0;
// Flush any data that may have been written implicitly by OpenSSL in case a shutdown/alert occurs.
bytesProduced = SSL.bioFlushByteBuffer(networkBIO);
for (; offset < endOffset; ++offset) {
final ByteBuffer src = srcs[offset];
final int remaining = src.remaining();
if (remaining == 0) {
continue;
}
// Write plaintext application data to the SSL engine
int bytesWritten = writePlaintextData(src, min(remaining, MAX_PLAINTEXT_LENGTH - bytesConsumed));
if (bytesWritten > 0) {
bytesConsumed += bytesWritten;
// Determine how much encrypted data was generated:
final int pendingNow = SSL.bioLengthByteBuffer(networkBIO);
bytesProduced += bioLengthBefore - pendingNow;
bioLengthBefore = pendingNow;
if (bytesConsumed == MAX_PLAINTEXT_LENGTH || bytesProduced == dst.remaining()) {
return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced);
}
} else {
int sslError = SSL.getError(ssl, bytesWritten);
if (sslError == SSL.SSL_ERROR_ZERO_RETURN) {
// This means the connection was shutdown correctly, close inbound and outbound
if (!receivedShutdown) {
closeAll();
bytesProduced += bioLengthBefore - SSL.bioLengthByteBuffer(networkBIO);
SSLEngineResult.HandshakeStatus hs = mayFinishHandshake(status != FINISHED ? getHandshakeStatus(SSL.bioLengthNonApplication(networkBIO)) : FINISHED);
return newResult(hs, bytesConsumed, bytesProduced);
}
return newResult(NOT_HANDSHAKING, bytesConsumed, bytesProduced);
} else if (sslError == SSL.SSL_ERROR_WANT_READ) {
// been closed. [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html
return newResult(NEED_UNWRAP, bytesConsumed, bytesProduced);
} else if (sslError == SSL.SSL_ERROR_WANT_WRITE) {
// [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html
return newResult(NEED_WRAP, bytesConsumed, bytesProduced);
} else {
// Everything else is considered as error
throw shutdownWithError("SSL_write");
}
}
}
return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced);
} finally {
SSL.bioClearByteBuffer(networkBIO);
if (bioReadCopyBuf == null) {
dst.position(dst.position() + bytesProduced);
} else {
assert bioReadCopyBuf.readableBytes() <= dst.remaining() : "The destination buffer " + dst + " didn't have enough remaining space to hold the encrypted content in " + bioReadCopyBuf;
dst.put(bioReadCopyBuf.internalNioBuffer(bioReadCopyBuf.readerIndex(), bytesProduced));
bioReadCopyBuf.release();
}
}
}
}
use of javax.net.ssl.SSLEngineResult in project netty by netty.
the class ReferenceCountedOpenSslEngine method unwrap.
public final SSLEngineResult unwrap(final ByteBuffer[] srcs, int srcsOffset, final int srcsLength, final ByteBuffer[] dsts, int dstsOffset, final int dstsLength) throws SSLException {
// Throw required runtime exceptions
if (srcs == null) {
throw new NullPointerException("srcs");
}
if (srcsOffset >= srcs.length || srcsOffset + srcsLength > srcs.length) {
throw new IndexOutOfBoundsException("offset: " + srcsOffset + ", length: " + srcsLength + " (expected: offset <= offset + length <= srcs.length (" + srcs.length + "))");
}
if (dsts == null) {
throw new IllegalArgumentException("dsts is null");
}
if (dstsOffset >= dsts.length || dstsOffset + dstsLength > dsts.length) {
throw new IndexOutOfBoundsException("offset: " + dstsOffset + ", length: " + dstsLength + " (expected: offset <= offset + length <= dsts.length (" + dsts.length + "))");
}
long capacity = 0;
final int dstsEndOffset = dstsOffset + dstsLength;
for (int i = dstsOffset; i < dstsEndOffset; i++) {
ByteBuffer dst = dsts[i];
if (dst == null) {
throw new IllegalArgumentException("dsts[" + i + "] is null");
}
if (dst.isReadOnly()) {
throw new ReadOnlyBufferException();
}
capacity += dst.remaining();
}
final int srcsEndOffset = srcsOffset + srcsLength;
long len = 0;
for (int i = srcsOffset; i < srcsEndOffset; i++) {
ByteBuffer src = srcs[i];
if (src == null) {
throw new IllegalArgumentException("srcs[" + i + "] is null");
}
len += src.remaining();
}
synchronized (this) {
if (isInboundDone()) {
return isOutboundDone() || isDestroyed() ? CLOSED_NOT_HANDSHAKING : NEED_WRAP_CLOSED;
}
SSLEngineResult.HandshakeStatus status = NOT_HANDSHAKING;
// Prepare OpenSSL to work in server mode and receive handshake
if (handshakeState != HandshakeState.FINISHED) {
if (handshakeState != HandshakeState.STARTED_EXPLICITLY) {
// Update accepted so we know we triggered the handshake via wrap
handshakeState = HandshakeState.STARTED_IMPLICITLY;
}
status = handshake();
if (status == NEED_WRAP) {
return NEED_WRAP_OK;
}
// Check if the inbound is considered to be closed if so let us try to wrap again.
if (isInboundDone) {
return NEED_WRAP_CLOSED;
}
}
if (len < SSL_RECORD_HEADER_LENGTH) {
return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0);
}
int packetLength = SslUtils.getEncryptedPacketLength(srcs, srcsOffset);
if (packetLength == SslUtils.NOT_ENCRYPTED) {
throw new NotSslRecordException("not an SSL/TLS record");
}
if (packetLength - SSL_RECORD_HEADER_LENGTH > capacity) {
// that the buffer needs to be increased.
return newResultMayFinishHandshake(BUFFER_OVERFLOW, status, 0, 0);
}
if (len < packetLength) {
// the whole packet.
return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0);
}
// This must always be the case when we reached here as if not we returned BUFFER_UNDERFLOW.
assert srcsOffset < srcsEndOffset;
// This must always be the case if we reached here.
assert capacity > 0;
// Number of produced bytes
int bytesProduced = 0;
int bytesConsumed = 0;
try {
for (; srcsOffset < srcsEndOffset; ++srcsOffset) {
ByteBuffer src = srcs[srcsOffset];
int remaining = src.remaining();
if (remaining == 0) {
// with length 0.
continue;
}
// Write more encrypted data into the BIO. Ensure we only read one packet at a time as
// stated in the SSLEngine javadocs.
int pendingEncryptedBytes = min(packetLength, remaining);
ByteBuf bioWriteCopyBuf = writeEncryptedData(src, pendingEncryptedBytes);
try {
readLoop: for (; dstsOffset < dstsEndOffset; ++dstsOffset) {
ByteBuffer dst = dsts[dstsOffset];
if (!dst.hasRemaining()) {
// No space left in the destination buffer, skip it.
continue;
}
int bytesRead = readPlaintextData(dst);
// We are directly using the ByteBuffer memory for the write, and so we only know what
// has been consumed after we let SSL decrypt the data. At this point we should update
// the number of bytes consumed, update the ByteBuffer position, and release temp
// ByteBuf.
int localBytesConsumed = pendingEncryptedBytes - SSL.bioLengthByteBuffer(networkBIO);
bytesConsumed += localBytesConsumed;
packetLength -= localBytesConsumed;
pendingEncryptedBytes -= localBytesConsumed;
src.position(src.position() + localBytesConsumed);
if (bytesRead > 0) {
bytesProduced += bytesRead;
if (!dst.hasRemaining()) {
// Move to the next dst buffer as this one is full.
continue;
}
if (packetLength == 0) {
// We read everything return now.
return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, bytesConsumed, bytesProduced);
}
// try to write again to the BIO. stop reading from it by break out of the readLoop.
break;
} else {
int sslError = SSL.getError(ssl, bytesRead);
if (sslError == SSL.SSL_ERROR_WANT_READ || sslError == SSL.SSL_ERROR_WANT_WRITE) {
// write more to the BIO.
break readLoop;
} else if (sslError == SSL.SSL_ERROR_ZERO_RETURN) {
// This means the connection was shutdown correctly, close inbound and outbound
if (!receivedShutdown) {
closeAll();
}
return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, bytesConsumed, bytesProduced);
} else {
return sslReadErrorResult(SSL.getLastErrorNumber(), bytesConsumed, bytesProduced);
}
}
}
// Either we have no more dst buffers to put the data, or no more data to generate; we are done.
if (dstsOffset >= dstsEndOffset || packetLength == 0) {
break;
}
} finally {
if (bioWriteCopyBuf != null) {
bioWriteCopyBuf.release();
}
}
}
} finally {
SSL.bioClearByteBuffer(networkBIO);
rejectRemoteInitiatedRenegation();
}
// Check to see if we received a close_notify message from the peer.
if (!receivedShutdown && (SSL.getShutdown(ssl) & SSL.SSL_RECEIVED_SHUTDOWN) == SSL.SSL_RECEIVED_SHUTDOWN) {
closeAll();
}
return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, bytesConsumed, bytesProduced);
}
}
use of javax.net.ssl.SSLEngineResult in project netty by netty.
the class SslHandler method wrapNonAppData.
/**
* This method will not call
* {@link #setHandshakeFailure(ChannelHandlerContext, Throwable, boolean)} or
* {@link #setHandshakeFailure(ChannelHandlerContext, Throwable)}.
* @return {@code true} if this method ends on {@link SSLEngineResult.HandshakeStatus#NOT_HANDSHAKING}.
*/
private boolean wrapNonAppData(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException {
ByteBuf out = null;
ByteBufAllocator alloc = ctx.alloc();
try {
// See https://github.com/netty/netty/issues/5860
while (!ctx.isRemoved()) {
if (out == null) {
// As this is called for the handshake we have no real idea how big the buffer needs to be.
// That said 2048 should give us enough room to include everything like ALPN / NPN data.
// If this is not enough we will increase the buffer in wrap(...).
out = allocateOutNetBuf(ctx, 2048, 1);
}
SSLEngineResult result = wrap(alloc, engine, Unpooled.EMPTY_BUFFER, out);
if (result.bytesProduced() > 0) {
ctx.write(out);
if (inUnwrap) {
needsFlush = true;
}
out = null;
}
switch(result.getHandshakeStatus()) {
case FINISHED:
setHandshakeSuccess();
return false;
case NEED_TASK:
runDelegatedTasks();
break;
case NEED_UNWRAP:
if (!inUnwrap) {
unwrapNonAppData(ctx);
}
break;
case NEED_WRAP:
break;
case NOT_HANDSHAKING:
setHandshakeSuccessIfStillHandshaking();
// https://github.com/netty/netty/issues/1108#issuecomment-14266970
if (!inUnwrap) {
unwrapNonAppData(ctx);
}
return true;
default:
throw new IllegalStateException("Unknown handshake status: " + result.getHandshakeStatus());
}
if (result.bytesProduced() == 0) {
break;
}
// Fix for Android, where it was encrypting empty buffers even when not handshaking
if (result.bytesConsumed() == 0 && result.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
break;
}
}
} finally {
if (out != null) {
out.release();
}
}
return false;
}
Aggregations