Files
wolfssl/src/ssl_api_hs.c
T
Sean Parkinson 5a1cd9fc3e 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.
2026-08-07 09:30:56 +10:00

2661 lines
83 KiB
C

/* ssl_api_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 <wolfssl/wolfcrypt/libwolfssl_sources.h>
#if !defined(WOLFSSL_SSL_API_HS_INCLUDED)
#ifndef WOLFSSL_IGNORE_FILE_WARN
#warning ssl_api_hs.c does not need to be compiled separately from ssl.c
#endif
#else
#ifndef WOLFCRYPT_ONLY
#ifndef NO_TLS
/* 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.
* @return Any other error passed through from wolfSSL_connect() or
* wolfSSL_accept(), such as the raw error from their ReinitSSL()
* step.
*/
int wolfSSL_negotiate(WOLFSSL* ssl)
{
int err = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR);
WOLFSSL_ENTER("wolfSSL_negotiate");
/* 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);
}
}
#endif
}
WOLFSSL_LEAVE("wolfSSL_negotiate", err);
return err;
}
#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))
#ifndef NO_WOLFSSL_CLIENT
/* Send any buffered output and retry a pending alert, for the client.
*
* Called once on entry to wolfSSL_connect(), before the state machine runs,
* so that a message left unsent by a previous call is flushed before the next
* one is built. The steps inside fall through to each other without coming
* back here.
*
* Whether the state may be advanced is decided before the send, which is what
* the client did before these two flushes were split out of their callers.
* The server decides after; see wolfssl_accept_flush().
*
* @param [in, out] ssl SSL/TLS object.
* @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_connect_flush(WOLFSSL* ssl)
{
int ret = 0;
byte advanceState;
/* 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
/* A DTLS 1.3 ACK or retransmit is not a step of the handshake, so
* finishing one does not move the state on. */
if ((ssl->options.dtls) && (IsAtLeastTLSv1_3(ssl->version))) {
advanceState = (byte)((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");
}
#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;
}
#endif /* !NO_WOLFSSL_CLIENT */
#ifndef NO_WOLFSSL_SERVER
/* Send any buffered output and retry a pending alert, for the server.
*
* Called once on entry to wolfSSL_accept(), before the state machine runs, so
* that a message left unsent by a previous call is flushed before the next
* one is built. The steps inside fall through to each other without coming
* back here.
*
* Whether the state may be advanced is decided after the send, which is what
* the server did before these two flushes were split out of their callers.
* The client decides before; see wolfssl_connect_flush().
*
* @param [in, out] ssl SSL/TLS object.
* @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_accept_flush(WOLFSSL* ssl)
{
int ret = 0;
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)) {
/* The accept states listed here are the ones reached after a
* message has been sent, so they are the ones that may be
* advanced. */
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");
}
#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;
}
#endif /* !NO_WOLFSSL_SERVER */
/* 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");
/* The caller reports the failure, so don't trace it here too. */
ret = WC_NO_ERR_TRACE(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 /* !NO_TLS && !(WOLFSSL_NO_TLS12 && NO_OLD_TLS && WOLFSSL_TLS13) &&
* (!NO_WOLFSSL_CLIENT || !NO_WOLFSSL_SERVER) */
/* client only parts */
#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)
/* 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.
* @return The error from ReinitSSL(), unchanged, when the object cannot be
* prepared for a handshake. This is a raw error code rather than
* WOLFSSL_FATAL_ERROR, and ssl->error is not set with it.
*
* 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;
#endif
int ret = 0;
(void)ret;
#ifdef HAVE_ERRNO_H
errno = 0;
#endif
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 */
}
#ifdef OPENSSL_EXTRA
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);
#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");
/* 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;
}
#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 */
if (ssl->options.side != WOLFSSL_CLIENT_END) {
ssl->error = SIDE_ERROR;
WOLFSSL_ERROR(ssl->error);
return WOLFSSL_FATAL_ERROR;
}
#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 = wolfssl_connect_flush(ssl);
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;
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) {
#ifdef WOLFSSL_DTLS
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;
}
}
#endif /* WOLFSSL_DTLS13 */
}
ssl->options.connectState = HELLO_AGAIN;
WOLFSSL_MSG("connect state: HELLO_AGAIN");
FALL_THROUGH;
case HELLO_AGAIN :
#ifdef WOLFSSL_TLS13
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;
}
}
}
#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;
}
}
}
}
#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");
}
#endif
ssl->options.connectState = FIRST_REPLY_FIRST;
WOLFSSL_MSG("connect state: FIRST_REPLY_FIRST");
FALL_THROUGH;
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: 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;
}
#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;
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;
}
}
ssl->options.connectState = SECOND_REPLY_DONE;
WOLFSSL_MSG("connect state: SECOND_REPLY_DONE");
FALL_THROUGH;
case SECOND_REPLY_DONE:
if (wolfssl_handshake_done(ssl) != 0) {
return WOLFSSL_FATAL_ERROR;
}
ssl->error = 0; /* clear the error */
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 */
}
/* 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) {
ret = WOLFSSL_FAILURE;
}
else {
ssl->options.certOnly = 1;
ret = wolfSSL_connect(ssl);
ssl->options.certOnly = 0;
}
return ret;
}
#endif /* !NO_WOLFSSL_CLIENT && !NO_TLS */
/* end client only parts */
/* server only parts */
#if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS)
/* 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))
/* 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;
#ifndef NO_PSK
havePSK = ssl->options.havePSK;
#endif
#ifdef HAVE_ANON
haveAnon = ssl->options.useAnon;
#endif
#ifdef WOLFSSL_MULTICAST
haveMcast = ssl->options.haveMcast;
#endif
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 == NULL) ||
(ssl->buffers.certificate->buffer == NULL)) {
WOLFSSL_MSG("accept error: server cert required");
ssl->error = NO_PRIVATE_KEY;
WOLFSSL_ERROR(ssl->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;
}
}
}
}
#else
(void)ssl;
#endif /* !NO_CERTS */
return ret;
}
#endif /* !(WOLFSSL_NO_TLS12 && NO_OLD_TLS && WOLFSSL_TLS13) */
/* 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.
* @return The error from ReinitSSL(), unchanged, when the object cannot be
* prepared for a handshake. This is a raw error code rather than
* WOLFSSL_FATAL_ERROR, and ssl->error is not set with it.
*
* 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)
{
int ret = 0;
(void)ret;
if (ssl == NULL) {
return WOLFSSL_FATAL_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 */
#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");
/* 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;
}
#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;
}
}
#endif
ret = wolfssl_accept_flush(ssl);
if (ret != 0) {
return ret;
}
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;
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 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;
}
}
#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;
case CERT_STATUS_SENT :
#ifdef WOLFSSL_TLS13
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;
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;
}
}
}
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) {
ssl->options.peerAuthGood = 1;
}
#endif
#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;
}
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 CHANGE_CIPHER_SENT :
if ((ssl->error = SendFinished(ssl)) != 0) {
wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error);
WOLFSSL_ERROR(ssl->error);
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;
case ACCEPT_THIRD_REPLY_DONE :
if (wolfssl_handshake_done(ssl) != 0) {
return WOLFSSL_FATAL_ERROR;
}
#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 */
#ifndef NO_HANDSHAKE_DONE_CB
/* Set the callback to call when the handshake completes.
*
* @param [in, out] ssl SSL/TLS object.
* @param [in] cb Callback to call. NULL to clear.
* @param [in] user_ctx Context to pass to the callback.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
*/
int wolfSSL_SetHsDoneCb(WOLFSSL* ssl, HandShakeDoneCb cb, void* user_ctx)
{
int ret = WOLFSSL_SUCCESS;
WOLFSSL_ENTER("wolfSSL_SetHsDoneCb");
if (ssl == NULL) {
ret = BAD_FUNC_ARG;
}
else {
ssl->hsDoneCb = cb;
ssl->hsDoneCtx = user_ctx;
}
return ret;
}
#endif /* NO_HANDSHAKE_DONE_CB */
#ifdef WOLFSSL_CALLBACKS
typedef struct itimerval Itimerval;
/* don't keep calling simple functions while setting up timer and signals
if no inlining these are the next best */
#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)) \
/* 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, 0
* when not.
* @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;
*timerWasOn = 0;
/* 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;
/* 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) {
ret = SIGACT_ERROR;
}
}
if (ret == 0) {
if (setitimer(ITIMER_REAL, &myTimeout, 0) < 0) {
ret = SETITIMER_ERROR;
}
}
return ret;
}
/* 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) \
do { ssl->hsInfoOn = 0; ssl->toInfoOn = 0; return x; } while (0)
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);
}
}
tret = wolfssl_ex_wrapper_reset_timer(&startTime, &endTime,
&oldTimeout, oldTimerOn, &oact);
if (tret != 0) {
ret = tret; /* more pressing error, stomp */
}
/* 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);
}
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);
}
#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);
}
#endif
/* Local to this file, which is compiled into ssl.c, so do not leave them
* defined for the files included after it. */
#undef ERR_OUT
#undef SubtractTimes
#undef CmpTimes
#endif /* WOLFSSL_CALLBACKS */
#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");
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;
#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");
}
#endif
if ((ssl->options.haveStaticECC) && (ssl->buffers.key != NULL)) {
if (wc_ecc_init(key) >= 0) {
DerBuffer* privKey = ssl->buffers.key;
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
DerBuffer* unblinded = NULL;
/* Only a key that has a mask is masked. Without one - after
* wolfSSL_use_PrivateKey_Id(), for example - the buffer is
* stored as-is and is used directly, so that this build
* behaves the same as one without key blinding. */
if (ssl->buffers.keyMask != NULL) {
/* The stored key is masked, so work on a plain copy. */
unblinded = wolfssl_priv_der_unblind(ssl->buffers.key,
ssl->buffers.keyMask);
privKey = unblinded;
}
#endif
if (privKey == NULL) {
/* Only an allocation failure gets here, and that 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;
}
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
/* Only the plain copy is disposed of - the stored key is
* not ours to free. */
wolfssl_priv_der_unblind_free(unblinded);
#endif
wc_ecc_free(key);
}
}
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;
}
#endif
}
if (InitSSL_Side(ssl, WOLFSSL_SERVER_END) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("Error initializing server side");
}
}
#endif /* OPENSSL_EXTRA || WOLFSSL_EXTRA || WOLFSSL_WPAS_SMALL */
/* 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 (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);
}
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) {
ret = 1;
}
}
}
return ret;
}
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)
/* Set the SSL/TLS object to be a client.
*
* Resets the handshake state and cipher suites. Must be called before the
* handshake starts.
*
* @param [in, out] ssl SSL/TLS object.
*/
void wolfSSL_set_connect_state(WOLFSSL* ssl)
{
WOLFSSL_ENTER("wolfSSL_set_connect_state");
if (ssl == NULL) {
WOLFSSL_MSG("WOLFSSL struct pointer passed in was null");
return;
}
#ifndef NO_DH
/* client creates its own DH parameters on handshake */
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 != NULL) &&
(ssl->buffers.weOwnDH)) {
XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap,
DYNAMIC_TYPE_PUBLIC_KEY);
}
ssl->buffers.serverDH_G.buffer = NULL;
#endif
if (InitSSL_Side(ssl, WOLFSSL_CLIENT_END) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("Error initializing client side");
}
}
#endif /* OPENSSL_EXTRA || WOLFSSL_WPAS_SMALL */
#ifdef OPENSSL_EXTRA
#define STATE_STRINGS_PROTO(s) \
{ \
{"SSLv3 " s, \
"SSLv3 " s, \
"SSLv3 " s}, \
{"TLSv1 " s, \
"TLSv1 " s, \
"TLSv1 " s}, \
{"TLSv1_1 " s, \
"TLSv1_1 " s, \
"TLSv1_1 " s}, \
{"TLSv1_2 " s, \
"TLSv1_2 " s, \
"TLSv1_2 " s}, \
{"TLSv1_3 " s, \
"TLSv1_3 " s, \
"TLSv1_3 " s}, \
{"DTLSv1 " s, \
"DTLSv1 " s, \
"DTLSv1 " s}, \
{"DTLSv1_2 " s, \
"DTLSv1_2 " s, \
"DTLSv1_2 " s}, \
{"DTLSv1_3 " s, \
"DTLSv1_3 " 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 the enumerators, whose names would
* otherwise be far too generic to sit in that namespace. The enum tags
* follow the naming of those in wolfssl/ssl.h.
*/
enum StateStringProtocol {
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,
/* Number of protocols above - the second dimension of OUTPUT_STR in
* wolfSSL_state_string_long(). Keep last of the indices. */
WOLFSSL_SS_PROTO_CNT,
WOLFSSL_SS_UNKNOWN = 100
};
enum StateStringIoMode {
WOLFSSL_SS_READ = 0,
WOLFSSL_SS_WRITE,
WOLFSSL_SS_NEITHER,
/* Number of modes above - the third dimension of OUTPUT_STR. */
WOLFSSL_SS_IO_CNT
};
enum StateStringState {
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,
/* Number of states above - the first dimension of OUTPUT_STR. Each state
* indexes a row of that table, so the two must stay the same size. */
WOLFSSL_SS_STATE_CNT
};
/* 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;
}
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;
}
return state;
}
/* Get a human readable description of the current handshake state.
*
* The description names the protocol version, whether the last message was
* read or written, and the message itself.
*
* @param [in] ssl SSL/TLS object.
* @return A human readable string describing the state.
* @return An empty string when the protocol version is not one this can
* name, so the result is always safe to print.
* @return NULL when ssl is NULL. That is the only case that returns NULL.
*/
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"),
STATE_STRINGS_PROTO_RW("Server Hello Verify Request"),
STATE_STRINGS_PROTO_RW("Server Hello Retry Request"),
STATE_STRINGS_PROTO_RW("Server Hello"),
STATE_STRINGS_PROTO_RW("Server Certificate Status"),
STATE_STRINGS_PROTO_RW("Server Encrypted Extensions"),
STATE_STRINGS_PROTO_RW("Server Session Ticket"),
STATE_STRINGS_PROTO_RW("Server Certificate Request"),
STATE_STRINGS_PROTO_RW("Server Cert"),
STATE_STRINGS_PROTO_RW("Server Key Exchange"),
STATE_STRINGS_PROTO_RW("Server Hello Done"),
STATE_STRINGS_PROTO_RW("Server Change CipherSpec"),
STATE_STRINGS_PROTO_RW("Server Finished"),
STATE_STRINGS_PROTO_RW("server Key Update"),
STATE_STRINGS_PROTO_RW("Client Hello"),
STATE_STRINGS_PROTO_RW("Client Key Exchange"),
STATE_STRINGS_PROTO_RW("Client Cert"),
STATE_STRINGS_PROTO_RW("Client Change CipherSpec"),
STATE_STRINGS_PROTO_RW("Client Certificate Verify"),
STATE_STRINGS_PROTO_RW("Client End Of Early Data"),
STATE_STRINGS_PROTO_RW("Client Finished"),
STATE_STRINGS_PROTO_RW("Client Key Update"),
STATE_STRINGS_PROTO("Handshake Done"),
};
int protocol;
int cbmode;
int state;
const char* ret = NULL;
/* The three indices below come from enumerations declared at the top of
* this file, well away from the table they index. Adding an entry to one
* of them without adding the matching entry here is a build error rather
* than a read off the end of the table. */
wc_static_assert(XELEM_CNT(OUTPUT_STR) == WOLFSSL_SS_STATE_CNT);
wc_static_assert(XELEM_CNT(OUTPUT_STR[0]) == WOLFSSL_SS_PROTO_CNT);
wc_static_assert(XELEM_CNT(OUTPUT_STR[0][0]) == WOLFSSL_SS_IO_CNT);
WOLFSSL_ENTER("wolfSSL_state_string_long");
if (ssl == NULL) {
WOLFSSL_MSG("Null argument passed in");
}
else {
cbmode = wolfssl_state_string_io_mode(ssl);
protocol = wolfssl_state_string_protocol(ssl);
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];
}
}
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) \
|| defined(OPENSSL_EXTRA) || defined(HAVE_LIGHTY)
#ifndef NO_TLS
/* Perform the handshake.
*
* Calls the connect or accept for the side of the object.
*
* @param [in, out] s SSL/TLS object.
* @return WOLFSSL_SUCCESS when the handshake completes.
* @return WOLFSSL_FATAL_ERROR when the side is not set or the handshake
* fails.
*/
int wolfSSL_SSL_do_handshake_internal(WOLFSSL *s)
{
int ret;
WOLFSSL_ENTER("wolfSSL_SSL_do_handshake_internal");
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");
ret = WOLFSSL_FAILURE;
#endif
}
else {
#ifndef NO_WOLFSSL_SERVER
ret = wolfSSL_accept(s);
#else
WOLFSSL_MSG("Server not compiled in");
ret = WOLFSSL_FAILURE;
#endif
}
return ret;
}
/* Perform the handshake.
*
* @param [in, out] s SSL/TLS object.
* @return WOLFSSL_SUCCESS when the handshake completes.
* @return WOLFSSL_FATAL_ERROR when the handshake fails. Call
* wolfSSL_get_error() for the reason.
*/
int wolfSSL_SSL_do_handshake(WOLFSSL *s)
{
int ret;
WOLFSSL_ENTER("wolfSSL_SSL_do_handshake");
#ifdef WOLFSSL_QUIC
if (WOLFSSL_IS_QUIC(s)) {
ret = wolfSSL_quic_do_handshake(s);
}
else
#endif
{
ret = wolfSSL_SSL_do_handshake_internal(s);
}
return ret;
}
#endif /* !NO_TLS */
/* Determine whether the handshake has not completed.
*
* @param [in] ssl SSL/TLS object.
* @return 1 when the handshake has not completed.
* @return 0 when the handshake has completed.
*/
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
int wolfSSL_SSL_in_init(const WOLFSSL *ssl)
#else
int wolfSSL_SSL_in_init(WOLFSSL *ssl)
#endif
{
WOLFSSL_ENTER("wolfSSL_SSL_in_init");
return !wolfSSL_is_init_finished(ssl);
}
/* Determine whether the handshake has not started.
*
* @param [in] ssl SSL/TLS object.
* @return 1 when the handshake has not started.
* @return 0 when the handshake has started or ssl is NULL.
*/
int wolfSSL_SSL_in_before(const WOLFSSL *ssl)
{
int ret;
WOLFSSL_ENTER("wolfSSL_SSL_in_before");
if (ssl != NULL) {
ret = (ssl->options.handShakeState == NULL_STATE);
}
else {
ret = WOLFSSL_FAILURE;
}
return ret;
}
/* Determine whether the handshake is in progress.
*
* @param [in] ssl SSL/TLS object.
* @return 1 when the handshake has started but not completed.
* @return 0 otherwise or when ssl is NULL.
*/
int wolfSSL_SSL_in_connect_init(WOLFSSL* ssl)
{
int ret;
WOLFSSL_ENTER("wolfSSL_SSL_in_connect_init");
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);
}
}
else {
ret = WOLFSSL_FAILURE;
}
return ret;
}
#endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY ||
OPENSSL_EXTRA || HAVE_LIGHTY */
#ifndef NO_CERTS
#ifdef HAVE_PK_CALLBACKS
/* 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 != NULL) {
ctx->GenPreMasterCb = cb;
}
}
/* 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 != NULL) {
ssl->GenPreMasterCtx = ctx;
}
}
/* 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)
{
void* ret = NULL;
if (ssl != NULL) {
ret = ssl->GenPreMasterCtx;
}
return ret;
}
/* 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 != NULL) {
ctx->GenMasterCb = cb;
}
}
/* 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 != NULL) {
ssl->GenMasterCtx = ctx;
}
}
/* 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)
{
void* ret = NULL;
if (ssl != NULL) {
ret = ssl->GenMasterCtx;
}
return ret;
}
/* 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 != NULL) {
ctx->GenExtMasterCb = cb;
}
}
/* 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 != NULL) {
ssl->GenExtMasterCtx = ctx;
}
}
/* 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)
{
void* ret = NULL;
if (ssl != NULL) {
ret = ssl->GenExtMasterCtx;
}
return ret;
}
/* 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 != NULL) {
ctx->GenSessionKeyCb = cb;
}
}
/* 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 != NULL) {
ssl->GenSessionKeyCtx = ctx;
}
}
/* 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)
{
void* ret = NULL;
if (ssl != NULL) {
ret = ssl->GenSessionKeyCtx;
}
return ret;
}
/* 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 != NULL) {
ctx->EncryptKeysCb = cb;
}
}
/* 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 != NULL) {
ssl->EncryptKeysCtx = ctx;
}
}
/* 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)
{
void* ret = NULL;
if (ssl != NULL) {
ret = ssl->EncryptKeysCtx;
}
return ret;
}
/* 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 != NULL) {
ctx->TlsFinishedCb = cb;
}
}
/* 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 != NULL) {
ssl->TlsFinishedCtx = ctx;
}
}
/* 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)
{
void* ret = NULL;
if (ssl != NULL) {
ret = ssl->TlsFinishedCtx;
}
return ret;
}
#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY)
/* 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 != NULL) {
ctx->VerifyMacCb = cb;
}
}
/* 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 != NULL) {
ssl->VerifyMacCtx = ctx;
}
}
/* 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)
{
void* ret = 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 != NULL) {
ctx->HKDFExpandLabelCb = cb;
}
}
#ifdef WOLFSSL_PUBLIC_ASN
/* Set the callback to call to process the peer's certificate.
*
* @param [in, out] ctx SSL/TLS CTX object.
* @param [in] cb Callback to call. NULL to clear.
*/
void wolfSSL_CTX_SetProcessPeerCertCb(WOLFSSL_CTX* ctx,
CallbackProcessPeerCert cb)
{
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 != NULL) {
ctx->ProcessServerSigKexCb = cb;
}
}
/* Set the callback to call to encrypt and decrypt TLS records.
*
* @param [in, out] ctx SSL/TLS CTX object.
* @param [in] cb Callback to call. NULL to clear.
*/
void wolfSSL_CTX_SetPerformTlsRecordProcessingCb(WOLFSSL_CTX* ctx,
CallbackPerformTlsRecordProcessing cb)
{
if (ctx != NULL) {
ctx->PerformTlsRecordProcessingCb = cb;
}
}
#endif /* HAVE_PK_CALLBACKS */
#endif /* NO_CERTS */
#if defined(HAVE_PK_CALLBACKS) && defined(HAVE_HKDF)
/* Set the callback to call to perform the HKDF extract operation.
*
* @param [in, out] ctx SSL/TLS CTX object.
* @param [in] cb Callback to call. NULL to clear.
*/
void wolfSSL_CTX_SetHKDFExtractCb(WOLFSSL_CTX* ctx, CallbackHKDFExtract cb)
{
if (ctx != NULL) {
ctx->HkdfExtractCb = cb;
}
}
/* Set the context to pass to the HKDF extract callback.
*
* @param [in, out] ssl SSL/TLS object.
* @param [in] ctx Context to pass to the callback.
*/
void wolfSSL_SetHKDFExtractCtx(WOLFSSL* ssl, void *ctx)
{
if (ssl != NULL) {
ssl->HkdfExtractCtx = ctx;
}
}
/* Get the context passed to the HKDF extract callback.
*
* @param [in] ssl SSL/TLS object.
* @return Context on success.
* @return NULL when ssl is NULL.
*/
void* wolfSSL_GetHKDFExtractCtx(WOLFSSL* ssl)
{
void* ret = NULL;
if (ssl != NULL) {
ret = ssl->HkdfExtractCtx;
}
return ret;
}
#endif /* HAVE_PK_CALLBACKS && HAVE_HKDF */
#endif /* !WOLFCRYPT_ONLY */
#endif /* !WOLFSSL_SSL_API_HS_INCLUDED */