Merge branch 'pr_946' into development-proposed

This commit is contained in:
Gilles Peskine 2018-04-04 10:33:45 +02:00
commit 80aa3b8d65
12 changed files with 1429 additions and 353 deletions

View File

@ -19,6 +19,14 @@ Features
mbedtls_ecdh_compute_shared()) are supported for now. Contributed by mbedtls_ecdh_compute_shared()) are supported for now. Contributed by
Nicholas Wilson (#348). Nicholas Wilson (#348).
API Changes
* Add function mbedtls_net_poll to public API allowing to wait for a
network context to become ready for reading or writing.
* Add function mbedtls_ssl_check_pending to public API allowing to check
if more data is pending to be processed in the internal message buffers.
This function is necessary to determine when it is safe to idle on the
underlying transport in case event-driven IO is used.
Bugfix Bugfix
* Fix spurious uninitialized variable warning in cmac.c. Fix independently * Fix spurious uninitialized variable warning in cmac.c. Fix independently
contributed by Brian J Murray and David Brown. contributed by Brian J Murray and David Brown.
@ -39,6 +47,14 @@ Bugfix
the mbedtls_cipher_update() documentation. Contributed by Andy Leiserson. the mbedtls_cipher_update() documentation. Contributed by Andy Leiserson.
* Fix overriding and ignoring return values when parsing and writing to * Fix overriding and ignoring return values when parsing and writing to
a file in pk_sign program. Found by kevlut in #1142. a file in pk_sign program. Found by kevlut in #1142.
* Restrict usage of error code MBEDTLS_ERR_SSL_WANT_READ to situations
where data needs to be fetched from the underlying transport in order
to make progress. Previously, this error code was also occasionally
returned when unexpected messages were being discarded, ignoring that
further messages could potentially already be pending to be processed
in the internal buffers; these cases lead to deadlocks in case
event-driven I/O was used.
Found and reported by Hubert Mis in #772.
Changes Changes
* Remove some redundant code in bignum.c. Contributed by Alexey Skalozub. * Remove some redundant code in bignum.c. Contributed by Alexey Skalozub.

View File

@ -46,12 +46,17 @@
#define MBEDTLS_ERR_NET_UNKNOWN_HOST -0x0052 /**< Failed to get an IP address for the given hostname. */ #define MBEDTLS_ERR_NET_UNKNOWN_HOST -0x0052 /**< Failed to get an IP address for the given hostname. */
#define MBEDTLS_ERR_NET_BUFFER_TOO_SMALL -0x0043 /**< Buffer is too small to hold the data. */ #define MBEDTLS_ERR_NET_BUFFER_TOO_SMALL -0x0043 /**< Buffer is too small to hold the data. */
#define MBEDTLS_ERR_NET_INVALID_CONTEXT -0x0045 /**< The context is invalid, eg because it was free()ed. */ #define MBEDTLS_ERR_NET_INVALID_CONTEXT -0x0045 /**< The context is invalid, eg because it was free()ed. */
#define MBEDTLS_ERR_NET_POLL_FAILED -0x0047 /**< Polling the net context failed. */
#define MBEDTLS_ERR_NET_BAD_INPUT_DATA -0x0049 /**< Input invalid. */
#define MBEDTLS_NET_LISTEN_BACKLOG 10 /**< The backlog that listen() should use. */ #define MBEDTLS_NET_LISTEN_BACKLOG 10 /**< The backlog that listen() should use. */
#define MBEDTLS_NET_PROTO_TCP 0 /**< The TCP transport protocol */ #define MBEDTLS_NET_PROTO_TCP 0 /**< The TCP transport protocol */
#define MBEDTLS_NET_PROTO_UDP 1 /**< The UDP transport protocol */ #define MBEDTLS_NET_PROTO_UDP 1 /**< The UDP transport protocol */
#define MBEDTLS_NET_POLL_READ 1 /**< Used in \c mbedtls_net_poll to check for pending data */
#define MBEDTLS_NET_POLL_WRITE 2 /**< Used in \c mbedtls_net_poll to check if write possible */
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -133,6 +138,29 @@ int mbedtls_net_accept( mbedtls_net_context *bind_ctx,
mbedtls_net_context *client_ctx, mbedtls_net_context *client_ctx,
void *client_ip, size_t buf_size, size_t *ip_len ); void *client_ip, size_t buf_size, size_t *ip_len );
/**
* \brief Check and wait for the context to be ready for read/write
*
* \param ctx Socket to check
* \param rw Bitflag composed of MBEDTLS_NET_POLL_READ and
* MBEDTLS_NET_POLL_WRITE specifying the events
* to wait for:
* - If MBEDTLS_NET_POLL_READ is set, the function
* will return as soon as the net context is available
* for reading.
* - If MBEDTLS_NET_POLL_WRITE is set, the function
* will return as soon as the net context is available
* for writing.
* \param timeout Maximal amount of time to wait before returning,
* in milliseconds. If \c timeout is zero, the
* function returns immediately. If \c timeout is
* -1u, the function blocks potentially indefinitely.
*
* \return Bitmask composed of MBEDTLS_NET_POLL_READ/WRITE
* on success or timeout, or a negative return code otherwise.
*/
int mbedtls_net_poll( mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout );
/** /**
* \brief Set the socket blocking * \brief Set the socket blocking
* *

View File

@ -112,13 +112,14 @@
#define MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED -0x6A80 /**< DTLS client must retry for hello verification */ #define MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED -0x6A80 /**< DTLS client must retry for hello verification */
#define MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL -0x6A00 /**< A buffer is too small to receive or write a message */ #define MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL -0x6A00 /**< A buffer is too small to receive or write a message */
#define MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE -0x6980 /**< None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages). */ #define MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE -0x6980 /**< None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages). */
#define MBEDTLS_ERR_SSL_WANT_READ -0x6900 /**< Connection requires a read call. */ #define MBEDTLS_ERR_SSL_WANT_READ -0x6900 /**< No data of requested type currently available on underlying transport. */
#define MBEDTLS_ERR_SSL_WANT_WRITE -0x6880 /**< Connection requires a write call. */ #define MBEDTLS_ERR_SSL_WANT_WRITE -0x6880 /**< Connection requires a write call. */
#define MBEDTLS_ERR_SSL_TIMEOUT -0x6800 /**< The operation timed out. */ #define MBEDTLS_ERR_SSL_TIMEOUT -0x6800 /**< The operation timed out. */
#define MBEDTLS_ERR_SSL_CLIENT_RECONNECT -0x6780 /**< The client initiated a reconnect from the same port. */ #define MBEDTLS_ERR_SSL_CLIENT_RECONNECT -0x6780 /**< The client initiated a reconnect from the same port. */
#define MBEDTLS_ERR_SSL_UNEXPECTED_RECORD -0x6700 /**< Record header looks valid but is not expected. */ #define MBEDTLS_ERR_SSL_UNEXPECTED_RECORD -0x6700 /**< Record header looks valid but is not expected. */
#define MBEDTLS_ERR_SSL_NON_FATAL -0x6680 /**< The alert message received indicates a non-fatal error. */ #define MBEDTLS_ERR_SSL_NON_FATAL -0x6680 /**< The alert message received indicates a non-fatal error. */
#define MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH -0x6600 /**< Couldn't set the hash for verifying CertificateVerify */ #define MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH -0x6600 /**< Couldn't set the hash for verifying CertificateVerify */
#define MBEDTLS_ERR_SSL_CONTINUE_PROCESSING -0x6580 /**< Internal-only message signaling that further message-processing should be done */
/* /*
* Various constants * Various constants
@ -2301,11 +2302,59 @@ void mbedtls_ssl_conf_renegotiation_period( mbedtls_ssl_config *conf,
#endif /* MBEDTLS_SSL_RENEGOTIATION */ #endif /* MBEDTLS_SSL_RENEGOTIATION */
/** /**
* \brief Return the number of data bytes available to read * \brief Check if there is data already read from the
* underlying transport but not yet processed.
* *
* \param ssl SSL context * \param ssl SSL context
* *
* \return how many bytes are available in the read buffer * \return 0 if nothing's pending, 1 otherwise.
*
* \note This is different in purpose and behaviour from
* \c mbedtls_ssl_get_bytes_avail in that it considers
* any kind of unprocessed data, not only unread
* application data. If \c mbedtls_ssl_get_bytes
* returns a non-zero value, this function will
* also signal pending data, but the converse does
* not hold. For example, in DTLS there might be
* further records waiting to be processed from
* the current underlying transport's datagram.
*
* \note If this function returns 1 (data pending), this
* does not imply that a subsequent call to
* \c mbedtls_ssl_read will provide any data;
* e.g., the unprocessed data might turn out
* to be an alert or a handshake message.
*
* \note This function is useful in the following situation:
* If the SSL/TLS module successfully returns from an
* operation - e.g. a handshake or an application record
* read - and you're awaiting incoming data next, you
* must not immediately idle on the underlying transport
* to have data ready, but you need to check the value
* of this function first. The reason is that the desired
* data might already be read but not yet processed.
* If, in contrast, a previous call to the SSL/TLS module
* returned MBEDTLS_ERR_SSL_WANT_READ, it is not necessary
* to call this function, as the latter error code entails
* that all internal data has been processed.
*
*/
int mbedtls_ssl_check_pending( const mbedtls_ssl_context *ssl );
/**
* \brief Return the number of application data bytes
* remaining to be read from the current record.
*
* \param ssl SSL context
*
* \return How many bytes are available in the application
* data record read buffer.
*
* \note When working over a datagram transport, this is
* useful to detect the current datagram's boundary
* in case \c mbedtls_ssl_read has written the maximal
* amount of data fitting into the input buffer.
*
*/ */
size_t mbedtls_ssl_get_bytes_avail( const mbedtls_ssl_context *ssl ); size_t mbedtls_ssl_get_bytes_avail( const mbedtls_ssl_context *ssl );
@ -2420,11 +2469,25 @@ int mbedtls_ssl_get_session( const mbedtls_ssl_context *ssl, mbedtls_ssl_session
* MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED (see below), or * MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED (see below), or
* a specific SSL error code. * a specific SSL error code.
* *
* If this function returns MBEDTLS_ERR_SSL_WANT_READ, the
* handshake is unfinished and no further data is available
* from the underlying transport. In this case, you must call
* the function again at some later stage.
*
* \note Remarks regarding event-driven DTLS:
* If the function returns MBEDTLS_ERR_SSL_WANT_READ, no datagram
* from the underlying transport layer is currently being processed,
* and it is safe to idle until the timer or the underlying transport
* signal a new event. This is not true for a successful handshake,
* in which case the datagram of the underlying transport that is
* currently being processed might or might not contain further
* DTLS records.
*
* \note If this function returns something other than 0 or * \note If this function returns something other than 0 or
* MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
* becomes unusable, and you should either free it or call * the SSL context for reading or writing, and either free it or
* \c mbedtls_ssl_session_reset() on it before re-using it for * call \c mbedtls_ssl_session_reset() on it before re-using it
* a new connection; the current connection must be closed. * for a new connection; the current connection must be closed.
* *
* \note If DTLS is in use, then you may choose to handle * \note If DTLS is in use, then you may choose to handle
* MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED specially for logging * MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED specially for logging
@ -2441,10 +2504,10 @@ int mbedtls_ssl_handshake( mbedtls_ssl_context *ssl );
* call this function if state is MBEDTLS_SSL_HANDSHAKE_OVER. * call this function if state is MBEDTLS_SSL_HANDSHAKE_OVER.
* *
* \note If this function returns something other than 0 or * \note If this function returns something other than 0 or
* MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
* becomes unusable, and you should either free it or call * the SSL context for reading or writing, and either free it or
* \c mbedtls_ssl_session_reset() on it before re-using it for * call \c mbedtls_ssl_session_reset() on it before re-using it
* a new connection; the current connection must be closed. * for a new connection; the current connection must be closed.
* *
* \param ssl SSL context * \param ssl SSL context
* *
@ -2468,10 +2531,10 @@ int mbedtls_ssl_handshake_step( mbedtls_ssl_context *ssl );
* value. * value.
* *
* \note If this function returns something other than 0 or * \note If this function returns something other than 0 or
* MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
* becomes unusable, and you should either free it or call * the SSL context for reading or writing, and either free it or
* \c mbedtls_ssl_session_reset() on it before re-using it for * call \c mbedtls_ssl_session_reset() on it before re-using it
* a new connection; the current connection must be closed. * for a new connection; the current connection must be closed.
*/ */
int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl ); int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl );
#endif /* MBEDTLS_SSL_RENEGOTIATION */ #endif /* MBEDTLS_SSL_RENEGOTIATION */
@ -2483,20 +2546,20 @@ int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl );
* \param buf buffer that will hold the data * \param buf buffer that will hold the data
* \param len maximum number of bytes to read * \param len maximum number of bytes to read
* *
* \return the number of bytes read, or * \return One of the following:
* 0 for EOF, or * - 0 if the read end of the underlying transport was closed,
* MBEDTLS_ERR_SSL_WANT_READ or MBEDTLS_ERR_SSL_WANT_WRITE, or * - the (positive) number of bytes read, or
* MBEDTLS_ERR_SSL_CLIENT_RECONNECT (see below), or * - a negative error code on failure.
* another negative error code.
* *
* \note If this function returns something other than a positive * If MBEDTLS_ERR_SSL_WANT_READ is returned, no application data
* value or MBEDTLS_ERR_SSL_WANT_READ/WRITE or * is available from the underlying transport. In this case,
* MBEDTLS_ERR_SSL_CLIENT_RECONNECT, then the ssl context * the function needs to be called again at some later stage.
* becomes unusable, and you should either free it or call
* \c mbedtls_ssl_session_reset() on it before re-using it for
* a new connection; the current connection must be closed.
* *
* \note When this function return MBEDTLS_ERR_SSL_CLIENT_RECONNECT * If MBEDTLS_ERR_SSL_WANT_WRITE is returned, a write is pending
* but the underlying transport isn't available for writing. In this
* case, the function needs to be called again at some later stage.
*
* When this function return MBEDTLS_ERR_SSL_CLIENT_RECONNECT
* (which can only happen server-side), it means that a client * (which can only happen server-side), it means that a client
* is initiating a new connection using the same source port. * is initiating a new connection using the same source port.
* You can either treat that as a connection close and wait * You can either treat that as a connection close and wait
@ -2509,6 +2572,28 @@ int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl );
* again. WARNING: not validating the identity of the client * again. WARNING: not validating the identity of the client
* again, or not transmitting the new identity to the * again, or not transmitting the new identity to the
* application layer, would allow authentication bypass! * application layer, would allow authentication bypass!
*
* \note If this function returns something other than a positive value
* or MBEDTLS_ERR_SSL_WANT_READ/WRITE or MBEDTLS_ERR_SSL_CLIENT_RECONNECT,
* you must stop using the SSL context for reading or writing,
* and either free it or call \c mbedtls_ssl_session_reset() on it
* before re-using it for a new connection; the current connection
* must be closed.
*
* \note Remarks regarding event-driven DTLS:
* - If the function returns MBEDTLS_ERR_SSL_WANT_READ, no datagram
* from the underlying transport layer is currently being processed,
* and it is safe to idle until the timer or the underlying transport
* signal a new event.
* - This function may return MBEDTLS_ERR_SSL_WANT_READ even if data was
* initially available on the underlying transport, as this data may have
* been only e.g. duplicated messages or a renegotiation request.
* Therefore, you must be prepared to receive MBEDTLS_ERR_SSL_WANT_READ even
* when reacting to an incoming-data event from the underlying transport.
* - On success, the datagram of the underlying transport that is currently
* being processed may contain further DTLS records. You should call
* \c mbedtls_ssl_check_pending to check for remaining records.
*
*/ */
int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len ); int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len );
@ -2529,11 +2614,11 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
* or MBEDTLS_ERR_SSL_WANT_WRITE or MBEDTLS_ERR_SSL_WANT_READ, * or MBEDTLS_ERR_SSL_WANT_WRITE or MBEDTLS_ERR_SSL_WANT_READ,
* or another negative error code. * or another negative error code.
* *
* \note If this function returns something other than a positive * \note If this function returns something other than a positive value
* value or MBEDTLS_ERR_SSL_WANT_READ/WRITE, the ssl context * or MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
* becomes unusable, and you should either free it or call * the SSL context for reading or writing, and either free it or
* \c mbedtls_ssl_session_reset() on it before re-using it for * call \c mbedtls_ssl_session_reset() on it before re-using it
* a new connection; the current connection must be closed. * for a new connection; the current connection must be closed.
* *
* \note When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ, * \note When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ,
* it must be called later with the *same* arguments, * it must be called later with the *same* arguments,
@ -2562,10 +2647,10 @@ int mbedtls_ssl_write( mbedtls_ssl_context *ssl, const unsigned char *buf, size_
* \return 0 if successful, or a specific SSL error code. * \return 0 if successful, or a specific SSL error code.
* *
* \note If this function returns something other than 0 or * \note If this function returns something other than 0 or
* MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
* becomes unusable, and you should either free it or call * the SSL context for reading or writing, and either free it or
* \c mbedtls_ssl_session_reset() on it before re-using it for * call \c mbedtls_ssl_session_reset() on it before re-using it
* a new connection; the current connection must be closed. * for a new connection; the current connection must be closed.
*/ */
int mbedtls_ssl_send_alert_message( mbedtls_ssl_context *ssl, int mbedtls_ssl_send_alert_message( mbedtls_ssl_context *ssl,
unsigned char level, unsigned char level,
@ -2578,10 +2663,10 @@ int mbedtls_ssl_send_alert_message( mbedtls_ssl_context *ssl,
* \return 0 if successful, or a specific SSL error code. * \return 0 if successful, or a specific SSL error code.
* *
* \note If this function returns something other than 0 or * \note If this function returns something other than 0 or
* MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
* becomes unusable, and you should either free it or call * the SSL context for reading or writing, and either free it or
* \c mbedtls_ssl_session_reset() on it before re-using it for * call \c mbedtls_ssl_session_reset() on it before re-using it
* a new connection; the current connection must be closed. * for a new connection; the current connection must be closed.
*/ */
int mbedtls_ssl_close_notify( mbedtls_ssl_context *ssl ); int mbedtls_ssl_close_notify( mbedtls_ssl_context *ssl );

View File

@ -478,7 +478,7 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen )
if( use_ret == -(MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE) ) if( use_ret == -(MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE) )
mbedtls_snprintf( buf, buflen, "SSL - None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages)" ); mbedtls_snprintf( buf, buflen, "SSL - None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages)" );
if( use_ret == -(MBEDTLS_ERR_SSL_WANT_READ) ) if( use_ret == -(MBEDTLS_ERR_SSL_WANT_READ) )
mbedtls_snprintf( buf, buflen, "SSL - Connection requires a read call" ); mbedtls_snprintf( buf, buflen, "SSL - No data of requested type currently available on underlying transport" );
if( use_ret == -(MBEDTLS_ERR_SSL_WANT_WRITE) ) if( use_ret == -(MBEDTLS_ERR_SSL_WANT_WRITE) )
mbedtls_snprintf( buf, buflen, "SSL - Connection requires a write call" ); mbedtls_snprintf( buf, buflen, "SSL - Connection requires a write call" );
if( use_ret == -(MBEDTLS_ERR_SSL_TIMEOUT) ) if( use_ret == -(MBEDTLS_ERR_SSL_TIMEOUT) )
@ -491,6 +491,8 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen )
mbedtls_snprintf( buf, buflen, "SSL - The alert message received indicates a non-fatal error" ); mbedtls_snprintf( buf, buflen, "SSL - The alert message received indicates a non-fatal error" );
if( use_ret == -(MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH) ) if( use_ret == -(MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH) )
mbedtls_snprintf( buf, buflen, "SSL - Couldn't set the hash for verifying CertificateVerify" ); mbedtls_snprintf( buf, buflen, "SSL - Couldn't set the hash for verifying CertificateVerify" );
if( use_ret == -(MBEDTLS_ERR_SSL_CONTINUE_PROCESSING) )
mbedtls_snprintf( buf, buflen, "SSL - Internal-only message signaling that further message-processing should be done" );
#endif /* MBEDTLS_SSL_TLS_C */ #endif /* MBEDTLS_SSL_TLS_C */
#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) #if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C)
@ -745,6 +747,10 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen )
mbedtls_snprintf( buf, buflen, "NET - Buffer is too small to hold the data" ); mbedtls_snprintf( buf, buflen, "NET - Buffer is too small to hold the data" );
if( use_ret == -(MBEDTLS_ERR_NET_INVALID_CONTEXT) ) if( use_ret == -(MBEDTLS_ERR_NET_INVALID_CONTEXT) )
mbedtls_snprintf( buf, buflen, "NET - The context is invalid, eg because it was free()ed" ); mbedtls_snprintf( buf, buflen, "NET - The context is invalid, eg because it was free()ed" );
if( use_ret == -(MBEDTLS_ERR_NET_POLL_FAILED) )
mbedtls_snprintf( buf, buflen, "NET - Polling the net context failed" );
if( use_ret == -(MBEDTLS_ERR_NET_BAD_INPUT_DATA) )
mbedtls_snprintf( buf, buflen, "NET - Input invalid" );
#endif /* MBEDTLS_NET_C */ #endif /* MBEDTLS_NET_C */
#if defined(MBEDTLS_OID_C) #if defined(MBEDTLS_OID_C)

View File

@ -45,6 +45,8 @@
#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ #if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \
!defined(EFI32) !defined(EFI32)
#define IS_EINTR( ret ) ( ( ret ) == WSAEINTR )
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
@ -82,6 +84,8 @@ static int wsa_init_done = 0;
#include <netdb.h> #include <netdb.h>
#include <errno.h> #include <errno.h>
#define IS_EINTR( ret ) ( ( ret ) == EINTR )
#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */ #endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */
/* Some MS functions want int and MSVC warns if we pass size_t, /* Some MS functions want int and MSVC warns if we pass size_t,
@ -438,6 +442,68 @@ int mbedtls_net_set_nonblock( mbedtls_net_context *ctx )
#endif #endif
} }
/*
* Check if data is available on the socket
*/
int mbedtls_net_poll( mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout )
{
int ret;
struct timeval tv;
fd_set read_fds;
fd_set write_fds;
int fd = ctx->fd;
if( fd < 0 )
return( MBEDTLS_ERR_NET_INVALID_CONTEXT );
/* Ensure that memory sanitizers consider
* read_fds and write_fds as initialized even
* if FD_ZERO is implemented in assembly. */
memset( &read_fds, 0, sizeof( read_fds ) );
memset( &write_fds, 0, sizeof( write_fds ) );
FD_ZERO( &read_fds );
if( rw & MBEDTLS_NET_POLL_READ )
{
rw &= ~MBEDTLS_NET_POLL_READ;
FD_SET( fd, &read_fds );
}
FD_ZERO( &write_fds );
if( rw & MBEDTLS_NET_POLL_WRITE )
{
rw &= ~MBEDTLS_NET_POLL_WRITE;
FD_SET( fd, &write_fds );
}
if( rw != 0 )
return( MBEDTLS_ERR_NET_BAD_INPUT_DATA );
tv.tv_sec = timeout / 1000;
tv.tv_usec = ( timeout % 1000 ) * 1000;
do
{
ret = select( fd + 1, &read_fds, &write_fds, NULL,
timeout == (uint32_t) -1 ? NULL : &tv );
}
while( IS_EINTR( ret ) );
if( ret < 0 )
return( MBEDTLS_ERR_NET_POLL_FAILED );
ret = 0;
if( FD_ISSET( fd, &read_fds ) )
ret |= MBEDTLS_NET_POLL_READ;
if( FD_ISSET( fd, &write_fds ) )
ret |= MBEDTLS_NET_POLL_WRITE;
return( ret );
}
/* /*
* Portable usleep helper * Portable usleep helper
*/ */
@ -497,8 +563,8 @@ int mbedtls_net_recv( void *ctx, unsigned char *buf, size_t len )
/* /*
* Read at most 'len' characters, blocking for at most 'timeout' ms * Read at most 'len' characters, blocking for at most 'timeout' ms
*/ */
int mbedtls_net_recv_timeout( void *ctx, unsigned char *buf, size_t len, int mbedtls_net_recv_timeout( void *ctx, unsigned char *buf,
uint32_t timeout ) size_t len, uint32_t timeout )
{ {
int ret; int ret;
struct timeval tv; struct timeval tv;

View File

@ -3750,7 +3750,10 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl )
/* Read the message without adding it to the checksum */ /* Read the message without adding it to the checksum */
do { do {
if( ( ret = mbedtls_ssl_read_record_layer( ssl ) ) != 0 ) do ret = mbedtls_ssl_read_record_layer( ssl );
while( ret == MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
if( ret != 0 )
{ {
MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret ); MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret );
return( ret ); return( ret );
@ -3758,7 +3761,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl )
ret = mbedtls_ssl_handle_message_type( ssl ); ret = mbedtls_ssl_handle_message_type( ssl );
} while( MBEDTLS_ERR_SSL_NON_FATAL == ret ); } while( MBEDTLS_ERR_SSL_NON_FATAL == ret ||
MBEDTLS_ERR_SSL_CONTINUE_PROCESSING == ret );
if( 0 != ret ) if( 0 != ret )
{ {

View File

@ -2337,7 +2337,10 @@ int mbedtls_ssl_fetch_input( mbedtls_ssl_context *ssl, size_t nb_want )
* that will end up being dropped. * that will end up being dropped.
*/ */
if( ssl_check_timer( ssl ) != 0 ) if( ssl_check_timer( ssl ) != 0 )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "timer has expired" ) );
ret = MBEDTLS_ERR_SSL_TIMEOUT; ret = MBEDTLS_ERR_SSL_TIMEOUT;
}
else else
{ {
len = MBEDTLS_SSL_BUFFER_LEN - ( ssl->in_hdr - ssl->in_buf ); len = MBEDTLS_SSL_BUFFER_LEN - ( ssl->in_hdr - ssl->in_buf );
@ -3085,7 +3088,7 @@ static int ssl_reassemble_dtls_handshake( mbedtls_ssl_context *ssl )
if( ssl_bitmask_check( bitmask, msg_len ) != 0 ) if( ssl_bitmask_check( bitmask, msg_len ) != 0 )
{ {
MBEDTLS_SSL_DEBUG_MSG( 2, ( "message is not complete yet" ) ); MBEDTLS_SSL_DEBUG_MSG( 2, ( "message is not complete yet" ) );
return( MBEDTLS_ERR_SSL_WANT_READ ); return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
} }
MBEDTLS_SSL_DEBUG_MSG( 2, ( "handshake message completed" ) ); MBEDTLS_SSL_DEBUG_MSG( 2, ( "handshake message completed" ) );
@ -3162,9 +3165,11 @@ int mbedtls_ssl_prepare_handshake_record( mbedtls_ssl_context *ssl )
int ret; int ret;
unsigned int recv_msg_seq = ( ssl->in_msg[4] << 8 ) | ssl->in_msg[5]; unsigned int recv_msg_seq = ( ssl->in_msg[4] << 8 ) | ssl->in_msg[5];
/* ssl->handshake is NULL when receiving ClientHello for renego */
if( ssl->handshake != NULL && if( ssl->handshake != NULL &&
recv_msg_seq != ssl->handshake->in_msg_seq ) ( ( ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER &&
recv_msg_seq != ssl->handshake->in_msg_seq ) ||
( ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER &&
ssl->in_msg[0] != MBEDTLS_SSL_HS_CLIENT_HELLO ) ) )
{ {
/* Retransmit only on last message from previous flight, to avoid /* Retransmit only on last message from previous flight, to avoid
* too many retransmissions. * too many retransmissions.
@ -3191,7 +3196,7 @@ int mbedtls_ssl_prepare_handshake_record( mbedtls_ssl_context *ssl )
ssl->handshake->in_msg_seq ) ); ssl->handshake->in_msg_seq ) );
} }
return( MBEDTLS_ERR_SSL_WANT_READ ); return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
} }
/* Wait until message completion to increment in_msg_seq */ /* Wait until message completion to increment in_msg_seq */
@ -3594,81 +3599,23 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl )
return( MBEDTLS_ERR_SSL_INVALID_RECORD ); return( MBEDTLS_ERR_SSL_INVALID_RECORD );
} }
/* Check length against bounds of the current transform and version */
if( ssl->transform_in == NULL )
{
if( ssl->in_msglen < 1 ||
ssl->in_msglen > MBEDTLS_SSL_MAX_CONTENT_LEN )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
}
else
{
if( ssl->in_msglen < ssl->transform_in->minlen )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
#if defined(MBEDTLS_SSL_PROTO_SSL3)
if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 &&
ssl->in_msglen > ssl->transform_in->minlen + MBEDTLS_SSL_MAX_CONTENT_LEN )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
#endif
#if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
defined(MBEDTLS_SSL_PROTO_TLS1_2)
/*
* TLS encrypted messages can have up to 256 bytes of padding
*/
if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 &&
ssl->in_msglen > ssl->transform_in->minlen +
MBEDTLS_SSL_MAX_CONTENT_LEN + 256 )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
#endif
}
/* /*
* DTLS-related tests done last, because most of them may result in * DTLS-related tests.
* silently dropping the record (but not the whole datagram), and we only * Check epoch before checking length constraint because
* want to consider that after ensuring that the "basic" fields (type, * the latter varies with the epoch. E.g., if a ChangeCipherSpec
* version, length) are sane. * message gets duplicated before the corresponding Finished message,
* the second ChangeCipherSpec should be discarded because it belongs
* to an old epoch, but not because its length is shorter than
* the minimum record length for packets using the new record transform.
* Note that these two kinds of failures are handled differently,
* as an unexpected record is silently skipped but an invalid
* record leads to the entire datagram being dropped.
*/ */
#if defined(MBEDTLS_SSL_PROTO_DTLS) #if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM ) if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
{ {
unsigned int rec_epoch = ( ssl->in_ctr[0] << 8 ) | ssl->in_ctr[1]; unsigned int rec_epoch = ( ssl->in_ctr[0] << 8 ) | ssl->in_ctr[1];
/* Drop unexpected ChangeCipherSpec messages */
if( ssl->in_msgtype == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC &&
ssl->state != MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC &&
ssl->state != MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ChangeCipherSpec" ) );
return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
}
/* Drop unexpected ApplicationData records,
* except at the beginning of renegotiations */
if( ssl->in_msgtype == MBEDTLS_SSL_MSG_APPLICATION_DATA &&
ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER
#if defined(MBEDTLS_SSL_RENEGOTIATION)
&& ! ( ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS &&
ssl->state == MBEDTLS_SSL_SERVER_HELLO )
#endif
)
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ApplicationData" ) );
return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
}
/* Check epoch (and sequence number) with DTLS */ /* Check epoch (and sequence number) with DTLS */
if( rec_epoch != ssl->in_epoch ) if( rec_epoch != ssl->in_epoch )
{ {
@ -3708,9 +3655,74 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl )
return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD ); return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
} }
#endif #endif
/* Drop unexpected ChangeCipherSpec messages */
if( ssl->in_msgtype == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC &&
ssl->state != MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC &&
ssl->state != MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ChangeCipherSpec" ) );
return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
}
/* Drop unexpected ApplicationData records,
* except at the beginning of renegotiations */
if( ssl->in_msgtype == MBEDTLS_SSL_MSG_APPLICATION_DATA &&
ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER
#if defined(MBEDTLS_SSL_RENEGOTIATION)
&& ! ( ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS &&
ssl->state == MBEDTLS_SSL_SERVER_HELLO )
#endif
)
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ApplicationData" ) );
return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
}
} }
#endif /* MBEDTLS_SSL_PROTO_DTLS */ #endif /* MBEDTLS_SSL_PROTO_DTLS */
/* Check length against bounds of the current transform and version */
if( ssl->transform_in == NULL )
{
if( ssl->in_msglen < 1 ||
ssl->in_msglen > MBEDTLS_SSL_MAX_CONTENT_LEN )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
}
else
{
if( ssl->in_msglen < ssl->transform_in->minlen )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
#if defined(MBEDTLS_SSL_PROTO_SSL3)
if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 &&
ssl->in_msglen > ssl->transform_in->minlen + MBEDTLS_SSL_MAX_CONTENT_LEN )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
#endif
#if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
defined(MBEDTLS_SSL_PROTO_TLS1_2)
/*
* TLS encrypted messages can have up to 256 bytes of padding
*/
if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 &&
ssl->in_msglen > ssl->transform_in->minlen +
MBEDTLS_SSL_MAX_CONTENT_LEN + 256 )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
#endif
}
return( 0 ); return( 0 );
} }
@ -3799,7 +3811,10 @@ int mbedtls_ssl_read_record( mbedtls_ssl_context *ssl )
{ {
do { do {
if( ( ret = mbedtls_ssl_read_record_layer( ssl ) ) != 0 ) do ret = mbedtls_ssl_read_record_layer( ssl );
while( ret == MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
if( ret != 0 )
{ {
MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret ); MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret );
return( ret ); return( ret );
@ -3807,11 +3822,12 @@ int mbedtls_ssl_read_record( mbedtls_ssl_context *ssl )
ret = mbedtls_ssl_handle_message_type( ssl ); ret = mbedtls_ssl_handle_message_type( ssl );
} while( MBEDTLS_ERR_SSL_NON_FATAL == ret ); } while( MBEDTLS_ERR_SSL_NON_FATAL == ret ||
MBEDTLS_ERR_SSL_CONTINUE_PROCESSING == ret );
if( 0 != ret ) if( 0 != ret )
{ {
MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret ); MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_handle_message_type" ), ret );
return( ret ); return( ret );
} }
@ -3849,11 +3865,6 @@ int mbedtls_ssl_read_record_layer( mbedtls_ssl_context *ssl )
* (2) Alert messages: * (2) Alert messages:
* Consume whole record content, in_msglen = 0. * Consume whole record content, in_msglen = 0.
* *
* NOTE: This needs to be fixed, since like for
* handshake messages it is allowed to have
* multiple alerts witin a single record.
* Internal reference IOTSSL-1321.
*
* (3) Change cipher spec: * (3) Change cipher spec:
* Consume whole record content, in_msglen = 0. * Consume whole record content, in_msglen = 0.
* *
@ -3881,12 +3892,12 @@ int mbedtls_ssl_read_record_layer( mbedtls_ssl_context *ssl )
*/ */
/* Notes: /* Notes:
* (1) in_hslen is *NOT* necessarily the size of the * (1) in_hslen is not necessarily the size of the
* current handshake content: If DTLS handshake * current handshake content: If DTLS handshake
* fragmentation is used, that's the fragment * fragmentation is used, that's the fragment
* size instead. Using the total handshake message * size instead. Using the total handshake message
* size here is FAULTY and should be changed at * size here is faulty and should be changed at
* some point. Internal reference IOTSSL-1414. * some point.
* (2) While it doesn't seem to cause problems, one * (2) While it doesn't seem to cause problems, one
* has to be very careful not to assume that in_hslen * has to be very careful not to assume that in_hslen
* is always <= in_msglen in a sensible communication. * is always <= in_msglen in a sensible communication.
@ -3937,12 +3948,6 @@ int mbedtls_ssl_read_record_layer( mbedtls_ssl_context *ssl )
return( 0 ); return( 0 );
} }
/* Need to fetch a new record */
#if defined(MBEDTLS_SSL_PROTO_DTLS)
read_record_header:
#endif
/* Current record either fully processed or to be discarded. */ /* Current record either fully processed or to be discarded. */
if( ( ret = mbedtls_ssl_fetch_input( ssl, mbedtls_ssl_hdr_len( ssl ) ) ) != 0 ) if( ( ret = mbedtls_ssl_fetch_input( ssl, mbedtls_ssl_hdr_len( ssl ) ) ) != 0 )
@ -3977,7 +3982,7 @@ read_record_header:
} }
/* Get next record */ /* Get next record */
goto read_record_header; return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
} }
#endif #endif
return( ret ); return( ret );
@ -3996,7 +4001,13 @@ read_record_header:
/* Done reading this record, get ready for the next one */ /* Done reading this record, get ready for the next one */
#if defined(MBEDTLS_SSL_PROTO_DTLS) #if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM ) if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
{
ssl->next_record_offset = ssl->in_msglen + mbedtls_ssl_hdr_len( ssl ); ssl->next_record_offset = ssl->in_msglen + mbedtls_ssl_hdr_len( ssl );
if( ssl->next_record_offset < ssl->in_left )
{
MBEDTLS_SSL_DEBUG_MSG( 3, ( "more than one record within datagram" ) );
}
}
else else
#endif #endif
ssl->in_left = 0; ssl->in_left = 0;
@ -4043,7 +4054,7 @@ read_record_header:
ssl->in_left = 0; ssl->in_left = 0;
MBEDTLS_SSL_DEBUG_MSG( 1, ( "discarding invalid record (mac)" ) ); MBEDTLS_SSL_DEBUG_MSG( 1, ( "discarding invalid record (mac)" ) );
goto read_record_header; return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
} }
return( ret ); return( ret );
@ -4064,46 +4075,6 @@ read_record_header:
} }
} }
/*
* When we sent the last flight of the handshake, we MUST respond to a
* retransmit of the peer's previous flight with a retransmit. (In
* practice, only the Finished message will make it, other messages
* including CCS use the old transform so they're dropped as invalid.)
*
* If the record we received is not a handshake message, however, it
* means the peer received our last flight so we can clean up
* handshake info.
*
* This check needs to be done before prepare_handshake() due to an edge
* case: if the client immediately requests renegotiation, this
* finishes the current handshake first, avoiding the new ClientHello
* being mistaken for an ancient message in the current handshake.
*/
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
ssl->handshake != NULL &&
ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER )
{
if( ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE &&
ssl->in_msg[0] == MBEDTLS_SSL_HS_FINISHED )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "received retransmit of last flight" ) );
if( ( ret = mbedtls_ssl_resend( ssl ) ) != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_resend", ret );
return( ret );
}
return( MBEDTLS_ERR_SSL_WANT_READ );
}
else
{
ssl_handshake_wrapup_free_hs_transform( ssl );
}
}
#endif
return( 0 ); return( 0 );
} }
@ -4148,7 +4119,7 @@ int mbedtls_ssl_handle_message_type( mbedtls_ssl_context *ssl )
if( ssl->in_msg[0] == MBEDTLS_SSL_ALERT_LEVEL_WARNING && if( ssl->in_msg[0] == MBEDTLS_SSL_ALERT_LEVEL_WARNING &&
ssl->in_msg[1] == MBEDTLS_SSL_ALERT_MSG_NO_RENEGOTIATION ) ssl->in_msg[1] == MBEDTLS_SSL_ALERT_MSG_NO_RENEGOTIATION )
{ {
MBEDTLS_SSL_DEBUG_MSG( 2, ( "is a SSLv3 no_cert" ) ); MBEDTLS_SSL_DEBUG_MSG( 2, ( "is a SSLv3 no renegotiation alert" ) );
/* Will be handled when trying to parse ServerHello */ /* Will be handled when trying to parse ServerHello */
return( 0 ); return( 0 );
} }
@ -4170,6 +4141,15 @@ int mbedtls_ssl_handle_message_type( mbedtls_ssl_context *ssl )
return MBEDTLS_ERR_SSL_NON_FATAL; return MBEDTLS_ERR_SSL_NON_FATAL;
} }
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
ssl->handshake != NULL &&
ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER )
{
ssl_handshake_wrapup_free_hs_transform( ssl );
}
#endif
return( 0 ); return( 0 );
} }
@ -6506,6 +6486,61 @@ size_t mbedtls_ssl_get_bytes_avail( const mbedtls_ssl_context *ssl )
return( ssl->in_offt == NULL ? 0 : ssl->in_msglen ); return( ssl->in_offt == NULL ? 0 : ssl->in_msglen );
} }
int mbedtls_ssl_check_pending( const mbedtls_ssl_context *ssl )
{
/*
* Case A: We're currently holding back
* a message for further processing.
*/
if( ssl->keep_current_message == 1 )
{
MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: record held back for processing" ) );
return( 1 );
}
/*
* Case B: Further records are pending in the current datagram.
*/
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
ssl->in_left > ssl->next_record_offset )
{
MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: more records within current datagram" ) );
return( 1 );
}
#endif /* MBEDTLS_SSL_PROTO_DTLS */
/*
* Case C: A handshake message is being processed.
*/
if( ssl->in_hslen > 0 && ssl->in_hslen < ssl->in_msglen )
{
MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: more handshake messages within current record" ) );
return( 1 );
}
/*
* Case D: An application data message is being processed
*/
if( ssl->in_offt != NULL )
{
MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: application data record is being processed" ) );
return( 1 );
}
/*
* In all other cases, the rest of the message can be dropped.
* As in ssl_read_record_layer, this needs to be adapted if
* we implement support for multiple alerts in single records.
*/
MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: nothing pending" ) );
return( 0 );
}
uint32_t mbedtls_ssl_get_verify_result( const mbedtls_ssl_context *ssl ) uint32_t mbedtls_ssl_get_verify_result( const mbedtls_ssl_context *ssl )
{ {
if( ssl->session != NULL ) if( ssl->session != NULL )
@ -6914,25 +6949,16 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
} }
/* /*
* TODO * The logic could be streamlined here. Instead of
*
* The logic should be streamlined here:
*
* Instead of
*
* - Manually checking whether ssl->in_offt is NULL * - Manually checking whether ssl->in_offt is NULL
* - Fetching a new record if yes * - Fetching a new record if yes
* - Setting ssl->in_offt if one finds an application record * - Setting ssl->in_offt if one finds an application record
* - Resetting keep_current_message after handling the application data * - Resetting keep_current_message after handling the application data
*
* one should * one should
*
* - Adapt read_record to set ssl->in_offt automatically * - Adapt read_record to set ssl->in_offt automatically
* when a new application data record is processed. * when a new application data record is processed.
* - Always call mbedtls_ssl_read_record here. * - Always call mbedtls_ssl_read_record here.
*
* This way, the logic of ssl_read would be much clearer: * This way, the logic of ssl_read would be much clearer:
*
* (1) Always call record layer and see what kind of record is on * (1) Always call record layer and see what kind of record is on
* and have it ready for consumption (in particular, in_offt * and have it ready for consumption (in particular, in_offt
* properly set for application data records). * properly set for application data records).
@ -6942,13 +6968,12 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
* (3) If it's something different from application data, * (3) If it's something different from application data,
* handle it accordingly, e.g. potentially start a * handle it accordingly, e.g. potentially start a
* renegotiation. * renegotiation.
*
* This will also remove the need to manually reset * This will also remove the need to manually reset
* ssl->keep_current_message = 0 below. * ssl->keep_current_message = 0 below.
*
*/ */
if( ssl->in_offt == NULL ) /* Loop as long as no application data record is available */
while( ssl->in_offt == NULL )
{ {
/* Start timer if not already running */ /* Start timer if not already running */
if( ssl->f_get_timer != NULL && if( ssl->f_get_timer != NULL &&
@ -7002,7 +7027,9 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
/* With DTLS, drop the packet (probably from last handshake) */ /* With DTLS, drop the packet (probably from last handshake) */
#if defined(MBEDTLS_SSL_PROTO_DTLS) #if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM ) if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
return( MBEDTLS_ERR_SSL_WANT_READ ); {
continue;
}
#endif #endif
return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE ); return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE );
} }
@ -7017,7 +7044,9 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
/* With DTLS, drop the packet (probably from last handshake) */ /* With DTLS, drop the packet (probably from last handshake) */
#if defined(MBEDTLS_SSL_PROTO_DTLS) #if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM ) if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
return( MBEDTLS_ERR_SSL_WANT_READ ); {
continue;
}
#endif #endif
return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE ); return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE );
} }
@ -7090,7 +7119,25 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
} }
} }
return( MBEDTLS_ERR_SSL_WANT_READ ); /* At this point, we don't know whether the renegotiation has been
* completed or not. The cases to consider are the following:
* 1) The renegotiation is complete. In this case, no new record
* has been read yet.
* 2) The renegotiation is incomplete because the client received
* an application data record while awaiting the ServerHello.
* 3) The renegotiation is incomplete because the client received
* a non-handshake, non-application data message while awaiting
* the ServerHello.
* In each of these case, looping will be the proper action:
* - For 1), the next iteration will read a new record and check
* if it's application data.
* - For 2), the loop condition isn't satisfied as application data
* is present, hence continue is the same as break
* - For 3), the loop condition is satisfied and read_record
* will re-deliver the message that was held back by the client
* when expecting the ServerHello.
*/
continue;
} }
#if defined(MBEDTLS_SSL_RENEGOTIATION) #if defined(MBEDTLS_SSL_RENEGOTIATION)
else if( ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_PENDING ) else if( ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_PENDING )

View File

@ -73,6 +73,7 @@ int main( void )
#define DFL_REQUEST_SIZE -1 #define DFL_REQUEST_SIZE -1
#define DFL_DEBUG_LEVEL 0 #define DFL_DEBUG_LEVEL 0
#define DFL_NBIO 0 #define DFL_NBIO 0
#define DFL_EVENT 0
#define DFL_READ_TIMEOUT 0 #define DFL_READ_TIMEOUT 0
#define DFL_MAX_RESEND 0 #define DFL_MAX_RESEND 0
#define DFL_CA_FILE "" #define DFL_CA_FILE ""
@ -245,24 +246,26 @@ int main( void )
" server_addr=%%s default: given by name\n" \ " server_addr=%%s default: given by name\n" \
" server_port=%%d default: 4433\n" \ " server_port=%%d default: 4433\n" \
" request_page=%%s default: \".\"\n" \ " request_page=%%s default: \".\"\n" \
" request_size=%%d default: about 34 (basic request)\n" \ " request_size=%%d default: about 34 (basic request)\n" \
" (minimum: 0, max: " MAX_REQUEST_SIZE_STR " )\n" \ " (minimum: 0, max: " MAX_REQUEST_SIZE_STR " )\n" \
" debug_level=%%d default: 0 (disabled)\n" \ " debug_level=%%d default: 0 (disabled)\n" \
" nbio=%%d default: 0 (blocking I/O)\n" \ " nbio=%%d default: 0 (blocking I/O)\n" \
" options: 1 (non-blocking), 2 (added delays)\n" \ " options: 1 (non-blocking), 2 (added delays)\n" \
" read_timeout=%%d default: 0 ms (no timeout)\n" \ " event=%%d default: 0 (loop)\n" \
" options: 1 (level-triggered, implies nbio=1),\n" \
" read_timeout=%%d default: 0 ms (no timeout)\n" \
" max_resend=%%d default: 0 (no resend on timeout)\n" \ " max_resend=%%d default: 0 (no resend on timeout)\n" \
"\n" \ "\n" \
USAGE_DTLS \ USAGE_DTLS \
"\n" \ "\n" \
" auth_mode=%%s default: (library default: none)\n" \ " auth_mode=%%s default: (library default: none)\n" \
" options: none, optional, required\n" \ " options: none, optional, required\n" \
USAGE_IO \ USAGE_IO \
"\n" \ "\n" \
USAGE_PSK \ USAGE_PSK \
USAGE_ECJPAKE \ USAGE_ECJPAKE \
"\n" \ "\n" \
" allow_legacy=%%d default: (library default: no)\n" \ " allow_legacy=%%d default: (library default: no)\n" \
USAGE_RENEGO \ USAGE_RENEGO \
" exchanges=%%d default: 1\n" \ " exchanges=%%d default: 1\n" \
" reconnect=%%d default: 0 (disabled)\n" \ " reconnect=%%d default: 0 (disabled)\n" \
@ -302,7 +305,8 @@ struct options
const char *server_port; /* port on which the ssl service runs */ const char *server_port; /* port on which the ssl service runs */
int debug_level; /* level of debugging */ int debug_level; /* level of debugging */
int nbio; /* should I/O be blocking? */ int nbio; /* should I/O be blocking? */
uint32_t read_timeout; /* timeout on mbedtls_ssl_read() in milliseconds */ int event; /* loop or event-driven IO? level or edge triggered? */
uint32_t read_timeout; /* timeout on mbedtls_ssl_read() in milliseconds */
int max_resend; /* DTLS times to resend on read timeout */ int max_resend; /* DTLS times to resend on read timeout */
const char *request_page; /* page on server to request */ const char *request_page; /* page on server to request */
int request_size; /* pad request with header to requested size */ int request_size; /* pad request with header to requested size */
@ -353,7 +357,8 @@ static void my_debug( void *ctx, int level,
if( *p == '/' || *p == '\\' ) if( *p == '/' || *p == '\\' )
basename = p + 1; basename = p + 1;
mbedtls_fprintf( (FILE *) ctx, "%s:%04d: |%d| %s", basename, line, level, str ); mbedtls_fprintf( (FILE *) ctx, "%s:%04d: |%d| %s",
basename, line, level, str );
fflush( (FILE *) ctx ); fflush( (FILE *) ctx );
} }
@ -399,7 +404,8 @@ static int my_send( void *ctx, const unsigned char *buf, size_t len )
/* /*
* Enabled if debug_level > 1 in code below * Enabled if debug_level > 1 in code below
*/ */
static int my_verify( void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags ) static int my_verify( void *data, mbedtls_x509_crt *crt,
int depth, uint32_t *flags )
{ {
char buf[1024]; char buf[1024];
((void) data); ((void) data);
@ -436,6 +442,57 @@ static int ssl_sig_hashes_for_test[] = {
}; };
#endif /* MBEDTLS_X509_CRT_PARSE_C */ #endif /* MBEDTLS_X509_CRT_PARSE_C */
/*
* Wait for an event from the underlying transport or the timer
* (Used in event-driven IO mode).
*/
#if !defined(MBEDTLS_TIMING_C)
int idle( mbedtls_net_context *fd,
int idle_reason )
#else
int idle( mbedtls_net_context *fd,
mbedtls_timing_delay_context *timer,
int idle_reason )
#endif
{
int ret;
int poll_type = 0;
if( idle_reason == MBEDTLS_ERR_SSL_WANT_WRITE )
poll_type = MBEDTLS_NET_POLL_WRITE;
else if( idle_reason == MBEDTLS_ERR_SSL_WANT_READ )
poll_type = MBEDTLS_NET_POLL_READ;
#if !defined(MBEDTLS_TIMING_C)
else
return( 0 );
#endif
while( 1 )
{
/* Check if timer has expired */
#if defined(MBEDTLS_TIMING_C)
if( timer != NULL &&
mbedtls_timing_get_delay( timer ) == 2 )
{
break;
}
#endif /* MBEDTLS_TIMING_C */
/* Check if underlying transport became available */
if( poll_type != 0 )
{
ret = mbedtls_net_poll( fd, poll_type, 0 );
if( ret < 0 )
return( ret );
if( ret == poll_type )
break;
}
}
return( 0 );
}
int main( int argc, char *argv[] ) int main( int argc, char *argv[] )
{ {
int ret = 0, len, tail_len, i, written, frags, retry_left; int ret = 0, len, tail_len, i, written, frags, retry_left;
@ -521,6 +578,7 @@ int main( int argc, char *argv[] )
opt.server_port = DFL_SERVER_PORT; opt.server_port = DFL_SERVER_PORT;
opt.debug_level = DFL_DEBUG_LEVEL; opt.debug_level = DFL_DEBUG_LEVEL;
opt.nbio = DFL_NBIO; opt.nbio = DFL_NBIO;
opt.event = DFL_EVENT;
opt.read_timeout = DFL_READ_TIMEOUT; opt.read_timeout = DFL_READ_TIMEOUT;
opt.max_resend = DFL_MAX_RESEND; opt.max_resend = DFL_MAX_RESEND;
opt.request_page = DFL_REQUEST_PAGE; opt.request_page = DFL_REQUEST_PAGE;
@ -594,6 +652,12 @@ int main( int argc, char *argv[] )
if( opt.nbio < 0 || opt.nbio > 2 ) if( opt.nbio < 0 || opt.nbio > 2 )
goto usage; goto usage;
} }
else if( strcmp( p, "event" ) == 0 )
{
opt.event = atoi( q );
if( opt.event < 0 || opt.event > 2 )
goto usage;
}
else if( strcmp( p, "read_timeout" ) == 0 ) else if( strcmp( p, "read_timeout" ) == 0 )
opt.read_timeout = atoi( q ); opt.read_timeout = atoi( q );
else if( strcmp( p, "max_resend" ) == 0 ) else if( strcmp( p, "max_resend" ) == 0 )
@ -638,16 +702,23 @@ int main( int argc, char *argv[] )
} }
else if( strcmp( p, "renegotiation" ) == 0 ) else if( strcmp( p, "renegotiation" ) == 0 )
{ {
opt.renegotiation = (atoi( q )) ? MBEDTLS_SSL_RENEGOTIATION_ENABLED : opt.renegotiation = (atoi( q )) ?
MBEDTLS_SSL_RENEGOTIATION_DISABLED; MBEDTLS_SSL_RENEGOTIATION_ENABLED :
MBEDTLS_SSL_RENEGOTIATION_DISABLED;
} }
else if( strcmp( p, "allow_legacy" ) == 0 ) else if( strcmp( p, "allow_legacy" ) == 0 )
{ {
switch( atoi( q ) ) switch( atoi( q ) )
{ {
case -1: opt.allow_legacy = MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE; break; case -1:
case 0: opt.allow_legacy = MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION; break; opt.allow_legacy = MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE;
case 1: opt.allow_legacy = MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION; break; break;
case 0:
opt.allow_legacy = MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION;
break;
case 1:
opt.allow_legacy = MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION;
break;
default: goto usage; default: goto usage;
} }
} }
@ -704,8 +775,12 @@ int main( int argc, char *argv[] )
{ {
switch( atoi( q ) ) switch( atoi( q ) )
{ {
case 0: opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_DISABLED; break; case 0:
case 1: opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED; break; opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_DISABLED;
break;
case 1:
opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED;
break;
default: goto usage; default: goto usage;
} }
} }
@ -864,6 +939,15 @@ int main( int argc, char *argv[] )
goto usage; goto usage;
} }
/* Event-driven IO is incompatible with the above custom
* receive and send functions, as the polling builds on
* refers to the underlying net_context. */
if( opt.event == 1 && opt.nbio != 1 )
{
mbedtls_printf( "Warning: event-driven IO mandates nbio=1 - overwrite\n" );
opt.nbio = 1;
}
#if defined(MBEDTLS_DEBUG_C) #if defined(MBEDTLS_DEBUG_C)
mbedtls_debug_set_threshold( opt.debug_level ); mbedtls_debug_set_threshold( opt.debug_level );
#endif #endif
@ -871,19 +955,20 @@ int main( int argc, char *argv[] )
if( opt.force_ciphersuite[0] > 0 ) if( opt.force_ciphersuite[0] > 0 )
{ {
const mbedtls_ssl_ciphersuite_t *ciphersuite_info; const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
ciphersuite_info = mbedtls_ssl_ciphersuite_from_id( opt.force_ciphersuite[0] ); ciphersuite_info =
mbedtls_ssl_ciphersuite_from_id( opt.force_ciphersuite[0] );
if( opt.max_version != -1 && if( opt.max_version != -1 &&
ciphersuite_info->min_minor_ver > opt.max_version ) ciphersuite_info->min_minor_ver > opt.max_version )
{ {
mbedtls_printf("forced ciphersuite not allowed with this protocol version\n"); mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
ret = 2; ret = 2;
goto usage; goto usage;
} }
if( opt.min_version != -1 && if( opt.min_version != -1 &&
ciphersuite_info->max_minor_ver < opt.min_version ) ciphersuite_info->max_minor_ver < opt.min_version )
{ {
mbedtls_printf("forced ciphersuite not allowed with this protocol version\n"); mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
ret = 2; ret = 2;
goto usage; goto usage;
} }
@ -909,7 +994,7 @@ int main( int argc, char *argv[] )
{ {
if( opt.arc4 == MBEDTLS_SSL_ARC4_DISABLED ) if( opt.arc4 == MBEDTLS_SSL_ARC4_DISABLED )
{ {
mbedtls_printf("forced RC4 ciphersuite with RC4 disabled\n"); mbedtls_printf( "forced RC4 ciphersuite with RC4 disabled\n" );
ret = 2; ret = 2;
goto usage; goto usage;
} }
@ -929,7 +1014,7 @@ int main( int argc, char *argv[] )
if( strlen( opt.psk ) % 2 != 0 ) if( strlen( opt.psk ) % 2 != 0 )
{ {
mbedtls_printf("pre-shared key not valid hex\n"); mbedtls_printf( "pre-shared key not valid hex\n" );
goto exit; goto exit;
} }
@ -946,7 +1031,7 @@ int main( int argc, char *argv[] )
c -= 'A' - 10; c -= 'A' - 10;
else else
{ {
mbedtls_printf("pre-shared key not valid hex\n"); mbedtls_printf( "pre-shared key not valid hex\n" );
goto exit; goto exit;
} }
psk[ j / 2 ] = c << 4; psk[ j / 2 ] = c << 4;
@ -960,7 +1045,7 @@ int main( int argc, char *argv[] )
c -= 'A' - 10; c -= 'A' - 10;
else else
{ {
mbedtls_printf("pre-shared key not valid hex\n"); mbedtls_printf( "pre-shared key not valid hex\n" );
goto exit; goto exit;
} }
psk[ j / 2 ] |= c; psk[ j / 2 ] |= c;
@ -1051,11 +1136,12 @@ int main( int argc, char *argv[] )
fflush( stdout ); fflush( stdout );
mbedtls_entropy_init( &entropy ); mbedtls_entropy_init( &entropy );
if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func,
(const unsigned char *) pers, &entropy, (const unsigned char *) pers,
strlen( pers ) ) ) != 0 ) strlen( pers ) ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n",
-ret );
goto exit; goto exit;
} }
@ -1093,12 +1179,13 @@ int main( int argc, char *argv[] )
#else #else
{ {
ret = 1; ret = 1;
mbedtls_printf("MBEDTLS_CERTS_C not defined."); mbedtls_printf( "MBEDTLS_CERTS_C not defined." );
} }
#endif #endif
if( ret < 0 ) if( ret < 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
@ -1121,7 +1208,8 @@ int main( int argc, char *argv[] )
else else
#endif #endif
#if defined(MBEDTLS_CERTS_C) #if defined(MBEDTLS_CERTS_C)
ret = mbedtls_x509_crt_parse( &clicert, (const unsigned char *) mbedtls_test_cli_crt, ret = mbedtls_x509_crt_parse( &clicert,
(const unsigned char *) mbedtls_test_cli_crt,
mbedtls_test_cli_crt_len ); mbedtls_test_cli_crt_len );
#else #else
{ {
@ -1131,7 +1219,8 @@ int main( int argc, char *argv[] )
#endif #endif
if( ret != 0 ) if( ret != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
@ -1144,7 +1233,8 @@ int main( int argc, char *argv[] )
else else
#endif #endif
#if defined(MBEDTLS_CERTS_C) #if defined(MBEDTLS_CERTS_C)
ret = mbedtls_pk_parse_key( &pkey, (const unsigned char *) mbedtls_test_cli_key, ret = mbedtls_pk_parse_key( &pkey,
(const unsigned char *) mbedtls_test_cli_key,
mbedtls_test_cli_key_len, NULL, 0 ); mbedtls_test_cli_key_len, NULL, 0 );
#else #else
{ {
@ -1154,7 +1244,8 @@ int main( int argc, char *argv[] )
#endif #endif
if( ret != 0 ) if( ret != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_pk_parse_key returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_pk_parse_key returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
@ -1172,11 +1263,13 @@ int main( int argc, char *argv[] )
opt.server_addr, opt.server_port ); opt.server_addr, opt.server_port );
fflush( stdout ); fflush( stdout );
if( ( ret = mbedtls_net_connect( &server_fd, opt.server_addr, opt.server_port, if( ( ret = mbedtls_net_connect( &server_fd,
opt.transport == MBEDTLS_SSL_TRANSPORT_STREAM ? opt.server_addr, opt.server_port,
MBEDTLS_NET_PROTO_TCP : MBEDTLS_NET_PROTO_UDP ) ) != 0 ) opt.transport == MBEDTLS_SSL_TRANSPORT_STREAM ?
MBEDTLS_NET_PROTO_TCP : MBEDTLS_NET_PROTO_UDP ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_net_connect returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_net_connect returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
@ -1186,7 +1279,8 @@ int main( int argc, char *argv[] )
ret = mbedtls_net_set_block( &server_fd ); ret = mbedtls_net_set_block( &server_fd );
if( ret != 0 ) if( ret != 0 )
{ {
mbedtls_printf( " failed\n ! net_set_(non)block() returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! net_set_(non)block() returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
@ -1203,7 +1297,8 @@ int main( int argc, char *argv[] )
opt.transport, opt.transport,
MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_config_defaults returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_config_defaults returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
@ -1226,13 +1321,15 @@ int main( int argc, char *argv[] )
#if defined(MBEDTLS_SSL_PROTO_DTLS) #if defined(MBEDTLS_SSL_PROTO_DTLS)
if( opt.hs_to_min != DFL_HS_TO_MIN || opt.hs_to_max != DFL_HS_TO_MAX ) if( opt.hs_to_min != DFL_HS_TO_MIN || opt.hs_to_max != DFL_HS_TO_MAX )
mbedtls_ssl_conf_handshake_timeout( &conf, opt.hs_to_min, opt.hs_to_max ); mbedtls_ssl_conf_handshake_timeout( &conf, opt.hs_to_min,
opt.hs_to_max );
#endif /* MBEDTLS_SSL_PROTO_DTLS */ #endif /* MBEDTLS_SSL_PROTO_DTLS */
#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
if( ( ret = mbedtls_ssl_conf_max_frag_len( &conf, opt.mfl_code ) ) != 0 ) if( ( ret = mbedtls_ssl_conf_max_frag_len( &conf, opt.mfl_code ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_conf_max_frag_len returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_conf_max_frag_len returned %d\n\n",
ret );
goto exit; goto exit;
} }
#endif #endif
@ -1255,8 +1352,8 @@ int main( int argc, char *argv[] )
#if defined(MBEDTLS_SSL_CBC_RECORD_SPLITTING) #if defined(MBEDTLS_SSL_CBC_RECORD_SPLITTING)
if( opt.recsplit != DFL_RECSPLIT ) if( opt.recsplit != DFL_RECSPLIT )
mbedtls_ssl_conf_cbc_record_splitting( &conf, opt.recsplit mbedtls_ssl_conf_cbc_record_splitting( &conf, opt.recsplit
? MBEDTLS_SSL_CBC_RECORD_SPLITTING_ENABLED ? MBEDTLS_SSL_CBC_RECORD_SPLITTING_ENABLED
: MBEDTLS_SSL_CBC_RECORD_SPLITTING_DISABLED ); : MBEDTLS_SSL_CBC_RECORD_SPLITTING_DISABLED );
#endif #endif
#if defined(MBEDTLS_DHM_C) #if defined(MBEDTLS_DHM_C)
@ -1268,7 +1365,8 @@ int main( int argc, char *argv[] )
if( opt.alpn_string != NULL ) if( opt.alpn_string != NULL )
if( ( ret = mbedtls_ssl_conf_alpn_protocols( &conf, alpn_list ) ) != 0 ) if( ( ret = mbedtls_ssl_conf_alpn_protocols( &conf, alpn_list ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_conf_alpn_protocols returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_conf_alpn_protocols returned %d\n\n",
ret );
goto exit; goto exit;
} }
#endif #endif
@ -1307,7 +1405,8 @@ int main( int argc, char *argv[] )
{ {
if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &clicert, &pkey ) ) != 0 ) if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &clicert, &pkey ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n",
ret );
goto exit; goto exit;
} }
} }
@ -1326,16 +1425,19 @@ int main( int argc, char *argv[] )
(const unsigned char *) opt.psk_identity, (const unsigned char *) opt.psk_identity,
strlen( opt.psk_identity ) ) ) != 0 ) strlen( opt.psk_identity ) ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_conf_psk returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_conf_psk returned %d\n\n",
ret );
goto exit; goto exit;
} }
#endif #endif
if( opt.min_version != DFL_MIN_VERSION ) if( opt.min_version != DFL_MIN_VERSION )
mbedtls_ssl_conf_min_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3, opt.min_version ); mbedtls_ssl_conf_min_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3,
opt.min_version );
if( opt.max_version != DFL_MAX_VERSION ) if( opt.max_version != DFL_MAX_VERSION )
mbedtls_ssl_conf_max_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3, opt.max_version ); mbedtls_ssl_conf_max_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3,
opt.max_version );
#if defined(MBEDTLS_SSL_FALLBACK_SCSV) #if defined(MBEDTLS_SSL_FALLBACK_SCSV)
if( opt.fallback != DFL_FALLBACK ) if( opt.fallback != DFL_FALLBACK )
@ -1344,14 +1446,16 @@ int main( int argc, char *argv[] )
if( ( ret = mbedtls_ssl_setup( &ssl, &conf ) ) != 0 ) if( ( ret = mbedtls_ssl_setup( &ssl, &conf ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_setup returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_setup returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
#if defined(MBEDTLS_X509_CRT_PARSE_C) #if defined(MBEDTLS_X509_CRT_PARSE_C)
if( ( ret = mbedtls_ssl_set_hostname( &ssl, opt.server_name ) ) != 0 ) if( ( ret = mbedtls_ssl_set_hostname( &ssl, opt.server_name ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n",
ret );
goto exit; goto exit;
} }
#endif #endif
@ -1363,7 +1467,8 @@ int main( int argc, char *argv[] )
(const unsigned char *) opt.ecjpake_pw, (const unsigned char *) opt.ecjpake_pw,
strlen( opt.ecjpake_pw ) ) ) != 0 ) strlen( opt.ecjpake_pw ) ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_set_hs_ecjpake_password returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_set_hs_ecjpake_password returned %d\n\n",
ret );
goto exit; goto exit;
} }
} }
@ -1372,7 +1477,8 @@ int main( int argc, char *argv[] )
if( opt.nbio == 2 ) if( opt.nbio == 2 )
mbedtls_ssl_set_bio( &ssl, &server_fd, my_send, my_recv, NULL ); mbedtls_ssl_set_bio( &ssl, &server_fd, my_send, my_recv, NULL );
else else
mbedtls_ssl_set_bio( &ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_ssl_set_bio( &ssl, &server_fd,
mbedtls_net_send, mbedtls_net_recv,
opt.nbio == 0 ? mbedtls_net_recv_timeout : NULL ); opt.nbio == 0 ? mbedtls_net_recv_timeout : NULL );
#if defined(MBEDTLS_TIMING_C) #if defined(MBEDTLS_TIMING_C)
@ -1390,9 +1496,11 @@ int main( int argc, char *argv[] )
while( ( ret = mbedtls_ssl_handshake( &ssl ) ) != 0 ) while( ( ret = mbedtls_ssl_handshake( &ssl ) ) != 0 )
{ {
if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n",
-ret );
if( ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED ) if( ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED )
mbedtls_printf( mbedtls_printf(
" Unable to verify the server's certificate. " " Unable to verify the server's certificate. "
@ -1404,10 +1512,23 @@ int main( int argc, char *argv[] )
mbedtls_printf( "\n" ); mbedtls_printf( "\n" );
goto exit; goto exit;
} }
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
ret = idle( &server_fd, &timer, ret );
#else
ret = idle( &server_fd, ret );
#endif
if( ret != 0 )
goto exit;
}
} }
mbedtls_printf( " ok\n [ Protocol is %s ]\n [ Ciphersuite is %s ]\n", mbedtls_printf( " ok\n [ Protocol is %s ]\n [ Ciphersuite is %s ]\n",
mbedtls_ssl_get_version( &ssl ), mbedtls_ssl_get_ciphersuite( &ssl ) ); mbedtls_ssl_get_version( &ssl ),
mbedtls_ssl_get_ciphersuite( &ssl ) );
if( ( ret = mbedtls_ssl_get_record_expansion( &ssl ) ) >= 0 ) if( ( ret = mbedtls_ssl_get_record_expansion( &ssl ) ) >= 0 )
mbedtls_printf( " [ Record expansion is %d ]\n", ret ); mbedtls_printf( " [ Record expansion is %d ]\n", ret );
@ -1435,7 +1556,8 @@ int main( int argc, char *argv[] )
if( ( ret = mbedtls_ssl_get_session( &ssl, &saved_session ) ) != 0 ) if( ( ret = mbedtls_ssl_get_session( &ssl, &saved_session ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_get_session returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_get_session returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
@ -1454,7 +1576,8 @@ int main( int argc, char *argv[] )
mbedtls_printf( " failed\n" ); mbedtls_printf( " failed\n" );
mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! ", flags ); mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ),
" ! ", flags );
mbedtls_printf( "%s\n", vrfy_buf ); mbedtls_printf( "%s\n", vrfy_buf );
} }
@ -1484,9 +1607,21 @@ int main( int argc, char *argv[] )
if( ret != MBEDTLS_ERR_SSL_WANT_READ && if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE ) ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_renegotiate returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_renegotiate returned %d\n\n",
ret );
goto exit; goto exit;
} }
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &server_fd, &timer, ret );
#else
idle( &server_fd, ret );
#endif
}
} }
mbedtls_printf( " ok\n" ); mbedtls_printf( " ok\n" );
} }
@ -1530,27 +1665,54 @@ send_request:
{ {
for( written = 0, frags = 0; written < len; written += ret, frags++ ) for( written = 0, frags = 0; written < len; written += ret, frags++ )
{ {
while( ( ret = mbedtls_ssl_write( &ssl, buf + written, len - written ) ) while( ( ret = mbedtls_ssl_write( &ssl, buf + written,
<= 0 ) len - written ) ) <= 0 )
{ {
if( ret != MBEDTLS_ERR_SSL_WANT_READ && if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE ) ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_write returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_write returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &server_fd, &timer, ret );
#else
idle( &server_fd, ret );
#endif
}
} }
} }
} }
else /* Not stream, so datagram */ else /* Not stream, so datagram */
{ {
do ret = mbedtls_ssl_write( &ssl, buf, len ); while( 1 )
while( ret == MBEDTLS_ERR_SSL_WANT_READ || {
ret == MBEDTLS_ERR_SSL_WANT_WRITE ); ret = mbedtls_ssl_write( &ssl, buf, len );
if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
break;
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &server_fd, &timer, ret );
#else
idle( &server_fd, ret );
#endif
}
}
if( ret < 0 ) if( ret < 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_write returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_write returned %d\n\n",
ret );
goto exit; goto exit;
} }
@ -1565,7 +1727,8 @@ send_request:
} }
buf[written] = '\0'; buf[written] = '\0';
mbedtls_printf( " %d bytes written in %d fragments\n\n%s\n", written, frags, (char *) buf ); mbedtls_printf( " %d bytes written in %d fragments\n\n%s\n",
written, frags, (char *) buf );
/* /*
* 7. Read the HTTP response * 7. Read the HTTP response
@ -1586,7 +1749,18 @@ send_request:
if( ret == MBEDTLS_ERR_SSL_WANT_READ || if( ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_WANT_WRITE ) ret == MBEDTLS_ERR_SSL_WANT_WRITE )
{
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &server_fd, &timer, ret );
#else
idle( &server_fd, ret );
#endif
}
continue; continue;
}
if( ret <= 0 ) if( ret <= 0 )
{ {
@ -1604,7 +1778,8 @@ send_request:
goto reconnect; goto reconnect;
default: default:
mbedtls_printf( " mbedtls_ssl_read returned -0x%x\n", -ret ); mbedtls_printf( " mbedtls_ssl_read returned -0x%x\n",
-ret );
goto exit; goto exit;
} }
} }
@ -1628,9 +1803,24 @@ send_request:
len = sizeof( buf ) - 1; len = sizeof( buf ) - 1;
memset( buf, 0, sizeof( buf ) ); memset( buf, 0, sizeof( buf ) );
do ret = mbedtls_ssl_read( &ssl, buf, len ); while( 1 )
while( ret == MBEDTLS_ERR_SSL_WANT_READ || {
ret == MBEDTLS_ERR_SSL_WANT_WRITE ); ret = mbedtls_ssl_read( &ssl, buf, len );
if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
break;
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &server_fd, &timer, ret );
#else
idle( &server_fd, ret );
#endif
}
}
if( ret <= 0 ) if( ret <= 0 )
{ {
@ -1671,7 +1861,8 @@ send_request:
if( ( ret = mbedtls_ssl_session_reset( &ssl ) ) != 0 ) if( ( ret = mbedtls_ssl_session_reset( &ssl ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
@ -1680,9 +1871,20 @@ send_request:
if( ret != MBEDTLS_ERR_SSL_WANT_READ && if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE ) ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &server_fd, &timer, ret );
#else
idle( &server_fd, ret );
#endif
}
} }
mbedtls_printf( " ok\n" ); mbedtls_printf( " ok\n" );
@ -1729,21 +1931,25 @@ reconnect:
if( ( ret = mbedtls_ssl_session_reset( &ssl ) ) != 0 ) if( ( ret = mbedtls_ssl_session_reset( &ssl ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
if( ( ret = mbedtls_ssl_set_session( &ssl, &saved_session ) ) != 0 ) if( ( ret = mbedtls_ssl_set_session( &ssl, &saved_session ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_conf_session returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_conf_session returned %d\n\n",
ret );
goto exit; goto exit;
} }
if( ( ret = mbedtls_net_connect( &server_fd, opt.server_addr, opt.server_port, if( ( ret = mbedtls_net_connect( &server_fd,
opt.transport == MBEDTLS_SSL_TRANSPORT_STREAM ? opt.server_addr, opt.server_port,
MBEDTLS_NET_PROTO_TCP : MBEDTLS_NET_PROTO_UDP ) ) != 0 ) opt.transport == MBEDTLS_SSL_TRANSPORT_STREAM ?
MBEDTLS_NET_PROTO_TCP : MBEDTLS_NET_PROTO_UDP ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_net_connect returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_net_connect returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
@ -1754,7 +1960,7 @@ reconnect:
if( ret != 0 ) if( ret != 0 )
{ {
mbedtls_printf( " failed\n ! net_set_(non)block() returned -0x%x\n\n", mbedtls_printf( " failed\n ! net_set_(non)block() returned -0x%x\n\n",
-ret ); -ret );
goto exit; goto exit;
} }
@ -1763,7 +1969,8 @@ reconnect:
if( ret != MBEDTLS_ERR_SSL_WANT_READ && if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE ) ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{ {
mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
} }

View File

@ -101,6 +101,7 @@ int main( void )
#define DFL_SERVER_PORT "4433" #define DFL_SERVER_PORT "4433"
#define DFL_DEBUG_LEVEL 0 #define DFL_DEBUG_LEVEL 0
#define DFL_NBIO 0 #define DFL_NBIO 0
#define DFL_EVENT 0
#define DFL_READ_TIMEOUT 0 #define DFL_READ_TIMEOUT 0
#define DFL_CA_FILE "" #define DFL_CA_FILE ""
#define DFL_CA_PATH "" #define DFL_CA_PATH ""
@ -331,6 +332,8 @@ int main( void )
" debug_level=%%d default: 0 (disabled)\n" \ " debug_level=%%d default: 0 (disabled)\n" \
" nbio=%%d default: 0 (blocking I/O)\n" \ " nbio=%%d default: 0 (blocking I/O)\n" \
" options: 1 (non-blocking), 2 (added delays)\n" \ " options: 1 (non-blocking), 2 (added delays)\n" \
" event=%%d default: 0 (loop)\n" \
" options: 1 (level-triggered, implies nbio=1),\n" \
" read_timeout=%%d default: 0 ms (no timeout)\n" \ " read_timeout=%%d default: 0 ms (no timeout)\n" \
"\n" \ "\n" \
USAGE_DTLS \ USAGE_DTLS \
@ -399,6 +402,7 @@ struct options
const char *server_port; /* port on which the ssl service runs */ const char *server_port; /* port on which the ssl service runs */
int debug_level; /* level of debugging */ int debug_level; /* level of debugging */
int nbio; /* should I/O be blocking? */ int nbio; /* should I/O be blocking? */
int event; /* loop or event-driven IO? level or edge triggered? */
uint32_t read_timeout; /* timeout on mbedtls_ssl_read() in milliseconds */ uint32_t read_timeout; /* timeout on mbedtls_ssl_read() in milliseconds */
const char *ca_file; /* the file with the CA certificate(s) */ const char *ca_file; /* the file with the CA certificate(s) */
const char *ca_path; /* the path with the CA certificate(s) reside */ const char *ca_path; /* the path with the CA certificate(s) reside */
@ -837,6 +841,56 @@ static int ssl_sig_hashes_for_test[] = {
}; };
#endif /* MBEDTLS_X509_CRT_PARSE_C */ #endif /* MBEDTLS_X509_CRT_PARSE_C */
/*
* Wait for an event from the underlying transport or the timer
* (Used in event-driven IO mode).
*/
#if !defined(MBEDTLS_TIMING_C)
int idle( mbedtls_net_context *fd,
int idle_reason )
#else
int idle( mbedtls_net_context *fd,
mbedtls_timing_delay_context *timer,
int idle_reason )
#endif
{
int ret;
int poll_type = 0;
if( idle_reason == MBEDTLS_ERR_SSL_WANT_WRITE )
poll_type = MBEDTLS_NET_POLL_WRITE;
else if( idle_reason == MBEDTLS_ERR_SSL_WANT_READ )
poll_type = MBEDTLS_NET_POLL_READ;
#if !defined(MBEDTLS_TIMING_C)
else
return( 0 );
#endif
while( 1 )
{
/* Check if timer has expired */
#if defined(MBEDTLS_TIMING_C)
if( timer != NULL &&
mbedtls_timing_get_delay( timer ) == 2 )
{
break;
}
#endif /* MBEDTLS_TIMING_C */
/* Check if underlying transport became available */
if( poll_type != 0 )
{
ret = mbedtls_net_poll( fd, poll_type, 0 );
if( ret < 0 )
return( ret );
if( ret == poll_type )
break;
}
}
return( 0 );
}
int main( int argc, char *argv[] ) int main( int argc, char *argv[] )
{ {
int ret = 0, len, written, frags, exchanges_left; int ret = 0, len, written, frags, exchanges_left;
@ -969,6 +1023,7 @@ int main( int argc, char *argv[] )
opt.server_addr = DFL_SERVER_ADDR; opt.server_addr = DFL_SERVER_ADDR;
opt.server_port = DFL_SERVER_PORT; opt.server_port = DFL_SERVER_PORT;
opt.debug_level = DFL_DEBUG_LEVEL; opt.debug_level = DFL_DEBUG_LEVEL;
opt.event = DFL_EVENT;
opt.nbio = DFL_NBIO; opt.nbio = DFL_NBIO;
opt.read_timeout = DFL_READ_TIMEOUT; opt.read_timeout = DFL_READ_TIMEOUT;
opt.ca_file = DFL_CA_FILE; opt.ca_file = DFL_CA_FILE;
@ -1047,6 +1102,12 @@ int main( int argc, char *argv[] )
if( opt.nbio < 0 || opt.nbio > 2 ) if( opt.nbio < 0 || opt.nbio > 2 )
goto usage; goto usage;
} }
else if( strcmp( p, "event" ) == 0 )
{
opt.event = atoi( q );
if( opt.event < 0 || opt.event > 2 )
goto usage;
}
else if( strcmp( p, "read_timeout" ) == 0 ) else if( strcmp( p, "read_timeout" ) == 0 )
opt.read_timeout = atoi( q ); opt.read_timeout = atoi( q );
else if( strcmp( p, "ca_file" ) == 0 ) else if( strcmp( p, "ca_file" ) == 0 )
@ -1088,16 +1149,23 @@ int main( int argc, char *argv[] )
opt.version_suites = q; opt.version_suites = q;
else if( strcmp( p, "renegotiation" ) == 0 ) else if( strcmp( p, "renegotiation" ) == 0 )
{ {
opt.renegotiation = (atoi( q )) ? MBEDTLS_SSL_RENEGOTIATION_ENABLED : opt.renegotiation = (atoi( q )) ?
MBEDTLS_SSL_RENEGOTIATION_DISABLED; MBEDTLS_SSL_RENEGOTIATION_ENABLED :
MBEDTLS_SSL_RENEGOTIATION_DISABLED;
} }
else if( strcmp( p, "allow_legacy" ) == 0 ) else if( strcmp( p, "allow_legacy" ) == 0 )
{ {
switch( atoi( q ) ) switch( atoi( q ) )
{ {
case -1: opt.allow_legacy = MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE; break; case -1:
case 0: opt.allow_legacy = MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION; break; opt.allow_legacy = MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE;
case 1: opt.allow_legacy = MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION; break; break;
case 0:
opt.allow_legacy = MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION;
break;
case 1:
opt.allow_legacy = MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION;
break;
default: goto usage; default: goto usage;
} }
} }
@ -1254,8 +1322,12 @@ int main( int argc, char *argv[] )
{ {
switch( atoi( q ) ) switch( atoi( q ) )
{ {
case 0: opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_DISABLED; break; case 0:
case 1: opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED; break; opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_DISABLED;
break;
case 1:
opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED;
break;
default: goto usage; default: goto usage;
} }
} }
@ -1328,6 +1400,15 @@ int main( int argc, char *argv[] )
goto usage; goto usage;
} }
/* Event-driven IO is incompatible with the above custom
* receive and send functions, as the polling builds on
* refers to the underlying net_context. */
if( opt.event == 1 && opt.nbio != 1 )
{
mbedtls_printf( "Warning: event-driven IO mandates nbio=1 - overwrite\n" );
opt.nbio = 1;
}
#if defined(MBEDTLS_DEBUG_C) #if defined(MBEDTLS_DEBUG_C)
mbedtls_debug_set_threshold( opt.debug_level ); mbedtls_debug_set_threshold( opt.debug_level );
#endif #endif
@ -1335,19 +1416,20 @@ int main( int argc, char *argv[] )
if( opt.force_ciphersuite[0] > 0 ) if( opt.force_ciphersuite[0] > 0 )
{ {
const mbedtls_ssl_ciphersuite_t *ciphersuite_info; const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
ciphersuite_info = mbedtls_ssl_ciphersuite_from_id( opt.force_ciphersuite[0] ); ciphersuite_info =
mbedtls_ssl_ciphersuite_from_id( opt.force_ciphersuite[0] );
if( opt.max_version != -1 && if( opt.max_version != -1 &&
ciphersuite_info->min_minor_ver > opt.max_version ) ciphersuite_info->min_minor_ver > opt.max_version )
{ {
mbedtls_printf("forced ciphersuite not allowed with this protocol version\n"); mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
ret = 2; ret = 2;
goto usage; goto usage;
} }
if( opt.min_version != -1 && if( opt.min_version != -1 &&
ciphersuite_info->max_minor_ver < opt.min_version ) ciphersuite_info->max_minor_ver < opt.min_version )
{ {
mbedtls_printf("forced ciphersuite not allowed with this protocol version\n"); mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
ret = 2; ret = 2;
goto usage; goto usage;
} }
@ -1526,11 +1608,12 @@ int main( int argc, char *argv[] )
fflush( stdout ); fflush( stdout );
mbedtls_entropy_init( &entropy ); mbedtls_entropy_init( &entropy );
if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func,
(const unsigned char *) pers, &entropy, (const unsigned char *) pers,
strlen( pers ) ) ) != 0 ) strlen( pers ) ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n",
-ret );
goto exit; goto exit;
} }
@ -1627,7 +1710,7 @@ int main( int argc, char *argv[] )
if( ( ret = mbedtls_pk_parse_keyfile( &pkey2, opt.key_file2, "" ) ) != 0 ) if( ( ret = mbedtls_pk_parse_keyfile( &pkey2, opt.key_file2, "" ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_pk_parse_keyfile(2) returned -0x%x\n\n", mbedtls_printf( " failed\n ! mbedtls_pk_parse_keyfile(2) returned -0x%x\n\n",
-ret ); -ret );
goto exit; goto exit;
} }
} }
@ -1645,8 +1728,7 @@ int main( int argc, char *argv[] )
strcmp( opt.key_file2, "none" ) != 0 ) strcmp( opt.key_file2, "none" ) != 0 )
{ {
#if !defined(MBEDTLS_CERTS_C) #if !defined(MBEDTLS_CERTS_C)
mbedtls_printf( "Not certificated or key provided, and \n" mbedtls_printf( "Not certificated or key provided, and \nMBEDTLS_CERTS_C not defined!\n" );
"MBEDTLS_CERTS_C not defined!\n" );
goto exit; goto exit;
#else #else
#if defined(MBEDTLS_RSA_C) #if defined(MBEDTLS_RSA_C)
@ -1654,14 +1736,16 @@ int main( int argc, char *argv[] )
(const unsigned char *) mbedtls_test_srv_crt_rsa, (const unsigned char *) mbedtls_test_srv_crt_rsa,
mbedtls_test_srv_crt_rsa_len ) ) != 0 ) mbedtls_test_srv_crt_rsa_len ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
if( ( ret = mbedtls_pk_parse_key( &pkey, if( ( ret = mbedtls_pk_parse_key( &pkey,
(const unsigned char *) mbedtls_test_srv_key_rsa, (const unsigned char *) mbedtls_test_srv_key_rsa,
mbedtls_test_srv_key_rsa_len, NULL, 0 ) ) != 0 ) mbedtls_test_srv_key_rsa_len, NULL, 0 ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! mbedtls_pk_parse_key returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! mbedtls_pk_parse_key returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
key_cert_init = 2; key_cert_init = 2;
@ -1671,14 +1755,16 @@ int main( int argc, char *argv[] )
(const unsigned char *) mbedtls_test_srv_crt_ec, (const unsigned char *) mbedtls_test_srv_crt_ec,
mbedtls_test_srv_crt_ec_len ) ) != 0 ) mbedtls_test_srv_crt_ec_len ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! x509_crt_parse2 returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! x509_crt_parse2 returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
if( ( ret = mbedtls_pk_parse_key( &pkey2, if( ( ret = mbedtls_pk_parse_key( &pkey2,
(const unsigned char *) mbedtls_test_srv_key_ec, (const unsigned char *) mbedtls_test_srv_key_ec,
mbedtls_test_srv_key_ec_len, NULL, 0 ) ) != 0 ) mbedtls_test_srv_key_ec_len, NULL, 0 ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! pk_parse_key2 returned -0x%x\n\n", -ret ); mbedtls_printf( " failed\n ! pk_parse_key2 returned -0x%x\n\n",
-ret );
goto exit; goto exit;
} }
key_cert_init2 = 2; key_cert_init2 = 2;
@ -2088,8 +2174,8 @@ reset:
if( ( ret = mbedtls_ssl_set_client_transport_id( &ssl, if( ( ret = mbedtls_ssl_set_client_transport_id( &ssl,
client_ip, cliip_len ) ) != 0 ) client_ip, cliip_len ) ) != 0 )
{ {
mbedtls_printf( " failed\n ! " mbedtls_printf( " failed\n ! mbedtls_ssl_set_client_transport_id() returned -0x%x\n\n",
"mbedtls_ssl_set_client_transport_id() returned -0x%x\n\n", -ret ); -ret );
goto exit; goto exit;
} }
} }
@ -2117,9 +2203,24 @@ handshake:
mbedtls_printf( " . Performing the SSL/TLS handshake..." ); mbedtls_printf( " . Performing the SSL/TLS handshake..." );
fflush( stdout ); fflush( stdout );
do ret = mbedtls_ssl_handshake( &ssl ); while( ( ret = mbedtls_ssl_handshake( &ssl ) ) != 0 )
while( ret == MBEDTLS_ERR_SSL_WANT_READ || {
ret == MBEDTLS_ERR_SSL_WANT_WRITE ); if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
break;
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
ret = idle( &client_fd, &timer, ret );
#else
ret = idle( &client_fd, ret );
#endif
if( ret != 0 )
goto reset;
}
}
if( ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED ) if( ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED )
{ {
@ -2225,7 +2326,18 @@ data_exchange:
if( ret == MBEDTLS_ERR_SSL_WANT_READ || if( ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_WANT_WRITE ) ret == MBEDTLS_ERR_SSL_WANT_WRITE )
{
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &client_fd, &timer, ret );
#else
idle( &client_fd, ret );
#endif
}
continue; continue;
}
if( ret <= 0 ) if( ret <= 0 )
{ {
@ -2313,9 +2425,40 @@ data_exchange:
len = sizeof( buf ) - 1; len = sizeof( buf ) - 1;
memset( buf, 0, sizeof( buf ) ); memset( buf, 0, sizeof( buf ) );
do ret = mbedtls_ssl_read( &ssl, buf, len ); while( 1 )
while( ret == MBEDTLS_ERR_SSL_WANT_READ || {
ret == MBEDTLS_ERR_SSL_WANT_WRITE ); /* Without the call to `mbedtls_ssl_check_pending`, it might
* happen that the client sends application data in the same
* datagram as the Finished message concluding the handshake.
* In this case, the application data would be ready to be
* processed while the underlying transport wouldn't signal
* any further incoming data.
*
* See the test 'Event-driven I/O: session-id resume, UDP packing'
* in tests/ssl-opt.sh.
*/
/* For event-driven IO, wait for socket to become available */
if( mbedtls_ssl_check_pending( &ssl ) == 0 &&
opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &client_fd, &timer, MBEDTLS_ERR_SSL_WANT_READ );
#else
idle( &client_fd, MBEDTLS_ERR_SSL_WANT_READ );
#endif
}
ret = mbedtls_ssl_read( &ssl, buf, len );
/* Note that even if `mbedtls_ssl_check_pending` returns true,
* it can happen that the subsequent call to `mbedtls_ssl_read`
* returns `MBEDTLS_ERR_SSL_WANT_READ`, because the pending messages
* might be discarded (e.g. because they are retransmissions). */
if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
break;
}
if( ret <= 0 ) if( ret <= 0 )
{ {
@ -2356,6 +2499,16 @@ data_exchange:
mbedtls_printf( " failed\n ! mbedtls_ssl_renegotiate returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_renegotiate returned %d\n\n", ret );
goto reset; goto reset;
} }
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &client_fd, &timer, ret );
#else
idle( &client_fd, ret );
#endif
}
} }
mbedtls_printf( " ok\n" ); mbedtls_printf( " ok\n" );
@ -2390,14 +2543,39 @@ data_exchange:
mbedtls_printf( " failed\n ! mbedtls_ssl_write returned %d\n\n", ret ); mbedtls_printf( " failed\n ! mbedtls_ssl_write returned %d\n\n", ret );
goto reset; goto reset;
} }
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &client_fd, &timer, ret );
#else
idle( &client_fd, ret );
#endif
}
} }
} }
} }
else /* Not stream, so datagram */ else /* Not stream, so datagram */
{ {
do ret = mbedtls_ssl_write( &ssl, buf, len ); while( 1 )
while( ret == MBEDTLS_ERR_SSL_WANT_READ || {
ret == MBEDTLS_ERR_SSL_WANT_WRITE ); ret = mbedtls_ssl_write( &ssl, buf, len );
if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
break;
/* For event-driven IO, wait for socket to become available */
if( opt.event == 1 /* level triggered IO */ )
{
#if defined(MBEDTLS_TIMING_C)
idle( &client_fd, &timer, ret );
#else
idle( &client_fd, ret );
#endif
}
}
if( ret < 0 ) if( ret < 0 )
{ {

View File

@ -53,6 +53,7 @@ int main( void )
#include "mbedtls/net_sockets.h" #include "mbedtls/net_sockets.h"
#include "mbedtls/error.h" #include "mbedtls/error.h"
#include "mbedtls/ssl.h" #include "mbedtls/ssl.h"
#include "mbedtls/timing.h"
#include <string.h> #include <string.h>
@ -74,17 +75,21 @@ int main( void )
#include <unistd.h> #include <unistd.h>
#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */ #endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */
/* For gettimeofday() */
#if !defined(_WIN32)
#include <sys/time.h>
#endif
#define MAX_MSG_SIZE 16384 + 2048 /* max record/datagram size */ #define MAX_MSG_SIZE 16384 + 2048 /* max record/datagram size */
#define DFL_SERVER_ADDR "localhost" #define DFL_SERVER_ADDR "localhost"
#define DFL_SERVER_PORT "4433" #define DFL_SERVER_PORT "4433"
#define DFL_LISTEN_ADDR "localhost" #define DFL_LISTEN_ADDR "localhost"
#define DFL_LISTEN_PORT "5556" #define DFL_LISTEN_PORT "5556"
#define DFL_PACK 0
#if defined(MBEDTLS_TIMING_C)
#define USAGE_PACK \
" pack=%%d default: 0 (don't pack)\n" \
" options: t > 0 (pack for t milliseconds)\n"
#else
#define USAGE_PACK
#endif
#define USAGE \ #define USAGE \
"\n usage: udp_proxy param=<>...\n" \ "\n usage: udp_proxy param=<>...\n" \
@ -105,9 +110,10 @@ int main( void )
" drop packets larger than N bytes\n" \ " drop packets larger than N bytes\n" \
" bad_ad=0/1 default: 0 (don't add bad ApplicationData)\n" \ " bad_ad=0/1 default: 0 (don't add bad ApplicationData)\n" \
" protect_hvr=0/1 default: 0 (don't protect HelloVerifyRequest)\n" \ " protect_hvr=0/1 default: 0 (don't protect HelloVerifyRequest)\n" \
" protect_len=%%d default: (don't protect packets of this size)\n" \ " protect_len=%%d default: (don't protect packets of this size)\n" \
"\n" \ "\n" \
" seed=%%d default: (use current time)\n" \ " seed=%%d default: (use current time)\n" \
USAGE_PACK \
"\n" "\n"
/* /*
@ -128,7 +134,8 @@ static struct options
int bad_ad; /* inject corrupted ApplicationData record */ int bad_ad; /* inject corrupted ApplicationData record */
int protect_hvr; /* never drop or delay HelloVerifyRequest */ int protect_hvr; /* never drop or delay HelloVerifyRequest */
int protect_len; /* never drop/delay packet of the given size*/ int protect_len; /* never drop/delay packet of the given size*/
unsigned pack; /* merge packets into single datagram for
* at most \c merge milliseconds if > 0 */
unsigned int seed; /* seed for "random" events */ unsigned int seed; /* seed for "random" events */
} opt; } opt;
@ -152,6 +159,7 @@ static void get_options( int argc, char *argv[] )
opt.server_port = DFL_SERVER_PORT; opt.server_port = DFL_SERVER_PORT;
opt.listen_addr = DFL_LISTEN_ADDR; opt.listen_addr = DFL_LISTEN_ADDR;
opt.listen_port = DFL_LISTEN_PORT; opt.listen_port = DFL_LISTEN_PORT;
opt.pack = DFL_PACK;
/* Other members default to 0 */ /* Other members default to 0 */
for( i = 1; i < argc; i++ ) for( i = 1; i < argc; i++ )
@ -193,6 +201,15 @@ static void get_options( int argc, char *argv[] )
if( opt.drop < 0 || opt.drop > 20 || opt.drop == 1 ) if( opt.drop < 0 || opt.drop > 20 || opt.drop == 1 )
exit_usage( p, q ); exit_usage( p, q );
} }
else if( strcmp( p, "pack" ) == 0 )
{
#if defined(MBEDTLS_TIMING_C)
opt.pack = (unsigned) atoi( q );
#else
mbedtls_printf( " option pack only defined if MBEDTLS_TIMING_C is enabled\n" );
exit( 1 );
#endif
}
else if( strcmp( p, "mtu" ) == 0 ) else if( strcmp( p, "mtu" ) == 0 )
{ {
opt.mtu = atoi( q ); opt.mtu = atoi( q );
@ -267,25 +284,122 @@ static const char *msg_type( unsigned char *msg, size_t len )
} }
} }
#if defined(MBEDTLS_TIMING_C)
/* Return elapsed time in milliseconds since the first call */ /* Return elapsed time in milliseconds since the first call */
static unsigned long ellapsed_time( void ) static unsigned ellapsed_time( void )
{ {
#if defined(_WIN32) static int initialized = 0;
return( 0 ); static struct mbedtls_timing_hr_time hires;
#else
static struct timeval ref = { 0, 0 };
struct timeval now;
if( ref.tv_sec == 0 && ref.tv_usec == 0 ) if( initialized == 0 )
{ {
gettimeofday( &ref, NULL ); (void) mbedtls_timing_get_timer( &hires, 1 );
initialized = 1;
return( 0 ); return( 0 );
} }
gettimeofday( &now, NULL ); return( mbedtls_timing_get_timer( &hires, 0 ) );
return( 1000 * ( now.tv_sec - ref.tv_sec ) }
+ ( now.tv_usec - ref.tv_usec ) / 1000 );
#endif typedef struct
{
mbedtls_net_context *ctx;
const char *description;
unsigned packet_lifetime;
unsigned num_datagrams;
unsigned char data[MAX_MSG_SIZE];
size_t len;
} ctx_buffer;
static ctx_buffer outbuf[2];
static int ctx_buffer_flush( ctx_buffer *buf )
{
int ret;
mbedtls_printf( " %05u flush %s: %u bytes, %u datagrams, last %u ms\n",
ellapsed_time(), buf->description,
(unsigned) buf->len, buf->num_datagrams,
ellapsed_time() - buf->packet_lifetime );
ret = mbedtls_net_send( buf->ctx, buf->data, buf->len );
buf->len = 0;
buf->num_datagrams = 0;
return( ret );
}
static unsigned ctx_buffer_time_remaining( ctx_buffer *buf )
{
unsigned const cur_time = ellapsed_time();
if( buf->num_datagrams == 0 )
return( (unsigned) -1 );
if( cur_time - buf->packet_lifetime >= opt.pack )
return( 0 );
return( opt.pack - ( cur_time - buf->packet_lifetime ) );
}
static int ctx_buffer_append( ctx_buffer *buf,
const unsigned char * data,
size_t len )
{
int ret;
if( len > (size_t) INT_MAX )
return( -1 );
if( len > sizeof( buf->data ) )
{
mbedtls_printf( " ! buffer size %u too large (max %u)\n",
(unsigned) len, (unsigned) sizeof( buf->data ) );
return( -1 );
}
if( sizeof( buf->data ) - buf->len < len )
{
if( ( ret = ctx_buffer_flush( buf ) ) <= 0 )
return( ret );
}
memcpy( buf->data + buf->len, data, len );
buf->len += len;
if( ++buf->num_datagrams == 1 )
buf->packet_lifetime = ellapsed_time();
return( (int) len );
}
#endif /* MBEDTLS_TIMING_C */
static int dispatch_data( mbedtls_net_context *ctx,
const unsigned char * data,
size_t len )
{
#if defined(MBEDTLS_TIMING_C)
ctx_buffer *buf = NULL;
if( opt.pack > 0 )
{
if( outbuf[0].ctx == ctx )
buf = &outbuf[0];
else if( outbuf[1].ctx == ctx )
buf = &outbuf[1];
if( buf == NULL )
return( -1 );
return( ctx_buffer_append( buf, data, len ) );
}
#endif /* MBEDTLS_TIMING_C */
return( mbedtls_net_send( ctx, data, len ) );
} }
typedef struct typedef struct
@ -300,12 +414,22 @@ typedef struct
/* Print packet. Outgoing packets come with a reason (forward, dupl, etc.) */ /* Print packet. Outgoing packets come with a reason (forward, dupl, etc.) */
void print_packet( const packet *p, const char *why ) void print_packet( const packet *p, const char *why )
{ {
#if defined(MBEDTLS_TIMING_C)
if( why == NULL ) if( why == NULL )
mbedtls_printf( " %05lu %s %s (%u bytes)\n", mbedtls_printf( " %05u dispatch %s %s (%u bytes)\n",
ellapsed_time(), p->way, p->type, p->len ); ellapsed_time(), p->way, p->type, p->len );
else else
mbedtls_printf( " %s %s (%u bytes): %s\n", mbedtls_printf( " %05u dispatch %s %s (%u bytes): %s\n",
ellapsed_time(), p->way, p->type, p->len, why );
#else
if( why == NULL )
mbedtls_printf( " dispatch %s %s (%u bytes)\n",
p->way, p->type, p->len );
else
mbedtls_printf( " dispatch %s %s (%u bytes): %s\n",
p->way, p->type, p->len, why ); p->way, p->type, p->len, why );
#endif
fflush( stdout ); fflush( stdout );
} }
@ -320,20 +444,28 @@ int send_packet( const packet *p, const char *why )
{ {
unsigned char buf[MAX_MSG_SIZE]; unsigned char buf[MAX_MSG_SIZE];
memcpy( buf, p->buf, p->len ); memcpy( buf, p->buf, p->len );
++buf[p->len - 1];
print_packet( p, "corrupted" ); if( p->len <= 13 )
if( ( ret = mbedtls_net_send( dst, buf, p->len ) ) <= 0 )
{ {
mbedtls_printf( " ! mbedtls_net_send returned %d\n", ret ); mbedtls_printf( " ! can't corrupt empty AD record" );
}
else
{
++buf[13];
print_packet( p, "corrupted" );
}
if( ( ret = dispatch_data( dst, buf, p->len ) ) <= 0 )
{
mbedtls_printf( " ! dispatch returned %d\n", ret );
return( ret ); return( ret );
} }
} }
print_packet( p, why ); print_packet( p, why );
if( ( ret = mbedtls_net_send( dst, p->buf, p->len ) ) <= 0 ) if( ( ret = dispatch_data( dst, p->buf, p->len ) ) <= 0 )
{ {
mbedtls_printf( " ! mbedtls_net_send returned %d\n", ret ); mbedtls_printf( " ! dispatch returned %d\n", ret );
return( ret ); return( ret );
} }
@ -344,9 +476,9 @@ int send_packet( const packet *p, const char *why )
{ {
print_packet( p, "duplicated" ); print_packet( p, "duplicated" );
if( ( ret = mbedtls_net_send( dst, p->buf, p->len ) ) <= 0 ) if( ( ret = dispatch_data( dst, p->buf, p->len ) ) <= 0 )
{ {
mbedtls_printf( " ! mbedtls_net_send returned %d\n", ret ); mbedtls_printf( " ! dispatch returned %d\n", ret );
return( ret ); return( ret );
} }
} }
@ -472,6 +604,12 @@ int main( int argc, char *argv[] )
mbedtls_net_context listen_fd, client_fd, server_fd; mbedtls_net_context listen_fd, client_fd, server_fd;
#if defined( MBEDTLS_TIMING_C )
struct timeval tm;
#endif
struct timeval *tm_ptr = NULL;
int nb_fds; int nb_fds;
fd_set read_fds; fd_set read_fds;
@ -560,14 +698,65 @@ accept:
nb_fds = listen_fd.fd; nb_fds = listen_fd.fd;
++nb_fds; ++nb_fds;
#if defined(MBEDTLS_TIMING_C)
if( opt.pack > 0 )
{
outbuf[0].ctx = &server_fd;
outbuf[0].description = "S <- C";
outbuf[0].num_datagrams = 0;
outbuf[0].len = 0;
outbuf[1].ctx = &client_fd;
outbuf[1].description = "S -> C";
outbuf[1].num_datagrams = 0;
outbuf[1].len = 0;
}
#endif /* MBEDTLS_TIMING_C */
while( 1 ) while( 1 )
{ {
#if defined(MBEDTLS_TIMING_C)
if( opt.pack > 0 )
{
unsigned max_wait_server, max_wait_client, max_wait;
max_wait_server = ctx_buffer_time_remaining( &outbuf[0] );
max_wait_client = ctx_buffer_time_remaining( &outbuf[1] );
max_wait = (unsigned) -1;
if( max_wait_server == 0 )
ctx_buffer_flush( &outbuf[0] );
else
max_wait = max_wait_server;
if( max_wait_client == 0 )
ctx_buffer_flush( &outbuf[1] );
else
{
if( max_wait_client < max_wait )
max_wait = max_wait_client;
}
if( max_wait != (unsigned) -1 )
{
tm.tv_sec = max_wait / 1000;
tm.tv_usec = ( max_wait % 1000 ) * 1000;
tm_ptr = &tm;
}
else
{
tm_ptr = NULL;
}
}
#endif /* MBEDTLS_TIMING_C */
FD_ZERO( &read_fds ); FD_ZERO( &read_fds );
FD_SET( server_fd.fd, &read_fds ); FD_SET( server_fd.fd, &read_fds );
FD_SET( client_fd.fd, &read_fds ); FD_SET( client_fd.fd, &read_fds );
FD_SET( listen_fd.fd, &read_fds ); FD_SET( listen_fd.fd, &read_fds );
if( ( ret = select( nb_fds, &read_fds, NULL, NULL, NULL ) ) <= 0 ) if( ( ret = select( nb_fds, &read_fds, NULL, NULL, tm_ptr ) ) < 0 )
{ {
perror( "select" ); perror( "select" );
goto exit; goto exit;
@ -589,6 +778,7 @@ accept:
&client_fd, &server_fd ) ) != 0 ) &client_fd, &server_fd ) ) != 0 )
goto accept; goto accept;
} }
} }
exit: exit:

View File

@ -0,0 +1,117 @@
#!/bin/sh
# -*-sh-basic-offset: 4-*-
# Usage: udp_proxy_wrapper.sh [PROXY_PARAM...] -- [SERVER_PARAM...]
set -u
MBEDTLS_BASE="$(dirname -- "$0")/../.."
TPXY_BIN="$MBEDTLS_BASE/programs/test/udp_proxy"
SRV_BIN="$MBEDTLS_BASE/programs/ssl/ssl_server2"
: ${VERBOSE:=0}
stop_proxy() {
if [ -n "${tpxy_pid:-}" ]; then
echo
echo " * Killing proxy (pid $tpxy_pid) ..."
kill $tpxy_pid
fi
}
stop_server() {
if [ -n "${srv_pid:-}" ]; then
echo
echo " * Killing server (pid $srv_pid) ..."
kill $srv_pid >/dev/null 2>/dev/null
fi
}
cleanup() {
stop_server
stop_proxy
exit 129
}
trap cleanup INT TERM HUP
# Extract the proxy parameters
tpxy_cmd_snippet='"$TPXY_BIN"'
while [ $# -ne 0 ] && [ "$1" != "--" ]; do
tail="$1" quoted=""
while [ -n "$tail" ]; do
case "$tail" in
*\'*) quoted="${quoted}${tail%%\'*}'\\''" tail="${tail#*\'}";;
*) quoted="${quoted}${tail}"; tail=; false;;
esac
done
tpxy_cmd_snippet="$tpxy_cmd_snippet '$quoted'"
shift
done
unset tail quoted
if [ $# -eq 0 ]; then
echo " * No server arguments (must be preceded by \" -- \") - exit"
exit 3
fi
shift
dtls_enabled=
ipv6_in_use=
server_port_orig=
server_addr_orig=
for param; do
case "$param" in
server_port=*) server_port_orig="${param#*=}";;
server_addr=*:*) server_addr_orig="${param#*=}"; ipv6_in_use=1;;
server_addr=*) server_addr_orig="${param#*=}";;
dtls=[!0]*) dtls_enabled=1;;
esac
done
if [ -z "$dtls_enabled" ] || [ -n "$ipv6_in_use" ]; then
echo >&2 "$0: Couldn't find DTLS enabling, or IPv6 is in use - immediate fallback to server application..."
if [ $VERBOSE -gt 0 ]; then
echo "[ $SRV_BIN $* ]"
fi
exec "$SRV_BIN" "$@"
fi
if [ -z "$server_port_orig" ]; then
server_port_orig=4433
fi
echo " * Server port: $server_port_orig"
tpxy_cmd_snippet="$tpxy_cmd_snippet \"listen_port=\$server_port_orig\""
tpxy_cmd_snippet="$tpxy_cmd_snippet \"server_port=\$server_port\""
if [ -n "$server_addr_orig" ]; then
echo " * Server address: $server_addr_orig"
tpxy_cmd_snippet="$tpxy_cmd_snippet \"server_addr=\$server_addr_orig\""
tpxy_cmd_snippet="$tpxy_cmd_snippet \"listen_addr=\$server_addr_orig\""
fi
server_port=$(( server_port_orig + 1 ))
set -- "$@" "server_port=$server_port"
echo " * Intermediate port: $server_port"
echo " * Start proxy in background ..."
if [ $VERBOSE -gt 0 ]; then
echo "[ $tpxy_cmd_snippet ]"
fi
eval exec "$tpxy_cmd_snippet" >/dev/null 2>&1 &
tpxy_pid=$!
if [ $VERBOSE -gt 0 ]; then
echo " * Proxy ID: $TPXY_PID"
fi
echo " * Starting server ..."
if [ $VERBOSE -gt 0 ]; then
echo "[ $SRV_BIN $* ]"
fi
exec "$SRV_BIN" "$@" >&2 &
srv_pid=$!
wait $srv_pid
stop_proxy
return 0

View File

@ -465,9 +465,12 @@ run_test() {
eval "$CLI_CMD" >> $CLI_OUT 2>&1 & eval "$CLI_CMD" >> $CLI_OUT 2>&1 &
wait_client_done wait_client_done
sleep 0.05
# terminate the server (and the proxy) # terminate the server (and the proxy)
kill $SRV_PID kill $SRV_PID
wait $SRV_PID wait $SRV_PID
if [ -n "$PXY_CMD" ]; then if [ -n "$PXY_CMD" ]; then
kill $PXY_PID >/dev/null 2>&1 kill $PXY_PID >/dev/null 2>&1
wait $PXY_PID wait $PXY_PID
@ -631,16 +634,19 @@ fi
get_options "$@" get_options "$@"
# sanity checks, avoid an avalanche of errors # sanity checks, avoid an avalanche of errors
if [ ! -x "$P_SRV" ]; then P_SRV_BIN="${P_SRV%%[ ]*}"
echo "Command '$P_SRV' is not an executable file" P_CLI_BIN="${P_CLI%%[ ]*}"
P_PXY_BIN="${P_PXY%%[ ]*}"
if [ ! -x "$P_SRV_BIN" ]; then
echo "Command '$P_SRV_BIN' is not an executable file"
exit 1 exit 1
fi fi
if [ ! -x "$P_CLI" ]; then if [ ! -x "$P_CLI_BIN" ]; then
echo "Command '$P_CLI' is not an executable file" echo "Command '$P_CLI_BIN' is not an executable file"
exit 1 exit 1
fi fi
if [ ! -x "$P_PXY" ]; then if [ ! -x "$P_PXY_BIN" ]; then
echo "Command '$P_PXY' is not an executable file" echo "Command '$P_PXY_BIN' is not an executable file"
exit 1 exit 1
fi fi
if [ "$MEMCHECK" -gt 0 ]; then if [ "$MEMCHECK" -gt 0 ]; then
@ -2704,6 +2710,118 @@ run_test "Non-blocking I/O: session-id resume" \
-C "mbedtls_ssl_handshake returned" \ -C "mbedtls_ssl_handshake returned" \
-c "Read from server: .* bytes read" -c "Read from server: .* bytes read"
# Tests for event-driven I/O: exercise a variety of handshake flows
run_test "Event-driven I/O: basic handshake" \
"$P_SRV event=1 tickets=0 auth_mode=none" \
"$P_CLI event=1 tickets=0" \
0 \
-S "mbedtls_ssl_handshake returned" \
-C "mbedtls_ssl_handshake returned" \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O: client auth" \
"$P_SRV event=1 tickets=0 auth_mode=required" \
"$P_CLI event=1 tickets=0" \
0 \
-S "mbedtls_ssl_handshake returned" \
-C "mbedtls_ssl_handshake returned" \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O: ticket" \
"$P_SRV event=1 tickets=1 auth_mode=none" \
"$P_CLI event=1 tickets=1" \
0 \
-S "mbedtls_ssl_handshake returned" \
-C "mbedtls_ssl_handshake returned" \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O: ticket + client auth" \
"$P_SRV event=1 tickets=1 auth_mode=required" \
"$P_CLI event=1 tickets=1" \
0 \
-S "mbedtls_ssl_handshake returned" \
-C "mbedtls_ssl_handshake returned" \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O: ticket + client auth + resume" \
"$P_SRV event=1 tickets=1 auth_mode=required" \
"$P_CLI event=1 tickets=1 reconnect=1" \
0 \
-S "mbedtls_ssl_handshake returned" \
-C "mbedtls_ssl_handshake returned" \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O: ticket + resume" \
"$P_SRV event=1 tickets=1 auth_mode=none" \
"$P_CLI event=1 tickets=1 reconnect=1" \
0 \
-S "mbedtls_ssl_handshake returned" \
-C "mbedtls_ssl_handshake returned" \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O: session-id resume" \
"$P_SRV event=1 tickets=0 auth_mode=none" \
"$P_CLI event=1 tickets=0 reconnect=1" \
0 \
-S "mbedtls_ssl_handshake returned" \
-C "mbedtls_ssl_handshake returned" \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O, DTLS: basic handshake" \
"$P_SRV dtls=1 event=1 tickets=0 auth_mode=none" \
"$P_CLI dtls=1 event=1 tickets=0" \
0 \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O, DTLS: client auth" \
"$P_SRV dtls=1 event=1 tickets=0 auth_mode=required" \
"$P_CLI dtls=1 event=1 tickets=0" \
0 \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O, DTLS: ticket" \
"$P_SRV dtls=1 event=1 tickets=1 auth_mode=none" \
"$P_CLI dtls=1 event=1 tickets=1" \
0 \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O, DTLS: ticket + client auth" \
"$P_SRV dtls=1 event=1 tickets=1 auth_mode=required" \
"$P_CLI dtls=1 event=1 tickets=1" \
0 \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O, DTLS: ticket + client auth + resume" \
"$P_SRV dtls=1 event=1 tickets=1 auth_mode=required" \
"$P_CLI dtls=1 event=1 tickets=1 reconnect=1" \
0 \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O, DTLS: ticket + resume" \
"$P_SRV dtls=1 event=1 tickets=1 auth_mode=none" \
"$P_CLI dtls=1 event=1 tickets=1 reconnect=1" \
0 \
-c "Read from server: .* bytes read"
run_test "Event-driven I/O, DTLS: session-id resume" \
"$P_SRV dtls=1 event=1 tickets=0 auth_mode=none" \
"$P_CLI dtls=1 event=1 tickets=0 reconnect=1" \
0 \
-c "Read from server: .* bytes read"
# This test demonstrates the need for the mbedtls_ssl_check_pending function.
# During session resumption, the client will send its ApplicationData record
# within the same datagram as the Finished messages. In this situation, the
# server MUST NOT idle on the underlying transport after handshake completion,
# because the ApplicationData request has already been queued internally.
run_test "Event-driven I/O, DTLS: session-id resume, UDP packing" \
-p "$P_PXY pack=50" \
"$P_SRV dtls=1 event=1 tickets=0 auth_mode=required" \
"$P_CLI dtls=1 event=1 tickets=0 reconnect=1" \
0 \
-c "Read from server: .* bytes read"
# Tests for version negotiation # Tests for version negotiation
run_test "Version check: all -> 1.2" \ run_test "Version check: all -> 1.2" \
@ -4195,8 +4313,8 @@ run_test "DTLS proxy: duplicate every packet" \
0 \ 0 \
-c "replayed record" \ -c "replayed record" \
-s "replayed record" \ -s "replayed record" \
-c "discarding invalid record" \ -c "record from another epoch" \
-s "discarding invalid record" \ -s "record from another epoch" \
-S "resend" \ -S "resend" \
-s "Extra-header:" \ -s "Extra-header:" \
-c "HTTP/1.0 200 OK" -c "HTTP/1.0 200 OK"
@ -4208,13 +4326,29 @@ run_test "DTLS proxy: duplicate every packet, server anti-replay off" \
0 \ 0 \
-c "replayed record" \ -c "replayed record" \
-S "replayed record" \ -S "replayed record" \
-c "discarding invalid record" \ -c "record from another epoch" \
-s "discarding invalid record" \ -s "record from another epoch" \
-c "resend" \ -c "resend" \
-s "resend" \ -s "resend" \
-s "Extra-header:" \ -s "Extra-header:" \
-c "HTTP/1.0 200 OK" -c "HTTP/1.0 200 OK"
run_test "DTLS proxy: multiple records in same datagram" \
-p "$P_PXY pack=50" \
"$P_SRV dtls=1 debug_level=2" \
"$P_CLI dtls=1 debug_level=2" \
0 \
-c "next record in same datagram" \
-s "next record in same datagram"
run_test "DTLS proxy: multiple records in same datagram, duplicate every packet" \
-p "$P_PXY pack=50 duplicate=1" \
"$P_SRV dtls=1 debug_level=2" \
"$P_CLI dtls=1 debug_level=2" \
0 \
-c "next record in same datagram" \
-s "next record in same datagram"
run_test "DTLS proxy: inject invalid AD record, default badmac_limit" \ run_test "DTLS proxy: inject invalid AD record, default badmac_limit" \
-p "$P_PXY bad_ad=1" \ -p "$P_PXY bad_ad=1" \
"$P_SRV dtls=1 debug_level=1" \ "$P_SRV dtls=1 debug_level=1" \
@ -4270,8 +4404,6 @@ run_test "DTLS proxy: delay ChangeCipherSpec" \
0 \ 0 \
-c "record from another epoch" \ -c "record from another epoch" \
-s "record from another epoch" \ -s "record from another epoch" \
-c "discarding invalid record" \
-s "discarding invalid record" \
-s "Extra-header:" \ -s "Extra-header:" \
-c "HTTP/1.0 200 OK" -c "HTTP/1.0 200 OK"