diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 5a02182c0e..09b1dfcf85 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -1188,6 +1188,10 @@ struct mbedtls_ssl_session uint8_t MBEDTLS_PRIVATE(resumption_key_len); /*!< resumption_key length */ unsigned char MBEDTLS_PRIVATE(resumption_key)[MBEDTLS_SSL_TLS1_3_TICKET_RESUMPTION_KEY_LEN]; +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) && defined(MBEDTLS_SSL_CLI_C) + char *MBEDTLS_PRIVATE(hostname); /*!< host name binded with tickets */ +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION && MBEDTLS_SSL_CLI_C */ + #if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_CLI_C) mbedtls_time_t MBEDTLS_PRIVATE(ticket_received); /*!< time ticket was received */ #endif /* MBEDTLS_HAVE_TIME && MBEDTLS_SSL_CLI_C */ diff --git a/library/ssl_client.c b/library/ssl_client.c index d2513915c1..90ec252d42 100644 --- a/library/ssl_client.c +++ b/library/ssl_client.c @@ -707,6 +707,7 @@ static int ssl_generate_random( mbedtls_ssl_context *ssl ) MBEDTLS_CLIENT_HELLO_RANDOM_LEN - gmt_unix_time_len ); return( ret ); } + MBEDTLS_CHECK_RETURN_CRITICAL static int ssl_prepare_client_hello( mbedtls_ssl_context *ssl ) { @@ -864,6 +865,37 @@ static int ssl_prepare_client_hello( mbedtls_ssl_context *ssl ) } } +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if( ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_3 && + ssl->handshake->resume ) + { + int hostname_mismatch = ssl->hostname != NULL || + session_negotiate->hostname != NULL; + if( ssl->hostname != NULL && session_negotiate->hostname != NULL ) + { + hostname_mismatch = strcmp( + ssl->hostname, session_negotiate->hostname ) != 0; + } + + if( hostname_mismatch ) + { + MBEDTLS_SSL_DEBUG_MSG( + 1, ( "Hostname mismatch the session ticket, " + "disable session resumption." ) ); + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + } + } + else + { + return mbedtls_ssl_session_set_hostname( session_negotiate, + ssl->hostname ); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && + MBEDTLS_SSL_SESSION_TICKETS && + MBEDTLS_SSL_SERVER_NAME_INDICATION */ + return( 0 ); } /* diff --git a/library/ssl_misc.h b/library/ssl_misc.h index afacb76f01..828937c3f6 100644 --- a/library/ssl_misc.h +++ b/library/ssl_misc.h @@ -2494,4 +2494,13 @@ int mbedtls_ssl_tls13_write_binders_of_pre_shared_key_ext( unsigned char *buf, unsigned char *end ); #endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) && \ + defined(MBEDTLS_SSL_CLI_C) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_session_set_hostname( mbedtls_ssl_session *session, + const char *hostname ); +#endif + #endif /* ssl_misc.h */ diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 3357cd62af..c36729fc56 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -235,10 +235,13 @@ int mbedtls_ssl_session_copy( mbedtls_ssl_session *dst, { mbedtls_ssl_session_free( dst ); memcpy( dst, src, sizeof( mbedtls_ssl_session ) ); - #if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) dst->ticket = NULL; +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + dst->hostname = NULL; #endif +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ #if defined(MBEDTLS_X509_CRT_PARSE_C) @@ -287,6 +290,18 @@ int mbedtls_ssl_session_copy( mbedtls_ssl_session *dst, memcpy( dst->ticket, src->ticket, src->ticket_len ); } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if( src->endpoint == MBEDTLS_SSL_IS_CLIENT ) + { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + ret = mbedtls_ssl_session_set_hostname( dst, src->hostname ); + if( ret != 0 ) + return ( ret ); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && + MBEDTLS_SSL_SERVER_NAME_INDICATION */ #endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ return( 0 ); @@ -1926,6 +1941,7 @@ mbedtls_ssl_mode_t mbedtls_ssl_get_mode_from_ciphersuite( /* Serialization of TLS 1.3 sessions: * * struct { + * opaque hostname<0..2^16-1>; * uint64 ticket_received; * uint32 ticket_lifetime; * opaque ticket<1..2^16-1>; @@ -1952,6 +1968,11 @@ static int ssl_tls13_session_save( const mbedtls_ssl_session *session, size_t *olen ) { unsigned char *p = buf; +#if defined(MBEDTLS_SSL_CLI_C) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + size_t hostname_len = ( session->hostname == NULL ) ? + 0 : strlen( session->hostname ) + 1; +#endif size_t needed = 1 /* endpoint */ + 2 /* ciphersuite */ + 4 /* ticket_age_add */ @@ -1970,6 +1991,11 @@ static int ssl_tls13_session_save( const mbedtls_ssl_session *session, #if defined(MBEDTLS_SSL_CLI_C) if( session->endpoint == MBEDTLS_SSL_IS_CLIENT ) { +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + needed += 2 /* hostname_len */ + + hostname_len; /* hostname */ +#endif + needed += 4 /* ticket_lifetime */ + 2; /* ticket_len */ @@ -2007,6 +2033,17 @@ static int ssl_tls13_session_save( const mbedtls_ssl_session *session, #if defined(MBEDTLS_SSL_CLI_C) if( session->endpoint == MBEDTLS_SSL_IS_CLIENT ) { +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + MBEDTLS_PUT_UINT16_BE( hostname_len, p, 0 ); + p += 2; + if( hostname_len > 0 ) + { + /* save host name */ + memcpy( p, session->hostname, hostname_len ); + p += hostname_len; + } +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + #if defined(MBEDTLS_HAVE_TIME) MBEDTLS_PUT_UINT64_BE( (uint64_t) session->ticket_received, p, 0 ); p += 8; @@ -2067,6 +2104,28 @@ static int ssl_tls13_session_load( mbedtls_ssl_session *session, #if defined(MBEDTLS_SSL_CLI_C) if( session->endpoint == MBEDTLS_SSL_IS_CLIENT ) { +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) + size_t hostname_len; + /* load host name */ + if( end - p < 2 ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + hostname_len = MBEDTLS_GET_UINT16_BE( p, 0 ); + p += 2; + + if( end - p < ( long int )hostname_len ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + if( hostname_len > 0 ) + { + session->hostname = mbedtls_calloc( 1, hostname_len ); + if( session->hostname == NULL ) + return( MBEDTLS_ERR_SSL_ALLOC_FAILED ); + memcpy( session->hostname, p, hostname_len ); + p += hostname_len; + } +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION && + MBEDTLS_SSL_SESSION_TICKETS */ + #if defined(MBEDTLS_HAVE_TIME) if( end - p < 8 ) return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); @@ -3668,6 +3727,10 @@ void mbedtls_ssl_session_free( mbedtls_ssl_session *session ) #endif #if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + mbedtls_free( session->hostname ); +#endif mbedtls_free( session->ticket ); #endif @@ -8790,4 +8853,54 @@ int mbedtls_ssl_write_alpn_ext( mbedtls_ssl_context *ssl, } #endif /* MBEDTLS_SSL_ALPN */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) && \ + defined(MBEDTLS_SSL_CLI_C) +int mbedtls_ssl_session_set_hostname( mbedtls_ssl_session *session, + const char *hostname ) +{ + /* Initialize to suppress unnecessary compiler warning */ + size_t hostname_len = 0; + + /* Check if new hostname is valid before + * making any change to current one */ + if( hostname != NULL ) + { + hostname_len = strlen( hostname ); + + if( hostname_len > MBEDTLS_SSL_MAX_HOST_NAME_LEN ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + } + + /* Now it's clear that we will overwrite the old hostname, + * so we can free it safely */ + if( session->hostname != NULL ) + { + mbedtls_platform_zeroize( session->hostname, + strlen( session->hostname ) ); + mbedtls_free( session->hostname ); + } + + /* Passing NULL as hostname shall clear the old one */ + if( hostname == NULL ) + { + session->hostname = NULL; + } + else + { + session->hostname = mbedtls_calloc( 1, hostname_len + 1 ); + if( session->hostname == NULL ) + return( MBEDTLS_ERR_SSL_ALLOC_FAILED ); + + memcpy( session->hostname, hostname, hostname_len ); + } + + return( 0 ); +} +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && + MBEDTLS_SSL_SESSION_TICKETS && + MBEDTLS_SSL_SERVER_NAME_INDICATION && + MBEDTLS_SSL_CLI_C */ + #endif /* MBEDTLS_SSL_TLS_C */ diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c index db53310e93..3e54f38790 100644 --- a/library/ssl_tls13_server.c +++ b/library/ssl_tls13_server.c @@ -2842,7 +2842,8 @@ static int ssl_tls13_write_new_session_ticket_body( mbedtls_ssl_context *ssl, * MAY treat a ticket as valid for a shorter period of time than what * is stated in the ticket_lifetime. */ - ticket_lifetime %= 604800; + if( ticket_lifetime > 604800 ) + ticket_lifetime = 604800; MBEDTLS_PUT_UINT32_BE( ticket_lifetime, p, 0 ); MBEDTLS_SSL_DEBUG_MSG( 3, ( "ticket_lifetime: %u", ( unsigned int )ticket_lifetime ) ); diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 6377162b2d..be474d4737 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -405,6 +405,7 @@ int main( void ) " exchanges=%%d default: 1\n" \ " reconnect=%%d number of reconnections using session resumption\n" \ " default: 0 (disabled)\n" \ + " reco_server_name=%%s default: localhost\n" \ " reco_delay=%%d default: 0 seconds\n" \ " reco_mode=%%d 0: copy session, 1: serialize session\n" \ " default: 1\n" \ @@ -498,6 +499,7 @@ struct options int recsplit; /* enable record splitting? */ int dhmlen; /* minimum DHM params len in bits */ int reconnect; /* attempt to resume session */ + const char *reco_server_name; /* hostname of the server (re-connect) */ int reco_delay; /* delay in seconds before resuming session */ int reco_mode; /* how to keep the session around */ int reconnect_hard; /* unexpectedly reconnect from the same port */ @@ -923,6 +925,7 @@ int main( int argc, char *argv[] ) opt.recsplit = DFL_RECSPLIT; opt.dhmlen = DFL_DHMLEN; opt.reconnect = DFL_RECONNECT; + opt.reco_server_name = DFL_SERVER_NAME; opt.reco_delay = DFL_RECO_DELAY; opt.reco_mode = DFL_RECO_MODE; opt.reconnect_hard = DFL_RECONNECT_HARD; @@ -1119,6 +1122,8 @@ int main( int argc, char *argv[] ) if( opt.reconnect < 0 || opt.reconnect > 2 ) goto usage; } + else if( strcmp( p, "rec_server_name" ) == 0 ) + opt.reco_server_name = q; else if( strcmp( p, "reco_delay" ) == 0 ) { opt.reco_delay = atoi( q ); @@ -3113,6 +3118,16 @@ reconnect: goto exit; } +#if defined(MBEDTLS_X509_CRT_PARSE_C) + if( ( ret = mbedtls_ssl_set_hostname( &ssl, + opt.reco_server_name ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", + ret ); + goto exit; + } +#endif + if( ( ret = mbedtls_net_connect( &server_fd, opt.server_addr, opt.server_port, opt.transport == MBEDTLS_SSL_TRANSPORT_STREAM ? diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 48dd89e357..f6437f5153 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -12873,6 +12873,47 @@ run_test "TLS 1.2: Check rsa_pss_rsae compatibility issue, m->G" \ -c "Protocol is TLSv1.2" \ -c "HTTP/1.0 200 [Oo][Kk]" +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 +requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS +requires_config_enabled MBEDTLS_SSL_SRV_C +requires_config_enabled MBEDTLS_SSL_CLI_C +requires_config_enabled MBEDTLS_DEBUG_C +run_test "TLS 1.3: NewSessionTicket: servername check, m->m" \ + "$P_SRV debug_level=4 crt_file=data_files/server5.crt key_file=data_files/server5.key force_version=tls13 tickets=4 \ + sni=localhost,data_files/server2.crt,data_files/server2.key,-,-,-,polarssl.example,data_files/server1-nospace.crt,data_files/server1.key,-,-,-" \ + "$P_CLI debug_level=4 server_name=localhost reco_mode=1 reconnect=1" \ + 0 \ + -c "Protocol is TLSv1.3" \ + -c "got new session ticket." \ + -c "Saving session for reuse... ok" \ + -c "Reconnecting with saved session" \ + -c "HTTP/1.0 200 OK" \ + -s "=> write NewSessionTicket msg" \ + -s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET" \ + -s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET_FLUSH" \ + -s "key exchange mode: ephemeral" \ + -s "key exchange mode: psk_ephemeral" \ + -s "found pre_shared_key extension" + +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 +requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS +requires_config_enabled MBEDTLS_SSL_SRV_C +requires_config_enabled MBEDTLS_SSL_CLI_C +requires_config_enabled MBEDTLS_DEBUG_C +run_test "TLS 1.3: NewSessionTicket: servername negative check, m->m" \ + "$P_SRV debug_level=4 crt_file=data_files/server5.crt key_file=data_files/server5.key force_version=tls13 tickets=4 \ + sni=localhost,data_files/server2.crt,data_files/server2.key,-,-,-,polarssl.example,data_files/server1-nospace.crt,data_files/server1.key,-,-,-" \ + "$P_CLI debug_level=4 server_name=localhost rec_server_name=remote reco_mode=1 reconnect=1" \ + 1 \ + -c "Protocol is TLSv1.3" \ + -c "got new session ticket." \ + -c "Saving session for reuse... ok" \ + -c "Reconnecting with saved session" \ + -c "Hostname mismatch the session ticket, disable session resumption." \ + -s "=> write NewSessionTicket msg" \ + -s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET" \ + -s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET_FLUSH" + # Test heap memory usage after handshake requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 requires_config_enabled MBEDTLS_MEMORY_DEBUG