From 03bad1c056127f339c27dcf0215851244c306d9b Mon Sep 17 00:00:00 2001 From: TakayukiMatsuo Date: Thu, 25 Mar 2021 12:54:05 +0900 Subject: [PATCH 1/3] Added logic to wait for TCP disconnect so that SSL_read behaves the same as OpenSSL after a bidirectional shutdown. --- src/ssl.c | 24 +++++ tests/api.c | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 285 insertions(+), 1 deletion(-) diff --git a/src/ssl.c b/src/ssl.c index 7432ffe73..c3e81dadc 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -2025,6 +2025,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"); diff --git a/tests/api.c b/tests/api.c index dac2330bc..0c9f27e91 100644 --- a/tests/api.c +++ b/tests/api.c @@ -41601,6 +41601,266 @@ 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 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()); + +#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 WOLFSSL_FAILURE; + } + + /* read and write data */ + XMEMSET( input, 0, sizeof(input)); + idx = 0; + while (1) { + ret = wolfSSL_read(ssl, input + idx, sizeof(input)); + if (ret > 0) { + idx += ret; + break; + } + else { + err = wolfSSL_get_error(ssl,ret); + if (err == WOLFSSL_ERROR_WANT_READ) { + continue; + } + break; + } + } + + if (err == WOLFSSL_ERROR_ZERO_RETURN || err == WOLFSSL_ERROR_WANT_READ) { + do { + ret = wolfSSL_write(ssl, msg, len); + if (ret > 0) { + break; + } + } while ( ret < 0); + } + + /* bidirectional shutdown */ + while ((ret = wolfSSL_shutdown(ssl)) != WOLFSSL_SUCCESS) { + continue; + } + + ((func_args*)args)->return_code = TEST_SUCCESS; + + /* 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) @@ -42032,7 +42292,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(); From d1e3be1f43cdbe9c56b1e112d5acf663ff231083 Mon Sep 17 00:00:00 2001 From: TakayukiMatsuo Date: Thu, 27 May 2021 06:20:34 +0900 Subject: [PATCH 2/3] Replace return code from literal to value --- tests/api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/api.c b/tests/api.c index 0c9f27e91..eff41a394 100644 --- a/tests/api.c +++ b/tests/api.c @@ -41675,7 +41675,7 @@ static THREAD_RETURN WOLFSSL_THREAD SSL_read_test_server_thread(void* args) wolfSSL_CTX_free(ctx); CloseSocket(cfd); ((func_args*)args)->return_code = TEST_FAIL; - return WOLFSSL_FAILURE; + return 0; } /* read and write data */ From 0186d19aba479e51e68a3b8e441e19e9253ba36e Mon Sep 17 00:00:00 2001 From: TakayukiMatsuo Date: Tue, 8 Jun 2021 16:25:28 +0900 Subject: [PATCH 3/3] Fix some coding style issues. --- tests/api.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tests/api.c b/tests/api.c index eff41a394..764111b6d 100644 --- a/tests/api.c +++ b/tests/api.c @@ -41614,7 +41614,6 @@ static THREAD_RETURN WOLFSSL_THREAD SSL_read_test_server_thread(void* args) char msg[] = "I hear you fa shizzle!"; int len = (int) XSTRLEN(msg); char input[1024]; - int idx; int ret, err; if (!args) @@ -41680,11 +41679,10 @@ static THREAD_RETURN WOLFSSL_THREAD SSL_read_test_server_thread(void* args) /* read and write data */ XMEMSET( input, 0, sizeof(input)); - idx = 0; + while (1) { - ret = wolfSSL_read(ssl, input + idx, sizeof(input)); + ret = wolfSSL_read(ssl, input, sizeof(input)); if (ret > 0) { - idx += ret; break; } else { @@ -41696,13 +41694,13 @@ static THREAD_RETURN WOLFSSL_THREAD SSL_read_test_server_thread(void* args) } } - if (err == WOLFSSL_ERROR_ZERO_RETURN || err == WOLFSSL_ERROR_WANT_READ) { + if (err == WOLFSSL_ERROR_ZERO_RETURN) { do { ret = wolfSSL_write(ssl, msg, len); if (ret > 0) { break; } - } while ( ret < 0); + } while (ret < 0); } /* bidirectional shutdown */ @@ -41710,13 +41708,11 @@ static THREAD_RETURN WOLFSSL_THREAD SSL_read_test_server_thread(void* args) continue; } - ((func_args*)args)->return_code = TEST_SUCCESS; - /* 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); + } while (ret > 0 || err != WOLFSSL_ERROR_ZERO_RETURN); /* detect TCP disconnect */ AssertIntLE(ret,WOLFSSL_FAILURE);