diff --git a/src/bio.c b/src/bio.c index d80b7c096..a861e3015 100644 --- a/src/bio.c +++ b/src/bio.c @@ -202,8 +202,9 @@ int wolfSSL_BIO_read(WOLFSSL_BIO* bio, void* buf, int len) } } - /* start at end of list and work backwards */ - while ((bio != NULL) && (bio->next != NULL)) { + /* start at end of list (or a WOLFSSL_BIO_SSL object since it takes care of + * the rest of the chain) and work backwards */ + while (bio != NULL && bio->next != NULL && bio->type != WOLFSSL_BIO_SSL) { bio = bio->next; } @@ -250,6 +251,10 @@ int wolfSSL_BIO_read(WOLFSSL_BIO* bio, void* buf, int len) } #endif + if (bio->type == WOLFSSL_BIO_SOCKET) { + ret = wolfIO_Recv(bio->num, (char*)buf, len, 0); + } + /* case where front of list is done */ if (bio == front) { break; /* at front of list so be done */ @@ -606,6 +611,8 @@ int wolfSSL_BIO_write(WOLFSSL_BIO* bio, const void* data, int len) else { ret = wolfSSL_BIO_SSL_write(bio, data, len, front); } + /* Rest of chain is taken care of inside call */ + break; } if (bio->type == WOLFSSL_BIO_MD) { @@ -615,6 +622,10 @@ int wolfSSL_BIO_write(WOLFSSL_BIO* bio, const void* data, int len) } #endif /* WOLFCRYPT_ONLY */ + if (bio->type == WOLFSSL_BIO_SOCKET) { + ret = wolfIO_Send(bio->num, (char*)data, len, 0); + } + /* advance to the next bio in list */ bio = bio->next; } @@ -944,6 +955,21 @@ size_t wolfSSL_BIO_wpending(const WOLFSSL_BIO *bio) return 0; } +/* Custom wolfSSL API to check if current bio object supports checking + * pending state. + */ +int wolfSSL_BIO_supports_pending(const WOLFSSL_BIO *bio) +{ + while (bio) { + if (bio->type == WOLFSSL_BIO_SSL || + bio->type == WOLFSSL_BIO_MEMORY || + bio->type == WOLFSSL_BIO_BIO) + return 1; + bio = bio->next; + } + return 0; +} + /* Return the number of pending bytes in read and write buffers */ size_t wolfSSL_BIO_ctrl_pending(WOLFSSL_BIO *bio) { diff --git a/src/internal.c b/src/internal.c index 79ec90351..c1d2b5cf6 100644 --- a/src/internal.c +++ b/src/internal.c @@ -6426,9 +6426,14 @@ void SSL_ResourceFree(WOLFSSL* ssl) #endif /* WOLFSSL_DTLS */ #ifdef OPENSSL_EXTRA #ifndef NO_BIO + /* Don't free if there was/is a previous element in the chain. + * This means that this BIO was part of a chain that will be + * free'd separately. */ if (ssl->biord != ssl->biowr) /* only free write if different */ - wolfSSL_BIO_free(ssl->biowr); - wolfSSL_BIO_free(ssl->biord); /* always free read bio */ + if (ssl->biowr != NULL && ssl->biowr->prev == NULL) + wolfSSL_BIO_free(ssl->biowr); + if (ssl->biord != NULL && ssl->biord->prev == NULL) + wolfSSL_BIO_free(ssl->biord); ssl->biowr = NULL; ssl->biord = NULL; #endif diff --git a/src/ssl.c b/src/ssl.c index aa324b07e..6750de564 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -15136,37 +15136,27 @@ int wolfSSL_set_compression(WOLFSSL* ssl) return; } - /* if WOLFSSL_BIO is socket type then set WOLFSSL socket to use */ - if (rd != NULL && rd->type == WOLFSSL_BIO_SOCKET) { - wolfSSL_set_rfd(ssl, rd->num); - } - if (wr != NULL && wr->type == WOLFSSL_BIO_SOCKET) { - wolfSSL_set_wfd(ssl, wr->num); - } - - /* free any existing WOLFSSL_BIOs in use */ + /* free any existing WOLFSSL_BIOs in use but don't free those in + * a chain */ if (ssl->biord != NULL) { if (ssl->biord != ssl->biowr) { - if (ssl->biowr != NULL) { + if (ssl->biowr != NULL && ssl->biowr->prev != NULL) wolfSSL_BIO_free(ssl->biowr); - ssl->biowr = NULL; - } + ssl->biowr = NULL; } - wolfSSL_BIO_free(ssl->biord); + if (ssl->biord->prev != NULL) + wolfSSL_BIO_free(ssl->biord); ssl->biord = NULL; } - ssl->biord = rd; ssl->biowr = wr; /* set SSL to use BIO callbacks instead */ - if (((ssl->cbioFlag & WOLFSSL_CBIO_RECV) == 0) && - (rd != NULL && rd->type != WOLFSSL_BIO_SOCKET)) { + if (((ssl->cbioFlag & WOLFSSL_CBIO_RECV) == 0)) { ssl->CBIORecv = BioReceive; } - if (((ssl->cbioFlag & WOLFSSL_CBIO_SEND) == 0) && - (wr != NULL && wr->type != WOLFSSL_BIO_SOCKET)) { + if (((ssl->cbioFlag & WOLFSSL_CBIO_SEND) == 0)) { ssl->CBIOSend = BioSend; } @@ -16049,6 +16039,21 @@ int wolfSSL_set_compression(WOLFSSL* ssl) return 0; } + long wolfSSL_BIO_do_handshake(WOLFSSL_BIO *b) + { + WOLFSSL_ENTER("wolfSSL_BIO_do_handshake"); + if (b == NULL) { + WOLFSSL_MSG("Bad parameter"); + return WOLFSSL_FAILURE; + } + if (b->type == WOLFSSL_BIO_SSL && b->ptr != NULL) { + return wolfSSL_negotiate((WOLFSSL*)b->ptr); + } + else { + WOLFSSL_MSG("Not SSL BIO or no SSL object set"); + return WOLFSSL_FAILURE; + } + } long wolfSSL_BIO_set_ssl(WOLFSSL_BIO* b, WOLFSSL* ssl, int closeF) { @@ -16059,6 +16064,8 @@ int wolfSSL_set_compression(WOLFSSL* ssl) if (b != NULL) { b->ptr = ssl; b->shutdown = (byte)closeF; + if (b->next != NULL) + wolfSSL_set_bio(ssl, b->next, b->next); /* add to ssl for bio free if SSL_free called before/instead of free_all? */ ret = WOLFSSL_SUCCESS; } @@ -16280,6 +16287,10 @@ int wolfSSL_set_compression(WOLFSSL* ssl) top->next = append; append->prev = top; + /* SSL BIO's should use the next object in the chain for IO */ + if (top->type == WOLFSSL_BIO_SSL && top->ptr) + wolfSSL_set_bio((WOLFSSL*)top->ptr, append, append); + return top; } #endif /* !NO_BIO */ diff --git a/src/wolfio.c b/src/wolfio.c index 27b335c66..1924fc3d4 100644 --- a/src/wolfio.c +++ b/src/wolfio.c @@ -133,31 +133,46 @@ int BioReceive(WOLFSSL* ssl, char* buf, int sz, void* ctx) return WOLFSSL_CBIO_ERR_GENERAL; } - if (ssl->biord->method && ssl->biord->method->readCb) { - WOLFSSL_MSG("Calling custom biord"); - recvd = ssl->biord->method->readCb(ssl->biord, buf, sz); - if (recvd < 0 && recvd != WOLFSSL_CBIO_ERR_WANT_READ) - return WOLFSSL_CBIO_ERR_GENERAL; - return recvd; + if (wolfSSL_BIO_supports_pending(ssl->biord) && + wolfSSL_BIO_ctrl_pending(ssl->biord) == 0) { + WOLFSSL_MSG("BIO want read"); + return WOLFSSL_CBIO_ERR_WANT_READ; } + recvd = wolfSSL_BIO_read(ssl->biord, buf, sz); + if (recvd <= 0) { + if (ssl->biord->type == WOLFSSL_BIO_SOCKET) { + int err; - switch (ssl->biord->type) { - case WOLFSSL_BIO_MEMORY: - case WOLFSSL_BIO_BIO: - if (wolfSSL_BIO_ctrl_pending(ssl->biord) == 0) { - WOLFSSL_MSG("BIO want read"); - return WOLFSSL_CBIO_ERR_WANT_READ; + if (recvd == 0) { + WOLFSSL_MSG("BioReceive connection closed"); + return WOLFSSL_CBIO_ERR_CONN_CLOSE; } - recvd = wolfSSL_BIO_read(ssl->biord, buf, sz); - if (recvd <= 0) { - WOLFSSL_MSG("BIO general error"); + + err = wolfSSL_LastError(recvd); + if (err == SOCKET_EWOULDBLOCK || err == SOCKET_EAGAIN) { + WOLFSSL_MSG("\tWould block"); + return WOLFSSL_CBIO_ERR_WANT_READ; + } + else if (err == SOCKET_ECONNRESET) { + WOLFSSL_MSG("\tConnection reset"); + return WOLFSSL_CBIO_ERR_CONN_RST; + } + else if (err == SOCKET_EINTR) { + WOLFSSL_MSG("\tSocket interrupted"); + return WOLFSSL_CBIO_ERR_ISR; + } + else if (err == SOCKET_ECONNABORTED) { + WOLFSSL_MSG("\tConnection aborted"); + return WOLFSSL_CBIO_ERR_CONN_CLOSE; + } + else { + WOLFSSL_MSG("\tGeneral error"); return WOLFSSL_CBIO_ERR_GENERAL; } - break; + } - default: - WOLFSSL_MSG("This BIO type is unknown / unsupported"); - return WOLFSSL_CBIO_ERR_GENERAL; + WOLFSSL_MSG("BIO general error"); + return WOLFSSL_CBIO_ERR_GENERAL; } (void)ctx; @@ -186,27 +201,32 @@ int BioSend(WOLFSSL* ssl, char *buf, int sz, void *ctx) return WOLFSSL_CBIO_ERR_GENERAL; } - if (ssl->biowr->method && ssl->biowr->method->writeCb) { - WOLFSSL_MSG("Calling custom biowr"); - sent = ssl->biowr->method->writeCb(ssl->biowr, buf, sz); - if ((sent < 0) && (sent != WOLFSSL_CBIO_ERR_WANT_WRITE)) { - return WOLFSSL_CBIO_ERR_GENERAL; - } - return sent; - } - - switch (ssl->biowr->type) { - case WOLFSSL_BIO_MEMORY: - case WOLFSSL_BIO_BIO: - sent = wolfSSL_BIO_write(ssl->biowr, buf, sz); - if (sent < 0) { + sent = wolfSSL_BIO_write(ssl->biowr, buf, sz); + if (sent < 0) { + if (ssl->biowr->type == WOLFSSL_BIO_SOCKET) { + int err = wolfSSL_LastError(sent); + if (err == SOCKET_EWOULDBLOCK || err == SOCKET_EAGAIN) { + WOLFSSL_MSG("\tWould Block"); + return WOLFSSL_CBIO_ERR_WANT_WRITE; + } + else if (err == SOCKET_ECONNRESET) { + WOLFSSL_MSG("\tConnection reset"); + return WOLFSSL_CBIO_ERR_CONN_RST; + } + else if (err == SOCKET_EINTR) { + WOLFSSL_MSG("\tSocket interrupted"); + return WOLFSSL_CBIO_ERR_ISR; + } + else if (err == SOCKET_EPIPE) { + WOLFSSL_MSG("\tSocket EPIPE"); + return WOLFSSL_CBIO_ERR_CONN_CLOSE; + } + else { + WOLFSSL_MSG("\tGeneral error"); return WOLFSSL_CBIO_ERR_GENERAL; } - break; - - default: - WOLFSSL_MSG("This BIO type is unknown / unsupported"); - return WOLFSSL_CBIO_ERR_GENERAL; + } + return WOLFSSL_CBIO_ERR_GENERAL; } (void)ctx; diff --git a/tests/api.c b/tests/api.c index 85436a5ff..8dbdc7848 100644 --- a/tests/api.c +++ b/tests/api.c @@ -31459,6 +31459,75 @@ static void test_wolfSSL_BIO_should_retry(void) #endif } +static void test_wolfSSL_BIO_connect(void) +{ +#if defined(OPENSSL_ALL) && defined(HAVE_IO_TESTS_DEPENDENCIES) + tcp_ready ready; + func_args server_args; + THREAD_TYPE serverThread; + BIO *tcp_bio; + BIO *ssl_bio; + SSL_CTX* ctx; + SSL *ssl; + char msg[] = "hello wolfssl!"; + char reply[30]; + char buff[10] = {0}; + + printf(testingFmt, "wolfSSL_BIO_new_connect()"); + + /* Setup server */ + XMEMSET(&server_args, 0, sizeof(func_args)); + StartTCP(); + InitTcpReady(&ready); +#if defined(USE_WINDOWS_API) + /* use RNG to get random port if using windows */ + ready.port = GetRandomPort(); +#endif + server_args.signal = &ready; + start_thread(test_server_nofail, &server_args, &serverThread); + wait_tcp_ready(&server_args); + AssertIntGT(XSPRINTF(buff, "%d", ready.port), 0); + + /* Start the test proper */ + /* Setup the TCP BIO */ + AssertNotNull(tcp_bio = BIO_new_connect(wolfSSLIP)); + AssertIntEQ(BIO_set_conn_port(tcp_bio, buff), 1); + /* Setup the SSL object */ + AssertNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + AssertIntEQ(WOLFSSL_SUCCESS, + wolfSSL_CTX_load_verify_locations(ctx, caCertFile, 0)); + AssertIntEQ(WOLFSSL_SUCCESS, + wolfSSL_CTX_use_certificate_file(ctx, cliCertFile, SSL_FILETYPE_PEM)); + AssertIntEQ(WOLFSSL_SUCCESS, + wolfSSL_CTX_use_PrivateKey_file(ctx, cliKeyFile, SSL_FILETYPE_PEM)); + AssertNotNull(ssl = SSL_new(ctx)); + SSL_set_connect_state(ssl); + /* Setup the SSL BIO */ + AssertNotNull(ssl_bio = BIO_new(BIO_f_ssl())); + AssertIntEQ(BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE), 1); + /* Link BIO's so that ssl_bio uses tcp_bio for IO */ + AssertPtrEq(BIO_push(ssl_bio, tcp_bio), ssl_bio); + /* Do TCP connect */ + AssertIntEQ(BIO_do_connect(ssl_bio), 1); + /* Do TLS handshake */ + AssertIntEQ(BIO_do_handshake(ssl_bio), 1); + /* Test writing */ + AssertIntEQ(BIO_write(ssl_bio, msg, sizeof(msg)), sizeof(msg)); + /* Expect length of default wolfSSL reply */ + AssertIntEQ(BIO_read(ssl_bio, reply, sizeof(reply)), 23); + + /* Clean it all up */ + BIO_free_all(ssl_bio); + SSL_CTX_free(ctx); + + /* Server clean up */ + join_thread(serverThread); + FreeTcpReady(&ready); + + printf(resultFmt, passed); +#endif +} + static void test_wolfSSL_BIO_write(void) { #if defined(OPENSSL_EXTRA) && defined(WOLFSSL_BASE64_ENCODE) @@ -40621,6 +40690,7 @@ void ApiTest(void) test_wolfSSL_BIO_should_retry(); test_wolfSSL_d2i_PUBKEY(); test_wolfSSL_BIO_write(); + test_wolfSSL_BIO_connect(); test_wolfSSL_BIO_printf(); test_wolfSSL_BIO_f_md(); #endif diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 623930bda..b81049733 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -672,11 +672,10 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define BIO_set_write_buffer_size wolfSSL_BIO_set_write_buffer_size #define BIO_f_ssl wolfSSL_BIO_f_ssl #define BIO_new_socket wolfSSL_BIO_new_socket -#ifndef NO_WOLFSSL_STUB #define BIO_new_connect wolfSSL_BIO_new_connect #define BIO_set_conn_port wolfSSL_BIO_set_conn_port #define BIO_do_connect wolfSSL_BIO_do_connect -#endif +#define BIO_do_handshake wolfSSL_BIO_do_handshake #define SSL_set_bio wolfSSL_set_bio #define BIO_set_ssl wolfSSL_BIO_set_ssl #define BIO_eof wolfSSL_BIO_eof diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 9d32bb222..46ce7806c 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1325,6 +1325,8 @@ WOLFSSL_API WOLFSSL_BIO *wolfSSL_BIO_new_connect(const char *str); WOLFSSL_API long wolfSSL_BIO_set_conn_port(WOLFSSL_BIO *b, char* port); WOLFSSL_API long wolfSSL_BIO_do_connect(WOLFSSL_BIO *b); +WOLFSSL_API long wolfSSL_BIO_do_handshake(WOLFSSL_BIO *b); + WOLFSSL_API long wolfSSL_BIO_ctrl(WOLFSSL_BIO *bp, int cmd, long larg, void *parg); WOLFSSL_API long wolfSSL_BIO_int_ctrl(WOLFSSL_BIO *bp, int cmd, long larg, int iarg); @@ -3549,6 +3551,7 @@ WOLFSSL_API size_t wolfSSL_get_client_random(const WOLFSSL* ssl, #if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) WOLFSSL_API size_t wolfSSL_BIO_wpending(const WOLFSSL_BIO *bio); +WOLFSSL_API int wolfSSL_BIO_supports_pending(const WOLFSSL_BIO *bio); WOLFSSL_API size_t wolfSSL_BIO_ctrl_pending(WOLFSSL_BIO *b); WOLFSSL_API int wolfSSL_get_server_tmp_key(const WOLFSSL*, WOLFSSL_EVP_PKEY**);