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.
diff --git a/ChangeLog.d/ssl_context-user_data.txt b/ChangeLog.d/ssl_context-user_data.txt
new file mode 100644
index 0000000..630d8f0
--- /dev/null
+++ b/ChangeLog.d/ssl_context-user_data.txt
@@ -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.
diff --git a/ChangeLog.d/ssl_context-version_number.txt b/ChangeLog.d/ssl_context-version_number.txt
new file mode 100644
index 0000000..b5951d0
--- /dev/null
+++ b/ChangeLog.d/ssl_context-version_number.txt
@@ -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.
diff --git a/ChangeLog.d/ssl_get_version_1_3.txt b/ChangeLog.d/ssl_get_version_1_3.txt
new file mode 100644
index 0000000..4436522
--- /dev/null
+++ b/ChangeLog.d/ssl_get_version_1_3.txt
@@ -0,0 +1,2 @@
+Bugfix
+ * Fix mbedtls_ssl_get_version() not reporting TLSv1.3. Fixes #5406.
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index 7e5fb19..7544f42 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -1161,6 +1161,14 @@
#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 @@
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 @@
#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 @@
/** 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);
};
/**
@@ -1837,6 +1875,22 @@
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 @@
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 @@
/**
* \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 @@
*/
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 @@
*
* \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 @@
* (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
diff --git a/library/ssl_debug_helpers.h b/library/ssl_debug_helpers.h
index 2fc4163..2ffc5f4 100644
--- a/library/ssl_debug_helpers.h
+++ b/library/ssl_debug_helpers.h
@@ -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 );
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index e80adb1..adb18ab 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -2206,6 +2206,21 @@
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 @@
{
case MBEDTLS_SSL_MINOR_VERSION_3:
return( "TLSv1.2" );
-
+ case MBEDTLS_SSL_MINOR_VERSION_4:
+ return( "TLSv1.3" );
default:
return( "unknown" );
}
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index dd05716..2fe7a40 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -9668,6 +9668,7 @@
-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 @@
-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
diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function
index cb66f3a..4f5ee97 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -886,6 +886,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
#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 );