Skip to content

Commit 78cef06

Browse files
committed
openssl: Revert to less sensitivity for SYSCALL errors
- Disable the extra sensitivity except in debug builds (--enable-debug). - Improve SYSCALL error message logic in ossl_send and ossl_recv so that "No error" / "Success" socket error text isn't shown on SYSCALL error. Prior to this change 0ab38f5 (precedes 7.67.0) increased the sensitivity of OpenSSL's SSL_ERROR_SYSCALL error so that abrupt server closures were also considered errors. For example, a server that does not send a known protocol termination point (eg HTTP content length or chunked encoding) _and_ does not send a TLS termination point (close_notify alert) would cause an error if it closed the connection. To be clear that behavior made it into release build 7.67.0 unintentionally. Several users have reported it as an issue. Ultimately the idea is a good one, since it can help prevent against a truncation attack. Other SSL backends may already behave similarly (such as Windows native OS SSL Schannel). However much more of our user base is using OpenSSL and there is a mass of legacy users in that space, so I think that behavior should be partially reverted and then rolled out slowly. This commit changes the behavior so that the increased sensitivity is disabled in all curl builds except curl debug builds (DEBUGBUILD). If after a period of time there are no major issues then it can be enabled in dev and release builds with the newest OpenSSL (1.1.1+), since users using the newest OpenSSL are the least likely to have legacy problems. Bug: #4409 (comment) Reported-by: Bjoern Franke Fixes #4624 Closes #4623
1 parent 1f4e7dc commit 78cef06

File tree

1 file changed

+59
-13
lines changed

1 file changed

+59
-13
lines changed

lib/vtls/openssl.c

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -392,11 +392,20 @@ static const char *SSL_ERROR_to_str(int err)
392392
*/
393393
static char *ossl_strerror(unsigned long error, char *buf, size_t size)
394394
{
395+
if(size)
396+
*buf = '\0';
397+
395398
#ifdef OPENSSL_IS_BORINGSSL
396399
ERR_error_string_n((uint32_t)error, buf, size);
397400
#else
398401
ERR_error_string_n(error, buf, size);
399402
#endif
403+
404+
if(size > 1 && !*buf) {
405+
strncpy(buf, (error ? "Unknown error" : "No error"), size);
406+
buf[size - 1] = '\0';
407+
}
408+
400409
return buf;
401410
}
402411

@@ -3833,10 +3842,22 @@ static ssize_t ossl_send(struct connectdata *conn,
38333842
*curlcode = CURLE_AGAIN;
38343843
return -1;
38353844
case SSL_ERROR_SYSCALL:
3836-
Curl_strerror(SOCKERRNO, error_buffer, sizeof(error_buffer));
3837-
failf(conn->data, OSSL_PACKAGE " SSL_write: %s", error_buffer);
3838-
*curlcode = CURLE_SEND_ERROR;
3839-
return -1;
3845+
{
3846+
int sockerr = SOCKERRNO;
3847+
sslerror = ERR_get_error();
3848+
if(sslerror)
3849+
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
3850+
else if(sockerr)
3851+
Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
3852+
else {
3853+
strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
3854+
error_buffer[sizeof(error_buffer) - 1] = '\0';
3855+
}
3856+
failf(conn->data, OSSL_PACKAGE " SSL_write: %s, errno %d",
3857+
error_buffer, sockerr);
3858+
*curlcode = CURLE_SEND_ERROR;
3859+
return -1;
3860+
}
38403861
case SSL_ERROR_SSL:
38413862
/* A failure in the SSL library occurred, usually a protocol error.
38423863
The OpenSSL error queue contains more information on the error. */
@@ -3901,11 +3922,6 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */
39013922
/* there's data pending, re-invoke SSL_read() */
39023923
*curlcode = CURLE_AGAIN;
39033924
return -1;
3904-
case SSL_ERROR_SYSCALL:
3905-
Curl_strerror(SOCKERRNO, error_buffer, sizeof(error_buffer));
3906-
failf(conn->data, OSSL_PACKAGE " SSL_read: %s", error_buffer);
3907-
*curlcode = CURLE_RECV_ERROR;
3908-
return -1;
39093925
default:
39103926
/* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
39113927
value/errno" */
@@ -3914,14 +3930,44 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */
39143930
if((nread < 0) || sslerror) {
39153931
/* If the return code was negative or there actually is an error in the
39163932
queue */
3933+
int sockerr = SOCKERRNO;
3934+
if(sslerror)
3935+
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
3936+
else if(sockerr && err == SSL_ERROR_SYSCALL)
3937+
Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
3938+
else {
3939+
strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
3940+
error_buffer[sizeof(error_buffer) - 1] = '\0';
3941+
}
39173942
failf(conn->data, OSSL_PACKAGE " SSL_read: %s, errno %d",
3918-
(sslerror ?
3919-
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)) :
3920-
SSL_ERROR_to_str(err)),
3921-
SOCKERRNO);
3943+
error_buffer, sockerr);
3944+
*curlcode = CURLE_RECV_ERROR;
3945+
return -1;
3946+
}
3947+
/* For debug builds be a little stricter and error on any
3948+
SSL_ERROR_SYSCALL. For example a server may have closed the connection
3949+
abruptly without a close_notify alert. For compatibility with older
3950+
peers we don't do this by default. #4624
3951+
3952+
We can use this to gauge how many users may be affected, and
3953+
if it goes ok eventually transition to allow in dev and release with
3954+
the newest OpenSSL: #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) */
3955+
#ifdef DEBUGBUILD
3956+
if(err == SSL_ERROR_SYSCALL) {
3957+
int sockerr = SOCKERRNO;
3958+
if(sockerr)
3959+
Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
3960+
else {
3961+
msnprintf(error_buffer, sizeof(error_buffer),
3962+
"Connection closed abruptly");
3963+
}
3964+
failf(conn->data, OSSL_PACKAGE " SSL_read: %s, errno %d"
3965+
" (Fatal because this is a curl debug build)",
3966+
error_buffer, sockerr);
39223967
*curlcode = CURLE_RECV_ERROR;
39233968
return -1;
39243969
}
3970+
#endif
39253971
}
39263972
}
39273973
return nread;

0 commit comments

Comments
 (0)