From 361679dfad83c9cf37008748b0394ba401c654af Mon Sep 17 00:00:00 2001 From: Sean Parkinson Date: Fri, 31 Jul 2026 07:06:42 +1000 Subject: [PATCH] ssl.c split: cleanup Clean up includes single return point from every function, consistent formatting function block comment and tests added. ssl_api_rw.c: Cleanup; 6 helpers extracted from write_dup/shutdown; fixed a SendBuffered corner case that returned 0 instead of an error. ssl_api_ext.c: Cleanup; extracted wolfssl_ticket_key_cb_process, wolfssl_rehandshake_prepare. ssl_api_hs.c: Cleanup + de-indent; 9 helpers extracted, incl. shared wolfssl_handshake_flush/_done between connect and accept (~100 duplicated lines removed). wolfSSL_connect/accept left multi-exit. ssl_api_cert.c: Cleanup of newer functions at end of file; extracted PushPeerCertToChain; fixed a double free in CreatePeerCertChain; fixed 5 wrong @param names. ssl_api_crl_ocsp.c: Full cleanup of all 49 functions; wolfSSL_OCSP_parse_url rewritten; fixed a URL with no host returning success with the rest of the URL as the host; added IPv6 literal support; fixed 4 wrong WOLFSSL_ENTER names. ssl.c: Moved the x509GetIssuerFromCM forward declaration here for clarity. --- CMakeLists.txt | 3 + ChangeLog.md | 76 + doc/dox_comments/header_files/ssl.h | 320 ++- src/ssl.c | 12 + src/ssl_api_cert.c | 803 ++++--- src/ssl_api_crl_ocsp.c | 1354 ++++++++--- src/ssl_api_dtls.c | 18 +- src/ssl_api_ext.c | 1343 ++++++----- src/ssl_api_hs.c | 3279 +++++++++++++++------------ src/ssl_api_pk.c | 3 +- src/ssl_api_rw.c | 1272 +++++++---- src/ssl_p7p12.c | 3 +- src/x509_str.c | 14 +- tests/api.c | 6 + tests/api/include.am | 6 + tests/api/test_ssl_cert.c | 1017 ++++++++- tests/api/test_ssl_cert.h | 39 +- tests/api/test_ssl_crl_ocsp.c | 582 +++++ tests/api/test_ssl_crl_ocsp.h | 44 + tests/api/test_ssl_ext.c | 519 ++++- tests/api/test_ssl_ext.h | 65 +- tests/api/test_ssl_hs.c | 1743 ++++++++++++++ tests/api/test_ssl_hs.h | 74 + tests/api/test_ssl_rw.c | 936 ++++++++ tests/api/test_ssl_rw.h | 54 + wolfssl/ssl.h | 3 +- 26 files changed, 10481 insertions(+), 3107 deletions(-) create mode 100644 tests/api/test_ssl_crl_ocsp.c create mode 100644 tests/api/test_ssl_crl_ocsp.h create mode 100644 tests/api/test_ssl_hs.c create mode 100644 tests/api/test_ssl_hs.h create mode 100644 tests/api/test_ssl_rw.c create mode 100644 tests/api/test_ssl_rw.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8705b5b0220..a18d75aed40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4417,8 +4417,11 @@ if(WOLFSSL_EXAMPLES) tests/api/test_dtls.c tests/api/test_dtls13.c tests/api/test_ssl_cert.c + tests/api/test_ssl_crl_ocsp.c tests/api/test_ssl_pk.c tests/api/test_ssl_ext.c + tests/api/test_ssl_rw.c + tests/api/test_ssl_hs.c tests/api/test_ocsp.c tests/api/test_evp.c tests/api/test_tls_ext.c diff --git a/ChangeLog.md b/ChangeLog.md index 61dd30457cb..6a9d5a7be12 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,79 @@ +# wolfSSL Release (unreleased) + +## Enhancements + +* **Behavioral change (`wolfSSL_shutdown` when no close_notify can be sent)**: + when the connection is already closed or reset and no close_notify was ever + sent, the shutdown exchange can never complete. That case now returns + `WOLFSSL_FATAL_ERROR` and records `SOCKET_PEER_CLOSED_E`, so the caller has + a reason to query with `wolfSSL_get_error()`. Previously it returned 0, + which is `WOLFSSL_SHUTDOWN_NOT_DONE` under `WOLFSSL_ERROR_CODE_OPENSSL`, so + an application looping while the result is 0 never left the loop; such a + loop now terminates. `SOCKET_PEER_CLOSED_E` is used whatever closed the + connection, including this side sending a fatal alert, and only when no + more specific error has already been recorded. Under `OPENSSL_EXTRA`, + `wolfSSL_get_error()` reports it as `WOLFSSL_ERROR_SYSCALL`, so a locally + aborted connection can surface as a syscall error. + +* **Behavioral change (`wolfSSL_set0_verify_cert_store` reference handling)**: + the handed-over reference is now always consumed. Previously it was + silently dropped - neither stored nor released - when the store passed was + the one the object or its context was already using, leaking a reference on + every such call. As `wolfSSL_set0_verify_cert_store()` takes ownership, + callers must own a reference before calling; handing over a borrowed + pointer, such as one straight from `wolfSSL_CTX_get_cert_store()`, now + releases a reference the caller never took and can free a store still in + use. Take a reference with `wolfSSL_X509_STORE_up_ref()` first, or use + `wolfSSL_set1_verify_cert_store()`, which takes its own. + +* **Behavioral change (`wolfSSL_X509_STORE_up_ref` on a store owned by another + object)**: the reference count is now only taken for a store allocated with + `wolfSSL_X509_STORE_new()`. A store that is part of another object, such as + the one returned by `wolfSSL_CTX_get_cert_store()` when no store has been + set on the context, has no reference count to take - its lifetime is that of + the object holding it. Such a call now returns 1 without touching the + count, matching `wolfSSL_X509_STORE_free()`, which already did nothing for + such a store. Previously the count was incremented although it had never + been initialized, which on a build using mutexes rather than atomics for + reference counting meant locking a mutex that was never set up. A NULL + store still returns 0. + +* **Behavioral change (NULL store passed to the verify cert store setters)**: + `wolfSSL_set0_verify_cert_store()`, `wolfSSL_set1_verify_cert_store()` and + `wolfSSL_CTX_set1_verify_cert_store()` now treat a NULL store as a request + to clear any store previously set, releasing the reference held on it and + reverting to the store of the context (for an SSL object) or the one the + context owns (for a context). They return 1, and clearing when no store is + set is a successful no-op. Previously a NULL store was rejected with a 0 + return and no other effect. This matches OpenSSL, where + `SSL_set0_verify_cert_store()` and friends clear the verify store when + passed NULL. The object being set is still required: a NULL `ssl` or `ctx` + returns 0 as before. + +* **Fix (`wolfSSL_set_accept_state` with `WOLFSSL_BLIND_PRIVATE_KEY`)**: the + static-ECC check decoded `ssl->buffers.key` directly. Under + `WOLFSSL_BLIND_PRIVATE_KEY` that buffer is masked, so the decode always + failed and the server silently dropped `haveECDSAsig`, `haveECC` and + `haveStaticECC`, losing the static ECC cipher suites for a key that was + valid. The key is now unmasked into a plain copy for the check. An + allocation failure while unmasking leaves the capabilities alone rather + than withdrawing them, matching what a failure to allocate the `ecc_key` + already did. Only affects builds with `WOLFSSL_BLIND_PRIVATE_KEY`. + +* **Behavioral change (`wolfSSL_read_ex` with a NULL object)**: the NULL check + is now made in every build rather than only under `OPENSSL_EXTRA`, so + `wolfSSL_read_ex(NULL, ...)` returns `BAD_FUNC_ARG` consistently. Builds + without `OPENSSL_EXTRA` previously returned 0, the same value used for "no + application data was read", so a caller testing for 0 could not tell the + two apart. Callers that treat any non-1 result as failure are unaffected. + +* **Behavioral change (`wolfSSL_write_ex` with a NULL object)**: a NULL object + is now rejected with `BAD_FUNC_ARG` rather than reported as 0, matching + `wolfSSL_read_ex()` and the rest of the read/write API. 0 is also the value + used for "no application data was written", so a caller testing for 0 could + not tell the two apart. Callers that treat any non-1 result as failure are + unaffected. + # wolfSSL Release 5.9.2 (Jun 23, 2026) Release 5.9.2 has been developed according to wolfSSL's development and QA diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 20c57722160..84eb77d017f 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -2141,6 +2141,8 @@ int wolfSSL_get_using_nonblock(WOLFSSL*); SSL_ERROR_WANT_WRITE error was received and and the application needs to call wolfSSL_write() again. Use wolfSSL_get_error() to get a specific error code. + \return BAD_FUNC_ARG will be returned when ssl or data is NULL, or sz + is negative. \param ssl pointer to the SSL session, created with wolfSSL_new(). \param data data buffer which will be sent to peer. @@ -2167,6 +2169,46 @@ int wolfSSL_get_using_nonblock(WOLFSSL*); */ int wolfSSL_write(WOLFSSL* ssl, const void* data, int sz); +/*! + \ingroup IO + + \brief This function writes sz bytes from the buffer, data, to the SSL + connection, ssl, and reports the number of bytes written. It is equivalent + to wolfSSL_write() except that the length written is returned through wr + and the return value only indicates success or failure. Whether a partial + write counts as success depends on WOLFSSL_MODE_ENABLE_PARTIAL_WRITE having + been set with wolfSSL_CTX_set_mode(); without it, anything short of the + full length is a failure. + + \return 1 on success. + \return 0 on failure. Call wolfSSL_get_error() for the reason. + \return BAD_FUNC_ARG when ssl is NULL. + + \param ssl pointer to the SSL session, created with wolfSSL_new(). + \param data data buffer to write to the SSL connection. + \param sz number of bytes to write. + \param wr pointer that receives the number of bytes written. May be NULL. + + _Example_ + \code + WOLFSSL* ssl = 0; + char msg[] = "hello wolfssl!"; + size_t written = 0; + ... + + if (wolfSSL_write_ex(ssl, msg, sizeof(msg), &written) != 1) { + // handle the failure, see wolfSSL_get_error() + } + \endcode + + \sa wolfSSL_write + \sa wolfSSL_read_ex + \sa wolfSSL_CTX_set_mode + \sa wolfSSL_get_error +*/ +int wolfSSL_write_ex(WOLFSSL* ssl, const void* data, size_t sz, size_t* wr); + + /*! \ingroup IO @@ -2198,6 +2240,8 @@ int wolfSSL_write(WOLFSSL* ssl, const void* data, int sz); SSL_ERROR_WANT_WRITE error was received and and the application needs to call wolfSSL_read() again. Use wolfSSL_get_error() to get a specific error code. + \return BAD_FUNC_ARG will be returned when ssl or data is NULL, or sz + is negative. \param ssl pointer to the SSL session, created with wolfSSL_new(). \param data buffer where wolfSSL_read() will place data read. @@ -2225,6 +2269,46 @@ int wolfSSL_write(WOLFSSL* ssl, const void* data, int sz); */ int wolfSSL_read(WOLFSSL* ssl, void* data, int sz); +/*! + \ingroup IO + + \brief This function reads up to sz bytes of decrypted application data + from the SSL connection, ssl, into the buffer, data, and reports the number + of bytes read. It is equivalent to wolfSSL_read() except that the length + read is returned through rd and the return value only indicates whether any + application data was read. + + \return 1 when application data was read. + \return 0 when no application data was read. Call wolfSSL_get_error() for + the reason. + \return BAD_FUNC_ARG when ssl is NULL. + + \param ssl pointer to the SSL session, created with wolfSSL_new(). + \param data buffer to hold the data read. + \param sz size of the buffer in bytes. + \param rd pointer that receives the number of bytes read. May be NULL and + is only set when data was read. + + _Example_ + \code + WOLFSSL* ssl = 0; + char reply[1024]; + size_t bytesRead = 0; + ... + + if (wolfSSL_read_ex(ssl, reply, sizeof(reply), &bytesRead) == 1) { + // "bytesRead" bytes returned into buffer "reply" + } + \endcode + + \sa wolfSSL_read + \sa wolfSSL_write_ex + \sa wolfSSL_pending + \sa wolfSSL_get_error +*/ +int wolfSSL_read_ex(WOLFSSL* ssl, void* data, size_t sz, size_t* rd); + + /*! \ingroup IO @@ -2258,6 +2342,8 @@ int wolfSSL_read(WOLFSSL* ssl, void* data, int sz); SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE error was received and and the application needs to call wolfSSL_peek() again. Use wolfSSL_get_error() to get a specific error code. + \return BAD_FUNC_ARG will be returned when ssl or data is NULL, or sz + is negative. \param ssl pointer to the SSL session, created with wolfSSL_new(). \param data buffer where wolfSSL_peek() will place data read. @@ -2439,6 +2525,16 @@ void wolfSSL_free(WOLFSSL* ssl); \return SSL_FATAL_ERROR will be returned upon failure. Call wolfSSL_get_error() for a more specific error code. + When the connection is already closed or reset and no close notify was + ever sent, the exchange can never complete and SSL_FATAL_ERROR is + returned, so a loop that calls this function until the shutdown completes + terminates. wolfSSL_get_error() then reports SOCKET_PEER_CLOSED_E, unless + a more specific error has already been recorded. That code is used + whatever closed the connection, including this side sending a fatal + alert, so under OPENSSL_EXTRA, where wolfSSL_get_error() reports it as + SSL_ERROR_SYSCALL, a locally aborted connection can surface as a syscall + error. + \param ssl pointer to the SSL session created with wolfSSL_new(). _Example_ @@ -2459,6 +2555,47 @@ void wolfSSL_free(WOLFSSL* ssl); */ int wolfSSL_shutdown(WOLFSSL* ssl); +/*! + \ingroup TLS + + \brief This function sends a user_canceled alert to the peer and then + shuts the connection down by calling wolfSSL_shutdown(). It is used when + the application abandons a connection for its own reasons rather than + because of a protocol failure. + + \return WOLFSSL_SUCCESS on successful shutdown. + \return WOLFSSL_SHUTDOWN_NOT_DONE when the peer has yet to send its + close notify alert. Call wolfSSL_shutdown() again to complete the + bidirectional shutdown. Under WOLFSSL_ERROR_CODE_OPENSSL this value is 0, + which is also WOLFSSL_FAILURE, so in that configuration the return value + alone does not separate this case from the one below. + \return WOLFSSL_FAILURE when ssl is NULL or the alert could not be sent. + Call wolfSSL_get_error() for the reason. + \return WOLFSSL_FATAL_ERROR when the shutdown that follows the alert + fails. Call wolfSSL_get_error() for the reason. + \return SSL_SHUTDOWN_ALREADY_DONE_E when the connection was already shut + down and WOLFSSL_SHUTDOWNONCE is defined. + + \param ssl pointer to the SSL session, created with wolfSSL_new(). + + _Example_ + \code + int ret = 0; + WOLFSSL* ssl = 0; + ... + + ret = wolfSSL_SendUserCanceled(ssl); + if (ret != WOLFSSL_SUCCESS) { + // failed to shut the connection down, see wolfSSL_get_error() + } + \endcode + + \sa wolfSSL_shutdown + \sa wolfSSL_get_error +*/ +int wolfSSL_SendUserCanceled(WOLFSSL* ssl); + + /*! \ingroup IO @@ -2484,6 +2621,8 @@ int wolfSSL_shutdown(WOLFSSL* ssl); SSL_ERROR_WANT_WRITE error was received and and the application needs to call wolfSSL_send() again. Use wolfSSL_get_error() to get a specific error code. + \return BAD_FUNC_ARG will be returned when ssl or data is NULL, or sz + is negative. \param ssl pointer to the SSL session, created with wolfSSL_new(). \param data data buffer to send to peer. @@ -2544,6 +2683,8 @@ int wolfSSL_send(WOLFSSL* ssl, const void* data, int sz, int flags); SSL_ERROR_WANT_WRITE error was received and and the application needs to call wolfSSL_recv() again. Use wolfSSL_get_error() to get a specific error code. + \return BAD_FUNC_ARG will be returned when ssl or data is NULL, or sz + is negative. \param ssl pointer to the SSL session, created with wolfSSL_new(). \param data buffer where wolfSSL_recv() will place data read. @@ -3042,6 +3183,7 @@ void wolfSSL_CTX_SetCertCbCtx(WOLFSSL_CTX* ctx, void* userCtx); available in the SSL object to be read by wolfSSL_read(). \return int This function returns the number of bytes pending. + \return SSL_FAILURE will be returned when ssl is NULL. \param ssl pointer to the SSL session, created with wolfSSL_new(). @@ -5744,11 +5886,13 @@ long wolfSSL_CTX_get_default_read_buffer_len(WOLFSSL_CTX* ctx); long wolfSSL_get_default_read_buffer_len(const WOLFSSL* ssl); /*! - \ingroup Setup + \ingroup OCSP - \brief This function sets the options argument to use with OCSP. + \brief This function sets the argument to be passed to the OCSP status + callback. - \return SSL_FAILURE If ctx or it’s cert manager is NULL. + \return SSL_FAILURE If ctx or it’s cert manager is NULL, or stapling is + not set up. \return SSL_SUCCESS If successfully set. \param ctx WOLFSSL_CTX structure to set user argument. @@ -5765,6 +5909,7 @@ long wolfSSL_get_default_read_buffer_len(const WOLFSSL* ssl); //check ret value \endcode + \sa wolfSSL_CTX_set_tlsext_status_cb \sa wolfSSL_CTX_new \sa wolfSSL_CTX_free */ @@ -5799,9 +5944,9 @@ void wolfSSL_CTX_set_client_cert_cb(WOLFSSL_CTX *ctx, client_cert_cb cb); \brief Sets a generic certificate setup callback. - This function allows the application to register a callback that will be invoked - during certificate setup. The callback can perform custom certificate selection - or loading logic. + The callback is called whenever a certificate is about to be used, so the + application can inspect, set or clear certificates - for example to react + to a CA list sent by the peer. \param ctx The WOLFSSL_CTX object. \param cb The callback function for certificate setup. @@ -5816,6 +5961,8 @@ void wolfSSL_CTX_set_client_cert_cb(WOLFSSL_CTX *ctx, client_cert_cb cb); \endcode \sa wolfSSL_CTX_set_client_cert_cb + \sa wolfSSL_get0_peer_CA_list + \sa wolfSSL_get_client_CA_list */ void wolfSSL_CTX_set_cert_cb(WOLFSSL_CTX* ctx, CertSetupCallback cb, void *arg); @@ -5833,6 +5980,8 @@ void wolfSSL_CTX_set_cert_cb(WOLFSSL_CTX* ctx, CertSetupCallback cb, void *arg); \param cb The callback function to handle OCSP status requests. \return SSL_SUCCESS on success, SSL_FAILURE otherwise. + \return SSL_FAILURE will be returned when a parameter is NULL or + stapling is not set up. _Example_ \code @@ -5854,25 +6003,13 @@ int wolfSSL_CTX_set_tlsext_status_cb(WOLFSSL_CTX* ctx, tlsextStatusCb cb); \param cb Pointer to receive the callback function. \return SSL_SUCCESS on success, SSL_FAILURE otherwise. + \return SSL_FAILURE will be returned when a parameter is NULL or + stapling is not set up. \sa wolfSSL_CTX_set_tlsext_status_cb */ int wolfSSL_CTX_get_tlsext_status_cb(WOLFSSL_CTX* ctx, tlsextStatusCb* cb); -/*! - \ingroup OCSP - - \brief Sets the argument to be passed to the OCSP status callback. - - \param ctx The WOLFSSL_CTX object. - \param arg The user argument to pass to the callback. - - \return SSL_SUCCESS on success, SSL_FAILURE otherwise. - - \sa wolfSSL_CTX_set_tlsext_status_cb -*/ -long wolfSSL_CTX_set_tlsext_status_arg(WOLFSSL_CTX* ctx, void* arg); - /*! \ingroup OCSP @@ -5901,6 +6038,8 @@ long wolfSSL_get_tlsext_status_ocsp_resp(WOLFSSL *ssl, unsigned char **resp); \param len Length of the response buffer. \return SSL_SUCCESS on success, SSL_FAILURE otherwise. + \return SSL_FAILURE will be returned when ssl is NULL or the + response and length disagree. \sa wolfSSL_get_tlsext_status_ocsp_resp */ @@ -5926,6 +6065,8 @@ long wolfSSL_set_tlsext_status_ocsp_resp(WOLFSSL *ssl, unsigned char *resp, int \param idx Index of the certificate chain. \return SSL_SUCCESS on success, SSL_FAILURE otherwise. + \return SSL_FAILURE will be returned when ssl is NULL, idx is out + of range, or the response and length disagree. */ int wolfSSL_set_tlsext_status_ocsp_resp_multi(WOLFSSL* ssl, unsigned char *resp, int len, word32 idx); @@ -6205,6 +6346,8 @@ long wolfSSL_set_tlsext_debug_arg(WOLFSSL *s, void *arg); \return 1 upon success. \return 0 upon error. + \return BAD_FUNC_ARG will be returned when s is NULL. + \return SSL_FAILURE will be returned when the type is not OCSP. \param s pointer to WOLFSSL struct which is created by SSL_new() function \param type ssl extension type which TLSEXT_STATUSTYPE_ocsp is @@ -6841,6 +6984,41 @@ int wolfSSL_want_read(WOLFSSL* ssl); */ int wolfSSL_want_write(WOLFSSL* ssl); +/*! + \ingroup Debug + + \brief This function reports which I/O operation, if any, the SSL session + is waiting on. It reflects the same state that wolfSSL_want_read() and + wolfSSL_want_write() report individually. Unlike those two, which are + always available, this function is only built when OPENSSL_EXTRA is + defined. + + \return WOLFSSL_READING when the underlying I/O needs data to be read + before progress can be made. + \return WOLFSSL_WRITING when the underlying I/O needs data to be written + before progress can be made. + \return WOLFSSL_NOTHING when the session is not waiting on the underlying + I/O, or ssl is NULL. + + \param ssl pointer to the SSL session, created with wolfSSL_new(). + + _Example_ + \code + WOLFSSL* ssl = 0; + ... + + if (wolfSSL_want(ssl) == WOLFSSL_READING) { + // wait for the socket to become readable, then retry + } + \endcode + + \sa wolfSSL_want_read + \sa wolfSSL_want_write + \sa wolfSSL_get_error +*/ +int wolfSSL_want(WOLFSSL* ssl); + + /*! \ingroup Setup @@ -7264,13 +7442,20 @@ WOLFSSL_X509* wolfSSL_get_chain_X509(WOLFSSL_X509_CHAIN* chain, int idx); \brief Retrieves the peer’s PEM certificate at index (idx). - \return Success If successful the call will return the peer’s - certificate by index. - \return 0 will be returned if an invalid chain pointer is passed to - the function. + \return SSL_SUCCESS will be returned on success. + \return SSL_FAILURE will be returned when the certificate cannot be + converted. + \return BAD_FUNC_ARG will be returned when chain is NULL, idx is out of + range, or outLen is NULL. + \return LENGTH_ONLY_E will be returned when buf is NULL, with the + required length returned through outLen. \param chain pointer to a valid WOLFSSL_X509_CHAIN structure. - \param idx indexto start of chain. + \param idx index to start of chain. + \param buf buffer to hold the PEM certificate. May be NULL to ask for + the required length only. + \param inLen length of buf in bytes. + \param outLen length of the PEM data in bytes. _Example_ \code @@ -8397,11 +8582,15 @@ int wolfSSL_make_eap_keys(WOLFSSL* ssl, void* key, unsigned int len, \return 0 will be returned upon failure. Call wolfSSL_get_error() for the specific error code. \return MEMORY_ERROR will be returned if a memory error was encountered. + \return BAD_FUNC_ARG will be returned when ssl is NULL, iovcnt is negative, + or iov is NULL with a non-zero iovcnt. \return SSL_FATAL_ERROR will be returned upon failure when either an error occurred or, when using non-blocking sockets, the SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE error was received and and the application needs to call wolfSSL_write() again. Use wolfSSL_get_error() to get a specific error code. + \return BUFFER_E will be returned when the total length of the + segments overflows. \param ssl pointer to the SSL session, created with wolfSSL_new(). \param iov array of I/O vectors to write @@ -8505,6 +8694,7 @@ int wolfSSL_CTX_UnloadIntermediateCerts(WOLFSSL_CTX* ctx); \return SSL_BAD_FILE will be returned if the file doesn’t exist, can’t be read, or is corrupted. \return MEMORY_E will be returned if an out of memory condition occurs. + \return BAD_MUTEX_E will be returned when locking fails. \param ctx pointer to the SSL context, created with wolfSSL_CTX_new(). @@ -9078,6 +9268,8 @@ int wolfSSL_UnloadCertsKeys(WOLFSSL* ssl); \endcode \sa wolfSSL_set_group_messages + \sa wolfSSL_CTX_clear_group_messages + \sa wolfSSL_clear_group_messages \sa wolfSSL_CTX_new */ int wolfSSL_CTX_set_group_messages(WOLFSSL_CTX* ctx); @@ -9103,6 +9295,8 @@ int wolfSSL_CTX_set_group_messages(WOLFSSL_CTX* ctx); \endcode \sa wolfSSL_CTX_set_group_messages + \sa wolfSSL_clear_group_messages + \sa wolfSSL_CTX_clear_group_messages \sa wolfSSL_new */ int wolfSSL_set_group_messages(WOLFSSL* ssl); @@ -11578,6 +11772,7 @@ int wolfSSL_CTX_SetCRL_Cb(WOLFSSL_CTX* ctx, CbMissingCRL cb); \return SSL_FAILURE is returned upon failure. \return NOT_COMPILED_IN is returned when this function has been called, but OCSP support was not enabled when wolfSSL was compiled. + \return BAD_FUNC_ARG will be returned when ctx is NULL. \param ctx pointer to the SSL context, created with wolfSSL_CTX_new(). \param options value used to set the OCSP options. @@ -11633,6 +11828,7 @@ int wolfSSL_CTX_DisableOCSP(WOLFSSL_CTX* ctx); \return SSL_FAILURE is returned upon failure. \return NOT_COMPILED_IN is returned when this function has been called, but OCSP support was not enabled when wolfSSL was compiled. + \return BAD_FUNC_ARG will be returned when ctx is NULL. \param ctx pointer to the SSL context, created with wolfSSL_CTX_new(). \param url pointer to the OCSP URL for wolfSSL to use. @@ -12489,6 +12685,7 @@ int wolfSSL_CTX_UseOCSPStaplingV2(WOLFSSL_CTX* ctx, \return BAD_FUNC_ARG is the error that will be returned in one of these cases: ssl is NULL, name is a unknown value. (see below) \return MEMORY_E is the error returned when there is not enough memory. + \return SSL_FAILURE will be returned when the curve is not recognised. \param ssl pointer to a SSL object, created with wolfSSL_new(). \param name indicates which curve will be supported for the session. The @@ -12532,6 +12729,7 @@ int wolfSSL_UseSupportedCurve(WOLFSSL* ssl, word16 name); \return BAD_FUNC_ARG is the error that will be returned in one of these cases: ctx is NULL, name is a unknown value. (see below) \return MEMORY_E is the error returned when there is not enough memory. + \return SSL_FAILURE will be returned when the curve is not recognised. \param ctx pointer to a SSL context, created with wolfSSL_CTX_new(). \param name indicates which curve will be supported for the session. @@ -12607,6 +12805,8 @@ int wolfSSL_UseSecureRenegotiation(WOLFSSL* ssl); \return SSL_FATAL_ERROR returned if there was an error with the server or client configuration and the renegotiation could not be completed. See wolfSSL_negotiate(). + \return SSL_FAILURE will be returned when secure renegotiation + is not available. \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). @@ -12698,6 +12898,8 @@ int wolfSSL_CTX_UseSessionTicket(WOLFSSL_CTX* ctx); \return SSL_SUCCESS returned if the function executed without error. \return BAD_FUNC_ARG returned if ssl or bufSz is NULL, or if bufSz is non-NULL and buf is NULL + \return LENGTH_ONLY_E will be returned when buf is NULL, with the + required length returned through bufSz. \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). @@ -12735,6 +12937,8 @@ int wolfSSL_get_SessionTicket(WOLFSSL* ssl, unsigned char* buf, word32* bufSz); \return BAD_FUNC_ARG returned if the WOLFSSL structure is NULL. This will also be thrown if the buf argument is NULL but the bufSz argument is not zero. + \return MEMORY_ERROR will be returned when the ticket cannot be + allocated. \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). \param buf a byte pointer that gets loaded into the ticket member @@ -14179,6 +14383,7 @@ WOLFSSL_ASN1_TIME* wolfSSL_X509_get_notBefore(WOLFSSL_X509*); \return SSL_SUCCESS If successful. \return SSL_FATAL_ERROR will be returned if an error occurred. To get a more detailed error code, call wolfSSL_get_error(). + \return BAD_FUNC_ARG will be returned when ssl is NULL. \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). @@ -16176,30 +16381,6 @@ int wolfSSL_set_client_cert_type(WOLFSSL* ssl, const char* buf, int len); */ int wolfSSL_set_server_cert_type(WOLFSSL* ssl, const char* buf, int len); -/*! - \ingroup Setup - - \brief Enables handshake message grouping for the given WOLFSSL_CTX context. - - This function turns on handshake message grouping for all SSL objects created from the specified context. - - \return WOLFSSL_SUCCESS on success. - \return BAD_FUNC_ARG if ctx is NULL. - - \param ctx Pointer to the WOLFSSL_CTX structure. - - _Example_ - \code - WOLFSSL_CTX* ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()); - wolfSSL_CTX_set_group_messages(ctx); - \endcode - - \sa wolfSSL_CTX_clear_group_messages - \sa wolfSSL_set_group_messages - \sa wolfSSL_clear_group_messages -*/ -int wolfSSL_CTX_set_group_messages(WOLFSSL_CTX* ctx); - /*! \ingroup Setup @@ -16224,30 +16405,6 @@ int wolfSSL_CTX_set_group_messages(WOLFSSL_CTX* ctx); */ int wolfSSL_CTX_clear_group_messages(WOLFSSL_CTX* ctx); -/*! - \ingroup Setup - - \brief Enables handshake message grouping for the given WOLFSSL object. - - This function turns on handshake message grouping for the specified SSL object. - - \return WOLFSSL_SUCCESS on success. - \return BAD_FUNC_ARG if ssl is NULL. - - \param ssl Pointer to the WOLFSSL structure. - - _Example_ - \code - WOLFSSL* ssl = wolfSSL_new(ctx); - wolfSSL_set_group_messages(ssl); - \endcode - - \sa wolfSSL_clear_group_messages - \sa wolfSSL_CTX_set_group_messages - \sa wolfSSL_CTX_clear_group_messages -*/ -int wolfSSL_set_group_messages(WOLFSSL* ssl); - /*! \ingroup Setup @@ -16885,23 +17042,6 @@ WOLFSSL_STACK *wolfSSL_get0_CA_list( */ WOLFSSL_STACK *wolfSSL_get0_peer_CA_list(const WOLFSSL *ssl); -/*! - \ingroup TLS - \brief This function sets a callback that will be called whenever a - certificate is about to be used, to allow the application to inspect, set - or clear any certificates, for example to react to a CA list sent from the - peer. - - \param [in] ctx Pointer to the wolfSSL context - \param [in] cb Function pointer to the callback - \param [in] arg Pointer that will be passed to the callback - - \sa wolfSSL_get0_peer_CA_list - \sa wolfSSL_get_client_CA_list -*/ -void wolfSSL_CTX_set_cert_cb(WOLFSSL_CTX* ctx, - int (*cb)(WOLFSSL *, void *), void *arg); - /*! \ingroup TLS diff --git a/src/ssl.c b/src/ssl.c index 0c286cf9171..e596279bb5d 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -417,6 +417,18 @@ WC_RNG* wolfssl_make_rng(WC_RNG* rng, int* local) #define WOLFSSL_SSL_SESS_INCLUDED #include "src/ssl_sess.c" +/* Forward declarations for static functions that are defined in a file + * included later in this amalgamation but used by one included earlier. Keep + * them here, next to the includes, rather than inside the files that need + * them. + * + * x509GetIssuerFromCM() is defined in src/x509_str.c, which also requires + * NO_CERTS to be undefined. */ +#if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && !defined(NO_CERTS) +static int x509GetIssuerFromCM(WOLFSSL_X509 **issuer, WOLFSSL_CERT_MANAGER* cm, + WOLFSSL_X509 *x); +#endif + #define WOLFSSL_SSL_API_CERT_INCLUDED #include "src/ssl_api_cert.c" diff --git a/src/ssl_api_cert.c b/src/ssl_api_cert.c index 8bc0f2dfaae..25cc2d3d44d 100644 --- a/src/ssl_api_cert.c +++ b/src/ssl_api_cert.c @@ -54,8 +54,8 @@ int wolfSSL_CTX_mutual_auth(WOLFSSL_CTX* ctx, int req) /* Set whether mutual authentication is required for the connection. * Server side only. * - * @param [in] ssl The SSL/TLS object. - * @param [in] req 1 to indicate required and 0 when not. + * @param [in, out] ssl SSL/TLS object. + * @param [in] req 1 to indicate required and 0 when not. * @return 0 on success. * @return BAD_FUNC_ARG when ssl is NULL. * @return SIDE_ERROR when not a server @@ -127,11 +127,11 @@ long wolfSSL_CTX_get_verify_depth(WOLFSSL_CTX* ctx) else { /* A configurable depth is only tracked with the OpenSSL extra APIs; * otherwise the fixed maximum chain depth applies. */ - #ifndef OPENSSL_EXTRA + #ifndef OPENSSL_EXTRA ret = MAX_CHAIN_DEPTH; - #else + #else ret = ctx->verifyDepth; - #endif + #endif } return ret; @@ -153,11 +153,11 @@ long wolfSSL_get_verify_depth(WOLFSSL* ssl) else { /* A configurable depth is only tracked with the OpenSSL extra APIs; * otherwise the fixed maximum chain depth applies. */ - #ifndef OPENSSL_EXTRA + #ifndef OPENSSL_EXTRA ret = MAX_CHAIN_DEPTH; - #else + #else ret = ssl->options.verifyDepth; - #endif + #endif } return ret; @@ -525,7 +525,7 @@ int wolfSSL_CTX_clear_expected_rpk(WOLFSSL_CTX* ctx) /* Remove all pinned expected peer Raw Public Keys from the SSL/TLS object, so * the table can be repopulated (e.g. across a peer key rotation). * - * @param [in] ssl SSL/TLS object. + * @param [in, out] ssl SSL/TLS object. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl is NULL. */ @@ -550,10 +550,10 @@ typedef struct { byte failNoCert:1; /* Fail when no peer certificate except when PSK handshake performed. */ byte failNoCertxPSK:1; -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) + #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) /* Verify peer certificate post handshake. */ byte verifyPostHandshake:1; -#endif + #endif } SetVerifyOptions; /* Convert the mode flags into certificate verification options. @@ -579,10 +579,10 @@ static SetVerifyOptions ModeToVerifyOptions(int mode) (mode & WOLFSSL_VERIFY_FAIL_EXCEPT_PSK) != 0; opts.failNoCert = (mode & WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT) != 0; -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) + #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) opts.verifyPostHandshake = (mode & WOLFSSL_VERIFY_POST_HANDSHAKE) != 0; -#endif + #endif } } @@ -595,7 +595,8 @@ static SetVerifyOptions ModeToVerifyOptions(int mode) * @param [in] mode Verification mode options. * @param [in] verify_callback Verification callback. */ -WOLFSSL_ABI void wolfSSL_CTX_set_verify(WOLFSSL_CTX* ctx, int mode, +WOLFSSL_ABI +void wolfSSL_CTX_set_verify(WOLFSSL_CTX* ctx, int mode, VerifyCallback verify_callback) { WOLFSSL_ENTER("wolfSSL_CTX_set_verify"); @@ -609,9 +610,9 @@ WOLFSSL_ABI void wolfSSL_CTX_set_verify(WOLFSSL_CTX* ctx, int mode, ctx->verifyPeer = opts.verifyPeer; ctx->failNoCert = opts.failNoCert; ctx->failNoCertxPSK = opts.failNoCertxPSK; - #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) + #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) ctx->verifyPostHandshake = opts.verifyPostHandshake; - #endif + #endif /* Store the user verification callback against the context. */ ctx->verifyCallback = verify_callback; @@ -640,9 +641,9 @@ void wolfSSL_CTX_set_cert_verify_callback(WOLFSSL_CTX* ctx, /* Set the verification options against the SSL/TLS object. * - * @param [in] ssl SSL/TLS object. - * @param [in] mode Verification mode options. - * @param [in] verify_callback Verification callback. + * @param [in, out] ssl SSL/TLS object. + * @param [in] mode Verification mode options. + * @param [in] verify_callback Verification callback. */ void wolfSSL_set_verify(WOLFSSL* ssl, int mode, VerifyCallback verify_callback) { @@ -657,9 +658,9 @@ void wolfSSL_set_verify(WOLFSSL* ssl, int mode, VerifyCallback verify_callback) ssl->options.verifyPeer = opts.verifyPeer; ssl->options.failNoCert = opts.failNoCert; ssl->options.failNoCertxPSK = opts.failNoCertxPSK; - #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) + #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) ssl->options.verifyPostHandshake = opts.verifyPostHandshake; - #endif + #endif /* Store the user verification callback against the object. */ ssl->verifyCallback = verify_callback; @@ -668,8 +669,8 @@ void wolfSSL_set_verify(WOLFSSL* ssl, int mode, VerifyCallback verify_callback) /* Set the certificate verification result for the SSL/TLS object. * - * @param [in] ssl SSL/TLS object. - * @param [in] v Verification result. + * @param [in, out] ssl SSL/TLS object. + * @param [in] v Verification result. */ void wolfSSL_set_verify_result(WOLFSSL *ssl, long v) { @@ -677,12 +678,12 @@ void wolfSSL_set_verify_result(WOLFSSL *ssl, long v) /* Ensure we have an SSL/TLS object to work with. */ if (ssl != NULL) { - #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) ssl->peerVerifyRet = (unsigned long)v; - #else + #else WOLFSSL_STUB("wolfSSL_set_verify_result"); (void)v; - #endif + #endif } } @@ -703,8 +704,8 @@ void wolfSSL_CTX_SetCertCbCtx(WOLFSSL_CTX* ctx, void* userCtx) /* Store user ctx for verify callback into SSL/TLS object. * - * @param [in] ssl SSL/TLS object. - * @param [in] ctx User context for verify callback. + * @param [in, out] ssl SSL/TLS object. + * @param [in] ctx User context for verify callback. */ void wolfSSL_SetCertCbCtx(WOLFSSL* ssl, void* ctx) { @@ -718,10 +719,10 @@ void wolfSSL_SetCertCbCtx(WOLFSSL* ssl, void* ctx) -/* Store context CA Cache addition callback into SSL/TLS context. +/* Set the callback called when a CA is added to the cache. * - * @param [in] ctx SSL/TLS context. - * @param [in] userCtx User context for verify callback. + * @param [in, out] ctx SSL/TLS context. + * @param [in] cb Callback to call. NULL to clear. */ void wolfSSL_CTX_SetCACb(WOLFSSL_CTX* ctx, CallbackCACache cb) { @@ -735,6 +736,7 @@ void wolfSSL_CTX_SetCACb(WOLFSSL_CTX* ctx, CallbackCACache cb) defined(WOLFSSL_POST_HANDSHAKE_AUTH) /* For TLS v1.3, send authentication messages after handshake completes. * + * @param [in, out] ssl SSL/TLS object. * @return 1 on success. * @return UNSUPPORTED_PROTO_VERSION when not a TLSv1.3 handshake. * @return 0 on other failure. @@ -785,8 +787,8 @@ int wolfSSL_CTX_set_post_handshake_auth(WOLFSSL_CTX* ctx, int val) } /* Set whether handshakes with this SSL/TLS object allow auth post handshake. * - * @param [in] ctx SSL/TLS context. - * @param [in] val Whether to allow post handshake authentication. + * @param [in, out] ssl SSL/TLS object. + * @param [in] val Whether to allow post handshake authentication. * @return 1 on success. * @return 0 on failure. */ @@ -900,9 +902,9 @@ int wolfSSL_CTX_memsave_cert_cache(WOLFSSL_CTX* ctx, void* mem, /* Load certificate cache into SSL/TLS context from memory. * - * @param [in] ctx SSL/TLS context. - * @param [in] mem Memory with certificate cache. - * @param [in] sz Size of certificate cache in bytes + * @param [in] ctx SSL/TLS context. + * @param [in] mem Memory with certificate cache. + * @param [in] sz Size of certificate cache in bytes * @return 1 on success. * @return BAD_FUNC_ARG when ctx or mem is NULL. * @return BAD_FUNC_ARG when sz is less than or equal to zero. @@ -957,7 +959,7 @@ int wolfSSL_CTX_get_cert_cache_memsize(WOLFSSL_CTX* ctx) * * The WOLFSSL_CTX referenced is untouched. * - * @param [in] ssl SSL/TLS object. + * @param [in, out] ssl SSL/TLS object. * @return 1 on success. * @return BAD_FUNC_ARG when ssl is NULL. */ @@ -974,10 +976,10 @@ int wolfSSL_UnloadCertsKeys(WOLFSSL* ssl) if (ssl->buffers.weOwnCert && (!ssl->keepCert)) { WOLFSSL_MSG("Unloading cert"); FreeDer(&ssl->buffers.certificate); - #ifdef KEEP_OUR_CERT + #ifdef KEEP_OUR_CERT wolfSSL_X509_free(ssl->ourCert); ssl->ourCert = NULL; - #endif + #endif ssl->buffers.weOwnCert = 0; } @@ -992,13 +994,13 @@ int wolfSSL_UnloadCertsKeys(WOLFSSL* ssl) if ((ssl->buffers.key != NULL) && (ssl->buffers.key->buffer != NULL)) ForceZero(ssl->buffers.key->buffer, ssl->buffers.key->length); FreeDer(&ssl->buffers.key); - #ifdef WOLFSSL_BLIND_PRIVATE_KEY + #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.keyMask); - #endif + #endif ssl->buffers.weOwnKey = 0; } - #ifdef WOLFSSL_DUAL_ALG_CERTS + #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->buffers.weOwnAltKey) { WOLFSSL_MSG("Unloading alt key"); if ((ssl->buffers.altKey != NULL) && @@ -1007,12 +1009,12 @@ int wolfSSL_UnloadCertsKeys(WOLFSSL* ssl) ssl->buffers.altKey->length); } FreeDer(&ssl->buffers.altKey); - #ifdef WOLFSSL_BLIND_PRIVATE_KEY + #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.altKeyMask); - #endif + #endif ssl->buffers.weOwnAltKey = 0; } - #endif /* WOLFSSL_DUAL_ALG_CERTS */ + #endif /* WOLFSSL_DUAL_ALG_CERTS */ } return ret; @@ -1047,6 +1049,7 @@ int wolfSSL_CTX_UnloadCAs(WOLFSSL_CTX* ctx) * @param [in] ctx SSL/TLS context. * @return 1 on success. * @return BAD_FUNC_ARG when ctx or ctx->cm is NULL. + * @return BAD_STATE_E when another reference to the context is held. * @return BAD_MUTEX_E when locking fails. */ int wolfSSL_CTX_UnloadIntermediateCerts(WOLFSSL_CTX* ctx) @@ -1109,7 +1112,7 @@ int wolfSSL_CTX_Unload_trust_peers(WOLFSSL_CTX* ctx) #ifdef WOLFSSL_LOCAL_X509_STORE /* Unload trusted peers from the certificate manager of the SSL/TLS object. * - * @param [in] ctx SSL/TLS context. + * @param [in, out] ssl SSL/TLS object. * @return 1 on success. * @return BAD_FUNC_ARG when ssl is NULL. * @return BAD_MUTEX_E when locking fails. @@ -1118,7 +1121,7 @@ int wolfSSL_Unload_trust_peers(WOLFSSL* ssl) { int ret; - WOLFSSL_ENTER("wolfSSL_CTX_Unload_trust_peers"); + WOLFSSL_ENTER("wolfSSL_Unload_trust_peers"); /* Validate parameter. */ if (ssl == NULL) { @@ -1200,8 +1203,8 @@ int wolfSSL_CTX_add_client_CA(WOLFSSL_CTX* ctx, WOLFSSL_X509* x509) /* Add a client's CA to SSL/TLS object. * - * @param [in] ssl SSL/TLS object. - * @param [in] x509 X509 certificate. + * @param [in, out] ssl SSL/TLS object. + * @param [in] x509 X509 certificate. * @return 1 on success. * @return 0 on failure. */ @@ -1268,8 +1271,8 @@ int wolfSSL_CTX_add1_to_CA_list(WOLFSSL_CTX* ctx, WOLFSSL_X509* x509) /* Add a CA to SSL/TLS object. * - * @param [in] ssl SSL/TLS object. - * @param [in] x509 X509 certificate. + * @param [in, out] ssl SSL/TLS object. + * @param [in] x509 X509 certificate. * @return 1 on success. * @return 0 on failure. */ @@ -1531,7 +1534,7 @@ WOLF_STACK_OF(WOLFSSL_X509_NAME)* wolfSSL_load_client_CA_file(const char* fname) * for client authentication as an option. Have this return NULL in * that case. If OPENSSL_EXTRA is enabled, go ahead and include * the function. */ -#ifdef OPENSSL_EXTRA + #ifdef OPENSSL_EXTRA WOLFSSL_STACK *list = NULL; WOLFSSL_BIO* bio = NULL; WOLFSSL_X509 *cert = NULL; @@ -1593,10 +1596,10 @@ WOLF_STACK_OF(WOLFSSL_X509_NAME)* wolfSSL_load_client_CA_file(const char* fname) } wolfSSL_BIO_free(bio); return list; -#else + #else (void)fname; return NULL; -#endif + #endif } #endif /* !NO_BIO */ #endif /* WOLFSSL_NO_CA_NAMES */ @@ -1630,9 +1633,10 @@ WOLFSSL_X509_STORE* wolfSSL_CTX_get_cert_store(const WOLFSSL_CTX* ctx) /* Set the certificate store of the SSL/TLS context. * - * @param [in] ctx SSL/TLS context. - * @return X509 certificate store on success. - * @return NULL when ctx is NULL. + * The store is not taken when it shares the context's certificate manager. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] str X509 certificate store to use. */ void wolfSSL_CTX_set_cert_store(WOLFSSL_CTX* ctx, WOLFSSL_X509_STORE* str) { @@ -1659,7 +1663,7 @@ void wolfSSL_CTX_set_cert_store(WOLFSSL_CTX* ctx, WOLFSSL_X509_STORE* str) /* Context has ownership and free it with context free. */ ctx->cm->x509_store_p = ctx->x509_store_pt; -#ifdef OPENSSL_EXTRA + #ifdef OPENSSL_EXTRA /* Non-self-signed certs (intermediates) added via * X509_STORE_add_cert only go into store->certs, not the * CertManager. Push them into the CM now so that all @@ -1668,17 +1672,21 @@ void wolfSSL_CTX_set_cert_store(WOLFSSL_CTX* ctx, WOLFSSL_X509_STORE* str) WOLFSSL_MSG("wolfSSL_CTX_set_cert_store: failed to push some " "certs to CertManager"); } -#endif + #endif } } #ifdef OPENSSL_ALL /* Set certificate store into SSL/TLS context but don't take ownership. + * + * A NULL store clears any store previously set on the context, which reverts + * it to using the store it owns. This matches OpenSSL, where a NULL store + * clears the verify store rather than being rejected. * * @param [in] ctx SSL/TLS context. - * @param [in] str Certificate store. + * @param [in] str Certificate store. NULL to clear. * @return 1 on success. - * @return 0 when ctx or str is NULL or on other error. + * @return 0 when ctx is NULL or on other error. */ int wolfSSL_CTX_set1_verify_cert_store(WOLFSSL_CTX* ctx, WOLFSSL_X509_STORE* str) @@ -1688,10 +1696,16 @@ int wolfSSL_CTX_set1_verify_cert_store(WOLFSSL_CTX* ctx, WOLFSSL_ENTER("wolfSSL_CTX_set1_verify_cert_store"); /* Validate parameters. */ - if ((ctx == NULL) || (str == NULL)) { + if (ctx == NULL) { WOLFSSL_MSG("Bad parameter"); ret = 0; } + /* Clear any store set on the context - revert to the one it owns. */ + else if (str == NULL) { + wolfSSL_X509_STORE_free(ctx->x509_store_pt); + ctx->x509_store_pt = NULL; + ret = 1; + } /* Nothing to do when store being set is the same as existing in context. */ else if (str == CTX_STORE(ctx)) { ret = 1; @@ -1714,13 +1728,27 @@ int wolfSSL_CTX_set1_verify_cert_store(WOLFSSL_CTX* ctx, #endif -/* Set certificate store into SSL/TLS object. +/* Set the certificate store used for verification by the SSL/TLS object. * - * @param [in] ssl SSL/TLS object. - * @param [in] str Certificate store. - * @param [in] ref Take a reference to passed in certificate store. + * Ownership: with ref set, a reference is taken here (set1 semantics); + * without it the caller's reference is handed over (set0 semantics). Either + * way this call becomes responsible for exactly one reference, which is kept + * with the stored pointer or released when no pointer is kept. + * + * A set0 caller must therefore own a reference. Handing over a borrowed + * pointer, such as one straight from wolfSSL_CTX_get_cert_store(), releases + * a reference the caller never took and can destroy a store still in use. + * + * The object uses the context's store by keeping no pointer of its own, so + * that is how both a NULL store, which clears, and being handed the store the + * context already uses are done. Clearing on NULL rather than rejecting it + * matches OpenSSL. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] str X509 certificate store to use. NULL to clear. + * @param [in] ref Whether to take a reference to the store. * @return 1 on success. - * @return 0 when ssl or str is NULL or on other error. + * @return 0 when ssl is NULL, or the reference cannot be taken. */ static int wolfssl_set_verify_cert_store(WOLFSSL *ssl, WOLFSSL_X509_STORE* str, int ref) @@ -1730,28 +1758,30 @@ static int wolfssl_set_verify_cert_store(WOLFSSL *ssl, WOLFSSL_X509_STORE* str, WOLFSSL_ENTER("wolfssl_set_verify_cert_store"); /* Validate parameters. */ - if ((ssl == NULL) || (str == NULL)) { + if (ssl == NULL) { WOLFSSL_MSG("Bad parameter"); ret = 0; } - /* Nothing to do when store being set is the same as existing in object. */ - else if (str == SSL_STORE(ssl)) { - ret = 1; - } - else if (ref && (wolfSSL_X509_STORE_up_ref(str) != 1)) { + /* Take the reference to become responsible for before releasing any the + * object holds - they may be references to the same store. */ + else if (ref && (str != NULL) && (wolfSSL_X509_STORE_up_ref(str) != 1)) { WOLFSSL_MSG("wolfSSL_X509_STORE_up_ref error"); ret = 0; } else { - /* Free any external store. */ + /* The object uses the context's store by keeping no pointer, so that + * is what being handed that store means here. A NULL store, which + * clears, is kept as it is and so keeps no pointer either. */ + WOLFSSL_X509_STORE* keep = (str == CTX_STORE(ssl->ctx)) ? NULL : str; + + /* Release the store held, if any, and take on the new one. */ wolfSSL_X509_STORE_free(ssl->x509_store_pt); - if (str == ssl->ctx->x509_store_pt) { - /* Setting ctx store - just revert to using that instead. */ - ssl->x509_store_pt = NULL; - } - else { - /* Ref count increased - store pointer and free with object free. */ - ssl->x509_store_pt = str; + ssl->x509_store_pt = keep; + if (keep == NULL) { + /* No pointer kept, so release the reference this call is + * responsible for: taken above for set1, handed over for set0. + * Does nothing when clearing, as there is no store. */ + wolfSSL_X509_STORE_free(str); } ret = 1; } @@ -1760,11 +1790,14 @@ static int wolfssl_set_verify_cert_store(WOLFSSL *ssl, WOLFSSL_X509_STORE* str, } /* Set certificate store into SSL/TLS object and take ownership. + * + * The caller must own a reference to the store - it is consumed here. A NULL + * store clears any store previously set on the object. * * @param [in] ssl SSL/TLS object. - * @param [in] str Certificate store. + * @param [in] str Certificate store. NULL to clear. * @return 1 on success. - * @return 0 when ssl or str is NULL or on other error. + * @return 0 when ssl is NULL or on other error. */ int wolfSSL_set0_verify_cert_store(WOLFSSL *ssl, WOLFSSL_X509_STORE* str) { @@ -1774,11 +1807,13 @@ int wolfSSL_set0_verify_cert_store(WOLFSSL *ssl, WOLFSSL_X509_STORE* str) } /* Set certificate store into SSL/TLS object but don't take ownership. + * + * A NULL store clears any store previously set on the object. * * @param [in] ssl SSL/TLS object. - * @param [in] str Certificate store. + * @param [in] str Certificate store. NULL to clear. * @return 1 on success. - * @return 0 when ssl or str is NULL or on other error. + * @return 0 when ssl is NULL or on other error. */ int wolfSSL_set1_verify_cert_store(WOLFSSL *ssl, WOLFSSL_X509_STORE* str) { @@ -1813,7 +1848,7 @@ WOLFSSL_X509* wolfSSL_CTX_get0_certificate(WOLFSSL_CTX* ctx) if (ctx->certificate == NULL) { WOLFSSL_MSG("Ctx Certificate buffer not set!"); } - #ifndef WOLFSSL_X509_STORE_CERTS + #ifndef WOLFSSL_X509_STORE_CERTS else { /* Create a certificate object from raw data. */ ctx->ourCert = wolfSSL_X509_d2i_ex(NULL, @@ -1821,7 +1856,7 @@ WOLFSSL_X509* wolfSSL_CTX_get0_certificate(WOLFSSL_CTX* ctx) ctx->heap); ctx->ownOurCert = 1; } - #endif + #endif } /* Return certificate cached against SSL/TLS context. */ ret = ctx->ourCert; @@ -1832,7 +1867,7 @@ WOLFSSL_X509* wolfSSL_CTX_get0_certificate(WOLFSSL_CTX* ctx) /* Get the certificate in the SSL/TLS object. * - * @param [in] ssl SSL/TLS object. + * @param [in, out] ssl SSL/TLS object. * @return Certificate being sent to peer. * @return NULL when ssl is NULL, no certificate set or on other error. */ @@ -1868,14 +1903,14 @@ WOLFSSL_X509* wolfSSL_get_certificate(WOLFSSL* ssl) if (ssl->buffers.certificate == NULL) { WOLFSSL_MSG("Certificate buffer not set!"); } - #ifndef WOLFSSL_X509_STORE_CERTS + #ifndef WOLFSSL_X509_STORE_CERTS else { /* Create a certificate object from raw data. */ ssl->ourCert = wolfSSL_X509_d2i_ex(NULL, ssl->buffers.certificate->buffer, (int)ssl->buffers.certificate->length, ssl->heap); } - #endif + #endif } /* Return certificate cached against SSL/TLS object. */ ret = ssl->ourCert; @@ -1957,11 +1992,11 @@ int wolfSSL_cmp_peer_cert_to_file(WOLFSSL* ssl, const char *fname) ret = WOLFSSL_FATAL_ERROR; } else { - #ifdef WOLFSSL_SMALL_STACK + #ifdef WOLFSSL_SMALL_STACK byte staticBuffer[1]; /* force heap usage */ - #else + #else byte staticBuffer[FILE_BUFFER_SIZE]; - #endif + #endif byte* myBuf = staticBuffer; XFILE file; long sz = 0; @@ -2197,7 +2232,7 @@ WOLFSSL_X509* wolfSSL_get_chain_X509(WOLFSSL_X509_CHAIN* chain, int idx) int wolfSSL_get_chain_cert_pem(WOLFSSL_X509_CHAIN* chain, int idx, unsigned char* buf, int inLen, int* outLen) { -#ifdef WOLFSSL_DER_TO_PEM + #ifdef WOLFSSL_DER_TO_PEM int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_get_chain_cert_pem"); @@ -2234,7 +2269,7 @@ int wolfSSL_get_chain_cert_pem(WOLFSSL_X509_CHAIN* chain, int idx, } return ret; -#elif defined(WOLFSSL_PEM_TO_DER) + #elif defined(WOLFSSL_PEM_TO_DER) int ret = WOLFSSL_SUCCESS; const char* header = NULL; const char* footer = NULL; @@ -2300,14 +2335,14 @@ int wolfSSL_get_chain_cert_pem(WOLFSSL_X509_CHAIN* chain, int idx, } return ret; -#else + #else (void)chain; (void)idx; (void)buf; (void)inLen; (void)outLen; return WOLFSSL_FAILURE; -#endif /* WOLFSSL_PEM_TO_DER || WOLFSSL_DER_TO_PEM */ + #endif /* WOLFSSL_PEM_TO_DER || WOLFSSL_DER_TO_PEM */ } #endif /* SESSION_CERTS */ @@ -2409,11 +2444,11 @@ int wolfSSL_get_verify_mode(const WOLFSSL* ssl) if (ssl->options.failNoCertxPSK) { mode |= WOLFSSL_VERIFY_FAIL_EXCEPT_PSK; } -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) + #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) if (ssl->options.verifyPostHandshake) { mode |= WOLFSSL_VERIFY_POST_HANDSHAKE; } -#endif + #endif } WOLFSSL_LEAVE("wolfSSL_get_verify_mode", mode); @@ -2450,11 +2485,11 @@ int wolfSSL_CTX_get_verify_mode(const WOLFSSL_CTX* ctx) if (ctx->failNoCertxPSK) { mode |= WOLFSSL_VERIFY_FAIL_EXCEPT_PSK; } -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) + #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) if (ctx->verifyPostHandshake) { mode |= WOLFSSL_VERIFY_POST_HANDSHAKE; } -#endif + #endif } WOLFSSL_LEAVE("wolfSSL_CTX_get_verify_mode", mode); @@ -2633,331 +2668,473 @@ int wolfSSL_get0_chain_certs(WOLFSSL *ssl, WOLF_STACK_OF(WOLFSSL_X509) **sk) #endif #ifdef WOLFSSL_CERT_SETUP_CB -#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) - /* registers client cert callback, called during handshake if server - requests client auth but user has not loaded client cert/key */ - void wolfSSL_CTX_set_client_cert_cb(WOLFSSL_CTX *ctx, client_cert_cb cb) - { - WOLFSSL_ENTER("wolfSSL_CTX_set_client_cert_cb"); +/* ctx->CBClientCert is only in the structure under OPENSSL_EXTRA, so the + * setter cannot be compiled more widely than that. */ +#ifdef OPENSSL_EXTRA +/* Set the callback that supplies a client certificate and key. + * + * Called during the handshake when the server asks for client authentication + * and no certificate and key have been loaded. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Callback to call. NULL to clear. + */ +void wolfSSL_CTX_set_client_cert_cb(WOLFSSL_CTX *ctx, client_cert_cb cb) +{ + WOLFSSL_ENTER("wolfSSL_CTX_set_client_cert_cb"); - if (ctx != NULL) { - ctx->CBClientCert = cb; - } + if (ctx != NULL) { + ctx->CBClientCert = cb; } +} #endif - /* Set the certificate setup callback on the SSL/TLS CTX object. - * - * The callback is called during the handshake to allow the certificate and - * key to be chosen or loaded on demand. - * - * @param [in, out] ctx SSL/TLS CTX object. - * @param [in] cb Certificate setup callback. NULL to clear. - * @param [in] arg Context to pass to the callback. - */ - void wolfSSL_CTX_set_cert_cb(WOLFSSL_CTX* ctx, - CertSetupCallback cb, void *arg) - { - WOLFSSL_ENTER("wolfSSL_CTX_set_cert_cb"); - if (ctx == NULL) - return; +/* Set the certificate setup callback on the SSL/TLS CTX object. + * + * The callback is called during the handshake to allow the certificate and + * key to be chosen or loaded on demand. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Certificate setup callback. NULL to clear. + * @param [in] arg Context to pass to the callback. + */ +void wolfSSL_CTX_set_cert_cb(WOLFSSL_CTX* ctx, + CertSetupCallback cb, void *arg) +{ + WOLFSSL_ENTER("wolfSSL_CTX_set_cert_cb"); + if (ctx != NULL) { ctx->certSetupCb = cb; ctx->certSetupCbArg = arg; } +} - /** - * Internal wrapper for calling certSetupCb - * @param ssl The SSL/TLS Object - * @return 0 on success - */ - int CertSetupCbWrapper(WOLFSSL* ssl) - { - int ret = 0; - if (ssl->ctx->certSetupCb != NULL) { - WOLFSSL_MSG("Calling user cert setup callback"); - ret = ssl->ctx->certSetupCb(ssl, ssl->ctx->certSetupCbArg); - if (ret == 1) { - WOLFSSL_MSG("User cert callback returned success"); - ret = 0; - } - else if (ret == 0) { - SendAlert(ssl, alert_fatal, internal_error); - ret = CLIENT_CERT_CB_ERROR; - } - else if (ret < 0) { - ret = WOLFSSL_ERROR_WANT_X509_LOOKUP; - } - else { - WOLFSSL_MSG("Unexpected user callback return"); - ret = CLIENT_CERT_CB_ERROR; - } +/* Call the certificate setup callback and translate its result. + * + * @param [in, out] ssl SSL/TLS object. + * @return 0 when no callback is set or the callback reported success. + * @return CLIENT_CERT_CB_ERROR when the callback failed or returned an + * unrecognized value. A fatal alert is sent when it failed. + * @return WOLFSSL_ERROR_WANT_X509_LOOKUP when the callback returned a + * negative value to ask to be called again. + */ +int CertSetupCbWrapper(WOLFSSL* ssl) +{ + int ret = 0; + + if (ssl->ctx->certSetupCb != NULL) { + WOLFSSL_MSG("Calling user cert setup callback"); + ret = ssl->ctx->certSetupCb(ssl, ssl->ctx->certSetupCbArg); + if (ret == 1) { + WOLFSSL_MSG("User cert callback returned success"); + ret = 0; + } + else if (ret == 0) { + SendAlert(ssl, alert_fatal, internal_error); + ret = CLIENT_CERT_CB_ERROR; + } + else if (ret < 0) { + ret = WOLFSSL_ERROR_WANT_X509_LOOKUP; + } + else { + WOLFSSL_MSG("Unexpected user callback return"); + ret = CLIENT_CERT_CB_ERROR; } - return ret; } + return ret; +} #endif /* WOLFSSL_CERT_SETUP_CB */ #ifdef SESSION_CERTS - /* Decode the X509 DER encoded certificate into a WOLFSSL_X509 object. - * - * x509 WOLFSSL_X509 object to decode into. - * in X509 DER data. - * len Length of the X509 DER data. - * returns the new certificate on success, otherwise NULL. - */ - static int DecodeToX509(WOLFSSL_X509* x509, const byte* in, int len) - { - int ret; - WC_DECLARE_VAR(cert, DecodedCert, 1, 0); - if (x509 == NULL || in == NULL || len <= 0) - return BAD_FUNC_ARG; +/* Decode the X509 DER encoded certificate into a WOLFSSL_X509 object. + * + * @param [in, out] x509 WOLFSSL_X509 object to decode into. + * @param [in] in X509 DER data. + * @param [in] len Length of the X509 DER data. + * @return 0 on success. + * @return BAD_FUNC_ARG when x509 or in is NULL, or len is not positive. + * @return MEMORY_E when dynamic memory allocation fails. + * @return Other negative value when the certificate cannot be parsed. + */ +static int DecodeToX509(WOLFSSL_X509* x509, const byte* in, int len) +{ + int ret = 0; + WC_DECLARE_VAR(cert, DecodedCert, 1, 0); + /* Validate parameters. */ + if ((x509 == NULL) || (in == NULL) || (len <= 0)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { WC_ALLOC_VAR_EX(cert, DecodedCert, 1, NULL, DYNAMIC_TYPE_DCERT, - return MEMORY_E); + ret = MEMORY_E); + } - /* Create a DecodedCert object and copy fields into WOLFSSL_X509 object. - */ + if (ret == 0) { + /* Create a DecodedCert object and copy fields into WOLFSSL_X509 + * object. */ InitDecodedCert(cert, (byte*)in, (word32)len, NULL); - if ((ret = ParseCertRelative(cert, CERT_TYPE, 0, NULL, NULL)) == 0) { - /* Check if x509 was not previously initialized by wolfSSL_X509_new() */ - if (x509->dynamicMemory != TRUE) + ret = ParseCertRelative(cert, CERT_TYPE, 0, NULL, NULL); + if (ret == 0) { + /* Initialize when not done by wolfSSL_X509_new() already. */ + if (x509->dynamicMemory != TRUE) { InitX509(x509, 0, NULL); + } ret = CopyDecodedToX509(x509, cert); } FreeDecodedCert(cert); WC_FREE_VAR_EX(cert, NULL, DYNAMIC_TYPE_DCERT); - - return ret; } + + return ret; +} #endif /* SESSION_CERTS */ #ifdef KEEP_PEER_CERT - /* Get a copy of the peer's certificate. - * - * The certificate is decoded from the session chain when not already - * available on the object. Caller must free the returned certificate with - * wolfSSL_X509_free(). - * - * @param [in, out] ssl SSL/TLS object. - * @return Peer's X509 certificate on success. - * @return NULL when ssl is NULL, no peer certificate was kept or dynamic - * memory allocation fails. - */ - WOLFSSL_ABI - WOLFSSL_X509* wolfSSL_get_peer_certificate(WOLFSSL* ssl) - { - WOLFSSL_X509* ret = NULL; - WOLFSSL_ENTER("wolfSSL_get_peer_certificate"); - if (ssl != NULL) { - if (ssl->peerCert.issuer.sz) +/* Get a copy of the peer's certificate. + * + * The certificate is decoded from the session chain when not already + * available on the object. Caller must free the returned certificate with + * wolfSSL_X509_free(). + * + * @param [in, out] ssl SSL/TLS object. + * @return Peer's X509 certificate on success. + * @return NULL when ssl is NULL, no peer certificate was kept or dynamic + * memory allocation fails. + */ +WOLFSSL_ABI +WOLFSSL_X509* wolfSSL_get_peer_certificate(WOLFSSL* ssl) +{ + WOLFSSL_X509* ret = NULL; + + WOLFSSL_ENTER("wolfSSL_get_peer_certificate"); + + if (ssl != NULL) { + if (ssl->peerCert.issuer.sz > 0) { + ret = wolfSSL_X509_dup(&ssl->peerCert); + } + #ifdef SESSION_CERTS + else if (ssl->session->chain.count > 0) { + if (DecodeToX509(&ssl->peerCert, + ssl->session->chain.certs[0].buffer, + ssl->session->chain.certs[0].length) == 0) { ret = wolfSSL_X509_dup(&ssl->peerCert); -#ifdef SESSION_CERTS - else if (ssl->session->chain.count > 0) { - if (DecodeToX509(&ssl->peerCert, - ssl->session->chain.certs[0].buffer, - ssl->session->chain.certs[0].length) == 0) { - ret = wolfSSL_X509_dup(&ssl->peerCert); - } } -#endif } - WOLFSSL_LEAVE("wolfSSL_get_peer_certificate", ret != NULL); - return ret; + #endif } + WOLFSSL_LEAVE("wolfSSL_get_peer_certificate", ret != NULL); + return ret; +} #endif /* KEEP_PEER_CERT */ #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) -/* Return stack of peer certs. - * Caller does not need to free return. The stack is Free'd when WOLFSSL* ssl - * is. +/* Get the stack of the peer's certificates. + * + * The stack is owned by the SSL/TLS object and is disposed of with it, so the + * caller must not free it. + * + * @param [in, out] ssl SSL/TLS object. Declared const, but the chain is + * built into it on the first call. + * @return Stack of the peer's certificates on success. + * @return NULL when ssl is NULL or no chain was received. */ WOLF_STACK_OF(WOLFSSL_X509)* wolfSSL_get_peer_cert_chain(const WOLFSSL* ssl) { - WOLFSSL_ENTER("wolfSSL_get_peer_cert_chain"); + WOLF_STACK_OF(WOLFSSL_X509)* ret = NULL; - if (ssl == NULL) - return NULL; + WOLFSSL_ENTER("wolfSSL_get_peer_cert_chain"); - /* Try to populate if NULL or empty */ - if (ssl->peerCertChain == NULL || - wolfSSL_sk_X509_num(ssl->peerCertChain) == 0) { - wolfSSL_set_peer_cert_chain((WOLFSSL*) ssl); + if (ssl != NULL) { + /* Try to populate when not present or empty. */ + if ((ssl->peerCertChain == NULL) || + (wolfSSL_sk_X509_num(ssl->peerCertChain) == 0)) { + wolfSSL_set_peer_cert_chain((WOLFSSL*)ssl); + } + ret = ssl->peerCertChain; } - return ssl->peerCertChain; -} + return ret; +} -static int x509GetIssuerFromCM(WOLFSSL_X509 **issuer, WOLFSSL_CERT_MANAGER* cm, - WOLFSSL_X509 *x); -/** - * Recursively push the issuer CA chain onto the stack - * @param cm The cert manager that is queried for the issuer - * @param x This cert's issuer will be queried in cm - * @param sk The issuer is pushed onto this stack - * @return 0 on success or no issuer found - * WOLFSSL_FATAL_ERROR on a fatal error +/* Push the chain of issuing CAs onto the stack. + * + * Each certificate's issuer is looked up in turn, stopping when no further + * issuer is known or the maximum chain depth is reached. + * + * @param [in] cm Certificate manager queried for each issuer. + * @param [in] x Certificate whose issuer is looked up first. + * @param [in, out] sk Stack the issuers are pushed onto. + * @return 0 on success or when no issuer was found. + * @return WOLFSSL_FATAL_ERROR when an issuer could not be pushed. */ static int PushCAx509Chain(WOLFSSL_CERT_MANAGER* cm, WOLFSSL_X509 *x, WOLFSSL_STACK* sk) { + int ret = 0; int i; - for (i = 0; i < MAX_CHAIN_DEPTH; i++) { + + for (i = 0; (ret == 0) && (i < MAX_CHAIN_DEPTH); i++) { WOLFSSL_X509* issuer = NULL; - if (x509GetIssuerFromCM(&issuer, cm, x) != WOLFSSL_SUCCESS) + + /* No more issuers known - chain is as complete as it can be. */ + if (x509GetIssuerFromCM(&issuer, cm, x) != WOLFSSL_SUCCESS) { break; + } if (wolfSSL_sk_X509_push(sk, issuer) <= 0) { + /* Not stored on the stack - dispose of it here. */ wolfSSL_X509_free(issuer); - issuer = NULL; - return WOLFSSL_FATAL_ERROR; + ret = WOLFSSL_FATAL_ERROR; + } + else { + x = issuer; } - x = issuer; } - return 0; + + return ret; } -/* Builds up and creates a stack of peer certificates for ssl->peerCertChain - or ssl->verifiedChain based off of the ssl session chain. Attempts to place - CA certificates at the bottom of the stack for a verified chain. Returns - stack of WOLFSSL_X509 certs or NULL on failure */ +/* Decode one certificate of the session chain onto the stack. + * + * On the last certificate of a verified chain the CA chain known for it is + * appended as well. + * + * @param [in] ssl SSL/TLS object. + * @param [in] idx Index of the certificate in the chain. + * @param [in] verifiedFlag Whether to append the known CA chain. + * @param [in, out] sk Stack to add the certificate to. + * @return 0 on success. + * @return MEMORY_E when the certificate object cannot be created. + * @return Other negative value when the certificate cannot be decoded or + * stored. + */ +static int PushPeerCertToChain(const WOLFSSL* ssl, int idx, int verifiedFlag, + WOLFSSL_STACK* sk) +{ + int ret; + WOLFSSL_X509* x509 = wolfSSL_X509_new_ex(ssl->heap); + + if (x509 == NULL) { + WOLFSSL_MSG("Error Creating X509"); + ret = MEMORY_E; + } + else { + ret = DecodeToX509(x509, ssl->session->chain.certs[idx].buffer, + ssl->session->chain.certs[idx].length); + if (ret == 0) { + if (wolfSSL_sk_X509_push(sk, x509) <= 0) { + ret = WOLFSSL_FATAL_ERROR; + } + else { + if ((idx == ssl->session->chain.count - 1) && + (verifiedFlag)) { + /* On the last certificate of a verified chain, append the + * CA chain known for it. The certificate is needed to look + * the issuers up, so this is done before the reference to + * it is dropped below. */ + SSL_CM_WARNING(ssl); + ret = PushCAx509Chain(SSL_CM(ssl), x509, sk); + } + /* The stack owns the certificate from here on. */ + x509 = NULL; + } + } + if (ret != 0) { + WOLFSSL_MSG("Error decoding cert"); + /* NULL once the stack has taken ownership, and freeing NULL does + * nothing, so this only releases a certificate that never got + * there. */ + wolfSSL_X509_free(x509); + } + } + + return ret; +} + +/* Build a stack of the peer's certificates from the session chain. + * + * For a verified chain the CA certificates known for the last certificate are + * placed at the bottom of the stack. + * + * @param [in] ssl SSL/TLS object. + * @param [in] verifiedFlag Whether to append the known CA chain. + * @return Stack of the peer's certificates on success. + * @return NULL when ssl is NULL, the session holds no chain, or a certificate + * cannot be created, decoded or stored. + */ static WOLF_STACK_OF(WOLFSSL_X509)* CreatePeerCertChain(const WOLFSSL* ssl, int verifiedFlag) { - WOLFSSL_STACK* sk; - WOLFSSL_X509* x509; - int i = 0; - int err; + WOLFSSL_STACK* sk = NULL; + int err = 0; - WOLFSSL_ENTER("wolfSSL_set_peer_cert_chain"); - if ((ssl == NULL) || (ssl->session->chain.count == 0)) - return NULL; + WOLFSSL_ENTER("CreatePeerCertChain"); - sk = wolfSSL_sk_X509_new_null(); - if (sk == NULL) { - WOLFSSL_MSG("Error Creating sk"); - return NULL; + /* There is nothing to build from without a session chain. */ + if ((ssl == NULL) || (ssl->session->chain.count == 0)) { + err = 1; + } + else { + sk = wolfSSL_sk_X509_new_null(); + if (sk == NULL) { + WOLFSSL_MSG("Error creating stack"); + err = 1; + } } - for (i = 0; i < ssl->session->chain.count; i++) { - x509 = wolfSSL_X509_new_ex(ssl->heap); - if (x509 == NULL) { - WOLFSSL_MSG("Error Creating X509"); - wolfSSL_sk_X509_pop_free(sk, NULL); - return NULL; - } - err = DecodeToX509(x509, ssl->session->chain.certs[i].buffer, - ssl->session->chain.certs[i].length); - if (err == 0 && wolfSSL_sk_X509_push(sk, x509) <= 0) - err = WOLFSSL_FATAL_ERROR; - if (err == 0 && i == ssl->session->chain.count-1 && verifiedFlag) { - /* On the last element in the verified chain try to add the CA chain - * if we have one for this cert */ - SSL_CM_WARNING(ssl); - err = PushCAx509Chain(SSL_CM(ssl), x509, sk); - } - if (err != 0) { - WOLFSSL_MSG("Error decoding cert"); - wolfSSL_X509_free(x509); - x509 = NULL; - wolfSSL_sk_X509_pop_free(sk, NULL); - return NULL; + if (!err) { + int i; + + for (i = 0; i < ssl->session->chain.count; i++) { + if (PushPeerCertToChain(ssl, i, verifiedFlag, sk) != 0) { + err = 1; + break; + } } } + if (err) { + /* Certificates already pushed are freed with the stack. */ + wolfSSL_sk_X509_pop_free(sk, NULL); + sk = NULL; + } + return sk; } -/* Builds up and creates a stack of peer certificates for ssl->peerCertChain - returns the stack on success and NULL on failure */ +/* Build and store the stack of the peer's certificates. + * + * On the server the leaf certificate is moved out of the stack and kept as the + * session's peer. The stack is disposed of when the SSL/TLS object is. + * + * @param [in, out] ssl SSL/TLS object. + * @return Stack of the peer's certificates on success. + * @return NULL when ssl is NULL, the session holds no chain, or the stack + * cannot be built. + */ WOLF_STACK_OF(WOLFSSL_X509)* wolfSSL_set_peer_cert_chain(WOLFSSL* ssl) { - WOLFSSL_STACK* sk; + WOLFSSL_STACK* sk = NULL; WOLFSSL_ENTER("wolfSSL_set_peer_cert_chain"); - if ((ssl == NULL) || (ssl->session->chain.count == 0)) - return NULL; - sk = CreatePeerCertChain(ssl, 0); + /* Validate parameters. */ + if ((ssl != NULL) && (ssl->session->chain.count > 0)) { + sk = CreatePeerCertChain(ssl, 0); + } if (sk != NULL) { if (ssl->options.side == WOLFSSL_SERVER_END) { - if (ssl->session->peer) + /* Replace any peer kept from a previous call. */ + if (ssl->session->peer != NULL) { wolfSSL_X509_free(ssl->session->peer); + } ssl->session->peer = wolfSSL_sk_X509_shift(sk); ssl->session->peerVerifyRet = ssl->peerVerifyRet; } - if (ssl->peerCertChain != NULL) + if (ssl->peerCertChain != NULL) { wolfSSL_sk_X509_pop_free(ssl->peerCertChain, NULL); + } /* This is Free'd when ssl is Free'd */ ssl->peerCertChain = sk; } + return sk; } #ifdef KEEP_PEER_CERT -/** - * Implemented in a similar way that ngx_ssl_ocsp_validate does it when - * SSL_get0_verified_chain is not available. - * @param ssl WOLFSSL object to extract certs from - * @return Stack of verified certs +/* Get the peer's certificate chain, verified against the store. + * + * Implemented in a similar way to ngx_ssl_ocsp_validate() when + * SSL_get0_verified_chain is not available. The chain is stored on the SSL/TLS + * object and disposed of with it, so the caller must not free it. + * + * @param [in, out] ssl SSL/TLS object. Declared const, but the verified + * chain is stored into it. + * @return Stack of verified certificates on success. + * @return NULL when ssl or its context is NULL, no peer certificate was kept, + * the chain cannot be built, or verification fails. */ WOLF_STACK_OF(WOLFSSL_X509) *wolfSSL_get0_verified_chain(const WOLFSSL *ssl) { WOLF_STACK_OF(WOLFSSL_X509)* chain = NULL; WOLFSSL_X509_STORE_CTX* storeCtx = NULL; WOLFSSL_X509* peerCert = NULL; + int err = 0; WOLFSSL_ENTER("wolfSSL_get0_verified_chain"); - if (ssl == NULL || ssl->ctx == NULL) { + /* Validate parameters. */ + if ((ssl == NULL) || (ssl->ctx == NULL)) { WOLFSSL_MSG("Bad parameter"); - return NULL; + err = 1; } - peerCert = wolfSSL_get_peer_certificate((WOLFSSL*)ssl); - if (peerCert == NULL) { - WOLFSSL_MSG("wolfSSL_get_peer_certificate error"); - return NULL; - } - /* wolfSSL_get_peer_certificate returns a copy. We want the internal - * member so that we don't have to worry about free'ing it. We call - * wolfSSL_get_peer_certificate so that we don't have to worry about - * setting up the internal pointer. */ - wolfSSL_X509_free(peerCert); - peerCert = (WOLFSSL_X509*)&ssl->peerCert; - chain = CreatePeerCertChain((WOLFSSL*)ssl, 1); - if (chain == NULL) { - WOLFSSL_MSG("wolfSSL_get_peer_cert_chain error"); - return NULL; + if (!err) { + peerCert = wolfSSL_get_peer_certificate((WOLFSSL*)ssl); + if (peerCert == NULL) { + WOLFSSL_MSG("wolfSSL_get_peer_certificate error"); + err = 1; + } + else { + /* wolfSSL_get_peer_certificate returns a copy. We want the + * internal member so that we don't have to worry about free'ing + * it. We call wolfSSL_get_peer_certificate so that we don't have + * to worry about setting up the internal pointer. */ + wolfSSL_X509_free(peerCert); + peerCert = (WOLFSSL_X509*)&ssl->peerCert; + } } - if (ssl->verifiedChain != NULL) { - wolfSSL_sk_X509_pop_free(ssl->verifiedChain, NULL); + if (!err) { + chain = CreatePeerCertChain((WOLFSSL*)ssl, 1); + if (chain == NULL) { + WOLFSSL_MSG("wolfSSL_get_peer_cert_chain error"); + err = 1; + } + else { + /* Replace any chain kept from a previous call. */ + if (ssl->verifiedChain != NULL) { + wolfSSL_sk_X509_pop_free(ssl->verifiedChain, NULL); + } + /* This is Free'd when ssl is Free'd */ + ((WOLFSSL*)ssl)->verifiedChain = chain; + } } - ((WOLFSSL*)ssl)->verifiedChain = chain; - storeCtx = wolfSSL_X509_STORE_CTX_new(); - if (storeCtx == NULL) { - WOLFSSL_MSG("wolfSSL_X509_STORE_CTX_new error"); - return NULL; - } - if (wolfSSL_X509_STORE_CTX_init(storeCtx, SSL_STORE(ssl), - peerCert, chain) != WOLFSSL_SUCCESS) { - WOLFSSL_MSG("wolfSSL_X509_STORE_CTX_init error"); - wolfSSL_X509_STORE_CTX_free(storeCtx); - return NULL; + if (!err) { + storeCtx = wolfSSL_X509_STORE_CTX_new(); + if (storeCtx == NULL) { + WOLFSSL_MSG("wolfSSL_X509_STORE_CTX_new error"); + err = 1; + } } - if (wolfSSL_X509_verify_cert(storeCtx) <= 0) { - WOLFSSL_MSG("wolfSSL_X509_verify_cert error"); - wolfSSL_X509_STORE_CTX_free(storeCtx); - return NULL; + + if (!err) { + if (wolfSSL_X509_STORE_CTX_init(storeCtx, SSL_STORE(ssl), peerCert, + chain) != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("wolfSSL_X509_STORE_CTX_init error"); + err = 1; + } + else if (wolfSSL_X509_verify_cert(storeCtx) <= 0) { + WOLFSSL_MSG("wolfSSL_X509_verify_cert error"); + err = 1; + } } + wolfSSL_X509_STORE_CTX_free(storeCtx); + if (err) { + /* The chain stays owned by the object; report failure only. */ + chain = NULL; + } + return chain; } #endif /* KEEP_PEER_CERT */ diff --git a/src/ssl_api_crl_ocsp.c b/src/ssl_api_crl_ocsp.c index 3971572cab1..4c64c7d1590 100644 --- a/src/ssl_api_crl_ocsp.c +++ b/src/ssl_api_crl_ocsp.c @@ -31,181 +31,406 @@ #ifdef HAVE_CRL +/* Load a CRL from a buffer into the context. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] buff Buffer holding the CRL. + * @param [in] sz Length of the buffer in bytes. + * @param [in] type Format of the data: PEM or DER. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_LoadCRLBuffer(WOLFSSL_CTX* ctx, const unsigned char* buff, long sz, int type) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_LoadCRLBuffer"); - if (ctx == NULL) - return BAD_FUNC_ARG; + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerLoadCRLBuffer(ctx->cm, buff, sz, type); + } - return wolfSSL_CertManagerLoadCRLBuffer(ctx->cm, buff, sz, type); + return ret; } +/* Load a CRL from a buffer into the object. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] buff Buffer holding the CRL. + * @param [in] sz Length of the buffer in bytes. + * @param [in] type Format of the data: PEM or DER. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl or its context is NULL. + */ int wolfSSL_LoadCRLBuffer(WOLFSSL* ssl, const unsigned char* buff, long sz, int type) { + int ret; + WOLFSSL_ENTER("wolfSSL_LoadCRLBuffer"); - if (ssl == NULL || ssl->ctx == NULL) - return BAD_FUNC_ARG; + if ((ssl == NULL) || (ssl->ctx == NULL)) { + ret = BAD_FUNC_ARG; + } + else { + SSL_CM_WARNING(ssl); + ret = wolfSSL_CertManagerLoadCRLBuffer(SSL_CM(ssl), buff, sz, type); + } - SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerLoadCRLBuffer(SSL_CM(ssl), buff, sz, type); + return ret; } +/* Turn on CRL checking for the object. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] options Options to apply. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_EnableCRL(WOLFSSL* ssl, int options) { + int ret; + WOLFSSL_ENTER("wolfSSL_EnableCRL"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerEnableCRL(SSL_CM(ssl), options); + ret = wolfSSL_CertManagerEnableCRL(SSL_CM(ssl), options); } - else - return BAD_FUNC_ARG; + + return ret; } +/* Turn off CRL checking for the object. + * + * @param [in, out] ssl SSL/TLS object. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_DisableCRL(WOLFSSL* ssl) { + int ret; + WOLFSSL_ENTER("wolfSSL_DisableCRL"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerDisableCRL(SSL_CM(ssl)); + ret = wolfSSL_CertManagerDisableCRL(SSL_CM(ssl)); } - else - return BAD_FUNC_ARG; + + return ret; } #ifndef NO_FILESYSTEM +/* Load CRLs from a directory into the object. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] path Path of the directory to load CRLs from. + * @param [in] type Format of the data: PEM or DER. + * @param [in] monitor Whether to monitor the directory for changes. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_LoadCRL(WOLFSSL* ssl, const char* path, int type, int monitor) { + int ret; + WOLFSSL_ENTER("wolfSSL_LoadCRL"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerLoadCRL(SSL_CM(ssl), path, type, monitor); + ret = wolfSSL_CertManagerLoadCRL(SSL_CM(ssl), path, type, monitor); } - else - return BAD_FUNC_ARG; + + return ret; } +/* Load a CRL from a file into the object. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] file Path of the file to load the CRL from. + * @param [in] type Format of the data: PEM or DER. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_LoadCRLFile(WOLFSSL* ssl, const char* file, int type) { + int ret; + WOLFSSL_ENTER("wolfSSL_LoadCRLFile"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerLoadCRLFile(SSL_CM(ssl), file, type); + ret = wolfSSL_CertManagerLoadCRLFile(SSL_CM(ssl), file, type); } - else - return BAD_FUNC_ARG; + + return ret; } #endif +/* Set the callback called when a CRL is missing. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] cb Callback to call. NULL to clear. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_SetCRL_Cb(WOLFSSL* ssl, CbMissingCRL cb) { + int ret; + WOLFSSL_ENTER("wolfSSL_SetCRL_Cb"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerSetCRL_Cb(SSL_CM(ssl), cb); + ret = wolfSSL_CertManagerSetCRL_Cb(SSL_CM(ssl), cb); } - else - return BAD_FUNC_ARG; + + return ret; } +/* Set the callback called when a CRL check fails. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] cb Callback to call. NULL to clear. + * @param [in] ctx Context to pass to the callback. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_SetCRL_ErrorCb(WOLFSSL* ssl, crlErrorCb cb, void* ctx) { - WOLFSSL_ENTER("wolfSSL_SetCRL_Cb"); - if (ssl) { + int ret; + + WOLFSSL_ENTER("wolfSSL_SetCRL_ErrorCb"); + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerSetCRL_ErrorCb(SSL_CM(ssl), cb, ctx); + ret = wolfSSL_CertManagerSetCRL_ErrorCb(SSL_CM(ssl), cb, ctx); } - else - return BAD_FUNC_ARG; + + return ret; } #ifdef HAVE_CRL_IO +/* Set the callback used to fetch a CRL. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] cb Callback to call. NULL to clear. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_SetCRL_IOCb(WOLFSSL* ssl, CbCrlIO cb) { - WOLFSSL_ENTER("wolfSSL_SetCRL_Cb"); - if (ssl) { + int ret; + + WOLFSSL_ENTER("wolfSSL_SetCRL_IOCb"); + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerSetCRL_IOCb(SSL_CM(ssl), cb); + ret = wolfSSL_CertManagerSetCRL_IOCb(SSL_CM(ssl), cb); } - else - return BAD_FUNC_ARG; + + return ret; } #endif +/* Turn on CRL checking for the context. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] options Options to apply. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_EnableCRL(WOLFSSL_CTX* ctx, int options) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_EnableCRL"); - if (ctx) - return wolfSSL_CertManagerEnableCRL(ctx->cm, options); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerEnableCRL(ctx->cm, options); + } + + return ret; } +/* Turn off CRL checking for the context. + * + * @param [in, out] ctx SSL/TLS context. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_DisableCRL(WOLFSSL_CTX* ctx) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_DisableCRL"); - if (ctx) - return wolfSSL_CertManagerDisableCRL(ctx->cm); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerDisableCRL(ctx->cm); + } + + return ret; } #ifndef NO_FILESYSTEM +/* Load CRLs from a directory into the context. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] path Path of the directory to load CRLs from. + * @param [in] type Format of the data: PEM or DER. + * @param [in] monitor Whether to monitor the directory for changes. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_LoadCRL(WOLFSSL_CTX* ctx, const char* path, int type, int monitor) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_LoadCRL"); - if (ctx) - return wolfSSL_CertManagerLoadCRL(ctx->cm, path, type, monitor); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerLoadCRL(ctx->cm, path, type, monitor); + } + + return ret; } +/* Load a CRL from a file into the context. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] file Path of the file to load the CRL from. + * @param [in] type Format of the data: PEM or DER. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_LoadCRLFile(WOLFSSL_CTX* ctx, const char* file, int type) { - WOLFSSL_ENTER("wolfSSL_CTX_LoadCRL"); - if (ctx) - return wolfSSL_CertManagerLoadCRLFile(ctx->cm, file, type); - else - return BAD_FUNC_ARG; + int ret; + + WOLFSSL_ENTER("wolfSSL_CTX_LoadCRLFile"); + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerLoadCRLFile(ctx->cm, file, type); + } + + return ret; } #endif +/* Set the callback called when a CRL is missing. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] cb Callback to call. NULL to clear. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_SetCRL_Cb(WOLFSSL_CTX* ctx, CbMissingCRL cb) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_SetCRL_Cb"); - if (ctx) - return wolfSSL_CertManagerSetCRL_Cb(ctx->cm, cb); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerSetCRL_Cb(ctx->cm, cb); + } + + return ret; } +/* Set the callback called when a CRL check fails. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] cb Callback to call. NULL to clear. + * @param [in] cbCtx Context to pass to the callback. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_SetCRL_ErrorCb(WOLFSSL_CTX* ctx, crlErrorCb cb, void* cbCtx) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_SetCRL_ErrorCb"); - if (ctx) - return wolfSSL_CertManagerSetCRL_ErrorCb(ctx->cm, cb, cbCtx); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerSetCRL_ErrorCb(ctx->cm, cb, cbCtx); + } + + return ret; } #ifdef HAVE_CRL_IO +/* Set the callback used to fetch a CRL. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] cb Callback to call. NULL to clear. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_SetCRL_IOCb(WOLFSSL_CTX* ctx, CbCrlIO cb) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_SetCRL_IOCb"); - if (ctx) - return wolfSSL_CertManagerSetCRL_IOCb(ctx->cm, cb); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerSetCRL_IOCb(ctx->cm, cb); + } + + return ret; } #endif @@ -213,221 +438,493 @@ int wolfSSL_CTX_SetCRL_IOCb(WOLFSSL_CTX* ctx, CbCrlIO cb) #ifdef HAVE_OCSP +/* Turn on OCSP checking for the object. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] options Options to apply. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_EnableOCSP(WOLFSSL* ssl, int options) { + int ret; + WOLFSSL_ENTER("wolfSSL_EnableOCSP"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerEnableOCSP(SSL_CM(ssl), options); + ret = wolfSSL_CertManagerEnableOCSP(SSL_CM(ssl), options); } - else - return BAD_FUNC_ARG; + + return ret; } +/* Turn off OCSP checking for the object. + * + * @param [in, out] ssl SSL/TLS object. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_DisableOCSP(WOLFSSL* ssl) { + int ret; + WOLFSSL_ENTER("wolfSSL_DisableOCSP"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerDisableOCSP(SSL_CM(ssl)); + ret = wolfSSL_CertManagerDisableOCSP(SSL_CM(ssl)); } - else - return BAD_FUNC_ARG; + + return ret; } +/* Turn on OCSP stapling for the object. + * + * @param [in, out] ssl SSL/TLS object. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_EnableOCSPStapling(WOLFSSL* ssl) { + int ret; + WOLFSSL_ENTER("wolfSSL_EnableOCSPStapling"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerEnableOCSPStapling(SSL_CM(ssl)); + ret = wolfSSL_CertManagerEnableOCSPStapling(SSL_CM(ssl)); } - else - return BAD_FUNC_ARG; + + return ret; } +/* Turn off OCSP stapling for the object. + * + * @param [in, out] ssl SSL/TLS object. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_DisableOCSPStapling(WOLFSSL* ssl) { + int ret; + WOLFSSL_ENTER("wolfSSL_DisableOCSPStapling"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerDisableOCSPStapling(SSL_CM(ssl)); + ret = wolfSSL_CertManagerDisableOCSPStapling(SSL_CM(ssl)); } - else - return BAD_FUNC_ARG; + + return ret; } +/* Set the responder URL to use instead of the one in the certificate. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] url URL of the responder to use. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_SetOCSP_OverrideURL(WOLFSSL* ssl, const char* url) { + int ret; + WOLFSSL_ENTER("wolfSSL_SetOCSP_OverrideURL"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); - return wolfSSL_CertManagerSetOCSPOverrideURL(SSL_CM(ssl), url); + ret = wolfSSL_CertManagerSetOCSPOverrideURL(SSL_CM(ssl), url); } - else - return BAD_FUNC_ARG; + + return ret; } +/* Set the callbacks used to fetch and release an OCSP response. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] ioCb I/O callback to call. NULL to clear. + * @param [in] respFreeCb Callback that releases a response. NULL to + * clear. + * @param [in] ioCbCtx Context to pass to the I/O callback. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_SetOCSP_Cb(WOLFSSL* ssl, CbOCSPIO ioCb, CbOCSPRespFree respFreeCb, void* ioCbCtx) { + int ret; + WOLFSSL_ENTER("wolfSSL_SetOCSP_Cb"); - if (ssl) { + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { SSL_CM_WARNING(ssl); ssl->ocspIOCtx = ioCbCtx; /* use SSL specific ioCbCtx */ - return wolfSSL_CertManagerSetOCSP_Cb(SSL_CM(ssl), - ioCb, respFreeCb, NULL); + ret = wolfSSL_CertManagerSetOCSP_Cb(SSL_CM(ssl), ioCb, respFreeCb, + NULL); } - else - return BAD_FUNC_ARG; + + return ret; } +/* Turn on OCSP checking for the context. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] options Options to apply. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_EnableOCSP(WOLFSSL_CTX* ctx, int options) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_EnableOCSP"); - if (ctx) - return wolfSSL_CertManagerEnableOCSP(ctx->cm, options); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerEnableOCSP(ctx->cm, options); + } + + return ret; } +/* Turn off OCSP checking for the context. + * + * @param [in, out] ctx SSL/TLS context. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_DisableOCSP(WOLFSSL_CTX* ctx) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_DisableOCSP"); - if (ctx) - return wolfSSL_CertManagerDisableOCSP(ctx->cm); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerDisableOCSP(ctx->cm); + } + + return ret; } +/* Set the responder URL to use instead of the one in the certificate. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] url URL of the responder to use. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_SetOCSP_OverrideURL(WOLFSSL_CTX* ctx, const char* url) { - WOLFSSL_ENTER("wolfSSL_SetOCSP_OverrideURL"); - if (ctx) - return wolfSSL_CertManagerSetOCSPOverrideURL(ctx->cm, url); - else - return BAD_FUNC_ARG; + int ret; + + WOLFSSL_ENTER("wolfSSL_CTX_SetOCSP_OverrideURL"); + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerSetOCSPOverrideURL(ctx->cm, url); + } + + return ret; } +/* Set the callbacks used to fetch and release an OCSP response. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] ioCb I/O callback to call. NULL to clear. + * @param [in] respFreeCb Callback that releases a response. NULL to + * clear. + * @param [in] ioCbCtx Context to pass to the I/O callback. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_SetOCSP_Cb(WOLFSSL_CTX* ctx, CbOCSPIO ioCb, CbOCSPRespFree respFreeCb, void* ioCbCtx) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_SetOCSP_Cb"); - if (ctx) - return wolfSSL_CertManagerSetOCSP_Cb(ctx->cm, ioCb, - respFreeCb, ioCbCtx); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerSetOCSP_Cb(ctx->cm, ioCb, + respFreeCb, ioCbCtx); + } + + return ret; } #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) \ || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) +/* Turn on OCSP stapling for the context. + * + * @param [in, out] ctx SSL/TLS context. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_EnableOCSPStapling(WOLFSSL_CTX* ctx) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_EnableOCSPStapling"); - if (ctx) - return wolfSSL_CertManagerEnableOCSPStapling(ctx->cm); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerEnableOCSPStapling(ctx->cm); + } + + return ret; } +/* Turn off OCSP stapling for the context. + * + * @param [in, out] ctx SSL/TLS context. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_DisableOCSPStapling(WOLFSSL_CTX* ctx) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_DisableOCSPStapling"); - if (ctx) - return wolfSSL_CertManagerDisableOCSPStapling(ctx->cm); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerDisableOCSPStapling(ctx->cm); + } + + return ret; } +/* Require the peer to staple an OCSP response. + * + * @param [in, out] ctx SSL/TLS context. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_EnableOCSPMustStaple(WOLFSSL_CTX* ctx) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_EnableOCSPMustStaple"); - if (ctx) - return wolfSSL_CertManagerEnableOCSPMustStaple(ctx->cm); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerEnableOCSPMustStaple(ctx->cm); + } + + return ret; } +/* Stop requiring the peer to staple an OCSP response. + * + * @param [in, out] ctx SSL/TLS context. + * @return Result of the certificate manager operation. + * @return BAD_FUNC_ARG when ctx is NULL. + */ int wolfSSL_CTX_DisableOCSPMustStaple(WOLFSSL_CTX* ctx) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_DisableOCSPMustStaple"); - if (ctx) - return wolfSSL_CertManagerDisableOCSPMustStaple(ctx->cm); - else - return BAD_FUNC_ARG; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CertManagerDisableOCSPMustStaple(ctx->cm); + } + + return ret; } #endif /* HAVE_CERTIFICATE_STATUS_REQUEST || \ * HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) -/* Not an OpenSSL API. */ +/* Get the OCSP response this side has staged to staple. + * + * The response is the one set by wolfSSL_set_tlsext_status_ocsp_resp(), which + * is what the status callback supplies for the server to send. Nothing stores + * a response received from the peer here. + * + * Not an OpenSSL API. + * + * @param [in] ssl SSL/TLS object. + * @param [out] response Staged response, which is NULL when there is + * none. + * @return Length of the response, which is zero when there is none or when + * a parameter is NULL. + */ int wolfSSL_get_ocsp_response(WOLFSSL* ssl, byte** response) { - *response = ssl->ocspCsrResp[0].buffer; - return ssl->ocspCsrResp[0].length; + int ret = 0; + + if ((ssl != NULL) && (response != NULL)) { + *response = ssl->ocspCsrResp[0].buffer; + ret = (int)ssl->ocspCsrResp[0].length; + } + + return ret; } -/* Not an OpenSSL API. */ +/* Get the OCSP responder URL set on the object. + * + * Not an OpenSSL API. + * + * @param [in] ssl SSL/TLS object. + * @return URL set on the object, which is NULL when none was set or when + * ssl is NULL. + */ char* wolfSSL_get_ocsp_url(WOLFSSL* ssl) { - return ssl->url; + char* ret = NULL; + + if (ssl != NULL) { + ret = ssl->url; + } + + return ret; } -/* Not an OpenSSL API. */ +/* Set the OCSP responder URL on the object. + * + * The string is not copied, so it must outlive the object. + * + * Not an OpenSSL API. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] url URL to use. NULL to clear. + * @return WOLFSSL_SUCCESS on success. + * @return WOLFSSL_FAILURE when ssl is NULL. + */ int wolfSSL_set_ocsp_url(WOLFSSL* ssl, char* url) { - if (ssl == NULL) - return WOLFSSL_FAILURE; + int ret = WOLFSSL_SUCCESS; + + if (ssl == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + ssl->url = url; + } - ssl->url = url; - return WOLFSSL_SUCCESS; + return ret; } #endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY */ #if !defined(NO_ASN_TIME) +/* Get the date the last OCSP response was produced. + * + * @param [in] ssl SSL/TLS object. + * @param [out] producedDate Buffer to hold the date. + * @param [in] producedDate_space Length of the buffer in bytes. + * @param [out] producedDateFormat Format of the date returned. + * @return 0 on success. + * @return BAD_FUNC_ARG when ssl is NULL, no response has been processed, or + * an output parameter is NULL. + * @return BUFFER_E when the buffer is too small for the date. + */ int wolfSSL_get_ocsp_producedDate( WOLFSSL *ssl, byte *producedDate, size_t producedDate_space, int *producedDateFormat) { - if ((ssl->ocspProducedDateFormat != ASN_UTC_TIME) && - (ssl->ocspProducedDateFormat != ASN_GENERALIZED_TIME)) - return BAD_FUNC_ARG; - - if ((producedDate == NULL) || (producedDateFormat == NULL)) - return BAD_FUNC_ARG; + int ret = 0; - if (XSTRLEN((char *)ssl->ocspProducedDate) >= producedDate_space) - return BUFFER_E; - - XSTRNCPY((char *)producedDate, (const char *)ssl->ocspProducedDate, - producedDate_space); - *producedDateFormat = ssl->ocspProducedDateFormat; + /* Validate parameter. */ + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + /* No response has been processed when no date format was recorded. */ + else if ((ssl->ocspProducedDateFormat != ASN_UTC_TIME) && + (ssl->ocspProducedDateFormat != ASN_GENERALIZED_TIME)) { + ret = BAD_FUNC_ARG; + } + else if ((producedDate == NULL) || (producedDateFormat == NULL)) { + ret = BAD_FUNC_ARG; + } + else if (XSTRLEN((char *)ssl->ocspProducedDate) >= producedDate_space) { + ret = BUFFER_E; + } + else { + XSTRNCPY((char *)producedDate, (const char *)ssl->ocspProducedDate, + producedDate_space); + *producedDateFormat = ssl->ocspProducedDateFormat; + } - return 0; + return ret; } -int wolfSSL_get_ocsp_producedDate_tm(WOLFSSL *ssl, struct tm *produced_tm) { +/* Get the date the last OCSP response was produced as a broken-down time. + * + * @param [in] ssl SSL/TLS object. + * @param [out] produced_tm Broken-down time to fill in. + * @return 0 on success. + * @return BAD_FUNC_ARG when ssl is NULL, no response has been processed, or + * produced_tm is NULL. + * @return ASN_PARSE_E when the date cannot be parsed. + */ +int wolfSSL_get_ocsp_producedDate_tm(WOLFSSL *ssl, struct tm *produced_tm) +{ + int ret = 0; int idx = 0; - if ((ssl->ocspProducedDateFormat != ASN_UTC_TIME) && - (ssl->ocspProducedDateFormat != ASN_GENERALIZED_TIME)) - return BAD_FUNC_ARG; - - if (produced_tm == NULL) - return BAD_FUNC_ARG; - - if (ExtractDate(ssl->ocspProducedDate, + /* Validate parameter. */ + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + /* No response has been processed when no date format was recorded. */ + else if ((ssl->ocspProducedDateFormat != ASN_UTC_TIME) && + (ssl->ocspProducedDateFormat != ASN_GENERALIZED_TIME)) { + ret = BAD_FUNC_ARG; + } + else if (produced_tm == NULL) { + ret = BAD_FUNC_ARG; + } + else if (!ExtractDate(ssl->ocspProducedDate, (unsigned char)ssl->ocspProducedDateFormat, produced_tm, &idx, - MAX_DATE_SIZE)) - return 0; - else - return ASN_PARSE_E; + MAX_DATE_SIZE)) { + ret = ASN_PARSE_E; + } + + return ret; } #endif /* !NO_ASN_TIME */ #endif /* HAVE_OCSP */ @@ -435,52 +932,108 @@ int wolfSSL_get_ocsp_producedDate_tm(WOLFSSL *ssl, struct tm *produced_tm) { #if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) #ifdef HAVE_CERTIFICATE_STATUS_REQUEST +/* Ask for an OCSP response to be stapled to the handshake. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] status_type Type of status request. + * @param [in] options Options to apply. + * @return Result of adding the extension. + * @return BAD_FUNC_ARG when ssl is NULL or the object is not a client. + */ int wolfSSL_UseOCSPStapling(WOLFSSL* ssl, byte status_type, byte options) { + int ret; + WOLFSSL_ENTER("wolfSSL_UseOCSPStapling"); - if (ssl == NULL || ssl->options.side != WOLFSSL_CLIENT_END) - return BAD_FUNC_ARG; + if ((ssl == NULL) || (ssl->options.side != WOLFSSL_CLIENT_END)) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseCertificateStatusRequest(&ssl->extensions, status_type, + options, NULL, ssl->heap, ssl->devId); + } - return TLSX_UseCertificateStatusRequest(&ssl->extensions, status_type, - options, NULL, ssl->heap, ssl->devId); + return ret; } +/* Ask for an OCSP response to be stapled to handshakes. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] status_type Type of status request. + * @param [in] options Options to apply. + * @return Result of adding the extension. + * @return BAD_FUNC_ARG when ctx is NULL or the context is not a client. + */ int wolfSSL_CTX_UseOCSPStapling(WOLFSSL_CTX* ctx, byte status_type, byte options) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_UseOCSPStapling"); - if (ctx == NULL || ctx->method->side != WOLFSSL_CLIENT_END) - return BAD_FUNC_ARG; + if ((ctx == NULL) || (ctx->method->side != WOLFSSL_CLIENT_END)) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseCertificateStatusRequest(&ctx->extensions, status_type, + options, NULL, ctx->heap, ctx->devId); + } - return TLSX_UseCertificateStatusRequest(&ctx->extensions, status_type, - options, NULL, ctx->heap, ctx->devId); + return ret; } #endif /* HAVE_CERTIFICATE_STATUS_REQUEST */ #ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2 +/* Ask for version 2 OCSP stapling on the handshake. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] status_type Type of status request. + * @param [in] options Options to apply. + * @return Result of adding the extension. + * @return BAD_FUNC_ARG when ssl is NULL or the object is not a client. + */ int wolfSSL_UseOCSPStaplingV2(WOLFSSL* ssl, byte status_type, byte options) { - if (ssl == NULL || ssl->options.side != WOLFSSL_CLIENT_END) - return BAD_FUNC_ARG; + int ret; + + if ((ssl == NULL) || (ssl->options.side != WOLFSSL_CLIENT_END)) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseCertificateStatusRequestV2(&ssl->extensions, status_type, + options, ssl->heap, ssl->devId); + } - return TLSX_UseCertificateStatusRequestV2(&ssl->extensions, status_type, - options, ssl->heap, ssl->devId); + return ret; } +/* Ask for version 2 OCSP stapling on handshakes. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] status_type Type of status request. + * @param [in] options Options to apply. + * @return Result of adding the extension. + * @return BAD_FUNC_ARG when ctx is NULL or the context is not a client. + */ int wolfSSL_CTX_UseOCSPStaplingV2(WOLFSSL_CTX* ctx, byte status_type, byte options) { - if (ctx == NULL || ctx->method->side != WOLFSSL_CLIENT_END) - return BAD_FUNC_ARG; + int ret; + + if ((ctx == NULL) || (ctx->method->side != WOLFSSL_CLIENT_END)) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseCertificateStatusRequestV2(&ctx->extensions, status_type, + options, ctx->heap, ctx->devId); + } - return TLSX_UseCertificateStatusRequestV2(&ctx->extensions, status_type, - options, ctx->heap, ctx->devId); + return ret; } #endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ @@ -488,134 +1041,240 @@ int wolfSSL_CTX_UseOCSPStaplingV2(WOLFSSL_CTX* ctx, byte status_type, #ifdef OPENSSL_EXTRA #ifdef HAVE_CERTIFICATE_STATUS_REQUEST +/* Ask for a certificate status of the given type. + * + * @param [in, out] s SSL/TLS object. + * @param [in] type Status request type. Only + * WOLFSSL_TLSEXT_STATUSTYPE_ocsp is supported. + * @return Result of adding the extension. + * @return BAD_FUNC_ARG when s is NULL. + * @return WOLFSSL_FAILURE when the type is not OCSP. + */ long wolfSSL_set_tlsext_status_type(WOLFSSL *s, int type) { + long ret; + WOLFSSL_ENTER("wolfSSL_set_tlsext_status_type"); - if (s == NULL){ - return BAD_FUNC_ARG; + if (s == NULL) { + ret = BAD_FUNC_ARG; } - - if (type == WOLFSSL_TLSEXT_STATUSTYPE_ocsp){ - int r = TLSX_UseCertificateStatusRequest(&s->extensions, (byte)type, 0, - s, s->heap, s->devId); - return (long)r; - } else { + else if (type != WOLFSSL_TLSEXT_STATUSTYPE_ocsp) { WOLFSSL_MSG( "SSL_set_tlsext_status_type only supports TLSEXT_STATUSTYPE_ocsp type."); - return WOLFSSL_FAILURE; + ret = WOLFSSL_FAILURE; + } + else { + ret = (long)TLSX_UseCertificateStatusRequest(&s->extensions, + (byte)type, 0, s, s->heap, s->devId); } + return ret; } +/* Get the type of certificate status requested. + * + * @param [in] s SSL/TLS object. + * @return WOLFSSL_TLSEXT_STATUSTYPE_ocsp when a status was requested. + * @return WOLFSSL_FATAL_ERROR when s is NULL or no status was requested. + */ long wolfSSL_get_tlsext_status_type(WOLFSSL *s) { - TLSX* extension; + long ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR); + + if (s != NULL) { + TLSX* extension = TLSX_Find(s->extensions, TLSX_STATUS_REQUEST); + + if (extension != NULL) { + ret = WOLFSSL_TLSEXT_STATUSTYPE_ocsp; + } + } - if (s == NULL) - return WOLFSSL_FATAL_ERROR; - extension = TLSX_Find(s->extensions, TLSX_STATUS_REQUEST); - return (extension != NULL) ? WOLFSSL_TLSEXT_STATUSTYPE_ocsp : - WOLFSSL_FATAL_ERROR; + return ret; } #endif /* HAVE_CERTIFICATE_STATUS_REQUEST */ #endif /* OPENSSL_EXTRA */ #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) +/* Get the callback that supplies the certificate status. + * + * @param [in] ctx SSL/TLS context. + * @param [out] cb Receives the status callback. + * @return WOLFSSL_SUCCESS on success. + * @return WOLFSSL_FAILURE when a parameter is NULL or stapling is not set up. + */ int wolfSSL_CTX_get_tlsext_status_cb(WOLFSSL_CTX* ctx, tlsextStatusCb* cb) { - if (ctx == NULL || ctx->cm == NULL || cb == NULL) - return WOLFSSL_FAILURE; - -#if !defined(NO_WOLFSSL_SERVER) && (defined(HAVE_CERTIFICATE_STATUS_REQUEST) \ - || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) - if (ctx->cm->ocsp_stapling == NULL) - return WOLFSSL_FAILURE; - - *cb = ctx->cm->ocsp_stapling->statusCb; -#else - (void)cb; - *cb = NULL; -#endif + int ret = WOLFSSL_SUCCESS; - return WOLFSSL_SUCCESS; + if ((ctx == NULL) || (ctx->cm == NULL) || (cb == NULL)) { + ret = WOLFSSL_FAILURE; + } + else { + #if !defined(NO_WOLFSSL_SERVER) && \ + (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) + if (ctx->cm->ocsp_stapling == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + *cb = ctx->cm->ocsp_stapling->statusCb; + } + #else + *cb = NULL; + #endif + } + return ret; } +/* Set the callback that supplies the certificate status. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] cb Callback to call. NULL to clear. + * @return WOLFSSL_SUCCESS on success. + * @return WOLFSSL_FAILURE when ctx is NULL or stapling cannot be turned on. + */ int wolfSSL_CTX_set_tlsext_status_cb(WOLFSSL_CTX* ctx, tlsextStatusCb cb) { - if (ctx == NULL || ctx->cm == NULL) - return WOLFSSL_FAILURE; - -#if !defined(NO_WOLFSSL_SERVER) && (defined(HAVE_CERTIFICATE_STATUS_REQUEST) \ - || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) - /* Ensure stapling is on for callback to be used. */ - wolfSSL_CTX_EnableOCSPStapling(ctx); + int ret = WOLFSSL_SUCCESS; - if (ctx->cm->ocsp_stapling == NULL) - return WOLFSSL_FAILURE; - - ctx->cm->ocsp_stapling->statusCb = cb; -#else - (void)cb; -#endif + if ((ctx == NULL) || (ctx->cm == NULL)) { + ret = WOLFSSL_FAILURE; + } + else { + #if !defined(NO_WOLFSSL_SERVER) && \ + (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) + /* Ensure stapling is on for callback to be used. */ + wolfSSL_CTX_EnableOCSPStapling(ctx); + + if (ctx->cm->ocsp_stapling == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + ctx->cm->ocsp_stapling->statusCb = cb; + } + #else + (void)cb; + #endif + } - return WOLFSSL_SUCCESS; + return ret; } +/* Set the argument passed to the certificate status callback. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] arg Argument to pass to the callback. + * @return WOLFSSL_SUCCESS on success. + * @return WOLFSSL_FAILURE when ctx is NULL or stapling cannot be turned on. + */ long wolfSSL_CTX_set_tlsext_status_arg(WOLFSSL_CTX* ctx, void* arg) { - if (ctx == NULL || ctx->cm == NULL) - return WOLFSSL_FAILURE; - -#if !defined(NO_WOLFSSL_SERVER) && (defined(HAVE_CERTIFICATE_STATUS_REQUEST) \ - || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) - /* Ensure stapling is on for callback to be used. */ - wolfSSL_CTX_EnableOCSPStapling(ctx); + long ret = WOLFSSL_SUCCESS; - if (ctx->cm->ocsp_stapling == NULL) - return WOLFSSL_FAILURE; - - ctx->cm->ocsp_stapling->statusCbArg = arg; -#else - (void)arg; -#endif + if ((ctx == NULL) || (ctx->cm == NULL)) { + ret = WOLFSSL_FAILURE; + } + else { + #if !defined(NO_WOLFSSL_SERVER) && \ + (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) + /* Ensure stapling is on for callback to be used. */ + wolfSSL_CTX_EnableOCSPStapling(ctx); + + if (ctx->cm->ocsp_stapling == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + ctx->cm->ocsp_stapling->statusCbArg = arg; + } + #else + (void)arg; + #endif + } - return WOLFSSL_SUCCESS; + return ret; } +/* Get the OCSP response this side has staged to staple. + * + * The response is the one set by wolfSSL_set_tlsext_status_ocsp_resp(), not + * one received from the peer. It stays owned by the SSL/TLS object, so the + * caller must not free it. + * + * @param [in] ssl SSL/TLS object. + * @param [out] resp Receives the stapled response, or NULL when there is + * none. + * @return Length of the response, which is zero when there is none. + */ long wolfSSL_get_tlsext_status_ocsp_resp(WOLFSSL *ssl, unsigned char **resp) { - if (ssl == NULL || resp == NULL) - return 0; + long ret = 0; + + if ((ssl != NULL) && (resp != NULL)) { + *resp = ssl->ocspCsrResp[0].buffer; + ret = (long)ssl->ocspCsrResp[0].length; + } - *resp = ssl->ocspCsrResp[0].buffer; - return (long)ssl->ocspCsrResp[0].length; + return ret; } +/* Set the OCSP response to staple for the first certificate. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] resp Response to store. Ownership is taken. + * @param [in] len Length of the response in bytes. + * @return WOLFSSL_SUCCESS on success. + * @return WOLFSSL_FAILURE when ssl is NULL or the response and length + * disagree. + */ long wolfSSL_set_tlsext_status_ocsp_resp(WOLFSSL *ssl, unsigned char *resp, int len) { return wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, resp, len, 0); } +/* Set the OCSP response to staple for one certificate of the chain. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] resp Response to store. Ownership is taken. + * @param [in] len Length of the response in bytes. + * @param [in] idx Index of the certificate the response is for. + * @return WOLFSSL_SUCCESS on success. + * @return WOLFSSL_FAILURE when ssl is NULL, idx is out of range, or the + * response and length disagree. + */ int wolfSSL_set_tlsext_status_ocsp_resp_multi(WOLFSSL* ssl, unsigned char *resp, int len, word32 idx) { - if (ssl == NULL || idx >= XELEM_CNT(ssl->ocspCsrResp) || len < 0) - return WOLFSSL_FAILURE; - if (!((resp == NULL) ^ (len > 0))) - return WOLFSSL_FAILURE; + int ret = WOLFSSL_SUCCESS; - XFREE(ssl->ocspCsrResp[idx].buffer, NULL, 0); - ssl->ocspCsrResp[idx].buffer = resp; - ssl->ocspCsrResp[idx].length = (word32)len; + if ((ssl == NULL) || (idx >= XELEM_CNT(ssl->ocspCsrResp)) || (len < 0)) { + ret = WOLFSSL_FAILURE; + } + /* A response and a length must be supplied together. */ + else if (!((resp == NULL) ^ (len > 0))) { + ret = WOLFSSL_FAILURE; + } + else { + XFREE(ssl->ocspCsrResp[idx].buffer, NULL, 0); + ssl->ocspCsrResp[idx].buffer = resp; + ssl->ocspCsrResp[idx].length = (word32)len; + } - return WOLFSSL_SUCCESS; + return ret; } #ifndef NO_WOLFSSL_SERVER +/* Set the callback that verifies a stapled OCSP response. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] cb Callback to call. NULL to clear. + * @param [in] cbArg Argument to pass to the callback. + */ void wolfSSL_CTX_set_ocsp_status_verify_cb(WOLFSSL_CTX* ctx, ocspVerifyStatusCb cb, void* cbArg) { @@ -653,16 +1312,24 @@ int wolfSSL_OCSP_parse_url(const char* url, char** host, char** port, const char* c; const char* upath; /* path in u */ const char* uport; /* port in u */ + const char* authEnd; /* end of the authority in u */ + const char* hostStart; const char* hostEnd; - - WOLFSSL_ENTER("OCSP_parse_url"); - - if (url == NULL || host == NULL || port == NULL || path == NULL || - ssl == NULL) { - return WOLFSSL_FAILURE; + int hostLen; + int portLen; + word32 portNum = 0; + const char* at; + int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); + + WOLFSSL_ENTER("wolfSSL_OCSP_parse_url"); + + /* Skip the cleanup below: the out parameters cannot be dereferenced when + * they are the ones that are NULL. */ + if ((url == NULL) || (host == NULL) || (port == NULL) || (path == NULL) || + (ssl == NULL)) { + goto done; } - u = url; *host = NULL; *port = NULL; *path = NULL; @@ -671,78 +1338,171 @@ int wolfSSL_OCSP_parse_url(const char* url, char** host, char** port, /* CR/LF in the parsed out host or path would split a request built from * them into extra header lines. */ for (c = url; *c != '\0'; c++) { - if (*c == '\r' || *c == '\n') { + if ((*c == '\r') || (*c == '\n')) { WOLFSSL_MSG("CR/LF in URL"); goto err; } } - if (*(u++) != 'h') goto err; - if (*(u++) != 't') goto err; - if (*(u++) != 't') goto err; - if (*(u++) != 'p') goto err; - if (*u == 's') { + /* Only http and https are understood. Each has a default port, which an + * explicit port in the URL replaces below. */ + if (XSTRNCMP(url, "http://", 7) == 0) { + u = url + 7; + *port = CopyString("80", -1, NULL, DYNAMIC_TYPE_OPENSSL); + } + else if (XSTRNCMP(url, "https://", 8) == 0) { *ssl = 1; - u++; + u = url + 8; *port = CopyString("443", -1, NULL, DYNAMIC_TYPE_OPENSSL); } - else if (*u == ':') { - *ssl = 0; - *port = CopyString("80", -1, NULL, DYNAMIC_TYPE_OPENSSL); - } - else + else { + WOLFSSL_MSG("URL scheme is not http or https"); goto err; - if (*port == NULL) + } + if (*port == NULL) { goto err; - if (*(u++) != ':') goto err; - if (*(u++) != '/') goto err; - if (*(u++) != '/') goto err; + } - /* Look for path */ + /* The path is everything from the first '/' after the scheme, and the + * authority is what comes before it. */ upath = XSTRSTR(u, "/"); - *path = CopyString(upath == NULL ? "/" : upath, -1, NULL, + authEnd = (upath != NULL) ? upath : (u + XSTRLEN(u)); + + *path = CopyString((upath == NULL) ? "/" : upath, -1, NULL, DYNAMIC_TYPE_OPENSSL); - if (*path == NULL) + if (*path == NULL) { goto err; + } - /* Look for port */ - uport = XSTRSTR(u, ":"); - if (uport != NULL) { - if (*(++uport) == '\0') + /* An IPv6 literal is bracketed and holds colons of its own, so the host + * is what is inside the brackets and any port follows the ']'. The + * brackets are not part of the host, matching wolfIO_DecodeUrl(). */ + hostStart = u; + uport = NULL; + if (*u == '[') { + for (c = u; (c < authEnd) && (*c != ']'); c++) { + /* Find the end of the literal. */ + } + if (c == authEnd) { + WOLFSSL_MSG("Unterminated IPv6 literal in URL"); goto err; - /* port must be before path */ - if (upath != NULL && uport >= upath) + } + hostStart = u + 1; + hostEnd = c; + c++; + /* Only a port may follow the literal. Anything else would be dropped + * silently, so reject it. */ + if ((c < authEnd) && (*c != ':')) { + WOLFSSL_MSG("Unexpected character after IPv6 literal in URL"); goto err; - XFREE(*port, NULL, DYNAMIC_TYPE_OPENSSL); - if (upath) - *port = CopyString(uport, (int)(upath - uport), NULL, - DYNAMIC_TYPE_OPENSSL); - else - *port = CopyString(uport, -1, NULL, DYNAMIC_TYPE_OPENSSL); - if (*port == NULL) + } + } + else { + c = u; + hostEnd = authEnd; + } + /* Responder URLs never carry userinfo, and an '@' would put the real + * host after it. Reject rather than fold it into the host or the port. */ + for (at = u; at < authEnd; at++) { + if (*at == '@') { + WOLFSSL_MSG("'@' in URL authority"); goto err; - hostEnd = uport - 1; + } + } + + /* The first ':' left in the authority separates the port. */ + for (; c < authEnd; c++) { + if (*c == ':') { + uport = c; + if (hostEnd == authEnd) { + hostEnd = c; + } + break; + } + } + + if (uport == NULL) { + /* No port in the authority. A ':' later on would have been the first + * in the URL, which is rejected rather than interpreted: these URLs + * come from certificates and it would leave the port of the request + * open to argument between this parser and any other. */ + if (upath != NULL) { + const char* p; + + for (p = upath; *p != '\0'; p++) { + if (*p == ':') { + WOLFSSL_MSG("':' in URL path with no port"); + goto err; + } + } + } } - else - hostEnd = upath; + else { + /* Step over the ':'. */ + uport++; + if (uport == authEnd) { + WOLFSSL_MSG("No port after ':' in URL"); + goto err; + } + /* Validate as wolfIO_DecodeUrl() does, so the two parsers cannot + * disagree about the same URL: digits only, at most five of them, + * and in range. Anything else would otherwise be copied through and + * reinterpreted downstream. */ + portLen = (int)(authEnd - uport); + if (portLen > 5) { + WOLFSSL_MSG("Port too long in URL"); + goto err; + } + for (c = uport; c < authEnd; c++) { + if ((*c < '0') || (*c > '9')) { + WOLFSSL_MSG("Non-digit in URL port"); + goto err; + } + portNum = (portNum * 10) + (word32)(*c - '0'); + } + if (portNum > WOLFSSL_MAX_16BIT) { + WOLFSSL_MSG("Port out of range in URL"); + goto err; + } - if (hostEnd) - *host = CopyString(u, (int)(hostEnd - u), NULL, DYNAMIC_TYPE_OPENSSL); - else - *host = CopyString(u, -1, NULL, DYNAMIC_TYPE_OPENSSL); + /* Replace the default port with the one in the URL. */ + XFREE(*port, NULL, DYNAMIC_TYPE_OPENSSL); + *port = CopyString(uport, portLen, NULL, DYNAMIC_TYPE_OPENSSL); + if (*port == NULL) { + goto err; + } + } - if (*host == NULL) + /* The length is computed rather than passed as -1 because CopyString() + * treats a length of zero as "copy the rest of the string", which would + * turn an empty host into the remainder of the URL. */ + hostLen = (int)(hostEnd - hostStart); + if (hostLen == 0) { + WOLFSSL_MSG("No host in URL"); goto err; + } + *host = CopyString(hostStart, hostLen, NULL, DYNAMIC_TYPE_OPENSSL); + if (*host == NULL) { + goto err; + } + + ret = WOLFSSL_SUCCESS; + goto done; - return WOLFSSL_SUCCESS; err: + /* Release anything parsed out so nothing is returned half-filled. The + * scheme flag is set before the authority is validated, so it is reset + * here too. */ XFREE(*host, NULL, DYNAMIC_TYPE_OPENSSL); *host = NULL; XFREE(*port, NULL, DYNAMIC_TYPE_OPENSSL); *port = NULL; XFREE(*path, NULL, DYNAMIC_TYPE_OPENSSL); *path = NULL; - return WOLFSSL_FAILURE; + *ssl = 0; + +done: + return ret; } #endif /* OPENSSL_EXTRA */ diff --git a/src/ssl_api_dtls.c b/src/ssl_api_dtls.c index d7262a6a7e2..2216c8d15e3 100644 --- a/src/ssl_api_dtls.c +++ b/src/ssl_api_dtls.c @@ -752,15 +752,17 @@ static WC_INLINE word32 UpdateHighwaterMark(word32 cur, word32 first, * * Used with multicast to install externally derived keys. * - * @param [in] ssl SSL/TLS object. - * @param [in] epoch DTLS epoch to use. - * @param [in] preMasterSecret Pre-master secret data. - * @param [in] preMasterSz Length of pre-master secret in bytes. - * @param [in] clientRandom Client random data (RAN_LEN bytes). - * @param [in] serverRandom Server random data (RAN_LEN bytes). - * @param [in] suite Cipher suite bytes (2). + * @param [in, out] ssl SSL/TLS object. + * @param [in] epoch DTLS epoch to use. + * @param [in] preMasterSecret Pre-master secret data. + * @param [in] preMasterSz Length of pre-master secret in bytes. + * @param [in] clientRandom Client random data (RAN_LEN bytes). + * @param [in] serverRandom Server random data (RAN_LEN bytes). + * @param [in] suite Cipher suite bytes (2). * @return WOLFSSL_SUCCESS on success. - * @return WOLFSSL_FATAL_ERROR on error, including invalid arguments. + * @return WOLFSSL_FATAL_ERROR on error, including invalid arguments and a + * failure to allocate the pre-master secret. The specific code is + * recorded in ssl->error and can be read with wolfSSL_get_error(). */ int wolfSSL_set_secret(WOLFSSL* ssl, word16 epoch, const byte* preMasterSecret, word32 preMasterSz, diff --git a/src/ssl_api_ext.c b/src/ssl_api_ext.c index d76521968bb..1199336b031 100644 --- a/src/ssl_api_ext.c +++ b/src/ssl_api_ext.c @@ -45,10 +45,16 @@ WOLFSSL_ABI int wolfSSL_UseSNI(WOLFSSL* ssl, byte type, const void* data, word16 size) { - if (ssl == NULL) - return BAD_FUNC_ARG; + int ret; - return TLSX_UseSNI(&ssl->extensions, type, data, size, ssl->heap); + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseSNI(&ssl->extensions, type, data, size, ssl->heap); + } + + return ret; } @@ -64,12 +70,18 @@ int wolfSSL_UseSNI(WOLFSSL* ssl, byte type, const void* data, word16 size) */ WOLFSSL_ABI int wolfSSL_CTX_UseSNI(WOLFSSL_CTX* ctx, byte type, const void* data, - word16 size) + word16 size) { - if (ctx == NULL) - return BAD_FUNC_ARG; + int ret; - return TLSX_UseSNI(&ctx->extensions, type, data, size, ctx->heap); + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseSNI(&ctx->extensions, type, data, size, ctx->heap); + } + + return ret; } #ifndef NO_WOLFSSL_SERVER @@ -123,13 +135,17 @@ byte wolfSSL_SNI_Status(WOLFSSL* ssl, byte type) */ word16 wolfSSL_SNI_GetRequest(WOLFSSL* ssl, byte type, void** data) { - if (data) + word16 ret = 0; + + if (data != NULL) { *data = NULL; + } - if (ssl && ssl->extensions) - return TLSX_SNI_GetRequest(ssl->extensions, type, data, 0); + if ((ssl != NULL) && (ssl->extensions != NULL)) { + ret = TLSX_SNI_GetRequest(ssl->extensions, type, data, 0); + } - return 0; + return ret; } @@ -146,10 +162,14 @@ word16 wolfSSL_SNI_GetRequest(WOLFSSL* ssl, byte type, void** data) int wolfSSL_SNI_GetFromBuffer(const byte* clientHello, word32 helloSz, byte type, byte* sni, word32* inOutSz) { - if (clientHello && helloSz > 0 && sni && inOutSz && *inOutSz > 0) - return TLSX_SNI_GetFromBuffer(clientHello, helloSz, type, sni, inOutSz); + int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); - return BAD_FUNC_ARG; + if ((clientHello != NULL) && (helloSz > 0) && (sni != NULL) && + (inOutSz != NULL) && (*inOutSz > 0)) { + ret = TLSX_SNI_GetFromBuffer(clientHello, helloSz, type, sni, inOutSz); + } + + return ret; } #endif /* !NO_WOLFSSL_SERVER */ @@ -172,29 +192,40 @@ int wolfSSL_SNI_GetFromBuffer(const byte* clientHello, word32 helloSz, int wolfSSL_UseTrustedCA(WOLFSSL* ssl, byte type, const byte* certId, word32 certIdSz) { - if (ssl == NULL) - return BAD_FUNC_ARG; + int ret = 0; - if (type == WOLFSSL_TRUSTED_CA_PRE_AGREED) { - if (certId != NULL || certIdSz != 0) - return BAD_FUNC_ARG; + /* Validate the identifier against the type it is announced as. */ + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else if (type == WOLFSSL_TRUSTED_CA_PRE_AGREED) { + if ((certId != NULL) || (certIdSz != 0)) { + ret = BAD_FUNC_ARG; + } } else if (type == WOLFSSL_TRUSTED_CA_X509_NAME) { - if (certId == NULL || certIdSz == 0) - return BAD_FUNC_ARG; + if ((certId == NULL) || (certIdSz == 0)) { + ret = BAD_FUNC_ARG; + } } #ifndef NO_SHA - else if (type == WOLFSSL_TRUSTED_CA_KEY_SHA1 || - type == WOLFSSL_TRUSTED_CA_CERT_SHA1) { - if (certId == NULL || certIdSz != WC_SHA_DIGEST_SIZE) - return BAD_FUNC_ARG; + else if ((type == WOLFSSL_TRUSTED_CA_KEY_SHA1) || + (type == WOLFSSL_TRUSTED_CA_CERT_SHA1)) { + if ((certId == NULL) || (certIdSz != WC_SHA_DIGEST_SIZE)) { + ret = BAD_FUNC_ARG; + } } #endif - else - return BAD_FUNC_ARG; + else { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + ret = TLSX_UseTrustedCA(&ssl->extensions, type, certId, certIdSz, + ssl->heap); + } - return TLSX_UseTrustedCA(&ssl->extensions, - type, certId, certIdSz, ssl->heap); + return ret; } #endif /* HAVE_TRUSTED_CA */ @@ -205,21 +236,29 @@ int wolfSSL_UseTrustedCA(WOLFSSL* ssl, byte type, /* Set the Maximum Fragment Length extension on the object. * - * @param [in] ssl SSL/TLS object. - * @param [in] mfl Maximum fragment length code, e.g. WOLFSSL_MFL_2_9. + * @param [in, out] ssl SSL/TLS object. + * @param [in] mfl Maximum fragment length code, e.g. WOLFSSL_MFL_2_9. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl is NULL. * @return Negative value on error. */ int wolfSSL_UseMaxFragment(WOLFSSL* ssl, byte mfl) { - if (ssl == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; + /* A separate flag rather than gating on ret: the reconfigure below + * succeeds without an extension being set, and both paths report + * WOLFSSL_SUCCESS. */ + int done = 0; -#ifdef WOLFSSL_ALLOW_MAX_FRAGMENT_ADJUST + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + done = 1; + } + + #ifdef WOLFSSL_ALLOW_MAX_FRAGMENT_ADJUST /* The following is a non-standard way to reconfigure the max packet size post-handshake for wolfSSL_write/wolfSSL_read */ - if (ssl->options.handShakeState == HANDSHAKE_DONE) { + if ((!done) && (ssl->options.handShakeState == HANDSHAKE_DONE)) { switch (mfl) { case WOLFSSL_MFL_2_8 : ssl->max_fragment = 256; break; case WOLFSSL_MFL_2_9 : ssl->max_fragment = 512; break; @@ -229,14 +268,19 @@ int wolfSSL_UseMaxFragment(WOLFSSL* ssl, byte mfl) case WOLFSSL_MFL_2_13: ssl->max_fragment = 8192; break; default: ssl->max_fragment = MAX_RECORD_SIZE; break; } - return WOLFSSL_SUCCESS; + /* Reconfigured directly, so the extension is not also set below. */ + done = 1; + } + #endif /* WOLFSSL_MAX_FRAGMENT_ADJUST */ + + if (!done) { + /* This call sets the max fragment TLS extension, which gets sent to + server. The server_hello response is what sets the + `ssl->max_fragment` in TLSX_MFL_Parse */ + ret = TLSX_UseMaxFragment(&ssl->extensions, mfl, ssl->heap); } -#endif /* WOLFSSL_MAX_FRAGMENT_ADJUST */ - /* This call sets the max fragment TLS extension, which gets sent to server. - The server_hello response is what sets the `ssl->max_fragment` in - TLSX_MFL_Parse */ - return TLSX_UseMaxFragment(&ssl->extensions, mfl, ssl->heap); + return ret; } @@ -250,10 +294,16 @@ int wolfSSL_UseMaxFragment(WOLFSSL* ssl, byte mfl) */ int wolfSSL_CTX_UseMaxFragment(WOLFSSL_CTX* ctx, byte mfl) { - if (ctx == NULL) - return BAD_FUNC_ARG; + int ret; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseMaxFragment(&ctx->extensions, mfl, ctx->heap); + } - return TLSX_UseMaxFragment(&ctx->extensions, mfl, ctx->heap); + return ret; } #endif /* NO_WOLFSSL_CLIENT */ @@ -271,10 +321,16 @@ int wolfSSL_CTX_UseMaxFragment(WOLFSSL_CTX* ctx, byte mfl) */ int wolfSSL_UseTruncatedHMAC(WOLFSSL* ssl) { - if (ssl == NULL) - return BAD_FUNC_ARG; + int ret; + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseTruncatedHMAC(&ssl->extensions, ssl->heap); + } - return TLSX_UseTruncatedHMAC(&ssl->extensions, ssl->heap); + return ret; } @@ -287,10 +343,16 @@ int wolfSSL_UseTruncatedHMAC(WOLFSSL* ssl) */ int wolfSSL_CTX_UseTruncatedHMAC(WOLFSSL_CTX* ctx) { - if (ctx == NULL) - return BAD_FUNC_ARG; + int ret; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseTruncatedHMAC(&ctx->extensions, ctx->heap); + } - return TLSX_UseTruncatedHMAC(&ctx->extensions, ctx->heap); + return ret; } #endif /* NO_WOLFSSL_CLIENT */ @@ -307,6 +369,8 @@ int wolfSSL_CTX_UseTruncatedHMAC(WOLFSSL_CTX* ctx) */ static int isValidCurveGroup(word16 name) { + int ret; + switch (name) { case WOLFSSL_ECC_SECP160K1: case WOLFSSL_ECC_SECP160R1: @@ -335,27 +399,27 @@ static int isValidCurveGroup(word16 name) case WOLFSSL_FFDHE_6144: case WOLFSSL_FFDHE_8192: -#ifdef WOLFSSL_HAVE_MLKEM -#ifndef WOLFSSL_NO_ML_KEM - #ifndef WOLFSSL_TLS_NO_MLKEM_STANDALONE + #ifdef WOLFSSL_HAVE_MLKEM + #ifndef WOLFSSL_NO_ML_KEM + #ifndef WOLFSSL_TLS_NO_MLKEM_STANDALONE case WOLFSSL_ML_KEM_512: case WOLFSSL_ML_KEM_768: case WOLFSSL_ML_KEM_1024: - #endif /* !WOLFSSL_TLS_NO_MLKEM_STANDALONE */ - #ifdef WOLFSSL_PQC_HYBRIDS + #endif /* !WOLFSSL_TLS_NO_MLKEM_STANDALONE */ + #ifdef WOLFSSL_PQC_HYBRIDS case WOLFSSL_SECP384R1MLKEM1024: case WOLFSSL_X25519MLKEM768: case WOLFSSL_SECP256R1MLKEM768: - #endif /* WOLFSSL_PQC_HYBRIDS */ - #ifdef WOLFSSL_EXTRA_PQC_HYBRIDS + #endif /* WOLFSSL_PQC_HYBRIDS */ + #ifdef WOLFSSL_EXTRA_PQC_HYBRIDS case WOLFSSL_SECP256R1MLKEM512: case WOLFSSL_SECP384R1MLKEM768: case WOLFSSL_SECP521R1MLKEM1024: case WOLFSSL_X25519MLKEM512: case WOLFSSL_X448MLKEM768: - #endif /* WOLFSSL_EXTRA_PQC_HYBRIDS */ -#endif /* !WOLFSSL_NO_ML_KEM */ -#ifdef WOLFSSL_MLKEM_KYBER + #endif /* WOLFSSL_EXTRA_PQC_HYBRIDS */ + #endif /* !WOLFSSL_NO_ML_KEM */ + #ifdef WOLFSSL_MLKEM_KYBER case WOLFSSL_KYBER_LEVEL1: case WOLFSSL_KYBER_LEVEL3: case WOLFSSL_KYBER_LEVEL5: @@ -366,35 +430,45 @@ static int isValidCurveGroup(word16 name) case WOLFSSL_X448_KYBER_LEVEL3: case WOLFSSL_X25519_KYBER_LEVEL3: case WOLFSSL_P256_KYBER_LEVEL3: -#endif /* WOLFSSL_MLKEM_KYBER */ -#endif - return 1; + #endif /* WOLFSSL_MLKEM_KYBER */ + #endif + ret = 1; + break; default: - return 0; + ret = 0; + break; } + + return ret; } /* Set a named group in the Supported Groups extension on the object. * - * @param [in] ssl SSL/TLS object. - * @param [in] name Named group identifier. + * @param [in, out] ssl SSL/TLS object. + * @param [in] name Named group identifier. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl is NULL or the group is invalid. * @return WOLFSSL_FAILURE when TLS is not compiled in. */ int wolfSSL_UseSupportedCurve(WOLFSSL* ssl, word16 name) { - if (ssl == NULL || !isValidCurveGroup(name)) - return BAD_FUNC_ARG; + int ret; - ssl->options.userCurves = 1; -#if defined(NO_TLS) - return WOLFSSL_FAILURE; -#else - return TLSX_UseSupportedCurve(&ssl->extensions, name, ssl->heap, - ssl->options.side); -#endif /* NO_TLS */ + if ((ssl == NULL) || (!isValidCurveGroup(name))) { + ret = BAD_FUNC_ARG; + } + else { + ssl->options.userCurves = 1; + #if defined(NO_TLS) + ret = WOLFSSL_FAILURE; + #else + ret = TLSX_UseSupportedCurve(&ssl->extensions, name, ssl->heap, + ssl->options.side); + #endif /* NO_TLS */ + } + + return ret; } @@ -408,16 +482,22 @@ int wolfSSL_UseSupportedCurve(WOLFSSL* ssl, word16 name) */ int wolfSSL_CTX_UseSupportedCurve(WOLFSSL_CTX* ctx, word16 name) { - if (ctx == NULL || !isValidCurveGroup(name)) - return BAD_FUNC_ARG; + int ret; - ctx->userCurves = 1; -#if defined(NO_TLS) - return WOLFSSL_FAILURE; -#else - return TLSX_UseSupportedCurve(&ctx->extensions, name, ctx->heap, - ctx->method->side); -#endif /* NO_TLS */ + if ((ctx == NULL) || (!isValidCurveGroup(name))) { + ret = BAD_FUNC_ARG; + } + else { + ctx->userCurves = 1; + #if defined(NO_TLS) + ret = WOLFSSL_FAILURE; + #else + ret = TLSX_UseSupportedCurve(&ctx->extensions, name, ctx->heap, + ctx->method->side); + #endif /* NO_TLS */ + } + + return ret; } #if defined(OPENSSL_EXTRA) @@ -442,7 +522,7 @@ static int wolfssl_validate_groups(const int* groups, int count, int* outGroups) if (isValidCurveGroup((word16)groups[i])) { outGroups[i] = groups[i]; } -#ifdef HAVE_ECC + #ifdef HAVE_ECC else { /* Groups may be populated with curve NIDs. */ int oid = (int)nid2oid(groups[i], oidCurveType); @@ -454,13 +534,13 @@ static int wolfssl_validate_groups(const int* groups, int count, int* outGroups) } outGroups[i] = name; } -#else + #else else { WOLFSSL_MSG("Invalid group name"); ret = WOLFSSL_FAILURE; break; } -#endif + #endif } return ret; @@ -482,7 +562,8 @@ int wolfSSL_CTX_set1_groups(WOLFSSL_CTX* ctx, int* groups, int count) int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CTX_set1_groups"); - if (groups == NULL || count <= 0) { + + if ((groups == NULL) || (count <= 0)) { WOLFSSL_MSG("Groups NULL or count not positive"); ret = WOLFSSL_FAILURE; } @@ -521,7 +602,8 @@ int wolfSSL_set1_groups(WOLFSSL* ssl, int* groups, int count) int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_set1_groups"); - if (groups == NULL || count <= 0) { + + if ((groups == NULL) || (count <= 0)) { WOLFSSL_MSG("Groups NULL or count not positive"); ret = WOLFSSL_FAILURE; } @@ -551,10 +633,10 @@ int wolfSSL_set1_groups(WOLFSSL* ssl, int* groups, int count) /* Set the Application-Layer Protocol Negotiation extension on the object. * - * @param [in] ssl SSL/TLS object. - * @param [in] protocol_name_list Comma-separated list of protocol names. - * @param [in] protocol_name_listSz Length of the list in bytes. - * @param [in] options Bitmask of ALPN options. A mismatch + * @param [in] ssl SSL/TLS object. + * @param [in] protocol_name_list Comma-separated list of protocol names. + * @param [in] protocol_name_listSz Length of the list in bytes. + * @param [in] options Bitmask of ALPN options. A mismatch * behavior must be set or BAD_FUNC_ARG is returned. * WOLFSSL_ALPN_FAILED_ON_MISMATCH sends the fatal * no_application_protocol alert and fails the handshake when no @@ -582,26 +664,28 @@ int wolfSSL_UseALPN(WOLFSSL* ssl, char *protocol_name_list, WOLFSSL_ENTER("wolfSSL_UseALPN"); if ((ssl == NULL) || (protocol_name_list == NULL)) { - return BAD_FUNC_ARG; + ret = BAD_FUNC_ARG; } else if (protocol_name_listSz > (WOLFSSL_MAX_ALPN_NUMBER * WOLFSSL_MAX_ALPN_PROTO_NAME_LEN + WOLFSSL_MAX_ALPN_NUMBER)) { WOLFSSL_MSG("Invalid arguments, protocol name list too long"); - return BAD_FUNC_ARG; + ret = BAD_FUNC_ARG; } else if ((!(options & WOLFSSL_ALPN_CONTINUE_ON_MISMATCH)) && (!(options & WOLFSSL_ALPN_FAILED_ON_MISMATCH))) { WOLFSSL_MSG("Invalid arguments, options not supported"); - return BAD_FUNC_ARG; + ret = BAD_FUNC_ARG; } - list = (char *)XMALLOC(protocol_name_listSz + 1, ssl->heap, - DYNAMIC_TYPE_ALPN); - token = (char **)XMALLOC(sizeof(char*) * (WOLFSSL_MAX_ALPN_NUMBER + 1), - ssl->heap, DYNAMIC_TYPE_ALPN); - if ((list == NULL) || (token == NULL)) { - WOLFSSL_MSG("Memory failure"); - ret = MEMORY_ERROR; + if (ret == WOLFSSL_SUCCESS) { + list = (char *)XMALLOC(protocol_name_listSz + 1, ssl->heap, + DYNAMIC_TYPE_ALPN); + token = (char **)XMALLOC(sizeof(char*) * (WOLFSSL_MAX_ALPN_NUMBER + 1), + ssl->heap, DYNAMIC_TYPE_ALPN); + if ((list == NULL) || (token == NULL)) { + WOLFSSL_MSG("Memory failure"); + ret = MEMORY_ERROR; + } } if (ret == WOLFSSL_SUCCESS) { @@ -629,8 +713,10 @@ int wolfSSL_UseALPN(WOLFSSL* ssl, char *protocol_name_list, } } - XFREE(token, ssl->heap, DYNAMIC_TYPE_ALPN); - XFREE(list, ssl->heap, DYNAMIC_TYPE_ALPN); + if (ssl != NULL) { + XFREE(token, ssl->heap, DYNAMIC_TYPE_ALPN); + XFREE(list, ssl->heap, DYNAMIC_TYPE_ALPN); + } return ret; } @@ -663,46 +749,59 @@ int wolfSSL_ALPN_GetProtocol(WOLFSSL* ssl, char **protocol_name, word16 *size) */ int wolfSSL_ALPN_GetPeerProtocol(WOLFSSL* ssl, char **list, word16 *listSz) { - int i, len; - char *p; + int ret = WOLFSSL_SUCCESS; + int i; + int len = 0; + char *p = NULL; byte *s; - if (ssl == NULL || list == NULL || listSz == NULL) - return BAD_FUNC_ARG; + if ((ssl == NULL) || (list == NULL) || (listSz == NULL)) { + ret = BAD_FUNC_ARG; + } + else if ((ssl->alpn_peer_requested == NULL) || + (ssl->alpn_peer_requested_length == 0)) { + ret = BUFFER_ERROR; + } + else { + /* ssl->alpn_peer_requested are the original bytes sent in a + * ClientHello, formatted as (len-byte chars+)+. To turn n protocols + * into a comma-separated C string, one needs (n-1) commas and a final + * 0 byte which has the same length as the original. + * The returned length is the strlen() of the C string, so -1 of that. + */ + *listSz = ssl->alpn_peer_requested_length-1; + *list = p = (char *)XMALLOC(ssl->alpn_peer_requested_length, ssl->heap, + DYNAMIC_TYPE_TLSX); + if (p == NULL) { + ret = MEMORY_ERROR; + } + } - if (ssl->alpn_peer_requested == NULL - || ssl->alpn_peer_requested_length == 0) - return BUFFER_ERROR; - - /* ssl->alpn_peer_requested are the original bytes sent in a ClientHello, - * formatted as (len-byte chars+)+. To turn n protocols into a - * comma-separated C string, one needs (n-1) commas and a final 0 byte - * which has the same length as the original. - * The returned length is the strlen() of the C string, so -1 of that. */ - *listSz = ssl->alpn_peer_requested_length-1; - *list = p = (char *)XMALLOC(ssl->alpn_peer_requested_length, ssl->heap, - DYNAMIC_TYPE_TLSX); - if (p == NULL) - return MEMORY_ERROR; - - for (i = 0, s = ssl->alpn_peer_requested; - i < ssl->alpn_peer_requested_length; - p += len, i += len) - { - if (i) - *p++ = ','; - len = s[i++]; - /* guard against bad length bytes. */ - if (i + len > ssl->alpn_peer_requested_length) { - XFREE(*list, ssl->heap, DYNAMIC_TYPE_TLSX); - *list = NULL; - return WOLFSSL_FAILURE; + if (ret == WOLFSSL_SUCCESS) { + for (i = 0, s = ssl->alpn_peer_requested; + i < ssl->alpn_peer_requested_length; + p += len, i += len) + { + if (i != 0) { + *p++ = ','; + } + len = s[i++]; + /* guard against bad length bytes. */ + if (i + len > ssl->alpn_peer_requested_length) { + XFREE(*list, ssl->heap, DYNAMIC_TYPE_TLSX); + *list = NULL; + ret = WOLFSSL_FAILURE; + break; + } + XMEMCPY(p, s + i, (size_t)len); } - XMEMCPY(p, s + i, (size_t)len); } - *p = 0; - return WOLFSSL_SUCCESS; + if (ret == WOLFSSL_SUCCESS) { + *p = 0; + } + + return ret; } @@ -715,14 +814,17 @@ int wolfSSL_ALPN_GetPeerProtocol(WOLFSSL* ssl, char **list, word16 *listSz) */ int wolfSSL_ALPN_FreePeerProtocol(WOLFSSL* ssl, char **list) { + int ret = WOLFSSL_SUCCESS; + if (ssl == NULL) { - return BAD_FUNC_ARG; + ret = BAD_FUNC_ARG; + } + else { + XFREE(*list, ssl->heap, DYNAMIC_TYPE_TLSX); + *list = NULL; } - XFREE(*list, ssl->heap, DYNAMIC_TYPE_TLSX); - *list = NULL; - - return WOLFSSL_SUCCESS; + return ret; } #endif /* HAVE_ALPN */ @@ -734,7 +836,7 @@ int wolfSSL_ALPN_FreePeerProtocol(WOLFSSL* ssl, char **list) * * Use of secure renegotiation is discouraged. * - * @param [in] ssl SSL/TLS object. + * @param [in, out] ssl SSL/TLS object. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl is NULL. * @return Negative value on error. @@ -742,9 +844,9 @@ int wolfSSL_ALPN_FreePeerProtocol(WOLFSSL* ssl, char **list) int wolfSSL_UseSecureRenegotiation(WOLFSSL* ssl) { int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); -#if defined(NO_TLS) + #if defined(NO_TLS) (void)ssl; -#else + #else if (ssl != NULL) { ret = TLSX_UseSecureRenegotiation(&ssl->extensions, ssl->heap); } @@ -758,7 +860,7 @@ int wolfSSL_UseSecureRenegotiation(WOLFSSL* ssl) ssl->secure_renegotiation = (SecureRenegotiation*)extension->data; } } -#endif /* !NO_TLS */ + #endif /* !NO_TLS */ return ret; } @@ -772,19 +874,92 @@ int wolfSSL_UseSecureRenegotiation(WOLFSSL* ssl) */ int wolfSSL_CTX_UseSecureRenegotiation(WOLFSSL_CTX* ctx) { - if (ctx == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; - ctx->useSecureReneg = 1; - return WOLFSSL_SUCCESS; + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ctx->useSecureReneg = 1; + } + + return ret; } #ifdef HAVE_SECURE_RENEGOTIATION +/* Get the object ready for a renegotiation handshake. + * + * A renegotiation already under way keeps its handshake state so that it can + * continue. Otherwise the state is reset so a new negotiation starts from the + * beginning. The caller performs the negotiation either way. + * + * @param [in, out] ssl SSL/TLS object. + * @return WOLFSSL_SUCCESS when the object is ready to negotiate. + * @return SECURE_RENEGOTIATION_E when the initial handshake has not + * completed. + * @return WOLFSSL_FATAL_ERROR on error. ssl->error holds the reason. + */ +static int wolfssl_rehandshake_prepare(WOLFSSL* ssl) +{ + int ret = WOLFSSL_SUCCESS; + + if (ssl->options.handShakeState != HANDSHAKE_DONE) { + if (!ssl->options.handShakeDone) { + WOLFSSL_MSG("Can't renegotiate until initial " + "handshake complete"); + ret = SECURE_RENEGOTIATION_E; + } + else { + /* Leave the state alone - resetting it would discard the + * renegotiation that is already in progress. */ + WOLFSSL_MSG("Renegotiation already started. " + "Moving it forward."); + } + } + else { + /* reset handshake states */ + ssl->options.sendVerify = 0; + ssl->options.serverState = NULL_STATE; + ssl->options.clientState = NULL_STATE; + ssl->options.connectState = CONNECT_BEGIN; + ssl->options.acceptState = ACCEPT_BEGIN_RENEG; + ssl->options.handShakeState = NULL_STATE; + /* TODO, move states in internal.h */ + ssl->options.processReply = 0; + + XMEMSET(&ssl->msgsReceived, 0, sizeof(ssl->msgsReceived)); + + ssl->secure_renegotiation->cache_status = SCR_CACHE_NEEDED; + + #if !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12) + if (ssl->options.side == WOLFSSL_SERVER_END) { + int helloRet = SendHelloRequest(ssl); + + if (helloRet != 0) { + ssl->error = helloRet; + ret = WOLFSSL_FATAL_ERROR; + } + } + #endif /* !NO_WOLFSSL_SERVER && !WOLFSSL_NO_TLS12 */ + + if (ret == WOLFSSL_SUCCESS) { + int hashRet = InitHandshakeHashes(ssl); + + if (hashRet != 0) { + ssl->error = hashRet; + ret = WOLFSSL_FATAL_ERROR; + } + } + } + + return ret; +} + /* Perform a secure renegotiation handshake on the object. * * User forced; use of secure renegotiation is discouraged. * - * @param [in] ssl SSL/TLS object. + * @param [in, out] ssl SSL/TLS object. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl is NULL. * @return SECURE_RENEGOTIATION_E when renegotiation is not allowed. @@ -792,93 +967,49 @@ int wolfSSL_CTX_UseSecureRenegotiation(WOLFSSL_CTX* ctx) */ static int _Rehandshake(WOLFSSL* ssl) { - int ret; - - if (ssl == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; - if (IsAtLeastTLSv1_3(ssl->version)) { + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else if (IsAtLeastTLSv1_3(ssl->version)) { WOLFSSL_MSG("Secure Renegotiation not supported in TLS 1.3"); - return SECURE_RENEGOTIATION_E; + ret = SECURE_RENEGOTIATION_E; } - - if (ssl->secure_renegotiation == NULL) { + else if (ssl->secure_renegotiation == NULL) { WOLFSSL_MSG("Secure Renegotiation not forced on by user"); - return SECURE_RENEGOTIATION_E; + ret = SECURE_RENEGOTIATION_E; } - - if (ssl->secure_renegotiation->enabled == 0) { + else if (ssl->secure_renegotiation->enabled == 0) { WOLFSSL_MSG("Secure Renegotiation not enabled at extension level"); - return SECURE_RENEGOTIATION_E; + ret = SECURE_RENEGOTIATION_E; } - - if (ssl->secure_renegotiation->advertiseOnly) { + else if (ssl->secure_renegotiation->advertiseOnly) { /* Extension was advertised only for the RFC 5746 check; the * application did not call wolfSSL_UseSecureRenegotiation(). */ WOLFSSL_MSG("Secure Renegotiation not forced on by user"); - return SECURE_RENEGOTIATION_E; + ret = SECURE_RENEGOTIATION_E; } - -#ifdef WOLFSSL_DTLS - if (ssl->options.dtls && ssl->keys.dtls_epoch == 0xFFFF) { + #ifdef WOLFSSL_DTLS + else if ((ssl->options.dtls) && (ssl->keys.dtls_epoch == 0xFFFF)) { WOLFSSL_MSG("Secure Renegotiation not allowed. Epoch would wrap"); - return SECURE_RENEGOTIATION_E; + ret = SECURE_RENEGOTIATION_E; + } + #endif + /* Prepare, unless this is a server that has already processed a + * client-initiated hello, in which case there is nothing to prepare. */ + else if ((ssl->options.side != WOLFSSL_SERVER_END) || + (ssl->options.acceptState != ACCEPT_FIRST_REPLY_DONE)) { + ret = wolfssl_rehandshake_prepare(ssl); } -#endif - - /* If the client started the renegotiation, the server will already - * have processed the client's hello. */ - if (ssl->options.side != WOLFSSL_SERVER_END || - ssl->options.acceptState != ACCEPT_FIRST_REPLY_DONE) { - - if (ssl->options.handShakeState != HANDSHAKE_DONE) { - if (!ssl->options.handShakeDone) { - WOLFSSL_MSG("Can't renegotiate until initial " - "handshake complete"); - return SECURE_RENEGOTIATION_E; - } - else { - WOLFSSL_MSG("Renegotiation already started. " - "Moving it forward."); - ret = wolfSSL_negotiate(ssl); - if (ret == WOLFSSL_SUCCESS) - ssl->secure_rene_count++; - return ret; - } - } - - /* reset handshake states */ - ssl->options.sendVerify = 0; - ssl->options.serverState = NULL_STATE; - ssl->options.clientState = NULL_STATE; - ssl->options.connectState = CONNECT_BEGIN; - ssl->options.acceptState = ACCEPT_BEGIN_RENEG; - ssl->options.handShakeState = NULL_STATE; - ssl->options.processReply = 0; /* TODO, move states in internal.h */ - - XMEMSET(&ssl->msgsReceived, 0, sizeof(ssl->msgsReceived)); - - ssl->secure_renegotiation->cache_status = SCR_CACHE_NEEDED; - -#if !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12) - if (ssl->options.side == WOLFSSL_SERVER_END) { - ret = SendHelloRequest(ssl); - if (ret != 0) { - ssl->error = ret; - return WOLFSSL_FATAL_ERROR; - } - } -#endif /* !NO_WOLFSSL_SERVER && !WOLFSSL_NO_TLS12 */ - ret = InitHandshakeHashes(ssl); - if (ret != 0) { - ssl->error = ret; - return WOLFSSL_FATAL_ERROR; + if (ret == WOLFSSL_SUCCESS) { + ret = wolfSSL_negotiate(ssl); + if (ret == WOLFSSL_SUCCESS) { + ssl->secure_rene_count++; } } - ret = wolfSSL_negotiate(ssl); - if (ret == WOLFSSL_SUCCESS) - ssl->secure_rene_count++; + return ret; } @@ -887,44 +1018,43 @@ static int _Rehandshake(WOLFSSL* ssl) * * User forced; use of secure renegotiation is discouraged. * - * @param [in] ssl SSL/TLS object. + * @param [in, out] ssl SSL/TLS object. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_FAILURE when ssl is NULL. * @return Negative value on error. */ int wolfSSL_Rehandshake(WOLFSSL* ssl) { - int ret; - WOLFSSL_ENTER("wolfSSL_Rehandshake"); - - if (ssl == NULL) - return WOLFSSL_FAILURE; + int ret = WOLFSSL_SUCCESS; -#ifdef HAVE_SESSION_TICKET - ret = WOLFSSL_SUCCESS; -#endif + WOLFSSL_ENTER("wolfSSL_Rehandshake"); - if (ssl->options.side == WOLFSSL_SERVER_END) { - /* Reset option to send certificate verify. */ - ssl->options.sendVerify = 0; - /* Reset resuming flag to do full secure handshake. */ - ssl->options.resuming = 0; + if (ssl == NULL) { + ret = WOLFSSL_FAILURE; } else { - /* Reset resuming flag to do full secure handshake. */ - ssl->options.resuming = 0; - #if defined(HAVE_SESSION_TICKET) && !defined(NO_WOLFSSL_CLIENT) - /* Clearing the ticket. */ - ret = wolfSSL_UseSessionTicket(ssl); - #endif - } - /* CLIENT/SERVER: Reset peer authentication for full secure handshake. */ - ssl->options.peerAuthGood = 0; + if (ssl->options.side == WOLFSSL_SERVER_END) { + /* Reset option to send certificate verify. */ + ssl->options.sendVerify = 0; + /* Reset resuming flag to do full secure handshake. */ + ssl->options.resuming = 0; + } + else { + /* Reset resuming flag to do full secure handshake. */ + ssl->options.resuming = 0; + #if defined(HAVE_SESSION_TICKET) && !defined(NO_WOLFSSL_CLIENT) + /* Clearing the ticket. */ + ret = wolfSSL_UseSessionTicket(ssl); + #endif + } + /* CLIENT/SERVER: Reset peer authentication for full secure + * handshake. */ + ssl->options.peerAuthGood = 0; -#ifdef HAVE_SESSION_TICKET - if (ret == WOLFSSL_SUCCESS) -#endif - ret = _Rehandshake(ssl); + if (ret == WOLFSSL_SUCCESS) { + ret = _Rehandshake(ssl); + } + } return ret; } @@ -936,24 +1066,29 @@ int wolfSSL_Rehandshake(WOLFSSL* ssl) * * Client side only. User forced; use of secure renegotiation is discouraged. * - * @param [in] ssl SSL/TLS object. + * @param [in, out] ssl SSL/TLS object. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl is NULL. * @return WOLFSSL_FATAL_ERROR when called on a server. */ int wolfSSL_SecureResume(WOLFSSL* ssl) { - WOLFSSL_ENTER("wolfSSL_SecureResume"); + int ret; - if (ssl == NULL) - return BAD_FUNC_ARG; + WOLFSSL_ENTER("wolfSSL_SecureResume"); - if (ssl->options.side == WOLFSSL_SERVER_END) { + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else if (ssl->options.side == WOLFSSL_SERVER_END) { ssl->error = SIDE_ERROR; - return WOLFSSL_FATAL_ERROR; + ret = WOLFSSL_FATAL_ERROR; + } + else { + ret = _Rehandshake(ssl); } - return _Rehandshake(ssl); + return ret; } #endif /* NO_WOLFSSL_CLIENT */ @@ -987,30 +1122,41 @@ long wolfSSL_SSL_get_secure_renegotiation_support(WOLFSSL* ssl) */ WOLFSSL_API int wolfSSL_get_scr_check_enabled(const WOLFSSL* ssl) { + int ret; + WOLFSSL_ENTER("wolfSSL_get_scr_check_enabled"); - if (ssl == NULL) - return BAD_FUNC_ARG; + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = ssl->scr_check_enabled; + } - return ssl->scr_check_enabled; + return ret; } /* Set whether the secure renegotiation check is enabled for the object. * - * @param [in] ssl SSL/TLS object. - * @param [in] enabled Non-zero to enable the check, 0 to disable it. + * @param [in, out] ssl SSL/TLS object. + * @param [in] enabled Non-zero to enable the check, 0 to disable it. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl is NULL. */ WOLFSSL_API int wolfSSL_set_scr_check_enabled(WOLFSSL* ssl, byte enabled) { + int ret = WOLFSSL_SUCCESS; + WOLFSSL_ENTER("wolfSSL_set_scr_check_enabled"); - if (ssl == NULL) - return BAD_FUNC_ARG; + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ssl->scr_check_enabled = !!enabled; + } - ssl->scr_check_enabled = !!enabled; - return WOLFSSL_SUCCESS; + return ret; } /* Get whether the secure renegotiation check is enabled for the context. @@ -1065,28 +1211,36 @@ WOLFSSL_API int wolfSSL_CTX_set_scr_check_enabled(WOLFSSL_CTX* ctx, */ int wolfSSL_CTX_NoTicketTLSv12(WOLFSSL_CTX* ctx) { - if (ctx == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; - ctx->noTicketTls12 = 1; + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ctx->noTicketTls12 = 1; + } - return WOLFSSL_SUCCESS; + return ret; } /* Disable use of session tickets with TLS 1.2 on the object. * - * @param [in] ssl SSL/TLS object. + * @param [in, out] ssl SSL/TLS object. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl is NULL. */ int wolfSSL_NoTicketTLSv12(WOLFSSL* ssl) { - if (ssl == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; - ssl->options.noTicketTls12 = 1; + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ssl->options.noTicketTls12 = 1; + } - return WOLFSSL_SUCCESS; + return ret; } /* Set the session ticket encryption callback on the context. @@ -1098,12 +1252,16 @@ int wolfSSL_NoTicketTLSv12(WOLFSSL* ssl) */ int wolfSSL_CTX_set_TicketEncCb(WOLFSSL_CTX* ctx, SessionTicketEncCb cb) { - if (ctx == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; - ctx->ticketEncCb = cb; + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ctx->ticketEncCb = cb; + } - return WOLFSSL_SUCCESS; + return ret; } /* Set the session ticket lifetime hint, in seconds, on the context. @@ -1115,17 +1273,18 @@ int wolfSSL_CTX_set_TicketEncCb(WOLFSSL_CTX* ctx, SessionTicketEncCb cb) */ int wolfSSL_CTX_set_TicketHint(WOLFSSL_CTX* ctx, int hint) { - if (ctx == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; /* RFC8446 Section 4.6.1: Servers MUST NOT use any value greater than * 604800 seconds (7 days). */ - if (hint < 0 || hint > 604800) - return BAD_FUNC_ARG; - - ctx->ticketHint = hint; + if ((ctx == NULL) || (hint < 0) || (hint > 604800)) { + ret = BAD_FUNC_ARG; + } + else { + ctx->ticketHint = hint; + } - return WOLFSSL_SUCCESS; + return ret; } /* Set the user context passed to the session ticket encryption callback. @@ -1137,12 +1296,16 @@ int wolfSSL_CTX_set_TicketHint(WOLFSSL_CTX* ctx, int hint) */ int wolfSSL_CTX_set_TicketEncCtx(WOLFSSL_CTX* ctx, void* userCtx) { - if (ctx == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; - ctx->ticketEncCtx = userCtx; + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ctx->ticketEncCtx = userCtx; + } - return WOLFSSL_SUCCESS; + return ret; } /* Get the user context passed to the session ticket encryption callback. @@ -1153,10 +1316,13 @@ int wolfSSL_CTX_set_TicketEncCtx(WOLFSSL_CTX* ctx, void* userCtx) */ void* wolfSSL_CTX_get_TicketEncCtx(WOLFSSL_CTX* ctx) { - if (ctx == NULL) - return NULL; + void* userCtx = NULL; + + if (ctx != NULL) { + userCtx = ctx->ticketEncCtx; + } - return ctx->ticketEncCtx; + return userCtx; } #ifdef WOLFSSL_TLS13 @@ -1169,11 +1335,16 @@ void* wolfSSL_CTX_get_TicketEncCtx(WOLFSSL_CTX* ctx) */ int wolfSSL_CTX_set_num_tickets(WOLFSSL_CTX* ctx, size_t mxTickets) { - if (ctx == NULL) - return WOLFSSL_FAILURE; + int ret = WOLFSSL_SUCCESS; - ctx->maxTicketTls13 = (unsigned int)mxTickets; - return WOLFSSL_SUCCESS; + if (ctx == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + ctx->maxTicketTls13 = (unsigned int)mxTickets; + } + + return ret; } /* Get the maximum number of TLS 1.3 session tickets to send. @@ -1183,10 +1354,13 @@ int wolfSSL_CTX_set_num_tickets(WOLFSSL_CTX* ctx, size_t mxTickets) */ size_t wolfSSL_CTX_get_num_tickets(WOLFSSL_CTX* ctx) { - if (ctx == NULL) - return 0; + size_t mxTickets = 0; + + if (ctx != NULL) { + mxTickets = (size_t)ctx->maxTicketTls13; + } - return (size_t)ctx->maxTicketTls13; + return mxTickets; } #endif /* WOLFSSL_TLS13 */ #endif /* !NO_WOLFSSL_SERVER */ @@ -1201,10 +1375,16 @@ size_t wolfSSL_CTX_get_num_tickets(WOLFSSL_CTX* ctx) */ int wolfSSL_UseSessionTicket(WOLFSSL* ssl) { - if (ssl == NULL) - return BAD_FUNC_ARG; + int ret; + + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseSessionTicket(&ssl->extensions, NULL, ssl->heap); + } - return TLSX_UseSessionTicket(&ssl->extensions, NULL, ssl->heap); + return ret; } /* Enable use of the session ticket extension on the context. @@ -1216,10 +1396,16 @@ int wolfSSL_UseSessionTicket(WOLFSSL* ssl) */ int wolfSSL_CTX_UseSessionTicket(WOLFSSL_CTX* ctx) { - if (ctx == NULL) - return BAD_FUNC_ARG; + int ret; + + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ret = TLSX_UseSessionTicket(&ctx->extensions, NULL, ctx->heap); + } - return TLSX_UseSessionTicket(&ctx->extensions, NULL, ctx->heap); + return ret; } /* Get the session ticket stored on the object. @@ -1235,25 +1421,28 @@ int wolfSSL_CTX_UseSessionTicket(WOLFSSL_CTX* ctx) */ int wolfSSL_get_SessionTicket(WOLFSSL* ssl, byte* buf, word32* bufSz) { - if (ssl == NULL || bufSz == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; - if (*bufSz == 0 && buf == NULL) { + if ((ssl == NULL) || (bufSz == NULL)) { + ret = BAD_FUNC_ARG; + } + else if ((*bufSz == 0) && (buf == NULL)) { + /* Report the length needed to hold the ticket. */ *bufSz = ssl->session->ticketLen; - return LENGTH_ONLY_E; + ret = LENGTH_ONLY_E; } - - if (buf == NULL) - return BAD_FUNC_ARG; - - if (ssl->session->ticketLen <= *bufSz) { + else if (buf == NULL) { + ret = BAD_FUNC_ARG; + } + else if (ssl->session->ticketLen <= *bufSz) { XMEMCPY(buf, ssl->session->ticket, ssl->session->ticketLen); *bufSz = ssl->session->ticketLen; } - else + else { *bufSz = 0; + } - return WOLFSSL_SUCCESS; + return ret; } /* Set the session ticket to use on the object. @@ -1268,10 +1457,13 @@ int wolfSSL_get_SessionTicket(WOLFSSL* ssl, byte* buf, word32* bufSz) int wolfSSL_set_SessionTicket(WOLFSSL* ssl, const byte* buf, word32 bufSz) { - if (ssl == NULL || (buf == NULL && bufSz > 0)) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; + + if ((ssl == NULL) || ((buf == NULL) && (bufSz > 0))) { + ret = BAD_FUNC_ARG; + } - if (bufSz > 0) { + if ((ret == WOLFSSL_SUCCESS) && (bufSz > 0)) { /* Ticket will fit into static ticket */ if (bufSz <= SESSION_TICKET_LEN) { if (ssl->session->ticketLenAlloc > 0) { @@ -1290,40 +1482,52 @@ int wolfSSL_set_SessionTicket(WOLFSSL* ssl, const byte* buf, } ssl->session->ticket = (byte*)XMALLOC(bufSz, ssl->session->heap, DYNAMIC_TYPE_SESSION_TICK); - if(ssl->session->ticket == NULL) { + if (ssl->session->ticket == NULL) { ssl->session->ticket = ssl->session->staticTicket; ssl->session->ticketLenAlloc = 0; - return MEMORY_ERROR; + ret = MEMORY_ERROR; + } + else { + ssl->session->ticketLenAlloc = (word16)bufSz; } - ssl->session->ticketLenAlloc = (word16)bufSz; } } - XMEMCPY(ssl->session->ticket, buf, bufSz); + + if (ret == WOLFSSL_SUCCESS) { + XMEMCPY(ssl->session->ticket, buf, bufSz); + } } - ssl->session->ticketLen = (word16)bufSz; - return WOLFSSL_SUCCESS; + if (ret == WOLFSSL_SUCCESS) { + ssl->session->ticketLen = (word16)bufSz; + } + + return ret; } /* Set the session ticket callback and user context on the object. * - * @param [in] ssl SSL/TLS object. - * @param [in] cb Session ticket callback. - * @param [in] ctx User context passed to the callback. + * @param [in, out] ssl SSL/TLS object. + * @param [in] cb Session ticket callback. + * @param [in] ctx User context passed to the callback. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl is NULL. */ int wolfSSL_set_SessionTicket_cb(WOLFSSL* ssl, CallbackSessionTicket cb, void* ctx) { - if (ssl == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; - ssl->session_ticket_cb = cb; - ssl->session_ticket_ctx = ctx; + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ssl->session_ticket_cb = cb; + ssl->session_ticket_ctx = ctx; + } - return WOLFSSL_SUCCESS; + return ret; } #endif /* !NO_WOLFSSL_CLIENT */ @@ -1341,29 +1545,37 @@ int wolfSSL_set_SessionTicket_cb(WOLFSSL* ssl, */ int wolfSSL_CTX_DisableExtendedMasterSecret(WOLFSSL_CTX* ctx) { - if (ctx == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; - ctx->haveEMS = 0; + if (ctx == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ctx->haveEMS = 0; + } - return WOLFSSL_SUCCESS; + return ret; } /* Disable the Extended Master Secret extension on the object. * - * @param [in] ssl SSL/TLS object. + * @param [in, out] ssl SSL/TLS object. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl is NULL. */ int wolfSSL_DisableExtendedMasterSecret(WOLFSSL* ssl) { - if (ssl == NULL) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; - ssl->options.haveEMS = 0; + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ssl->options.haveEMS = 0; + } - return WOLFSSL_SUCCESS; + return ret; } #endif @@ -1377,19 +1589,23 @@ int wolfSSL_DisableExtendedMasterSecret(WOLFSSL* ssl) #ifdef HAVE_PK_CALLBACKS /* Set the debug argument passed to the logging callback on the object. * - * @param [in] ssl SSL/TLS object. - * @param [in] arg Debug argument. + * @param [in, out] ssl SSL/TLS object. + * @param [in] arg Debug argument. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_FAILURE when ssl is NULL. */ long wolfSSL_set_tlsext_debug_arg(WOLFSSL* ssl, void *arg) { + long ret = WOLFSSL_SUCCESS; + if (ssl == NULL) { - return WOLFSSL_FAILURE; + ret = WOLFSSL_FAILURE; + } + else { + ssl->loggingCtx = arg; } - ssl->loggingCtx = arg; - return WOLFSSL_SUCCESS; + return ret; } #endif /* HAVE_PK_CALLBACKS */ @@ -1477,10 +1693,16 @@ long wolfSSL_set_tlsext_status_ids(WOLFSSL *s, void *arg) int wolfSSL_CTX_set_tlsext_max_fragment_length(WOLFSSL_CTX *c, unsigned char mode) { - if (c == NULL || (mode < WOLFSSL_MFL_2_9 || mode > WOLFSSL_MFL_2_12 )) - return BAD_FUNC_ARG; + int ret; + + if ((c == NULL) || (mode < WOLFSSL_MFL_2_9) || (mode > WOLFSSL_MFL_2_12)) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_CTX_UseMaxFragment(c, mode); + } - return wolfSSL_CTX_UseMaxFragment(c, mode); + return ret; } /* Set the Maximum Fragment Length extension on the object. * @@ -1491,10 +1713,16 @@ int wolfSSL_CTX_set_tlsext_max_fragment_length(WOLFSSL_CTX *c, */ int wolfSSL_set_tlsext_max_fragment_length(WOLFSSL *s, unsigned char mode) { - if (s == NULL || (mode < WOLFSSL_MFL_2_9 || mode > WOLFSSL_MFL_2_12 )) - return BAD_FUNC_ARG; + int ret; + + if ((s == NULL) || (mode < WOLFSSL_MFL_2_9) || (mode > WOLFSSL_MFL_2_12)) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_UseMaxFragment(s, mode); + } - return wolfSSL_UseMaxFragment(s, mode); + return ret; } #endif /* !NO_WOLFSSL_CLIENT && !NO_TLS */ #endif /* HAVE_MAX_FRAGMENT */ @@ -1508,17 +1736,22 @@ int wolfSSL_set_tlsext_max_fragment_length(WOLFSSL *s, unsigned char mode) */ int wolfSSL_CTX_set1_sigalgs_list(WOLFSSL_CTX* ctx, const char* list) { + int ret = WOLFSSL_SUCCESS; + WOLFSSL_MSG("wolfSSL_CTX_set1_sigalg_list"); - if (ctx == NULL || list == NULL) { + if ((ctx == NULL) || (list == NULL)) { WOLFSSL_MSG("Bad function arguments"); - return WOLFSSL_FAILURE; + ret = WOLFSSL_FAILURE; + } + else if (AllocateCtxSuites(ctx) != 0) { + ret = WOLFSSL_FAILURE; + } + else { + ret = SetSuitesHashSigAlgo(ctx->suites, list); } - if (AllocateCtxSuites(ctx) != 0) - return WOLFSSL_FAILURE; - - return SetSuitesHashSigAlgo(ctx->suites, list); + return ret; } /* Set the signature algorithms list on the object. @@ -1530,17 +1763,22 @@ int wolfSSL_CTX_set1_sigalgs_list(WOLFSSL_CTX* ctx, const char* list) */ int wolfSSL_set1_sigalgs_list(WOLFSSL* ssl, const char* list) { + int ret = WOLFSSL_SUCCESS; + WOLFSSL_MSG("wolfSSL_set1_sigalg_list"); - if (ssl == NULL || list == NULL) { + if ((ssl == NULL) || (list == NULL)) { WOLFSSL_MSG("Bad function arguments"); - return WOLFSSL_FAILURE; + ret = WOLFSSL_FAILURE; + } + else if (AllocateSuites(ssl) != 0) { + ret = WOLFSSL_FAILURE; + } + else { + ret = SetSuitesHashSigAlgo(ssl->suites, list); } - if (AllocateSuites(ssl) != 0) - return WOLFSSL_FAILURE; - - return SetSuitesHashSigAlgo(ssl->suites, list); + return ret; } #ifdef HAVE_ECC @@ -1555,11 +1793,16 @@ int wolfSSL_set1_sigalgs_list(WOLFSSL* ssl, const char* list) */ int wolfSSL_CTX_set1_groups_list(WOLFSSL_CTX *ctx, const char *list) { - if (!ctx || !list) { - return WOLFSSL_FAILURE; + int ret; + + if ((ctx == NULL) || (list == NULL)) { + ret = WOLFSSL_FAILURE; + } + else { + ret = set_curves_list(NULL, ctx, list, 0); } - return set_curves_list(NULL, ctx, list, 0); + return ret; } /* Set the supported groups list, by name, on the object. @@ -1571,11 +1814,16 @@ int wolfSSL_CTX_set1_groups_list(WOLFSSL_CTX *ctx, const char *list) */ int wolfSSL_set1_groups_list(WOLFSSL *ssl, const char *list) { - if (!ssl || !list) { - return WOLFSSL_FAILURE; + int ret; + + if ((ssl == NULL) || (list == NULL)) { + ret = WOLFSSL_FAILURE; + } + else { + ret = set_curves_list(ssl, NULL, list, 0); } - return set_curves_list(ssl, NULL, list, 0); + return ret; } #endif /* WOLFSSL_TLS13 */ @@ -1618,10 +1866,12 @@ int wolfSSL_set_tlsext_host_name(WOLFSSL* ssl, const char* host_name) const char * wolfSSL_get_servername(WOLFSSL* ssl, byte type) { void * serverName = NULL; - if (ssl == NULL) - return NULL; - TLSX_SNI_GetRequest(ssl->extensions, type, &serverName, - !wolfSSL_is_server(ssl)); + + if (ssl != NULL) { + TLSX_SNI_GetRequest(ssl->extensions, type, &serverName, + !wolfSSL_is_server(ssl)); + } + return (const char *)serverName; } #endif @@ -1641,12 +1891,16 @@ const char * wolfSSL_get_servername(WOLFSSL* ssl, byte type) int wolfSSL_CTX_set_tlsext_servername_callback(WOLFSSL_CTX* ctx, CallbackSniRecv cb) { + int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); + WOLFSSL_ENTER("wolfSSL_CTX_set_tlsext_servername_callback"); - if (ctx) { + + if (ctx != NULL) { ctx->sniRecvCb = cb; - return WOLFSSL_SUCCESS; + ret = WOLFSSL_SUCCESS; } - return WOLFSSL_FAILURE; + + return ret; } #endif /* HAVE_SNI */ @@ -1679,12 +1933,16 @@ void wolfSSL_CTX_set_servername_callback(WOLFSSL_CTX* ctx, CallbackSniRecv cb) */ int wolfSSL_CTX_set_servername_arg(WOLFSSL_CTX* ctx, void* arg) { + int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); + WOLFSSL_ENTER("wolfSSL_CTX_set_servername_arg"); - if (ctx) { + + if (ctx != NULL) { ctx->sniRecvCbArg = arg; - return WOLFSSL_SUCCESS; + ret = WOLFSSL_SUCCESS; } - return WOLFSSL_FAILURE; + + return ret; } #endif /* HAVE_SNI */ @@ -1820,6 +2078,83 @@ static int wolfssl_ticket_key_dec(WOLFSSL_EVP_CIPHER_CTX* evpCtx, return ret; } +/* Run the application's ticket key callback and process the ticket. + * + * The cipher and HMAC contexts are initialized by the caller. The HMAC context + * is released here, once the ticket has been encrypted or decrypted. + * + * @param [in] ssl SSL/TLS object. + * @param [in] keyName Key name identifying the key to use. + * @param [in] iv IV to use. + * @param [in, out] mac MAC of the encrypted data. + * @param [in] enc 1 to encrypt the ticket, 0 to decrypt. + * @param [in, out] encTicket Ticket data, encrypted/decrypted in place. + * @param [in] encTicketLen Length of the ticket data in bytes. + * @param [in, out] encLen In: space available. Out: length of ticket. + * @param [in] evpCtx Initialized cipher context. + * @param [in, out] hmacCtx Initialized HMAC context. Released on return. + * @return WOLFSSL_TICKET_RET_OK on success. + * @return WOLFSSL_TICKET_RET_CREATE when a new ticket is required. + * @return WOLFSSL_TICKET_RET_FATAL on error. + */ +static int wolfssl_ticket_key_cb_process(WOLFSSL* ssl, + unsigned char keyName[WOLFSSL_TICKET_NAME_SZ], + unsigned char iv[WOLFSSL_TICKET_IV_SZ], + unsigned char mac[WOLFSSL_TICKET_MAC_SZ], + int enc, unsigned char* encTicket, int encTicketLen, int* encLen, + WOLFSSL_EVP_CIPHER_CTX* evpCtx, WOLFSSL_HMAC_CTX* hmacCtx) +{ + int ret = WOLFSSL_TICKET_RET_OK; + int res; + int totalSz = 0; + + res = ssl->ctx->ticketEncWrapCb(ssl, keyName, iv, evpCtx, hmacCtx, enc); + if ((res != TICKET_KEY_CB_RET_OK) && (res != TICKET_KEY_CB_RET_RENEW)) { + WOLFSSL_MSG("Ticket callback error"); + ret = WOLFSSL_TICKET_RET_FATAL; + } + + if (ret == WOLFSSL_TICKET_RET_OK) { + if (wolfSSL_HMAC_size(hmacCtx) > WOLFSSL_TICKET_MAC_SZ) { + WOLFSSL_MSG("Ticket cipher MAC size error"); + ret = WOLFSSL_TICKET_RET_FATAL; + } + } + + if (ret == WOLFSSL_TICKET_RET_OK) { + if (enc) { + if (!wolfssl_ticket_key_enc(evpCtx, hmacCtx, encTicket, + encTicketLen, *encLen, mac, &totalSz)) { + ret = WOLFSSL_TICKET_RET_FATAL; + } + } + else { + if (!wolfssl_ticket_key_dec(evpCtx, hmacCtx, encTicket, + encTicketLen, mac, &totalSz)) { + ret = WOLFSSL_TICKET_RET_FATAL; + } + } + } + + if (ret == WOLFSSL_TICKET_RET_OK) { + *encLen = totalSz; + + /* Below TLS 1.3 a renewed key means the peer needs a new ticket. + * TLS 1.3 issues tickets separately. */ + if ((res == TICKET_KEY_CB_RET_RENEW) && + (!IsAtLeastTLSv1_3(ssl->version)) && (!enc)) { + ret = WOLFSSL_TICKET_RET_CREATE; + } + else { + ret = WOLFSSL_TICKET_RET_OK; + } + } + + wolfSSL_HMAC_CTX_cleanup(hmacCtx); + + return ret; +} + /* Encrypt or decrypt a session ticket using the OpenSSL ticket key callback. * * Wraps the application's OpenSSL-style callback that initializes the cipher @@ -1828,7 +2163,7 @@ static int wolfssl_ticket_key_dec(WOLFSSL_EVP_CIPHER_CTX* evpCtx, * @param [in] ssl SSL/TLS object. * @param [in] keyName Key name identifying the key to use. * @param [in] iv IV to use. - * @param [in, out] mac MAC of the encrypted data. + * @param [in, out] mac MAC of the encrypted data. * @param [in] enc 1 to encrypt the ticket, 0 to decrypt. * @param [in, out] encTicket Ticket data, encrypted/decrypted in place. * @param [in] encTicketLen Length of the ticket data in bytes. @@ -1858,7 +2193,7 @@ static int wolfSSL_TicketKeyCb(WOLFSSL* ssl, ret = WOLFSSL_TICKET_RET_FATAL; } -#ifdef WOLFSSL_SMALL_STACK + #ifdef WOLFSSL_SMALL_STACK if (ret == WOLFSSL_TICKET_RET_OK) { evpCtx = (WOLFSSL_EVP_CIPHER_CTX *)XMALLOC(sizeof(*evpCtx), ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); @@ -1867,7 +2202,7 @@ static int wolfSSL_TicketKeyCb(WOLFSSL* ssl, ret = WOLFSSL_TICKET_RET_FATAL; } } -#endif + #endif if (ret == WOLFSSL_TICKET_RET_OK) { WOLFSSL_HMAC_CTX hmacCtx; @@ -1881,52 +2216,8 @@ static int wolfSSL_TicketKeyCb(WOLFSSL* ssl, } if (ret == WOLFSSL_TICKET_RET_OK) { - int res; - int totalSz = 0; - - res = ssl->ctx->ticketEncWrapCb(ssl, keyName, iv, evpCtx, &hmacCtx, - enc); - if ((res != TICKET_KEY_CB_RET_OK) && - (res != TICKET_KEY_CB_RET_RENEW)) { - WOLFSSL_MSG("Ticket callback error"); - ret = WOLFSSL_TICKET_RET_FATAL; - } - - if (ret == WOLFSSL_TICKET_RET_OK) { - if (wolfSSL_HMAC_size(&hmacCtx) > WOLFSSL_TICKET_MAC_SZ) { - WOLFSSL_MSG("Ticket cipher MAC size error"); - ret = WOLFSSL_TICKET_RET_FATAL; - } - } - - if (ret == WOLFSSL_TICKET_RET_OK) { - if (enc) { - if (!wolfssl_ticket_key_enc(evpCtx, &hmacCtx, encTicket, - encTicketLen, *encLen, mac, &totalSz)) { - ret = WOLFSSL_TICKET_RET_FATAL; - } - } - else { - if (!wolfssl_ticket_key_dec(evpCtx, &hmacCtx, encTicket, - encTicketLen, mac, &totalSz)) { - ret = WOLFSSL_TICKET_RET_FATAL; - } - } - } - - if (ret == WOLFSSL_TICKET_RET_OK) { - *encLen = totalSz; - - if ((res == TICKET_KEY_CB_RET_RENEW) && - (!IsAtLeastTLSv1_3(ssl->version)) && (!enc)) { - ret = WOLFSSL_TICKET_RET_CREATE; - } - else { - ret = WOLFSSL_TICKET_RET_OK; - } - } - - wolfSSL_HMAC_CTX_cleanup(&hmacCtx); + ret = wolfssl_ticket_key_cb_process(ssl, keyName, iv, mac, enc, + encTicket, encTicketLen, encLen, evpCtx, &hmacCtx); } (void)wolfSSL_EVP_CIPHER_CTX_cleanup(evpCtx); WC_FREE_VAR_EX(evpCtx, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); @@ -1964,9 +2255,9 @@ int wolfSSL_CTX_set_tlsext_ticket_key_cb(WOLFSSL_CTX *ctx, ticketCompatCb cb) !defined(NO_WOLFSSL_SERVER) /* Serialize the session ticket encryption keys. * - * @param [in] ctx SSL/TLS context object. - * @param [in] keys Buffer to hold session ticket keys. - * @param [in] keylen Length of buffer. + * @param [in] ctx SSL/TLS context object. + * @param [in] keys Buffer to hold session ticket keys. + * @param [in] keylen Length of buffer. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_FAILURE when ctx is NULL, keys is NULL or keylen is not the * correct length. @@ -1974,31 +2265,32 @@ int wolfSSL_CTX_set_tlsext_ticket_key_cb(WOLFSSL_CTX *ctx, ticketCompatCb cb) long wolfSSL_CTX_get_tlsext_ticket_keys(WOLFSSL_CTX *ctx, unsigned char *keys, int keylen) { - if (ctx == NULL || keys == NULL) { - return WOLFSSL_FAILURE; + long ret = WOLFSSL_SUCCESS; + + if ((ctx == NULL) || (keys == NULL) || + (keylen != WOLFSSL_TICKET_KEYS_SZ)) { + ret = WOLFSSL_FAILURE; } - if (keylen != WOLFSSL_TICKET_KEYS_SZ) { - return WOLFSSL_FAILURE; + else { + XMEMCPY(keys, ctx->ticketKeyCtx.name, WOLFSSL_TICKET_NAME_SZ); + keys += WOLFSSL_TICKET_NAME_SZ; + XMEMCPY(keys, ctx->ticketKeyCtx.key[0], WOLFSSL_TICKET_KEY_SZ); + keys += WOLFSSL_TICKET_KEY_SZ; + XMEMCPY(keys, ctx->ticketKeyCtx.key[1], WOLFSSL_TICKET_KEY_SZ); + keys += WOLFSSL_TICKET_KEY_SZ; + c32toa(ctx->ticketKeyCtx.expirary[0], keys); + keys += OPAQUE32_LEN; + c32toa(ctx->ticketKeyCtx.expirary[1], keys); } - XMEMCPY(keys, ctx->ticketKeyCtx.name, WOLFSSL_TICKET_NAME_SZ); - keys += WOLFSSL_TICKET_NAME_SZ; - XMEMCPY(keys, ctx->ticketKeyCtx.key[0], WOLFSSL_TICKET_KEY_SZ); - keys += WOLFSSL_TICKET_KEY_SZ; - XMEMCPY(keys, ctx->ticketKeyCtx.key[1], WOLFSSL_TICKET_KEY_SZ); - keys += WOLFSSL_TICKET_KEY_SZ; - c32toa(ctx->ticketKeyCtx.expirary[0], keys); - keys += OPAQUE32_LEN; - c32toa(ctx->ticketKeyCtx.expirary[1], keys); - - return WOLFSSL_SUCCESS; + return ret; } /* Deserialize the session ticket encryption keys. * - * @param [in] ctx SSL/TLS context object. - * @param [in] keys Session ticket keys. - * @param [in] keylen Length of data. + * @param [in] ctx SSL/TLS context object. + * @param [in] keys_vp Session ticket keys. + * @param [in] keylen Length of data. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_FAILURE when ctx is NULL, keys is NULL or keylen is not the * correct length. @@ -2007,24 +2299,25 @@ long wolfSSL_CTX_set_tlsext_ticket_keys(WOLFSSL_CTX *ctx, const void *keys_vp, int keylen) { const byte* keys = (const byte*)keys_vp; - if (ctx == NULL || keys == NULL) { - return WOLFSSL_FAILURE; + long ret = WOLFSSL_SUCCESS; + + if ((ctx == NULL) || (keys == NULL) || + (keylen != WOLFSSL_TICKET_KEYS_SZ)) { + ret = WOLFSSL_FAILURE; } - if (keylen != WOLFSSL_TICKET_KEYS_SZ) { - return WOLFSSL_FAILURE; + else { + XMEMCPY(ctx->ticketKeyCtx.name, keys, WOLFSSL_TICKET_NAME_SZ); + keys += WOLFSSL_TICKET_NAME_SZ; + XMEMCPY(ctx->ticketKeyCtx.key[0], keys, WOLFSSL_TICKET_KEY_SZ); + keys += WOLFSSL_TICKET_KEY_SZ; + XMEMCPY(ctx->ticketKeyCtx.key[1], keys, WOLFSSL_TICKET_KEY_SZ); + keys += WOLFSSL_TICKET_KEY_SZ; + ato32(keys, &ctx->ticketKeyCtx.expirary[0]); + keys += OPAQUE32_LEN; + ato32(keys, &ctx->ticketKeyCtx.expirary[1]); } - XMEMCPY(ctx->ticketKeyCtx.name, keys, WOLFSSL_TICKET_NAME_SZ); - keys += WOLFSSL_TICKET_NAME_SZ; - XMEMCPY(ctx->ticketKeyCtx.key[0], keys, WOLFSSL_TICKET_KEY_SZ); - keys += WOLFSSL_TICKET_KEY_SZ; - XMEMCPY(ctx->ticketKeyCtx.key[1], keys, WOLFSSL_TICKET_KEY_SZ); - keys += WOLFSSL_TICKET_KEY_SZ; - ato32(keys, &ctx->ticketKeyCtx.expirary[0]); - keys += OPAQUE32_LEN; - ato32(keys, &ctx->ticketKeyCtx.expirary[1]); - - return WOLFSSL_SUCCESS; + return ret; } #endif @@ -2054,10 +2347,10 @@ void wolfSSL_get0_alpn_selected(const WOLFSSL *ssl, const unsigned char **data, * The client's list is in wire format: each entry is a length byte followed * by that many protocol-name bytes. * - * @param [in] proto Protocol name to look for. - * @param [in] protoLen Length of the protocol name in bytes. - * @param [in] clientNames Client's protocol list. - * @param [in] clientLen Length of the client's list in bytes. + * @param [in] proto Protocol name to look for. + * @param [in] protoLen Length of the protocol name in bytes. + * @param [in] clientNames Client's protocol list. + * @param [in] clientLen Length of the client's list in bytes. * @return 1 when the protocol is in the list. * @return 0 when the protocol is not in the list. */ @@ -2090,11 +2383,11 @@ static int wolfssl_protocol_in_list(const unsigned char* proto, byte protoLen, * On no overlap, the first client protocol is selected. * * @param [out] out Selected protocol data. - * @param [out] outLen Length of the selected protocol in bytes. - * @param [in] in Peer's protocol list. - * @param [in] inLen Length of the peer's list in bytes. - * @param [in] clientNames Client's protocol list. - * @param [in] clientLen Length of the client's list in bytes. + * @param [out] outLen Length of the selected protocol in bytes. + * @param [in] in Peer's protocol list. + * @param [in] inLen Length of the peer's list in bytes. + * @param [in] clientNames Client's protocol list. + * @param [in] clientLen Length of the client's list in bytes. * @return WOLFSSL_NPN_NEGOTIATED when a match was found. * @return WOLFSSL_NPN_NO_OVERLAP when no match was found. * @return WOLFSSL_NPN_UNSUPPORTED when an argument is NULL. @@ -2287,12 +2580,19 @@ int wolfSSL_curve_is_disabled(const WOLFSSL* ssl, word16 curve_id) */ int wolfSSL_CTX_set1_curves_list(WOLFSSL_CTX* ctx, const char* names) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_set1_curves_list"); - if (ctx == NULL || names == NULL) { + + if ((ctx == NULL) || (names == NULL)) { WOLFSSL_MSG("ctx or names was NULL"); - return WOLFSSL_FAILURE; + ret = WOLFSSL_FAILURE; + } + else { + ret = set_curves_list(NULL, ctx, names, 1); } - return set_curves_list(NULL, ctx, names, 1); + + return ret; } /* Set the supported curves list, by name, on the object. @@ -2304,12 +2604,19 @@ int wolfSSL_CTX_set1_curves_list(WOLFSSL_CTX* ctx, const char* names) */ int wolfSSL_set1_curves_list(WOLFSSL* ssl, const char* names) { + int ret; + WOLFSSL_ENTER("wolfSSL_set1_curves_list"); - if (ssl == NULL || names == NULL) { + + if ((ssl == NULL) || (names == NULL)) { WOLFSSL_MSG("ssl or names was NULL"); - return WOLFSSL_FAILURE; + ret = WOLFSSL_FAILURE; + } + else { + ret = set_curves_list(ssl, NULL, names, 1); } - return set_curves_list(ssl, NULL, names, 1); + + return ret; } #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */ @@ -2329,36 +2636,44 @@ int wolfSSL_set1_curves_list(WOLFSSL* ssl, const char* names) int wolfSSL_CTX_set_alpn_protos(WOLFSSL_CTX *ctx, const unsigned char *p, unsigned int p_len) { + int ret; + WOLFSSL_ENTER("wolfSSL_CTX_set_alpn_protos"); - if (ctx == NULL || p == NULL) - return BAD_FUNC_ARG; - if (ctx->alpn_cli_protos != NULL) { - XFREE((void*)ctx->alpn_cli_protos, ctx->heap, DYNAMIC_TYPE_OPENSSL); + + if ((ctx == NULL) || (p == NULL)) { + ret = BAD_FUNC_ARG; } + else { + if (ctx->alpn_cli_protos != NULL) { + XFREE((void*)ctx->alpn_cli_protos, ctx->heap, DYNAMIC_TYPE_OPENSSL); + } - ctx->alpn_cli_protos = (const unsigned char*)XMALLOC(p_len, - ctx->heap, DYNAMIC_TYPE_OPENSSL); - if (ctx->alpn_cli_protos == NULL) { -#if defined(WOLFSSL_ERROR_CODE_OPENSSL) - /* 0 on success in OpenSSL, non-0 on failure in OpenSSL - * the function reverses the return value convention. - */ - return 1; -#else - return WOLFSSL_FAILURE; -#endif + ctx->alpn_cli_protos = (const unsigned char*)XMALLOC(p_len, + ctx->heap, DYNAMIC_TYPE_OPENSSL); + if (ctx->alpn_cli_protos == NULL) { + /* 0 on success in OpenSSL, non-0 on failure in OpenSSL - the + * function reverses the return value convention. */ + #if defined(WOLFSSL_ERROR_CODE_OPENSSL) + ret = 1; + #else + ret = WOLFSSL_FAILURE; + #endif + } + else { + XMEMCPY((void*)ctx->alpn_cli_protos, p, p_len); + ctx->alpn_cli_protos_len = p_len; + + /* 0 on success in OpenSSL, non-0 on failure in OpenSSL - the + * function reverses the return value convention. */ + #if defined(WOLFSSL_ERROR_CODE_OPENSSL) + ret = 0; + #else + ret = WOLFSSL_SUCCESS; + #endif + } } - XMEMCPY((void*)ctx->alpn_cli_protos, p, p_len); - ctx->alpn_cli_protos_len = p_len; -#if defined(WOLFSSL_ERROR_CODE_OPENSSL) - /* 0 on success in OpenSSL, non-0 on failure in OpenSSL - * the function reverses the return value convention. - */ - return 0; -#else - return WOLFSSL_SUCCESS; -#endif + return ret; } @@ -2432,11 +2747,11 @@ int wolfSSL_set_alpn_protos(WOLFSSL* ssl, * protocols MUST send no_application_protocol. Match that contract on * the OpenSSL-compat surface rather than silently continuing. */ int alpn_opt = WOLFSSL_ALPN_FAILED_ON_MISMATCH; -#if defined(WOLFSSL_ERROR_CODE_OPENSSL) + #if defined(WOLFSSL_ERROR_CODE_OPENSSL) int ret = 1; -#else + #else int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); -#endif + #endif WOLFSSL_ENTER("wolfSSL_set_alpn_protos"); @@ -2453,11 +2768,11 @@ int wolfSSL_set_alpn_protos(WOLFSSL* ssl, if (wolfSSL_UseALPN(ssl, pt, ptIdx, (byte)alpn_opt) == WOLFSSL_SUCCESS) { - #if defined(WOLFSSL_ERROR_CODE_OPENSSL) + #if defined(WOLFSSL_ERROR_CODE_OPENSSL) ret = 0; - #else + #else ret = WOLFSSL_SUCCESS; - #endif + #endif } } diff --git a/src/ssl_api_hs.c b/src/ssl_api_hs.c index 1f91406aacd..69db2d2ea59 100644 --- a/src/ssl_api_hs.c +++ b/src/ssl_api_hs.c @@ -30,39 +30,54 @@ #ifndef WOLFCRYPT_ONLY #ifndef NO_TLS -/* return underlying connect or accept, WOLFSSL_SUCCESS on ok */ +/* Perform the handshake, calling connect or accept as appropriate. + * + * The side must already have been established, either by the method used to + * create the object or with wolfSSL_set_connect_state() or + * wolfSSL_set_accept_state(). + * + * @param [in, out] ssl SSL/TLS object. + * @return WOLFSSL_SUCCESS when the handshake completes. + * @return WOLFSSL_FATAL_ERROR when ssl is NULL, no side has been established + * or the handshake fails. + */ int wolfSSL_negotiate(WOLFSSL* ssl) { int err = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR); WOLFSSL_ENTER("wolfSSL_negotiate"); - if (ssl == NULL) - return WOLFSSL_FATAL_ERROR; - -#ifndef NO_WOLFSSL_SERVER - if (ssl->options.side == WOLFSSL_SERVER_END) { -#ifdef WOLFSSL_TLS13 - if (IsAtLeastTLSv1_3(ssl->version)) - err = wolfSSL_accept_TLSv13(ssl); - else -#endif - err = wolfSSL_accept(ssl); - } -#endif + /* err starts as a failure, which is what a NULL object and an object with + * no side established both report. */ + if (ssl != NULL) { + #ifndef NO_WOLFSSL_SERVER + if (ssl->options.side == WOLFSSL_SERVER_END) { + #ifdef WOLFSSL_TLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + err = wolfSSL_accept_TLSv13(ssl); + } + else + #endif + { + err = wolfSSL_accept(ssl); + } + } + #endif -#ifndef NO_WOLFSSL_CLIENT - if (ssl->options.side == WOLFSSL_CLIENT_END) { -#ifdef WOLFSSL_TLS13 - if (IsAtLeastTLSv1_3(ssl->version)) - err = wolfSSL_connect_TLSv13(ssl); - else -#endif - err = wolfSSL_connect(ssl); + #ifndef NO_WOLFSSL_CLIENT + if (ssl->options.side == WOLFSSL_CLIENT_END) { + #ifdef WOLFSSL_TLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + err = wolfSSL_connect_TLSv13(ssl); + } + else + #endif + { + err = wolfSSL_connect(ssl); + } + } + #endif } -#endif - - (void)ssl; WOLFSSL_LEAVE("wolfSSL_negotiate", err); @@ -70,449 +85,570 @@ int wolfSSL_negotiate(WOLFSSL* ssl) } #endif /* !NO_TLS */ +#if !defined(NO_TLS) && !(defined(WOLFSSL_NO_TLS12) && \ + defined(NO_OLD_TLS) && defined(WOLFSSL_TLS13)) && \ + (!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER)) +/* Send any buffered output and retry a pending alert. + * + * Called at the start of each handshake step so that a message left unsent by + * a previous call is flushed before the next one is built. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] advance Whether the handshake state may be advanced + * once the whole message has been sent. + * @param [in, out] state Handshake state to advance. + * @param [in] isConnect Whether this is the client's handshake. Only + * names the side in the log messages. + * @return 0 when there was nothing to send or everything was sent. + * @return WOLFSSL_FATAL_ERROR when sending fails. ssl->error holds the reason. + */ +static int wolfssl_handshake_flush(WOLFSSL* ssl, int advance, byte* state, + int isConnect) +{ + int ret = 0; + + /* Only used in the log messages, which may be compiled out. */ + (void)isConnect; + + if ((ssl->buffers.outputBuffer.length > 0) + #ifdef WOLFSSL_ASYNC_CRYPT + /* do not send buffered or advance state if last error was an + async pending operation */ + && (ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E)) + #endif + ) { + ret = SendBuffered(ssl); + if (ret == 0) { + /* fragOffset is non-zero when sending fragments. On the last + * fragment, fragOffset is zero again, and the state can be + * advanced. */ + if ((ssl->fragOffset == 0) && (!ssl->options.buildingMsg)) { + if (advance) { + (*state)++; + WOLFSSL_MSG(isConnect ? + "connect state: Advanced from last buffered " + "fragment send" : + "accept state: Advanced from last buffered " + "fragment send"); + #ifdef WOLFSSL_ASYNC_IO + /* Cleanup async */ + FreeAsyncCtx(ssl, 0); + #endif + } + } + else { + WOLFSSL_MSG(isConnect ? + "connect state: Not advanced, more fragments to send" : + "accept state: Not advanced, more fragments to send"); + } + #ifdef WOLFSSL_DTLS13 + if (ssl->options.dtls) { + ssl->dtls13SendingAckOrRtx = 0; + } + #endif /* WOLFSSL_DTLS13 */ + } + else { + ssl->error = ret; + WOLFSSL_ERROR(ssl->error); + ret = WOLFSSL_FATAL_ERROR; + } + } + + if (ret == 0) { + ret = RetrySendAlert(ssl); + if (ret != 0) { + ssl->error = ret; + WOLFSSL_ERROR(ssl->error); + ret = WOLFSSL_FATAL_ERROR; + } + } + + return ret; +} + +/* Only reached from the server's accept, and from the client's connect when + * a pre-TLS-1.3 version is built, so it is guarded more tightly than the + * flush above. */ +#if !defined(NO_WOLFSSL_SERVER) || \ + (!defined(NO_WOLFSSL_CLIENT) && \ + (!defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS))) +/* Finish the handshake. + * + * Notifies the application, releases the memory used only during the handshake + * and discards any asynchronous state. + * + * @param [in, out] ssl SSL/TLS object. + * @return 0 when the handshake is finished. + * @return WOLFSSL_FATAL_ERROR when the handshake done callback asks to stop. + * ssl->error holds the value the callback returned. + */ +static int wolfssl_handshake_done(WOLFSSL* ssl) +{ + int ret = 0; + + #ifndef NO_HANDSHAKE_DONE_CB + if (ssl->hsDoneCb != NULL) { + int cbret = ssl->hsDoneCb(ssl, ssl->hsDoneCtx); + if (cbret < 0) { + ssl->error = cbret; + WOLFSSL_MSG("HandShake Done Cb don't continue error"); + ret = WOLFSSL_FATAL_ERROR; + } + } + #endif /* NO_HANDSHAKE_DONE_CB */ + + if (ret == 0) { + if (!ssl->options.dtls) { + if (!ssl->options.keepResources) { + FreeHandshakeResources(ssl); + } + } + #ifdef WOLFSSL_DTLS + else { + ssl->options.dtlsHsRetain = 1; + } + #endif /* WOLFSSL_DTLS */ + + #if defined(WOLFSSL_ASYNC_CRYPT) && defined(HAVE_SECURE_RENEGOTIATION) + /* This may be necessary in async so that we don't try to + * renegotiate again */ + if ((ssl->secure_renegotiation != NULL) && + (ssl->secure_renegotiation->startScr)) { + ssl->secure_renegotiation->startScr = 0; + } + #endif /* WOLFSSL_ASYNC_CRYPT && HAVE_SECURE_RENEGOTIATION */ + #if defined(WOLFSSL_ASYNC_IO) && !defined(WOLFSSL_ASYNC_CRYPT) + /* Free the remaining async context if not using it for crypto */ + FreeAsyncCtx(ssl, 1); + #endif + } + + return ret; +} +#endif /* !NO_WOLFSSL_SERVER || (!NO_WOLFSSL_CLIENT && + * (!WOLFSSL_NO_TLS12 || !NO_OLD_TLS)) */ +#endif + /* client only parts */ #if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) - /* please see note at top of README if you get an error from connect */ - WOLFSSL_ABI - int wolfSSL_connect(WOLFSSL* ssl) - { +/* Perform the client side of the handshake. + * + * Drives the handshake state machine, resuming from wherever the previous call + * stopped. When non-blocking I/O is in use, the call returns before the + * handshake completes and must be called again. + * + * Please see the note at the top of README if you get an error from connect. + * + * @param [in, out] ssl SSL/TLS object. + * @return WOLFSSL_SUCCESS when the handshake completes. + * @return BAD_FUNC_ARG when ssl is NULL. + * @return WOLFSSL_FATAL_ERROR when the object is not a client, a message + * cannot be sent or received, or the peer reports an error. Call + * wolfSSL_get_error() to determine whether the operation should be + * retried. + * + * Unlike the rest of this file, the handshake state machine below + * returns from each step rather than using a single exit. Each step + * must stop the handshake where it failed, and several of the steps + * return from inside a receive loop, where a break would only leave + * the loop. + */ +WOLFSSL_ABI +int wolfSSL_connect(WOLFSSL* ssl) +{ #if !(defined(WOLFSSL_NO_TLS12) && defined(NO_OLD_TLS) && \ - defined(WOLFSSL_TLS13)) - int neededState; - byte advanceState; + defined(WOLFSSL_TLS13)) + int neededState; + byte advanceState; #endif - int ret = 0; + int ret = 0; - (void)ret; + (void)ret; - #ifdef HAVE_ERRNO_H - errno = 0; - #endif + #ifdef HAVE_ERRNO_H + errno = 0; + #endif - if (ssl == NULL) - return BAD_FUNC_ARG; + if (ssl == NULL) { + return BAD_FUNC_ARG; + } #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE) - if (ssl->options.side == WOLFSSL_NEITHER_END) { - ssl->error = InitSSL_Side(ssl, WOLFSSL_CLIENT_END); - if (ssl->error != WOLFSSL_SUCCESS) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - ssl->error = 0; /* expected to be zero here */ + if (ssl->options.side == WOLFSSL_NEITHER_END) { + ssl->error = InitSSL_Side(ssl, WOLFSSL_CLIENT_END); + if (ssl->error != WOLFSSL_SUCCESS) { + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; } + ssl->error = 0; /* expected to be zero here */ + } #ifdef OPENSSL_EXTRA - if (ssl->CBIS != NULL) { - ssl->CBIS(ssl, WOLFSSL_ST_CONNECT, WOLFSSL_SUCCESS); - ssl->cbmode = WOLFSSL_CB_WRITE; - } + if (ssl->CBIS != NULL) { + ssl->CBIS(ssl, WOLFSSL_ST_CONNECT, WOLFSSL_SUCCESS); + ssl->cbmode = WOLFSSL_CB_WRITE; + } #endif #endif /* OPENSSL_EXTRA || WOLFSSL_EITHER_SIDE */ #if defined(WOLFSSL_NO_TLS12) && defined(NO_OLD_TLS) && \ defined(WOLFSSL_TLS13) - return wolfSSL_connect_TLSv13(ssl); + return wolfSSL_connect_TLSv13(ssl); #else - #ifdef WOLFSSL_TLS13 - if (ssl->options.tls1_3) { - WOLFSSL_MSG("TLS 1.3"); - return wolfSSL_connect_TLSv13(ssl); - } - #endif - - WOLFSSL_MSG("TLS 1.2 or lower"); - WOLFSSL_ENTER("wolfSSL_connect"); + #ifdef WOLFSSL_TLS13 + if (ssl->options.tls1_3) { + WOLFSSL_MSG("TLS 1.3"); + return wolfSSL_connect_TLSv13(ssl); + } + #endif - /* make sure this wolfSSL object has arrays and rng setup. Protects - * case where the WOLFSSL object is reused via wolfSSL_clear() */ - if ((ret = ReinitSSL(ssl, ssl->ctx, 0)) != 0) { - return ret; - } + WOLFSSL_MSG("TLS 1.2 or lower"); + WOLFSSL_ENTER("wolfSSL_connect"); -#ifdef WOLFSSL_WOLFSENTRY_HOOKS - if ((ssl->ConnectFilter != NULL) && - (ssl->options.connectState == CONNECT_BEGIN)) { - wolfSSL_netfilter_decision_t res; - if ((ssl->ConnectFilter(ssl, ssl->ConnectFilter_arg, &res) == - WOLFSSL_SUCCESS) && - (res == WOLFSSL_NETFILTER_REJECT)) { - ssl->error = SOCKET_FILTERED_E; - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - } -#endif /* WOLFSSL_WOLFSENTRY_HOOKS */ + /* make sure this wolfSSL object has arrays and rng setup. Protects + * case where the WOLFSSL object is reused via wolfSSL_clear() */ + if ((ret = ReinitSSL(ssl, ssl->ctx, 0)) != 0) { + return ret; + } - if (ssl->options.side != WOLFSSL_CLIENT_END) { - ssl->error = SIDE_ERROR; + #ifdef WOLFSSL_WOLFSENTRY_HOOKS + if ((ssl->ConnectFilter != NULL) && + (ssl->options.connectState == CONNECT_BEGIN)) { + wolfSSL_netfilter_decision_t res; + if ((ssl->ConnectFilter(ssl, ssl->ConnectFilter_arg, &res) == + WOLFSSL_SUCCESS) && + (res == WOLFSSL_NETFILTER_REJECT)) { + ssl->error = SOCKET_FILTERED_E; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } + } + #endif /* WOLFSSL_WOLFSENTRY_HOOKS */ - #ifdef WOLFSSL_DTLS - if (ssl->version.major == DTLS_MAJOR) { - ssl->options.dtls = 1; - ssl->options.tls = 1; - ssl->options.tls1_1 = 1; - ssl->options.dtlsStateful = 1; - } - #endif + if (ssl->options.side != WOLFSSL_CLIENT_END) { + ssl->error = SIDE_ERROR; + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } - /* fragOffset is non-zero when sending fragments. On the last - * fragment, fragOffset is zero again, and the state can be - * advanced. */ - advanceState = ssl->fragOffset == 0 && - (ssl->options.connectState == CONNECT_BEGIN || - ssl->options.connectState == HELLO_AGAIN || - (ssl->options.connectState >= FIRST_REPLY_DONE && - ssl->options.connectState <= FIRST_REPLY_FOURTH)); - -#ifdef WOLFSSL_DTLS13 - if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) - advanceState = advanceState && !ssl->dtls13SendingAckOrRtx; -#endif /* WOLFSSL_DTLS13 */ - - if (ssl->buffers.outputBuffer.length > 0 - #ifdef WOLFSSL_ASYNC_CRYPT - /* do not send buffered or advance state if last error was an - async pending operation */ - && ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E) - #endif - ) { - ret = SendBuffered(ssl); - if (ret == 0) { - if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) { - if (advanceState) { - ssl->options.connectState++; - WOLFSSL_MSG("connect state: Advanced from last " - "buffered fragment send"); - #ifdef WOLFSSL_ASYNC_IO - /* Cleanup async */ - FreeAsyncCtx(ssl, 0); - #endif - } - } - else { - WOLFSSL_MSG("connect state: " - "Not advanced, more fragments to send"); - } - } - else { - ssl->error = ret; - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } -#ifdef WOLFSSL_DTLS13 - if (ssl->options.dtls) - ssl->dtls13SendingAckOrRtx = 0; -#endif /* WOLFSSL_DTLS13 */ - } + #ifdef WOLFSSL_DTLS + if (ssl->version.major == DTLS_MAJOR) { + ssl->options.dtls = 1; + ssl->options.tls = 1; + ssl->options.tls1_1 = 1; + ssl->options.dtlsStateful = 1; + } + #endif - ret = RetrySendAlert(ssl); - if (ret != 0) { - ssl->error = ret; + /* fragOffset is non-zero when sending fragments. On the last + * fragment, fragOffset is zero again, and the state can be + * advanced. */ + advanceState = (byte)((ssl->fragOffset == 0) && + ((ssl->options.connectState == CONNECT_BEGIN) || + (ssl->options.connectState == HELLO_AGAIN) || + ((ssl->options.connectState >= FIRST_REPLY_DONE) && + (ssl->options.connectState <= FIRST_REPLY_FOURTH)))); + + #ifdef WOLFSSL_DTLS13 + if ((ssl->options.dtls) && (IsAtLeastTLSv1_3(ssl->version))) { + advanceState = (byte)((advanceState) && + (!ssl->dtls13SendingAckOrRtx)); + } + #endif /* WOLFSSL_DTLS13 */ + + ret = wolfssl_handshake_flush(ssl, advanceState, + &ssl->options.connectState, 1); + if (ret != 0) { + return ret; + } + + switch (ssl->options.connectState) { + + case CONNECT_BEGIN : + /* always send client hello first */ + if ((ssl->error = SendClientHello(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } + ssl->options.connectState = CLIENT_HELLO_SENT; + WOLFSSL_MSG("connect state: CLIENT_HELLO_SENT"); + FALL_THROUGH; - switch (ssl->options.connectState) { - - case CONNECT_BEGIN : - /* always send client hello first */ - if ( (ssl->error = SendClientHello(ssl)) != 0) { + case CLIENT_HELLO_SENT : + neededState = ssl->options.resuming ? SERVER_FINISHED_COMPLETE : + SERVER_HELLODONE_COMPLETE; + #ifdef WOLFSSL_DTLS + /* In DTLS, when resuming, we can go straight to FINISHED, + * or do a cookie exchange and then skip to FINISHED, assume + * we need the cookie exchange first. */ + if (IsDtlsNotSctpMode(ssl)) { + neededState = SERVER_HELLOVERIFYREQUEST_COMPLETE; + } + #endif + /* get response */ + WOLFSSL_MSG("Server state up to needed state."); + while (ssl->options.serverState < neededState) { + WOLFSSL_MSG("Progressing server state..."); + #ifdef WOLFSSL_TLS13 + if (ssl->options.tls1_3) { + return wolfSSL_connect_TLSv13(ssl); + } + #endif + WOLFSSL_MSG("ProcessReply..."); + if ((ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } - ssl->options.connectState = CLIENT_HELLO_SENT; - WOLFSSL_MSG("connect state: CLIENT_HELLO_SENT"); - FALL_THROUGH; - - case CLIENT_HELLO_SENT : - neededState = ssl->options.resuming ? SERVER_FINISHED_COMPLETE : - SERVER_HELLODONE_COMPLETE; - #ifdef WOLFSSL_DTLS - /* In DTLS, when resuming, we can go straight to FINISHED, - * or do a cookie exchange and then skip to FINISHED, assume - * we need the cookie exchange first. */ - if (IsDtlsNotSctpMode(ssl)) - neededState = SERVER_HELLOVERIFYREQUEST_COMPLETE; - #endif - /* get response */ - WOLFSSL_MSG("Server state up to needed state."); - while (ssl->options.serverState < neededState) { - WOLFSSL_MSG("Progressing server state..."); - #ifdef WOLFSSL_TLS13 - if (ssl->options.tls1_3) - return wolfSSL_connect_TLSv13(ssl); - #endif - WOLFSSL_MSG("ProcessReply..."); - if ( (ssl->error = ProcessReply(ssl)) < 0) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - /* if resumption failed, reset needed state */ - else if (neededState == SERVER_FINISHED_COMPLETE) { - if (!ssl->options.resuming) { + /* if resumption failed, reset needed state */ + else if (neededState == SERVER_FINISHED_COMPLETE) { + if (!ssl->options.resuming) { #ifdef WOLFSSL_DTLS - if (IsDtlsNotSctpMode(ssl)) - neededState = SERVER_HELLOVERIFYREQUEST_COMPLETE; - else - #endif - neededState = SERVER_HELLODONE_COMPLETE; + if (IsDtlsNotSctpMode(ssl)) { + neededState = SERVER_HELLOVERIFYREQUEST_COMPLETE; } + else + #endif + neededState = SERVER_HELLODONE_COMPLETE; } - WOLFSSL_MSG("ProcessReply done."); - -#ifdef WOLFSSL_DTLS13 - if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version) - && ssl->dtls13Rtx.sendAcks == 1 - && ssl->options.seenUnifiedHdr) { - /* we aren't negotiated the version yet, so we aren't sure - * the other end can speak v1.3. On the other side we have - * received a unified records, assuming that the - * ServerHello got lost, we will send an empty ACK. In case - * the server is a DTLS with version less than 1.3, it - * should just ignore the message */ - ssl->dtls13Rtx.sendAcks = 0; - if ((ssl->error = SendDtls13Ack(ssl)) < 0) { - if (ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) - ssl->dtls13SendingAckOrRtx = 1; - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; + } + WOLFSSL_MSG("ProcessReply done."); + + #ifdef WOLFSSL_DTLS13 + if ((ssl->options.dtls) && (IsAtLeastTLSv1_3(ssl->version)) + && (ssl->dtls13Rtx.sendAcks == 1) + && (ssl->options.seenUnifiedHdr)) { + /* we aren't negotiated the version yet, so we aren't sure + * the other end can speak v1.3. On the other side we have + * received a unified records, assuming that the + * ServerHello got lost, we will send an empty ACK. In case + * the server is a DTLS with version less than 1.3, it + * should just ignore the message */ + ssl->dtls13Rtx.sendAcks = 0; + if ((ssl->error = SendDtls13Ack(ssl)) < 0) { + if (ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) { + ssl->dtls13SendingAckOrRtx = 1; } + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; } -#endif /* WOLFSSL_DTLS13 */ } + #endif /* WOLFSSL_DTLS13 */ + } - ssl->options.connectState = HELLO_AGAIN; - WOLFSSL_MSG("connect state: HELLO_AGAIN"); - FALL_THROUGH; + ssl->options.connectState = HELLO_AGAIN; + WOLFSSL_MSG("connect state: HELLO_AGAIN"); + FALL_THROUGH; - case HELLO_AGAIN : + case HELLO_AGAIN : #ifdef WOLFSSL_TLS13 - if (ssl->options.tls1_3) - return wolfSSL_connect_TLSv13(ssl); + if (ssl->options.tls1_3) { + return wolfSSL_connect_TLSv13(ssl); + } #endif - #ifdef WOLFSSL_DTLS - if (ssl->options.serverState == - SERVER_HELLOVERIFYREQUEST_COMPLETE) { - if (IsDtlsNotSctpMode(ssl)) { - /* re-init hashes, exclude first hello and verify request */ - if ((ssl->error = InitHandshakeHashes(ssl)) != 0) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - if ( (ssl->error = SendClientHello(ssl)) != 0) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } + #ifdef WOLFSSL_DTLS + if (ssl->options.serverState == + SERVER_HELLOVERIFYREQUEST_COMPLETE) { + if (IsDtlsNotSctpMode(ssl)) { + /* re-init hashes, exclude first hello and verify request */ + if ((ssl->error = InitHandshakeHashes(ssl)) != 0) { + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; } - } - #endif - - ssl->options.connectState = HELLO_AGAIN_REPLY; - WOLFSSL_MSG("connect state: HELLO_AGAIN_REPLY"); - FALL_THROUGH; - - case HELLO_AGAIN_REPLY : - #ifdef WOLFSSL_DTLS - if (IsDtlsNotSctpMode(ssl)) { - neededState = ssl->options.resuming ? - SERVER_FINISHED_COMPLETE : SERVER_HELLODONE_COMPLETE; - - /* get response */ - while (ssl->options.serverState < neededState) { - if ( (ssl->error = ProcessReply(ssl)) < 0) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - /* if resumption failed, reset needed state */ - if (neededState == SERVER_FINISHED_COMPLETE) { - if (!ssl->options.resuming) - neededState = SERVER_HELLODONE_COMPLETE; - } - } + if ((ssl->error = SendClientHello(ssl)) != 0) { + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; } - #endif + } + } + #endif - ssl->options.connectState = FIRST_REPLY_DONE; - WOLFSSL_MSG("connect state: FIRST_REPLY_DONE"); - FALL_THROUGH; - - case FIRST_REPLY_DONE : - if (ssl->options.certOnly) - return WOLFSSL_SUCCESS; - #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) - #ifdef WOLFSSL_TLS13 - if (ssl->options.tls1_3) - return wolfSSL_connect_TLSv13(ssl); - #endif - if (ssl->options.sendVerify) { - if ( (ssl->error = SendCertificate(ssl)) != 0) { - wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - WOLFSSL_MSG("sent: certificate"); - } + ssl->options.connectState = HELLO_AGAIN_REPLY; + WOLFSSL_MSG("connect state: HELLO_AGAIN_REPLY"); + FALL_THROUGH; - #endif - ssl->options.connectState = FIRST_REPLY_FIRST; - WOLFSSL_MSG("connect state: FIRST_REPLY_FIRST"); - FALL_THROUGH; + case HELLO_AGAIN_REPLY : + #ifdef WOLFSSL_DTLS + if (IsDtlsNotSctpMode(ssl)) { + neededState = ssl->options.resuming ? + SERVER_FINISHED_COMPLETE : SERVER_HELLODONE_COMPLETE; - case FIRST_REPLY_FIRST : - #ifdef WOLFSSL_TLS13 - if (ssl->options.tls1_3) - return wolfSSL_connect_TLSv13(ssl); - #endif - if (!ssl->options.resuming) { - if ( (ssl->error = SendClientKeyExchange(ssl)) != 0) { - wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); -#ifdef WOLFSSL_EXTRA_ALERTS - if (ssl->error == WC_NO_ERR_TRACE(NO_PEER_KEY) || - ssl->error == WC_NO_ERR_TRACE(PSK_KEY_ERROR)) { - SendAlert(ssl, alert_fatal, handshake_failure); - } -#endif + /* get response */ + while (ssl->options.serverState < neededState) { + if ((ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } - WOLFSSL_MSG("sent: client key exchange"); + /* if resumption failed, reset needed state */ + if (neededState == SERVER_FINISHED_COMPLETE) { + if (!ssl->options.resuming) { + neededState = SERVER_HELLODONE_COMPLETE; + } + } } + } + #endif - ssl->options.connectState = FIRST_REPLY_SECOND; - WOLFSSL_MSG("connect state: FIRST_REPLY_SECOND"); - FALL_THROUGH; + ssl->options.connectState = FIRST_REPLY_DONE; + WOLFSSL_MSG("connect state: FIRST_REPLY_DONE"); + FALL_THROUGH; - #if !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS) - case FIRST_REPLY_SECOND : - /* CLIENT: Fail-safe for Server Authentication. */ - if (!ssl->options.peerAuthGood) { - WOLFSSL_MSG("Server authentication did not happen"); - ssl->error = NO_PEER_VERIFY; + case FIRST_REPLY_DONE : + if (ssl->options.certOnly) { + return WOLFSSL_SUCCESS; + } + #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) + #ifdef WOLFSSL_TLS13 + if (ssl->options.tls1_3) { + return wolfSSL_connect_TLSv13(ssl); + } + #endif + if (ssl->options.sendVerify) { + if ((ssl->error = SendCertificate(ssl)) != 0) { + wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } + WOLFSSL_MSG("sent: certificate"); + } - #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) - if (ssl->options.sendVerify) { - if ( (ssl->error = SendCertificateVerify(ssl)) != 0) { - wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - WOLFSSL_MSG("sent: certificate verify"); - } - #endif /* !NO_CERTS && !WOLFSSL_NO_CLIENT_AUTH */ - ssl->options.connectState = FIRST_REPLY_THIRD; - WOLFSSL_MSG("connect state: FIRST_REPLY_THIRD"); - FALL_THROUGH; + #endif + ssl->options.connectState = FIRST_REPLY_FIRST; + WOLFSSL_MSG("connect state: FIRST_REPLY_FIRST"); + FALL_THROUGH; - case FIRST_REPLY_THIRD : - if ( (ssl->error = SendChangeCipher(ssl)) != 0) { + case FIRST_REPLY_FIRST : + #ifdef WOLFSSL_TLS13 + if (ssl->options.tls1_3) { + return wolfSSL_connect_TLSv13(ssl); + } + #endif + if (!ssl->options.resuming) { + if ((ssl->error = SendClientKeyExchange(ssl)) != 0) { wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + #ifdef WOLFSSL_EXTRA_ALERTS + if ((ssl->error == WC_NO_ERR_TRACE(NO_PEER_KEY)) || + (ssl->error == WC_NO_ERR_TRACE(PSK_KEY_ERROR))) { + SendAlert(ssl, alert_fatal, handshake_failure); + } + #endif WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } - WOLFSSL_MSG("sent: change cipher spec"); - ssl->options.connectState = FIRST_REPLY_FOURTH; - WOLFSSL_MSG("connect state: FIRST_REPLY_FOURTH"); - FALL_THROUGH; + WOLFSSL_MSG("sent: client key exchange"); + } + + ssl->options.connectState = FIRST_REPLY_SECOND; + WOLFSSL_MSG("connect state: FIRST_REPLY_SECOND"); + FALL_THROUGH; + + #if !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS) + case FIRST_REPLY_SECOND : + /* CLIENT: Fail-safe for Server Authentication. */ + if (!ssl->options.peerAuthGood) { + WOLFSSL_MSG("Server authentication did not happen"); + ssl->error = NO_PEER_VERIFY; + return WOLFSSL_FATAL_ERROR; + } - case FIRST_REPLY_FOURTH : - if ( (ssl->error = SendFinished(ssl)) != 0) { + #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) + if (ssl->options.sendVerify) { + if ((ssl->error = SendCertificateVerify(ssl)) != 0) { wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } - WOLFSSL_MSG("sent: finished"); - ssl->options.connectState = FINISHED_DONE; - WOLFSSL_MSG("connect state: FINISHED_DONE"); - FALL_THROUGH; - -#ifdef WOLFSSL_DTLS13 - case WAIT_FINISHED_ACK: - ssl->options.connectState = FINISHED_DONE; - FALL_THROUGH; -#endif /* WOLFSSL_DTLS13 */ - - case FINISHED_DONE : - /* get response */ - while (ssl->options.serverState < SERVER_FINISHED_COMPLETE) - if ( (ssl->error = ProcessReply(ssl)) < 0) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - - ssl->options.connectState = SECOND_REPLY_DONE; - WOLFSSL_MSG("connect state: SECOND_REPLY_DONE"); - FALL_THROUGH; - - case SECOND_REPLY_DONE: - #ifndef NO_HANDSHAKE_DONE_CB - if (ssl->hsDoneCb) { - int cbret = ssl->hsDoneCb(ssl, ssl->hsDoneCtx); - if (cbret < 0) { - ssl->error = cbret; - WOLFSSL_MSG("HandShake Done Cb don't continue error"); - return WOLFSSL_FATAL_ERROR; - } - } - #endif /* NO_HANDSHAKE_DONE_CB */ + WOLFSSL_MSG("sent: certificate verify"); + } + #endif /* !NO_CERTS && !WOLFSSL_NO_CLIENT_AUTH */ + ssl->options.connectState = FIRST_REPLY_THIRD; + WOLFSSL_MSG("connect state: FIRST_REPLY_THIRD"); + FALL_THROUGH; + + case FIRST_REPLY_THIRD : + if ((ssl->error = SendChangeCipher(ssl)) != 0) { + wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } + WOLFSSL_MSG("sent: change cipher spec"); + ssl->options.connectState = FIRST_REPLY_FOURTH; + WOLFSSL_MSG("connect state: FIRST_REPLY_FOURTH"); + FALL_THROUGH; + + case FIRST_REPLY_FOURTH : + if ((ssl->error = SendFinished(ssl)) != 0) { + wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } + WOLFSSL_MSG("sent: finished"); + ssl->options.connectState = FINISHED_DONE; + WOLFSSL_MSG("connect state: FINISHED_DONE"); + FALL_THROUGH; + + #ifdef WOLFSSL_DTLS13 + case WAIT_FINISHED_ACK: + ssl->options.connectState = FINISHED_DONE; + FALL_THROUGH; + #endif /* WOLFSSL_DTLS13 */ + + case FINISHED_DONE : + /* get response */ + while (ssl->options.serverState < SERVER_FINISHED_COMPLETE) { + if ((ssl->error = ProcessReply(ssl)) < 0) { + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } + } - if (!ssl->options.dtls) { - if (!ssl->options.keepResources) { - FreeHandshakeResources(ssl); - } - } - #ifdef WOLFSSL_DTLS - else { - ssl->options.dtlsHsRetain = 1; - } - #endif /* WOLFSSL_DTLS */ + ssl->options.connectState = SECOND_REPLY_DONE; + WOLFSSL_MSG("connect state: SECOND_REPLY_DONE"); + FALL_THROUGH; - #if defined(WOLFSSL_ASYNC_CRYPT) && defined(HAVE_SECURE_RENEGOTIATION) - /* This may be necessary in async so that we don't try to - * renegotiate again */ - if (ssl->secure_renegotiation && - ssl->secure_renegotiation->startScr) { - ssl->secure_renegotiation->startScr = 0; - } - #endif /* WOLFSSL_ASYNC_CRYPT && HAVE_SECURE_RENEGOTIATION */ - #if defined(WOLFSSL_ASYNC_IO) && !defined(WOLFSSL_ASYNC_CRYPT) - /* Free the remaining async context if not using it for crypto */ - FreeAsyncCtx(ssl, 1); - #endif + case SECOND_REPLY_DONE: + if (wolfssl_handshake_done(ssl) != 0) { + return WOLFSSL_FATAL_ERROR; + } - ssl->error = 0; /* clear the error */ + ssl->error = 0; /* clear the error */ - WOLFSSL_LEAVE("wolfSSL_connect", WOLFSSL_SUCCESS); - return WOLFSSL_SUCCESS; - #endif /* !WOLFSSL_NO_TLS12 || !NO_OLD_TLS */ + WOLFSSL_LEAVE("wolfSSL_connect", WOLFSSL_SUCCESS); + return WOLFSSL_SUCCESS; + #endif /* !WOLFSSL_NO_TLS12 || !NO_OLD_TLS */ - default: - WOLFSSL_MSG("Unknown connect state ERROR"); - return WOLFSSL_FATAL_ERROR; /* unknown connect state */ - } - #endif /* !WOLFSSL_NO_TLS12 || !NO_OLD_TLS || !WOLFSSL_TLS13 */ + default: + WOLFSSL_MSG("Unknown connect state ERROR"); + return WOLFSSL_FATAL_ERROR; /* unknown connect state */ } + #endif /* !WOLFSSL_NO_TLS12 || !NO_OLD_TLS || !WOLFSSL_TLS13 */ +} -/* connect enough to get peer cert chain */ +/* Perform enough of the handshake to get the peer's certificate chain. + * + * The handshake stops once the server's certificate has been processed, so no + * secure connection is established. + * + * @param [in, out] ssl SSL/TLS object. + * @return WOLFSSL_SUCCESS when the certificate chain was received. + * @return WOLFSSL_FAILURE when ssl is NULL. + * @return WOLFSSL_FATAL_ERROR when the handshake fails. + */ int wolfSSL_connect_cert(WOLFSSL* ssl) { - int ret; - - if (ssl == NULL) - return WOLFSSL_FAILURE; + int ret; - ssl->options.certOnly = 1; - ret = wolfSSL_connect(ssl); - ssl->options.certOnly = 0; + if (ssl == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + ssl->options.certOnly = 1; + ret = wolfSSL_connect(ssl); + ssl->options.certOnly = 0; + } return ret; } @@ -522,472 +658,452 @@ int wolfSSL_connect_cert(WOLFSSL* ssl) /* server only parts */ #if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) - /* Accept a connection from a client. - * - * Performs the server side of the handshake, resuming from where it last - * stopped when non-blocking. Dispatches to the TLS 1.3 or DTLS handshake - * when negotiated. - * - * @param [in, out] ssl SSL/TLS object. - * @return WOLFSSL_SUCCESS when the handshake completes. - * @return WOLFSSL_FATAL_ERROR when ssl is NULL or the handshake fails. - * Call wolfSSL_get_error() for the reason. WOLFSSL_ERROR_WANT_READ - * and WOLFSSL_ERROR_WANT_WRITE mean call again. - */ - WOLFSSL_ABI - int wolfSSL_accept(WOLFSSL* ssl) - { +/* Only called from the TLS 1.2 and earlier accept path, so it is guarded to + * match: a TLS 1.3-only build returns before reaching it. */ #if !(defined(WOLFSSL_NO_TLS12) && defined(NO_OLD_TLS) && \ - defined(WOLFSSL_TLS13)) - word16 havePSK = 0; - word16 haveAnon = 0; - word16 haveMcast = 0; -#endif - int ret = 0; - - (void)ret; + defined(WOLFSSL_TLS13)) +/* Check the server has the credentials needed to perform a handshake. + * + * A certificate and private key are required unless an anonymous or PSK cipher + * suite may be chosen, the object is multicast, a certificate setup callback + * will supply them, or the private key is held externally. + * + * Checked on every call in case wolfSSL_set_accept_state() was used after the + * object was initialized. + * + * @param [in, out] ssl SSL/TLS object. + * @return 0 when the credentials can be used. + * @return WOLFSSL_FATAL_ERROR when the certificate or private key is missing. + */ +static int wolfssl_accept_check_creds(WOLFSSL* ssl) +{ + int ret = 0; + #ifndef NO_CERTS + word16 havePSK = 0; + word16 haveAnon = 0; + word16 haveMcast = 0; - if (ssl == NULL) - return WOLFSSL_FATAL_ERROR; + #ifndef NO_PSK + havePSK = ssl->options.havePSK; + #endif - #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE) - if (ssl->options.side == WOLFSSL_NEITHER_END) { - WOLFSSL_MSG("Setting WOLFSSL_SSL to be server side"); - ssl->error = InitSSL_Side(ssl, WOLFSSL_SERVER_END); - if (ssl->error != WOLFSSL_SUCCESS) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - ssl->error = 0; /* expected to be zero here */ - } - #endif /* OPENSSL_EXTRA || WOLFSSL_EITHER_SIDE */ + #ifdef HAVE_ANON + haveAnon = ssl->options.useAnon; + #endif -#if defined(WOLFSSL_NO_TLS12) && defined(NO_OLD_TLS) && defined(WOLFSSL_TLS13) - return wolfSSL_accept_TLSv13(ssl); -#else - #ifdef WOLFSSL_TLS13 - if (ssl->options.tls1_3) - return wolfSSL_accept_TLSv13(ssl); + #ifdef WOLFSSL_MULTICAST + haveMcast = ssl->options.haveMcast; #endif - WOLFSSL_ENTER("wolfSSL_accept"); - /* make sure this wolfSSL object has arrays and rng setup. Protects - * case where the WOLFSSL object is reused via wolfSSL_clear() */ - if ((ret = ReinitSSL(ssl, ssl->ctx, 0)) != 0) { - return ret; + if ((!havePSK) && (!haveAnon) && (!haveMcast)) { + #ifdef WOLFSSL_CERT_SETUP_CB + if (ssl->ctx->certSetupCb != NULL) { + WOLFSSL_MSG("CertSetupCb set. server cert and " + "key not checked"); } - -#ifdef WOLFSSL_WOLFSENTRY_HOOKS - if ((ssl->AcceptFilter != NULL) && - ((ssl->options.acceptState == ACCEPT_BEGIN) -#ifdef HAVE_SECURE_RENEGOTIATION - || (ssl->options.acceptState == ACCEPT_BEGIN_RENEG) -#endif - )) + else + #endif { - wolfSSL_netfilter_decision_t res; - if ((ssl->AcceptFilter(ssl, ssl->AcceptFilter_arg, &res) == - WOLFSSL_SUCCESS) && - (res == WOLFSSL_NETFILTER_REJECT)) { - ssl->error = SOCKET_FILTERED_E; + if ((ssl->buffers.certificate == NULL) || + (ssl->buffers.certificate->buffer == NULL)) { + WOLFSSL_MSG("accept error: server cert required"); + ssl->error = NO_PRIVATE_KEY; WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; + ret = WOLFSSL_FATAL_ERROR; + } + else if ((ssl->buffers.key == NULL) || + (ssl->buffers.key->buffer == NULL)) { + /* allow no private key if using existing key */ + #ifdef WOLF_PRIVATE_KEY_ID + if ((ssl->devId != INVALID_DEVID) + #ifdef HAVE_PK_CALLBACKS + || (wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)) + #endif + ) { + WOLFSSL_MSG("Allowing no server private key " + "(external)"); + } + else + #endif + { + WOLFSSL_MSG("accept error: server key required"); + ssl->error = NO_PRIVATE_KEY; + WOLFSSL_ERROR(ssl->error); + ret = WOLFSSL_FATAL_ERROR; + } } } -#endif /* WOLFSSL_WOLFSENTRY_HOOKS */ + } + #else + (void)ssl; + #endif /* !NO_CERTS */ - #ifdef HAVE_ERRNO_H - errno = 0; - #endif + return ret; +} +#endif /* !(WOLFSSL_NO_TLS12 && NO_OLD_TLS && WOLFSSL_TLS13) */ - #ifndef NO_PSK - havePSK = ssl->options.havePSK; - #endif - (void)havePSK; +/* Accept a connection from a client. + * + * Performs the server side of the handshake, resuming from where it last + * stopped when non-blocking. Dispatches to the TLS 1.3 or DTLS handshake + * when negotiated. + * + * @param [in, out] ssl SSL/TLS object. + * @return WOLFSSL_SUCCESS when the handshake completes. + * @return WOLFSSL_FATAL_ERROR when ssl is NULL or the handshake fails. + * Call wolfSSL_get_error() for the reason. WOLFSSL_ERROR_WANT_READ + * and WOLFSSL_ERROR_WANT_WRITE mean call again. + * + * Unlike the rest of this file, the handshake state machine below + * returns from each step rather than using a single exit. Each step + * must stop the handshake where it failed, and several of the steps + * return from inside a receive loop, where a break would only leave + * the loop. + */ +WOLFSSL_ABI +int wolfSSL_accept(WOLFSSL* ssl) +{ + #if !(defined(WOLFSSL_NO_TLS12) && defined(NO_OLD_TLS) && \ + defined(WOLFSSL_TLS13)) + byte advanceState; + #endif + int ret = 0; - #ifdef HAVE_ANON - haveAnon = ssl->options.useAnon; - #endif - (void)haveAnon; + (void)ret; - #ifdef WOLFSSL_MULTICAST - haveMcast = ssl->options.haveMcast; - #endif - (void)haveMcast; + if (ssl == NULL) { + return WOLFSSL_FATAL_ERROR; + } - if (ssl->options.side != WOLFSSL_SERVER_END) { - ssl->error = SIDE_ERROR; + #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE) + if (ssl->options.side == WOLFSSL_NEITHER_END) { + WOLFSSL_MSG("Setting WOLFSSL_SSL to be server side"); + ssl->error = InitSSL_Side(ssl, WOLFSSL_SERVER_END); + if (ssl->error != WOLFSSL_SUCCESS) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } + ssl->error = 0; /* expected to be zero here */ + } + #endif /* OPENSSL_EXTRA || WOLFSSL_EITHER_SIDE */ - #ifndef NO_CERTS - /* in case used set_accept_state after init */ - if (!havePSK && !haveAnon && !haveMcast) { - #ifdef WOLFSSL_CERT_SETUP_CB - if (ssl->ctx->certSetupCb != NULL) { - WOLFSSL_MSG("CertSetupCb set. server cert and " - "key not checked"); - } - else - #endif - { - if (!ssl->buffers.certificate || - !ssl->buffers.certificate->buffer) { + #if defined(WOLFSSL_NO_TLS12) && defined(NO_OLD_TLS) && \ + defined(WOLFSSL_TLS13) + return wolfSSL_accept_TLSv13(ssl); + #else + #ifdef WOLFSSL_TLS13 + if (ssl->options.tls1_3) { + return wolfSSL_accept_TLSv13(ssl); + } + #endif + WOLFSSL_ENTER("wolfSSL_accept"); - WOLFSSL_MSG("accept error: server cert required"); - ssl->error = NO_PRIVATE_KEY; - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } + /* make sure this wolfSSL object has arrays and rng setup. Protects + * case where the WOLFSSL object is reused via wolfSSL_clear() */ + if ((ret = ReinitSSL(ssl, ssl->ctx, 0)) != 0) { + return ret; + } - if (!ssl->buffers.key || !ssl->buffers.key->buffer) { - /* allow no private key if using existing key */ - #ifdef WOLF_PRIVATE_KEY_ID - if (ssl->devId != INVALID_DEVID - #ifdef HAVE_PK_CALLBACKS - || wolfSSL_CTX_IsPrivatePkSet(ssl->ctx) - #endif - ) { - WOLFSSL_MSG("Allowing no server private key " - "(external)"); - } - else - #endif - { - WOLFSSL_MSG("accept error: server key required"); - ssl->error = NO_PRIVATE_KEY; - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - } - } + #ifdef WOLFSSL_WOLFSENTRY_HOOKS + if ((ssl->AcceptFilter != NULL) && + ((ssl->options.acceptState == ACCEPT_BEGIN) + #ifdef HAVE_SECURE_RENEGOTIATION + || (ssl->options.acceptState == ACCEPT_BEGIN_RENEG) + #endif + )) + { + wolfSSL_netfilter_decision_t res; + if ((ssl->AcceptFilter(ssl, ssl->AcceptFilter_arg, &res) == + WOLFSSL_SUCCESS) && + (res == WOLFSSL_NETFILTER_REJECT)) { + ssl->error = SOCKET_FILTERED_E; + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; } + } + #endif /* WOLFSSL_WOLFSENTRY_HOOKS */ + + #ifdef HAVE_ERRNO_H + errno = 0; #endif + if (ssl->options.side != WOLFSSL_SERVER_END) { + ssl->error = SIDE_ERROR; + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } + + ret = wolfssl_accept_check_creds(ssl); + if (ret != 0) { + return ret; + } + #ifdef WOLFSSL_DTLS - if (ssl->version.major == DTLS_MAJOR) { - ssl->options.dtls = 1; - ssl->options.tls = 1; - ssl->options.tls1_1 = 1; - if (!IsDtlsNotSctpMode(ssl) || IsSCR(ssl)) - ssl->options.dtlsStateful = 1; + if (ssl->version.major == DTLS_MAJOR) { + ssl->options.dtls = 1; + ssl->options.tls = 1; + ssl->options.tls1_1 = 1; + if ((!IsDtlsNotSctpMode(ssl)) || (IsSCR(ssl))) { + ssl->options.dtlsStateful = 1; } + } #endif - if (ssl->buffers.outputBuffer.length > 0 - #ifdef WOLFSSL_ASYNC_CRYPT - /* do not send buffered or advance state if last error was an - async pending operation */ - && ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E) + /* The accept states listed here are the ones reached after a message has + * been sent, so they are the ones that may be advanced. */ + advanceState = (byte)( + (ssl->options.acceptState == ACCEPT_FIRST_REPLY_DONE) || + (ssl->options.acceptState == SERVER_HELLO_SENT) || + (ssl->options.acceptState == CERT_SENT) || + (ssl->options.acceptState == CERT_STATUS_SENT) || + (ssl->options.acceptState == KEY_EXCHANGE_SENT) || + (ssl->options.acceptState == CERT_REQ_SENT) || + (ssl->options.acceptState == ACCEPT_SECOND_REPLY_DONE) || + (ssl->options.acceptState == TICKET_SENT) || + (ssl->options.acceptState == CHANGE_CIPHER_SENT)); + + ret = wolfssl_handshake_flush(ssl, advanceState, + &ssl->options.acceptState, 0); + if (ret != 0) { + return ret; + } + + switch (ssl->options.acceptState) { + + case ACCEPT_BEGIN : + #ifdef HAVE_SECURE_RENEGOTIATION + case ACCEPT_BEGIN_RENEG: #endif - ) { - ret = SendBuffered(ssl); - if (ret == 0) { - /* fragOffset is non-zero when sending fragments. On the last - * fragment, fragOffset is zero again, and the state can be - * advanced. */ - if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) { - if (ssl->options.acceptState == ACCEPT_FIRST_REPLY_DONE || - ssl->options.acceptState == SERVER_HELLO_SENT || - ssl->options.acceptState == CERT_SENT || - ssl->options.acceptState == CERT_STATUS_SENT || - ssl->options.acceptState == KEY_EXCHANGE_SENT || - ssl->options.acceptState == CERT_REQ_SENT || - ssl->options.acceptState == ACCEPT_SECOND_REPLY_DONE || - ssl->options.acceptState == TICKET_SENT || - ssl->options.acceptState == CHANGE_CIPHER_SENT) { - ssl->options.acceptState++; - WOLFSSL_MSG("accept state: Advanced from last " - "buffered fragment send"); - #ifdef WOLFSSL_ASYNC_IO - /* Cleanup async */ - FreeAsyncCtx(ssl, 0); - #endif - } - } - else { - WOLFSSL_MSG("accept state: " - "Not advanced, more fragments to send"); - } - } - else { - ssl->error = ret; + /* get response */ + while (ssl->options.clientState < CLIENT_HELLO_COMPLETE) { + if ((ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } -#ifdef WOLFSSL_DTLS13 - if (ssl->options.dtls) - ssl->dtls13SendingAckOrRtx = 0; -#endif /* WOLFSSL_DTLS13 */ } + #ifdef WOLFSSL_TLS13 + ssl->options.acceptState = ACCEPT_CLIENT_HELLO_DONE; + WOLFSSL_MSG("accept state ACCEPT_CLIENT_HELLO_DONE"); + FALL_THROUGH; - ret = RetrySendAlert(ssl); - if (ret != 0) { - ssl->error = ret; - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; + case ACCEPT_CLIENT_HELLO_DONE : + if (ssl->options.tls1_3) { + return wolfSSL_accept_TLSv13(ssl); } + #endif - switch (ssl->options.acceptState) { - - case ACCEPT_BEGIN : -#ifdef HAVE_SECURE_RENEGOTIATION - case ACCEPT_BEGIN_RENEG: -#endif - /* get response */ - while (ssl->options.clientState < CLIENT_HELLO_COMPLETE) - if ( (ssl->error = ProcessReply(ssl)) < 0) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } -#ifdef WOLFSSL_TLS13 - ssl->options.acceptState = ACCEPT_CLIENT_HELLO_DONE; - WOLFSSL_MSG("accept state ACCEPT_CLIENT_HELLO_DONE"); - FALL_THROUGH; - - case ACCEPT_CLIENT_HELLO_DONE : - if (ssl->options.tls1_3) { - return wolfSSL_accept_TLSv13(ssl); - } -#endif + ssl->options.acceptState = ACCEPT_FIRST_REPLY_DONE; + WOLFSSL_MSG("accept state ACCEPT_FIRST_REPLY_DONE"); + FALL_THROUGH; - ssl->options.acceptState = ACCEPT_FIRST_REPLY_DONE; - WOLFSSL_MSG("accept state ACCEPT_FIRST_REPLY_DONE"); - FALL_THROUGH; + case ACCEPT_FIRST_REPLY_DONE : + if (ssl->options.returnOnGoodCh) { + /* Higher level in stack wants us to return. Simulate a + * WANT_WRITE to accomplish this. */ + ssl->error = WANT_WRITE; + return WOLFSSL_FATAL_ERROR; + } + if ((ssl->error = SendServerHello(ssl)) != 0) { + wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } + ssl->options.acceptState = SERVER_HELLO_SENT; + WOLFSSL_MSG("accept state SERVER_HELLO_SENT"); + FALL_THROUGH; - case ACCEPT_FIRST_REPLY_DONE : - if (ssl->options.returnOnGoodCh) { - /* Higher level in stack wants us to return. Simulate a - * WANT_WRITE to accomplish this. */ - ssl->error = WANT_WRITE; - return WOLFSSL_FATAL_ERROR; - } - if ( (ssl->error = SendServerHello(ssl)) != 0) { + case SERVER_HELLO_SENT : + #ifdef WOLFSSL_TLS13 + if (ssl->options.tls1_3) { + return wolfSSL_accept_TLSv13(ssl); + } + #endif + #ifndef NO_CERTS + if (!ssl->options.resuming) { + if ((ssl->error = SendCertificate(ssl)) != 0) { wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } - ssl->options.acceptState = SERVER_HELLO_SENT; - WOLFSSL_MSG("accept state SERVER_HELLO_SENT"); - FALL_THROUGH; + } + #endif + ssl->options.acceptState = CERT_SENT; + WOLFSSL_MSG("accept state CERT_SENT"); + FALL_THROUGH; - case SERVER_HELLO_SENT : - #ifdef WOLFSSL_TLS13 - if (ssl->options.tls1_3) { - return wolfSSL_accept_TLSv13(ssl); + case CERT_SENT : + #ifndef NO_CERTS + if (!ssl->options.resuming) { + if ((ssl->error = SendCertificateStatus(ssl)) != 0) { + wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; } + } #endif - #ifndef NO_CERTS - if (!ssl->options.resuming) - if ( (ssl->error = SendCertificate(ssl)) != 0) { - wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - #endif - ssl->options.acceptState = CERT_SENT; - WOLFSSL_MSG("accept state CERT_SENT"); - FALL_THROUGH; - - case CERT_SENT : - #ifndef NO_CERTS - if (!ssl->options.resuming) - if ( (ssl->error = SendCertificateStatus(ssl)) != 0) { - wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - #endif - ssl->options.acceptState = CERT_STATUS_SENT; - WOLFSSL_MSG("accept state CERT_STATUS_SENT"); - FALL_THROUGH; + ssl->options.acceptState = CERT_STATUS_SENT; + WOLFSSL_MSG("accept state CERT_STATUS_SENT"); + FALL_THROUGH; - case CERT_STATUS_SENT : + case CERT_STATUS_SENT : #ifdef WOLFSSL_TLS13 - if (ssl->options.tls1_3) { - return wolfSSL_accept_TLSv13(ssl); - } + if (ssl->options.tls1_3) { + return wolfSSL_accept_TLSv13(ssl); + } #endif - if (!ssl->options.resuming) - if ( (ssl->error = SendServerKeyExchange(ssl)) != 0) { - wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - ssl->options.acceptState = KEY_EXCHANGE_SENT; - WOLFSSL_MSG("accept state KEY_EXCHANGE_SENT"); - FALL_THROUGH; - - case KEY_EXCHANGE_SENT : - #ifndef NO_CERTS - if (!ssl->options.resuming) { - if (ssl->options.verifyPeer) { - if ( (ssl->error = SendCertificateRequest(ssl)) != 0) { - wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - } - else { - /* SERVER: Peer auth good if not verifying client. */ - ssl->options.peerAuthGood = 1; - } - } - #endif - ssl->options.acceptState = CERT_REQ_SENT; - WOLFSSL_MSG("accept state CERT_REQ_SENT"); - FALL_THROUGH; + if (!ssl->options.resuming) { + if ((ssl->error = SendServerKeyExchange(ssl)) != 0) { + wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } + } + ssl->options.acceptState = KEY_EXCHANGE_SENT; + WOLFSSL_MSG("accept state KEY_EXCHANGE_SENT"); + FALL_THROUGH; - case CERT_REQ_SENT : - if (!ssl->options.resuming) - if ( (ssl->error = SendServerHelloDone(ssl)) != 0) { + case KEY_EXCHANGE_SENT : + #ifndef NO_CERTS + if (!ssl->options.resuming) { + if (ssl->options.verifyPeer) { + if ((ssl->error = SendCertificateRequest(ssl)) != 0) { wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } - ssl->options.acceptState = SERVER_HELLO_DONE; - WOLFSSL_MSG("accept state SERVER_HELLO_DONE"); - FALL_THROUGH; - - case SERVER_HELLO_DONE : - if (!ssl->options.resuming) { - while (ssl->options.clientState < CLIENT_FINISHED_COMPLETE) - if ( (ssl->error = ProcessReply(ssl)) < 0) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - } - ssl->options.acceptState = ACCEPT_SECOND_REPLY_DONE; - WOLFSSL_MSG("accept state ACCEPT_SECOND_REPLY_DONE"); - FALL_THROUGH; - - case ACCEPT_SECOND_REPLY_DONE : - #ifndef NO_CERTS - /* SERVER: When not resuming and verifying peer but no certificate - * received and not failing when not received then peer auth good. - */ - if (!ssl->options.resuming && ssl->options.verifyPeer && - !ssl->options.havePeerCert && !ssl->options.failNoCert) { - ssl->options.peerAuthGood = 1; } - #endif /* !NO_CERTS */ - #ifdef WOLFSSL_NO_CLIENT_AUTH - if (!ssl->options.resuming) { + else { + /* SERVER: Peer auth good if not verifying client. */ ssl->options.peerAuthGood = 1; } + } #endif + ssl->options.acceptState = CERT_REQ_SENT; + WOLFSSL_MSG("accept state CERT_REQ_SENT"); + FALL_THROUGH; -#ifdef HAVE_SESSION_TICKET - if (ssl->options.createTicket && !ssl->options.noTicketTls12) { - if ( (ssl->error = SendTicket(ssl)) != 0) { - wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); - WOLFSSL_MSG("Thought we need ticket but failed"); + case CERT_REQ_SENT : + if (!ssl->options.resuming) { + if ((ssl->error = SendServerHelloDone(ssl)) != 0) { + wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } + } + ssl->options.acceptState = SERVER_HELLO_DONE; + WOLFSSL_MSG("accept state SERVER_HELLO_DONE"); + FALL_THROUGH; + + case SERVER_HELLO_DONE : + if (!ssl->options.resuming) { + while (ssl->options.clientState < CLIENT_FINISHED_COMPLETE) { + if ((ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } -#endif /* HAVE_SESSION_TICKET */ - ssl->options.acceptState = TICKET_SENT; - WOLFSSL_MSG("accept state TICKET_SENT"); - FALL_THROUGH; - - case TICKET_SENT: - /* SERVER: Fail-safe for CLient Authentication. */ - if (!ssl->options.peerAuthGood) { - WOLFSSL_MSG("Client authentication did not happen"); - return WOLFSSL_FATAL_ERROR; - } + } + ssl->options.acceptState = ACCEPT_SECOND_REPLY_DONE; + WOLFSSL_MSG("accept state ACCEPT_SECOND_REPLY_DONE"); + FALL_THROUGH; - if ( (ssl->error = SendChangeCipher(ssl)) != 0) { - wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - ssl->options.acceptState = CHANGE_CIPHER_SENT; - WOLFSSL_MSG("accept state CHANGE_CIPHER_SENT"); - FALL_THROUGH; + case ACCEPT_SECOND_REPLY_DONE : + #ifndef NO_CERTS + /* SERVER: When not resuming and verifying peer but no certificate + * received and not failing when not received then peer auth good. + */ + if ((!ssl->options.resuming) && (ssl->options.verifyPeer) && + (!ssl->options.havePeerCert) && + (!ssl->options.failNoCert)) { + ssl->options.peerAuthGood = 1; + } + #endif /* !NO_CERTS */ + #ifdef WOLFSSL_NO_CLIENT_AUTH + if (!ssl->options.resuming) { + ssl->options.peerAuthGood = 1; + } + #endif - case CHANGE_CIPHER_SENT : - if ( (ssl->error = SendFinished(ssl)) != 0) { + #ifdef HAVE_SESSION_TICKET + if ((ssl->options.createTicket) && + (!ssl->options.noTicketTls12)) { + if ((ssl->error = SendTicket(ssl)) != 0) { wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + WOLFSSL_MSG("Thought we need ticket but failed"); WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } + } + #endif /* HAVE_SESSION_TICKET */ + ssl->options.acceptState = TICKET_SENT; + WOLFSSL_MSG("accept state TICKET_SENT"); + FALL_THROUGH; + + case TICKET_SENT: + /* SERVER: Fail-safe for CLient Authentication. */ + if (!ssl->options.peerAuthGood) { + WOLFSSL_MSG("Client authentication did not happen"); + return WOLFSSL_FATAL_ERROR; + } - ssl->options.acceptState = ACCEPT_FINISHED_DONE; - WOLFSSL_MSG("accept state ACCEPT_FINISHED_DONE"); - FALL_THROUGH; - - case ACCEPT_FINISHED_DONE : - if (ssl->options.resuming) { - while (ssl->options.clientState < CLIENT_FINISHED_COMPLETE) { - if ( (ssl->error = ProcessReply(ssl)) < 0) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - } - } - ssl->options.acceptState = ACCEPT_THIRD_REPLY_DONE; - WOLFSSL_MSG("accept state ACCEPT_THIRD_REPLY_DONE"); - FALL_THROUGH; + if ((ssl->error = SendChangeCipher(ssl)) != 0) { + wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } + ssl->options.acceptState = CHANGE_CIPHER_SENT; + WOLFSSL_MSG("accept state CHANGE_CIPHER_SENT"); + FALL_THROUGH; - case ACCEPT_THIRD_REPLY_DONE : -#ifndef NO_HANDSHAKE_DONE_CB - if (ssl->hsDoneCb) { - int cbret = ssl->hsDoneCb(ssl, ssl->hsDoneCtx); - if (cbret < 0) { - ssl->error = cbret; - WOLFSSL_MSG("HandShake Done Cb don't continue error"); - return WOLFSSL_FATAL_ERROR; - } - } -#endif /* NO_HANDSHAKE_DONE_CB */ + case CHANGE_CIPHER_SENT : + if ((ssl->error = SendFinished(ssl)) != 0) { + wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } - if (!ssl->options.dtls) { - if (!ssl->options.keepResources) { - FreeHandshakeResources(ssl); - } - } -#ifdef WOLFSSL_DTLS - else { - ssl->options.dtlsHsRetain = 1; - } -#endif /* WOLFSSL_DTLS */ - -#if defined(WOLFSSL_ASYNC_CRYPT) && defined(HAVE_SECURE_RENEGOTIATION) - /* This may be necessary in async so that we don't try to - * renegotiate again */ - if (ssl->secure_renegotiation && - ssl->secure_renegotiation->startScr) { - ssl->secure_renegotiation->startScr = 0; - } -#endif /* WOLFSSL_ASYNC_CRYPT && HAVE_SECURE_RENEGOTIATION */ -#if defined(WOLFSSL_ASYNC_IO) && !defined(WOLFSSL_ASYNC_CRYPT) - /* Free the remaining async context if not using it for crypto */ - FreeAsyncCtx(ssl, 1); -#endif + ssl->options.acceptState = ACCEPT_FINISHED_DONE; + WOLFSSL_MSG("accept state ACCEPT_FINISHED_DONE"); + FALL_THROUGH; -#if defined(WOLFSSL_SESSION_EXPORT) && defined(WOLFSSL_DTLS) - if (ssl->dtls_export) { - if ((ssl->error = wolfSSL_send_session(ssl)) != 0) { - WOLFSSL_MSG("Export DTLS session error"); + case ACCEPT_FINISHED_DONE : + if (ssl->options.resuming) { + while (ssl->options.clientState < CLIENT_FINISHED_COMPLETE) { + if ((ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } -#endif - ssl->error = 0; /* clear the error */ - - WOLFSSL_LEAVE("wolfSSL_accept", WOLFSSL_SUCCESS); - return WOLFSSL_SUCCESS; + } + ssl->options.acceptState = ACCEPT_THIRD_REPLY_DONE; + WOLFSSL_MSG("accept state ACCEPT_THIRD_REPLY_DONE"); + FALL_THROUGH; - default: - WOLFSSL_MSG("Unknown accept state ERROR"); + case ACCEPT_THIRD_REPLY_DONE : + if (wolfssl_handshake_done(ssl) != 0) { return WOLFSSL_FATAL_ERROR; } -#endif /* !WOLFSSL_NO_TLS12 */ + + #if defined(WOLFSSL_SESSION_EXPORT) && defined(WOLFSSL_DTLS) + if (ssl->dtls_export) { + if ((ssl->error = wolfSSL_send_session(ssl)) != 0) { + WOLFSSL_MSG("Export DTLS session error"); + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } + } + #endif + ssl->error = 0; /* clear the error */ + + WOLFSSL_LEAVE("wolfSSL_accept", WOLFSSL_SUCCESS); + return WOLFSSL_SUCCESS; + + default: + WOLFSSL_MSG("Unknown accept state ERROR"); + return WOLFSSL_FATAL_ERROR; } + #endif /* !WOLFSSL_NO_TLS12 */ +} #endif /* !NO_WOLFSSL_SERVER && !NO_TLS */ /* end server only parts */ @@ -1004,228 +1120,326 @@ int wolfSSL_connect_cert(WOLFSSL* ssl) */ int wolfSSL_SetHsDoneCb(WOLFSSL* ssl, HandShakeDoneCb cb, void* user_ctx) { - WOLFSSL_ENTER("wolfSSL_SetHsDoneCb"); + int ret = WOLFSSL_SUCCESS; - if (ssl == NULL) - return BAD_FUNC_ARG; + WOLFSSL_ENTER("wolfSSL_SetHsDoneCb"); - ssl->hsDoneCb = cb; - ssl->hsDoneCtx = user_ctx; + if (ssl == NULL) { + ret = BAD_FUNC_ARG; + } + else { + ssl->hsDoneCb = cb; + ssl->hsDoneCtx = user_ctx; + } - return WOLFSSL_SUCCESS; + return ret; } #endif /* NO_HANDSHAKE_DONE_CB */ #ifdef WOLFSSL_CALLBACKS - typedef struct itimerval Itimerval; +typedef struct itimerval Itimerval; - /* don't keep calling simple functions while setting up timer and signals - if no inlining these are the next best */ +/* don't keep calling simple functions while setting up timer and signals + if no inlining these are the next best */ - #define AddTimes(a, b, c) \ - do { \ - (c).tv_sec = (a).tv_sec + (b).tv_sec; \ - (c).tv_usec = (a).tv_usec + (b).tv_usec;\ - if ((c).tv_usec >= 1000000) { \ - (c).tv_sec++; \ - (c).tv_usec -= 1000000; \ - } \ - } while (0) +#define AddTimes(a, b, c) \ + do { \ + (c).tv_sec = (a).tv_sec + (b).tv_sec; \ + (c).tv_usec = (a).tv_usec + (b).tv_usec;\ + if ((c).tv_usec >= 1000000) { \ + (c).tv_sec++; \ + (c).tv_usec -= 1000000; \ + } \ + } while (0) - #define SubtractTimes(a, b, c) \ - do { \ - (c).tv_sec = (a).tv_sec - (b).tv_sec; \ - (c).tv_usec = (a).tv_usec - (b).tv_usec;\ - if ((c).tv_usec < 0) { \ - (c).tv_sec--; \ - (c).tv_usec += 1000000; \ - } \ - } while (0) +#define SubtractTimes(a, b, c) \ + do { \ + (c).tv_sec = (a).tv_sec - (b).tv_sec; \ + (c).tv_usec = (a).tv_usec - (b).tv_usec;\ + if ((c).tv_usec < 0) { \ + (c).tv_sec--; \ + (c).tv_usec += 1000000; \ + } \ + } while (0) - #define CmpTimes(a, b, cmp) \ - (((a).tv_sec == (b).tv_sec) ? \ - ((a).tv_usec cmp (b).tv_usec) : \ - ((a).tv_sec cmp (b).tv_sec)) \ +#define CmpTimes(a, b, cmp) \ + (((a).tv_sec == (b).tv_sec) ? \ + ((a).tv_usec cmp (b).tv_usec) : \ + ((a).tv_sec cmp (b).tv_sec)) \ - /* do nothing handler */ - static void myHandler(int signo) - { - (void)signo; - return; +/* Signal handler that does nothing. + * + * Installed for SIGALRM so that the timer interrupts a blocking call rather + * than terminating the process. + * + * @param [in] signo Signal number. Unused. + */ +static void myHandler(int signo) +{ + (void)signo; + return; +} + + +/* Replace any running timer with one that expires after the timeout. + * + * When a timer is already running and would expire first, the timeout is + * shortened to match it so the existing timer is not delayed. + * + * @param [in, out] timeout Maximum time to take. Shortened when a timer + * already running would expire sooner. + * @param [out] oldTimeout Timer that was running, to be restored later. + * @param [out] timerWasOn Set to 1 when a timer was already running. + * @param [out] oact Signal handler that was replaced. + * @return 0 on success. + * @return SETITIMER_ERROR when the timer cannot be read or set. + * @return SIGACT_ERROR when the signal handler cannot be installed. + */ +static int wolfssl_ex_wrapper_set_timer(WOLFSSL_TIMEVAL* timeout, + Itimerval* oldTimeout, int* timerWasOn, struct sigaction* oact) +{ + int ret = 0; + Itimerval myTimeout; + struct sigaction act; + + /* use setitimer to simulate getitimer, init 0 myTimeout */ + myTimeout.it_interval.tv_sec = 0; + myTimeout.it_interval.tv_usec = 0; + myTimeout.it_value.tv_sec = 0; + myTimeout.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &myTimeout, oldTimeout) < 0) { + ret = SETITIMER_ERROR; } + if (ret == 0) { + if ((oldTimeout->it_value.tv_sec) || + (oldTimeout->it_value.tv_usec)) { + *timerWasOn = 1; - /* Perform a handshake with monitoring callbacks and a timeout. - * - * An interval timer is used to abort the handshake when it takes longer - * than the timeout. Any existing timer is restored afterwards. - * - * @param [in, out] ssl SSL/TLS object. - * @param [in] hsCb Handshake information callback. May be NULL. - * @param [in] toCb Timeout callback. May be NULL. - * @param [in] timeout Maximum time to take. Zero for no timeout. - * @return WOLFSSL_SUCCESS when the handshake completes. - * @return WOLFSSL_FATAL_ERROR when ssl is NULL, the timeout value is bad, - * setting the timer fails or the handshake fails. - */ - static int wolfSSL_ex_wrapper(WOLFSSL* ssl, HandShakeCallBack hsCb, - TimeoutCallBack toCb, WOLFSSL_TIMEVAL timeout) - { - int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR); - int oldTimerOn = 0; /* was timer already on */ - WOLFSSL_TIMEVAL startTime; - WOLFSSL_TIMEVAL endTime; - WOLFSSL_TIMEVAL totalTime; - Itimerval myTimeout; - Itimerval oldTimeout; /* if old timer adjust from total time to reset */ - struct sigaction act, oact; - - #define ERR_OUT(x) { ssl->hsInfoOn = 0; ssl->toInfoOn = 0; return x; } - - if (hsCb) { - ssl->hsInfoOn = 1; - InitHandShakeInfo(&ssl->handShakeInfo, ssl); - } - if (toCb) { - ssl->toInfoOn = 1; - InitTimeoutInfo(&ssl->timeoutInfo); - - if (gettimeofday(&startTime, 0) < 0) - ERR_OUT(GETTIME_ERROR); - - /* use setitimer to simulate getitimer, init 0 myTimeout */ - myTimeout.it_interval.tv_sec = 0; - myTimeout.it_interval.tv_usec = 0; - myTimeout.it_value.tv_sec = 0; - myTimeout.it_value.tv_usec = 0; - if (setitimer(ITIMER_REAL, &myTimeout, &oldTimeout) < 0) - ERR_OUT(SETITIMER_ERROR); - - if (oldTimeout.it_value.tv_sec || oldTimeout.it_value.tv_usec) { - oldTimerOn = 1; - - /* is old timer going to expire before ours */ - if (CmpTimes(oldTimeout.it_value, timeout, <)) { - timeout.tv_sec = oldTimeout.it_value.tv_sec; - timeout.tv_usec = oldTimeout.it_value.tv_usec; - } + /* is old timer going to expire before ours */ + if (CmpTimes(oldTimeout->it_value, *timeout, <)) { + timeout->tv_sec = oldTimeout->it_value.tv_sec; + timeout->tv_usec = oldTimeout->it_value.tv_usec; } - myTimeout.it_value.tv_sec = timeout.tv_sec; - myTimeout.it_value.tv_usec = timeout.tv_usec; - - /* set up signal handler, don't restart socket send/recv */ - act.sa_handler = myHandler; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; -#ifdef SA_INTERRUPT - act.sa_flags |= SA_INTERRUPT; -#endif - if (sigaction(SIGALRM, &act, &oact) < 0) - ERR_OUT(SIGACT_ERROR); + } + myTimeout.it_value.tv_sec = timeout->tv_sec; + myTimeout.it_value.tv_usec = timeout->tv_usec; + + /* set up signal handler, don't restart socket send/recv */ + act.sa_handler = myHandler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + #ifdef SA_INTERRUPT + act.sa_flags |= SA_INTERRUPT; + #endif + if (sigaction(SIGALRM, &act, oact) < 0) { + ret = SIGACT_ERROR; + } + } - if (setitimer(ITIMER_REAL, &myTimeout, 0) < 0) - ERR_OUT(SETITIMER_ERROR); + if (ret == 0) { + if (setitimer(ITIMER_REAL, &myTimeout, 0) < 0) { + ret = SETITIMER_ERROR; } + } - /* do main work */ -#ifndef NO_WOLFSSL_CLIENT - if (ssl->options.side == WOLFSSL_CLIENT_END) - ret = wolfSSL_connect(ssl); -#endif -#ifndef NO_WOLFSSL_SERVER - if (ssl->options.side == WOLFSSL_SERVER_END) - ret = wolfSSL_accept(ssl); -#endif + return ret; +} - /* do callbacks */ - if (toCb) { - if (oldTimerOn) { - if (gettimeofday(&endTime, 0) < 0) - ERR_OUT(SYSLIB_FAILED_E); - SubtractTimes(endTime, startTime, totalTime); - /* adjust old timer for elapsed time */ - if (CmpTimes(totalTime, oldTimeout.it_value, <)) - SubtractTimes(oldTimeout.it_value, totalTime, - oldTimeout.it_value); - else { - /* reset value to interval, may be off */ - oldTimeout.it_value.tv_sec = oldTimeout.it_interval.tv_sec; - oldTimeout.it_value.tv_usec =oldTimeout.it_interval.tv_usec; - } - /* keep iter the same whether there or not */ - } - /* restore old handler */ - if (sigaction(SIGALRM, &oact, 0) < 0) - ret = SIGACT_ERROR; /* more pressing error, stomp */ - else - /* use old settings which may turn off (expired or not there) */ - if (setitimer(ITIMER_REAL, &oldTimeout, 0) < 0) - ret = SETITIMER_ERROR; - - /* if we had a timeout call callback */ - if (ssl->timeoutInfo.timeoutName[0]) { - ssl->timeoutInfo.timeoutValue.tv_sec = timeout.tv_sec; - ssl->timeoutInfo.timeoutValue.tv_usec = timeout.tv_usec; - (toCb)(&ssl->timeoutInfo); +/* Restore the timer and signal handler that were replaced. + * + * A restored timer is adjusted for the time that has since elapsed. + * + * @param [in] startTime When the handshake started. + * @param [in] endTime When the handshake finished. Only read when + * oldTimerOn is set. + * @param [in, out] oldTimeout Timer to restore. + * @param [in] oldTimerOn Whether a timer was already running. + * @param [in] oact Signal handler to restore. + * @return 0 on success. + * @return SIGACT_ERROR when the signal handler cannot be restored. + * @return SETITIMER_ERROR when the timer cannot be restored. + */ +static int wolfssl_ex_wrapper_reset_timer(const WOLFSSL_TIMEVAL* startTime, + const WOLFSSL_TIMEVAL* endTime, Itimerval* oldTimeout, int oldTimerOn, + struct sigaction* oact) +{ + int ret = 0; + WOLFSSL_TIMEVAL totalTime; + + if (oldTimerOn) { + SubtractTimes(*endTime, *startTime, totalTime); + /* adjust old timer for elapsed time */ + if (CmpTimes(totalTime, oldTimeout->it_value, <)) { + SubtractTimes(oldTimeout->it_value, totalTime, + oldTimeout->it_value); + } + else { + /* reset value to interval, may be off */ + oldTimeout->it_value.tv_sec = oldTimeout->it_interval.tv_sec; + oldTimeout->it_value.tv_usec = oldTimeout->it_interval.tv_usec; + } + /* keep iter the same whether there or not */ + } + + /* restore old handler */ + if (sigaction(SIGALRM, oact, 0) < 0) { + ret = SIGACT_ERROR; /* more pressing error, stomp */ + } + else { + /* use old settings which may turn off (expired or not there) */ + if (setitimer(ITIMER_REAL, oldTimeout, 0) < 0) { + ret = SETITIMER_ERROR; + } + } + + return ret; +} + +/* Perform a handshake with monitoring callbacks and a timeout. + * + * An interval timer is used to abort the handshake when it takes longer + * than the timeout. Any existing timer is restored afterwards. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] hsCb Handshake information callback. May be NULL. + * @param [in] toCb Timeout callback. May be NULL. + * @param [in] timeout Maximum time to take. Zero for no timeout. + * @return WOLFSSL_SUCCESS when the handshake completes. + * @return WOLFSSL_FATAL_ERROR when ssl is NULL, the timeout value is bad, + * setting the timer fails or the handshake fails. + */ +static int wolfSSL_ex_wrapper(WOLFSSL* ssl, HandShakeCallBack hsCb, + TimeoutCallBack toCb, WOLFSSL_TIMEVAL timeout) +{ + int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR); + int oldTimerOn = 0; /* was timer already on */ + WOLFSSL_TIMEVAL startTime; + /* Only filled in, and only read, when a timer was already running. Zeroed + * so the helper is never handed uninitialized storage. */ + WOLFSSL_TIMEVAL endTime; + Itimerval oldTimeout; /* if old timer adjust from total time to reset */ + struct sigaction oact; + +#define ERR_OUT(x) { ssl->hsInfoOn = 0; ssl->toInfoOn = 0; return x; } + + XMEMSET(&endTime, 0, sizeof(endTime)); + + if (hsCb) { + ssl->hsInfoOn = 1; + InitHandShakeInfo(&ssl->handShakeInfo, ssl); + } + if (toCb) { + /* Kept out of ret so the fatal default survives to the dispatch + * below, which leaves ret alone when no side has been established. */ + int sret; + + ssl->toInfoOn = 1; + InitTimeoutInfo(&ssl->timeoutInfo); + + if (gettimeofday(&startTime, 0) < 0) { + ERR_OUT(GETTIME_ERROR); + } + + sret = wolfssl_ex_wrapper_set_timer(&timeout, &oldTimeout, + &oldTimerOn, &oact); + if (sret != 0) { + ERR_OUT(sret); + } + } + + /* do main work */ + #ifndef NO_WOLFSSL_CLIENT + if (ssl->options.side == WOLFSSL_CLIENT_END) { + ret = wolfSSL_connect(ssl); + } + #endif + #ifndef NO_WOLFSSL_SERVER + if (ssl->options.side == WOLFSSL_SERVER_END) { + ret = wolfSSL_accept(ssl); + } + #endif + + /* do callbacks */ + if (toCb) { + int tret; + + if (oldTimerOn) { + if (gettimeofday(&endTime, 0) < 0) { + ERR_OUT(SYSLIB_FAILED_E); } - ssl->toInfoOn = 0; } - /* clean up buffers allocated by AddPacketInfo */ - FreeTimeoutInfo(&ssl->timeoutInfo, ssl->heap); + tret = wolfssl_ex_wrapper_reset_timer(&startTime, &endTime, + &oldTimeout, oldTimerOn, &oact); + if (tret != 0) { + ret = tret; /* more pressing error, stomp */ + } - if (hsCb) { - FinishHandShakeInfo(&ssl->handShakeInfo); - (hsCb)(&ssl->handShakeInfo); - ssl->hsInfoOn = 0; + /* if we had a timeout call callback */ + if (ssl->timeoutInfo.timeoutName[0]) { + ssl->timeoutInfo.timeoutValue.tv_sec = timeout.tv_sec; + ssl->timeoutInfo.timeoutValue.tv_usec = timeout.tv_usec; + (toCb)(&ssl->timeoutInfo); } - return ret; + ssl->toInfoOn = 0; + } + + /* clean up buffers allocated by AddPacketInfo */ + FreeTimeoutInfo(&ssl->timeoutInfo, ssl->heap); + + if (hsCb) { + FinishHandShakeInfo(&ssl->handShakeInfo); + (hsCb)(&ssl->handShakeInfo); + ssl->hsInfoOn = 0; } + return ret; +} #ifndef NO_WOLFSSL_CLIENT - /* Connect to a server with monitoring callbacks and a timeout. - * - * @param [in, out] ssl SSL/TLS object. - * @param [in] hsCb Handshake information callback. May be NULL. - * @param [in] toCb Timeout callback. May be NULL. - * @param [in] timeout Maximum time to take. Zero for no timeout. - * @return WOLFSSL_SUCCESS when the handshake completes. - * @return WOLFSSL_FATAL_ERROR when the handshake fails or times out. - */ - int wolfSSL_connect_ex(WOLFSSL* ssl, HandShakeCallBack hsCb, - TimeoutCallBack toCb, WOLFSSL_TIMEVAL timeout) - { - WOLFSSL_ENTER("wolfSSL_connect_ex"); - return wolfSSL_ex_wrapper(ssl, hsCb, toCb, timeout); - } +/* Connect to a server with monitoring callbacks and a timeout. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] hsCb Handshake information callback. May be NULL. + * @param [in] toCb Timeout callback. May be NULL. + * @param [in] timeout Maximum time to take. Zero for no timeout. + * @return WOLFSSL_SUCCESS when the handshake completes. + * @return WOLFSSL_FATAL_ERROR when the handshake fails or times out. + */ +int wolfSSL_connect_ex(WOLFSSL* ssl, HandShakeCallBack hsCb, + TimeoutCallBack toCb, WOLFSSL_TIMEVAL timeout) +{ + WOLFSSL_ENTER("wolfSSL_connect_ex"); + return wolfSSL_ex_wrapper(ssl, hsCb, toCb, timeout); +} #endif #ifndef NO_WOLFSSL_SERVER - /* Accept a connection from a client with monitoring callbacks and a - * timeout. - * - * @param [in, out] ssl SSL/TLS object. - * @param [in] hsCb Handshake information callback. May be NULL. - * @param [in] toCb Timeout callback. May be NULL. - * @param [in] timeout Maximum time to take. Zero for no timeout. - * @return WOLFSSL_SUCCESS when the handshake completes. - * @return WOLFSSL_FATAL_ERROR when the handshake fails or times out. - */ - int wolfSSL_accept_ex(WOLFSSL* ssl, HandShakeCallBack hsCb, - TimeoutCallBack toCb, WOLFSSL_TIMEVAL timeout) - { - WOLFSSL_ENTER("wolfSSL_accept_ex"); - return wolfSSL_ex_wrapper(ssl, hsCb, toCb, timeout); - } +/* Accept a connection from a client with monitoring callbacks and a + * timeout. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] hsCb Handshake information callback. May be NULL. + * @param [in] toCb Timeout callback. May be NULL. + * @param [in] timeout Maximum time to take. Zero for no timeout. + * @return WOLFSSL_SUCCESS when the handshake completes. + * @return WOLFSSL_FATAL_ERROR when the handshake fails or times out. + */ +int wolfSSL_accept_ex(WOLFSSL* ssl, HandShakeCallBack hsCb, + TimeoutCallBack toCb, WOLFSSL_TIMEVAL timeout) +{ + WOLFSSL_ENTER("wolfSSL_accept_ex"); + return wolfSSL_ex_wrapper(ssl, hsCb, toCb, timeout); +} #endif @@ -1235,84 +1449,116 @@ int wolfSSL_SetHsDoneCb(WOLFSSL* ssl, HandShakeDoneCb cb, void* user_ctx) #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EXTRA) || \ defined(WOLFSSL_WPAS_SMALL) - /* Set the SSL/TLS object to be a server. - * - * Resets the handshake state and cipher suites. Must be called before the - * handshake starts. - * - * @param [in, out] ssl SSL/TLS object. - */ - void wolfSSL_set_accept_state(WOLFSSL* ssl) - { - WOLFSSL_ENTER("wolfSSL_set_accept_state"); +/* Set the SSL/TLS object to be a server. + * + * Resets the handshake state and cipher suites. Must be called before the + * handshake starts. + * + * @param [in, out] ssl SSL/TLS object. + */ +void wolfSSL_set_accept_state(WOLFSSL* ssl) +{ + WOLFSSL_ENTER("wolfSSL_set_accept_state"); - if (ssl == NULL) - return; + if (ssl == NULL) { + return; + } - if (ssl->options.side == WOLFSSL_CLIENT_END) { - #ifdef HAVE_ECC - WC_DECLARE_VAR(key, ecc_key, 1, 0); - word32 idx = 0; + if (ssl->options.side == WOLFSSL_CLIENT_END) { + #ifdef HAVE_ECC + WC_DECLARE_VAR(key, ecc_key, 1, 0); + word32 idx = 0; #ifdef WOLFSSL_SMALL_STACK - key = (ecc_key*)XMALLOC(sizeof(ecc_key), ssl->heap, - DYNAMIC_TYPE_ECC); - if (key == NULL) { - WOLFSSL_MSG("Error allocating memory for ecc_key"); - } + key = (ecc_key*)XMALLOC(sizeof(ecc_key), ssl->heap, + DYNAMIC_TYPE_ECC); + if (key == NULL) { + WOLFSSL_MSG("Error allocating memory for ecc_key"); + } #endif - if (ssl->options.haveStaticECC && ssl->buffers.key != NULL) { - if (wc_ecc_init(key) >= 0) { - if (wc_EccPrivateKeyDecode(ssl->buffers.key->buffer, &idx, - key, ssl->buffers.key->length) != 0) { - ssl->options.haveECDSAsig = 0; - ssl->options.haveECC = 0; - ssl->options.haveStaticECC = 0; - } - wc_ecc_free(key); + if ((ssl->options.haveStaticECC) && (ssl->buffers.key != NULL)) { + if (wc_ecc_init(key) >= 0) { + DerBuffer* privKey; + + #ifdef WOLFSSL_BLIND_PRIVATE_KEY + /* The stored key is masked, so work on a plain copy. */ + privKey = wolfssl_priv_der_unblind(ssl->buffers.key, + ssl->buffers.keyMask); + #else + privKey = ssl->buffers.key; + #endif + + if (privKey == NULL) { + /* Without a plain copy there is nothing to decode, and an + * allocation failure says nothing about the key. Leave the + * capabilities as they are rather than withdraw them, + * which is also what a failure to allocate the ecc_key + * above does - that skips the check entirely. */ + WOLFSSL_MSG("Unable to unmask private key"); + } + /* Not an EC key, so withdraw the ECC capabilities. */ + else if (wc_EccPrivateKeyDecode(privKey->buffer, &idx, key, + privKey->length) != 0) { + ssl->options.haveECDSAsig = 0; + ssl->options.haveECC = 0; + ssl->options.haveStaticECC = 0; } - } - WC_FREE_VAR_EX(key, ssl->heap, DYNAMIC_TYPE_ECC); - #endif - #ifndef NO_DH - if (!ssl->options.haveDH && ssl->ctx->haveDH) { - ssl->buffers.serverDH_P = ssl->ctx->serverDH_P; - ssl->buffers.serverDH_G = ssl->ctx->serverDH_G; - ssl->options.haveDH = 1; + #ifdef WOLFSSL_BLIND_PRIVATE_KEY + wolfssl_priv_der_unblind_free(privKey); + #endif + wc_ecc_free(key); } - #endif } + WC_FREE_VAR_EX(key, ssl->heap, DYNAMIC_TYPE_ECC); + #endif - if (InitSSL_Side(ssl, WOLFSSL_SERVER_END) != WOLFSSL_SUCCESS) { - WOLFSSL_MSG("Error initializing server side"); + #ifndef NO_DH + if ((!ssl->options.haveDH) && (ssl->ctx->haveDH)) { + ssl->buffers.serverDH_P = ssl->ctx->serverDH_P; + ssl->buffers.serverDH_G = ssl->ctx->serverDH_G; + ssl->options.haveDH = 1; } + #endif + } + + if (InitSSL_Side(ssl, WOLFSSL_SERVER_END) != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("Error initializing server side"); } +} #endif /* OPENSSL_EXTRA || WOLFSSL_EXTRA || WOLFSSL_WPAS_SMALL */ - /* return true if connection established */ - /* this works for TLS and DTLS */ - int wolfSSL_is_init_finished(const WOLFSSL* ssl) - { - if (ssl == NULL) - return 0; +/* Determine whether the handshake has completed. + * + * Works for both TLS and DTLS. + * + * @param [in] ssl SSL/TLS object. + * @return 1 when the handshake has completed. + * @return 0 when the handshake has not completed or ssl is NULL. + */ +int wolfSSL_is_init_finished(const WOLFSSL* ssl) +{ + int ret = 0; -#if defined(WOLFSSL_DTLS13) && !defined(NO_WOLFSSL_CLIENT) - if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.dtls - && IsAtLeastTLSv1_3(ssl->version)) { - return ssl->options.serverState == SERVER_FINISHED_ACKED; + if (ssl != NULL) { + #if defined(WOLFSSL_DTLS13) && !defined(NO_WOLFSSL_CLIENT) + if ((ssl->options.side == WOLFSSL_CLIENT_END) && (ssl->options.dtls) + && (IsAtLeastTLSv1_3(ssl->version))) { + ret = (ssl->options.serverState == SERVER_FINISHED_ACKED); } -#endif /* WOLFSSL_DTLS13 && !NO_WOLFSSL_CLIENT */ - + else + #endif /* WOLFSSL_DTLS13 && !NO_WOLFSSL_CLIENT */ /* Can't use ssl->options.connectState and ssl->options.acceptState * because they differ in meaning for TLS <=1.2 and 1.3 */ - if (ssl->options.handShakeState == HANDSHAKE_DONE) - return 1; - - return 0; + if (ssl->options.handShakeState == HANDSHAKE_DONE) { + ret = 1; + } } + return ret; +} + #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) /* Set the SSL/TLS object to be a client. * @@ -1331,12 +1577,14 @@ void wolfSSL_set_connect_state(WOLFSSL* ssl) #ifndef NO_DH /* client creates its own DH parameters on handshake */ - if (ssl->buffers.serverDH_P.buffer && ssl->buffers.weOwnDH) { + if ((ssl->buffers.serverDH_P.buffer != NULL) && + (ssl->buffers.weOwnDH)) { XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); } ssl->buffers.serverDH_P.buffer = NULL; - if (ssl->buffers.serverDH_G.buffer && ssl->buffers.weOwnDH) { + if ((ssl->buffers.serverDH_G.buffer != NULL) && + (ssl->buffers.weOwnDH)) { XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); } @@ -1379,43 +1627,354 @@ void wolfSSL_set_connect_state(WOLFSSL* ssl) "DTLSv1_3 " s}, \ } -#define STATE_STRINGS_PROTO_RW(s) \ - { \ - {"SSLv3 read " s, \ - "SSLv3 write " s, \ - "SSLv3 " s}, \ - {"TLSv1 read " s, \ - "TLSv1 write " s, \ - "TLSv1 " s}, \ - {"TLSv1_1 read " s, \ - "TLSv1_1 write " s, \ - "TLSv1_1 " s}, \ - {"TLSv1_2 read " s, \ - "TLSv1_2 write " s, \ - "TLSv1_2 " s}, \ - {"TLSv1_3 read " s, \ - "TLSv1_3 write " s, \ - "TLSv1_3 " s}, \ - {"DTLSv1 read " s, \ - "DTLSv1 write " s, \ - "DTLSv1 " s}, \ - {"DTLSv1_2 read " s, \ - "DTLSv1_2 write " s, \ - "DTLSv1_2 " s}, \ - {"DTLSv1_3 read " s, \ - "DTLSv1_3 write " s, \ - "DTLSv1_3 " s}, \ +#define STATE_STRINGS_PROTO_RW(s) \ + { \ + {"SSLv3 read " s, \ + "SSLv3 write " s, \ + "SSLv3 " s}, \ + {"TLSv1 read " s, \ + "TLSv1 write " s, \ + "TLSv1 " s}, \ + {"TLSv1_1 read " s, \ + "TLSv1_1 write " s, \ + "TLSv1_1 " s}, \ + {"TLSv1_2 read " s, \ + "TLSv1_2 write " s, \ + "TLSv1_2 " s}, \ + {"TLSv1_3 read " s, \ + "TLSv1_3 write " s, \ + "TLSv1_3 " s}, \ + {"DTLSv1 read " s, \ + "DTLSv1 write " s, \ + "DTLSv1 " s}, \ + {"DTLSv1_2 read " s, \ + "DTLSv1_2 write " s, \ + "DTLSv1_2 " s}, \ + {"DTLSv1_3 read " s, \ + "DTLSv1_3 write " s, \ + "DTLSv1_3 " s}, \ + } + +/* Indices into OUTPUT_STR in wolfSSL_state_string_long(). + * + * These are shared by that function and its helpers below, so they cannot be + * local to any one of them. This file is compiled as part of ssl.c, which + * makes them visible to every other file included into it, hence the + * WOLFSSL_SS_ ("state string") prefix on names that would otherwise be far + * too generic to sit in that namespace. + */ +enum WolfsslSsProtocol { + WOLFSSL_SS_SSL_V3 = 0, + WOLFSSL_SS_TLS_V1, + WOLFSSL_SS_TLS_V1_1, + WOLFSSL_SS_TLS_V1_2, + WOLFSSL_SS_TLS_V1_3, + WOLFSSL_SS_DTLS_V1, + WOLFSSL_SS_DTLS_V1_2, + WOLFSSL_SS_DTLS_V1_3, + WOLFSSL_SS_UNKNOWN = 100 +}; + +enum WolfsslSsIoMode { + WOLFSSL_SS_READ = 0, + WOLFSSL_SS_WRITE, + WOLFSSL_SS_NEITHER +}; + +enum WolfsslSsState { + WOLFSSL_SS_NULL_STATE = 0, + WOLFSSL_SS_SERVER_HELLOREQUEST, + WOLFSSL_SS_SERVER_HELLOVERIFY, + WOLFSSL_SS_SERVER_HELLORETRYREQUEST, + WOLFSSL_SS_SERVER_HELLO, + WOLFSSL_SS_SERVER_CERTIFICATESTATUS, + WOLFSSL_SS_SERVER_ENCRYPTEDEXTENSIONS, + WOLFSSL_SS_SERVER_SESSIONTICKET, + WOLFSSL_SS_SERVER_CERTREQUEST, + WOLFSSL_SS_SERVER_CERT, + WOLFSSL_SS_SERVER_KEYEXCHANGE, + WOLFSSL_SS_SERVER_HELLODONE, + WOLFSSL_SS_SERVER_CHANGECIPHERSPEC, + WOLFSSL_SS_SERVER_FINISHED, + WOLFSSL_SS_SERVER_KEYUPDATE, + WOLFSSL_SS_CLIENT_HELLO, + WOLFSSL_SS_CLIENT_KEYEXCHANGE, + WOLFSSL_SS_CLIENT_CERT, + WOLFSSL_SS_CLIENT_CHANGECIPHERSPEC, + WOLFSSL_SS_CLIENT_CERTVERIFY, + WOLFSSL_SS_CLIENT_ENDOFEARLYDATA, + WOLFSSL_SS_CLIENT_FINISHED, + WOLFSSL_SS_CLIENT_KEYUPDATE, + WOLFSSL_SS_HANDSHAKE_DONE +}; + +/* Determine which direction the last handshake message travelled. + * + * @param [in] ssl SSL/TLS object. + * @return WOLFSSL_SS_READ when the last message was read. + * @return WOLFSSL_SS_WRITE when the last message was written. + * @return WOLFSSL_SS_NEITHER when no message has been read or written. + */ +static int wolfssl_state_string_io_mode(const WOLFSSL* ssl) +{ + int cbmode = WOLFSSL_SS_NEITHER; + + if (ssl->cbmode == WOLFSSL_CB_MODE_WRITE) { + cbmode = WOLFSSL_SS_WRITE; + } + else if (ssl->cbmode == WOLFSSL_CB_MODE_READ) { + cbmode = WOLFSSL_SS_READ; + } + else { + cbmode = WOLFSSL_SS_NEITHER; + } + + return cbmode; +} + +/* Determine the protocol version in use. + * + * @param [in] ssl SSL/TLS object. + * @return Index of the protocol version in the state string table. + * @return WOLFSSL_SS_UNKNOWN when the version is not recognized. + */ +static int wolfssl_state_string_protocol(const WOLFSSL* ssl) +{ + int protocol = WOLFSSL_SS_UNKNOWN; + + switch (ssl->version.major) { + case SSLv3_MAJOR: + switch (ssl->version.minor) { + case SSLv3_MINOR: + protocol = WOLFSSL_SS_SSL_V3; + break; + case TLSv1_MINOR: + protocol = WOLFSSL_SS_TLS_V1; + break; + case TLSv1_1_MINOR: + protocol = WOLFSSL_SS_TLS_V1_1; + break; + case TLSv1_2_MINOR: + protocol = WOLFSSL_SS_TLS_V1_2; + break; + case TLSv1_3_MINOR: + protocol = WOLFSSL_SS_TLS_V1_3; + break; + default: + protocol = WOLFSSL_SS_UNKNOWN; + } + break; + case DTLS_MAJOR: + switch (ssl->version.minor) { + case DTLS_MINOR: + protocol = WOLFSSL_SS_DTLS_V1; + break; + case DTLSv1_2_MINOR: + protocol = WOLFSSL_SS_DTLS_V1_2; + break; + case DTLSv1_3_MINOR: + protocol = WOLFSSL_SS_DTLS_V1_3; + break; + default: + protocol = WOLFSSL_SS_UNKNOWN; + } + break; + default: + protocol = WOLFSSL_SS_UNKNOWN; + } + + return protocol; +} + +/* Map the type of the last message read to a state string table index. + * + * @param [in] ssl SSL/TLS object. + * @return Index of the message in the state string table. + * @return WOLFSSL_SS_NULL_STATE when the message type is not recognized. + */ +static int wolfssl_state_string_recv_state(const WOLFSSL* ssl) +{ + int state = ssl->cbtype; + + switch (state) { + case hello_request: + state = WOLFSSL_SS_SERVER_HELLOREQUEST; + break; + case client_hello: + state = WOLFSSL_SS_CLIENT_HELLO; + break; + case server_hello: + state = WOLFSSL_SS_SERVER_HELLO; + break; + case hello_verify_request: + state = WOLFSSL_SS_SERVER_HELLOVERIFY; + break; + case session_ticket: + state = WOLFSSL_SS_SERVER_SESSIONTICKET; + break; + case end_of_early_data: + state = WOLFSSL_SS_CLIENT_ENDOFEARLYDATA; + break; + case hello_retry_request: + state = WOLFSSL_SS_SERVER_HELLORETRYREQUEST; + break; + case encrypted_extensions: + state = WOLFSSL_SS_SERVER_ENCRYPTEDEXTENSIONS; + break; + case certificate: + if (ssl->options.side == WOLFSSL_SERVER_END) { + state = WOLFSSL_SS_CLIENT_CERT; + } + else if (ssl->options.side == WOLFSSL_CLIENT_END) { + state = WOLFSSL_SS_SERVER_CERT; + } + else { + WOLFSSL_MSG("Unknown State"); + state = WOLFSSL_SS_NULL_STATE; + } + break; + case server_key_exchange: + state = WOLFSSL_SS_SERVER_KEYEXCHANGE; + break; + case certificate_request: + state = WOLFSSL_SS_SERVER_CERTREQUEST; + break; + case server_hello_done: + state = WOLFSSL_SS_SERVER_HELLODONE; + break; + case certificate_verify: + state = WOLFSSL_SS_CLIENT_CERTVERIFY; + break; + case client_key_exchange: + state = WOLFSSL_SS_CLIENT_KEYEXCHANGE; + break; + case finished: + if (ssl->options.side == WOLFSSL_SERVER_END) { + state = WOLFSSL_SS_CLIENT_FINISHED; + } + else if (ssl->options.side == WOLFSSL_CLIENT_END) { + state = WOLFSSL_SS_SERVER_FINISHED; + } + else { + WOLFSSL_MSG("Unknown State"); + state = WOLFSSL_SS_NULL_STATE; + } + break; + case certificate_status: + state = WOLFSSL_SS_SERVER_CERTIFICATESTATUS; + break; + case key_update: + if (ssl->options.side == WOLFSSL_SERVER_END) { + state = WOLFSSL_SS_CLIENT_KEYUPDATE; + } + else if (ssl->options.side == WOLFSSL_CLIENT_END) { + state = WOLFSSL_SS_SERVER_KEYUPDATE; + } + else { + WOLFSSL_MSG("Unknown State"); + state = WOLFSSL_SS_NULL_STATE; + } + break; + case change_cipher_hs: + if (ssl->options.side == WOLFSSL_SERVER_END) { + state = WOLFSSL_SS_CLIENT_CHANGECIPHERSPEC; + } + else if (ssl->options.side == WOLFSSL_CLIENT_END) { + state = WOLFSSL_SS_SERVER_CHANGECIPHERSPEC; + } + else { + WOLFSSL_MSG("Unknown State"); + state = WOLFSSL_SS_NULL_STATE; + } + break; + default: + WOLFSSL_MSG("Unknown State"); + state = WOLFSSL_SS_NULL_STATE; + } + + return state; +} + +/* Map the handshake state reached while sending to a state string table index. + * + * @param [in] ssl SSL/TLS object. + * @return Index of the message in the state string table. + * @return WOLFSSL_SS_NULL_STATE when the state is not recognized. + */ +static int wolfssl_state_string_send_state(const WOLFSSL* ssl) +{ + int state; + + if (ssl->options.side == WOLFSSL_SERVER_END) { + state = ssl->options.serverState; + } + else { + state = ssl->options.clientState; + } + + switch (state) { + case SERVER_HELLOVERIFYREQUEST_COMPLETE: + state = WOLFSSL_SS_SERVER_HELLOVERIFY; + break; + case SERVER_HELLO_RETRY_REQUEST_COMPLETE: + state = WOLFSSL_SS_SERVER_HELLORETRYREQUEST; + break; + case SERVER_HELLO_COMPLETE: + state = WOLFSSL_SS_SERVER_HELLO; + break; + case SERVER_ENCRYPTED_EXTENSIONS_COMPLETE: + state = WOLFSSL_SS_SERVER_ENCRYPTEDEXTENSIONS; + break; + case SERVER_CERT_COMPLETE: + state = WOLFSSL_SS_SERVER_CERT; + break; + case SERVER_KEYEXCHANGE_COMPLETE: + state = WOLFSSL_SS_SERVER_KEYEXCHANGE; + break; + case SERVER_HELLODONE_COMPLETE: + state = WOLFSSL_SS_SERVER_HELLODONE; + break; + case SERVER_CHANGECIPHERSPEC_COMPLETE: + state = WOLFSSL_SS_SERVER_CHANGECIPHERSPEC; + break; + case SERVER_FINISHED_COMPLETE: + state = WOLFSSL_SS_SERVER_FINISHED; + break; + case CLIENT_HELLO_RETRY: + case CLIENT_HELLO_COMPLETE: + state = WOLFSSL_SS_CLIENT_HELLO; + break; + case CLIENT_KEYEXCHANGE_COMPLETE: + state = WOLFSSL_SS_CLIENT_KEYEXCHANGE; + break; + case CLIENT_CHANGECIPHERSPEC_COMPLETE: + state = WOLFSSL_SS_CLIENT_CHANGECIPHERSPEC; + break; + case CLIENT_FINISHED_COMPLETE: + state = WOLFSSL_SS_CLIENT_FINISHED; + break; + case HANDSHAKE_DONE: + state = WOLFSSL_SS_HANDSHAKE_DONE; + break; + default: + WOLFSSL_MSG("Unknown State"); + state = WOLFSSL_SS_NULL_STATE; } -/* Gets the current state of the WOLFSSL structure + return state; +} + +/* Get a human readable description of the current handshake state. * - * ssl WOLFSSL structure to get state of + * The description names the protocol version, whether the last message was + * read or written, and the message itself. * - * Returns a human readable string of the WOLFSSL structure state + * @param [in] ssl SSL/TLS object. + * @return A human readable string describing the state. + * @return NULL when ssl is NULL or the state cannot be described. */ const char* wolfSSL_state_string_long(const WOLFSSL* ssl) { - static const char* OUTPUT_STR[24][8][3] = { STATE_STRINGS_PROTO("Initialization"), STATE_STRINGS_PROTO_RW("Server Hello Request"), @@ -1442,271 +2001,43 @@ const char* wolfSSL_state_string_long(const WOLFSSL* ssl) STATE_STRINGS_PROTO_RW("Client Key Update"), STATE_STRINGS_PROTO("Handshake Done"), }; - enum ProtocolVer { - SSL_V3 = 0, - TLS_V1, - TLS_V1_1, - TLS_V1_2, - TLS_V1_3, - DTLS_V1, - DTLS_V1_2, - DTLS_V1_3, - UNKNOWN = 100 - }; - - enum IOMode { - SS_READ = 0, - SS_WRITE, - SS_NEITHER - }; - - enum SslState { - ss_null_state = 0, - ss_server_hellorequest, - ss_server_helloverify, - ss_server_helloretryrequest, - ss_server_hello, - ss_server_certificatestatus, - ss_server_encryptedextensions, - ss_server_sessionticket, - ss_server_certrequest, - ss_server_cert, - ss_server_keyexchange, - ss_server_hellodone, - ss_server_changecipherspec, - ss_server_finished, - ss_server_keyupdate, - ss_client_hello, - ss_client_keyexchange, - ss_client_cert, - ss_client_changecipherspec, - ss_client_certverify, - ss_client_endofearlydata, - ss_client_finished, - ss_client_keyupdate, - ss_handshake_done - }; - - int protocol = 0; - int cbmode = 0; - int state = 0; + int protocol; + int cbmode; + int state; + const char* ret = NULL; WOLFSSL_ENTER("wolfSSL_state_string_long"); + if (ssl == NULL) { WOLFSSL_MSG("Null argument passed in"); - return NULL; - } - - /* Get state of callback */ - if (ssl->cbmode == WOLFSSL_CB_MODE_WRITE) { - cbmode = SS_WRITE; - } - else if (ssl->cbmode == WOLFSSL_CB_MODE_READ) { - cbmode = SS_READ; } else { - cbmode = SS_NEITHER; - } + cbmode = wolfssl_state_string_io_mode(ssl); + protocol = wolfssl_state_string_protocol(ssl); - /* Get protocol version */ - switch (ssl->version.major) { - case SSLv3_MAJOR: - switch (ssl->version.minor) { - case SSLv3_MINOR: - protocol = SSL_V3; - break; - case TLSv1_MINOR: - protocol = TLS_V1; - break; - case TLSv1_1_MINOR: - protocol = TLS_V1_1; - break; - case TLSv1_2_MINOR: - protocol = TLS_V1_2; - break; - case TLSv1_3_MINOR: - protocol = TLS_V1_3; - break; - default: - protocol = UNKNOWN; - } - break; - case DTLS_MAJOR: - switch (ssl->version.minor) { - case DTLS_MINOR: - protocol = DTLS_V1; - break; - case DTLSv1_2_MINOR: - protocol = DTLS_V1_2; - break; - case DTLSv1_3_MINOR: - protocol = DTLS_V1_3; - break; - default: - protocol = UNKNOWN; - } - break; - default: - protocol = UNKNOWN; - } - - /* accept process */ - if (ssl->cbmode == WOLFSSL_CB_MODE_READ) { - state = ssl->cbtype; - switch (state) { - case hello_request: - state = ss_server_hellorequest; - break; - case client_hello: - state = ss_client_hello; - break; - case server_hello: - state = ss_server_hello; - break; - case hello_verify_request: - state = ss_server_helloverify; - break; - case session_ticket: - state = ss_server_sessionticket; - break; - case end_of_early_data: - state = ss_client_endofearlydata; - break; - case hello_retry_request: - state = ss_server_helloretryrequest; - break; - case encrypted_extensions: - state = ss_server_encryptedextensions; - break; - case certificate: - if (ssl->options.side == WOLFSSL_SERVER_END) - state = ss_client_cert; - else if (ssl->options.side == WOLFSSL_CLIENT_END) - state = ss_server_cert; - else { - WOLFSSL_MSG("Unknown State"); - state = ss_null_state; - } - break; - case server_key_exchange: - state = ss_server_keyexchange; - break; - case certificate_request: - state = ss_server_certrequest; - break; - case server_hello_done: - state = ss_server_hellodone; - break; - case certificate_verify: - state = ss_client_certverify; - break; - case client_key_exchange: - state = ss_client_keyexchange; - break; - case finished: - if (ssl->options.side == WOLFSSL_SERVER_END) - state = ss_client_finished; - else if (ssl->options.side == WOLFSSL_CLIENT_END) - state = ss_server_finished; - else { - WOLFSSL_MSG("Unknown State"); - state = ss_null_state; - } - break; - case certificate_status: - state = ss_server_certificatestatus; - break; - case key_update: - if (ssl->options.side == WOLFSSL_SERVER_END) - state = ss_client_keyupdate; - else if (ssl->options.side == WOLFSSL_CLIENT_END) - state = ss_server_keyupdate; - else { - WOLFSSL_MSG("Unknown State"); - state = ss_null_state; - } - break; - case change_cipher_hs: - if (ssl->options.side == WOLFSSL_SERVER_END) - state = ss_client_changecipherspec; - else if (ssl->options.side == WOLFSSL_CLIENT_END) - state = ss_server_changecipherspec; - else { - WOLFSSL_MSG("Unknown State"); - state = ss_null_state; - } - break; - default: - WOLFSSL_MSG("Unknown State"); - state = ss_null_state; + if (ssl->cbmode == WOLFSSL_CB_MODE_READ) { + state = wolfssl_state_string_recv_state(ssl); + } + else { + state = wolfssl_state_string_send_state(ssl); + } + + if (protocol == WOLFSSL_SS_UNKNOWN) { + WOLFSSL_MSG("Unknown protocol"); + ret = ""; + } + else { + ret = OUTPUT_STR[state][protocol][cbmode]; } } - else { - /* Send process */ - if (ssl->options.side == WOLFSSL_SERVER_END) - state = ssl->options.serverState; - else - state = ssl->options.clientState; - - switch (state) { - case SERVER_HELLOVERIFYREQUEST_COMPLETE: - state = ss_server_helloverify; - break; - case SERVER_HELLO_RETRY_REQUEST_COMPLETE: - state = ss_server_helloretryrequest; - break; - case SERVER_HELLO_COMPLETE: - state = ss_server_hello; - break; - case SERVER_ENCRYPTED_EXTENSIONS_COMPLETE: - state = ss_server_encryptedextensions; - break; - case SERVER_CERT_COMPLETE: - state = ss_server_cert; - break; - case SERVER_KEYEXCHANGE_COMPLETE: - state = ss_server_keyexchange; - break; - case SERVER_HELLODONE_COMPLETE: - state = ss_server_hellodone; - break; - case SERVER_CHANGECIPHERSPEC_COMPLETE: - state = ss_server_changecipherspec; - break; - case SERVER_FINISHED_COMPLETE: - state = ss_server_finished; - break; - case CLIENT_HELLO_RETRY: - case CLIENT_HELLO_COMPLETE: - state = ss_client_hello; - break; - case CLIENT_KEYEXCHANGE_COMPLETE: - state = ss_client_keyexchange; - break; - case CLIENT_CHANGECIPHERSPEC_COMPLETE: - state = ss_client_changecipherspec; - break; - case CLIENT_FINISHED_COMPLETE: - state = ss_client_finished; - break; - case HANDSHAKE_DONE: - state = ss_handshake_done; - break; - default: - WOLFSSL_MSG("Unknown State"); - state = ss_null_state; - } - } - - if (protocol == UNKNOWN) { - WOLFSSL_MSG("Unknown protocol"); - return ""; - } - else { - return OUTPUT_STR[state][protocol][cbmode]; - } + + return ret; } +/* Only used by the table above, and this file is compiled into ssl.c, + * so do not leave them defined for the files included after it. */ +#undef STATE_STRINGS_PROTO +#undef STATE_STRINGS_PROTO_RW #endif /* OPENSSL_EXTRA */ #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) \ @@ -1724,25 +2055,29 @@ const char* wolfSSL_state_string_long(const WOLFSSL* ssl) */ int wolfSSL_SSL_do_handshake_internal(WOLFSSL *s) { + int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); + WOLFSSL_ENTER("wolfSSL_SSL_do_handshake_internal"); - if (s == NULL) - return WOLFSSL_FAILURE; - if (s->options.side == WOLFSSL_CLIENT_END) { - #ifndef NO_WOLFSSL_CLIENT - return wolfSSL_connect(s); - #else + if (s == NULL) { + ret = WOLFSSL_FAILURE; + } + else if (s->options.side == WOLFSSL_CLIENT_END) { + #ifndef NO_WOLFSSL_CLIENT + ret = wolfSSL_connect(s); + #else WOLFSSL_MSG("Client not compiled in"); - return WOLFSSL_FAILURE; - #endif + #endif + } + else { + #ifndef NO_WOLFSSL_SERVER + ret = wolfSSL_accept(s); + #else + WOLFSSL_MSG("Server not compiled in"); + #endif } -#ifndef NO_WOLFSSL_SERVER - return wolfSSL_accept(s); -#else - WOLFSSL_MSG("Server not compiled in"); - return WOLFSSL_FAILURE; -#endif + return ret; } /* Perform the handshake. @@ -1754,13 +2089,21 @@ int wolfSSL_SSL_do_handshake_internal(WOLFSSL *s) */ int wolfSSL_SSL_do_handshake(WOLFSSL *s) { + int ret; + WOLFSSL_ENTER("wolfSSL_SSL_do_handshake"); -#ifdef WOLFSSL_QUIC + + #ifdef WOLFSSL_QUIC if (WOLFSSL_IS_QUIC(s)) { - return wolfSSL_quic_do_handshake(s); + ret = wolfSSL_quic_do_handshake(s); } -#endif - return wolfSSL_SSL_do_handshake_internal(s); + else + #endif + { + ret = wolfSSL_SSL_do_handshake_internal(s); + } + + return ret; } #endif /* !NO_TLS */ @@ -1789,12 +2132,15 @@ int wolfSSL_SSL_in_init(WOLFSSL *ssl) */ int wolfSSL_SSL_in_before(const WOLFSSL *ssl) { + int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); + WOLFSSL_ENTER("wolfSSL_SSL_in_before"); - if (ssl == NULL) - return WOLFSSL_FAILURE; + if (ssl != NULL) { + ret = (ssl->options.handShakeState == NULL_STATE); + } - return ssl->options.handShakeState == NULL_STATE; + return ret; } /* Determine whether the handshake is in progress. @@ -1805,185 +2151,320 @@ int wolfSSL_SSL_in_before(const WOLFSSL *ssl) */ int wolfSSL_SSL_in_connect_init(WOLFSSL* ssl) { - WOLFSSL_ENTER("wolfSSL_SSL_in_connect_init"); + int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); - if (ssl == NULL) - return WOLFSSL_FAILURE; + WOLFSSL_ENTER("wolfSSL_SSL_in_connect_init"); - if (ssl->options.side == WOLFSSL_CLIENT_END) { - return ssl->options.connectState > CONNECT_BEGIN && - ssl->options.connectState < SECOND_REPLY_DONE; + if (ssl != NULL) { + if (ssl->options.side == WOLFSSL_CLIENT_END) { + ret = (ssl->options.connectState > CONNECT_BEGIN) && + (ssl->options.connectState < SECOND_REPLY_DONE); + } + else { + ret = (ssl->options.acceptState > ACCEPT_BEGIN) && + (ssl->options.acceptState < ACCEPT_THIRD_REPLY_DONE); + } } - return ssl->options.acceptState > ACCEPT_BEGIN && - ssl->options.acceptState < ACCEPT_THIRD_REPLY_DONE; + return ret; } #endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY || - OPENSSL_EXTRA || HAVE_LIGHTY */ +OPENSSL_EXTRA || HAVE_LIGHTY */ #ifndef NO_CERTS #ifdef HAVE_PK_CALLBACKS -/* callback for premaster secret generation */ +/* Set the premaster secret generation callback. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Callback to call. NULL to clear. + */ void wolfSSL_CTX_SetGenPreMasterCb(WOLFSSL_CTX* ctx, CallbackGenPreMaster cb) { - if (ctx) + if (ctx != NULL) { ctx->GenPreMasterCb = cb; + } } -/* Set premaster secret generation callback context */ +/* Set the context to pass to the premaster secret generation callback. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] ctx Context to pass to the callback. + */ void wolfSSL_SetGenPreMasterCtx(WOLFSSL* ssl, void *ctx) { - if (ssl) + if (ssl != NULL) { ssl->GenPreMasterCtx = ctx; + } } -/* Get premaster secret generation callback context */ +/* Get the context passed to the premaster secret generation callback. + * + * @param [in] ssl SSL/TLS object. + * @return Context on success. + * @return NULL when ssl is NULL. + */ void* wolfSSL_GetGenPreMasterCtx(WOLFSSL* ssl) { - if (ssl) - return ssl->GenPreMasterCtx; + void* ret = NULL; - return NULL; + if (ssl != NULL) { + ret = ssl->GenPreMasterCtx; + } + + return ret; } -/* callback for master secret generation */ +/* Set the master secret generation callback. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Callback to call. NULL to clear. + */ void wolfSSL_CTX_SetGenMasterSecretCb(WOLFSSL_CTX* ctx, CallbackGenMasterSecret cb) { - if (ctx) + if (ctx != NULL) { ctx->GenMasterCb = cb; + } } -/* Set master secret generation callback context */ +/* Set the context to pass to the master secret generation callback. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] ctx Context to pass to the callback. + */ void wolfSSL_SetGenMasterSecretCtx(WOLFSSL* ssl, void *ctx) { - if (ssl) + if (ssl != NULL) { ssl->GenMasterCtx = ctx; + } } -/* Get master secret generation callback context */ +/* Get the context passed to the master secret generation callback. + * + * @param [in] ssl SSL/TLS object. + * @return Context on success. + * @return NULL when ssl is NULL. + */ void* wolfSSL_GetGenMasterSecretCtx(WOLFSSL* ssl) { - if (ssl) - return ssl->GenMasterCtx; + void* ret = NULL; + + if (ssl != NULL) { + ret = ssl->GenMasterCtx; + } - return NULL; + return ret; } -/* callback for extended master secret generation */ +/* Set the extended master secret generation callback. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Callback to call. NULL to clear. + */ void wolfSSL_CTX_SetGenExtMasterSecretCb(WOLFSSL_CTX* ctx, CallbackGenExtMasterSecret cb) { - if (ctx) + if (ctx != NULL) { ctx->GenExtMasterCb = cb; + } } -/* Set extended master secret generation callback context */ +/* Set the context to pass to the extended master secret generation callback. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] ctx Context to pass to the callback. + */ void wolfSSL_SetGenExtMasterSecretCtx(WOLFSSL* ssl, void *ctx) { - if (ssl) + if (ssl != NULL) { ssl->GenExtMasterCtx = ctx; + } } -/* Get extended master secret generation callback context */ +/* Get the context passed to the extended master secret generation callback. + * + * @param [in] ssl SSL/TLS object. + * @return Context on success. + * @return NULL when ssl is NULL. + */ void* wolfSSL_GetGenExtMasterSecretCtx(WOLFSSL* ssl) { - if (ssl) - return ssl->GenExtMasterCtx; + void* ret = NULL; - return NULL; + if (ssl != NULL) { + ret = ssl->GenExtMasterCtx; + } + + return ret; } -/* callback for session key generation */ +/* Set the session key generation callback. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Callback to call. NULL to clear. + */ void wolfSSL_CTX_SetGenSessionKeyCb(WOLFSSL_CTX* ctx, CallbackGenSessionKey cb) { - if (ctx) + if (ctx != NULL) { ctx->GenSessionKeyCb = cb; + } } -/* Set session key generation callback context */ +/* Set the context to pass to the session key generation callback. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] ctx Context to pass to the callback. + */ void wolfSSL_SetGenSessionKeyCtx(WOLFSSL* ssl, void *ctx) { - if (ssl) + if (ssl != NULL) { ssl->GenSessionKeyCtx = ctx; + } } -/* Get session key generation callback context */ +/* Get the context passed to the session key generation callback. + * + * @param [in] ssl SSL/TLS object. + * @return Context on success. + * @return NULL when ssl is NULL. + */ void* wolfSSL_GetGenSessionKeyCtx(WOLFSSL* ssl) { - if (ssl) - return ssl->GenSessionKeyCtx; + void* ret = NULL; + + if (ssl != NULL) { + ret = ssl->GenSessionKeyCtx; + } - return NULL; + return ret; } -/* callback for setting encryption keys */ +/* Set the encryption key setting callback. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Callback to call. NULL to clear. + */ void wolfSSL_CTX_SetEncryptKeysCb(WOLFSSL_CTX* ctx, CallbackEncryptKeys cb) { - if (ctx) + if (ctx != NULL) { ctx->EncryptKeysCb = cb; + } } -/* Set encryption keys callback context */ +/* Set the context to pass to the encryption key setting callback. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] ctx Context to pass to the callback. + */ void wolfSSL_SetEncryptKeysCtx(WOLFSSL* ssl, void *ctx) { - if (ssl) + if (ssl != NULL) { ssl->EncryptKeysCtx = ctx; + } } -/* Get encryption keys callback context */ +/* Get the context passed to the encryption key setting callback. + * + * @param [in] ssl SSL/TLS object. + * @return Context on success. + * @return NULL when ssl is NULL. + */ void* wolfSSL_GetEncryptKeysCtx(WOLFSSL* ssl) { - if (ssl) - return ssl->EncryptKeysCtx; + void* ret = NULL; - return NULL; + if (ssl != NULL) { + ret = ssl->EncryptKeysCtx; + } + + return ret; } -/* callback for Tls finished */ -/* the callback can be used to build TLS Finished message if enabled */ +/* Set the TLS Finished message building callback. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Callback to call. NULL to clear. + */ void wolfSSL_CTX_SetTlsFinishedCb(WOLFSSL_CTX* ctx, CallbackTlsFinished cb) { - if (ctx) + if (ctx != NULL) { ctx->TlsFinishedCb = cb; + } } -/* Set Tls finished callback context */ +/* Set the context to pass to the TLS Finished message building callback. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] ctx Context to pass to the callback. + */ void wolfSSL_SetTlsFinishedCtx(WOLFSSL* ssl, void *ctx) { - if (ssl) + if (ssl != NULL) { ssl->TlsFinishedCtx = ctx; + } } -/* Get Tls finished callback context */ +/* Get the context passed to the TLS Finished message building callback. + * + * @param [in] ssl SSL/TLS object. + * @return Context on success. + * @return NULL when ssl is NULL. + */ void* wolfSSL_GetTlsFinishedCtx(WOLFSSL* ssl) { - if (ssl) - return ssl->TlsFinishedCtx; + void* ret = NULL; + + if (ssl != NULL) { + ret = ssl->TlsFinishedCtx; + } - return NULL; + return ret; } #if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY) -/* callback for verify data */ +/* Set the MAC verification callback. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Callback to call. NULL to clear. + */ void wolfSSL_CTX_SetVerifyMacCb(WOLFSSL_CTX* ctx, CallbackVerifyMac cb) { - if (ctx) + if (ctx != NULL) { ctx->VerifyMacCb = cb; + } } -/* Set set keys callback context */ +/* Set the context to pass to the MAC verification callback. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] ctx Context to pass to the callback. + */ void wolfSSL_SetVerifyMacCtx(WOLFSSL* ssl, void *ctx) { - if (ssl) + if (ssl != NULL) { ssl->VerifyMacCtx = ctx; + } } -/* Get set keys callback context */ +/* Get the context passed to the MAC verification callback. + * + * @param [in] ssl SSL/TLS object. + * @return Context on success. + * @return NULL when ssl is NULL. + */ void* wolfSSL_GetVerifyMacCtx(WOLFSSL* ssl) { - if (ssl) - return ssl->VerifyMacCtx; + void* ret = NULL; - return NULL; + if (ssl != NULL) { + ret = ssl->VerifyMacCtx; + } + + return ret; } #endif /* !WOLFSSL_NO_TLS12 && !WOLFSSL_AEAD_ONLY */ +/* Set the HKDF expand label callback. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Callback to call. NULL to clear. + */ void wolfSSL_CTX_SetHKDFExpandLabelCb(WOLFSSL_CTX* ctx, CallbackHKDFExpandLabel cb) { - if (ctx) + if (ctx != NULL) { ctx->HKDFExpandLabelCb = cb; + } } #ifdef WOLFSSL_PUBLIC_ASN /* Set the callback to call to process the peer's certificate. @@ -1994,15 +2475,23 @@ void wolfSSL_CTX_SetHKDFExpandLabelCb(WOLFSSL_CTX* ctx, void wolfSSL_CTX_SetProcessPeerCertCb(WOLFSSL_CTX* ctx, CallbackProcessPeerCert cb) { - if (ctx) + if (ctx != NULL) { ctx->ProcessPeerCertCb = cb; + } } #endif /* WOLFSSL_PUBLIC_ASN */ +/* Set the callback to call to process the server's signature and key + * exchange. + * + * @param [in, out] ctx SSL/TLS CTX object. + * @param [in] cb Callback to call. NULL to clear. + */ void wolfSSL_CTX_SetProcessServerSigKexCb(WOLFSSL_CTX* ctx, CallbackProcessServerSigKex cb) { - if (ctx) + if (ctx != NULL) { ctx->ProcessServerSigKexCb = cb; + } } /* Set the callback to call to encrypt and decrypt TLS records. * @@ -2012,8 +2501,9 @@ void wolfSSL_CTX_SetProcessServerSigKexCb(WOLFSSL_CTX* ctx, void wolfSSL_CTX_SetPerformTlsRecordProcessingCb(WOLFSSL_CTX* ctx, CallbackPerformTlsRecordProcessing cb) { - if (ctx) + if (ctx != NULL) { ctx->PerformTlsRecordProcessingCb = cb; + } } #endif /* HAVE_PK_CALLBACKS */ #endif /* NO_CERTS */ @@ -2027,8 +2517,9 @@ void wolfSSL_CTX_SetPerformTlsRecordProcessingCb(WOLFSSL_CTX* ctx, */ void wolfSSL_CTX_SetHKDFExtractCb(WOLFSSL_CTX* ctx, CallbackHKDFExtract cb) { - if (ctx) + if (ctx != NULL) { ctx->HkdfExtractCb = cb; + } } /* Set the context to pass to the HKDF extract callback. @@ -2038,8 +2529,9 @@ void wolfSSL_CTX_SetHKDFExtractCb(WOLFSSL_CTX* ctx, CallbackHKDFExtract cb) */ void wolfSSL_SetHKDFExtractCtx(WOLFSSL* ssl, void *ctx) { - if (ssl) + if (ssl != NULL) { ssl->HkdfExtractCtx = ctx; + } } /* Get the context passed to the HKDF extract callback. @@ -2050,10 +2542,13 @@ void wolfSSL_SetHKDFExtractCtx(WOLFSSL* ssl, void *ctx) */ void* wolfSSL_GetHKDFExtractCtx(WOLFSSL* ssl) { - if (ssl) - return ssl->HkdfExtractCtx; + void* ret = NULL; + + if (ssl != NULL) { + ret = ssl->HkdfExtractCtx; + } - return NULL; + return ret; } #endif /* HAVE_PK_CALLBACKS && HAVE_HKDF */ diff --git a/src/ssl_api_pk.c b/src/ssl_api_pk.c index 4223e4921bd..d2ce407d63f 100644 --- a/src/ssl_api_pk.c +++ b/src/ssl_api_pk.c @@ -795,7 +795,8 @@ void* wolfSSL_CTX_GetEccSignCtx(WOLFSSL_CTX* ctx) * @param [in] ctx SSL/TLS context. * @param [in] cb ECC sign callback. */ -WOLFSSL_ABI void wolfSSL_CTX_SetEccSignCb(WOLFSSL_CTX* ctx, CallbackEccSign cb) +WOLFSSL_ABI +void wolfSSL_CTX_SetEccSignCb(WOLFSSL_CTX* ctx, CallbackEccSign cb) { if (ctx != NULL) { ctx->EccSignCb = cb; diff --git a/src/ssl_api_rw.c b/src/ssl_api_rw.c index e20311f3327..aef587d912d 100644 --- a/src/ssl_api_rw.c +++ b/src/ssl_api_rw.c @@ -31,163 +31,146 @@ #ifndef NO_TLS -/* Write application data to the peer. +#if defined(HAVE_WRITE_DUP) && defined(WOLFSSL_TLS13) +/* Take over the TLS 1.3 work delegated by the read side. * - * Performs the handshake when it has not completed. When a write duplicate is - * in use, work delegated by the read side, such as sending a key update, is - * done here first. + * The read side of a write duplicate cannot send, so it records what needs to + * be sent in the write duplicate object. Move that state across to the SSL + * object of the write side. * - * @param [in, out] ssl SSL/TLS object. - * @param [in] data Application data to write. - * @param [in] sz Length of data in bytes. - * @return Number of bytes written on success. - * @return BAD_FUNC_ARG when ssl or data is NULL. - * @return WOLFSSL_FATAL_ERROR when the handshake or write fails. Call - * wolfSSL_get_error() for the reason. + * Must be called with ssl->dupWrite->dupMutex held. + * + * @param [in, out] ssl SSL/TLS object of the write side. + * @return 0 on success. + * @return Negative on error. */ -static int wolfSSL_write_internal(WOLFSSL* ssl, const void* data, size_t sz) +static int wolfssl_write_dup_take_tls13_work(WOLFSSL* ssl) { int ret = 0; - WOLFSSL_ENTER("wolfSSL_write"); - - if (ssl == NULL || data == NULL) - return BAD_FUNC_ARG; - -#ifdef WOLFSSL_QUIC - if (WOLFSSL_IS_QUIC(ssl)) { - WOLFSSL_MSG("SSL_write() on QUIC not allowed"); - return BAD_FUNC_ARG; - } -#endif - -#ifdef HAVE_WRITE_DUP - if (ssl->dupSide == READ_DUP_SIDE) { - WOLFSSL_MSG("Read dup side cannot write"); - return WRITE_DUP_WRITE_E; - } - /* Only enter special dupWrite logic when error is cleared. This will help - * with handling async data and other edge case errors. */ - if (ssl->dupWrite != NULL && ssl->error == 0) { - int dupErr = 0; /* local copy */ - /* Lock ssl->dupWrite to gather what needs to be done. */ - if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) - return BAD_MUTEX_E; - dupErr = ssl->dupWrite->dupErr; -#ifdef WOLFSSL_TLS13 - if (IsAtLeastTLSv1_3(ssl->version)) { - /* TLS 1.3: if the read side received a KeyUpdate(update_requested) - * it cannot respond; send the response from here. */ - ssl->keys.keyUpdateRespond |= ssl->dupWrite->keyUpdateRespond; - ssl->dupWrite->keyUpdateRespond = 0; -#ifdef WOLFSSL_POST_HANDSHAKE_AUTH - ssl->postHandshakeAuthPending |= - ssl->dupWrite->postHandshakeAuthPending; - ssl->dupWrite->postHandshakeAuthPending = 0; - if (ssl->postHandshakeAuthPending) { - /* Take ownership of the delegated auth state. */ - CertReqCtx** tail = &ssl->dupWrite->postHandshakeCertReqCtx; - while (*tail != NULL) - tail = &(*tail)->next; - *tail = ssl->certReqCtx; - ssl->certReqCtx = ssl->dupWrite->postHandshakeCertReqCtx; - ssl->dupWrite->postHandshakeCertReqCtx = NULL; - FreeHandshakeHashes(ssl); - ssl->hsHashes = ssl->dupWrite->postHandshakeHashState; - ssl->dupWrite->postHandshakeHashState = NULL; - ssl->options.sendVerify = - ssl->dupWrite->postHandshakeSendVerify; - ssl->options.sigAlgo = ssl->dupWrite->postHandshakeSigAlgo; - ssl->options.hashAlgo = ssl->dupWrite->postHandshakeHashAlgo; + if (IsAtLeastTLSv1_3(ssl->version)) { + /* TLS 1.3: if the read side received a KeyUpdate(update_requested) + * it cannot respond; send the response from here. */ + ssl->keys.keyUpdateRespond |= ssl->dupWrite->keyUpdateRespond; + ssl->dupWrite->keyUpdateRespond = 0; + #ifdef WOLFSSL_POST_HANDSHAKE_AUTH + ssl->postHandshakeAuthPending |= + ssl->dupWrite->postHandshakeAuthPending; + ssl->dupWrite->postHandshakeAuthPending = 0; + if (ssl->postHandshakeAuthPending) { + /* Take ownership of the delegated auth state. */ + CertReqCtx** tail = &ssl->dupWrite->postHandshakeCertReqCtx; + while (*tail != NULL) { + tail = &(*tail)->next; } -#endif /* WOLFSSL_POST_HANDSHAKE_AUTH */ -#ifdef WOLFSSL_DTLS13 - if (ssl->options.dtls) { - /* Schedule key update to be sent. */ - if (ssl->keys.keyUpdateRespond) - ssl->dtls13DoKeyUpdate = 1; - - /* Copy over ACKs */ - ssl->dtls13Rtx.sendAcks |= ssl->dupWrite->sendAcks; - if (ssl->dupWrite->sendAcks) { - /* Insert each record number so the - * ACK message is properly ordered. */ - struct Dtls13RecordNumber* rn; - for (rn = ssl->dupWrite->sendAckList; rn != NULL; - rn = rn->next) { - ret = Dtls13RtxAddAck(ssl, rn->epoch, rn->seq); - if (ret != 0) - break; + *tail = ssl->certReqCtx; + ssl->certReqCtx = ssl->dupWrite->postHandshakeCertReqCtx; + ssl->dupWrite->postHandshakeCertReqCtx = NULL; + FreeHandshakeHashes(ssl); + ssl->hsHashes = ssl->dupWrite->postHandshakeHashState; + ssl->dupWrite->postHandshakeHashState = NULL; + ssl->options.sendVerify = ssl->dupWrite->postHandshakeSendVerify; + ssl->options.sigAlgo = ssl->dupWrite->postHandshakeSigAlgo; + ssl->options.hashAlgo = ssl->dupWrite->postHandshakeHashAlgo; + } + #endif /* WOLFSSL_POST_HANDSHAKE_AUTH */ + #ifdef WOLFSSL_DTLS13 + if (ssl->options.dtls) { + /* Schedule key update to be sent. */ + if (ssl->keys.keyUpdateRespond) { + ssl->dtls13DoKeyUpdate = 1; + } + + /* Copy over ACKs */ + ssl->dtls13Rtx.sendAcks |= ssl->dupWrite->sendAcks; + if (ssl->dupWrite->sendAcks) { + /* Insert each record number so the + * ACK message is properly ordered. */ + struct Dtls13RecordNumber* rn; + for (rn = ssl->dupWrite->sendAckList; rn != NULL; + rn = rn->next) { + ret = Dtls13RtxAddAck(ssl, rn->epoch, rn->seq); + if (ret != 0) { + break; } - /* Clear only on success so no ACKs get dropped */ - if (ret == 0) { - rn = ssl->dupWrite->sendAckList; - ssl->dupWrite->sendAckList = NULL; - ssl->dupWrite->sendAcks = 0; - while (rn != NULL) { - struct Dtls13RecordNumber* next = rn->next; - XFREE(rn, ssl->heap, DYNAMIC_TYPE_DTLS_MSG); - rn = next; - } + } + /* Clear only on success so no ACKs get dropped */ + if (ret == 0) { + rn = ssl->dupWrite->sendAckList; + ssl->dupWrite->sendAckList = NULL; + ssl->dupWrite->sendAcks = 0; + while (rn != NULL) { + struct Dtls13RecordNumber* next = rn->next; + XFREE(rn, ssl->heap, DYNAMIC_TYPE_DTLS_MSG); + rn = next; } } + } - /* Remove KeyUpdate record from RTX list. */ - if (ssl->dupWrite->keyUpdateAcked) { - Dtls13RtxRemoveRecord(ssl, ssl->dupWrite->keyUpdateEpoch, - ssl->dupWrite->keyUpdateSeq); - } - /* Store if KeyUpdate was ACKed. */ - ssl->dtls13KeyUpdateAcked |= ssl->dupWrite->keyUpdateAcked; - ssl->dupWrite->keyUpdateAcked = 0; + /* Remove KeyUpdate record from RTX list. */ + if (ssl->dupWrite->keyUpdateAcked) { + Dtls13RtxRemoveRecord(ssl, ssl->dupWrite->keyUpdateEpoch, + ssl->dupWrite->keyUpdateSeq); } -#endif /* WOLFSSL_DTLS13 */ + /* Store if KeyUpdate was ACKed. */ + ssl->dtls13KeyUpdateAcked |= ssl->dupWrite->keyUpdateAcked; + ssl->dupWrite->keyUpdateAcked = 0; } -#endif /* WOLFSSL_TLS13 */ - wc_UnLockMutex(&ssl->dupWrite->dupMutex); + #endif /* WOLFSSL_DTLS13 */ + } - if (dupErr != 0) { - WOLFSSL_MSG("Write dup error from other side"); - ssl->error = dupErr; - return WOLFSSL_FATAL_ERROR; - } - if (ret != 0) { - ssl->error = ret; - return WOLFSSL_FATAL_ERROR; - } - -#ifdef WOLFSSL_TLS13 - if (IsAtLeastTLSv1_3(ssl->version)) { -#ifdef WOLFSSL_POST_HANDSHAKE_AUTH - /* Read side received a CertificateRequest but couldn't write; - * send Certificate+CertificateVerify+Finished from the write - * side. */ - if (ssl->postHandshakeAuthPending) { - /* reset handshake states */ - ssl->postHandshakeAuthPending = 0; - ssl->options.clientState = CLIENT_HELLO_COMPLETE; - ssl->options.connectState = FIRST_REPLY_DONE; - ssl->options.handShakeState = CLIENT_HELLO_COMPLETE; - ssl->options.processReply = 0; /* doProcessInit */ - if (wolfSSL_connect_TLSv13(ssl) != WOLFSSL_SUCCESS) { - if (ssl->error != WC_NO_ERR_TRACE(WANT_WRITE) && - ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E)) { - WOLFSSL_MSG("Post-handshake auth send failed"); - ssl->error = POST_HAND_AUTH_ERROR; - } - return WOLFSSL_FATAL_ERROR; + return ret; +} + +/* Perform the TLS 1.3 work delegated by the read side. + * + * Must be called after ssl->dupWrite->dupMutex has been released, as the work + * performed here sends records. + * + * @param [in, out] ssl SSL/TLS object of the write side. + * @return 0 on success. + * @return BAD_MUTEX_E when the write duplicate could not be locked. Returned + * as-is by the caller, so ssl->error is not set for it. + * @return WOLFSSL_FATAL_ERROR on error. Call wolfSSL_get_error() for the + * reason. + */ +static int wolfssl_write_dup_do_tls13_work(WOLFSSL* ssl) +{ + int ret = 0; + + if (IsAtLeastTLSv1_3(ssl->version)) { + #ifdef WOLFSSL_POST_HANDSHAKE_AUTH + /* Read side received a CertificateRequest but couldn't write; + * send Certificate+CertificateVerify+Finished from the write + * side. */ + if (ssl->postHandshakeAuthPending) { + /* reset handshake states */ + ssl->postHandshakeAuthPending = 0; + ssl->options.clientState = CLIENT_HELLO_COMPLETE; + ssl->options.connectState = FIRST_REPLY_DONE; + ssl->options.handShakeState = CLIENT_HELLO_COMPLETE; + ssl->options.processReply = 0; /* doProcessInit */ + if (wolfSSL_connect_TLSv13(ssl) != WOLFSSL_SUCCESS) { + if ((ssl->error != WC_NO_ERR_TRACE(WANT_WRITE)) && + (ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E))) { + WOLFSSL_MSG("Post-handshake auth send failed"); + ssl->error = POST_HAND_AUTH_ERROR; } - /* PHA response fully sent: publish the write side's updated - * transcript to the read side for the next PHA round. */ - if (ssl->hsHashes != NULL && ssl->dupWrite != NULL) { - int syncRet; - if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) - return BAD_MUTEX_E; - syncRet = InitHandshakeHashesAndCopy(ssl, ssl->hsHashes, + ret = WOLFSSL_FATAL_ERROR; + } + /* PHA response fully sent: publish the write side's updated + * transcript to the read side for the next PHA round. */ + else if ((ssl->hsHashes != NULL) && (ssl->dupWrite != NULL)) { + if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) { + ret = BAD_MUTEX_E; + } + else { + int syncRet = InitHandshakeHashesAndCopy(ssl, + ssl->hsHashes, &ssl->dupWrite->postHandshakeSyncedHashState); if (syncRet != 0) { /* On failure the copy may have left a partially - * initialized transcript. The read side only checks + * initialized transcript. The read side only checks * for non-NULL before consuming it, so drop it here to * avoid hashing onto a corrupt transcript, and surface * the error to the caller. */ @@ -199,53 +182,166 @@ static int wolfSSL_write_internal(WOLFSSL* ssl, const void* data, size_t sz) wc_UnLockMutex(&ssl->dupWrite->dupMutex); if (syncRet != 0) { ssl->error = syncRet; - return WOLFSSL_FATAL_ERROR; + ret = WOLFSSL_FATAL_ERROR; } } } -#endif /* WOLFSSL_POST_HANDSHAKE_AUTH */ -#ifdef WOLFSSL_DTLS13 + } + #endif /* WOLFSSL_POST_HANDSHAKE_AUTH */ + + if (ret == 0) { + #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { - if (ssl->dtls13KeyUpdateAcked) + if (ssl->dtls13KeyUpdateAcked) { ret = DoDtls13KeyUpdateAck(ssl); + } ssl->dtls13KeyUpdateAcked = 0; - if (ret == 0) + if (ret == 0) { ret = Dtls13DoScheduledWork(ssl); + } } else -#endif /* WOLFSSL_DTLS13 */ - if (ssl->keys.keyUpdateRespond) /* cleared in SendTls13KeyUpdate */ - ret = Tls13UpdateKeys(ssl); + #endif /* WOLFSSL_DTLS13 */ + { + /* keyUpdateRespond is cleared in SendTls13KeyUpdate. */ + if (ssl->keys.keyUpdateRespond) { + ret = Tls13UpdateKeys(ssl); + } + } + if (ret != 0) { ssl->error = ret; - return WOLFSSL_FATAL_ERROR; + ret = WOLFSSL_FATAL_ERROR; } - /* WANT_WRITE is safe to clear. Data is buffered in output buffer - * or in DTLS RTX queue */ - ret = 0; } -#endif /* WOLFSSL_TLS13 */ } -#endif -#ifdef HAVE_ERRNO_H - errno = 0; -#endif + return ret; +} +#endif /* HAVE_WRITE_DUP && WOLFSSL_TLS13 */ - #ifdef OPENSSL_EXTRA - if (ssl->CBIS != NULL) { - ssl->CBIS(ssl, WOLFSSL_CB_WRITE, WOLFSSL_SUCCESS); - ssl->cbmode = WOLFSSL_CB_WRITE; +#ifdef HAVE_WRITE_DUP +/* Settle the write duplicate state before application data is sent. + * + * Takes over the work the read side delegated and surfaces any error it + * recorded. Both are held under ssl->dupWrite->dupMutex, so they are collected + * with the lock held and acted on once it has been released. + * + * @param [in, out] ssl SSL/TLS object of the write side. + * @return 0 when the write may proceed. + * @return BAD_MUTEX_E when the write duplicate could not be locked. + * @return WOLFSSL_FATAL_ERROR on error. Call wolfSSL_get_error() for the + * reason. + */ +static int wolfssl_write_dup_prepare(WOLFSSL* ssl) +{ + int ret = 0; + + /* Lock ssl->dupWrite to gather what needs to be done. */ + if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) { + ret = BAD_MUTEX_E; + } + else { + int dupErr = ssl->dupWrite->dupErr; /* local copy */ + + #ifdef WOLFSSL_TLS13 + ret = wolfssl_write_dup_take_tls13_work(ssl); + #endif /* WOLFSSL_TLS13 */ + wc_UnLockMutex(&ssl->dupWrite->dupMutex); + + /* An error from the read side takes precedence over one hit while + * taking over its work. */ + if (dupErr != 0) { + WOLFSSL_MSG("Write dup error from other side"); + ret = dupErr; + } + + if (ret != 0) { + ssl->error = ret; + ret = WOLFSSL_FATAL_ERROR; + } + #ifdef WOLFSSL_TLS13 + else { + /* Do the work delegated by the read side. */ + ret = wolfssl_write_dup_do_tls13_work(ssl); + } + #endif /* WOLFSSL_TLS13 */ + } + + return ret; +} +#endif /* HAVE_WRITE_DUP */ + +/* Write application data to the peer. + * + * Performs the handshake when it has not completed. When a write duplicate is + * in use, work delegated by the read side, such as sending a key update, is + * done here first. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] data Application data to write. + * @param [in] sz Length of data in bytes. + * @return Number of bytes written on success. + * @return BAD_FUNC_ARG when ssl or data is NULL. + * @return WRITE_DUP_WRITE_E when called on the read side of a write + * duplicate. + * @return BAD_MUTEX_E when the write duplicate could not be locked. Neither + * of those two sets ssl->error. + * @return WOLFSSL_FATAL_ERROR when the handshake or write fails. Call + * wolfSSL_get_error() for the reason. + */ +static int wolfSSL_write_internal(WOLFSSL* ssl, const void* data, size_t sz) +{ + int ret = 0; + + WOLFSSL_ENTER("wolfSSL_write_internal"); + + /* Validate parameters. Nothing on the way to the send reports zero, so ret + * doubles as the "keep going" flag. */ + if ((ssl == NULL) || (data == NULL)) { + ret = BAD_FUNC_ARG; + } + + #ifdef WOLFSSL_QUIC + if ((ret == 0) && (WOLFSSL_IS_QUIC(ssl))) { + WOLFSSL_MSG("SSL_write() on QUIC not allowed"); + ret = BAD_FUNC_ARG; } #endif - ret = SendData(ssl, data, sz); - WOLFSSL_LEAVE("wolfSSL_write", ret); + #ifdef HAVE_WRITE_DUP + if ((ret == 0) && (ssl->dupSide == READ_DUP_SIDE)) { + WOLFSSL_MSG("Read dup side cannot write"); + ret = WRITE_DUP_WRITE_E; + } + /* Only enter special dupWrite logic when error is cleared. This will help + * with handling async data and other edge case errors. */ + if ((ret == 0) && (ssl->dupWrite != NULL) && (ssl->error == 0)) { + ret = wolfssl_write_dup_prepare(ssl); + } + #endif - if (ret < 0) - return WOLFSSL_FATAL_ERROR; - else - return ret; + if (ret == 0) { + #ifdef HAVE_ERRNO_H + errno = 0; + #endif + + #ifdef OPENSSL_EXTRA + if (ssl->CBIS != NULL) { + ssl->CBIS(ssl, WOLFSSL_CB_WRITE, WOLFSSL_SUCCESS); + ssl->cbmode = WOLFSSL_CB_WRITE; + } + #endif + ret = SendData(ssl, data, sz); + + WOLFSSL_LEAVE("wolfSSL_write_internal", ret); + + if (ret < 0) { + ret = WOLFSSL_FATAL_ERROR; + } + } + + return ret; } /* Write application data to the peer. @@ -261,12 +357,19 @@ static int wolfSSL_write_internal(WOLFSSL* ssl, const void* data, size_t sz) WOLFSSL_ABI int wolfSSL_write(WOLFSSL* ssl, const void* data, int sz) { + int ret; + WOLFSSL_ENTER("wolfSSL_write"); - if (sz < 0) - return BAD_FUNC_ARG; + /* Validate parameter. */ + if (sz < 0) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_write_internal(ssl, data, (size_t)sz); + } - return wolfSSL_write_internal(ssl, data, (size_t)sz); + return ret; } /* Inject data into the input buffer as if it was received from the peer. @@ -278,42 +381,54 @@ int wolfSSL_write(WOLFSSL* ssl, const void* data, int sz) * @param [in] sz Length of data in bytes. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when ssl or data is NULL, or sz is not positive. - * @return MEMORY_E when growing the input buffer fails. + * @return APP_DATA_READY when there is application data still to be read, + * as growing the buffer would invalidate it. + * @return Negative error code from growing the input buffer, such as + * MEMORY_E. */ int wolfSSL_inject(WOLFSSL* ssl, const void* data, int sz) { - int maxLength; - int usedLength; + int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_inject"); - if (ssl == NULL || data == NULL || sz <= 0) - return BAD_FUNC_ARG; + /* Validate parameters. */ + if ((ssl == NULL) || (data == NULL) || (sz <= 0)) { + ret = BAD_FUNC_ARG; + } - usedLength = (int)(ssl->buffers.inputBuffer.length - - ssl->buffers.inputBuffer.idx); - maxLength = (int)(ssl->buffers.inputBuffer.bufferSize - - (word32)usedLength); + if (ret == WOLFSSL_SUCCESS) { + int usedLength = (int)(ssl->buffers.inputBuffer.length - + ssl->buffers.inputBuffer.idx); + int maxLength = (int)(ssl->buffers.inputBuffer.bufferSize - + (word32)usedLength); - if (sz > maxLength) { - /* Need to make space */ - int ret; - if (ssl->buffers.clearOutputBuffer.length > 0) { - /* clearOutputBuffer points into so reallocating inputBuffer will - * invalidate clearOutputBuffer and lose app data */ - WOLFSSL_MSG("Can't inject while there is application data to read"); - return APP_DATA_READY; + if (sz > maxLength) { + /* Need to make space */ + if (ssl->buffers.clearOutputBuffer.length > 0) { + /* clearOutputBuffer points into so reallocating inputBuffer + * will invalidate clearOutputBuffer and lose app data */ + WOLFSSL_MSG( + "Can't inject while there is application data to read"); + ret = APP_DATA_READY; + } + else { + int growRet = GrowInputBuffer(ssl, sz, usedLength); + + if (growRet < 0) { + ret = growRet; + } + } } - ret = GrowInputBuffer(ssl, sz, usedLength); - if (ret < 0) - return ret; } - XMEMCPY(ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx, - data, sz); - ssl->buffers.inputBuffer.length += sz; + if (ret == WOLFSSL_SUCCESS) { + XMEMCPY(ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx, + data, sz); + ssl->buffers.inputBuffer.length += sz; + } - return WOLFSSL_SUCCESS; + return ret; } /* Write application data to the peer and return the number of bytes written. @@ -323,6 +438,7 @@ int wolfSSL_inject(WOLFSSL* ssl, const void* data, int sz) * @param [in] sz Length of data in bytes. * @param [out] wr Number of bytes written. May be NULL. * @return WOLFSSL_SUCCESS on success. + * @return BAD_FUNC_ARG when ssl is NULL. * @return WOLFSSL_FAILURE when the write fails. Call wolfSSL_get_error() for * the reason. */ @@ -334,6 +450,13 @@ int wolfSSL_write_ex(WOLFSSL* ssl, const void* data, size_t sz, size_t* wr) *wr = 0; } + /* Validate parameter, matching wolfSSL_read_ex() and the rest of the + * file. Reported as an error code rather than the 0 used for "nothing + * was written", which a caller cannot tell from a short write. */ + if (ssl == NULL) { + return BAD_FUNC_ARG; + } + ret = wolfSSL_write_internal(ssl, data, sz); if (ret >= 0) { if (wr != NULL) { @@ -342,10 +465,10 @@ int wolfSSL_write_ex(WOLFSSL* ssl, const void* data, size_t sz, size_t* wr) /* handle partial write cases, if not set then a partial write is * considered a failure case, or if set and ret is 0 then is a fail */ - if (ret == 0 && ssl->options.partialWrite) { + if ((ret == 0) && (ssl->options.partialWrite)) { ret = 0; } - else if ((size_t)ret < sz && !ssl->options.partialWrite) { + else if (((size_t)ret < sz) && (!ssl->options.partialWrite)) { ret = 0; } else { @@ -372,25 +495,34 @@ int wolfSSL_write_ex(WOLFSSL* ssl, const void* data, size_t sz, size_t* wr) * @return Number of bytes read on success. * @return 0 when the peer has closed the connection. * @return BAD_FUNC_ARG when ssl or data is NULL. + * @return WRITE_DUP_READ_E when called on the write side of a write + * duplicate. ssl->error is not set for it. * @return WOLFSSL_FATAL_ERROR when the handshake or read fails. Call * wolfSSL_get_error() for the reason. */ static int wolfSSL_read_internal(WOLFSSL* ssl, void* data, size_t sz, int peek) { - int ret; + int ret = 0; + /* A separate flag is needed rather than gating on ret: the OpenSSL + * shutdown simulation below reports WOLFSSL_FAILURE, which is zero. */ + int done = 0; WOLFSSL_ENTER("wolfSSL_read_internal"); - if (ssl == NULL || data == NULL) - return BAD_FUNC_ARG; + /* Validate parameters. */ + if ((ssl == NULL) || (data == NULL)) { + ret = BAD_FUNC_ARG; + done = 1; + } -#ifdef WOLFSSL_QUIC - if (WOLFSSL_IS_QUIC(ssl)) { + #ifdef WOLFSSL_QUIC + if ((!done) && (WOLFSSL_IS_QUIC(ssl))) { WOLFSSL_MSG("SSL_read() on QUIC not allowed"); - return BAD_FUNC_ARG; + ret = BAD_FUNC_ARG; + done = 1; } -#endif -#if defined(WOLFSSL_ERROR_CODE_OPENSSL) && defined(OPENSSL_EXTRA) + #endif + #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. @@ -403,7 +535,8 @@ static int wolfSSL_read_internal(WOLFSSL* ssl, void* data, size_t sz, int peek) */ /* make sure bidirectional TLS shutdown completes */ - if (ssl->error == WOLFSSL_ERROR_SYSCALL || ssl->options.shutdownDone) { + if ((!done) && ((ssl->error == WOLFSSL_ERROR_SYSCALL) || + (ssl->options.shutdownDone))) { /* ask the underlying transport the connection is closed */ if (ssl->CBIORecv(ssl, (char*)data, 0, ssl->IOCB_ReadCtx) == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_CONN_CLOSE)) @@ -411,47 +544,54 @@ static int wolfSSL_read_internal(WOLFSSL* ssl, void* data, size_t sz, int peek) ssl->options.isClosed = 1; ssl->error = WOLFSSL_ERROR_ZERO_RETURN; } - return WOLFSSL_FAILURE; + ret = WOLFSSL_FAILURE; + done = 1; } -#endif + #endif -#ifdef HAVE_WRITE_DUP - if (ssl->dupWrite && ssl->dupSide == WRITE_DUP_SIDE) { + #ifdef HAVE_WRITE_DUP + if ((!done) && (ssl->dupWrite != NULL) && + (ssl->dupSide == WRITE_DUP_SIDE)) { WOLFSSL_MSG("Write dup side cannot read"); - return WRITE_DUP_READ_E; + ret = WRITE_DUP_READ_E; + done = 1; } -#endif + #endif -#ifdef HAVE_ERRNO_H + if (!done) { + #ifdef HAVE_ERRNO_H errno = 0; -#endif - - ret = ReceiveData(ssl, (byte*)data, sz, peek); - -#ifdef HAVE_WRITE_DUP - if (ssl->dupWrite) { - if (ssl->error != 0 && ssl->error != WC_NO_ERR_TRACE(WANT_READ) - #ifdef WOLFSSL_ASYNC_CRYPT - && ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E) #endif - ) { - int notifyErr; - WOLFSSL_MSG("Notifying write side of fatal read error"); - notifyErr = NotifyWriteSide(ssl, ssl->error); - if (notifyErr < 0) { - ret = ssl->error = notifyErr; + ret = ReceiveData(ssl, (byte*)data, sz, peek); + + #ifdef HAVE_WRITE_DUP + if (ssl->dupWrite != NULL) { + if ((ssl->error != 0) && + (ssl->error != WC_NO_ERR_TRACE(WANT_READ)) + #ifdef WOLFSSL_ASYNC_CRYPT + && (ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E)) + #endif + ) { + int notifyErr; + + WOLFSSL_MSG("Notifying write side of fatal read error"); + notifyErr = NotifyWriteSide(ssl, ssl->error); + if (notifyErr < 0) { + ret = ssl->error = notifyErr; + } } } - } -#endif + #endif + + WOLFSSL_LEAVE("wolfSSL_read_internal", ret); - WOLFSSL_LEAVE("wolfSSL_read_internal", ret); + if (ret < 0) { + ret = WOLFSSL_FATAL_ERROR; + } + } - if (ret < 0) - return WOLFSSL_FATAL_ERROR; - else - return ret; + return ret; } /* Read application data from the peer without removing it. @@ -467,12 +607,19 @@ static int wolfSSL_read_internal(WOLFSSL* ssl, void* data, size_t sz, int peek) */ int wolfSSL_peek(WOLFSSL* ssl, void* data, int sz) { + int ret; + WOLFSSL_ENTER("wolfSSL_peek"); - if (sz < 0) - return BAD_FUNC_ARG; + /* Validate parameter. */ + if (sz < 0) { + ret = BAD_FUNC_ARG; + } + else { + ret = wolfSSL_read_internal(ssl, data, (size_t)sz, TRUE); + } - return wolfSSL_read_internal(ssl, data, (size_t)sz, TRUE); + return ret; } /* Read application data from the peer. @@ -489,44 +636,71 @@ int wolfSSL_peek(WOLFSSL* ssl, void* data, int sz) WOLFSSL_ABI int wolfSSL_read(WOLFSSL* ssl, void* data, int sz) { - WOLFSSL_ENTER("wolfSSL_read"); + int ret; - if (sz < 0) - return BAD_FUNC_ARG; + WOLFSSL_ENTER("wolfSSL_read"); - #ifdef OPENSSL_EXTRA - if (ssl == NULL) { - return BAD_FUNC_ARG; + /* Validate parameters. */ + if (sz < 0) { + ret = BAD_FUNC_ARG; } - if (ssl->CBIS != NULL) { - ssl->CBIS(ssl, WOLFSSL_CB_READ, WOLFSSL_SUCCESS); - ssl->cbmode = WOLFSSL_CB_READ; + #ifdef OPENSSL_EXTRA + else if (ssl == NULL) { + ret = BAD_FUNC_ARG; } #endif - return wolfSSL_read_internal(ssl, data, (size_t)sz, FALSE); + else { + #ifdef OPENSSL_EXTRA + if (ssl->CBIS != NULL) { + ssl->CBIS(ssl, WOLFSSL_CB_READ, WOLFSSL_SUCCESS); + ssl->cbmode = WOLFSSL_CB_READ; + } + #endif + ret = wolfSSL_read_internal(ssl, data, (size_t)sz, FALSE); + } + + return ret; } -/* returns 0 on failure and 1 on read */ +/* Read application data from the peer and report whether any was read. + * + * @param [in, out] ssl SSL/TLS object. + * @param [out] data Buffer to hold application data. + * @param [in] sz Length of buffer in bytes. + * @param [out] rd Number of bytes read. May be NULL. Only set when + * data was read. + * @return 1 when application data was read. + * @return 0 when no application data was read. Call wolfSSL_get_error() for + * the reason. + * @return BAD_FUNC_ARG when ssl is NULL. + */ int wolfSSL_read_ex(WOLFSSL* ssl, void* data, size_t sz, size_t* rd) { int ret; - #ifdef OPENSSL_EXTRA + /* Validate parameter. Checked unconditionally so the guarded branch does + * not leave a standalone block, and so every entry point in this file + * rejects a NULL object the same way. */ if (ssl == NULL) { - return BAD_FUNC_ARG; - } - if (ssl->CBIS != NULL) { - ssl->CBIS(ssl, WOLFSSL_CB_READ, WOLFSSL_SUCCESS); - ssl->cbmode = WOLFSSL_CB_READ; + ret = BAD_FUNC_ARG; } - #endif - ret = wolfSSL_read_internal(ssl, data, sz, FALSE); + else { + #ifdef OPENSSL_EXTRA + if (ssl->CBIS != NULL) { + ssl->CBIS(ssl, WOLFSSL_CB_READ, WOLFSSL_SUCCESS); + ssl->cbmode = WOLFSSL_CB_READ; + } + #endif + ret = wolfSSL_read_internal(ssl, data, sz, FALSE); + + if ((ret > 0) && (rd != NULL)) { + *rd = (size_t)ret; + } - if (ret > 0 && rd != NULL) { - *rd = (size_t)ret; + ret = (ret > 0) ? 1 : 0; } - return ret > 0 ? 1 : 0; + return ret; } #ifndef WOLFSSL_LEANPSK @@ -546,20 +720,22 @@ int wolfSSL_read_ex(WOLFSSL* ssl, void* data, size_t sz, size_t* rd) int wolfSSL_send(WOLFSSL* ssl, const void* data, int sz, int flags) { int ret; - int oldFlags; WOLFSSL_ENTER("wolfSSL_send"); - if (ssl == NULL || data == NULL || sz < 0) - return BAD_FUNC_ARG; - - oldFlags = ssl->wflags; + /* Validate parameters. */ + if ((ssl == NULL) || (data == NULL) || (sz < 0)) { + ret = BAD_FUNC_ARG; + } + else { + int oldFlags = ssl->wflags; - ssl->wflags = flags; - ret = wolfSSL_write(ssl, data, sz); - ssl->wflags = oldFlags; + ssl->wflags = flags; + ret = wolfSSL_write(ssl, data, sz); + ssl->wflags = oldFlags; - WOLFSSL_LEAVE("wolfSSL_send", ret); + WOLFSSL_LEAVE("wolfSSL_send", ret); + } return ret; } @@ -579,20 +755,22 @@ int wolfSSL_send(WOLFSSL* ssl, const void* data, int sz, int flags) int wolfSSL_recv(WOLFSSL* ssl, void* data, int sz, int flags) { int ret; - int oldFlags; WOLFSSL_ENTER("wolfSSL_recv"); - if (ssl == NULL || data == NULL || sz < 0) - return BAD_FUNC_ARG; - - oldFlags = ssl->rflags; + /* Validate parameters. */ + if ((ssl == NULL) || (data == NULL) || (sz < 0)) { + ret = BAD_FUNC_ARG; + } + else { + int oldFlags = ssl->rflags; - ssl->rflags = flags; - ret = wolfSSL_read(ssl, data, sz); - ssl->rflags = oldFlags; + ssl->rflags = flags; + ret = wolfSSL_read(ssl, data, sz); + ssl->rflags = oldFlags; - WOLFSSL_LEAVE("wolfSSL_recv", ret); + WOLFSSL_LEAVE("wolfSSL_recv", ret); + } return ret; } @@ -625,127 +803,239 @@ int wolfSSL_SendUserCanceled(WOLFSSL* ssl) return ret; } -/* WOLFSSL_SUCCESS on ok */ -WOLFSSL_ABI -int wolfSSL_shutdown(WOLFSSL* ssl) +/* Flush an alert still sitting in the output buffer. + * + * A previous call may have left the close_notify alert buffered when the + * transport reported WANT_WRITE. Get it out before doing anything else. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in, out] ret Result for wolfSSL_shutdown() to return. Updated as + * the shutdown progresses. + * @return 1 when the shutdown is finished and ret holds the result. + * @return 0 when the shutdown must continue. + */ +static int wolfssl_shutdown_flush_alert(WOLFSSL* ssl, int* ret) { - int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR); - WOLFSSL_ENTER("wolfSSL_shutdown"); + int done = 0; - if (ssl == NULL) - return WOLFSSL_FATAL_ERROR; - - if (ssl->options.quietShutdown) { - WOLFSSL_MSG("quiet shutdown, no close notify sent"); - ret = WOLFSSL_SUCCESS; - } - else { + if ((ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) && + (ssl->buffers.outputBuffer.length > 0)) { + int rc = SendBuffered(ssl); - /* Try to flush the buffer first, it might contain the alert */ - if (ssl->error == WC_NO_ERR_TRACE(WANT_WRITE) && - ssl->buffers.outputBuffer.length > 0) { - ret = SendBuffered(ssl); - if (ret != 0) { - ssl->error = ret; - /* for error tracing */ - if (ret != WC_NO_ERR_TRACE(WANT_WRITE)) - WOLFSSL_ERROR(ret); - ret = WOLFSSL_FATAL_ERROR; - WOLFSSL_LEAVE("wolfSSL_shutdown", ret); - return ret; + if (rc != 0) { + ssl->error = rc; + /* for error tracing */ + if (rc != WC_NO_ERR_TRACE(WANT_WRITE)) { + WOLFSSL_ERROR(rc); } - + *ret = WOLFSSL_FATAL_ERROR; + done = 1; + } + else { ssl->error = WOLFSSL_ERROR_NONE; /* we succeeded in sending the alert now */ if (ssl->options.sentNotify) { - /* just after we send the alert, if we didn't receive the alert - * from the other peer yet, return WOLFSSL_STHUDOWN_NOT_DONE */ + /* just after we send the alert, if we didn't receive the + * alert from the other peer yet, return + * WOLFSSL_SHUTDOWN_NOT_DONE */ if (!ssl->options.closeNotify) { - ret = WOLFSSL_SHUTDOWN_NOT_DONE; - WOLFSSL_LEAVE("wolfSSL_shutdown", ret); - return ret; + *ret = WOLFSSL_SHUTDOWN_NOT_DONE; + done = 1; } else { ssl->options.shutdownDone = 1; - ret = WOLFSSL_SUCCESS; + *ret = WOLFSSL_SUCCESS; } } } + } - /* try to send close notify, not an error if can't */ - if (!ssl->options.isClosed && !ssl->options.connReset && - !ssl->options.sentNotify) { - ssl->error = SendAlert(ssl, alert_warning, close_notify); + return done; +} - /* the alert is now sent or sitting in the buffer, - * where will be sent eventually */ - if (ssl->error == 0 || ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) - ssl->options.sentNotify = 1; +/* Send the close_notify alert to the peer. + * + * Not being able to send it right away is not an error - the alert is left in + * the output buffer and goes out eventually. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in, out] ret Result for wolfSSL_shutdown() to return. Updated as + * the shutdown progresses. + * @return 1 when the shutdown is finished and ret holds the result. + * @return 0 when the shutdown must continue. + */ +static int wolfssl_shutdown_send_close_notify(WOLFSSL* ssl, int* ret) +{ + int done = 0; + + /* try to send close notify, not an error if can't */ + if ((!ssl->options.isClosed) && (!ssl->options.connReset) && + (!ssl->options.sentNotify)) { + ssl->error = SendAlert(ssl, alert_warning, close_notify); + + /* the alert is now sent or sitting in the buffer, + * where will be sent eventually */ + if ((ssl->error == 0) || + (ssl->error == WC_NO_ERR_TRACE(WANT_WRITE))) { + ssl->options.sentNotify = 1; + } - if (ssl->error < 0) { - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } + if (ssl->error < 0) { + WOLFSSL_ERROR(ssl->error); + *ret = WOLFSSL_FATAL_ERROR; + done = 1; + } + else if (ssl->options.closeNotify) { + *ret = WOLFSSL_SUCCESS; + ssl->options.shutdownDone = 1; + } + else { + *ret = WOLFSSL_SHUTDOWN_NOT_DONE; + done = 1; + } + } - if (ssl->options.closeNotify) { - ret = WOLFSSL_SUCCESS; - ssl->options.shutdownDone = 1; - } - else { - ret = WOLFSSL_SHUTDOWN_NOT_DONE; - WOLFSSL_LEAVE("wolfSSL_shutdown", ret); - return ret; - } + return done; +} + +/* Wait for the peer's close_notify alert to complete a bidirectional shutdown. + * + * Called when this side has sent its close_notify but has not seen the + * peer's, i.e. wolfSSL_shutdown() called again. + * + * @param [in, out] ssl SSL/TLS object. + * @return WOLFSSL_SUCCESS when the shutdown is complete. + * @return WOLFSSL_SHUTDOWN_NOT_DONE when the peer's alert has not arrived. + * @return WOLFSSL_FATAL_ERROR on error. Call wolfSSL_get_error() for the + * reason. + */ +static int wolfssl_shutdown_recv_close_notify(WOLFSSL* ssl) +{ + int ret; + + /* If there is still buffered application data waiting to be read, do not + * process incoming records here. clearOutputBuffer.buffer points into + * inputBuffer, and ProcessReply() may call GrowInputBuffer(), which frees + * and reallocates inputBuffer. Require the pending data to be drained + * first. */ + if (ssl->buffers.clearOutputBuffer.length > 0) { + WOLFSSL_MSG("Pending application data, read it before shutdown"); + ret = WOLFSSL_SHUTDOWN_NOT_DONE; + } + else { + ret = ProcessReply(ssl); + if ((ret == WC_NO_ERR_TRACE(ZERO_RETURN)) || + (ret == WC_NO_ERR_TRACE(SOCKET_ERROR_E))) { + /* simulate OpenSSL behavior */ + ssl->options.shutdownDone = 1; + /* Clear error */ + ssl->error = WOLFSSL_ERROR_NONE; + ret = WOLFSSL_SUCCESS; + } + else if (ret == WC_NO_ERR_TRACE(MEMORY_E)) { + ret = WOLFSSL_FATAL_ERROR; + } + else if (ret == WC_NO_ERR_TRACE(WANT_READ)) { + ssl->error = ret; + ret = WOLFSSL_FATAL_ERROR; + } + else if (ssl->error == WOLFSSL_ERROR_NONE) { + ret = WOLFSSL_SHUTDOWN_NOT_DONE; } + else { + WOLFSSL_ERROR(ssl->error); + ret = WOLFSSL_FATAL_ERROR; + } + } + + return ret; +} + +/* Shut the connection down by exchanging close_notify alerts with the peer. + * + * Call repeatedly while WOLFSSL_SHUTDOWN_NOT_DONE is returned to complete a + * bidirectional shutdown. + * + * @param [in, out] ssl SSL/TLS object. + * @return WOLFSSL_SUCCESS when the shutdown is complete. + * @return WOLFSSL_SHUTDOWN_NOT_DONE when the peer's close_notify has not + * been received yet. + * @return SSL_SHUTDOWN_ALREADY_DONE_E when the connection was already closed + * and WOLFSSL_SHUTDOWNONCE is defined. + * @return WOLFSSL_FATAL_ERROR when ssl is NULL or on error. Call + * wolfSSL_get_error() for the reason. + * + * SOCKET_PEER_CLOSED_E is reported when the connection was already + * closed or reset and no close_notify was ever sent, so the exchange + * can never complete. That covers this side closing by sending a fatal + * alert as much as the peer going away, and it is only used when no + * more specific error has been recorded. Under OPENSSL_EXTRA + * wolfSSL_get_error() reports it as WOLFSSL_ERROR_SYSCALL, so a locally + * aborted connection surfaces as a syscall error. This case used to return 0, + * which is WOLFSSL_SHUTDOWN_NOT_DONE under WOLFSSL_ERROR_CODE_OPENSSL, + * so a caller looping while the result is 0 never terminated. + */ +WOLFSSL_ABI +int wolfSSL_shutdown(WOLFSSL* ssl) +{ + int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR); -#ifdef WOLFSSL_SHUTDOWNONCE - if (ssl->options.isClosed || ssl->options.connReset) { + WOLFSSL_ENTER("wolfSSL_shutdown"); + + /* Validate parameter. */ + if (ssl == NULL) { + ret = WOLFSSL_FATAL_ERROR; + } + else if (ssl->options.quietShutdown) { + WOLFSSL_MSG("quiet shutdown, no close notify sent"); + ret = WOLFSSL_SUCCESS; + } + else { + int done; + + /* Try to flush the buffer first, it might contain the alert */ + done = wolfssl_shutdown_flush_alert(ssl, &ret); + if (!done) { + done = wolfssl_shutdown_send_close_notify(ssl, &ret); + } + + #ifdef WOLFSSL_SHUTDOWNONCE + if ((!done) && + ((ssl->options.isClosed) || (ssl->options.connReset))) { /* Shutdown has already occurred. * Caller is free to ignore this error. */ - return SSL_SHUTDOWN_ALREADY_DONE_E; + ret = SSL_SHUTDOWN_ALREADY_DONE_E; + done = 1; } -#endif + #endif /* wolfSSL_shutdown called again for bidirectional shutdown */ - if (ssl->options.sentNotify && !ssl->options.closeNotify) { - /* If there is still buffered application data waiting to be read, - * do not process incoming records here. clearOutputBuffer.buffer - * points into inputBuffer, and ProcessReply() may call - * GrowInputBuffer(), which frees and reallocates inputBuffer. - * Require the pending data to be drained first. */ - if (ssl->buffers.clearOutputBuffer.length > 0) { - WOLFSSL_MSG("Pending application data, read it before shutdown"); - ret = WOLFSSL_SHUTDOWN_NOT_DONE; - WOLFSSL_LEAVE("wolfSSL_shutdown", ret); - return ret; - } - ret = ProcessReply(ssl); - if ((ret == WC_NO_ERR_TRACE(ZERO_RETURN)) || - (ret == WC_NO_ERR_TRACE(SOCKET_ERROR_E))) { - /* simulate OpenSSL behavior */ - ssl->options.shutdownDone = 1; - /* Clear error */ - ssl->error = WOLFSSL_ERROR_NONE; - ret = WOLFSSL_SUCCESS; - } - else if (ret == WC_NO_ERR_TRACE(MEMORY_E)) { - ret = WOLFSSL_FATAL_ERROR; - } - else if (ret == WC_NO_ERR_TRACE(WANT_READ)) { - ssl->error = ret; - ret = WOLFSSL_FATAL_ERROR; - } - else if (ssl->error == WOLFSSL_ERROR_NONE) { - ret = WOLFSSL_SHUTDOWN_NOT_DONE; - } - else { - WOLFSSL_ERROR(ssl->error); - ret = WOLFSSL_FATAL_ERROR; + if ((!done) && (ssl->options.sentNotify) && + (!ssl->options.closeNotify)) { + ret = wolfssl_shutdown_recv_close_notify(ssl); + } + else if ((!done) && (!ssl->options.sentNotify) && + (ret != WOLFSSL_SUCCESS)) { + /* No close_notify was sent and the exchange has not completed by + * other means, so it never will. Record why when nothing else + * has, so the caller is not left with a failure and no error to + * query, but keep any more specific error already set. + * + * A send can report failure without setting sentNotify and still + * leave the shutdown complete: SendAlert() returns a positive + * value when a QUIC send_alert callback fails, which is neither + * the success nor the negative-error case the helper checks, and + * the peer's close_notify may already have arrived. Leave a + * success decided above alone. */ + WOLFSSL_MSG("Connection closed before close_notify was sent"); + if (ssl->error == WOLFSSL_ERROR_NONE) { + ssl->error = SOCKET_PEER_CLOSED_E; } + ret = WOLFSSL_FATAL_ERROR; } } -#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) + #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) /* reset WOLFSSL structure state for possible reuse */ if (ret == WOLFSSL_SUCCESS) { if (wolfSSL_clear(ssl) != WOLFSSL_SUCCESS) { @@ -753,7 +1043,7 @@ int wolfSSL_shutdown(WOLFSSL* ssl) ret = WOLFSSL_FATAL_ERROR; } } -#endif + #endif WOLFSSL_LEAVE("wolfSSL_shutdown", ret); @@ -761,18 +1051,31 @@ int wolfSSL_shutdown(WOLFSSL* ssl) } #endif /* !NO_TLS */ -/* +/* Get the number of bytes of decrypted application data ready to be read. + * * TODO This ssl parameter needs to be changed to const once our ABI checker * stops flagging qualifier additions as ABI breaking. + * + * @param [in] ssl SSL/TLS object. + * @return Number of buffered application data bytes. + * @return WOLFSSL_FAILURE when ssl is NULL. */ WOLFSSL_ABI int wolfSSL_pending(WOLFSSL* ssl) { + int ret; + WOLFSSL_ENTER("wolfSSL_pending"); - if (ssl == NULL) - return WOLFSSL_FAILURE; - return (int)ssl->buffers.clearOutputBuffer.length; + /* Validate parameter. */ + if (ssl == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + ret = (int)ssl->buffers.clearOutputBuffer.length; + } + + return ret; } /* Determine whether there is application data available to read. @@ -784,14 +1087,18 @@ int wolfSSL_pending(WOLFSSL* ssl) */ int wolfSSL_has_pending(const WOLFSSL* ssl) { - WOLFSSL_ENTER("wolfSSL_has_pending"); - if (ssl == NULL) - return WOLFSSL_FAILURE; + int ret = 0; - if (ssl->buffers.clearOutputBuffer.length > 0) - return 1; + WOLFSSL_ENTER("wolfSSL_has_pending"); -#ifdef WOLFSSL_TLS_READ_AHEAD + /* Validate parameter. */ + if (ssl == NULL) { + ret = WOLFSSL_FAILURE; + } + else if (ssl->buffers.clearOutputBuffer.length > 0) { + ret = 1; + } + #ifdef WOLFSSL_TLS_READ_AHEAD /* Read-ahead can leave undecrypted data buffered while the socket itself * has no more data. This may be a complete record or only a partial one * (e.g. a coalesced read that pulled a record plus the head of the next), @@ -799,101 +1106,167 @@ int wolfSSL_has_pending(const WOLFSSL* ssl) * application data without another socket read. Report it so a * select()/poll() loop keeps draining until wolfSSL_read() reports * WANT_READ, instead of stalling on buffered data. */ - if (ssl->buffers.inputBuffer.length > ssl->buffers.inputBuffer.idx) - return 1; -#endif - return 0; + else if (ssl->buffers.inputBuffer.length > ssl->buffers.inputBuffer.idx) { + ret = 1; + } + #endif + + return ret; } #ifndef USE_WINDOWS_API - #if !defined(NO_WRITEV) && !defined(NO_TLS) +#if !defined(NO_WRITEV) && !defined(NO_TLS) - /* simulate writev semantics, doesn't actually do block at a time though - because of SSL_write behavior and because front adds may be small */ - int wolfSSL_writev(WOLFSSL* ssl, const struct iovec* iov, int iovcnt) - { - #ifdef WOLFSSL_SMALL_STACK - byte staticBuffer[1]; /* force heap usage */ - #else - byte staticBuffer[FILE_BUFFER_SIZE]; - #endif - byte* myBuffer = staticBuffer; - int dynamic = 0; - size_t sending = 0; - size_t idx = 0; - int i; - int ret; - - WOLFSSL_ENTER("wolfSSL_writev"); - - for (i = 0; i < iovcnt; i++) - if (! WC_SAFE_SUM_UNSIGNED(size_t, sending, iov[i].iov_len, - sending)) - return BUFFER_E; - - if (sending > sizeof(staticBuffer)) { - myBuffer = (byte*)XMALLOC(sending, ssl->heap, - DYNAMIC_TYPE_WRITEV); - if (!myBuffer) - return MEMORY_ERROR; - - dynamic = 1; - } - - for (i = 0; i < iovcnt; i++) { - XMEMCPY(&myBuffer[idx], iov[i].iov_base, iov[i].iov_len); - idx += (int)iov[i].iov_len; - } +/* Write the data described by an array of iovecs to the peer. + * + * Simulates writev semantics, doesn't actually do block at a time though + * because of SSL_write behavior and because front adds may be small. The + * segments are gathered into one buffer and written as a single call. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] iov Array of buffers to write. + * @param [in] iovcnt Number of entries in iov. + * @return Number of bytes written on success. + * @return BAD_FUNC_ARG when ssl is NULL, iovcnt is negative, or iov is + * NULL with a non-zero iovcnt. + * @return BUFFER_E when the total length of the segments overflows. + * @return MEMORY_ERROR when the gather buffer cannot be allocated. + * @return WOLFSSL_FATAL_ERROR when the write fails. Call wolfSSL_get_error() + * for the reason. + */ +int wolfSSL_writev(WOLFSSL* ssl, const struct iovec* iov, int iovcnt) +{ + #ifdef WOLFSSL_SMALL_STACK + byte staticBuffer[1]; /* force heap usage */ + #else + byte staticBuffer[FILE_BUFFER_SIZE]; + #endif + byte* myBuffer = staticBuffer; + int dynamic = 0; + size_t sending = 0; + size_t idx = 0; + int i; + int ret = 0; + + WOLFSSL_ENTER("wolfSSL_writev"); + + /* Validate parameters before anything is read from the object. */ + if ((ssl == NULL) || ((iov == NULL) && (iovcnt != 0)) || (iovcnt < 0)) { + ret = BAD_FUNC_ARG; + } - /* myBuffer may not be initialized fully, but the span up to the - * sending length will be. - */ - PRAGMA_GCC_DIAG_PUSH - PRAGMA_GCC("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") - ret = wolfSSL_write_internal(ssl, myBuffer, sending); - PRAGMA_GCC_DIAG_POP + /* Total up the length being sent, checking for overflow. */ + for (i = 0; (ret == 0) && (i < iovcnt); i++) { + if (!WC_SAFE_SUM_UNSIGNED(size_t, sending, iov[i].iov_len, sending)) { + ret = BUFFER_E; + } + } - if (dynamic) - XFREE(myBuffer, ssl->heap, DYNAMIC_TYPE_WRITEV); + /* Gather into the stack buffer, or the heap when it doesn't fit. Small + * stack builds have a one byte buffer, so always take the heap. */ + if ((ret == 0) && (sending > sizeof(staticBuffer))) { + myBuffer = (byte*)XMALLOC(sending, ssl->heap, DYNAMIC_TYPE_WRITEV); + if (myBuffer == NULL) { + ret = MEMORY_ERROR; + } + else { + dynamic = 1; + } + } - return ret; + if (ret == 0) { + /* The loop below writes exactly the span that is read, but the + * compiler cannot see that. Where the write is inlined, the warning + * is raised against the SendData() call inside it, which the pragma + * below cannot reach - only a definite store here silences it. Do + * not remove: builds have failed twice without it. */ + myBuffer[0] = 0; + + for (i = 0; i < iovcnt; i++) { + XMEMCPY(&myBuffer[idx], iov[i].iov_base, iov[i].iov_len); + idx += iov[i].iov_len; } - #endif + + /* Covers the warning when it is raised at the call site instead. */ + PRAGMA_GCC_DIAG_PUSH + PRAGMA_GCC("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") + ret = wolfSSL_write_internal(ssl, myBuffer, sending); + PRAGMA_GCC_DIAG_POP + } + + /* Only set when the allocation above succeeded, so ssl is not NULL. */ + if (dynamic) { + XFREE(myBuffer, ssl->heap, DYNAMIC_TYPE_WRITEV); + } + + return ret; +} +#endif #endif #ifdef OPENSSL_EXTRA -/* returns SSL_WRITING, SSL_READING or SSL_NOTHING */ +/* Get the I/O operation the SSL/TLS object is waiting on. + * + * @param [in] ssl SSL/TLS object. + * @return WOLFSSL_READING when waiting for the transport to be readable. + * @return WOLFSSL_WRITING when waiting for the transport to be writable. + * @return WOLFSSL_NOTHING when not waiting on the transport or ssl is NULL. + */ int wolfSSL_want(WOLFSSL* ssl) { int rw_state = WOLFSSL_NOTHING; - if (ssl) { - if (ssl->error == WC_NO_ERR_TRACE(WANT_READ)) + + if (ssl != NULL) { + if (ssl->error == WC_NO_ERR_TRACE(WANT_READ)) { rw_state = WOLFSSL_READING; - else if (ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) + } + else if (ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) { rw_state = WOLFSSL_WRITING; + } } + return rw_state; } #endif -/* return TRUE if current error is want read */ +/* Determine whether the last operation is waiting for the transport to be + * readable. + * + * @param [in] ssl SSL/TLS object. + * @return 1 when the current error is want read. + * @return 0 otherwise, including when ssl is NULL. + */ int wolfSSL_want_read(WOLFSSL* ssl) { + int ret = 0; + WOLFSSL_ENTER("wolfSSL_want_read"); - if (ssl->error == WC_NO_ERR_TRACE(WANT_READ)) - return 1; - return 0; + if ((ssl != NULL) && (ssl->error == WC_NO_ERR_TRACE(WANT_READ))) { + ret = 1; + } + + return ret; } -/* return TRUE if current error is want write */ +/* Determine whether the last operation is waiting for the transport to be + * writable. + * + * @param [in] ssl SSL/TLS object. + * @return 1 when the current error is want write. + * @return 0 otherwise, including when ssl is NULL. + */ int wolfSSL_want_write(WOLFSSL* ssl) { + int ret = 0; + WOLFSSL_ENTER("wolfSSL_want_write"); - if (ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) - return 1; - return 0; + if ((ssl != NULL) && (ssl->error == WC_NO_ERR_TRACE(WANT_WRITE))) { + ret = 1; + } + + return ret; } /* Get the shutdown state of the connection. @@ -908,8 +1281,8 @@ int wolfSSL_get_shutdown(const WOLFSSL* ssl) WOLFSSL_ENTER("wolfSSL_get_shutdown"); - if (ssl) { -#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) + if (ssl != NULL) { + #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) if (ssl->options.shutdownDone) { /* The SSL object was possibly cleared with wolfSSL_clear after * a successful shutdown. Simulate a response for a full @@ -917,16 +1290,17 @@ int wolfSSL_get_shutdown(const WOLFSSL* ssl) isShutdown = WOLFSSL_SENT_SHUTDOWN | WOLFSSL_RECEIVED_SHUTDOWN; } else -#endif + #endif { /* in OpenSSL, WOLFSSL_SENT_SHUTDOWN = 1, when closeNotifySent * * WOLFSSL_RECEIVED_SHUTDOWN = 2, from close notify or fatal err */ - if (ssl->options.sentNotify) + if (ssl->options.sentNotify) { isShutdown |= WOLFSSL_SENT_SHUTDOWN; - if (ssl->options.closeNotify||ssl->options.connReset) + } + if ((ssl->options.closeNotify) || (ssl->options.connReset)) { isShutdown |= WOLFSSL_RECEIVED_SHUTDOWN; + } } - } WOLFSSL_LEAVE("wolfSSL_get_shutdown", isShutdown); diff --git a/src/ssl_p7p12.c b/src/ssl_p7p12.c index 27cf6c89635..2c2345b930d 100644 --- a/src/ssl_p7p12.c +++ b/src/ssl_p7p12.c @@ -1090,8 +1090,7 @@ int wolfSSL_PEM_write_bio_PKCS7(WOLFSSL_BIO* bio, PKCS7* p7) * RETURNS: * returns pointer to a PKCS7 structure on success, otherwise returns NULL */ -PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, - WOLFSSL_BIO** bcont) +PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, WOLFSSL_BIO** bcont) { MimeHdr* allHdrs = NULL; MimeHdr* curHdr = NULL; diff --git a/src/x509_str.c b/src/x509_str.c index 7bd2eb22f11..90de6756ff1 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -1876,7 +1876,15 @@ void* wolfSSL_X509_STORE_get_ex_data(WOLFSSL_X509_STORE* store, int idx) int wolfSSL_X509_STORE_up_ref(WOLFSSL_X509_STORE* store) { - if (store) { + if (store == NULL) { + return WOLFSSL_FAILURE; + } + + /* A store that is part of another object, such as the one in a context, + * is not reference counted - its reference count was never initialized + * and its lifetime is that of the object holding it. Nothing to do, as + * in wolfSSL_X509_STORE_free(). */ + if (store->isDynamic) { int ret; wolfSSL_RefInc(&store->ref, &ret); #ifdef WOLFSSL_REFCNT_ERROR_RETURN @@ -1887,11 +1895,9 @@ int wolfSSL_X509_STORE_up_ref(WOLFSSL_X509_STORE* store) #else (void)ret; #endif - - return WOLFSSL_SUCCESS; } - return WOLFSSL_FAILURE; + return WOLFSSL_SUCCESS; } /** diff --git a/tests/api.c b/tests/api.c index 6f20ccf6e75..3266d10e07f 100644 --- a/tests/api.c +++ b/tests/api.c @@ -256,8 +256,11 @@ #include #include #include +#include #include #include +#include +#include #include #include #include @@ -39265,8 +39268,11 @@ TEST_CASE testCases[] = { TEST_DTLS_DECLS, TEST_DTLS13_DECLS, TEST_SSL_CERT_DECLS, + TEST_SSL_CRL_OCSP_DECLS, TEST_SSL_PK_DECLS, TEST_SSL_EXT_DECLS, + TEST_SSL_RW_DECLS, + TEST_SSL_HS_DECLS, TEST_DECL(test_tls_multi_handshakes_one_record), TEST_DECL(test_write_dup), TEST_DECL(test_write_dup_want_write), diff --git a/tests/api/include.am b/tests/api/include.am index c03995735b1..97191f343cb 100644 --- a/tests/api/include.am +++ b/tests/api/include.am @@ -68,8 +68,11 @@ tests_unit_test_SOURCES += tests/api/test_lms_xmss.c tests_unit_test_SOURCES += tests/api/test_dtls.c tests_unit_test_SOURCES += tests/api/test_dtls13.c tests_unit_test_SOURCES += tests/api/test_ssl_cert.c +tests_unit_test_SOURCES += tests/api/test_ssl_crl_ocsp.c tests_unit_test_SOURCES += tests/api/test_ssl_pk.c tests_unit_test_SOURCES += tests/api/test_ssl_ext.c +tests_unit_test_SOURCES += tests/api/test_ssl_rw.c +tests_unit_test_SOURCES += tests/api/test_ssl_hs.c # TLS Feature tests_unit_test_SOURCES += tests/api/test_ocsp.c tests_unit_test_SOURCES += tests/api/test_evp.c @@ -194,8 +197,11 @@ EXTRA_DIST += tests/api/test_lms_xmss.h EXTRA_DIST += tests/api/test_dtls.h EXTRA_DIST += tests/api/test_dtls13.h EXTRA_DIST += tests/api/test_ssl_cert.h +EXTRA_DIST += tests/api/test_ssl_crl_ocsp.h EXTRA_DIST += tests/api/test_ssl_pk.h EXTRA_DIST += tests/api/test_ssl_ext.h +EXTRA_DIST += tests/api/test_ssl_rw.h +EXTRA_DIST += tests/api/test_ssl_hs.h EXTRA_DIST += tests/api/test_ocsp.h EXTRA_DIST += tests/api/test_ocsp_test_blobs.h EXTRA_DIST += tests/api/create_ocsp_test_blobs.py diff --git a/tests/api/test_ssl_cert.c b/tests/api/test_ssl_cert.c index 9ffd4e9356d..b4591c085e8 100644 --- a/tests/api/test_ssl_cert.c +++ b/tests/api/test_ssl_cert.c @@ -36,6 +36,10 @@ /* Tests for the certificate APIs in src/ssl_api_cert.c (moved from ssl.c). */ +/* Test reading back the verification mode from an object. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_get_verify_mode(void) { EXPECT_DECLS; @@ -74,6 +78,10 @@ int test_wolfSSL_get_verify_mode(void) return EXPECT_RESULT(); } +/* Test reading back the verification mode from a context. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_CTX_get_verify_mode(void) { EXPECT_DECLS; @@ -123,6 +131,12 @@ static int test_cert_verify_cb(int preverify, WOLFSSL_X509_STORE_CTX* store) } #endif +/* Test reading back the verification callback. + * + * The object inherits the context's callback until one is set on it. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_get_verify_callback(void) { EXPECT_DECLS; @@ -150,13 +164,20 @@ int test_wolfSSL_get_verify_callback(void) return EXPECT_RESULT(); } +/* Test getting the extra certificates loaded with the chain. + * + * The stack is only present once a chain file has been loaded. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_CTX_get_extra_chain_certs(void) { EXPECT_DECLS; #if (defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || \ defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && \ !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \ - !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) + !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && \ + defined(WOLFSSL_PEM_TO_DER) WOLFSSL_CTX* ctx = NULL; WOLF_STACK_OF(WOLFSSL_X509)* sk = NULL; @@ -211,6 +232,13 @@ int test_wolfSSL_CTX_get_extra_chain_certs(void) return EXPECT_RESULT(); } +/* Test walking the peer's certificate chain by index. + * + * Covers the count, the per-certificate length and DER accessors, and the + * alternative chain used with alternative certificates. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_get_peer_chain(void) { EXPECT_DECLS; @@ -220,6 +248,10 @@ int test_wolfSSL_get_peer_chain(void) WOLFSSL *ssl_c = NULL, *ssl_s = NULL; struct test_memio_ctx test_ctx; WOLFSSL_X509_CHAIN* chain = NULL; +#if (defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || \ + defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && defined(KEEP_OUR_CERT) + WOLF_STACK_OF(WOLFSSL_X509)* osk = NULL; +#endif /* NULL / not-yet-populated cases. */ ExpectNull(wolfSSL_get_peer_chain(NULL)); @@ -245,11 +277,8 @@ int test_wolfSSL_get_peer_chain(void) #if (defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || \ defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && defined(KEEP_OUR_CERT) - { - WOLF_STACK_OF(WOLFSSL_X509)* osk = NULL; - ExpectIntEQ(wolfSSL_get0_chain_certs(NULL, &osk), WOLFSSL_FAILURE); - ExpectIntEQ(wolfSSL_get0_chain_certs(ssl_c, &osk), WOLFSSL_SUCCESS); - } + ExpectIntEQ(wolfSSL_get0_chain_certs(NULL, &osk), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_get0_chain_certs(ssl_c, &osk), WOLFSSL_SUCCESS); #endif wolfSSL_free(ssl_s); @@ -260,6 +289,12 @@ int test_wolfSSL_get_peer_chain(void) return EXPECT_RESULT(); } +/* Test getting a peer chain certificate as an X509 object. + * + * The object returned is owned by the caller and must be freed. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_get_chain_X509(void) { EXPECT_DECLS; @@ -296,6 +331,12 @@ int test_wolfSSL_get_chain_X509(void) return EXPECT_RESULT(); } +/* Test converting a peer chain certificate to PEM. + * + * A NULL buffer reports the length needed rather than converting. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_get_chain_cert_pem(void) { EXPECT_DECLS; @@ -336,6 +377,9 @@ int test_wolfSSL_get_chain_cert_pem(void) /* NULL buffer returns the size needed (length-only query). */ needed = 0; + /* A negative buffer length is rejected. */ + ExpectIntEQ(wolfSSL_get_chain_cert_pem(chain, 0, pem, -1, &pemSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wolfSSL_get_chain_cert_pem(chain, 0, NULL, 0, &needed), WC_NO_ERR_TRACE(LENGTH_ONLY_E)); ExpectIntGT(needed, 0); @@ -366,13 +410,20 @@ int test_wolfSSL_get_chain_cert_pem(void) return EXPECT_RESULT(); } +/* Test comparing the peer's certificate against one in a file. + * + * The file is parsed as PEM, so it must be the PEM form of the same certificate + * to match. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_cmp_peer_cert_to_file(void) { EXPECT_DECLS; #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ defined(KEEP_PEER_CERT) && defined(HAVE_EX_DATA) && \ !defined(NO_FILESYSTEM) && !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) \ - && !defined(NO_TLS) + && !defined(NO_TLS) && defined(WOLFSSL_PEM_TO_DER) WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; struct test_memio_ctx test_ctx; @@ -407,3 +458,955 @@ int test_wolfSSL_cmp_peer_cert_to_file(void) #endif return EXPECT_RESULT(); } + +/* Guarded to match its only caller, test_wolfSSL_CTX_set_client_cert_cb(), + * which needs OPENSSL_EXTRA for the ctx->CBClientCert field. */ +#if defined(WOLFSSL_CERT_SETUP_CB) && defined(OPENSSL_EXTRA) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) +/* Client certificate callback that supplies nothing. + * + * @param [in] ssl SSL/TLS object. Unused. + * @param [out] x509 Certificate to use. Unused. + * @param [out] pkey Private key to use. Unused. + * @return 0 to indicate no certificate was supplied. + */ +static int test_ssl_cert_client_cert_cb(WOLFSSL* ssl, WOLFSSL_X509** x509, + WOLFSSL_EVP_PKEY** pkey) +{ + (void)ssl; + (void)x509; + (void)pkey; + return 0; +} +#endif + +/* Test setting the client certificate callback on a context. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_CTX_set_client_cert_cb(void) +{ + EXPECT_DECLS; +/* Reads ctx->CBClientCert, which the structure only has under + * OPENSSL_EXTRA, so this is narrower than the setter's own guard. */ +#if defined(WOLFSSL_CERT_SETUP_CB) && defined(OPENSSL_EXTRA) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx = NULL; + + /* A NULL context is ignored rather than faulting. */ + wolfSSL_CTX_set_client_cert_cb(NULL, test_ssl_cert_client_cert_cb); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + if (ctx != NULL) { + wolfSSL_CTX_set_client_cert_cb(ctx, test_ssl_cert_client_cert_cb); + ExpectTrue(ctx->CBClientCert == test_ssl_cert_client_cert_cb); + + /* The callback can be cleared again. */ + wolfSSL_CTX_set_client_cert_cb(ctx, NULL); + ExpectNull(ctx->CBClientCert); + } + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Guarded to cover both callers: test_wolfSSL_CTX_set_cert_cb() needs a + * server, and test_wolfSSL_cert_setup_cb_ret() needs a memio handshake. The + * counters move with the callback so they cannot go unused either. */ +#if defined(WOLFSSL_CERT_SETUP_CB) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && (!defined(NO_WOLFSSL_SERVER) || \ + (defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA))) +static int test_ssl_cert_setup_ret = 1; +static int test_ssl_cert_setup_calls = 0; + +/* Certificate setup callback returning a value chosen by the test. + * + * @param [in] ssl SSL/TLS object. Unused. + * @param [in] arg Context passed when the callback was set. Unused. + * @return The value in test_ssl_cert_setup_ret. + */ +static int test_ssl_cert_setup_cb(WOLFSSL* ssl, void* arg) +{ + (void)ssl; + (void)arg; + test_ssl_cert_setup_calls++; + return test_ssl_cert_setup_ret; +} +#endif + +/* Test setting the certificate setup callback on a context. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_CTX_set_cert_cb(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_SETUP_CB) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx = NULL; + int arg = 0; + + /* A NULL context is ignored rather than faulting. */ + wolfSSL_CTX_set_cert_cb(NULL, test_ssl_cert_setup_cb, &arg); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + if (ctx != NULL) { + wolfSSL_CTX_set_cert_cb(ctx, test_ssl_cert_setup_cb, &arg); + ExpectTrue(ctx->certSetupCb == test_ssl_cert_setup_cb); + ExpectPtrEq(ctx->certSetupCbArg, &arg); + + /* Both the callback and its context can be cleared. */ + wolfSSL_CTX_set_cert_cb(ctx, NULL, NULL); + ExpectNull(ctx->certSetupCb); + ExpectNull(ctx->certSetupCbArg); + } + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test how the return value of the certificate setup callback is handled. + * + * The callback is called on the server while the ClientHello is processed, so + * each return value is observed as the outcome of the handshake. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_cert_setup_cb_ret(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_SETUP_CB) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && !defined(NO_TLS) + /* cbRet is what the callback returns. err is the error the server then + * reports: 0 means the handshake is expected to complete, and -1 means + * only that it must fail. + * + * A negative callback return makes the wrapper report + * WOLFSSL_ERROR_WANT_X509_LOOKUP, which is a positive value and so does + * not reach wolfSSL_get_error(). Only the failure is checked for that + * case rather than the code that happens to surface. */ + static const struct { + int cbRet; + int err; + } cases[] = { + { 1, 0 }, + { 0, WC_NO_ERR_TRACE(CLIENT_CERT_CB_ERROR) }, + { -1, -1 }, + { 2, WC_NO_ERR_TRACE(CLIENT_CERT_CB_ERROR) } + }; + int i; + + for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) { + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + test_ssl_cert_setup_ret = cases[i].cbRet; + test_ssl_cert_setup_calls = 0; + wolfSSL_CTX_set_cert_cb(ctx_s, test_ssl_cert_setup_cb, NULL); + + if (cases[i].err == 0) { + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + } + else { + /* Drive one step at a time so the error the server reports is the + * one the callback caused, not a later I/O failure. */ + ExpectIntEQ(wolfSSL_connect(ssl_c), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_accept(ssl_s), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + if (cases[i].err != -1) { + ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), cases[i].err); + } + else { + ExpectIntNE(wolfSSL_get_error(ssl_s, 0), 0); + } + } + /* The callback ran regardless of what it reported. */ + ExpectIntGT(test_ssl_cert_setup_calls, 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } + test_ssl_cert_setup_ret = 1; +#endif + return EXPECT_RESULT(); +} + +/* Test getting the stack of the peer's certificates. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_get_peer_cert_chain(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(SESSION_CERTS) && \ + defined(OPENSSL_EXTRA) && !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) \ + && !defined(NO_TLS) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + WOLF_STACK_OF(WOLFSSL_X509)* sk = NULL; + + ExpectNull(wolfSSL_get_peer_cert_chain(NULL)); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + /* Nothing has been received yet, so there is no chain to build. */ + ExpectNull(wolfSSL_get_peer_cert_chain(ssl_c)); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* The client now holds the server's chain. */ + ExpectNotNull(sk = wolfSSL_get_peer_cert_chain(ssl_c)); + ExpectIntGT(wolfSSL_sk_X509_num(sk), 0); + /* The stack is owned by the object, so asking again returns the same one + * rather than building another. */ + ExpectPtrEq(wolfSSL_get_peer_cert_chain(ssl_c), sk); + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); +#endif + return EXPECT_RESULT(); +} + +/* Test building the stack of the peer's certificates. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_set_peer_cert_chain(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(SESSION_CERTS) && \ + defined(OPENSSL_EXTRA) && !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) \ + && !defined(NO_TLS) && !defined(NO_FILESYSTEM) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + WOLF_STACK_OF(WOLFSSL_X509)* sk = NULL; + + ExpectNull(wolfSSL_set_peer_cert_chain(NULL)); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + /* An empty session chain has nothing to build from. */ + ExpectNull(wolfSSL_set_peer_cert_chain(ssl_c)); + + /* Ask for a client certificate so the server also ends up with a chain. + * The credentials go on the object because test_memio_setup() has already + * created it from the context. WOLFSSL_NO_CLIENT_AUTH compiles out the + * client's Certificate message, so there is nothing to ask for. */ +#ifndef WOLFSSL_NO_CLIENT_AUTH + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL); +#endif + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Client side: the chain is stored on the object. */ + ExpectNotNull(sk = wolfSSL_set_peer_cert_chain(ssl_c)); + if (ssl_c != NULL) { + ExpectPtrEq(ssl_c->peerCertChain, sk); + } + /* Called again the old chain is released and a new one stored. */ + ExpectNotNull(sk = wolfSSL_set_peer_cert_chain(ssl_c)); + if (ssl_c != NULL) { + ExpectPtrEq(ssl_c->peerCertChain, sk); + } + +#ifndef WOLFSSL_NO_CLIENT_AUTH + /* Server side: the leaf is moved out of the stack into the session. */ + ExpectNotNull(wolfSSL_set_peer_cert_chain(ssl_s)); + if (ssl_s != NULL) { + ExpectNotNull(ssl_s->session->peer); + } + /* Building it again releases the peer stored by the previous call. */ + ExpectNotNull(wolfSSL_set_peer_cert_chain(ssl_s)); + if (ssl_s != NULL) { + ExpectNotNull(ssl_s->session->peer); + } +#else + /* With no client certificate the server has no chain to build. */ + ExpectNull(wolfSSL_set_peer_cert_chain(ssl_s)); +#endif + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); +#endif + return EXPECT_RESULT(); +} + +/* Test getting the verified certificate chain. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_get0_verified_chain(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(SESSION_CERTS) && \ + defined(OPENSSL_EXTRA) && defined(KEEP_PEER_CERT) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && !defined(NO_TLS) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + WOLF_STACK_OF(WOLFSSL_X509)* chain = NULL; + + ExpectNull(wolfSSL_get0_verified_chain(NULL)); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + /* Without a peer certificate there is nothing to verify. */ + ExpectNull(wolfSSL_get0_verified_chain(ssl_c)); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* The server's chain verifies against the CA the client loaded. */ + ExpectNotNull(chain = wolfSSL_get0_verified_chain(ssl_c)); + ExpectIntGT(wolfSSL_sk_X509_num(chain), 0); + if (ssl_c != NULL) { + ExpectPtrEq(ssl_c->verifiedChain, chain); + } + /* Called again the previous chain is released and a new one stored. */ + ExpectNotNull(chain = wolfSSL_get0_verified_chain(ssl_c)); + if (ssl_c != NULL) { + ExpectPtrEq(ssl_c->verifiedChain, chain); + } + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); +#endif + return EXPECT_RESULT(); +} + +/* Test adding certificate subject names to the CA name lists. + * + * Covers the context and object variants of both the client-CA list and the + * general CA list, and the shared helper that appends to a list. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_CA_list_add(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_SERVER) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL_X509* x509 = NULL; + + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(caCertFile, + WOLFSSL_FILETYPE_PEM)); + + /* Both arguments are required. */ + ExpectIntEQ(wolfSSL_CTX_add_client_CA(NULL, x509), 0); + ExpectIntEQ(wolfSSL_add_client_CA(NULL, x509), 0); + ExpectIntEQ(wolfSSL_CTX_add1_to_CA_list(NULL, x509), 0); + ExpectIntEQ(wolfSSL_add1_to_CA_list(NULL, x509), 0); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + ExpectIntEQ(wolfSSL_CTX_add_client_CA(ctx, NULL), 0); + ExpectIntEQ(wolfSSL_add_client_CA(ssl, NULL), 0); + ExpectIntEQ(wolfSSL_CTX_add1_to_CA_list(ctx, NULL), 0); + ExpectIntEQ(wolfSSL_add1_to_CA_list(ssl, NULL), 0); + + /* The first call creates the list, the second appends to it. The object's + * lists are filled first as, while empty, they resolve to the context's. */ + ExpectNull(wolfSSL_get_client_CA_list(ssl)); + ExpectIntEQ(wolfSSL_add_client_CA(ssl, x509), 1); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_get_client_CA_list(ssl)), 1); + ExpectIntEQ(wolfSSL_add_client_CA(ssl, x509), 1); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_get_client_CA_list(ssl)), 2); + + ExpectNull(wolfSSL_CTX_get_client_CA_list(ctx)); + ExpectIntEQ(wolfSSL_CTX_add_client_CA(ctx, x509), 1); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num( + wolfSSL_CTX_get_client_CA_list(ctx)), 1); + ExpectIntEQ(wolfSSL_CTX_add_client_CA(ctx, x509), 1); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num( + wolfSSL_CTX_get_client_CA_list(ctx)), 2); + + ExpectNull(wolfSSL_get0_CA_list(ssl)); + ExpectIntEQ(wolfSSL_add1_to_CA_list(ssl, x509), 1); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_get0_CA_list(ssl)), 1); + ExpectIntEQ(wolfSSL_add1_to_CA_list(ssl, x509), 1); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_get0_CA_list(ssl)), 2); + + ExpectNull(wolfSSL_CTX_get0_CA_list(ctx)); + ExpectIntEQ(wolfSSL_CTX_add1_to_CA_list(ctx, x509), 1); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_CTX_get0_CA_list(ctx)), 1); + ExpectIntEQ(wolfSSL_CTX_add1_to_CA_list(ctx, x509), 1); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_CTX_get0_CA_list(ctx)), 2); + + wolfSSL_X509_free(x509); + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test retrieving the CA name lists. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_CA_list_get(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_WOLFSSL_CLIENT) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL_X509* x509 = NULL; + + /* A NULL object has no list. */ + ExpectNull(wolfSSL_CTX_get_client_CA_list(NULL)); + ExpectNull(wolfSSL_get_client_CA_list(NULL)); + ExpectNull(wolfSSL_CTX_get0_CA_list(NULL)); + ExpectNull(wolfSSL_get0_CA_list(NULL)); + ExpectNull(wolfSSL_get0_peer_CA_list(NULL)); + + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(caCertFile, + WOLFSSL_FILETYPE_PEM)); + + /* Server side: the client CA names are the object's own list. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Nothing added yet. */ + ExpectNull(wolfSSL_CTX_get0_CA_list(ctx)); + ExpectNull(wolfSSL_get0_CA_list(ssl)); + /* No hello has been received, so there are no peer names. */ + ExpectNull(wolfSSL_get0_peer_CA_list(ssl)); + + ExpectIntEQ(wolfSSL_CTX_add_client_CA(ctx, x509), 1); + ExpectIntEQ(wolfSSL_CTX_add1_to_CA_list(ctx, x509), 1); + ExpectIntEQ(wolfSSL_add_client_CA(ssl, x509), 1); + ExpectIntEQ(wolfSSL_add1_to_CA_list(ssl, x509), 1); + + ExpectNotNull(wolfSSL_CTX_get_client_CA_list(ctx)); + ExpectNotNull(wolfSSL_get_client_CA_list(ssl)); + ExpectNotNull(wolfSSL_CTX_get0_CA_list(ctx)); + ExpectNotNull(wolfSSL_get0_CA_list(ssl)); + + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; + + /* Client side: the client CA names come from the peer instead. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectNull(wolfSSL_get_client_CA_list(ssl)); + + wolfSSL_X509_free(x509); + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test reading a list of CA names from a file. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_load_client_CA_file(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) && \ + !defined(NO_BIO) && defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && \ + !defined(NO_RSA) && defined(WOLFSSL_PEM_TO_DER) + WOLF_STACK_OF(WOLFSSL_X509_NAME)* names = NULL; + + /* A file that cannot be opened reports no names. */ + ExpectNull(wolfSSL_load_client_CA_file("does/not/exist.pem")); + + /* Every certificate in the file contributes its subject name. */ + ExpectNotNull(names = wolfSSL_load_client_CA_file(caCertFile)); + ExpectIntGT(wolfSSL_sk_X509_NAME_num(names), 0); + wolfSSL_sk_X509_NAME_pop_free(names, NULL); +#endif + return EXPECT_RESULT(); +} + +/* Test requiring mutual authentication. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_mutual_auth(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ExpectIntEQ(wolfSSL_CTX_mutual_auth(NULL, 1), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_mutual_auth(NULL, 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* Mutual authentication is a server-only setting. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(wolfSSL_CTX_mutual_auth(ctx, 1), WC_NO_ERR_TRACE(SIDE_ERROR)); + ExpectIntEQ(wolfSSL_mutual_auth(ssl, 1), WC_NO_ERR_TRACE(SIDE_ERROR)); + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + ExpectIntEQ(wolfSSL_CTX_mutual_auth(ctx, 1), 0); + ExpectIntEQ(wolfSSL_mutual_auth(ssl, 1), 0); + /* The setting can be turned back off. */ + ExpectIntEQ(wolfSSL_CTX_mutual_auth(ctx, 0), 0); + ExpectIntEQ(wolfSSL_mutual_auth(ssl, 0), 0); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test enabling post-handshake authentication. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_post_handshake_auth(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && defined(OPENSSL_EXTRA) && defined(WOLFSSL_TLS13) && \ + defined(WOLFSSL_POST_HANDSHAKE_AUTH) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + /* A TLS 1.3 client may ask to be authenticated after the handshake. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(wolfSSL_CTX_set_post_handshake_auth(ctx, 1), 1); + ExpectIntEQ(wolfSSL_set_post_handshake_auth(ssl, 1), 1); + /* And can turn it back off. */ + ExpectIntEQ(wolfSSL_CTX_set_post_handshake_auth(ctx, 0), 1); + ExpectIntEQ(wolfSSL_set_post_handshake_auth(ssl, 0), 1); + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; + + /* A server cannot request it of itself. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(wolfSSL_CTX_set_post_handshake_auth(ctx, 1), 0); + ExpectIntEQ(wolfSSL_set_post_handshake_auth(ssl, 1), 0); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test setting the certificate store used for verification. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_verify_cert_store(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && defined(OPENSSL_ALL) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_CLIENT) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL_X509_STORE* store = NULL; + WOLFSSL_X509_STORE* store2 = NULL; + WOLFSSL_X509_STORE* store3 = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* The object being set is required. */ + ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(NULL, NULL), 0); + ExpectIntEQ(wolfSSL_set0_verify_cert_store(NULL, NULL), 0); + ExpectIntEQ(wolfSSL_set1_verify_cert_store(NULL, NULL), 0); + + /* A NULL store clears, so clearing when none is set succeeds and does + * nothing. */ + ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, NULL), 1); + ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl, NULL), 1); + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, NULL), 1); + + /* The store a context owns is not reference counted - its lifetime is + * the context's. Taking a reference on it succeeds without touching a + * count that was never initialized, and releasing it does nothing. */ + ExpectIntEQ(wolfSSL_X509_STORE_up_ref(wolfSSL_CTX_get_cert_store(ctx)), 1); + wolfSSL_X509_STORE_free(wolfSSL_CTX_get_cert_store(ctx)); + ExpectNotNull(wolfSSL_CTX_get_cert_store(ctx)); + /* There is no store to take a reference on. */ + ExpectIntEQ(wolfSSL_X509_STORE_up_ref(NULL), 0); + + /* Setting the store already in use is accepted and changes nothing, both + * for the context and for an object handed the store the context owns. */ + ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, + wolfSSL_CTX_get_cert_store(ctx)), 1); + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, + wolfSSL_CTX_get_cert_store(ctx)), 1); + + /* A different store is taken with a reference. */ + ExpectNotNull(store = wolfSSL_X509_STORE_new()); + ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, store), 1); + + /* Give the object a store of its own, then hand it the context's store: + * it drops its own and goes back to using the context's. */ + ExpectNotNull(store2 = wolfSSL_X509_STORE_new()); + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store2), 1); + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store), 1); + + /* set0 hands a reference over and always consumes it, so the caller must + * own one for every call. The object keeps the first. */ + ExpectNotNull(store3 = wolfSSL_X509_STORE_new()); + ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl, store3), 1); + + /* Setting the same store again consumes the reference handed over rather + * than keeping a second pointer to it. The object's own is untouched, so + * the store stays alive. */ + ExpectIntEQ(wolfSSL_X509_STORE_up_ref(store3), 1); + ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl, store3), 1); + + /* Handing over the context's store drops the object's own and reverts it + * to the context's, consuming the reference taken just above. The + * context keeps its own, and frees the store on its own teardown. */ + ExpectIntEQ(wolfSSL_X509_STORE_up_ref(store), 1); + ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl, + wolfSSL_CTX_get_cert_store(ctx)), 1); + ExpectPtrEq(wolfSSL_CTX_get_cert_store(ctx), store); + + /* The object is back on the context's store, so setting that same store + * with set1 takes no reference and releases none. */ + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store), 1); + ExpectPtrEq(wolfSSL_CTX_get_cert_store(ctx), store); + + /* Give the object a store of its own again and clear it: the object + * releases its reference and reverts to the context's store. The store + * itself is still alive, so a reference can still be taken on it. */ + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store2), 1); + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, NULL), 1); + ExpectIntEQ(wolfSSL_X509_STORE_up_ref(store2), 1); + wolfSSL_X509_STORE_free(store2); + + /* set0 clears the same way - no reference is handed over with a NULL + * store, so there is none to consume. */ + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store2), 1); + ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl, NULL), 1); + + /* Clearing the context's store releases its reference and reverts it to + * the store it owns. */ + ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, NULL), 1); + ExpectPtrNE(wolfSSL_CTX_get_cert_store(ctx), store); + + /* Release the references this test created. */ + wolfSSL_X509_STORE_free(store); + wolfSSL_X509_STORE_free(store2); + + /* A NULL context has no store. */ + ExpectNull(wolfSSL_CTX_get_cert_store(NULL)); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test which store an object uses for verification. + * + * An object with no store of its own uses the context's, and keeps no pointer + * to it, so it follows the context when the context's store changes. A store + * set on the object takes precedence until cleared. + * + * Each store has its own certificate manager, and only one of them is given + * the CA that signed the CRL. Loading a CRL through the object goes to the + * manager of the store the object resolves to, so whether the load succeeds + * says which store that is. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_verify_cert_store_follows_ctx(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && defined(OPENSSL_ALL) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_RSA) && defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL_X509_STORE* noCa = NULL; + WOLFSSL_X509_STORE* noCa2 = NULL; + WOLFSSL_X509_STORE* withCa = NULL; + WOLFSSL_X509_STORE* withCa2 = NULL; + /* Two CRLs from the same CA. A manager keeps the CRLs tried against it, + * whether or not they verified, so no manager below is asked for the same + * CRL twice - a second attempt would find the cached one and succeed. */ + const char* crlPem = "./certs/crl/crl.pem"; + const char* crlPem2 = "./certs/crl/crl.revoked"; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + ExpectNotNull(noCa = wolfSSL_X509_STORE_new()); + ExpectNotNull(noCa2 = wolfSSL_X509_STORE_new()); + ExpectNotNull(withCa = wolfSSL_X509_STORE_new()); + ExpectNotNull(withCa2 = wolfSSL_X509_STORE_new()); + ExpectIntEQ(wolfSSL_X509_STORE_load_locations(withCa, caCertFile, NULL), 1); + ExpectIntEQ(wolfSSL_X509_STORE_load_locations(withCa2, caCertFile, NULL), + 1); + + /* Hand the object the store the context is using while that is the store + * the context owns: no pointer to it is kept either. */ + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, + wolfSSL_CTX_get_cert_store(ctx)), 1); + /* So the object follows the context onto a store that has the CA. Were + * the context's own store pinned to the object instead, the manager in + * use would still be the context's, which has no CA. */ + ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, withCa), 1); + ExpectIntEQ(wolfSSL_LoadCRLFile(ssl, crlPem, WOLFSSL_FILETYPE_PEM), 1); + + /* Same again for a store the context was given rather than owns. */ + ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, noCa), 1); + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, noCa), 1); + /* That store has no CA to verify the CRL against. */ + ExpectIntNE(wolfSSL_LoadCRLFile(ssl, crlPem2, WOLFSSL_FILETYPE_PEM), 1); + /* Changing the context's store changes the one the object uses. Were the + * store above pinned to the object instead, it would still be in use. */ + ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, withCa2), 1); + ExpectIntEQ(wolfSSL_LoadCRLFile(ssl, crlPem, WOLFSSL_FILETYPE_PEM), 1); + + /* A store set on the object is used ahead of the context's. */ + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, noCa2), 1); + ExpectIntNE(wolfSSL_LoadCRLFile(ssl, crlPem, WOLFSSL_FILETYPE_PEM), 1); + + /* Clearing it puts the object back on the context's store. */ + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, NULL), 1); + ExpectIntEQ(wolfSSL_LoadCRLFile(ssl, crlPem2, WOLFSSL_FILETYPE_PEM), 1); + + /* Release the references this test created. */ + wolfSSL_X509_STORE_free(noCa); + wolfSSL_X509_STORE_free(noCa2); + wolfSSL_X509_STORE_free(withCa); + wolfSSL_X509_STORE_free(withCa2); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Guarded to match its only caller, test_wolfSSL_cert_cb_ctx(). */ +#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) +/* CA cache addition callback that does nothing. + * + * @param [in] der DER encoded certificate. Unused. + * @param [in] sz Length of the certificate. Unused. + * @param [in] type Type of the certificate. Unused. + */ +static void test_ssl_cert_ca_cache_cb(unsigned char* der, int sz, int type) +{ + (void)der; + (void)sz; + (void)type; +} +#endif + +/* Test storing the user contexts and callbacks used during verification. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_cert_cb_ctx(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + int userCtx = 0; + + /* NULL objects are ignored rather than faulting. */ + wolfSSL_CTX_SetCertCbCtx(NULL, &userCtx); + wolfSSL_SetCertCbCtx(NULL, &userCtx); + wolfSSL_CTX_SetCACb(NULL, test_ssl_cert_ca_cache_cb); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + wolfSSL_CTX_SetCertCbCtx(ctx, &userCtx); + wolfSSL_SetCertCbCtx(ssl, &userCtx); + if (ctx != NULL) { + ExpectPtrEq(ctx->verifyCbCtx, &userCtx); + } + if (ssl != NULL) { + ExpectPtrEq(ssl->verifyCbCtx, &userCtx); + } + + wolfSSL_CTX_SetCACb(ctx, test_ssl_cert_ca_cache_cb); + if ((ctx != NULL) && (ctx->cm != NULL)) { + ExpectTrue(ctx->cm->caCacheCallback == test_ssl_cert_ca_cache_cb); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test getting the certificate the object will present. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_get_certificate_api(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \ + defined(KEEP_OUR_CERT) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) \ + && !defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && \ + !defined(NO_RSA) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ExpectNull(wolfSSL_get_certificate(NULL)); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* The object borrows the context's certificate. */ + ExpectNotNull(wolfSSL_get_certificate(ssl)); + + /* Loading a certificate onto the object makes it own one instead. */ + ExpectIntEQ(wolfSSL_use_certificate_file(ssl, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(wolfSSL_get_certificate(ssl)); + /* Asked again the cached object is returned. */ + ExpectNotNull(wolfSSL_get_certificate(ssl)); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test the certificate unload and cache-size APIs. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_cert_unload(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) \ + && defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + +#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_TLS13) && \ + defined(WOLFSSL_POST_HANDSHAKE_AUTH) && !defined(NO_WOLFSSL_SERVER) + /* Requesting a certificate of nothing fails, and is reported as a general + * error rather than as a protocol version problem. */ + ExpectIntEQ(wolfSSL_verify_client_post_handshake(NULL), 0); +#endif +#ifdef PERSIST_CERT_CACHE + ExpectIntEQ(wolfSSL_CTX_get_cert_cache_memsize(NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#endif + + /* A NULL object reports a bad argument rather than a depth. */ + ExpectIntEQ(wolfSSL_CTX_get_verify_depth(NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_get_verify_depth(NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx, caCertFile, NULL), + WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* The loaded CAs can be released without freeing the context. */ + ExpectIntEQ(wolfSSL_CTX_UnloadCAs(ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_UnloadCAs(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* So can the object's own certificates and keys. */ + ExpectIntEQ(wolfSSL_UnloadCertsKeys(ssl), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UnloadCertsKeys(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + ExpectIntEQ(wolfSSL_CTX_UnloadIntermediateCerts(NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ssl_cert.h b/tests/api/test_ssl_cert.h index ed459891e26..112a91db640 100644 --- a/tests/api/test_ssl_cert.h +++ b/tests/api/test_ssl_cert.h @@ -30,6 +30,22 @@ int test_wolfSSL_get_peer_chain(void); int test_wolfSSL_get_chain_X509(void); int test_wolfSSL_get_chain_cert_pem(void); int test_wolfSSL_cmp_peer_cert_to_file(void); +int test_wolfSSL_CTX_set_client_cert_cb(void); +int test_wolfSSL_CTX_set_cert_cb(void); +int test_wolfSSL_cert_setup_cb_ret(void); +int test_wolfSSL_get_peer_cert_chain(void); +int test_wolfSSL_set_peer_cert_chain(void); +int test_wolfSSL_get0_verified_chain(void); +int test_wolfSSL_CA_list_add(void); +int test_wolfSSL_CA_list_get(void); +int test_wolfSSL_load_client_CA_file(void); +int test_wolfSSL_mutual_auth(void); +int test_wolfSSL_post_handshake_auth(void); +int test_wolfSSL_verify_cert_store(void); +int test_wolfSSL_verify_cert_store_follows_ctx(void); +int test_wolfSSL_cert_cb_ctx(void); +int test_wolfSSL_get_certificate_api(void); +int test_wolfSSL_cert_unload(void); #define TEST_SSL_CERT_DECLS \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_verify_mode), \ @@ -37,8 +53,25 @@ int test_wolfSSL_cmp_peer_cert_to_file(void); TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_verify_callback), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CTX_get_extra_chain_certs), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_peer_chain), \ - TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_chain_X509), \ - TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_chain_cert_pem), \ - TEST_DECL_GROUP("ssl_cert", test_wolfSSL_cmp_peer_cert_to_file) + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_chain_X509), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_chain_cert_pem), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_cmp_peer_cert_to_file), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CTX_set_client_cert_cb), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CTX_set_cert_cb), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_cert_setup_cb_ret), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_peer_cert_chain), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_set_peer_cert_chain), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get0_verified_chain), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CA_list_add), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CA_list_get), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_load_client_CA_file), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_mutual_auth), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_post_handshake_auth), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_verify_cert_store), \ + TEST_DECL_GROUP("ssl_cert", \ + test_wolfSSL_verify_cert_store_follows_ctx), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_cert_cb_ctx), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_certificate_api), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_cert_unload) #endif /* TESTS_API_SSL_CERT_H */ diff --git a/tests/api/test_ssl_crl_ocsp.c b/tests/api/test_ssl_crl_ocsp.c new file mode 100644 index 00000000000..536aa139baf --- /dev/null +++ b/tests/api/test_ssl_crl_ocsp.c @@ -0,0 +1,582 @@ +/* test_ssl_crl_ocsp.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#include +#include + +#include +#include + +/* Tests for the CRL and OCSP APIs in src/ssl_api_crl_ocsp.c (moved from + * ssl.c). */ + +/* Test that the CRL and OCSP APIs reject a NULL context or object. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_CTX_crl_bad_args(void) +{ + EXPECT_DECLS; +#ifdef HAVE_CRL + ExpectIntEQ(wolfSSL_CTX_EnableCRL(NULL, 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_CTX_DisableCRL(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_CTX_SetCRL_Cb(NULL, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_CTX_SetCRL_ErrorCb(NULL, NULL, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#ifdef HAVE_CRL_IO + ExpectIntEQ(wolfSSL_CTX_SetCRL_IOCb(NULL, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#endif + /* Only these two read the filesystem. */ +#ifndef NO_FILESYSTEM + ExpectIntEQ(wolfSSL_CTX_LoadCRL(NULL, "certs/crl", WOLFSSL_FILETYPE_PEM, + 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_CTX_LoadCRLFile(NULL, "certs/crl/crl.pem", + WOLFSSL_FILETYPE_PEM), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#endif + + /* The object-side wrappers reject a NULL object the same way. */ + ExpectIntEQ(wolfSSL_EnableCRL(NULL, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_DisableCRL(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_SetCRL_Cb(NULL, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_SetCRL_ErrorCb(NULL, NULL, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_LoadCRLBuffer(NULL, NULL, 0, WOLFSSL_FILETYPE_PEM), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#ifdef HAVE_CRL_IO + ExpectIntEQ(wolfSSL_SetCRL_IOCb(NULL, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#endif +#ifndef NO_FILESYSTEM + ExpectIntEQ(wolfSSL_LoadCRL(NULL, "certs/crl", WOLFSSL_FILETYPE_PEM, 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_LoadCRLFile(NULL, "certs/crl/crl.pem", + WOLFSSL_FILETYPE_PEM), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#endif +#endif /* HAVE_CRL */ + +#ifdef HAVE_OCSP + ExpectIntEQ(wolfSSL_EnableOCSP(NULL, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_DisableOCSP(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_SetOCSP_Cb(NULL, NULL, NULL, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#endif +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) +#ifdef HAVE_CERTIFICATE_STATUS_REQUEST + ExpectIntEQ(wolfSSL_UseOCSPStapling(NULL, 0, 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#endif +#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2 + ExpectIntEQ(wolfSSL_UseOCSPStaplingV2(NULL, 0, 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#endif +#endif + return EXPECT_RESULT(); +} + +/* Test setting the OCSP responder URL on an object. + * + * wolfSSL_get_ocsp_url() and wolfSSL_get_ocsp_response() are WOLFSSL_LOCAL, so + * only the setter can be reached from here. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_ocsp_url_api(void) +{ + EXPECT_DECLS; +#if defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \ + defined(WOLFSSL_HAPROXY)) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_CLIENT) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + char url[] = "http://127.0.0.1:22221"; + + /* A NULL object cannot hold a URL. */ + ExpectIntEQ(wolfSSL_set_ocsp_url(NULL, url), WOLFSSL_FAILURE); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + ExpectIntEQ(wolfSSL_set_ocsp_url(ssl, url), WOLFSSL_SUCCESS); + if (ssl != NULL) { + ExpectStrEQ(ssl->url, url); + } + /* The URL can be cleared again. */ + ExpectIntEQ(wolfSSL_set_ocsp_url(ssl, NULL), WOLFSSL_SUCCESS); + if (ssl != NULL) { + ExpectNull(ssl->url); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test reading the produced date of the last OCSP response. + * + * The date is normally filled in while a response is processed; it is set + * directly here so that each way of reporting it can be reached. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_get_ocsp_producedDate(void) +{ + EXPECT_DECLS; +#if defined(HAVE_OCSP) && !defined(NO_ASN_TIME) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_CLIENT) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + byte date[MAX_DATE_SIZE]; + int format = 0; + struct tm producedTm; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* No response processed, so no date format is recorded yet. */ + ExpectIntEQ(wolfSSL_get_ocsp_producedDate(ssl, date, sizeof(date), + &format), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_get_ocsp_producedDate_tm(ssl, &producedTm), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + if (ssl != NULL) { + /* Pretend a response carrying this date was processed. */ + XMEMSET(ssl->ocspProducedDate, 0, sizeof(ssl->ocspProducedDate)); + XMEMCPY(ssl->ocspProducedDate, "250101000000Z", 14); + ssl->ocspProducedDateFormat = ASN_UTC_TIME; + + /* Both output parameters are required. */ + ExpectIntEQ(wolfSSL_get_ocsp_producedDate(ssl, NULL, sizeof(date), + &format), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_get_ocsp_producedDate(ssl, date, sizeof(date), + NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + /* The buffer must be able to hold the whole date. */ + ExpectIntEQ(wolfSSL_get_ocsp_producedDate(ssl, date, 4, &format), + WC_NO_ERR_TRACE(BUFFER_E)); + + ExpectIntEQ(wolfSSL_get_ocsp_producedDate(ssl, date, sizeof(date), + &format), 0); + ExpectIntEQ(format, ASN_UTC_TIME); + ExpectStrEQ((char*)date, "250101000000Z"); + + /* The same date is also reported as a broken-down time. */ + ExpectIntEQ(wolfSSL_get_ocsp_producedDate_tm(ssl, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + XMEMSET(&producedTm, 0, sizeof(producedTm)); + ExpectIntEQ(wolfSSL_get_ocsp_producedDate_tm(ssl, &producedTm), 0); + ExpectIntEQ(producedTm.tm_year, 125); + + /* A date that cannot be parsed is reported as such. */ + XMEMSET(ssl->ocspProducedDate, 0, sizeof(ssl->ocspProducedDate)); + XMEMCPY(ssl->ocspProducedDate, "not-a-date", 11); + ExpectIntEQ(wolfSSL_get_ocsp_producedDate_tm(ssl, &producedTm), + WC_NO_ERR_TRACE(ASN_PARSE_E)); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test setting and getting the certificate status request type. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_tlsext_status_type(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ + !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ExpectIntEQ(wolfSSL_set_tlsext_status_type(NULL, + WOLFSSL_TLSEXT_STATUSTYPE_ocsp), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_get_tlsext_status_type(NULL), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* The extension is not requested until asked for. */ + ExpectIntEQ(wolfSSL_get_tlsext_status_type(ssl), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + + /* Only the OCSP status type is supported. */ + ExpectIntEQ(wolfSSL_set_tlsext_status_type(ssl, 99), WOLFSSL_FAILURE); + + ExpectIntEQ(wolfSSL_set_tlsext_status_type(ssl, + WOLFSSL_TLSEXT_STATUSTYPE_ocsp), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_get_tlsext_status_type(ssl), + WOLFSSL_TLSEXT_STATUSTYPE_ocsp); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Guarded to match its only caller, test_wolfSSL_CTX_tlsext_status_cb(). */ +#if (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_FILESYSTEM) && \ + !defined(NO_RSA) && defined(WOLFSSL_PEM_TO_DER) +/* Certificate status callback that does nothing. + * + * @param [in] ssl SSL/TLS object. Unused. + * @param [in] arg User argument. Unused. + * @return 0 always. + */ +static int test_ssl_crl_ocsp_status_cb(WOLFSSL* ssl, void* arg) +{ + (void)ssl; + (void)arg; + return 0; +} +#endif + +/* Test setting and getting the certificate status callback and its argument. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_CTX_tlsext_status_cb(void) +{ + EXPECT_DECLS; +#if (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) \ + && defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + tlsextStatusCb cb = NULL; + int arg = 0; + + /* Every argument is required. */ + ExpectIntEQ(wolfSSL_CTX_get_tlsext_status_cb(NULL, &cb), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_CTX_set_tlsext_status_cb(NULL, + test_ssl_crl_ocsp_status_cb), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_CTX_set_tlsext_status_arg(NULL, &arg), + WOLFSSL_FAILURE); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_CTX_get_tlsext_status_cb(ctx, NULL), WOLFSSL_FAILURE); + + /* Setting the callback turns stapling on so it can be used. */ + ExpectIntEQ(wolfSSL_CTX_set_tlsext_status_cb(ctx, + test_ssl_crl_ocsp_status_cb), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_get_tlsext_status_cb(ctx, &cb), WOLFSSL_SUCCESS); + ExpectTrue(cb == test_ssl_crl_ocsp_status_cb); + + ExpectIntEQ(wolfSSL_CTX_set_tlsext_status_arg(ctx, &arg), WOLFSSL_SUCCESS); + + wolfSSL_CTX_set_ocsp_status_verify_cb(NULL, NULL, NULL); + + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test storing and retrieving a stapled OCSP response. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_tlsext_status_ocsp_resp(void) +{ + EXPECT_DECLS; +#if (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) && \ + !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + unsigned char* resp = NULL; + unsigned char* stored = NULL; + int owned = 0; + + /* Both arguments are required. */ + ExpectIntEQ(wolfSSL_get_tlsext_status_ocsp_resp(NULL, &resp), 0); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + ExpectIntEQ(wolfSSL_get_tlsext_status_ocsp_resp(ssl, NULL), 0); + + /* Nothing stapled yet. */ + ExpectIntEQ(wolfSSL_get_tlsext_status_ocsp_resp(ssl, &resp), 0); + ExpectNull(resp); + + /* A response and a length must be given together. */ + ExpectIntEQ(wolfSSL_set_tlsext_status_ocsp_resp(ssl, NULL, 4), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, NULL, 0, + 1 + MAX_CHAIN_DEPTH), WOLFSSL_FAILURE); + + /* The object takes ownership of the response and releases it with a heap + * hint of NULL and a type of 0, so allocate it to match. */ + ExpectNotNull(stored = (unsigned char*)XMALLOC(4, NULL, 0)); + if (stored != NULL) { + XMEMCPY(stored, "resp", 4); + owned = (wolfSSL_set_tlsext_status_ocsp_resp(ssl, stored, 4) == + WOLFSSL_SUCCESS); + ExpectIntEQ(owned, 1); + if (owned) { + ExpectIntEQ(wolfSSL_get_tlsext_status_ocsp_resp(ssl, &resp), 4); + ExpectPtrEq(resp, stored); + } + else { + /* Ownership was not handed over, so this side still has it. */ + XFREE(stored, NULL, 0); + } + } + + /* Clearing it releases the stored response and leaves nothing to get. */ + ExpectIntEQ(wolfSSL_set_tlsext_status_ocsp_resp(ssl, NULL, 0), + WOLFSSL_SUCCESS); + resp = NULL; + ExpectIntEQ(wolfSSL_get_tlsext_status_ocsp_resp(ssl, &resp), 0); + ExpectNull(resp); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test splitting an OCSP responder URL into its parts. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_OCSP_parse_url_api(void) +{ + EXPECT_DECLS; +#if defined(HAVE_OCSP) && defined(OPENSSL_EXTRA) + char* host = NULL; + char* port = NULL; + char* path = NULL; + int isSsl = -1; + + /* Every argument is required. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url(NULL, &host, &port, &path, &isSsl), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://a/", NULL, &port, &path, + &isSsl), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://a/", &host, NULL, &path, + &isSsl), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://a/", &host, &port, NULL, + &isSsl), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://a/", &host, &port, &path, + NULL), WOLFSSL_FAILURE); + + /* A plain URL uses the default port and the whole path. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com/ocsp", &host, &port, + &path, &isSsl), WOLFSSL_SUCCESS); + ExpectStrEQ(host, "example.com"); + ExpectStrEQ(port, "80"); + ExpectStrEQ(path, "/ocsp"); + ExpectIntEQ(isSsl, 0); + XFREE(host, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(port, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(path, NULL, DYNAMIC_TYPE_OPENSSL); + host = NULL; port = NULL; path = NULL; + + /* https selects the secure default port. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("https://example.com", &host, &port, + &path, &isSsl), WOLFSSL_SUCCESS); + ExpectStrEQ(port, "443"); + ExpectStrEQ(path, "/"); + ExpectIntEQ(isSsl, 1); + XFREE(host, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(port, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(path, NULL, DYNAMIC_TYPE_OPENSSL); + host = NULL; port = NULL; path = NULL; + + /* An explicit port before a path replaces the default. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com:8080/ocsp", &host, + &port, &path, &isSsl), WOLFSSL_SUCCESS); + ExpectStrEQ(host, "example.com"); + ExpectStrEQ(port, "8080"); + ExpectStrEQ(path, "/ocsp"); + XFREE(host, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(port, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(path, NULL, DYNAMIC_TYPE_OPENSSL); + host = NULL; port = NULL; path = NULL; + + /* An explicit port with no path is also accepted. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com:8080", &host, &port, + &path, &isSsl), WOLFSSL_SUCCESS); + ExpectStrEQ(port, "8080"); + XFREE(host, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(port, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(path, NULL, DYNAMIC_TYPE_OPENSSL); + host = NULL; port = NULL; path = NULL; + + /* Malformed URLs are rejected and nothing is left allocated. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("ftp://example.com/", &host, &port, + &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + ExpectNull(port); + ExpectNull(path); + /* Scheme is neither http: nor https. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("httpx://example.com/", &host, &port, + &path, &isSsl), WOLFSSL_FAILURE); + /* Missing separator after the scheme. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http:/example.com/", &host, &port, + &path, &isSsl), WOLFSSL_FAILURE); + /* Port marker with no port. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com:", &host, &port, + &path, &isSsl), WOLFSSL_FAILURE); + /* With no port, a ':' in the path is rejected: it is ambiguous with a + * port and these URLs come from certificates. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com/ocsp:8080", &host, + &port, &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + + /* With an explicit port there is no ambiguity, so a ':' in the path is + * kept. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com:8080/ocsp:1", &host, + &port, &path, &isSsl), WOLFSSL_SUCCESS); + ExpectStrEQ(host, "example.com"); + ExpectStrEQ(port, "8080"); + ExpectStrEQ(path, "/ocsp:1"); + XFREE(host, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(port, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(path, NULL, DYNAMIC_TYPE_OPENSSL); + host = NULL; port = NULL; path = NULL; + + /* An IPv6 literal is bracketed; the brackets are not part of the host and + * the colons inside it are not port separators. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://[::1]/ocsp", &host, &port, + &path, &isSsl), WOLFSSL_SUCCESS); + ExpectStrEQ(host, "::1"); + ExpectStrEQ(port, "80"); + ExpectStrEQ(path, "/ocsp"); + XFREE(host, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(port, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(path, NULL, DYNAMIC_TYPE_OPENSSL); + host = NULL; port = NULL; path = NULL; + + /* A port after the literal is still found. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("https://[2001:db8::1]:8443/ocsp", + &host, &port, &path, &isSsl), WOLFSSL_SUCCESS); + ExpectStrEQ(host, "2001:db8::1"); + ExpectStrEQ(port, "8443"); + ExpectStrEQ(path, "/ocsp"); + ExpectIntEQ(isSsl, 1); + XFREE(host, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(port, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(path, NULL, DYNAMIC_TYPE_OPENSSL); + host = NULL; port = NULL; path = NULL; + + /* An unterminated literal is rejected. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://[::1/ocsp", &host, &port, + &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + + /* Anything other than a port after the literal is rejected rather than + * dropped. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://[::1]junk:80/ocsp", &host, + &port, &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://[::1]junk", &host, &port, + &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://[::1]junk/ocsp", &host, &port, + &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + + /* The port must be digits, at most five of them, and in range - the same + * rules wolfIO_DecodeUrl() applies, so the two parsers cannot disagree + * about the same URL. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com:8080junk/ocsp", + &host, &port, &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com:99999999/ocsp", + &host, &port, &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com:65536/ocsp", + &host, &port, &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + /* The largest valid port is accepted. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com:65535/ocsp", + &host, &port, &path, &isSsl), WOLFSSL_SUCCESS); + ExpectStrEQ(port, "65535"); + XFREE(host, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(port, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(path, NULL, DYNAMIC_TYPE_OPENSSL); + host = NULL; port = NULL; path = NULL; + + /* A failed https parse leaves nothing behind, the scheme flag included: + * it is set before the authority is validated. */ + isSsl = 0; + ExpectIntEQ(wolfSSL_OCSP_parse_url("https://example.com:99999999/ocsp", + &host, &port, &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + ExpectNull(port); + ExpectNull(path); + ExpectIntEQ(isSsl, 0); + isSsl = 0; + ExpectIntEQ(wolfSSL_OCSP_parse_url("https://:8080/ocsp", &host, &port, + &path, &isSsl), WOLFSSL_FAILURE); + ExpectIntEQ(isSsl, 0); + + /* Userinfo would put the real host after the '@', so it is rejected + * rather than folded into the host or the port. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://good.example@evil.example/x", + &host, &port, &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://ocsp.example:80@evil.example/x", + &host, &port, &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + + /* A URL with no host is rejected rather than reported with the rest of + * the URL as the host. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://:8080/ocsp", &host, &port, + &path, &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://", &host, &port, &path, + &isSsl), WOLFSSL_FAILURE); + ExpectNull(host); + /* CR or LF would split a request built from the parts. */ + ExpectIntEQ(wolfSSL_OCSP_parse_url("http://example.com/\r\nX:", &host, + &port, &path, &isSsl), WOLFSSL_FAILURE); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ssl_crl_ocsp.h b/tests/api/test_ssl_crl_ocsp.h new file mode 100644 index 00000000000..c7cb08944b1 --- /dev/null +++ b/tests/api/test_ssl_crl_ocsp.h @@ -0,0 +1,44 @@ +/* test_ssl_crl_ocsp.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef TESTS_API_SSL_CRL_OCSP_H +#define TESTS_API_SSL_CRL_OCSP_H + +#include + +int test_wolfSSL_CTX_crl_bad_args(void); +int test_wolfSSL_ocsp_url_api(void); +int test_wolfSSL_get_ocsp_producedDate(void); +int test_wolfSSL_tlsext_status_type(void); +int test_wolfSSL_CTX_tlsext_status_cb(void); +int test_wolfSSL_tlsext_status_ocsp_resp(void); +int test_wolfSSL_OCSP_parse_url_api(void); + +#define TEST_SSL_CRL_OCSP_DECLS \ + TEST_DECL_GROUP("ssl_crl_ocsp", test_wolfSSL_CTX_crl_bad_args), \ + TEST_DECL_GROUP("ssl_crl_ocsp", test_wolfSSL_ocsp_url_api), \ + TEST_DECL_GROUP("ssl_crl_ocsp", test_wolfSSL_get_ocsp_producedDate), \ + TEST_DECL_GROUP("ssl_crl_ocsp", test_wolfSSL_tlsext_status_type), \ + TEST_DECL_GROUP("ssl_crl_ocsp", test_wolfSSL_CTX_tlsext_status_cb), \ + TEST_DECL_GROUP("ssl_crl_ocsp", test_wolfSSL_tlsext_status_ocsp_resp), \ + TEST_DECL_GROUP("ssl_crl_ocsp", test_wolfSSL_OCSP_parse_url_api) + +#endif /* TESTS_API_SSL_CRL_OCSP_H */ diff --git a/tests/api/test_ssl_ext.c b/tests/api/test_ssl_ext.c index 09bbfc0e0cc..8d59e3465a7 100644 --- a/tests/api/test_ssl_ext.c +++ b/tests/api/test_ssl_ext.c @@ -37,6 +37,12 @@ /* Tests for the TLS extension APIs in src/ssl_api_ext.c (moved from ssl.c). * These cover functions not already exercised elsewhere in api.c. */ +/* Test turning off session tickets for TLS 1.2 and below. + * + * TLS 1.3 tickets are unaffected, so only the pre-1.3 path is disabled. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_NoTicketTLSv12_ext(void) { EXPECT_DECLS; @@ -68,6 +74,12 @@ int test_wolfSSL_NoTicketTLSv12_ext(void) return EXPECT_RESULT(); } +/* Test setting the maximum fragment length on a context. + * + * Each defined length code is accepted and out-of-range codes are refused. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_CTX_UseMaxFragment_ext(void) { EXPECT_DECLS; @@ -89,6 +101,10 @@ int test_wolfSSL_CTX_UseMaxFragment_ext(void) return EXPECT_RESULT(); } +/* Test setting and reading back the number of session tickets to send. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_CTX_num_tickets_ext(void) { EXPECT_DECLS; @@ -109,6 +125,12 @@ int test_wolfSSL_CTX_num_tickets_ext(void) return EXPECT_RESULT(); } +/* Test setting the supported groups from an array of identifiers. + * + * Covers both the context and object forms. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_set1_groups_ext(void) { EXPECT_DECLS; @@ -145,6 +167,12 @@ int test_wolfSSL_set1_groups_ext(void) return EXPECT_RESULT(); } +/* Test setting the supported groups from a colon separated list. + * + * Covers both the context and object forms, and rejects unknown names. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_set1_groups_list_ext(void) { EXPECT_DECLS; @@ -202,6 +230,10 @@ int test_wolfSSL_set1_groups_list_ext(void) return EXPECT_RESULT(); } +/* Test setting the session ticket lifetime hint. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_CTX_set_TicketHint_ext(void) { EXPECT_DECLS; @@ -295,8 +327,8 @@ int test_wolfSSL_CTX_set_TicketHint_default_cb_limit(void) { EXPECT_DECLS; #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) \ - && defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) \ - && !defined(NO_WOLFSSL_SERVER) + && defined(HAVE_SESSION_TICKET) \ + && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_WOLFSSL_SERVER) /* Default callback, hint below the limit: handshake succeeds, ticket issued. */ ExpectIntGT(test_TicketHint_client_ticket_len(wolfTLSv1_3_client_method, wolfTLSv1_3_server_method, WOLFSSL_TICKET_KEY_LIFETIME / 2 - 1, 0), 0); @@ -319,6 +351,12 @@ int test_wolfSSL_CTX_set_TicketHint_default_cb_limit(void) return EXPECT_RESULT(); } +/* Test the OpenSSL compatibility maximum fragment length setters. + * + * Covers both the context and object forms. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_tlsext_max_fragment_length_ext(void) { EXPECT_DECLS; @@ -352,6 +390,12 @@ int test_wolfSSL_tlsext_max_fragment_length_ext(void) return EXPECT_RESULT(); } +/* Test turning off the extended master secret extension. + * + * Covers both the context and object forms. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_DisableExtendedMasterSecret_ext(void) { EXPECT_DECLS; @@ -376,6 +420,10 @@ int test_wolfSSL_DisableExtendedMasterSecret_ext(void) return EXPECT_RESULT(); } +/* Test setting the SNI host name and reading it back. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_set_tlsext_host_name_ext(void) { EXPECT_DECLS; @@ -402,6 +450,10 @@ int test_wolfSSL_set_tlsext_host_name_ext(void) return EXPECT_RESULT(); } +/* Test installing the server name callback on a context. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_CTX_set_tlsext_servername_callback_ext(void) { EXPECT_DECLS; @@ -421,6 +473,10 @@ int test_wolfSSL_CTX_set_tlsext_servername_callback_ext(void) return EXPECT_RESULT(); } +/* Test storing and retrieving the debug argument on an object. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_set_tlsext_debug_arg_ext(void) { EXPECT_DECLS; @@ -442,6 +498,10 @@ int test_wolfSSL_set_tlsext_debug_arg_ext(void) return EXPECT_RESULT(); } +/* Test installing the session ticket callback and its context. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_set_SessionTicket_cb_ext(void) { EXPECT_DECLS; @@ -463,6 +523,10 @@ int test_wolfSSL_set_SessionTicket_cb_ext(void) return EXPECT_RESULT(); } +/* Test setting the supported curves from a colon separated list. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_set1_curves_list_ext(void) { EXPECT_DECLS; @@ -488,6 +552,10 @@ int test_wolfSSL_set1_curves_list_ext(void) return EXPECT_RESULT(); } +/* Test the secure renegotiation resumption request. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_SecureResume_ext(void) { EXPECT_DECLS; @@ -509,6 +577,10 @@ int test_wolfSSL_SecureResume_ext(void) return EXPECT_RESULT(); } +/* Test enabling secure renegotiation on a context. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_CTX_UseSecureRenegotiation_ext(void) { EXPECT_DECLS; @@ -527,6 +599,13 @@ int test_wolfSSL_CTX_UseSecureRenegotiation_ext(void) return EXPECT_RESULT(); } +/* Test the NPN advertise and select callbacks. + * + * Nothing has been negotiated before a handshake, so the negotiated protocol is + * empty. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_next_proto_cb_ext(void) { EXPECT_DECLS; @@ -554,6 +633,12 @@ int test_wolfSSL_next_proto_cb_ext(void) return EXPECT_RESULT(); } +/* Test the certificate status request extension and identifier lists. + * + * The getters report nothing until a list has been set. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_tlsext_status_exts_ids_ext(void) { EXPECT_DECLS; @@ -578,6 +663,12 @@ int test_wolfSSL_tlsext_status_exts_ids_ext(void) return EXPECT_RESULT(); } +/* Test that wolfSSL_SNI_GetFromBuffer() rejects bad arguments. + * + * Also covers buffers that are too short to hold the extension. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_SNI_GetFromBuffer_inval_ext(void) { EXPECT_DECLS; @@ -593,6 +684,10 @@ int test_wolfSSL_SNI_GetFromBuffer_inval_ext(void) return EXPECT_RESULT(); } +/* Test that wolfSSL_UseTrustedCA() rejects bad arguments. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_UseTrustedCA_inval_ext(void) { EXPECT_DECLS; @@ -614,6 +709,10 @@ int test_wolfSSL_UseTrustedCA_inval_ext(void) return EXPECT_RESULT(); } +/* Test that wolfSSL_UseMaxFragment() rejects bad arguments. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_UseMaxFragment_inval_ext(void) { EXPECT_DECLS; @@ -626,6 +725,10 @@ int test_wolfSSL_UseMaxFragment_inval_ext(void) return EXPECT_RESULT(); } +/* Test that the supported group setters rejects bad arguments. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_set1_groups_inval_ext(void) { EXPECT_DECLS; @@ -650,6 +753,13 @@ int test_wolfSSL_set1_groups_inval_ext(void) return EXPECT_RESULT(); } +/* Test that wolfSSL_UseALPN() rejects bad arguments. + * + * Covers a NULL object, a NULL list, an over-long list and unsupported option + * combinations. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_UseALPN_inval_ext(void) { EXPECT_DECLS; @@ -676,6 +786,10 @@ int test_wolfSSL_UseALPN_inval_ext(void) return EXPECT_RESULT(); } +/* Test that the peer ALPN protocol accessors rejects bad arguments. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_ALPN_GetPeerProtocol_inval_ext(void) { EXPECT_DECLS; @@ -705,6 +819,10 @@ int test_wolfSSL_ALPN_GetPeerProtocol_inval_ext(void) return EXPECT_RESULT(); } +/* Test that wolfSSL_CTX_set_TicketEncCb() rejects bad arguments. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_CTX_set_TicketEncCb_inval_ext(void) { EXPECT_DECLS; @@ -717,6 +835,10 @@ int test_wolfSSL_CTX_set_TicketEncCb_inval_ext(void) return EXPECT_RESULT(); } +/* Test that the session ticket APIs rejects bad arguments. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_SessionTicket_inval_ext(void) { EXPECT_DECLS; @@ -774,6 +896,10 @@ int test_wolfSSL_SessionTicket_inval_ext(void) return EXPECT_RESULT(); } +/* Test that wolfSSL_CTX_set_servername_arg() rejects bad arguments. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_CTX_set_servername_arg_inval_ext(void) { EXPECT_DECLS; @@ -784,6 +910,10 @@ int test_wolfSSL_CTX_set_servername_arg_inval_ext(void) return EXPECT_RESULT(); } +/* Test that wolfSSL_CTX_set_alpn_protos() rejects bad arguments. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_CTX_set_alpn_protos_inval_ext(void) { EXPECT_DECLS; @@ -812,6 +942,12 @@ int test_wolfSSL_CTX_set_alpn_protos_inval_ext(void) return EXPECT_RESULT(); } +/* Test parsing the dual algorithm certificate key share signature specifiers. + * + * An over-long list is rejected even when every specifier in it is valid. + * + * @return TEST_SUCCESS on success. + */ int test_wolfSSL_dual_alg_cks_parse_ext(void) { EXPECT_DECLS; @@ -864,3 +1000,382 @@ int test_wolfSSL_dual_alg_cks_parse_ext(void) #endif return EXPECT_RESULT(); } + +/* Test wolfSSL_set1_groups() and wolfSSL_CTX_set1_groups() argument checks. + * + * The NULL-list and non-positive-count guards are separate from the + * unrecognized-group check covered by test_wolfSSL_set1_groups_inval_ext(). + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_set1_groups_null_ext(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SUPPORTED_CURVES) && defined(OPENSSL_EXTRA) && \ + defined(HAVE_ECC) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + int groups[1]; + + groups[0] = WOLFSSL_ECC_SECP256R1; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* A NULL list is rejected. */ + ExpectIntEQ(wolfSSL_CTX_set1_groups(ctx, NULL, 1), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_set1_groups(ssl, NULL, 1), WOLFSSL_FAILURE); + + /* A non-positive count is rejected. */ + ExpectIntEQ(wolfSSL_CTX_set1_groups(ctx, groups, 0), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_set1_groups(ssl, groups, 0), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_CTX_set1_groups(ctx, groups, -1), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_set1_groups(ssl, groups, -1), WOLFSSL_FAILURE); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test that wolfSSL_set1_groups() accepts wolfSSL named group identifiers. + * + * Group values may be either a wolfSSL named group or, when ECC is available, + * a curve NID. This covers the named-group branch of the translation. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_set1_groups_named_ext(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SUPPORTED_CURVES) && defined(OPENSSL_EXTRA) && \ + defined(HAVE_ECC) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \ + !defined(NO_ECC_SECP) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + int groups[2]; + int count = 0; + + /* Only name curves this build accepts. ECC_USER_CURVES trims the set, so + * these mirror the checks the library makes on a supported curve. */ +#if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 + groups[count++] = WOLFSSL_ECC_SECP256R1; +#endif +#if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384 + groups[count++] = WOLFSSL_ECC_SECP384R1; +#endif + + if (count > 0) { + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Named groups are taken as-is rather than looked up as NIDs. */ + ExpectIntEQ(wolfSSL_CTX_set1_groups(ctx, groups, count), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set1_groups(ssl, groups, count), WOLFSSL_SUCCESS); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); + } +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_ALPN_FreePeerProtocol() argument checking. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_ALPN_FreePeerProtocol_inval_ext(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ALPN) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + char* list = NULL; + + /* A NULL object is rejected before the list is touched. */ + ExpectIntEQ(wolfSSL_ALPN_FreePeerProtocol(NULL, &list), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectNull(list); +#endif + return EXPECT_RESULT(); +} + +/* Test that wolfSSL_ALPN_GetPeerProtocol() rejects a malformed peer list. + * + * The peer's list is stored in wire format, so a length byte that runs past + * the end of the buffer must be caught rather than copied out of bounds. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_ALPN_GetPeerProtocol_badlen_ext(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ALPN) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + char* list = NULL; + word16 listSz = 0; + byte* peer = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* No list offered by the peer yet. */ + ExpectIntEQ(wolfSSL_ALPN_GetPeerProtocol(ssl, &list, &listSz), + WC_NO_ERR_TRACE(BUFFER_ERROR)); + + /* Install a list whose first length byte claims more bytes than are + * present. wolfSSL_free() releases the buffer with the object, so it must + * be allocated with the type the library frees it with. */ + if (ssl != NULL) { + peer = (byte*)XMALLOC(4, ssl->heap, DYNAMIC_TYPE_ALPN); + ExpectNotNull(peer); + if (peer != NULL) { + peer[0] = 8; /* claims 8 bytes of protocol name */ + peer[1] = 'h'; + peer[2] = '2'; + peer[3] = 0; + ssl->alpn_peer_requested = peer; + ssl->alpn_peer_requested_length = 4; + + ExpectIntEQ(wolfSSL_ALPN_GetPeerProtocol(ssl, &list, &listSz), + WC_NO_ERR_TRACE(WOLFSSL_FAILURE)); + ExpectNull(list); + } + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_SSL_get_secure_renegotiation_support(). + * + * Reports 0 before the extension is enabled and non-zero afterwards. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_get_secure_renegotiation_support_ext(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SERVER_RENEGOTIATION_INFO) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + /* A NULL object reports no support. */ + ExpectIntEQ(wolfSSL_SSL_get_secure_renegotiation_support(NULL), 0); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* A NULL object is rejected when requesting the extension. */ + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* Not requested yet. */ + ExpectIntEQ(wolfSSL_SSL_get_secure_renegotiation_support(ssl), 0); + + /* Requesting the extension is not enough - support is only reported once + * the peer has agreed to it during the handshake. */ + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_SSL_get_secure_renegotiation_support(ssl), 0); + + /* Once negotiated, support is reported. */ + if ((ssl != NULL) && (ssl->secure_renegotiation != NULL)) { + ssl->secure_renegotiation->enabled = 1; + ExpectIntEQ(wolfSSL_SSL_get_secure_renegotiation_support(ssl), 1); + ssl->secure_renegotiation->enabled = 0; + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_set_alpn_protos() with a malformed wire-format list. + * + * A length byte that runs past the end of the buffer must be rejected rather + * than producing a truncated protocol list. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_set_alpn_protos_badlen_ext(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_ALPN) && !defined(NO_BIO) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + /* First entry claims 8 bytes but only 3 follow. */ + const unsigned char bad[] = { 8, 'h', '2', 0 }; + const unsigned char good[] = { 2, 'h', '2' }; +#if defined(WOLFSSL_ERROR_CODE_OPENSSL) + const int okRet = 0; + const int failRet = 1; +#else + const int okRet = WOLFSSL_SUCCESS; + const int failRet = WOLFSSL_FAILURE; +#endif + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* A well-formed list is accepted. */ + ExpectIntEQ(wolfSSL_set_alpn_protos(ssl, good, (unsigned int)sizeof(good)), + okRet); + + /* A bad length byte is rejected. */ + ExpectIntEQ(wolfSSL_set_alpn_protos(ssl, bad, (unsigned int)sizeof(bad)), + failRet); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_TLS12) && \ + defined(OPENSSL_EXTRA) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + +/* Return values of an OpenSSL-style ticket key callback. These mirror the + * TICKET_KEY_CB_RET_* values used by wolfSSL_TicketKeyCb() in + * src/ssl_api_ext.c, which are private to that file. */ +#define TEST_SSL_EXT_TICKET_CB_OK 1 +#define TEST_SSL_EXT_TICKET_CB_RENEW 2 + +/* OpenSSL-style session ticket key callback that always asks for renewal. + * + * Uses fixed key material - the ticket never leaves this test. + * + * @param [in] ssl SSL/TLS object. Unused. + * @param [in, out] name Key name; set when encrypting, ignored when not - + * there is only ever the one key here. + * @param [in, out] iv Initialization vector; set when encrypting. + * @param [in, out] ectx Cipher context to initialize. + * @param [in, out] hctx HMAC context to initialize. + * @param [in] enc 1 when encrypting a ticket, 0 when decrypting. + * @return TEST_SSL_EXT_TICKET_CB_OK when encrypting. + * @return TEST_SSL_EXT_TICKET_CB_RENEW when decrypting, asking the ticket to + * be reissued. + * @return 0 when the cipher or HMAC cannot be set up. + */ +static int test_ssl_ext_ticket_renew_cb(WOLFSSL* ssl, unsigned char* name, + unsigned char* iv, WOLFSSL_EVP_CIPHER_CTX* ectx, WOLFSSL_HMAC_CTX* hctx, + int enc) +{ + static const unsigned char key[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + static const unsigned char hmacKey[32] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f + }; + int ret; + + (void)ssl; + + if (enc) { + XMEMSET(name, 'N', WOLFSSL_TICKET_NAME_SZ); + XMEMSET(iv, 'I', WOLFSSL_TICKET_IV_SZ); + } + + if (HMAC_Init_ex(hctx, hmacKey, (int)sizeof(hmacKey), EVP_sha256(), + NULL) != 1) { + ret = 0; + } + else if (enc) { + if (EVP_EncryptInit_ex(ectx, EVP_aes_256_cbc(), NULL, key, iv) != 1) { + ret = 0; + } + else { + ret = TEST_SSL_EXT_TICKET_CB_OK; + } + } + else if (EVP_DecryptInit_ex(ectx, EVP_aes_256_cbc(), NULL, key, iv) != 1) { + ret = 0; + } + else { + /* Ask for the ticket to be reissued after this resumption. */ + ret = TEST_SSL_EXT_TICKET_CB_RENEW; + } + + return ret; +} +#endif + +/* Test that a TLS 1.2 resumption honours a ticket key callback asking for + * renewal. + * + * When the callback reports renewal while decrypting, wolfSSL_TicketKeyCb() + * must report that a new ticket is needed rather than plain success. This only + * applies below TLS 1.3, which issues tickets separately. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_ticket_key_cb_renew_ext(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_TLS12) && \ + defined(OPENSSL_EXTRA) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + WOLFSSL_SESSION* session = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_key_cb(ctx_s, + test_ssl_ext_ticket_renew_cb), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + + /* First handshake issues a ticket. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(session = wolfSSL_get1_session(ssl_c)); + + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + + ExpectNotNull(ssl_c = wolfSSL_new(ctx_c)); + ExpectNotNull(ssl_s = wolfSSL_new(ctx_s)); + wolfSSL_SetIOReadCtx(ssl_c, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + ExpectIntEQ(wolfSSL_set_session(ssl_c, session), WOLFSSL_SUCCESS); + /* Make the ticket the only resumption path so the callback is reached. */ + if (ssl_s != NULL) { + ssl_s->options.sessionCacheOff = 1; + } + + /* The ticket decrypts and the session resumes even though the callback + * asked for the ticket to be renewed. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1); + + wolfSSL_SESSION_free(session); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ssl_ext.h b/tests/api/test_ssl_ext.h index d51f226ae89..879121de182 100644 --- a/tests/api/test_ssl_ext.h +++ b/tests/api/test_ssl_ext.h @@ -51,6 +51,13 @@ int test_wolfSSL_SessionTicket_inval_ext(void); int test_wolfSSL_CTX_set_servername_arg_inval_ext(void); int test_wolfSSL_CTX_set_alpn_protos_inval_ext(void); int test_wolfSSL_dual_alg_cks_parse_ext(void); +int test_wolfSSL_set1_groups_null_ext(void); +int test_wolfSSL_set1_groups_named_ext(void); +int test_wolfSSL_ALPN_FreePeerProtocol_inval_ext(void); +int test_wolfSSL_ALPN_GetPeerProtocol_badlen_ext(void); +int test_wolfSSL_get_secure_renegotiation_support_ext(void); +int test_wolfSSL_set_alpn_protos_badlen_ext(void); +int test_wolfSSL_ticket_key_cb_renew_ext(void); #define TEST_SSL_EXT_DECLS \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_NoTicketTLSv12_ext), \ @@ -59,40 +66,50 @@ int test_wolfSSL_dual_alg_cks_parse_ext(void); TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set1_groups_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set1_groups_list_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_CTX_set_TicketHint_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_CTX_set_TicketHint_default_cb_limit), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_tlsext_max_fragment_length_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_DisableExtendedMasterSecret_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_CTX_set_TicketHint_default_cb_limit), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_tlsext_max_fragment_length_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_DisableExtendedMasterSecret_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set_tlsext_host_name_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_CTX_set_tlsext_servername_callback_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_CTX_set_tlsext_servername_callback_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set_tlsext_debug_arg_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set_SessionTicket_cb_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set1_curves_list_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_SecureResume_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_CTX_UseSecureRenegotiation_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_CTX_UseSecureRenegotiation_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_next_proto_cb_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_tlsext_status_exts_ids_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_SNI_GetFromBuffer_inval_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_tlsext_status_exts_ids_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_SNI_GetFromBuffer_inval_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_UseTrustedCA_inval_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_UseMaxFragment_inval_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set1_groups_inval_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_UseALPN_inval_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_ALPN_GetPeerProtocol_inval_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_CTX_set_TicketEncCb_inval_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_ALPN_GetPeerProtocol_inval_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_CTX_set_TicketEncCb_inval_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_SessionTicket_inval_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_CTX_set_servername_arg_inval_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_CTX_set_alpn_protos_inval_ext), \ - TEST_DECL_GROUP("ssl_ext", \ - test_wolfSSL_dual_alg_cks_parse_ext) + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_CTX_set_servername_arg_inval_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_CTX_set_alpn_protos_inval_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_dual_alg_cks_parse_ext), \ + TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set1_groups_null_ext), \ + TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set1_groups_named_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_ALPN_FreePeerProtocol_inval_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_ALPN_GetPeerProtocol_badlen_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_get_secure_renegotiation_support_ext), \ + TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set_alpn_protos_badlen_ext), \ + TEST_DECL_GROUP("ssl_ext", test_wolfSSL_ticket_key_cb_renew_ext) #endif /* TESTS_API_SSL_EXT_H */ diff --git a/tests/api/test_ssl_hs.c b/tests/api/test_ssl_hs.c new file mode 100644 index 00000000000..6dc7ad8dfb8 --- /dev/null +++ b/tests/api/test_ssl_hs.c @@ -0,0 +1,1743 @@ +/* test_ssl_hs.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#include +#include +#include + +#include +#include + +/* Tests for the handshake APIs in src/ssl_api_hs.c (moved from ssl.c). These + * cover functions not already exercised elsewhere in api.c. */ + +/* Test wolfSSL_state_string_long() over a live handshake. + * + * Covers the NULL case, the unknown-protocol case and sampling the state + * before, during and after a handshake. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_state_string_long(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) \ + && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + /* A NULL object has no state to report. */ + ExpectNull(wolfSSL_state_string_long(NULL)); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + /* Before the handshake both sides report the initial state, named for the + * method's version rather than a negotiated one. */ + ExpectStrEQ(wolfSSL_state_string_long(ssl_c), "TLSv1_2 Initialization"); + ExpectStrEQ(wolfSSL_state_string_long(ssl_s), "TLSv1_2 Initialization"); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* After the handshake each side reports the last message it handled, + * prefixed with the negotiated protocol. Both have finished, so neither + * is still reporting the initial state. */ + ExpectIntEQ(XSTRNCMP(wolfSSL_state_string_long(ssl_c), "TLSv1_2 ", 8), 0); + ExpectIntEQ(XSTRNCMP(wolfSSL_state_string_long(ssl_s), "TLSv1_2 ", 8), 0); + ExpectStrNE(wolfSSL_state_string_long(ssl_c), "TLSv1_2 Initialization"); + ExpectStrNE(wolfSSL_state_string_long(ssl_s), "TLSv1_2 Initialization"); + + /* The completed state has one string for both directions, unlike the + * per-message states which name the direction. */ + if (ssl_c != NULL) { + ssl_c->cbmode = WOLFSSL_CB_MODE_WRITE; + ssl_c->options.clientState = HANDSHAKE_DONE; + ExpectStrEQ(wolfSSL_state_string_long(ssl_c), "TLSv1_2 Handshake Done"); + ssl_c->cbmode = WOLFSSL_CB_MODE_READ; + ssl_c->cbtype = server_hello; + ExpectStrEQ(wolfSSL_state_string_long(ssl_c), + "TLSv1_2 read Server Hello"); + } + + /* An unrecognized protocol version reports an empty string. */ + if (ssl_c != NULL) { + ProtocolVersion saved = ssl_c->version; + + ssl_c->version.major = 0x7f; + ExpectStrEQ(wolfSSL_state_string_long(ssl_c), ""); + ssl_c->version = saved; + } + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_state_string_long() across the states it can report. + * + * The reported string is chosen from the callback mode, the handshake message + * type when reading, and the connection state when writing. Walk each of those + * so every arm of the translation is taken. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_state_string_long_states(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_WOLFSSL_CLIENT) \ + && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + /* Handshake message types reported while reading, with the string each + * maps to. Only one side sends these, so the object's own side does not + * come into it. */ + static const struct { + int type; + const char* str; + } readStates[] = { + { hello_request, "TLSv1_2 read Server Hello Request" }, + { hello_verify_request, "TLSv1_2 read Server Hello Verify Request" }, + { session_ticket, "TLSv1_2 read Server Session Ticket" }, + { end_of_early_data, "TLSv1_2 read Client End Of Early Data" }, + { hello_retry_request, "TLSv1_2 read Server Hello Retry Request" }, + { client_hello, "TLSv1_2 read Client Hello" }, + { server_hello, "TLSv1_2 read Server Hello" }, + { encrypted_extensions, "TLSv1_2 read Server Encrypted Extensions" }, + { server_key_exchange, "TLSv1_2 read Server Key Exchange" }, + { certificate_request, "TLSv1_2 read Server Certificate Request" }, + { server_hello_done, "TLSv1_2 read Server Hello Done" }, + { certificate_verify, "TLSv1_2 read Client Certificate Verify" }, + { client_key_exchange, "TLSv1_2 read Client Key Exchange" }, + { certificate_status, "TLSv1_2 read Server Certificate Status" }, + /* An unrecognized type reports the null state. */ + { 0x7f, "TLSv1_2 Initialization" } + }; + /* Message types both sides send. The string names the side that sent the + * message, which is the peer - the opposite of the object's own side. */ + static const struct { + int type; + const char* asClient; + const char* asServer; + } sidedStates[] = { + { certificate, + "TLSv1_2 read Server Cert", + "TLSv1_2 read Client Cert" }, + { finished, + "TLSv1_2 read Server Finished", + "TLSv1_2 read Client Finished" }, + { key_update, + "TLSv1_2 read server Key Update", + "TLSv1_2 read Client Key Update" }, + { change_cipher_hs, + "TLSv1_2 read Server Change CipherSpec", + "TLSv1_2 read Client Change CipherSpec" } + }; + /* Connection states reported while writing. Every state from the first + * to HANDSHAKE_DONE is listed, so the walk below covers the range. */ + static const struct { + int state; + const char* str; + } writeStates[] = { + { NULL_STATE, + "TLSv1_2 Initialization" }, + { SERVER_HELLOVERIFYREQUEST_COMPLETE, + "TLSv1_2 write Server Hello Verify Request" }, + { SERVER_HELLO_RETRY_REQUEST_COMPLETE, + "TLSv1_2 write Server Hello Retry Request" }, + { SERVER_HELLO_COMPLETE, + "TLSv1_2 write Server Hello" }, + { SERVER_ENCRYPTED_EXTENSIONS_COMPLETE, + "TLSv1_2 write Server Encrypted Extensions" }, + { SERVER_CERT_COMPLETE, + "TLSv1_2 write Server Cert" }, + /* Has no string of its own, so reports the null state. */ + { SERVER_CERT_VERIFY_COMPLETE, + "TLSv1_2 Initialization" }, + { SERVER_KEYEXCHANGE_COMPLETE, + "TLSv1_2 write Server Key Exchange" }, + { SERVER_HELLODONE_COMPLETE, + "TLSv1_2 write Server Hello Done" }, + { SERVER_CHANGECIPHERSPEC_COMPLETE, + "TLSv1_2 write Server Change CipherSpec" }, + { SERVER_FINISHED_COMPLETE, + "TLSv1_2 write Server Finished" }, + { CLIENT_HELLO_RETRY, + "TLSv1_2 write Client Hello" }, + { CLIENT_HELLO_COMPLETE, + "TLSv1_2 write Client Hello" }, + { CLIENT_KEYEXCHANGE_COMPLETE, + "TLSv1_2 write Client Key Exchange" }, + { CLIENT_CHANGECIPHERSPEC_COMPLETE, + "TLSv1_2 write Client Change CipherSpec" }, + { CLIENT_FINISHED_COMPLETE, + "TLSv1_2 write Client Finished" }, + { HANDSHAKE_DONE, + "TLSv1_2 Handshake Done" } + }; + /* Each protocol version has its own set of strings. */ + static const struct { + int major; + int minor; + const char* str; + } protocols[] = { + { SSLv3_MAJOR, SSLv3_MINOR, "SSLv3 read Server Hello" }, + { SSLv3_MAJOR, TLSv1_MINOR, "TLSv1 read Server Hello" }, + { SSLv3_MAJOR, TLSv1_1_MINOR, "TLSv1_1 read Server Hello" }, + { SSLv3_MAJOR, TLSv1_2_MINOR, "TLSv1_2 read Server Hello" }, + { SSLv3_MAJOR, TLSv1_3_MINOR, "TLSv1_3 read Server Hello" }, + { DTLS_MAJOR, DTLS_MINOR, "DTLSv1 read Server Hello" }, + { DTLS_MAJOR, DTLSv1_2_MINOR, "DTLSv1_2 read Server Hello" }, + { DTLS_MAJOR, DTLSv1_3_MINOR, "DTLSv1_3 read Server Hello" }, + /* An unrecognized minor version of either major reports no string. */ + { SSLv3_MAJOR, 0x7f, "" }, + { DTLS_MAJOR, 0x7f, "" } + }; + int i; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + if (ssl != NULL) { + /* Reading: every message type maps to a state string. */ + ssl->cbmode = WOLFSSL_CB_MODE_READ; + for (i = 0; i < (int)XELEM_CNT(readStates); i++) { + ssl->cbtype = readStates[i].type; + ExpectStrEQ(wolfSSL_state_string_long(ssl), readStates[i].str); + } + + /* The messages both sides send name their sender, so the string + * follows the side of the object that read them. */ + for (i = 0; i < (int)XELEM_CNT(sidedStates); i++) { + ssl->cbtype = sidedStates[i].type; + ssl->options.side = WOLFSSL_CLIENT_END; + ExpectStrEQ(wolfSSL_state_string_long(ssl), + sidedStates[i].asClient); + ssl->options.side = WOLFSSL_SERVER_END; + ExpectStrEQ(wolfSSL_state_string_long(ssl), + sidedStates[i].asServer); + /* With no side established the sender cannot be named. */ + ssl->options.side = WOLFSSL_NEITHER_END; + ExpectStrEQ(wolfSSL_state_string_long(ssl), + "TLSv1_2 Initialization"); + } + ssl->options.side = WOLFSSL_CLIENT_END; + + /* Writing: the connection state is reported instead. */ + ssl->cbmode = WOLFSSL_CB_MODE_WRITE; + for (i = 0; i < (int)XELEM_CNT(writeStates); i++) { + ssl->options.clientState = (byte)writeStates[i].state; + ExpectStrEQ(wolfSSL_state_string_long(ssl), writeStates[i].str); + } + + /* Which state is reported follows the side: a server reports its own + * rather than the client's. */ + ssl->options.serverState = SERVER_HELLO_COMPLETE; + ssl->options.clientState = CLIENT_FINISHED_COMPLETE; + ssl->options.side = WOLFSSL_SERVER_END; + ExpectStrEQ(wolfSSL_state_string_long(ssl), + "TLSv1_2 write Server Hello"); + ssl->options.side = WOLFSSL_CLIENT_END; + ExpectStrEQ(wolfSSL_state_string_long(ssl), + "TLSv1_2 write Client Finished"); + + /* Neither reading nor writing: the string carries no direction. */ + ssl->cbmode = 0; + ssl->options.clientState = SERVER_HELLO_COMPLETE; + ExpectStrEQ(wolfSSL_state_string_long(ssl), "TLSv1_2 Server Hello"); + + /* Each protocol version has its own set of strings, and one that is + * not recognized has none. */ + ssl->cbmode = WOLFSSL_CB_MODE_READ; + ssl->cbtype = server_hello; + for (i = 0; i < (int)XELEM_CNT(protocols); i++) { + ssl->version.major = (byte)protocols[i].major; + ssl->version.minor = (byte)protocols[i].minor; + ExpectStrEQ(wolfSSL_state_string_long(ssl), protocols[i].str); + } + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_set_connect_state() and wolfSSL_set_accept_state(). + * + * Each switches the side the object will act as. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_set_connect_accept_state(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) \ + && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) \ + && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + /* A NULL object is ignored by both. */ + wolfSSL_set_connect_state(NULL); + wolfSSL_set_accept_state(NULL); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Created from a client method. */ + ExpectIntEQ(wolfSSL_is_server(ssl), 0); + + /* Switch to acting as a server, then back. */ + wolfSSL_set_accept_state(ssl); + ExpectIntEQ(wolfSSL_is_server(ssl), 1); + wolfSSL_set_connect_state(ssl); + ExpectIntEQ(wolfSSL_is_server(ssl), 0); + + /* Setting the same side again is harmless. */ + wolfSSL_set_connect_state(ssl); + ExpectIntEQ(wolfSSL_is_server(ssl), 0); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_SSL_do_handshake(). + * + * Drives a handshake through the OpenSSL-compatibility entry point, which + * dispatches to connect or accept based on the side of the object. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_SSL_do_handshake(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) \ + && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + int i; + int cRet = WOLFSSL_FATAL_ERROR; + int sRet = WOLFSSL_FATAL_ERROR; + + /* A NULL object is rejected. */ + ExpectIntEQ(wolfSSL_SSL_do_handshake(NULL), WOLFSSL_FAILURE); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + /* Alternate the two sides until both report the handshake is complete. */ + for (i = 0; i < 10; i++) { + if (cRet != WOLFSSL_SUCCESS) { + cRet = wolfSSL_SSL_do_handshake(ssl_c); + } + if (sRet != WOLFSSL_SUCCESS) { + sRet = wolfSSL_SSL_do_handshake(ssl_s); + } + if ((cRet == WOLFSSL_SUCCESS) && (sRet == WOLFSSL_SUCCESS)) { + break; + } + } + ExpectIntEQ(cRet, WOLFSSL_SUCCESS); + ExpectIntEQ(sRet, WOLFSSL_SUCCESS); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_SSL_in_init(), wolfSSL_SSL_in_before() and + * wolfSSL_SSL_in_connect_init(). + * + * The three report where in the handshake the object is. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_SSL_in_init_hs(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) \ + && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + /* NULL objects report no state. */ + ExpectIntEQ(wolfSSL_SSL_in_before(NULL), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_SSL_in_connect_init(NULL), WOLFSSL_FAILURE); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + /* Nothing has happened yet. */ + ExpectIntEQ(wolfSSL_SSL_in_before(ssl_c), 1); + ExpectIntEQ(wolfSSL_SSL_in_init(ssl_c), 1); + ExpectIntEQ(wolfSSL_SSL_in_connect_init(ssl_c), 0); + + /* A partial handshake leaves the client mid-connect. */ + ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_SSL_in_connect_init(ssl_c), 1); + ExpectIntEQ(wolfSSL_SSL_in_before(ssl_c), 1); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Once complete, none of the in-progress states hold. */ + ExpectIntEQ(wolfSSL_SSL_in_init(ssl_c), 0); + ExpectIntEQ(wolfSSL_SSL_in_before(ssl_c), 0); + ExpectIntEQ(wolfSSL_SSL_in_connect_init(ssl_c), 0); + ExpectIntEQ(wolfSSL_SSL_in_init(ssl_s), 0); + ExpectIntEQ(wolfSSL_SSL_in_connect_init(ssl_s), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_is_init_finished(). + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_is_init_finished(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + /* A NULL object has not finished. */ + ExpectIntEQ(wolfSSL_is_init_finished(NULL), 0); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + ExpectIntEQ(wolfSSL_is_init_finished(ssl_c), 0); + ExpectIntEQ(wolfSSL_is_init_finished(ssl_s), 0); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + ExpectIntEQ(wolfSSL_is_init_finished(ssl_c), 1); + ExpectIntEQ(wolfSSL_is_init_finished(ssl_s), 1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_HANDSHAKE_DONE_CB) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) +/* Count of handshake-done callback invocations. */ +static int test_ssl_hs_done_calls = 0; + +/* Handshake-done callback recording that it ran. + * + * @param [in] ssl SSL/TLS object. Unused. + * @param [in] user_ctx User context; expected to be &test_ssl_hs_done_calls. + * @return 0 to let the handshake complete. + */ +static int test_ssl_hs_done_cb(WOLFSSL* ssl, void* user_ctx) +{ + (void)ssl; + + if (user_ctx == &test_ssl_hs_done_calls) { + test_ssl_hs_done_calls++; + } + + return 0; +} +#endif + +/* Test wolfSSL_SetHsDoneCb(). + * + * The registered callback must run when the handshake completes. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_SetHsDoneCb(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) \ + && !defined(NO_HANDSHAKE_DONE_CB) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + /* A NULL object is rejected. */ + ExpectIntEQ(wolfSSL_SetHsDoneCb(NULL, test_ssl_hs_done_cb, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + test_ssl_hs_done_calls = 0; + ExpectIntEQ(wolfSSL_SetHsDoneCb(ssl_c, test_ssl_hs_done_cb, + &test_ssl_hs_done_calls), WOLFSSL_SUCCESS); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* The client's callback ran; the server had none registered. */ + ExpectIntEQ(test_ssl_hs_done_calls, 1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test the public-key callback context accessors. + * + * Each Set/Get pair stores and returns an opaque application pointer, and each + * accessor tolerates a NULL object. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_pk_callback_ctx(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PK_CALLBACKS) && !defined(NO_CERTS) \ + && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + int marker = 0; + void* p = ▮ + + /* NULL objects are tolerated by every accessor. */ + ExpectNull(wolfSSL_GetGenPreMasterCtx(NULL)); + ExpectNull(wolfSSL_GetGenMasterSecretCtx(NULL)); + ExpectNull(wolfSSL_GetGenSessionKeyCtx(NULL)); + ExpectNull(wolfSSL_GetEncryptKeysCtx(NULL)); + ExpectNull(wolfSSL_GetTlsFinishedCtx(NULL)); + /* The VerifyMac accessors only exist when a MAC is used, so an AEAD-only + * build does not have them. */ +#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY) + ExpectNull(wolfSSL_GetVerifyMacCtx(NULL)); +#endif + wolfSSL_SetGenPreMasterCtx(NULL, p); + wolfSSL_SetGenMasterSecretCtx(NULL, p); + wolfSSL_SetGenSessionKeyCtx(NULL, p); + wolfSSL_SetEncryptKeysCtx(NULL, p); + wolfSSL_SetTlsFinishedCtx(NULL, p); +#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY) + wolfSSL_SetVerifyMacCtx(NULL, p); +#endif + wolfSSL_CTX_SetGenPreMasterCb(NULL, NULL); + wolfSSL_CTX_SetGenMasterSecretCb(NULL, NULL); + wolfSSL_CTX_SetGenSessionKeyCb(NULL, NULL); + wolfSSL_CTX_SetEncryptKeysCb(NULL, NULL); + wolfSSL_CTX_SetTlsFinishedCb(NULL, NULL); +#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY) + wolfSSL_CTX_SetVerifyMacCb(NULL, NULL); +#endif + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Nothing stored yet. */ + ExpectNull(wolfSSL_GetGenPreMasterCtx(ssl)); + ExpectNull(wolfSSL_GetGenMasterSecretCtx(ssl)); + ExpectNull(wolfSSL_GetGenSessionKeyCtx(ssl)); + ExpectNull(wolfSSL_GetEncryptKeysCtx(ssl)); + ExpectNull(wolfSSL_GetTlsFinishedCtx(ssl)); +#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY) + ExpectNull(wolfSSL_GetVerifyMacCtx(ssl)); +#endif + + /* Each pair round-trips the pointer it was given. */ + wolfSSL_SetGenPreMasterCtx(ssl, p); + ExpectPtrEq(wolfSSL_GetGenPreMasterCtx(ssl), p); + wolfSSL_SetGenMasterSecretCtx(ssl, p); + ExpectPtrEq(wolfSSL_GetGenMasterSecretCtx(ssl), p); + wolfSSL_SetGenSessionKeyCtx(ssl, p); + ExpectPtrEq(wolfSSL_GetGenSessionKeyCtx(ssl), p); + wolfSSL_SetEncryptKeysCtx(ssl, p); + ExpectPtrEq(wolfSSL_GetEncryptKeysCtx(ssl), p); + wolfSSL_SetTlsFinishedCtx(ssl, p); + ExpectPtrEq(wolfSSL_GetTlsFinishedCtx(ssl), p); +#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY) + wolfSSL_SetVerifyMacCtx(ssl, p); + ExpectPtrEq(wolfSSL_GetVerifyMacCtx(ssl), p); +#endif + + /* The context-level callback setters accept a NULL callback. */ + wolfSSL_CTX_SetGenPreMasterCb(ctx, NULL); + wolfSSL_CTX_SetGenMasterSecretCb(ctx, NULL); + wolfSSL_CTX_SetGenSessionKeyCb(ctx, NULL); + wolfSSL_CTX_SetEncryptKeysCb(ctx, NULL); + wolfSSL_CTX_SetTlsFinishedCb(ctx, NULL); +#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY) + wolfSSL_CTX_SetVerifyMacCb(ctx, NULL); +#endif + +#ifdef HAVE_EXTENDED_MASTER + ExpectNull(wolfSSL_GetGenExtMasterSecretCtx(NULL)); + wolfSSL_SetGenExtMasterSecretCtx(NULL, p); + wolfSSL_CTX_SetGenExtMasterSecretCb(NULL, NULL); + ExpectNull(wolfSSL_GetGenExtMasterSecretCtx(ssl)); + wolfSSL_SetGenExtMasterSecretCtx(ssl, p); + ExpectPtrEq(wolfSSL_GetGenExtMasterSecretCtx(ssl), p); + wolfSSL_CTX_SetGenExtMasterSecretCb(ctx, NULL); +#endif + +#ifdef WOLFSSL_TLS13 + ExpectNull(wolfSSL_GetHKDFExtractCtx(NULL)); + wolfSSL_SetHKDFExtractCtx(NULL, p); + wolfSSL_CTX_SetHKDFExtractCb(NULL, NULL); + ExpectNull(wolfSSL_GetHKDFExtractCtx(ssl)); + wolfSSL_SetHKDFExtractCtx(ssl, p); + ExpectPtrEq(wolfSSL_GetHKDFExtractCtx(ssl), p); + wolfSSL_CTX_SetHKDFExtractCb(ctx, NULL); + wolfSSL_CTX_SetHKDFExpandLabelCb(NULL, NULL); + wolfSSL_CTX_SetHKDFExpandLabelCb(ctx, NULL); +#endif + +#ifdef WOLFSSL_PUBLIC_ASN + wolfSSL_CTX_SetProcessPeerCertCb(NULL, NULL); + wolfSSL_CTX_SetProcessPeerCertCb(ctx, NULL); +#endif + wolfSSL_CTX_SetProcessServerSigKexCb(NULL, NULL); + wolfSSL_CTX_SetProcessServerSigKexCb(ctx, NULL); + wolfSSL_CTX_SetPerformTlsRecordProcessingCb(NULL, NULL); + wolfSSL_CTX_SetPerformTlsRecordProcessingCb(ctx, NULL); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test that a static ECC key survives wolfSSL_set_accept_state(). + * + * The check that the key is an EC key decodes it, so under + * WOLFSSL_BLIND_PRIVATE_KEY it has to be unmasked first. Decoding the masked + * bytes always fails, which silently withdrew the ECC capabilities for a key + * that was perfectly good. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_set_accept_state_static_ecc(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_EXTRA) || \ + defined(WOLFSSL_WPAS_SMALL)) && \ + defined(HAVE_ECC) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + /* The key is only re-checked when a client object is switched to being a + * server, so start from a client. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, eccCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, eccKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) { + ssl->options.haveStaticECC = 1; + ssl->options.haveECC = 1; + ssl->options.haveECDSAsig = 1; + + wolfSSL_set_accept_state(ssl); + + /* The key really is an EC key, so nothing may be withdrawn. Without + * unmasking, the decode fails and all three are cleared. */ + ExpectIntEQ(ssl->options.haveStaticECC, 1); + ExpectIntEQ(ssl->options.haveECC, 1); + ExpectIntEQ(ssl->options.haveECDSAsig, 1); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test the client-to-server switch in wolfSSL_set_accept_state(). + * + * Switching a client object to act as a server re-checks a static ECC key and + * adopts any DH parameters the context has since acquired. A key that is not + * an EC key must clear the ECC capability flags rather than be trusted. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_set_accept_state_reinit(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) \ + && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) \ + && !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) && \ + defined(WOLFSSL_PEM_TO_DER) +/* Declared only when at least one of the cases below is built. */ +#if defined(HAVE_ECC) || (!defined(NO_DH) && !defined(NO_RSA)) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; +#endif + +#if defined(HAVE_ECC) && !defined(NO_RSA) + /* An RSA key cannot be decoded as an EC key, so the static ECC + * capability must be withdrawn. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + if (ssl != NULL) { + ExpectNotNull(ssl->buffers.key); + ssl->options.haveStaticECC = 1; + ssl->options.haveECC = 1; + ssl->options.haveECDSAsig = 1; + + wolfSSL_set_accept_state(ssl); + + ExpectIntEQ(ssl->options.haveStaticECC, 0); + ExpectIntEQ(ssl->options.haveECC, 0); + ExpectIntEQ(ssl->options.haveECDSAsig, 0); + ExpectIntEQ(wolfSSL_is_server(ssl), 1); + } + + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; +#endif /* HAVE_ECC && !NO_RSA */ + +#ifdef HAVE_ECC + /* A real EC key decodes, so the flags are left alone. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, eccKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + if (ssl != NULL) { + ssl->options.haveStaticECC = 1; + ssl->options.haveECC = 1; + + wolfSSL_set_accept_state(ssl); + + ExpectIntEQ(ssl->options.haveStaticECC, 1); + ExpectIntEQ(ssl->options.haveECC, 1); + } + + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; +#endif /* HAVE_ECC */ + +#if !defined(NO_DH) && (!defined(NO_RSA) || defined(HAVE_ECC)) + /* DH parameters added to the context after the object was created are + * picked up when the object becomes a server. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Not inherited at creation time - the context had none then. */ + if (ssl != NULL) { + ExpectIntEQ(ssl->options.haveDH, 0); + } + + ExpectIntEQ(wolfSSL_CTX_SetTmpDH_file(ctx, dhParamFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + + wolfSSL_set_accept_state(ssl); + + if ((ssl != NULL) && (ctx != NULL)) { + ExpectIntEQ(ssl->options.haveDH, 1); + ExpectPtrEq(ssl->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer); + ExpectPtrEq(ssl->buffers.serverDH_G.buffer, ctx->serverDH_G.buffer); + } + + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; +#endif /* !NO_DH */ +#endif + return EXPECT_RESULT(); +} + +/* Test the argument checks of wolfSSL_negotiate() and wolfSSL_connect_cert(). + * + * Both reject a NULL object, each with its own failure code. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_negotiate_bad_args(void) +{ + EXPECT_DECLS; +#ifndef NO_TLS + ExpectIntEQ(wolfSSL_negotiate(NULL), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); +#if !defined(NO_WOLFSSL_CLIENT) + ExpectIntEQ(wolfSSL_connect_cert(NULL), WC_NO_ERR_TRACE(WOLFSSL_FAILURE)); +#endif +#endif + return EXPECT_RESULT(); +} + +#if defined(WOLFSSL_QUIC) && \ + (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) +/* QUIC callbacks that do nothing. wolfSSL_SSL_do_handshake() only needs the + * method to be set so that the object counts as a QUIC one. */ +static int test_ssl_hs_quic_secrets(WOLFSSL* ssl, + WOLFSSL_ENCRYPTION_LEVEL level, const uint8_t* rx, const uint8_t* tx, + size_t len) +{ + (void)ssl; + (void)level; + (void)rx; + (void)tx; + (void)len; + + return 1; +} + +/* QUIC callback that accepts handshake data and does nothing with it. + * + * @param [in] ssl SSL/TLS object. Unused. + * @param [in] level Encryption level. Unused. + * @param [in] data Handshake data. Unused. + * @param [in] len Length of the data. Unused. + * @return 1 always. + */ +static int test_ssl_hs_quic_add_data(WOLFSSL* ssl, + WOLFSSL_ENCRYPTION_LEVEL level, const uint8_t* data, size_t len) +{ + (void)ssl; + (void)level; + (void)data; + (void)len; + + return 1; +} + +/* QUIC callback that reports the data as flushed without doing anything. + * + * @param [in] ssl SSL/TLS object. Unused. + * @return 1 always. + */ +static int test_ssl_hs_quic_flush(WOLFSSL* ssl) +{ + (void)ssl; + + return 1; +} + +/* QUIC callback that accepts an alert and does nothing with it. + * + * @param [in] ssl SSL/TLS object. Unused. + * @param [in] level Encryption level. Unused. + * @param [in] err Alert to send. Unused. + * @return 1 always. + */ +static int test_ssl_hs_quic_alert(WOLFSSL* ssl, + WOLFSSL_ENCRYPTION_LEVEL level, uint8_t err) +{ + (void)ssl; + (void)level; + (void)err; + + return 1; +} + +static WOLFSSL_QUIC_METHOD test_ssl_hs_quic_method = { + test_ssl_hs_quic_secrets, + test_ssl_hs_quic_add_data, + test_ssl_hs_quic_flush, + test_ssl_hs_quic_alert +}; +#endif + +/* Test that wolfSSL_SSL_do_handshake() dispatches QUIC objects to the QUIC + * handshake rather than to connect or accept. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_SSL_do_handshake_quic(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_QUIC) && \ + (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Not a QUIC object yet. */ + if (ssl != NULL) { + ExpectNull(ssl->quic.method); + } + + ExpectIntEQ(wolfSSL_set_quic_method(ssl, &test_ssl_hs_quic_method), + WOLFSSL_SUCCESS); + if (ssl != NULL) { + ExpectNotNull(ssl->quic.method); + } + + /* The QUIC handshake is attempted. With no transport parameters set and + * nothing to read it cannot complete, so only the dispatch is checked. */ + ExpectIntNE(wolfSSL_SSL_do_handshake(ssl), WOLFSSL_SUCCESS); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test that wolfSSL_set_connect_state() discards server DH parameters. + * + * A client generates its own DH parameters, so any server ones are dropped. + * Parameters the object owns are freed; parameters merely borrowed from the + * context are only unlinked, since the context still owns them. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_set_connect_state_dh(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) \ + && !defined(NO_DH) && !defined(NO_WOLFSSL_SERVER) \ + && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_FILESYSTEM) \ + && !defined(NO_CERTS) && !defined(NO_TLS) && !defined(NO_RSA) \ + && !defined(WOLFSSL_NO_TLS12) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL* ssl2 = NULL; + + /* Parameters owned by the object are freed. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + ExpectIntEQ(wolfSSL_SetTmpDH_file(ssl, dhParamFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + if (ssl != NULL) { + ExpectNotNull(ssl->buffers.serverDH_P.buffer); + ExpectNotNull(ssl->buffers.serverDH_G.buffer); + ExpectIntEQ(ssl->buffers.weOwnDH, 1); + } + + wolfSSL_set_connect_state(ssl); + + /* Freed and unlinked; the object is now a client. */ + if (ssl != NULL) { + ExpectNull(ssl->buffers.serverDH_P.buffer); + ExpectNull(ssl->buffers.serverDH_G.buffer); + } + ExpectIntEQ(wolfSSL_is_server(ssl), 0); + + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; + + /* Parameters borrowed from the context are left for the context to free. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_SetTmpDH_file(ctx, dhParamFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Inherited from the context, so not owned here. */ + if (ssl != NULL) { + ExpectPtrEq(ssl->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer); + ExpectIntEQ(ssl->buffers.weOwnDH, 0); + } + + wolfSSL_set_connect_state(ssl); + + if (ssl != NULL) { + ExpectNull(ssl->buffers.serverDH_P.buffer); + ExpectNull(ssl->buffers.serverDH_G.buffer); + } + /* The context kept its own copy, so it can still be used. */ + if (ctx != NULL) { + ExpectNotNull(ctx->serverDH_P.buffer); + ExpectNotNull(ctx->serverDH_G.buffer); + } + ExpectNotNull(ssl2 = wolfSSL_new(ctx)); + if (ssl2 != NULL) { + ExpectPtrEq(ssl2->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer); + } + + wolfSSL_free(ssl2); + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* The helpers below only install I/O callbacks, so they need nothing beyond + * TLS 1.2 itself. The guard is nevertheless the union of their six callers' + * guards, so the block is neither compiled without a caller nor missing when + * one is present. The first term covers the five callers that drive a + * handshake with real credentials; the second covers the memio caller, which + * runs without RSA when raw public keys are enabled. */ +#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \ + ((!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \ + defined(WOLFSSL_PEM_TO_DER)) || \ + (defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER))) +/* Transport send callback that always fails. + * + * Makes every handshake message send fail so that the error handling of each + * step of wolfSSL_connect()/wolfSSL_accept() can be reached. + * + * @param [in] ssl SSL/TLS object. Unused. + * @param [in] buf Data to send. Unused. + * @param [in] sz Length of data. Unused. + * @param [in] ctx I/O context. Unused. + * @return WOLFSSL_CBIO_ERR_GENERAL always. + */ +static int test_ssl_hs_send_fail(WOLFSSL* ssl, char* buf, int sz, void* ctx) +{ + (void)ssl; + (void)buf; + (void)sz; + (void)ctx; + + return WOLFSSL_CBIO_ERR_GENERAL; +} + +/* Transport receive callback that always fails. + * + * @param [in] ssl SSL/TLS object. Unused. + * @param [in] buf Buffer to fill. Unused. + * @param [in] sz Size of buffer. Unused. + * @param [in] ctx I/O context. Unused. + * @return WOLFSSL_CBIO_ERR_GENERAL always. + */ +static int test_ssl_hs_recv_fail(WOLFSSL* ssl, char* buf, int sz, void* ctx) +{ + (void)ssl; + (void)buf; + (void)sz; + (void)ctx; + + return WOLFSSL_CBIO_ERR_GENERAL; +} + +/* Require server credentials so that the checks in wolfSSL_accept() are made. + * + * Anonymous cipher suites are available by default in some builds and they let + * a server run without a certificate, which skips the credential checks. + * + * Only used by the tests that need server credentials, so it is guarded to + * match them rather than the callbacks above. + * + * @param [in, out] ssl SSL/TLS object. + */ +#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + defined(WOLFSSL_PEM_TO_DER) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) +static void test_ssl_hs_need_creds(WOLFSSL* ssl) +{ + if (ssl != NULL) { + #ifdef HAVE_ANON + ssl->options.useAnon = 0; + #endif + #ifndef NO_PSK + ssl->options.havePSK = 0; + #endif + #ifdef WOLFSSL_MULTICAST + ssl->options.haveMcast = 0; + #endif + } +} +#endif + +/* Replace the transport of an object with one that always fails. + * + * Lets a handshake step be driven to its error handling without a peer and + * without blocking on a socket. + * + * @param [in, out] ssl SSL/TLS object. + */ +static void test_ssl_hs_break_io(WOLFSSL* ssl) +{ + if (ssl != NULL) { + wolfSSL_SSLSetIOSend(ssl, test_ssl_hs_send_fail); + wolfSSL_SSLSetIORecv(ssl, test_ssl_hs_recv_fail); + } +} +#endif + +/* Test the argument and side checks of wolfSSL_connect(). + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_connect_bad_args(void) +{ + EXPECT_DECLS; +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + /* A NULL object is rejected. */ + ExpectIntEQ(wolfSSL_connect(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* Connecting with a server object is a side error. Credentials are loaded + * because a server object cannot be created without them when there are no + * anonymous cipher suites. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + test_ssl_hs_break_io(ssl); + ExpectIntEQ(wolfSSL_connect(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl, 0), WC_NO_ERR_TRACE(SIDE_ERROR)); + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; + + /* An unrecognized connect state is rejected. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + test_ssl_hs_break_io(ssl); + if (ssl != NULL) { + ssl->options.connectState = 0x7f; + ExpectIntEQ(wolfSSL_connect(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test the argument, side and credential checks of wolfSSL_accept(). + * + * A server needs both a certificate and a private key unless a certificate + * setup callback is installed to supply them later. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_accept_bad_args(void) +{ + EXPECT_DECLS; +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) && !defined(WOLFSSL_NO_TLS12) \ + && defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + /* A NULL object is rejected. */ + ExpectIntEQ(wolfSSL_accept(NULL), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + + /* Accepting with a client object is a side error. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + test_ssl_hs_break_io(ssl); + ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl, 0), WC_NO_ERR_TRACE(SIDE_ERROR)); + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; + + /* No certificate. It is hidden from the SSL object rather than left + * unloaded, because a server object cannot be created without credentials + * when there are no anonymous cipher suites. The pointer is put back + * afterwards so that the object still disposes of it: depending on the + * build the object owns this buffer rather than the context. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + test_ssl_hs_break_io(ssl); + test_ssl_hs_need_creds(ssl); + if (ssl != NULL) { + DerBuffer* savedCert = ssl->buffers.certificate; + + ssl->buffers.certificate = NULL; + ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl, 0), WC_NO_ERR_TRACE(NO_PRIVATE_KEY)); + ssl->buffers.certificate = savedCert; + } + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; + + /* Certificate present but no private key. The key is dropped from the SSL + * object rather than left unloaded so that the certificate check above is + * passed first; the context still owns the real key. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + test_ssl_hs_break_io(ssl); + test_ssl_hs_need_creds(ssl); + if (ssl != NULL) { + DerBuffer* savedKey = ssl->buffers.key; + + ExpectNotNull(ssl->buffers.certificate); + ssl->buffers.key = NULL; + ssl->devId = INVALID_DEVID; + ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl, 0), WC_NO_ERR_TRACE(NO_PRIVATE_KEY)); + /* Put it back so the object disposes of it. */ + ssl->buffers.key = savedKey; + } + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; + + /* An unrecognized accept state is rejected. Credentials are loaded so the + * checks above are passed. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + test_ssl_hs_break_io(ssl); + if (ssl != NULL) { + ssl->options.acceptState = 0x7f; + ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test that each step of wolfSSL_connect() copes with a failing transport. + * + * The object is placed in each connect state in turn with a transport that + * always fails, so that every step is entered. The call must reach a + * definite outcome rather than hang; which error arm ran is not + * observable from here. The object is + * discarded after each attempt because a failed handshake is not resumable. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_connect_step_failures(void) +{ + EXPECT_DECLS; +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \ + defined(WOLFSSL_PEM_TO_DER) + static const int states[] = { + CONNECT_BEGIN, CLIENT_HELLO_SENT, HELLO_AGAIN, HELLO_AGAIN_REPLY, + FIRST_REPLY_DONE, FIRST_REPLY_FIRST, FIRST_REPLY_SECOND, + FIRST_REPLY_THIRD, FIRST_REPLY_FOURTH, FINISHED_DONE, + SECOND_REPLY_DONE + }; + /* Index of the first state with nothing left to send. Everything before + * it must fail on the broken transport. */ + static const int firstDone = 10; + int i; + + for (i = 0; i < (int)(sizeof(states) / sizeof(states[0])); i++) { + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + int ret = 0; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx, caCertFile, NULL), + WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + test_ssl_hs_break_io(ssl); + + if (ssl != NULL) { + /* Pretend the handshake reached this step, and that the peer was + * authenticated so the fail-safe check is passed. A client + * certificate is requested so the certificate steps are taken. */ + ssl->options.connectState = (byte)states[i]; + ssl->options.peerAuthGood = 1; + ssl->options.sendVerify = SEND_CERT; + ssl->options.resuming = 0; + + ret = wolfSSL_connect(ssl); + + /* A step that sends must fail on the broken transport; only a + * state with nothing left to send may report success. Both arms + * are cast as they come from different enumerations, which a C++ + * compiler will not mix in a conditional. */ + ExpectIntEQ(ret, (i >= firstDone) ? (int)WOLFSSL_SUCCESS : + (int)WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); + } +#endif + return EXPECT_RESULT(); +} + +/* Test that each step of wolfSSL_accept() copes with a failing transport. + * + * The object is placed in each accept state in turn with a transport that + * always fails, so that every step is entered. The call must reach a + * definite outcome rather than hang; which error arm ran is not + * observable from here. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_accept_step_failures(void) +{ + EXPECT_DECLS; +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \ + defined(WOLFSSL_PEM_TO_DER) + static const int states[] = { + ACCEPT_BEGIN, ACCEPT_BEGIN_RENEG, ACCEPT_CLIENT_HELLO_DONE, + ACCEPT_HELLO_RETRY_REQUEST_DONE, ACCEPT_FIRST_REPLY_DONE, + SERVER_HELLO_SENT, CERT_SENT, CERT_VERIFY_SENT, CERT_STATUS_SENT, + KEY_EXCHANGE_SENT, CERT_REQ_SENT, SERVER_HELLO_DONE, + ACCEPT_SECOND_REPLY_DONE, TICKET_SENT, CHANGE_CIPHER_SENT, + ACCEPT_FINISHED_DONE, ACCEPT_THIRD_REPLY_DONE + }; + /* Index of the first state with nothing left to send. Everything before + * it must fail on the broken transport. */ + static const int firstDone = 15; + int i; + + for (i = 0; i < (int)(sizeof(states) / sizeof(states[0])); i++) { + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + int ret = 0; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + test_ssl_hs_break_io(ssl); + + if (ssl != NULL) { + ssl->options.acceptState = (byte)states[i]; + /* Ask for a client certificate so the certificate request and + * verify steps are taken. */ + ssl->options.verifyPeer = 1; + ssl->options.sendVerify = SEND_CERT; + /* Claim the client was authenticated so the fail-safe checks are + * passed and the steps after them are reached. */ + ssl->options.peerAuthGood = 1; + #ifdef HAVE_SESSION_TICKET + /* Ask for a session ticket so that step is taken too. */ + ssl->options.createTicket = 1; + ssl->options.noTicketTls12 = 0; + #endif + + ret = wolfSSL_accept(ssl); + + /* A step that sends fails on the broken transport. The states at + * the end of the handshake have nothing left to send and so + * report success. Both arms are cast as they come from different + * enumerations, which a C++ compiler will not mix in a + * conditional. */ + ExpectIntEQ(ret, (i >= firstDone) ? (int)WOLFSSL_SUCCESS : + (int)WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); + } +#endif + return EXPECT_RESULT(); +} + +/* Test that a failure to flush the output buffer is reported. + * + * A send that reports "want write" leaves the message in the output buffer. + * Failing the send on the next call makes the flush at the start of + * wolfSSL_connect() and wolfSSL_accept() fail. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_hs_send_buffered_fail(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + /* Client: the ClientHello is left unsent. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + test_memio_simulate_want_write(&test_ctx, 1, 1); + ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_WRITE); + if (ssl_c != NULL) { + ExpectIntGT(ssl_c->buffers.outputBuffer.length, 0); + } + /* The retry now fails outright. */ + test_ssl_hs_break_io(ssl_c); + ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WC_NO_ERR_TRACE(SOCKET_ERROR_E)); + + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + wolfSSL_CTX_free(ctx_c); + ctx_c = NULL; + wolfSSL_CTX_free(ctx_s); + ctx_s = NULL; + + /* Server: the ClientHello is delivered so the server has a reply to send, + * and that reply is left unsent. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ); + test_memio_simulate_want_write(&test_ctx, 0, 1); + ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WOLFSSL_ERROR_WANT_WRITE); + if (ssl_s != NULL) { + ExpectIntGT(ssl_s->buffers.outputBuffer.length, 0); + } + test_ssl_hs_break_io(ssl_s); + ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WC_NO_ERR_TRACE(SOCKET_ERROR_E)); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +#if defined(WOLFSSL_CALLBACKS) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) +/* Timeout callback that does nothing. + * + * Supplying one is what makes wolfSSL_ex_wrapper() set up the timer. + * + * @param [in] info Timeout information. Unused. + * @return 0 always. + */ +static int test_ssl_hs_to_cb(TimeoutInfo* info) +{ + (void)info; + return 0; +} +#endif + +/* Test that connecting with no side established is reported as a failure. + * + * wolfSSL_ex_wrapper() dispatches on the side and leaves its result alone + * when neither the client nor the server branch runs, so the failure it was + * seeded with has to survive the timer setup that precedes the dispatch. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_connect_ex_no_side(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CALLBACKS) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL_TIMEVAL timeout; + + /* Long enough that it cannot fire while the test runs. */ + timeout.tv_sec = 60; + timeout.tv_usec = 0; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) { + /* With a timeout callback the timer is set up first. Its success must + * not be mistaken for the handshake's. */ + ssl->options.side = WOLFSSL_NEITHER_END; + ExpectIntEQ(wolfSSL_connect_ex(ssl, NULL, test_ssl_hs_to_cb, timeout), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + /* And without one, where no timer is set up at all. */ + ssl->options.side = WOLFSSL_NEITHER_END; + ExpectIntEQ(wolfSSL_connect_ex(ssl, NULL, NULL, timeout), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test that a failure to send a pending alert is reported. + * + * An alert that could not be sent earlier is retried at the start of + * wolfSSL_connect() and wolfSSL_accept(). + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_hs_retry_alert_fail(void) +{ + EXPECT_DECLS; +#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + +#ifndef NO_WOLFSSL_CLIENT + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx, caCertFile, NULL), + WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + test_ssl_hs_break_io(ssl); + if (ssl != NULL) { + ssl->pendingAlert.code = unexpected_message; + ssl->pendingAlert.level = alert_fatal; + ExpectIntEQ(wolfSSL_connect(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl, 0), + WC_NO_ERR_TRACE(SOCKET_ERROR_E)); + } + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; +#endif + +#ifndef NO_WOLFSSL_SERVER + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + test_ssl_hs_break_io(ssl); + if (ssl != NULL) { + ssl->pendingAlert.code = unexpected_message; + ssl->pendingAlert.level = alert_fatal; + ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl, 0), + WC_NO_ERR_TRACE(SOCKET_ERROR_E)); + } + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_HANDSHAKE_DONE_CB) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) +/* Handshake done callback that refuses to let the handshake complete. + * + * @param [in] ssl SSL object. Unused. + * @param [in] user_ctx User context. Unused. + * @return A negative value to stop the handshake. + */ +static int test_ssl_hs_done_cb_fail(WOLFSSL* ssl, void* user_ctx) +{ + (void)ssl; + (void)user_ctx; + return -4242; +} +#endif + +/* Test that a handshake done callback reporting an error stops the handshake. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_hs_done_cb_error(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_HANDSHAKE_DONE_CB) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) + int i; + + /* Register the callback on the client and then on the server. */ + for (i = 0; i < 2; i++) { + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_SetHsDoneCb((i == 0) ? ssl_c : ssl_s, + test_ssl_hs_done_cb_fail, NULL), WOLFSSL_SUCCESS); + + /* The handshake cannot complete because the callback fails. */ + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_get_error((i == 0) ? ssl_c : ssl_s, 0), -4242); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } +#endif + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_EXTRA) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) +static int test_ssl_hs_info_calls_c = 0; +static int test_ssl_hs_info_calls_s = 0; + +/* Information callback for the client, recording that it was called. + * + * @param [in] ssl SSL object. Unused. + * @param [in] type Type of event. Unused. + * @param [in] val Value associated with event. Unused. + */ +static void test_ssl_hs_info_cb_c(const WOLFSSL* ssl, int type, int val) +{ + (void)ssl; + (void)type; + (void)val; + test_ssl_hs_info_calls_c++; +} + +/* Information callback for the server, recording that it was called. + * + * @param [in] ssl SSL object. Unused. + * @param [in] type Type of event. Unused. + * @param [in] val Value associated with event. Unused. + */ +static void test_ssl_hs_info_cb_s(const WOLFSSL* ssl, int type, int val) +{ + (void)ssl; + (void)type; + (void)val; + test_ssl_hs_info_calls_s++; +} +#endif + +/* Test that the information callback is called when a handshake starts. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_hs_info_cb(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + test_ssl_hs_info_calls_c = 0; + test_ssl_hs_info_calls_s = 0; + wolfSSL_set_info_callback(ssl_c, test_ssl_hs_info_cb_c); + wolfSSL_set_info_callback(ssl_s, test_ssl_hs_info_cb_s); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Each side reported against its own callback. */ + ExpectIntGT(test_ssl_hs_info_calls_c, 0); + ExpectIntGT(test_ssl_hs_info_calls_s, 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ssl_hs.h b/tests/api/test_ssl_hs.h new file mode 100644 index 00000000000..f326fc1f6b0 --- /dev/null +++ b/tests/api/test_ssl_hs.h @@ -0,0 +1,74 @@ +/* test_ssl_hs.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef TESTS_API_SSL_HS_H +#define TESTS_API_SSL_HS_H + +#include + +int test_wolfSSL_state_string_long(void); +int test_wolfSSL_state_string_long_states(void); +int test_wolfSSL_set_connect_accept_state(void); +int test_wolfSSL_SSL_do_handshake(void); +int test_wolfSSL_SSL_in_init_hs(void); +int test_wolfSSL_is_init_finished(void); +int test_wolfSSL_SetHsDoneCb(void); +int test_wolfSSL_pk_callback_ctx(void); +int test_wolfSSL_set_accept_state_reinit(void); +int test_wolfSSL_set_accept_state_static_ecc(void); +int test_wolfSSL_negotiate_bad_args(void); +int test_wolfSSL_SSL_do_handshake_quic(void); +int test_wolfSSL_set_connect_state_dh(void); +int test_wolfSSL_connect_bad_args(void); +int test_wolfSSL_accept_bad_args(void); +int test_wolfSSL_connect_step_failures(void); +int test_wolfSSL_accept_step_failures(void); +int test_wolfSSL_hs_send_buffered_fail(void); +int test_wolfSSL_hs_retry_alert_fail(void); +int test_wolfSSL_connect_ex_no_side(void); +int test_wolfSSL_hs_done_cb_error(void); +int test_wolfSSL_hs_info_cb(void); + +#define TEST_SSL_HS_DECLS \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_state_string_long), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_state_string_long_states), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_set_connect_accept_state), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_SSL_do_handshake), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_SSL_in_init_hs), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_is_init_finished), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_SetHsDoneCb), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_pk_callback_ctx), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_set_accept_state_reinit), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_set_accept_state_static_ecc), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_negotiate_bad_args), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_SSL_do_handshake_quic), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_set_connect_state_dh), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_connect_bad_args), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_accept_bad_args), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_connect_step_failures), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_accept_step_failures), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_hs_send_buffered_fail), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_hs_retry_alert_fail), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_connect_ex_no_side), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_hs_done_cb_error), \ + TEST_DECL_GROUP("ssl_hs", test_wolfSSL_hs_info_cb) + +#endif /* TESTS_API_SSL_HS_H */ diff --git a/tests/api/test_ssl_rw.c b/tests/api/test_ssl_rw.c new file mode 100644 index 00000000000..b914b9194f2 --- /dev/null +++ b/tests/api/test_ssl_rw.c @@ -0,0 +1,936 @@ +/* test_ssl_rw.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#include +#include + +#include +#include + +/* Tests for the application read/write APIs in src/ssl_api_rw.c (moved from + * ssl.c). These cover functions not already exercised elsewhere in api.c. */ + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) && \ + !defined(USE_WINDOWS_API) && !defined(_WIN32) && !defined(NO_WRITEV) && \ + !defined(WOLFSSL_NO_TLS12) +/* Read exactly want bytes of application data from ssl into out. + * + * wolfSSL_read() returns at most one record's worth, so a payload larger than + * a record needs several calls. + * + * @param [in, out] ssl SSL/TLS object to read from. + * @param [out] out Buffer to hold the data read. + * @param [in] want Number of bytes expected. + * @return want on success. + * @return -1 when a read fails or returns no data. + */ +static int test_ssl_rw_read_all(WOLFSSL* ssl, byte* out, int want) +{ + int ret = want; + int got = 0; + + while (got < want) { + int rd = wolfSSL_read(ssl, out + got, want - got); + + if (rd <= 0) { + ret = -1; + break; + } + got += rd; + } + + return ret; +} +#endif + +/* Test wolfSSL_send(). + * + * Covers parameter validation, that data written with socket flags reaches + * the peer, and that the caller's write flags are restored afterwards. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_send(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) \ + && !defined(WOLFSSL_LEANPSK) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + char msg[] = "hello wolfssl send"; + char reply[64]; + + /* NULL SSL object is rejected before anything is sent. */ + ExpectIntEQ(wolfSSL_send(NULL, msg, (int)sizeof(msg), 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* NULL data and a negative length are rejected. */ + ExpectIntEQ(wolfSSL_send(ssl_c, NULL, (int)sizeof(msg), 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_send(ssl_c, msg, -1, 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* Mark the write flags so the restore can be observed. The memio write + * callback ignores them, so any value is safe here. */ + if (ssl_c != NULL) { + ssl_c->wflags = 0x5a; + } + + /* Data sent with flags reaches the peer unchanged. */ + ExpectIntEQ(wolfSSL_send(ssl_c, msg, (int)sizeof(msg), 0), + (int)sizeof(msg)); + XMEMSET(reply, 0, sizeof(reply)); + ExpectIntEQ(wolfSSL_recv(ssl_s, reply, (int)sizeof(reply), 0), + (int)sizeof(msg)); + ExpectBufEQ(reply, msg, sizeof(msg)); + + /* The caller's write flags are put back after the send. */ + if (ssl_c != NULL) { + ExpectIntEQ(ssl_c->wflags, 0x5a); + } + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_writev(). + * + * Covers the length overflow check, the stack/static gather buffer path, the + * heap gather buffer path taken when the total exceeds FILE_BUFFER_SIZE, and + * an empty vector. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_writev(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && !defined(USE_WINDOWS_API) && !defined(_WIN32) && !defined(NO_WRITEV) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + /* Enough to push the total past FILE_BUFFER_SIZE and onto the heap. */ + byte msg[FILE_BUFFER_SIZE + 64]; + byte reply[FILE_BUFFER_SIZE + 64]; + struct iovec iov[3]; + int i; + int small_sz; + int large_sz; + + for (i = 0; i < (int)sizeof(msg); i++) { + msg[i] = (byte)(i * 7 + 13); + } + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* A total length that overflows is rejected. The buffers are never read + * so the bogus length is safe. */ + iov[0].iov_base = msg; + iov[0].iov_len = (size_t)-1; + iov[1].iov_base = msg; + iov[1].iov_len = 2; + ExpectIntEQ(wolfSSL_writev(ssl_c, iov, 2), WC_NO_ERR_TRACE(BUFFER_E)); + + /* Arguments are validated before anything is read from the object. */ + ExpectIntEQ(wolfSSL_writev(NULL, iov, 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_writev(ssl_c, NULL, 1), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_writev(ssl_c, iov, -1), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* An empty vector writes nothing and is not an error, with or without a + * vector array. */ + ExpectIntEQ(wolfSSL_writev(ssl_c, iov, 0), 0); + ExpectIntEQ(wolfSSL_writev(ssl_c, NULL, 0), 0); + + /* Segments totalling less than FILE_BUFFER_SIZE use the static buffer. */ + small_sz = 96; + iov[0].iov_base = msg; + iov[0].iov_len = 32; + iov[1].iov_base = msg + 32; + iov[1].iov_len = 32; + iov[2].iov_base = msg + 64; + iov[2].iov_len = 32; + ExpectIntEQ(wolfSSL_writev(ssl_c, iov, 3), small_sz); + XMEMSET(reply, 0, sizeof(reply)); + ExpectIntEQ(test_ssl_rw_read_all(ssl_s, reply, small_sz), small_sz); + ExpectBufEQ(reply, msg, small_sz); + + /* Segments totalling more than FILE_BUFFER_SIZE allocate from the heap. */ + large_sz = (int)sizeof(msg); + iov[0].iov_base = msg; + iov[0].iov_len = 64; + iov[1].iov_base = msg + 64; + iov[1].iov_len = (size_t)large_sz - 64; + ExpectIntEQ(wolfSSL_writev(ssl_c, iov, 2), large_sz); + XMEMSET(reply, 0, sizeof(reply)); + ExpectIntEQ(test_ssl_rw_read_all(ssl_s, reply, large_sz), large_sz); + ExpectBufEQ(reply, msg, large_sz); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_get_shutdown(). + * + * Covers the NULL object case and each stage of a bidirectional shutdown: + * nothing exchanged, close_notify sent, close_notify received, and the + * completed shutdown. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_get_shutdown(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + char reply[16]; + + /* NULL object reports no shutdown state. */ + ExpectIntEQ(wolfSSL_get_shutdown(NULL), 0); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Nothing has been sent or received on an open connection. */ + ExpectIntEQ(wolfSSL_get_shutdown(ssl_c), 0); + ExpectIntEQ(wolfSSL_get_shutdown(ssl_s), 0); + + /* The client sends its close_notify but has not seen the peer's. */ + ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SHUTDOWN_NOT_DONE); + ExpectIntEQ(wolfSSL_get_shutdown(ssl_c), WOLFSSL_SENT_SHUTDOWN); + + /* The server reads the alert and reports it as received. */ + ExpectIntEQ(wolfSSL_read(ssl_s, reply, (int)sizeof(reply)), 0); + ExpectIntEQ(wolfSSL_get_shutdown(ssl_s), WOLFSSL_RECEIVED_SHUTDOWN); + + /* The server replies, completing the exchange on its side. */ + ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_get_shutdown(ssl_s), + WOLFSSL_SENT_SHUTDOWN | WOLFSSL_RECEIVED_SHUTDOWN); + + /* The client processes the reply and is done too. */ + ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_get_shutdown(ssl_c), + WOLFSSL_SENT_SHUTDOWN | WOLFSSL_RECEIVED_SHUTDOWN); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_want(), wolfSSL_want_read() and wolfSSL_want_write(). + * + * Drives the SSL object into a WANT_READ state (read with nothing to read) + * and a WANT_WRITE state (write with the transport blocked) and checks what + * each accessor reports. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_want(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + char msg[] = "hello wolfssl want"; + char reply[64]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* A completed handshake leaves nothing outstanding. */ + ExpectIntEQ(wolfSSL_want_read(ssl_c), 0); + ExpectIntEQ(wolfSSL_want_write(ssl_c), 0); +#ifdef OPENSSL_EXTRA + ExpectIntEQ(wolfSSL_want(NULL), WOLFSSL_NOTHING); + ExpectIntEQ(wolfSSL_want(ssl_c), WOLFSSL_NOTHING); + /* The per-direction variants take NULL too. */ + ExpectIntEQ(wolfSSL_want_read(NULL), 0); + ExpectIntEQ(wolfSSL_want_write(NULL), 0); +#endif + + /* Reading with no record available reports a wanted read. */ + ExpectIntLT(wolfSSL_read(ssl_c, reply, (int)sizeof(reply)), 0); + ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_want_read(ssl_c), 1); + ExpectIntEQ(wolfSSL_want_write(ssl_c), 0); +#ifdef OPENSSL_EXTRA + ExpectIntEQ(wolfSSL_want(ssl_c), WOLFSSL_READING); +#endif + + /* Writing with the transport blocked reports a wanted write. */ + test_memio_simulate_want_write(&test_ctx, 1, 1); + ExpectIntLT(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)), 0); + ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_WRITE); + ExpectIntEQ(wolfSSL_want_write(ssl_c), 1); + ExpectIntEQ(wolfSSL_want_read(ssl_c), 0); +#ifdef OPENSSL_EXTRA + ExpectIntEQ(wolfSSL_want(ssl_c), WOLFSSL_WRITING); +#endif + test_memio_simulate_want_write(&test_ctx, 1, 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_pending() and wolfSSL_has_pending(). + * + * Leaves part of a record undelivered by reading less than was written and + * checks that both report the buffered remainder, then that draining it + * clears the report. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_pending_api(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte msg[32]; + byte reply[32]; + int i; + + for (i = 0; i < (int)sizeof(msg); i++) { + msg[i] = (byte)i; + } + + /* NULL object is rejected by both. */ + ExpectIntEQ(wolfSSL_pending(NULL), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_has_pending(NULL), WOLFSSL_FAILURE); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Nothing buffered on an idle connection. */ + ExpectIntEQ(wolfSSL_pending(ssl_c), 0); + ExpectIntEQ(wolfSSL_has_pending(ssl_c), 0); + + /* Read less than the record holds - the rest stays buffered. */ + ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)), + (int)sizeof(msg)); + ExpectIntEQ(wolfSSL_read(ssl_c, reply, 8), 8); + ExpectBufEQ(reply, msg, 8); + ExpectIntEQ(wolfSSL_pending(ssl_c), (int)sizeof(msg) - 8); + ExpectIntEQ(wolfSSL_has_pending(ssl_c), 1); + + /* Draining the remainder clears the buffered data. */ + ExpectIntEQ(wolfSSL_read(ssl_c, reply, (int)sizeof(msg) - 8), + (int)sizeof(msg) - 8); + ExpectBufEQ(reply, msg + 8, sizeof(msg) - 8); + ExpectIntEQ(wolfSSL_pending(ssl_c), 0); + ExpectIntEQ(wolfSSL_has_pending(ssl_c), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test parameter validation across the read/write APIs. + * + * Every entry point in ssl_api_rw.c rejects a NULL object, a NULL buffer or a + * negative length before touching the connection. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_rw_bad_args(void) +{ + EXPECT_DECLS; +/* wolfSSL_recv() below is only defined when WOLFSSL_LEANPSK is not, as in + * test_wolfSSL_send(). */ +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_LEANPSK) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + char buf[16]; + size_t rd = 0; + size_t wr = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* wolfSSL_write() and wolfSSL_read(). */ + ExpectIntEQ(wolfSSL_write(NULL, buf, (int)sizeof(buf)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_write(ssl_c, NULL, (int)sizeof(buf)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_write(ssl_c, buf, -1), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_read(NULL, buf, (int)sizeof(buf)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_read(ssl_c, NULL, (int)sizeof(buf)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_read(ssl_c, buf, -1), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* wolfSSL_peek() shares the read path. */ + ExpectIntEQ(wolfSSL_peek(NULL, buf, (int)sizeof(buf)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_peek(ssl_c, buf, -1), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* wolfSSL_recv() validates before setting the socket flags. */ + ExpectIntEQ(wolfSSL_recv(NULL, buf, (int)sizeof(buf), 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_recv(ssl_c, NULL, (int)sizeof(buf), 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_recv(ssl_c, buf, -1, 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* wolfSSL_shutdown() and wolfSSL_SendUserCanceled() report their own + * failure codes for a NULL object. */ + ExpectIntEQ(wolfSSL_shutdown(NULL), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_SendUserCanceled(NULL), + WC_NO_ERR_TRACE(WOLFSSL_FAILURE)); + + /* wolfSSL_inject() rejects a non-positive length as well. */ + ExpectIntEQ(wolfSSL_inject(NULL, buf, (int)sizeof(buf)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_inject(ssl_c, NULL, (int)sizeof(buf)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_inject(ssl_c, buf, 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* The _ex variants report a write or read failure as 0, but a NULL object + * is surfaced as an error code by both so it cannot be mistaken for one. + * Seed the counts with a value neither call could produce, so what each + * does to its own is visible. */ + wr = SIZE_MAX; + rd = SIZE_MAX; + /* write_ex clears the count before validating anything, so it reads as + * zero even though nothing was written. */ + ExpectIntEQ(wolfSSL_write_ex(NULL, buf, sizeof(buf), &wr), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wr, 0); + /* read_ex only sets the count when data was read, so it is left alone. */ + ExpectIntEQ(wolfSSL_read_ex(NULL, buf, sizeof(buf), &rd), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(rd, SIZE_MAX); + /* Neither writes through a NULL count. */ + ExpectIntEQ(wolfSSL_write_ex(NULL, buf, sizeof(buf), NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_read_ex(NULL, buf, sizeof(buf), NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) && \ + defined(OPENSSL_EXTRA) && !defined(WOLFSSL_NO_TLS12) +/* Number of times the info callback below has been invoked. */ +static int test_ssl_rw_info_calls = 0; +/* Type reported by the most recent info callback invocation. */ +static int test_ssl_rw_info_type = 0; + +/* Info callback recording what the read/write APIs report. + * + * @param [in] ssl SSL/TLS object reporting the state. Unused. + * @param [in] type State being reported. + * @param [in] val Value associated with the state. Unused. + */ +static void test_ssl_rw_info_cb(const WOLFSSL* ssl, int type, int val) +{ + (void)ssl; + (void)val; + + test_ssl_rw_info_calls++; + test_ssl_rw_info_type = type; +} +#endif + +/* Test that the read/write APIs invoke the info callback. + * + * wolfSSL_write() reports WOLFSSL_CB_WRITE and wolfSSL_read()/wolfSSL_read_ex() + * report WOLFSSL_CB_READ when an info callback is installed. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_rw_info_callback(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && defined(OPENSSL_EXTRA) && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + char msg[] = "hello wolfssl info cb"; + char reply[64]; + size_t rd = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Install after the handshake so only the data calls are counted. */ + test_ssl_rw_info_calls = 0; + test_ssl_rw_info_type = 0; + wolfSSL_set_info_callback(ssl_c, test_ssl_rw_info_cb); + wolfSSL_set_info_callback(ssl_s, test_ssl_rw_info_cb); + + /* Writing reports WOLFSSL_CB_WRITE. */ + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)), + (int)sizeof(msg)); + ExpectIntEQ(test_ssl_rw_info_calls, 1); + ExpectIntEQ(test_ssl_rw_info_type, WOLFSSL_CB_WRITE); + + /* Reading reports WOLFSSL_CB_READ. */ + XMEMSET(reply, 0, sizeof(reply)); + ExpectIntEQ(wolfSSL_read(ssl_s, reply, (int)sizeof(reply)), + (int)sizeof(msg)); + ExpectBufEQ(reply, msg, sizeof(msg)); + ExpectIntEQ(test_ssl_rw_info_calls, 2); + ExpectIntEQ(test_ssl_rw_info_type, WOLFSSL_CB_READ); + + /* wolfSSL_read_ex() reports it too. */ + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)), + (int)sizeof(msg)); + XMEMSET(reply, 0, sizeof(reply)); + ExpectIntEQ(wolfSSL_read_ex(ssl_s, reply, sizeof(reply), &rd), 1); + ExpectIntEQ(rd, sizeof(msg)); + ExpectIntEQ(test_ssl_rw_info_calls, 4); + ExpectIntEQ(test_ssl_rw_info_type, WOLFSSL_CB_READ); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test wolfSSL_write_ex() partial write handling. + * + * With partial writes enabled a zero-length write reports failure rather than + * success; with them disabled a full write reports success. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_write_ex_partial(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + char msg[] = "hello wolfssl write_ex"; + size_t wr = 1; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Writing everything reports success and the length written. */ + ExpectIntEQ(wolfSSL_write_ex(ssl_c, msg, sizeof(msg), &wr), 1); + ExpectIntEQ(wr, sizeof(msg)); + + /* With partial writes enabled, writing nothing is reported as a failure + * even though no error occurred. */ + if (ssl_c != NULL) { + ssl_c->options.partialWrite = 1; + } + wr = 1; + ExpectIntEQ(wolfSSL_write_ex(ssl_c, msg, 0, &wr), 0); + ExpectIntEQ(wr, 0); + if (ssl_c != NULL) { + ssl_c->options.partialWrite = 0; + } + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test that wolfSSL_inject() refuses to grow the input buffer while decrypted + * application data is still waiting to be read. + * + * Growing the input buffer would invalidate clearOutputBuffer, which points + * into it, so the pending data must be drained first. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_inject_app_data_ready(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \ + && !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte msg[32]; + byte reply[32]; + int usedLength = 0; + int maxLength = 0; + int i; + byte* big = NULL; + + for (i = 0; i < (int)sizeof(msg); i++) { + msg[i] = (byte)i; + } + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Leave application data buffered by reading less than was written. */ + ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)), + (int)sizeof(msg)); + ExpectIntEQ(wolfSSL_read(ssl_c, reply, 8), 8); + ExpectIntGT(wolfSSL_pending(ssl_c), 0); + + /* Anything that needs more room than the input buffer has is refused. + * The length is only compared, never used to read from the buffer. */ + if (ssl_c != NULL) { + usedLength = (int)(ssl_c->buffers.inputBuffer.length - + ssl_c->buffers.inputBuffer.idx); + maxLength = (int)(ssl_c->buffers.inputBuffer.bufferSize - + (word32)usedLength); + ExpectIntEQ(wolfSSL_inject(ssl_c, msg, maxLength + 1), + WC_NO_ERR_TRACE(APP_DATA_READY)); + } + + /* Once the pending data is drained the same call is accepted: the input + * buffer can be grown now that nothing points into it. The injected + * length must be backed by real bytes, so use a buffer of that size + * rather than the short message above. */ + ExpectIntEQ(wolfSSL_read(ssl_c, reply, (int)sizeof(msg) - 8), + (int)sizeof(msg) - 8); + ExpectIntEQ(wolfSSL_pending(ssl_c), 0); + if (ssl_c != NULL) { + ExpectNotNull(big = (byte*)XMALLOC((size_t)maxLength + 1, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + if (big != NULL) { + XMEMSET(big, 0, (size_t)maxLength + 1); + ExpectIntEQ(wolfSSL_inject(ssl_c, big, maxLength + 1), + WOLFSSL_SUCCESS); + } + } + + XFREE(big, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +#if defined(WOLFSSL_QUIC) && defined(WOLFSSL_TLS13) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) +/* QUIC secret callback. Only send_alert below matters to this test. + * + * @return 1 always, which the QUIC layer reads as success. + */ +static int test_ssl_rw_quic_secrets(WOLFSSL* ssl, + WOLFSSL_ENCRYPTION_LEVEL level, const uint8_t* rx, const uint8_t* tx, + size_t len) +{ + (void)ssl; + (void)level; + (void)rx; + (void)tx; + (void)len; + return 1; +} + +/* QUIC handshake data sink. + * + * @return 1 always, which the QUIC layer reads as success. + */ +static int test_ssl_rw_quic_add_hs(WOLFSSL* ssl, + WOLFSSL_ENCRYPTION_LEVEL level, const uint8_t* data, size_t len) +{ + (void)ssl; + (void)level; + (void)data; + (void)len; + return 1; +} + +/* QUIC flight flush. + * + * @return 1 always, which the QUIC layer reads as success. + */ +static int test_ssl_rw_quic_flush(WOLFSSL* ssl) +{ + (void)ssl; + return 1; +} + +/* QUIC alert send that refuses. SendAlert() negates this, so ssl->error + * becomes a positive 1 - neither zero nor a negative error code. + * + * @return 0 always, which the QUIC layer reads as failure. + */ +static int test_ssl_rw_quic_send_alert_fail(WOLFSSL* ssl, + WOLFSSL_ENCRYPTION_LEVEL level, uint8_t alertType) +{ + (void)ssl; + (void)level; + (void)alertType; + return 0; +} + +static const WOLFSSL_QUIC_METHOD test_ssl_rw_quic_method = { + test_ssl_rw_quic_secrets, + test_ssl_rw_quic_add_hs, + test_ssl_rw_quic_flush, + test_ssl_rw_quic_send_alert_fail +}; +#endif + +/* Test that a refused close_notify send does not undo a shutdown the peer + * already completed. + * + * The peer's close_notify has arrived but this side has not sent one. The + * QUIC send_alert callback refuses, which SendAlert() reports as a positive + * value, so sentNotify stays clear while the shutdown is none the less + * complete. The result must remain WOLFSSL_SUCCESS. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_shutdown_quic_alert_refused(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_QUIC) && defined(WOLFSSL_TLS13) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())); + ExpectIntEQ(wolfSSL_CTX_set_quic_method(ctx, &test_ssl_rw_quic_method), + WOLFSSL_SUCCESS); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) { + ssl->options.closeNotify = 1; + ExpectIntEQ(wolfSSL_shutdown(ssl), WOLFSSL_SUCCESS); + /* The exchange really did complete. */ + ExpectIntEQ(ssl->options.shutdownDone, 1); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test that a shutdown which flushes a buffered write but can never send + * close_notify still reports failure. + * + * A write that hit WANT_WRITE leaves data buffered without setting + * sentNotify. If the connection is closed before the shutdown runs, the + * buffered data flushes successfully but no close_notify can follow, so the + * exchange can never complete. This used to report the flush's success as + * the shutdown's, returning 0 - which is WOLFSSL_SHUTDOWN_NOT_DONE under + * WOLFSSL_ERROR_CODE_OPENSSL, so a caller looping while the result is 0 + * never terminated. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_shutdown_flush_no_notify(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_SHUTDOWNONCE) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + char msg[] = "buffered by a failed write"; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Leave application data in the output buffer. sentNotify stays clear + * because no close_notify has been attempted. */ + test_memio_simulate_want_write(&test_ctx, 1, 1); + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_WRITE); + if (ssl_c != NULL) { + ExpectIntGT(ssl_c->buffers.outputBuffer.length, 0); + ExpectIntEQ(ssl_c->options.sentNotify, 0); + } + + /* Let the flush succeed, but close the connection so no close_notify can + * follow it. */ + test_memio_simulate_want_write(&test_ctx, 1, 0); + if (ssl_c != NULL) { + ssl_c->options.isClosed = 1; + + ExpectIntEQ(wolfSSL_shutdown(ssl_c), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + /* The flush really did happen. */ + ExpectIntEQ(ssl_c->buffers.outputBuffer.length, 0); + /* And the caller has a reason to query. */ + ExpectIntEQ(ssl_c->error, WC_NO_ERR_TRACE(SOCKET_PEER_CLOSED_E)); + } + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Test that a shutdown which can never send close_notify reports why. + * + * When the connection is already closed no close_notify can be sent, so the + * exchange can never complete. The call fails, and the reason must be + * available from wolfSSL_get_error() rather than leaving the caller with a + * failure and no error to query. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_shutdown_no_notify(void) +{ + EXPECT_DECLS; +#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_SHUTDOWNONCE) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) { + /* The connection went away before a close_notify could be sent. */ + ssl->options.isClosed = 1; + ssl->options.sentNotify = 0; + ssl->error = WOLFSSL_ERROR_NONE; + + ExpectIntEQ(wolfSSL_shutdown(ssl), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + /* Check the recorded error rather than the value reported, which + * wolfSSL_get_error() translates for OpenSSL compatibility. */ + ExpectIntEQ(ssl->error, WC_NO_ERR_TRACE(SOCKET_PEER_CLOSED_E)); + /* The caller is not left with a failure and nothing to query. */ + ExpectIntNE(wolfSSL_get_error(ssl, 0), 0); + } + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; + + /* An error already recorded is more specific, so it is kept. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) { + ssl->options.connReset = 1; + ssl->options.sentNotify = 0; + ssl->error = WC_NO_ERR_TRACE(SOCKET_ERROR_E); + + ExpectIntEQ(wolfSSL_shutdown(ssl), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + /* The more specific error already recorded is left in place. */ + ExpectIntEQ(ssl->error, WC_NO_ERR_TRACE(SOCKET_ERROR_E)); + } + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ssl_rw.h b/tests/api/test_ssl_rw.h new file mode 100644 index 00000000000..8d4ced73265 --- /dev/null +++ b/tests/api/test_ssl_rw.h @@ -0,0 +1,54 @@ +/* test_ssl_rw.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef TESTS_API_SSL_RW_H +#define TESTS_API_SSL_RW_H + +#include + +int test_wolfSSL_send(void); +int test_wolfSSL_writev(void); +int test_wolfSSL_get_shutdown(void); +int test_wolfSSL_want(void); +int test_wolfSSL_pending_api(void); +int test_wolfSSL_rw_bad_args(void); +int test_wolfSSL_rw_info_callback(void); +int test_wolfSSL_write_ex_partial(void); +int test_wolfSSL_inject_app_data_ready(void); +int test_wolfSSL_shutdown_no_notify(void); +int test_wolfSSL_shutdown_flush_no_notify(void); +int test_wolfSSL_shutdown_quic_alert_refused(void); + +#define TEST_SSL_RW_DECLS \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_send), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_writev), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_get_shutdown), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_want), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_pending_api), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_rw_bad_args), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_rw_info_callback), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_write_ex_partial), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_inject_app_data_ready), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_shutdown_no_notify), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_shutdown_flush_no_notify), \ + TEST_DECL_GROUP("ssl_rw", test_wolfSSL_shutdown_quic_alert_refused) + +#endif /* TESTS_API_SSL_RW_H */ diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index c168ec24d9c..87b330d94c7 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1953,7 +1953,8 @@ WOLFSSL_API int wolfSSL_X509_STORE_CTX_get_error_depth(WOLFSSL_X509_STORE_CTX* /* -------- EXTRAS BEGIN -------- */ #ifdef WOLFSSL_CERT_SETUP_CB -#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) +/* CBClientCert is only in WOLFSSL_CTX under OPENSSL_EXTRA. */ +#ifdef OPENSSL_EXTRA typedef int (*client_cert_cb)(WOLFSSL *ssl, WOLFSSL_X509 **x509, WOLFSSL_EVP_PKEY **pkey); WOLFSSL_API void wolfSSL_CTX_set_client_cert_cb(WOLFSSL_CTX *ctx, client_cert_cb cb);