mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2025-07-30 10:47:28 +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)
|
if (ssl == NULL || data == NULL || sz < 0)
|
||||||
return BAD_FUNC_ARG;
|
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
|
#ifdef HAVE_WRITE_DUP
|
||||||
if (ssl->dupWrite && ssl->dupSide == WRITE_DUP_SIDE) {
|
if (ssl->dupWrite && ssl->dupSide == WRITE_DUP_SIDE) {
|
||||||
WOLFSSL_MSG("Write dup side cannot read");
|
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;
|
(void)ctx;
|
||||||
return ret;
|
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)
|
static void test_wolfSSL_CTX_get_min_proto_version(void)
|
||||||
{
|
{
|
||||||
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
|
||||||
@ -43892,7 +44148,7 @@ void ApiTest(void)
|
|||||||
test_wolfSSL_EC_get_builtin_curves();
|
test_wolfSSL_EC_get_builtin_curves();
|
||||||
|
|
||||||
test_wolfSSL_CRYPTO_memcmp();
|
test_wolfSSL_CRYPTO_memcmp();
|
||||||
|
test_wolfSSL_read_detect_TCP_disconnect();
|
||||||
/* test the no op functions for compatibility */
|
/* test the no op functions for compatibility */
|
||||||
test_no_op_functions();
|
test_no_op_functions();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user