Merge pull request #3135 from gilles-peskine-arm/changelog-assemble-text
Switch to the classic Mbed TLS ChangeLog format
diff --git a/.github/issue_template.md b/.github/issue_template.md
index 7c31353..18b87fc 100644
--- a/.github/issue_template.md
+++ b/.github/issue_template.md
@@ -1,7 +1,7 @@
Note: This is just a template, so feel free to use/remove the unnecessary things
### Description
-- Type: Bug | Enhancement\Feature Request | Question
+- Type: Bug | Enhancement\Feature Request
- Priority: Blocker | Major | Minor
---------------------------------------------------------------
@@ -38,4 +38,4 @@
## Question
-**Please first check for answers in the [Mbed TLS knowledge Base](https://tls.mbed.org/kb), and preferably file an issue in the [Mbed TLS support forum](https://forums.mbed.com/c/mbed-tls)**
+**Please first check for answers in the [Mbed TLS knowledge Base](https://tls.mbed.org/kb). If you can't find the answer you're looking for then please use the [Mbed TLS mailing list](https://lists.trustedfirmware.org/mailman/listinfo/mbed-tls)**
diff --git a/ChangeLog b/ChangeLog
index bcceebb..917c521 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,14 @@
* Deprecate MBEDTLS_SSL_HW_RECORD_ACCEL that enables function hooks in the
SSL module for hardware acceleration of individual records.
+Security
+ * Fix issue in DTLS handling of new associations with the same parameters
+ (RFC 6347 section 4.2.8): an attacker able to send forged UDP packets to
+ the server could cause it to drop established associations with
+ legitimate clients, resulting in a Denial of Service. This could only
+ happen when MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE was enabled in config.h
+ (which it is by default).
+
Bugfix
* Fix compilation failure when both MBEDTLS_SSL_PROTO_DTLS and
MBEDTLS_SSL_HW_RECORD_ACCEL are enabled.
diff --git a/doxygen/mbedtls.doxyfile b/doxygen/mbedtls.doxyfile
index 4732271..148fa27 100644
--- a/doxygen/mbedtls.doxyfile
+++ b/doxygen/mbedtls.doxyfile
@@ -1594,7 +1594,7 @@
# contain include files that are not input files but should be processed by
# the preprocessor.
-INCLUDE_PATH =
+INCLUDE_PATH = ../include
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h
index d904d5a..fa3caa7 100644
--- a/include/mbedtls/check_config.h
+++ b/include/mbedtls/check_config.h
@@ -619,6 +619,23 @@
#error "MBEDTLS_SSL_PROTO_TLS1_2 defined, but not all prerequisites"
#endif
+#if (defined(MBEDTLS_SSL_PROTO_SSL3) || defined(MBEDTLS_SSL_PROTO_TLS1) || \
+ defined(MBEDTLS_SSL_PROTO_TLS1_1) || defined(MBEDTLS_SSL_PROTO_TLS1_2)) && \
+ !(defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \
+ defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \
+ defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \
+ defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \
+ defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \
+ defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) || \
+ defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) || \
+ defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) || \
+ defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) || \
+ defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \
+ defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) )
+#error "One or more versions of the TLS protocol are enabled " \
+ "but no key exchange methods defined with MBEDTLS_KEY_EXCHANGE_xxxx"
+#endif
+
#if defined(MBEDTLS_SSL_PROTO_DTLS) && \
!defined(MBEDTLS_SSL_PROTO_TLS1_1) && \
!defined(MBEDTLS_SSL_PROTO_TLS1_2)
@@ -763,6 +780,10 @@
#error "MBEDTLS_X509_CREATE_C defined, but not all prerequisites"
#endif
+#if defined(MBEDTLS_CERTS_C) && !defined(MBEDTLS_X509_USE_C)
+#error "MBEDTLS_CERTS_C defined, but not all prerequisites"
+#endif
+
#if defined(MBEDTLS_X509_CRT_PARSE_C) && ( !defined(MBEDTLS_X509_USE_C) )
#error "MBEDTLS_X509_CRT_PARSE_C defined, but not all prerequisites"
#endif
diff --git a/library/ssl_msg.c b/library/ssl_msg.c
index 18fa555..428ace5 100644
--- a/library/ssl_msg.c
+++ b/library/ssl_msg.c
@@ -3197,16 +3197,17 @@
* that looks like a ClientHello.
*
* - if the input looks like a ClientHello without cookies,
- * send back HelloVerifyRequest, then
- * return MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED
+ * send back HelloVerifyRequest, then return 0
* - if the input looks like a ClientHello with a valid cookie,
* reset the session of the current context, and
* return MBEDTLS_ERR_SSL_CLIENT_RECONNECT
* - if anything goes wrong, return a specific error code
*
- * mbedtls_ssl_read_record() will ignore the record if anything else than
- * MBEDTLS_ERR_SSL_CLIENT_RECONNECT or 0 is returned, although this function
- * cannot not return 0.
+ * This function is called (through ssl_check_client_reconnect()) when an
+ * unexpected record is found in ssl_get_next_record(), which will discard the
+ * record if we return 0, and bubble up the return value otherwise (this
+ * includes the case of MBEDTLS_ERR_SSL_CLIENT_RECONNECT and of unexpected
+ * errors, and is the right thing to do in both cases).
*/
static int ssl_handle_possible_reconnect( mbedtls_ssl_context *ssl )
{
@@ -3218,6 +3219,8 @@
{
/* If we can't use cookies to verify reachability of the peer,
* drop the record. */
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "no cookie callbacks, "
+ "can't check reconnect validity" ) );
return( 0 );
}
@@ -3233,16 +3236,23 @@
if( ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED )
{
+ int send_ret;
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "sending HelloVerifyRequest" ) );
+ MBEDTLS_SSL_DEBUG_BUF( 4, "output record sent to network",
+ ssl->out_buf, len );
/* Don't check write errors as we can't do anything here.
* If the error is permanent we'll catch it later,
* if it's not, then hopefully it'll work next time. */
- (void) ssl->f_send( ssl->p_bio, ssl->out_buf, len );
- ret = 0;
+ send_ret = ssl->f_send( ssl->p_bio, ssl->out_buf, len );
+ MBEDTLS_SSL_DEBUG_RET( 2, "ssl->f_send", send_ret );
+ (void) send_ret;
+
+ return( 0 );
}
if( ret == 0 )
{
- /* Got a valid cookie, partially reset context */
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "cookie is valid, resetting context" ) );
if( ( ret = mbedtls_ssl_session_reset_int( ssl, 1 ) ) != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, "reset", ret );
@@ -4415,6 +4425,7 @@
ssl->in_msglen = rec.data_len;
ret = ssl_check_client_reconnect( ssl );
+ MBEDTLS_SSL_DEBUG_RET( 2, "ssl_check_client_reconnect", ret );
if( ret != 0 )
return( ret );
#endif
diff --git a/library/x509.c b/library/x509.c
index 7f8181b..c451332 100644
--- a/library/x509.c
+++ b/library/x509.c
@@ -1064,7 +1064,7 @@
mbedtls_x509_crt_free( &clicert );
#else
((void) verbose);
-#endif /* MBEDTLS_CERTS_C && MBEDTLS_SHA1_C */
+#endif /* MBEDTLS_CERTS_C && MBEDTLS_SHA256_C */
return( ret );
}
diff --git a/programs/pkey/ecdsa.c b/programs/pkey/ecdsa.c
index b851c31..9feb160 100644
--- a/programs/pkey/ecdsa.c
+++ b/programs/pkey/ecdsa.c
@@ -189,7 +189,7 @@
sig, &sig_len,
mbedtls_ctr_drbg_random, &ctr_drbg ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ecdsa_genkey returned %d\n", ret );
+ mbedtls_printf( " failed\n ! mbedtls_ecdsa_write_signature returned %d\n", ret );
goto exit;
}
mbedtls_printf( " ok (signature length = %u)\n", (unsigned int) sig_len );
diff --git a/programs/test/udp_proxy.c b/programs/test/udp_proxy.c
index 979910e..7447571 100644
--- a/programs/test/udp_proxy.c
+++ b/programs/test/udp_proxy.c
@@ -133,6 +133,7 @@
" modifying CID in first instance of the packet.\n" \
" protect_hvr=0/1 default: 0 (don't protect HelloVerifyRequest)\n" \
" protect_len=%%d default: (don't protect packets of this size)\n" \
+ " inject_clihlo=0/1 default: 0 (don't inject fake ClientHello)\n" \
"\n" \
" seed=%%d default: (use current time)\n" \
USAGE_PACK \
@@ -166,6 +167,7 @@
unsigned bad_cid; /* inject corrupted CID record */
int protect_hvr; /* never drop or delay HelloVerifyRequest */
int protect_len; /* never drop/delay packet of the given size*/
+ int inject_clihlo; /* inject fake ClientHello after handshake */
unsigned pack; /* merge packets into single datagram for
* at most \c merge milliseconds if > 0 */
unsigned int seed; /* seed for "random" events */
@@ -314,6 +316,12 @@
if( opt.protect_len < 0 )
exit_usage( p, q );
}
+ else if( strcmp( p, "inject_clihlo" ) == 0 )
+ {
+ opt.inject_clihlo = atoi( q );
+ if( opt.inject_clihlo < 0 || opt.inject_clihlo > 1 )
+ exit_usage( p, q );
+ }
else if( strcmp( p, "seed" ) == 0 )
{
opt.seed = atoi( q );
@@ -523,11 +531,41 @@
fflush( stdout );
}
+/*
+ * In order to test the server's behaviour when receiving a ClientHello after
+ * the connection is established (this could be a hard reset from the client,
+ * but the server must not drop the existing connection before establishing
+ * client reachability, see RFC 6347 Section 4.2.8), we memorize the first
+ * ClientHello we see (which can't have a cookie), then replay it after the
+ * first ApplicationData record - then we're done.
+ *
+ * This is controlled by the inject_clihlo option.
+ *
+ * We want an explicit state and a place to store the packet.
+ */
+typedef enum {
+ ICH_INIT, /* haven't seen the first ClientHello yet */
+ ICH_CACHED, /* cached the initial ClientHello */
+ ICH_INJECTED, /* ClientHello already injected, done */
+} inject_clihlo_state_t;
+
+static inject_clihlo_state_t inject_clihlo_state;
+static packet initial_clihlo;
+
int send_packet( const packet *p, const char *why )
{
int ret;
mbedtls_net_context *dst = p->dst;
+ /* save initial ClientHello? */
+ if( opt.inject_clihlo != 0 &&
+ inject_clihlo_state == ICH_INIT &&
+ strcmp( p->type, "ClientHello" ) == 0 )
+ {
+ memcpy( &initial_clihlo, p, sizeof( packet ) );
+ inject_clihlo_state = ICH_CACHED;
+ }
+
/* insert corrupted CID record? */
if( opt.bad_cid != 0 &&
strcmp( p->type, "CID" ) == 0 &&
@@ -592,6 +630,23 @@
}
}
+ /* Inject ClientHello after first ApplicationData */
+ if( opt.inject_clihlo != 0 &&
+ inject_clihlo_state == ICH_CACHED &&
+ strcmp( p->type, "ApplicationData" ) == 0 )
+ {
+ print_packet( &initial_clihlo, "injected" );
+
+ if( ( ret = dispatch_data( dst, initial_clihlo.buf,
+ initial_clihlo.len ) ) <= 0 )
+ {
+ mbedtls_printf( " ! dispatch returned %d\n", ret );
+ return( ret );
+ }
+
+ inject_clihlo_state = ICH_INJECTED;
+ }
+
return( 0 );
}
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 35f742f..9b6eee1 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -7279,8 +7279,8 @@
not_with_valgrind # spurious resend
run_test "DTLS client reconnect from same port: reference" \
- "$P_SRV dtls=1 exchanges=2 read_timeout=1000" \
- "$P_CLI dtls=1 exchanges=2 debug_level=2 hs_timeout=500-1000" \
+ "$P_SRV dtls=1 exchanges=2 read_timeout=20000 hs_timeout=10000-20000" \
+ "$P_CLI dtls=1 exchanges=2 debug_level=2 hs_timeout=10000-20000" \
0 \
-C "resend" \
-S "The operation timed out" \
@@ -7288,8 +7288,8 @@
not_with_valgrind # spurious resend
run_test "DTLS client reconnect from same port: reconnect" \
- "$P_SRV dtls=1 exchanges=2 read_timeout=1000" \
- "$P_CLI dtls=1 exchanges=2 debug_level=2 hs_timeout=500-1000 reconnect_hard=1" \
+ "$P_SRV dtls=1 exchanges=2 read_timeout=20000 hs_timeout=10000-20000" \
+ "$P_CLI dtls=1 exchanges=2 debug_level=2 hs_timeout=10000-20000 reconnect_hard=1" \
0 \
-C "resend" \
-S "The operation timed out" \
@@ -7318,6 +7318,14 @@
-s "The operation timed out" \
-S "Client initiated reconnection from same port"
+run_test "DTLS client reconnect from same port: attacker-injected" \
+ -p "$P_PXY inject_clihlo=1" \
+ "$P_SRV dtls=1 exchanges=2 debug_level=1" \
+ "$P_CLI dtls=1 exchanges=2" \
+ 0 \
+ -s "possible client reconnect from the same port" \
+ -S "Client initiated reconnection from same port"
+
# Tests for various cases of client authentication with DTLS
# (focused on handshake flows and message parsing)
@@ -8387,8 +8395,8 @@
not_with_valgrind # spurious resend due to timeout
run_test "DTLS proxy: reference" \
-p "$P_PXY" \
- "$P_SRV dtls=1 debug_level=2" \
- "$P_CLI dtls=1 debug_level=2" \
+ "$P_SRV dtls=1 debug_level=2 hs_timeout=10000-20000" \
+ "$P_CLI dtls=1 debug_level=2 hs_timeout=10000-20000" \
0 \
-C "replayed record" \
-S "replayed record" \
@@ -8405,8 +8413,8 @@
not_with_valgrind # spurious resend due to timeout
run_test "DTLS proxy: duplicate every packet" \
-p "$P_PXY duplicate=1" \
- "$P_SRV dtls=1 dgram_packing=0 debug_level=2" \
- "$P_CLI dtls=1 dgram_packing=0 debug_level=2" \
+ "$P_SRV dtls=1 dgram_packing=0 debug_level=2 hs_timeout=10000-20000" \
+ "$P_CLI dtls=1 dgram_packing=0 debug_level=2 hs_timeout=10000-20000" \
0 \
-c "replayed record" \
-s "replayed record" \
diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function
index 6467340..1069c24 100644
--- a/tests/suites/host_test.function
+++ b/tests/suites/host_test.function
@@ -425,7 +425,7 @@
*/
static void write_outcome_result( FILE *outcome_file,
size_t unmet_dep_count,
- char *unmet_dependencies[],
+ int unmet_dependencies[],
int ret,
const test_info_t *info )
{
@@ -443,7 +443,7 @@
mbedtls_fprintf( outcome_file, "SKIP" );
for( i = 0; i < unmet_dep_count; i++ )
{
- mbedtls_fprintf( outcome_file, "%c%s",
+ mbedtls_fprintf( outcome_file, "%c%d",
i == 0 ? ';' : ':',
unmet_dependencies[i] );
}
@@ -598,7 +598,7 @@
testfile_index++ )
{
size_t unmet_dep_count = 0;
- char *unmet_dependencies[20];
+ int unmet_dependencies[20];
test_filename = test_files[ testfile_index ];
@@ -647,19 +647,7 @@
int dep_id = strtol( params[i], NULL, 10 );
if( dep_check( dep_id ) != DEPENDENCY_SUPPORTED )
{
- if( 0 == option_verbose )
- {
- /* Only one count is needed if not verbose */
- unmet_dep_count++;
- break;
- }
-
- unmet_dependencies[ unmet_dep_count ] = strdup( params[i] );
- if( unmet_dependencies[ unmet_dep_count ] == NULL )
- {
- mbedtls_fprintf( stderr, "FATAL: Out of memory\n" );
- mbedtls_exit( MBEDTLS_EXIT_FAILURE );
- }
+ unmet_dependencies[unmet_dep_count] = dep_id;
unmet_dep_count++;
}
}
@@ -730,9 +718,8 @@
mbedtls_fprintf( stdout, "\n Unmet dependencies: " );
for( i = 0; i < unmet_dep_count; i++ )
{
- mbedtls_fprintf( stdout, "%s ",
+ mbedtls_fprintf( stdout, "%d ",
unmet_dependencies[i] );
- free( unmet_dependencies[i] );
}
}
mbedtls_fprintf( stdout, "\n" );
@@ -783,10 +770,6 @@
total_errors++;
}
fclose( file );
-
- /* In case we encounter early end of file */
- for( i = 0; i < unmet_dep_count; i++ )
- free( unmet_dependencies[i] );
}
if( outcome_file != NULL )