diff --git a/ChangeLog b/ChangeLog index e318bcb655..c43be03c57 100644 --- a/ChangeLog +++ b/ChangeLog @@ -35,6 +35,10 @@ Changes * It is now possible to #include a user-provided configuration file at the end of the default config.h by defining MBEDTLS_USER_CONFIG_FILE on the compiler's command line. + * Prepend a "thread identifier" to debug messages (issue pointed out by + Hugo Leisink) (#210). + * Add mbedtls_ssl_get_max_frag_len() to query the current maximum fragment + length. = mbed TLS 2.0.0 released 2015-07-13 diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 288627bf1a..d051035fc0 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -2027,6 +2027,26 @@ const char *mbedtls_ssl_get_version( const mbedtls_ssl_context *ssl ); */ int mbedtls_ssl_get_record_expansion( const mbedtls_ssl_context *ssl ); +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +/** + * \brief Return the maximum fragment length (payload, in bytes). + * This is the value negotiated with peer if any, + * or the locally configured value. + * + * \note With DTLS, \c mbedtls_ssl_write() will return an error if + * called with a larger length value. + * With TLS, \c mbedtls_ssl_write() will fragment the input if + * necessary and return the number of bytes written; it is up + * to the caller to call \c mbedtls_ssl_write() again in + * order to send the remaining bytes if any. + * + * \param ssl SSL context + * + * \return Current maximum fragment length. + */ +size_t mbedtls_ssl_get_max_frag_len( const mbedtls_ssl_context *ssl ); +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + #if defined(MBEDTLS_X509_CRT_PARSE_C) /** * \brief Return the peer certificate from the current connection @@ -2124,26 +2144,33 @@ int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl ); int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len ); /** - * \brief Write exactly 'len' application data bytes + * \brief Try to write exactly 'len' application data bytes + * + * \warning This function will do partial writes in some cases. If the + * return value is non-negative but less than length, the + * function must be called again with updated arguments: + * buf + ret, len - ret (if ret is the return value) until + * it returns a value equal to the last 'len' argument. * * \param ssl SSL context * \param buf buffer holding the data * \param len how many bytes must be written * - * \return the number of bytes written, - * or a negative error code. + * \return the number of bytes actually written (may be less than len), + * or MBEDTLS_ERR_SSL_WANT_WRITE of MBEDTLS_ERR_SSL_WANT_READ, + * or another negative error code. * - * \note When this function returns MBEDTLS_ERR_SSL_WANT_WRITE, + * \note When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ, * it must be called later with the *same* arguments, * until it returns a positive value. * * \note If the requested length is greater than the maximum * fragment length (either the built-in limit or the one set * or negotiated with the peer), then: - * - with TLS, less bytes than requested are written. (In - * order to write larger messages, this function should be - * called in a loop.) + * - with TLS, less bytes than requested are written. * - with DTLS, MBEDTLS_ERR_SSL_BAD_INPUT_DATA is returned. + * \c mbedtls_ssl_get_max_frag_len() may be used to query the + * active maximum fragment length. */ int mbedtls_ssl_write( mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len ); diff --git a/library/debug.c b/library/debug.c index 2220e33178..f9b8229715 100644 --- a/library/debug.c +++ b/library/debug.c @@ -43,6 +43,10 @@ #define mbedtls_snprintf snprintf #endif +#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && !defined(inline) +#define inline __inline +#endif + #define DEBUG_BUF_SIZE 512 static int debug_threshold = 0; @@ -52,6 +56,27 @@ void mbedtls_debug_set_threshold( int threshold ) debug_threshold = threshold; } +/* + * All calls to f_dbg must be made via this function + */ +static inline void debug_send_line( const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *str ) +{ + /* + * If in a threaded environment, we need a thread identifier. + * Since there is no portable way to get one, use the address of the ssl + * context instead, as it shouldn't be shared between threads. + */ +#if defined(MBEDTLS_THREADING_C) + char idstr[20 + DEBUG_BUF_SIZE]; /* 0x + 16 nibbles + ': ' */ + mbedtls_snprintf( idstr, sizeof( idstr ), "%p: %s", ssl, str ); + ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, idstr ); +#else + ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); +#endif +} + void mbedtls_debug_print_msg( const mbedtls_ssl_context *ssl, int level, const char *file, int line, const char *format, ... ) @@ -86,7 +111,7 @@ void mbedtls_debug_print_msg( const mbedtls_ssl_context *ssl, int level, str[ret + 1] = '\0'; } - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); + debug_send_line( ssl, level, file, line, str ); } void mbedtls_debug_print_ret( const mbedtls_ssl_context *ssl, int level, @@ -109,7 +134,7 @@ void mbedtls_debug_print_ret( const mbedtls_ssl_context *ssl, int level, mbedtls_snprintf( str, sizeof( str ), "%s() returned %d (-0x%04x)\n", text, ret, -ret ); - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); + debug_send_line( ssl, level, file, line, str ); } void mbedtls_debug_print_buf( const mbedtls_ssl_context *ssl, int level, @@ -126,7 +151,7 @@ void mbedtls_debug_print_buf( const mbedtls_ssl_context *ssl, int level, mbedtls_snprintf( str + idx, sizeof( str ) - idx, "dumping '%s' (%u bytes)\n", text, (unsigned int) len ); - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); + debug_send_line( ssl, level, file, line, str ); idx = 0; memset( txt, 0, sizeof( txt ) ); @@ -140,7 +165,7 @@ void mbedtls_debug_print_buf( const mbedtls_ssl_context *ssl, int level, if( i > 0 ) { mbedtls_snprintf( str + idx, sizeof( str ) - idx, " %s\n", txt ); - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); + debug_send_line( ssl, level, file, line, str ); idx = 0; memset( txt, 0, sizeof( txt ) ); @@ -162,7 +187,7 @@ void mbedtls_debug_print_buf( const mbedtls_ssl_context *ssl, int level, idx += mbedtls_snprintf( str + idx, sizeof( str ) - idx, " " ); mbedtls_snprintf( str + idx, sizeof( str ) - idx, " %s\n", txt ); - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); + debug_send_line( ssl, level, file, line, str ); } } @@ -207,7 +232,7 @@ void mbedtls_debug_print_mpi( const mbedtls_ssl_context *ssl, int level, mbedtls_snprintf( str + idx, sizeof( str ) - idx, "value of '%s' (%d bits) is:\n", text, (int) ( ( n * ( sizeof(mbedtls_mpi_uint) << 3 ) ) + j + 1 ) ); - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); + debug_send_line( ssl, level, file, line, str ); idx = 0; for( i = n + 1, j = 0; i > 0; i-- ) @@ -227,7 +252,7 @@ void mbedtls_debug_print_mpi( const mbedtls_ssl_context *ssl, int level, if( j > 0 ) { mbedtls_snprintf( str + idx, sizeof( str ) - idx, "\n" ); - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); + debug_send_line( ssl, level, file, line, str ); idx = 0; } } @@ -244,7 +269,7 @@ void mbedtls_debug_print_mpi( const mbedtls_ssl_context *ssl, int level, idx += mbedtls_snprintf( str + idx, sizeof( str ) - idx, " 00" ); mbedtls_snprintf( str + idx, sizeof( str ) - idx, "\n" ); - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); + debug_send_line( ssl, level, file, line, str ); } #endif /* MBEDTLS_BIGNUM_C */ @@ -261,7 +286,7 @@ static void debug_print_pk( const mbedtls_ssl_context *ssl, int level, if( mbedtls_pk_debug( pk, items ) != 0 ) { - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, + debug_send_line( ssl, level, file, line, "invalid PK context\n" ); return; } @@ -282,7 +307,7 @@ static void debug_print_pk( const mbedtls_ssl_context *ssl, int level, mbedtls_debug_print_ecp( ssl, level, file, line, name, items[i].value ); else #endif - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, + debug_send_line( ssl, level, file, line, "should not happen\n" ); } } @@ -305,7 +330,7 @@ static void debug_print_line_by_line( const mbedtls_ssl_context *ssl, int level, memcpy( str, start, len ); str[len] = '\0'; - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); + debug_send_line( ssl, level, file, line, str ); start = cur + 1; } @@ -327,7 +352,7 @@ void mbedtls_debug_print_crt( const mbedtls_ssl_context *ssl, int level, char buf[1024]; mbedtls_snprintf( str, sizeof( str ), "%s #%d:\n", text, ++i ); - ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); + debug_send_line( ssl, level, file, line, str ); mbedtls_x509_crt_info( buf, sizeof( buf ) - 1, "", crt ); debug_print_line_by_line( ssl, level, file, line, buf ); diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 9007562fb5..552a099f79 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3718,6 +3718,9 @@ int mbedtls_ssl_send_alert_message( mbedtls_ssl_context *ssl, { int ret; + if( ssl == NULL || ssl->conf == NULL ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> send alert message" ) ); ssl->out_msgtype = MBEDTLS_SSL_MSG_ALERT; @@ -5862,6 +5865,29 @@ int mbedtls_ssl_get_record_expansion( const mbedtls_ssl_context *ssl ) return( (int)( mbedtls_ssl_hdr_len( ssl ) + transform_expansion ) ); } +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +size_t mbedtls_ssl_get_max_frag_len( const mbedtls_ssl_context *ssl ) +{ + size_t max_len; + + /* + * Assume mfl_code is correct since it was checked when set + */ + max_len = mfl_code_to_length[ssl->conf->mfl_code]; + + /* + * Check if a smaller max length was negotiated + */ + if( ssl->session_out != NULL && + mfl_code_to_length[ssl->session_out->mfl_code] < max_len ) + { + max_len = mfl_code_to_length[ssl->session_out->mfl_code]; + } + + return max_len; +} +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + #if defined(MBEDTLS_X509_CRT_PARSE_C) const mbedtls_x509_crt *mbedtls_ssl_get_peer_cert( const mbedtls_ssl_context *ssl ) { @@ -5894,6 +5920,9 @@ int mbedtls_ssl_handshake_step( mbedtls_ssl_context *ssl ) { int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + if( ssl == NULL || ssl->conf == NULL ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + #if defined(MBEDTLS_SSL_CLI_C) if( ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT ) ret = mbedtls_ssl_handshake_client_step( ssl ); @@ -5913,6 +5942,9 @@ int mbedtls_ssl_handshake( mbedtls_ssl_context *ssl ) { int ret = 0; + if( ssl == NULL || ssl->conf == NULL ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> handshake" ) ); while( ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER ) @@ -6008,6 +6040,9 @@ int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl ) { int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + if( ssl == NULL || ssl->conf == NULL ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + #if defined(MBEDTLS_SSL_SRV_C) /* On server, just send the request */ if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER ) @@ -6085,6 +6120,9 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len ) int ret, record_read = 0; size_t n; + if( ssl == NULL || ssl->conf == NULL ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> read" ) ); #if defined(MBEDTLS_SSL_PROTO_DTLS) @@ -6339,23 +6377,7 @@ static int ssl_write_real( mbedtls_ssl_context *ssl, { int ret; #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) - unsigned int max_len; -#endif - -#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) - /* - * Assume mfl_code is correct since it was checked when set - */ - max_len = mfl_code_to_length[ssl->conf->mfl_code]; - - /* - * Check if a smaller max length was negotiated - */ - if( ssl->session_out != NULL && - mfl_code_to_length[ssl->session_out->mfl_code] < max_len ) - { - max_len = mfl_code_to_length[ssl->session_out->mfl_code]; - } + size_t max_len = mbedtls_ssl_get_max_frag_len( ssl ); if( len > max_len ) { @@ -6444,6 +6466,9 @@ int mbedtls_ssl_write( mbedtls_ssl_context *ssl, const unsigned char *buf, size_ MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write" ) ); + if( ssl == NULL || ssl->conf == NULL ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + #if defined(MBEDTLS_SSL_RENEGOTIATION) if( ( ret = ssl_check_ctr_renegotiate( ssl ) ) != 0 ) { @@ -6479,6 +6504,9 @@ int mbedtls_ssl_close_notify( mbedtls_ssl_context *ssl ) { int ret; + if( ssl == NULL || ssl->conf == NULL ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write close notify" ) ); if( ssl->out_left != 0 ) diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 2e0ac1edc4..c60e7100fd 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -1240,6 +1240,11 @@ int main( int argc, char *argv[] ) else mbedtls_printf( " [ Record expansion is unknown (compression) ]\n" ); +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + mbedtls_printf( " [ Maximum fragment length is %u ]\n", + (unsigned int) mbedtls_ssl_get_max_frag_len( &ssl ) ); +#endif + #if defined(MBEDTLS_SSL_ALPN) if( opt.alpn_string != NULL ) { diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 3aa05d5f17..e4940b889c 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -1946,6 +1946,11 @@ reset: else mbedtls_printf( " [ Record expansion is unknown (compression) ]\n" ); +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + mbedtls_printf( " [ Maximum fragment length is %u ]\n", + (unsigned int) mbedtls_ssl_get_max_frag_len( &ssl ) ); +#endif + #if defined(MBEDTLS_SSL_ALPN) if( opt.alpn_string != NULL ) { diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index bd36e227b7..77db588dbc 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -1085,6 +1085,8 @@ run_test "Max fragment length: not used, reference" \ "$P_SRV debug_level=3" \ "$P_CLI debug_level=3" \ 0 \ + -c "Maximum fragment length is 16384" \ + -s "Maximum fragment length is 16384" \ -C "client hello, adding max_fragment_length extension" \ -S "found max fragment length extension" \ -S "server hello, max_fragment_length extension" \ @@ -1094,6 +1096,8 @@ run_test "Max fragment length: used by client" \ "$P_SRV debug_level=3" \ "$P_CLI debug_level=3 max_frag_len=4096" \ 0 \ + -c "Maximum fragment length is 4096" \ + -s "Maximum fragment length is 4096" \ -c "client hello, adding max_fragment_length extension" \ -s "found max fragment length extension" \ -s "server hello, max_fragment_length extension" \ @@ -1103,6 +1107,8 @@ run_test "Max fragment length: used by server" \ "$P_SRV debug_level=3 max_frag_len=4096" \ "$P_CLI debug_level=3" \ 0 \ + -c "Maximum fragment length is 16384" \ + -s "Maximum fragment length is 4096" \ -C "client hello, adding max_fragment_length extension" \ -S "found max fragment length extension" \ -S "server hello, max_fragment_length extension" \ @@ -1113,6 +1119,7 @@ run_test "Max fragment length: gnutls server" \ "$G_SRV" \ "$P_CLI debug_level=3 max_frag_len=4096" \ 0 \ + -c "Maximum fragment length is 4096" \ -c "client hello, adding max_fragment_length extension" \ -c "found max_fragment_length extension" @@ -1120,6 +1127,8 @@ run_test "Max fragment length: client, message just fits" \ "$P_SRV debug_level=3" \ "$P_CLI debug_level=3 max_frag_len=2048 request_size=2048" \ 0 \ + -c "Maximum fragment length is 2048" \ + -s "Maximum fragment length is 2048" \ -c "client hello, adding max_fragment_length extension" \ -s "found max fragment length extension" \ -s "server hello, max_fragment_length extension" \ @@ -1131,6 +1140,8 @@ run_test "Max fragment length: client, larger message" \ "$P_SRV debug_level=3" \ "$P_CLI debug_level=3 max_frag_len=2048 request_size=2345" \ 0 \ + -c "Maximum fragment length is 2048" \ + -s "Maximum fragment length is 2048" \ -c "client hello, adding max_fragment_length extension" \ -s "found max fragment length extension" \ -s "server hello, max_fragment_length extension" \ @@ -1143,6 +1154,8 @@ run_test "Max fragment length: DTLS client, larger message" \ "$P_SRV debug_level=3 dtls=1" \ "$P_CLI debug_level=3 dtls=1 max_frag_len=2048 request_size=2345" \ 1 \ + -c "Maximum fragment length is 2048" \ + -s "Maximum fragment length is 2048" \ -c "client hello, adding max_fragment_length extension" \ -s "found max fragment length extension" \ -s "server hello, max_fragment_length extension" \ diff --git a/tests/suites/test_suite_debug.function b/tests/suites/test_suite_debug.function index 3c77ca56bc..98f98b061b 100644 --- a/tests/suites/test_suite_debug.function +++ b/tests/suites/test_suite_debug.function @@ -25,6 +25,11 @@ void string_debug(void *data, int level, const char *file, int line, const char *p++ = ':'; *p++ = ' '; +#if defined(MBEDTLS_THREADING_C) + /* Skip "thread ID" (up to the first space) as it is not predictable */ + while( *str++ != ' ' ); +#endif + memcpy( p, str, strlen( str ) ); p += strlen( str );