-
Notifications
You must be signed in to change notification settings - Fork 42
unwrap() Client-side pre-check in SSLEngine to fix BUFFER_UNDERFLOW #352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1459,10 +1459,46 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && | |||||||||||||||||||||||||||||||||||||||
| prevSessionTicketCount = this.sessionTicketCount; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| boolean hsUnderflow = false; | ||||||||||||||||||||||||||||||||||||||||
| if (this.handshakeFinished == false) { | ||||||||||||||||||||||||||||||||||||||||
| WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, | ||||||||||||||||||||||||||||||||||||||||
| () -> "starting or continuing handshake"); | ||||||||||||||||||||||||||||||||||||||||
| ret = DoHandshake(false); | ||||||||||||||||||||||||||||||||||||||||
| /* Client mode: pre-check record header to return | ||||||||||||||||||||||||||||||||||||||||
| * BUFFER_UNDERFLOW with bytesConsumed == 0 per | ||||||||||||||||||||||||||||||||||||||||
| * the JSSE spec. Bogus headers fall through to | ||||||||||||||||||||||||||||||||||||||||
| * wolfSSL. Server mode is excluded: wolfSSL buffers | ||||||||||||||||||||||||||||||||||||||||
| * partial records via the I/O callback and returns | ||||||||||||||||||||||||||||||||||||||||
| * WANT_READ, mapping to OK + NEED_UNWRAP. Returning | ||||||||||||||||||||||||||||||||||||||||
| * BUFFER_UNDERFLOW server-side would break the | ||||||||||||||||||||||||||||||||||||||||
| * SSLEngineExplorer pattern, whose BUFFER_UNDERFLOW | ||||||||||||||||||||||||||||||||||||||||
| * handler reads from the socket, not the pre-read | ||||||||||||||||||||||||||||||||||||||||
| * ClientHello buffer. */ | ||||||||||||||||||||||||||||||||||||||||
| if (this.getUseClientMode() && | ||||||||||||||||||||||||||||||||||||||||
| inRemaining > 0 && | ||||||||||||||||||||||||||||||||||||||||
| (this.ssl.dtls() == 0)) { | ||||||||||||||||||||||||||||||||||||||||
| synchronized (netDataLock) { | ||||||||||||||||||||||||||||||||||||||||
| int pos = in.position(); | ||||||||||||||||||||||||||||||||||||||||
| if (inRemaining < TLS_RECORD_HEADER_LEN) { | ||||||||||||||||||||||||||||||||||||||||
| hsUnderflow = true; | ||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||
| int recLen = | ||||||||||||||||||||||||||||||||||||||||
| ((in.get(pos + TLS_RECORD_LEN_HI_OFF) | ||||||||||||||||||||||||||||||||||||||||
| & 0xFF) << 8) | | ||||||||||||||||||||||||||||||||||||||||
| (in.get(pos + TLS_RECORD_LEN_LO_OFF) | ||||||||||||||||||||||||||||||||||||||||
| & 0xFF); | ||||||||||||||||||||||||||||||||||||||||
| if (recLen <= WolfSSL.MAX_RECORD_SIZE | ||||||||||||||||||||||||||||||||||||||||
| && inRemaining < | ||||||||||||||||||||||||||||||||||||||||
| TLS_RECORD_HEADER_LEN + recLen) { | ||||||||||||||||||||||||||||||||||||||||
| hsUnderflow = true; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
rlm2002 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||
| if (hsUnderflow) { | ||||||||||||||||||||||||||||||||||||||||
| status = SSLEngineResult.Status.BUFFER_UNDERFLOW; | ||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||
| WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, | ||||||||||||||||||||||||||||||||||||||||
| () -> "starting or continuing handshake"); | ||||||||||||||||||||||||||||||||||||||||
| ret = DoHandshake(false); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1474
to
+1501
|
||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||||||||||||||||
| /* TLS-only: peek at the record header and | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1488,8 +1524,9 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && | |||||||||||||||||||||||||||||||||||||||
| & 0xFF) << 8) | | ||||||||||||||||||||||||||||||||||||||||
| (in.get(pos + TLS_RECORD_LEN_LO_OFF) | ||||||||||||||||||||||||||||||||||||||||
| & 0xFF); | ||||||||||||||||||||||||||||||||||||||||
| if (inRemaining < | ||||||||||||||||||||||||||||||||||||||||
| TLS_RECORD_HEADER_LEN + recLen) { | ||||||||||||||||||||||||||||||||||||||||
| if (recLen <= WolfSSL.MAX_RECORD_SIZE | ||||||||||||||||||||||||||||||||||||||||
| && inRemaining < | ||||||||||||||||||||||||||||||||||||||||
| TLS_RECORD_HEADER_LEN + recLen) { | ||||||||||||||||||||||||||||||||||||||||
| bufferUnderflow = true; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1527
to
1531
|
||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1527,11 +1564,13 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && | |||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||||||||||||||||
| /* Still not enough output space */ | ||||||||||||||||||||||||||||||||||||||||
| status = SSLEngineResult.Status.BUFFER_OVERFLOW; | ||||||||||||||||||||||||||||||||||||||||
| status = | ||||||||||||||||||||||||||||||||||||||||
| SSLEngineResult.Status.BUFFER_OVERFLOW; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| else if (bufferUnderflow) { | ||||||||||||||||||||||||||||||||||||||||
| status = SSLEngineResult.Status.BUFFER_UNDERFLOW; | ||||||||||||||||||||||||||||||||||||||||
| status = | ||||||||||||||||||||||||||||||||||||||||
| SSLEngineResult.Status.BUFFER_UNDERFLOW; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| /* If we have input data, make sure output buffer | ||||||||||||||||||||||||||||||||||||||||
| * length is greater than zero, otherwise ask app to | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1616,7 +1655,8 @@ else if (inRemaining > 0 && | |||||||||||||||||||||||||||||||||||||||
| (ssl.getError(0) == 0) && | ||||||||||||||||||||||||||||||||||||||||
| !this.sessionStored) { | ||||||||||||||||||||||||||||||||||||||||
| WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, | ||||||||||||||||||||||||||||||||||||||||
| () -> "calling engineHelper.saveSession()"); | ||||||||||||||||||||||||||||||||||||||||
| () -> | ||||||||||||||||||||||||||||||||||||||||
| "calling engineHelper.saveSession()"); | ||||||||||||||||||||||||||||||||||||||||
| int ret2 = this.engineHelper.saveSession(); | ||||||||||||||||||||||||||||||||||||||||
| WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, | ||||||||||||||||||||||||||||||||||||||||
| () -> "return from saveSession(), ret = " + | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1628,123 +1668,129 @@ else if (inRemaining > 0 && | |||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } /* end DoHandshake() / RecvAppData() */ | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| if (outBoundOpen == false || this.closeNotifySent || | ||||||||||||||||||||||||||||||||||||||||
| this.closeNotifyReceived) { | ||||||||||||||||||||||||||||||||||||||||
| /* Mark SSLEngine status as CLOSED */ | ||||||||||||||||||||||||||||||||||||||||
| status = SSLEngineResult.Status.CLOSED; | ||||||||||||||||||||||||||||||||||||||||
| /* Handshake has finished and SSLEngine is closed, | ||||||||||||||||||||||||||||||||||||||||
| * release, global JNI verify callback pointer */ | ||||||||||||||||||||||||||||||||||||||||
| this.engineHelper.unsetVerifyCallback(); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| if (!hsUnderflow) { | ||||||||||||||||||||||||||||||||||||||||
| if (outBoundOpen == false || this.closeNotifySent || | ||||||||||||||||||||||||||||||||||||||||
| this.closeNotifyReceived) { | ||||||||||||||||||||||||||||||||||||||||
| /* Mark SSLEngine status as CLOSED */ | ||||||||||||||||||||||||||||||||||||||||
| status = SSLEngineResult.Status.CLOSED; | ||||||||||||||||||||||||||||||||||||||||
| /* Handshake has finished and SSLEngine is closed, | ||||||||||||||||||||||||||||||||||||||||
| * release, global JNI verify callback pointer */ | ||||||||||||||||||||||||||||||||||||||||
| this.engineHelper.unsetVerifyCallback(); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1671
to
1680
|
||||||||||||||||||||||||||||||||||||||||
| if (!hsUnderflow) { | |
| if (outBoundOpen == false || this.closeNotifySent || | |
| this.closeNotifyReceived) { | |
| /* Mark SSLEngine status as CLOSED */ | |
| status = SSLEngineResult.Status.CLOSED; | |
| /* Handshake has finished and SSLEngine is closed, | |
| * release, global JNI verify callback pointer */ | |
| this.engineHelper.unsetVerifyCallback(); | |
| } | |
| if (outBoundOpen == false || this.closeNotifySent || | |
| this.closeNotifyReceived) { | |
| /* Mark SSLEngine status as CLOSED */ | |
| status = SSLEngineResult.Status.CLOSED; | |
| /* Handshake has finished and SSLEngine is closed, | |
| * release, global JNI verify callback pointer */ | |
| this.engineHelper.unsetVerifyCallback(); | |
| } | |
| if (!hsUnderflow) { |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3147,9 +3147,12 @@ public void testHandshakeUnwrapConsumedNotBufferUnderflow() | |
| result = client.unwrap(firstHalf, cliAppBuf); | ||
|
|
||
| /* BUFFER_UNDERFLOW must not be returned when input was | ||
| * consumed - regression for inRemaining == 0 guard fix */ | ||
| * consumed - regression for inRemaining == 0 guard fix. | ||
| * If BUFFER_UNDERFLOW is returned, bytesConsumed() must be 0 | ||
| * per the JSSE spec ("No data was consumed"). */ | ||
| if (result.getStatus() == | ||
| SSLEngineResult.Status.BUFFER_UNDERFLOW) { | ||
| SSLEngineResult.Status.BUFFER_UNDERFLOW && | ||
| result.bytesConsumed() > 0) { | ||
|
Comment on lines
3147
to
+3155
|
||
| error("\t... failed"); | ||
| fail("unwrap() with consumed handshake data must not " + | ||
| "return BUFFER_UNDERFLOW (regression: inRemaining" + | ||
|
|
@@ -3159,7 +3162,8 @@ public void testHandshakeUnwrapConsumedNotBufferUnderflow() | |
|
|
||
| /* Input was non-empty so at least some bytes must have | ||
| * been consumed */ | ||
| if (result.bytesConsumed() == 0) { | ||
| if (result.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW | ||
| && result.bytesConsumed() == 0) { | ||
|
Comment on lines
+3165
to
+3166
|
||
| error("\t... failed"); | ||
| fail("unwrap() consumed 0 bytes from a non-empty " + | ||
| "handshake buffer"); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
hsUnderflowis detected, the method setsstatus = BUFFER_UNDERFLOWbut (from the shown hunk) does not update the handshake status to reflect “need more inbound data”. If the returnedSSLEngineResultends up carrying the priorHandshakeStatus(notablyNEED_WRAPin this surrounding branch), callers that prioritizeHandshakeStatusmay incorrectly try towrap()instead of reading more network data. Consider explicitly ensuring the result’sHandshakeStatusisNEED_UNWRAP(or otherwise consistent withBUFFER_UNDERFLOW) in thehsUnderflowpath.