mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2025-07-29 18:27:29 +02:00
Merge pull request #3911 from TakayukiMatsuo/tk11851
Fix SSL_read behaving differently from openSSL after bidirectional shutdown
This commit is contained in:
24
src/ssl.c
24
src/ssl.c
@ -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");
|
||||
|
258
tests/api.c
258
tests/api.c
@ -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();
|
||||
|
||||
|
Reference in New Issue
Block a user