Merge pull request #3911 from TakayukiMatsuo/tk11851

Fix SSL_read behaving differently from openSSL after bidirectional shutdown
This commit is contained in:
Sean Parkinson
2021-06-11 10:25:39 +10:00
committed by GitHub
2 changed files with 281 additions and 1 deletions

View File

@ -2053,6 +2053,30 @@ static int wolfSSL_read_internal(WOLFSSL* ssl, void* data, int sz, int peek)
if (ssl == NULL || data == NULL || sz < 0)
return BAD_FUNC_ARG;
#if defined(WOLFSSL_ERROR_CODE_OPENSSL) && defined(OPENSSL_EXTRA)
/* This additional logic is meant to simulate following openSSL behavior:
* After bidirectional SSL_shutdown complete, SSL_read returns 0 and
* SSL_get_error_code returns SSL_ERROR_ZERO_RETURN.
* This behavior is used to know the disconnect of the underlying
* transport layer.
*
* In this logic, CBIORecv is called with a read size of 0 to check the
* transport layer status. It also returns WOLFSSL_FAILURE so that
* SSL_read does not return a positive number on failure.
*/
/* make sure bidirectional TLS shutdown completes */
if (ssl->error == WOLFSSL_ERROR_SYSCALL) {
/* ask the underlying transport the connection is closed */
if (ssl->CBIORecv(ssl, (char*)data, 0, ssl->IOCB_ReadCtx) ==
WOLFSSL_CBIO_ERR_CONN_CLOSE) {
ssl->options.isClosed = 1;
ssl->error = WOLFSSL_ERROR_ZERO_RETURN;
}
return WOLFSSL_FAILURE;
}
#endif
#ifdef HAVE_WRITE_DUP
if (ssl->dupWrite && ssl->dupSide == WRITE_DUP_SIDE) {
WOLFSSL_MSG("Write dup side cannot read");

View File

@ -42987,6 +42987,262 @@ static int test_wolfSSL_CTX_set_ecdh_auto(void)
(void)ctx;
return ret;
}
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_ERROR_CODE_OPENSSL)
static THREAD_RETURN WOLFSSL_THREAD SSL_read_test_server_thread(void* args)
{
callback_functions* callbacks = NULL;
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;
SOCKET_T sfd = 0;
SOCKET_T cfd = 0;
word16 port;
char msg[] = "I hear you fa shizzle!";
int len = (int) XSTRLEN(msg);
char input[1024];
int ret, err;
if (!args)
return 0;
((func_args*)args)->return_code = TEST_FAIL;
callbacks = ((func_args*)args)->callbacks;
ctx = wolfSSL_CTX_new(callbacks->method());
#if defined(USE_WINDOWS_API)
port = ((func_args*)args)->signal->port;
#else
/* Let tcp_listen assign port */
port = 0;
#endif
#ifdef WOLFSSL_TIRTOS
fdOpenSession(Task_self());
#endif
AssertIntEQ(WOLFSSL_SUCCESS,
wolfSSL_CTX_load_verify_locations(ctx, caCertFile, 0));
AssertIntEQ(WOLFSSL_SUCCESS,
wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
WOLFSSL_FILETYPE_PEM));
AssertIntEQ(WOLFSSL_SUCCESS,
wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
WOLFSSL_FILETYPE_PEM));
if (callbacks->ctx_ready)
callbacks->ctx_ready(ctx);
ssl = wolfSSL_new(ctx);
AssertNotNull(ssl);
/* listen and accept */
tcp_accept(&sfd, &cfd, (func_args*)args, port, 0, 0, 0, 0, 1);
CloseSocket(sfd);
AssertIntEQ(WOLFSSL_SUCCESS, wolfSSL_set_fd(ssl, cfd));
if (callbacks->ssl_ready)
callbacks->ssl_ready(ssl);
do {
err = 0; /* Reset error */
ret = wolfSSL_accept(ssl);
if (ret != WOLFSSL_SUCCESS) {
err = wolfSSL_get_error(ssl, 0);
}
} while (ret != WOLFSSL_SUCCESS && err == WC_PENDING_E);
if (ret != WOLFSSL_SUCCESS) {
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
CloseSocket(cfd);
((func_args*)args)->return_code = TEST_FAIL;
return 0;
}
/* read and write data */
XMEMSET( input, 0, sizeof(input));
while (1) {
ret = wolfSSL_read(ssl, input, sizeof(input));
if (ret > 0) {
break;
}
else {
err = wolfSSL_get_error(ssl,ret);
if (err == WOLFSSL_ERROR_WANT_READ) {
continue;
}
break;
}
}
if (err == WOLFSSL_ERROR_ZERO_RETURN) {
do {
ret = wolfSSL_write(ssl, msg, len);
if (ret > 0) {
break;
}
} while (ret < 0);
}
/* bidirectional shutdown */
while ((ret = wolfSSL_shutdown(ssl)) != WOLFSSL_SUCCESS) {
continue;
}
/* wait for the peer to disconnect the tcp connection */
do {
ret = wolfSSL_read(ssl, input, sizeof(input));
err = wolfSSL_get_error(ssl, ret);
} while (ret > 0 || err != WOLFSSL_ERROR_ZERO_RETURN);
/* detect TCP disconnect */
AssertIntLE(ret,WOLFSSL_FAILURE);
AssertIntEQ(wolfSSL_get_error(ssl, ret), WOLFSSL_ERROR_ZERO_RETURN);
((func_args*)args)->return_code = TEST_SUCCESS;
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
CloseSocket(cfd);
return 0;
}
static THREAD_RETURN WOLFSSL_THREAD SSL_read_test_client_thread(void* args)
{
callback_functions* callbacks = NULL;
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;
SOCKET_T sfd = 0;
char msg[] = "hello wolfssl server!";
int len = (int) XSTRLEN(msg);
char input[1024];
int idx;
int ret, err;
if (!args)
return 0;
((func_args*)args)->return_code = TEST_FAIL;
callbacks = ((func_args*)args)->callbacks;
ctx = wolfSSL_CTX_new(callbacks->method());
#ifdef WOLFSSL_TIRTOS
fdOpenSession(Task_self());
#endif
AssertIntEQ(WOLFSSL_SUCCESS,
wolfSSL_CTX_load_verify_locations(ctx, caCertFile, 0));
AssertIntEQ(WOLFSSL_SUCCESS,
wolfSSL_CTX_use_certificate_file(ctx, cliCertFile,
WOLFSSL_FILETYPE_PEM));
AssertIntEQ(WOLFSSL_SUCCESS,
wolfSSL_CTX_use_PrivateKey_file(ctx, cliKeyFile,
WOLFSSL_FILETYPE_PEM));
AssertNotNull((ssl = wolfSSL_new(ctx)));
tcp_connect(&sfd, wolfSSLIP, ((func_args*)args)->signal->port, 0, 0, ssl);
AssertIntEQ(WOLFSSL_SUCCESS, wolfSSL_set_fd(ssl, sfd));
do {
err = 0; /* Reset error */
ret = wolfSSL_connect(ssl);
if (ret != WOLFSSL_SUCCESS) {
err = wolfSSL_get_error(ssl, 0);
}
} while (ret != WOLFSSL_SUCCESS && err == WC_PENDING_E);
ret = wolfSSL_write(ssl, msg, len);
if (0 < (idx = wolfSSL_read(ssl, input, sizeof(input)-1))) {
input[idx] = 0;
}
ret = wolfSSL_shutdown(ssl);
if ( ret == WOLFSSL_SHUTDOWN_NOT_DONE) {
ret = wolfSSL_shutdown(ssl);
}
AssertIntEQ(ret, WOLFSSL_SUCCESS);
((func_args*)args)->return_code = TEST_SUCCESS;
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
CloseSocket(sfd);
return 0;
}
#endif /* OPENSSL_EXTRA && WOLFSSL_ERROR_CODE_OPENSSL */
/* This test is to check wolfSSL_read behaves as same as
* openSSL when it is called after SSL_shutdown completes.
*/
static int test_wolfSSL_read_detect_TCP_disconnect(void)
{
int ret = 0;
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_ERROR_CODE_OPENSSL)
tcp_ready ready;
func_args client_args;
func_args server_args;
THREAD_TYPE serverThread;
THREAD_TYPE clientThread;
callback_functions server_cbf;
callback_functions client_cbf;
printf(testingFmt, "wolfSSL_read_detect_TCP_disconnect()");
#ifdef WOLFSSL_TIRTOS
fdOpenSession(Task_self());
#endif
StartTCP();
InitTcpReady(&ready);
#if defined(USE_WINDOWS_API)
/* use RNG to get random port if using windows */
ready.port = GetRandomPort();
#endif
XMEMSET(&client_args, 0, sizeof(func_args));
XMEMSET(&server_args, 0, sizeof(func_args));
XMEMSET(&server_cbf, 0, sizeof(callback_functions));
XMEMSET(&client_cbf, 0, sizeof(callback_functions));
server_cbf.method = wolfTLSv1_2_server_method;
client_cbf.method = wolfTLSv1_2_client_method;
server_args.callbacks = &server_cbf;
client_args.callbacks = &client_cbf;
server_args.signal = &ready;
client_args.signal = &ready;
start_thread(SSL_read_test_server_thread, &server_args, &serverThread);
wait_tcp_ready(&server_args);
start_thread(SSL_read_test_client_thread, &client_args, &clientThread);
join_thread(clientThread);
join_thread(serverThread);
AssertTrue(client_args.return_code);
AssertTrue(server_args.return_code);
FreeTcpReady(&ready);
printf(resultFmt, passed);
#endif
return ret;
}
static void test_wolfSSL_CTX_get_min_proto_version(void)
{
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
@ -43892,7 +44148,7 @@ void ApiTest(void)
test_wolfSSL_EC_get_builtin_curves();
test_wolfSSL_CRYPTO_memcmp();
test_wolfSSL_read_detect_TCP_disconnect();
/* test the no op functions for compatibility */
test_no_op_functions();