Merge pull request #5426 from gilles-peskine-arm/ssl-get-version-3.1

Add accessors to mbedtls_ssl_context: user data, version
ABI-API-checking fails which was expected as this PR adds a new field in mbedtls_ssl_context and mbedtls_ssl_config.
This commit is contained in:
Ronald Cron 2022-02-21 17:03:24 +01:00 committed by GitHub
commit 4579a972bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 342 additions and 17 deletions

View file

@ -0,0 +1,6 @@
Features
* The structures mbedtls_ssl_config and mbedtls_ssl_context now store
a piece of user data which is reserved for the application. The user
data can be either a pointer or an integer.
* Add an accessor function to get the configuration associated with
an SSL context.

View file

@ -0,0 +1,3 @@
Features
* Add a function to access the protocol version from an SSL context in a
form that's easy to compare. Fixes #5407.

View file

@ -0,0 +1,2 @@
Bugfix
* Fix mbedtls_ssl_get_version() not reporting TLSv1.3. Fixes #5406.

View file

@ -1161,6 +1161,14 @@ struct mbedtls_ssl_session
#endif
};
/** Human-friendly representation of the (D)TLS protocol version. */
typedef enum
{
MBEDTLS_SSL_VERSION_UNKNOWN, /*!< Context not in use or version not yet negotiated. */
MBEDTLS_SSL_VERSION_1_2, /*!< (D)TLS 1.2 */
MBEDTLS_SSL_VERSION_1_3, /*!< (D)TLS 1.3 */
} mbedtls_ssl_protocol_version;
/*
* Identifiers for PRFs used in various versions of TLS.
*/
@ -1210,6 +1218,18 @@ typedef void mbedtls_ssl_export_keys_t( void *p_expkey,
const unsigned char server_random[32],
mbedtls_tls_prf_types tls_prf_type );
/* A type for storing user data in a library structure.
*
* The representation of type may change in future versions of the library.
* Only the behaviors guaranteed by documented accessor functions are
* guaranteed to remain stable.
*/
typedef union
{
uintptr_t n; /* typically a handle to an associated object */
void *p; /* typically a pointer to extra data */
} mbedtls_ssl_user_data_t;
/**
* SSL/TLS configuration to be shared between mbedtls_ssl_context structures.
*/
@ -1448,6 +1468,13 @@ struct mbedtls_ssl_config
#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_CLI_C)
unsigned int MBEDTLS_PRIVATE(dhm_min_bitlen); /*!< min. bit length of the DHM prime */
#endif
/** User data pointer or handle.
*
* The library sets this to \p 0 when creating a context and does not
* access it afterwards.
*/
mbedtls_ssl_user_data_t MBEDTLS_PRIVATE(user_data);
};
struct mbedtls_ssl_context
@ -1669,6 +1696,17 @@ struct mbedtls_ssl_context
/** Callback to export key block and master secret */
mbedtls_ssl_export_keys_t *MBEDTLS_PRIVATE(f_export_keys);
void *MBEDTLS_PRIVATE(p_export_keys); /*!< context for key export callback */
/** User data pointer or handle.
*
* The library sets this to \p 0 when creating a context and does not
* access it afterwards.
*
* \warning Serializing and restoring an SSL context with
* mbedtls_ssl_context_save() and mbedtls_ssl_context_load()
* does not currently restore the user data.
*/
mbedtls_ssl_user_data_t MBEDTLS_PRIVATE(user_data);
};
/**
@ -1836,6 +1874,22 @@ void mbedtls_ssl_conf_dbg( mbedtls_ssl_config *conf,
void (*f_dbg)(void *, int, const char *, int, const char *),
void *p_dbg );
/**
* \brief Return the SSL configuration structure associated
* with the given SSL context.
*
* \note The pointer returned by this function is guaranteed to
* remain valid until the context is freed.
*
* \param ssl The SSL context to query.
* \return Pointer to the SSL configuration associated with \p ssl.
*/
static inline const mbedtls_ssl_config *mbedtls_ssl_context_get_config(
const mbedtls_ssl_context *ssl )
{
return( ssl->MBEDTLS_PRIVATE( conf ) );
}
/**
* \brief Set the underlying BIO callbacks for write, read and
* read-with-timeout.
@ -2263,6 +2317,132 @@ void mbedtls_ssl_set_export_keys_cb( mbedtls_ssl_context *ssl,
mbedtls_ssl_export_keys_t *f_export_keys,
void *p_export_keys );
/** \brief Set the user data in an SSL configuration to a pointer.
*
* You can retrieve this value later with mbedtls_ssl_conf_get_user_data_p().
*
* \note The library stores \c p without accessing it. It is the responsibility
* of the caller to ensure that the pointer remains valid.
*
* \param conf The SSL configuration context to modify.
* \param p The new value of the user data.
*/
static inline void mbedtls_ssl_conf_set_user_data_p(
mbedtls_ssl_config *conf,
void *p )
{
conf->MBEDTLS_PRIVATE(user_data).p = p;
}
/** \brief Set the user data in an SSL configuration to an integer.
*
* You can retrieve this value later with mbedtls_ssl_conf_get_user_data_n().
*
* \param conf The SSL configuration context to modify.
* \param n The new value of the user data.
*/
static inline void mbedtls_ssl_conf_set_user_data_n(
mbedtls_ssl_config *conf,
uintptr_t n )
{
conf->MBEDTLS_PRIVATE(user_data).n = n;
}
/** \brief Retrieve the user data in an SSL configuration as a pointer.
*
* This is the value last set with mbedtls_ssl_conf_set_user_data_p(), or
* \c NULL if mbedtls_ssl_conf_set_user_data_p() has not previously been
* called. The value is undefined if mbedtls_ssl_conf_set_user_data_n() has
* been called without a subsequent call to mbedtls_ssl_conf_set_user_data_p().
*
* \param conf The SSL configuration context to modify.
* \return The current value of the user data.
*/
static inline void *mbedtls_ssl_conf_get_user_data_p(
mbedtls_ssl_config *conf )
{
return( conf->MBEDTLS_PRIVATE(user_data).p );
}
/** \brief Retrieve the user data in an SSL configuration as an integer.
*
* This is the value last set with mbedtls_ssl_conf_set_user_data_n(), or
* \c 0 if mbedtls_ssl_conf_set_user_data_n() has not previously been
* called. The value is undefined if mbedtls_ssl_conf_set_user_data_p() has
* been called without a subsequent call to mbedtls_ssl_conf_set_user_data_n().
*
* \param conf The SSL configuration context to modify.
* \return The current value of the user data.
*/
static inline uintptr_t mbedtls_ssl_conf_get_user_data_n(
mbedtls_ssl_config *conf )
{
return( conf->MBEDTLS_PRIVATE(user_data).n );
}
/** \brief Set the user data in an SSL context to a pointer.
*
* You can retrieve this value later with mbedtls_ssl_get_user_data_p().
*
* \note The library stores \c p without accessing it. It is the responsibility
* of the caller to ensure that the pointer remains valid.
*
* \param ssl The SSL context context to modify.
* \param p The new value of the user data.
*/
static inline void mbedtls_ssl_set_user_data_p(
mbedtls_ssl_context *ssl,
void *p )
{
ssl->MBEDTLS_PRIVATE(user_data).p = p;
}
/** \brief Set the user data in an SSL context to an integer.
*
* You can retrieve this value later with mbedtls_ssl_get_user_data_n().
*
* \param ssl The SSL context context to modify.
* \param n The new value of the user data.
*/
static inline void mbedtls_ssl_set_user_data_n(
mbedtls_ssl_context *ssl,
uintptr_t n )
{
ssl->MBEDTLS_PRIVATE(user_data).n = n;
}
/** \brief Retrieve the user data in an SSL context as a pointer.
*
* This is the value last set with mbedtls_ssl_set_user_data_p(), or
* \c NULL if mbedtls_ssl_set_user_data_p() has not previously been
* called. The value is undefined if mbedtls_ssl_set_user_data_n() has
* been called without a subsequent call to mbedtls_ssl_set_user_data_p().
*
* \param ssl The SSL context context to modify.
* \return The current value of the user data.
*/
static inline void *mbedtls_ssl_get_user_data_p(
mbedtls_ssl_context *ssl )
{
return( ssl->MBEDTLS_PRIVATE(user_data).p );
}
/** \brief Retrieve the user data in an SSL context as an integer.
*
* This is the value last set with mbedtls_ssl_set_user_data_n(), or
* \c 0 if mbedtls_ssl_set_user_data_n() has not previously been
* called. The value is undefined if mbedtls_ssl_set_user_data_p() has
* been called without a subsequent call to mbedtls_ssl_set_user_data_n().
*
* \param ssl The SSL context context to modify.
* \return The current value of the user data.
*/
static inline uintptr_t mbedtls_ssl_get_user_data_n(
mbedtls_ssl_context *ssl )
{
return( ssl->MBEDTLS_PRIVATE(user_data).n );
}
#if defined(MBEDTLS_SSL_ASYNC_PRIVATE)
/**
* \brief Configure asynchronous private key operation callbacks.
@ -3558,31 +3738,50 @@ void mbedtls_ssl_get_dtls_srtp_negotiation_result( const mbedtls_ssl_context *ss
/**
* \brief Set the maximum supported version sent from the client side
* and/or accepted at the server side
* (Default: MBEDTLS_SSL_MAX_MAJOR_VERSION, MBEDTLS_SSL_MAX_MINOR_VERSION)
* and/or accepted at the server side.
*
* See also the documentation of mbedtls_ssl_conf_min_version().
*
* \note This ignores ciphersuites from higher versions.
*
* \note With DTLS, use MBEDTLS_SSL_MINOR_VERSION_3 for DTLS 1.2
*
* \param conf SSL configuration
* \param major Major version number (only MBEDTLS_SSL_MAJOR_VERSION_3 supported)
* \param minor Minor version number (only MBEDTLS_SSL_MINOR_VERSION_3 supported)
* \param major Major version number (#MBEDTLS_SSL_MAJOR_VERSION_3)
* \param minor Minor version number
* (#MBEDTLS_SSL_MINOR_VERSION_3 for (D)TLS 1.2,
* #MBEDTLS_SSL_MINOR_VERSION_4 for TLS 1.3)
*/
void mbedtls_ssl_conf_max_version( mbedtls_ssl_config *conf, int major, int minor );
/**
* \brief Set the minimum accepted SSL/TLS protocol version
* (Default: TLS 1.2)
*
* \note By default, all supported versions are accepted.
* Future versions of the library may disable older
* protocol versions by default if they become deprecated.
*
* \note The following versions are supported (if enabled at
* compile time):
* - (D)TLS 1.2: \p major = #MBEDTLS_SSL_MAJOR_VERSION_3,
* \p minor = #MBEDTLS_SSL_MINOR_VERSION_3
* - TLS 1.3: \p major = #MBEDTLS_SSL_MAJOR_VERSION_3,
* \p minor = #MBEDTLS_SSL_MINOR_VERSION_4
*
* Note that the numbers in the constant names are the
* TLS internal protocol numbers, and the minor versions
* differ by one from the human-readable versions!
*
* \note Input outside of the SSL_MAX_XXXXX_VERSION and
* SSL_MIN_XXXXX_VERSION range is ignored.
*
* \note With DTLS, use MBEDTLS_SSL_MINOR_VERSION_3 for DTLS 1.2
* \note After the handshake, you can call
* mbedtls_ssl_get_version_number() to see what version was
* negotiated.
*
* \param conf SSL configuration
* \param major Major version number (only MBEDTLS_SSL_MAJOR_VERSION_3 supported)
* \param minor Minor version number (only MBEDTLS_SSL_MINOR_VERSION_3 supported)
* \param major Major version number (#MBEDTLS_SSL_MAJOR_VERSION_3)
* \param minor Minor version number
* (#MBEDTLS_SSL_MINOR_VERSION_3 for (D)TLS 1.2,
* #MBEDTLS_SSL_MINOR_VERSION_4 for TLS 1.3)
*/
void mbedtls_ssl_conf_min_version( mbedtls_ssl_config *conf, int major, int minor );
@ -3903,6 +4102,21 @@ int mbedtls_ssl_get_ciphersuite_id_from_ssl( const mbedtls_ssl_context *ssl );
*/
const char *mbedtls_ssl_get_ciphersuite( const mbedtls_ssl_context *ssl );
/**
* \brief Return the (D)TLS protocol version negotiated in the
* given connection.
*
* \note If you call this function too early during the initial
* handshake, before the two sides have agreed on a version,
* this function returns #MBEDTLS_SSL_VERSION_UNKNOWN.
*
* \param ssl The SSL context to query.
* \return The negotiated protocol version.
*/
mbedtls_ssl_protocol_version mbedtls_ssl_get_version_number(
const mbedtls_ssl_context *ssl );
/**
* \brief Return the current TLS version
*
@ -4334,6 +4548,14 @@ void mbedtls_ssl_free( mbedtls_ssl_context *ssl );
*
* \see mbedtls_ssl_context_load()
*
* \note The serialized data only contains the data that is
* necessary to resume the connection: negotiated protocol
* options, session identifier, keys, etc.
* Loading a saved SSL context does not restore settings and
* state related to how the application accesses the context,
* such as configured callback functions, user data, pending
* incoming or outgoing data, etc.
*
* \note This feature is currently only available under certain
* conditions, see the documentation of the return value
* #MBEDTLS_ERR_SSL_BAD_INPUT_DATA for details.
@ -4412,8 +4634,11 @@ int mbedtls_ssl_context_save( mbedtls_ssl_context *ssl,
* (unless they were already set before calling
* mbedtls_ssl_session_reset() and the values are suitable for
* the present connection). Specifically, you want to call
* at least mbedtls_ssl_set_bio() and
* mbedtls_ssl_set_timer_cb(). All other SSL setter functions
* at least mbedtls_ssl_set_bio(),
* mbedtls_ssl_set_timer_cb(), and
* mbedtls_ssl_set_user_data_n() or
* mbedtls_ssl_set_user_data_p() if they were set originally.
* All other SSL setter functions
* are not necessary to call, either because they're only used
* in handshakes, or because the setting is already saved. You
* might choose to call them anyway, for example in order to

View file

@ -33,6 +33,8 @@
const char *mbedtls_ssl_states_str( mbedtls_ssl_states in );
const char *mbedtls_ssl_protocol_version_str( mbedtls_ssl_protocol_version in );
const char *mbedtls_tls_prf_types_str( mbedtls_tls_prf_types in );
const char *mbedtls_ssl_key_export_type_str( mbedtls_ssl_key_export_type in );

View file

@ -2206,6 +2206,21 @@ const char *mbedtls_ssl_get_ciphersuite( const mbedtls_ssl_context *ssl )
return mbedtls_ssl_get_ciphersuite_name( ssl->session->ciphersuite );
}
mbedtls_ssl_protocol_version mbedtls_ssl_get_version_number(
const mbedtls_ssl_context *ssl )
{
/* For major_ver, only 3 is supported, so skip checking it. */
switch( ssl->minor_ver )
{
case MBEDTLS_SSL_MINOR_VERSION_3:
return( MBEDTLS_SSL_VERSION_1_2 );
case MBEDTLS_SSL_MINOR_VERSION_4:
return( MBEDTLS_SSL_VERSION_1_3 );
default:
return( MBEDTLS_SSL_VERSION_UNKNOWN );
}
}
const char *mbedtls_ssl_get_version( const mbedtls_ssl_context *ssl )
{
#if defined(MBEDTLS_SSL_PROTO_DTLS)
@ -2226,7 +2241,8 @@ const char *mbedtls_ssl_get_version( const mbedtls_ssl_context *ssl )
{
case MBEDTLS_SSL_MINOR_VERSION_3:
return( "TLSv1.2" );
case MBEDTLS_SSL_MINOR_VERSION_4:
return( "TLSv1.3" );
default:
return( "unknown" );
}

View file

@ -9668,6 +9668,7 @@ run_test "TLS 1.3: minimal feature sets - openssl" \
-c "<= parse certificate verify" \
-c "mbedtls_ssl_tls13_process_certificate_verify() returned 0" \
-c "<= parse finished message" \
-c "Protocol is TLSv1.3" \
-c "HTTP/1.0 200 ok"
requires_gnutls_tls1_3
@ -9701,6 +9702,7 @@ run_test "TLS 1.3: minimal feature sets - gnutls" \
-c "<= parse certificate verify" \
-c "mbedtls_ssl_tls13_process_certificate_verify() returned 0" \
-c "<= parse finished message" \
-c "Protocol is TLSv1.3" \
-c "HTTP/1.0 200 OK"
requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3

View file

@ -886,6 +886,7 @@ int mbedtls_endpoint_init( mbedtls_endpoint *ep, int endpoint_type, int pk_alg,
mbedtls_test_message_queue *output_queue )
{
int ret = -1;
uintptr_t user_data_n;
if( dtls_context != NULL && ( input_queue == NULL || output_queue == NULL ) )
return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
@ -904,6 +905,18 @@ int mbedtls_endpoint_init( mbedtls_endpoint *ep, int endpoint_type, int pk_alg,
mbedtls_ctr_drbg_random,
&( ep->ctr_drbg ) );
mbedtls_entropy_init( &( ep->entropy ) );
TEST_ASSERT( mbedtls_ssl_conf_get_user_data_p( &ep->conf ) == NULL );
TEST_EQUAL( mbedtls_ssl_conf_get_user_data_n( &ep->conf ), 0 );
TEST_ASSERT( mbedtls_ssl_get_user_data_p( &ep->ssl ) == NULL );
TEST_EQUAL( mbedtls_ssl_get_user_data_n( &ep->ssl ), 0 );
(void) mbedtls_test_rnd_std_rand( NULL,
(void*) &user_data_n,
sizeof( user_data_n ) );
mbedtls_ssl_conf_set_user_data_n( &ep->conf, user_data_n );
mbedtls_ssl_set_user_data_n( &ep->ssl, user_data_n );
if( dtls_context != NULL )
{
TEST_ASSERT( mbedtls_message_socket_setup( input_queue, output_queue,
@ -954,6 +967,11 @@ int mbedtls_endpoint_init( mbedtls_endpoint *ep, int endpoint_type, int pk_alg,
ret = mbedtls_endpoint_certificate_init( ep, pk_alg );
TEST_ASSERT( ret == 0 );
TEST_EQUAL( mbedtls_ssl_conf_get_user_data_n( &ep->conf ), user_data_n );
mbedtls_ssl_conf_set_user_data_p( &ep->conf, ep );
TEST_EQUAL( mbedtls_ssl_get_user_data_n( &ep->ssl ), user_data_n );
mbedtls_ssl_set_user_data_p( &ep->ssl, ep );
exit:
return ret;
}
@ -1793,6 +1811,45 @@ int exchange_data( mbedtls_ssl_context *ssl_1,
ssl_2, 256, 1 );
}
int check_ssl_version( int expected_negotiated_version,
const mbedtls_ssl_context *ssl )
{
const char *version_string = mbedtls_ssl_get_version( ssl );
mbedtls_ssl_protocol_version version_number =
mbedtls_ssl_get_version_number( ssl );
TEST_EQUAL( ssl->major_ver, MBEDTLS_SSL_MAJOR_VERSION_3 );
TEST_EQUAL( ssl->minor_ver, expected_negotiated_version );
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
{
TEST_EQUAL( version_string[0], 'D' );
++version_string;
}
switch( expected_negotiated_version )
{
case MBEDTLS_SSL_MINOR_VERSION_3:
TEST_EQUAL( version_number, MBEDTLS_SSL_VERSION_1_2 );
TEST_ASSERT( strcmp( version_string, "TLSv1.2" ) == 0 );
break;
case MBEDTLS_SSL_MINOR_VERSION_4:
TEST_EQUAL( version_number, MBEDTLS_SSL_VERSION_1_3 );
TEST_ASSERT( strcmp( version_string, "TLSv1.3" ) == 0 );
break;
default:
TEST_ASSERT( ! "Version check not implemented for this protocol version" );
}
return( 1 );
exit:
return( 0 );
}
#if defined(MBEDTLS_X509_CRT_PARSE_C) && \
defined(MBEDTLS_ENTROPY_C) && \
defined(MBEDTLS_CTR_DRBG_C)
@ -1984,11 +2041,16 @@ void perform_handshake( handshake_test_options* options )
TEST_ASSERT( client.ssl.state == MBEDTLS_SSL_HANDSHAKE_OVER );
TEST_ASSERT( server.ssl.state == MBEDTLS_SSL_HANDSHAKE_OVER );
/* Check that we agree on the version... */
TEST_ASSERT( client.ssl.minor_ver == server.ssl.minor_ver );
/* Check that both sides have negotiated the expected version. */
mbedtls_test_set_step( 0 );
if( ! check_ssl_version( options->expected_negotiated_version,
&client.ssl ) )
goto exit;
/* And check that the version negotiated is the expected one. */
TEST_EQUAL( client.ssl.minor_ver, options->expected_negotiated_version );
mbedtls_test_set_step( 1 );
if( ! check_ssl_version( options->expected_negotiated_version,
&server.ssl ) )
goto exit;
#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH)
if( options->resize_buffers != 0 )
@ -2043,6 +2105,8 @@ void perform_handshake( handshake_test_options* options )
mbedtls_mock_tcp_recv_msg,
NULL );
mbedtls_ssl_set_user_data_p( &server.ssl, &server );
#if defined(MBEDTLS_TIMING_C)
mbedtls_ssl_set_timer_cb( &server.ssl, &timer_server,
mbedtls_timing_set_delay,
@ -2150,6 +2214,11 @@ void perform_handshake( handshake_test_options* options )
}
#endif /* MBEDTLS_SSL_RENEGOTIATION */
TEST_ASSERT( mbedtls_ssl_conf_get_user_data_p( &client.conf ) == &client );
TEST_ASSERT( mbedtls_ssl_get_user_data_p( &client.ssl ) == &client );
TEST_ASSERT( mbedtls_ssl_conf_get_user_data_p( &server.conf ) == &server );
TEST_ASSERT( mbedtls_ssl_get_user_data_p( &server.ssl ) == &server );
exit:
mbedtls_endpoint_free( &client, options->dtls != 0 ? &client_context : NULL );
mbedtls_endpoint_free( &server, options->dtls != 0 ? &server_context : NULL );