diff --git a/ChangeLog b/ChangeLog index be3861416..915c718da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ mbed TLS ChangeLog (Sorted per branch, date) -= mbed TLS 2.1.1 released 2015-09-?? += mbed TLS 2.1.1 released 2015-09-16 Bugfix * Fix warning when using a 64bit platform. (found by embedthis) (#275) @@ -10,6 +10,13 @@ Bugfix Changes * Made X509 profile pointer const in mbedtls_ssl_conf_cert_profile() to allow use of mbedtls_x509_crt_profile_next. (found by NWilson) + * When a client initiates a reconnect from the same port as a live + connection, if cookie verification is available + (MBEDTLS_SSL_DTLS_HELLO_VERIFY defined in config.h, and usable cookie + callbacks set with mbedtls_ssl_conf_dtls_cookies()), this will be + detected and mbedtls_ssl_read() will return + MBEDTLS_ERR_SSL_CLIENT_RECONNECT - it is then possible to start a new + handshake with the same context. (See RFC 6347 section 4.2.8.) = mbed TLS 2.1.0 released 2015-09-04 diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h index cb707e9bc..8dadbe1c5 100644 --- a/include/mbedtls/check_config.h +++ b/include/mbedtls/check_config.h @@ -421,6 +421,11 @@ #error "MBEDTLS_SSL_DTLS_HELLO_VERIFY defined, but not all prerequisites" #endif +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) && \ + !defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) +#error "MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE defined, but not all prerequisites" +#endif + #if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) && \ ( !defined(MBEDTLS_SSL_TLS_C) || !defined(MBEDTLS_SSL_PROTO_DTLS) ) #error "MBEDTLS_SSL_DTLS_ANTI_REPLAY defined, but not all prerequisites" diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h index 66657da55..6e9d8f3df 100644 --- a/include/mbedtls/config.h +++ b/include/mbedtls/config.h @@ -1134,6 +1134,22 @@ */ #define MBEDTLS_SSL_DTLS_HELLO_VERIFY +/** + * \def MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE + * + * Enable server-side support for clients that reconnect from the same port. + * + * Some clients unexpectedly close the connection and try to reconnect using the + * same source port. This needs special support from the server to handle the + * new connection securely, as described in section 4.2.8 of RFC 6347. This + * flag enables that support. + * + * Requires: MBEDTLS_SSL_DTLS_HELLO_VERIFY + * + * Comment this to disable support for clients reusing the source port. + */ +#define MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE + /** * \def MBEDTLS_SSL_DTLS_BADMAC_LIMIT * diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 907bba181..2d7beb359 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -125,6 +125,7 @@ #define MBEDTLS_ERR_SSL_WANT_READ -0x6900 /**< Connection requires a read call. */ #define MBEDTLS_ERR_SSL_WANT_WRITE -0x6880 /**< Connection requires a write call. */ #define MBEDTLS_ERR_SSL_TIMEOUT -0x6800 /**< The operation timed out. */ +#define MBEDTLS_ERR_SSL_CLIENT_RECONNECT -0x6780 /**< The client initiated a reconnect from the same port. */ /* * Various constants @@ -1169,6 +1170,11 @@ typedef int mbedtls_ssl_cookie_check_t( void *ctx, * the MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED that is expected * on the first handshake attempt when this is enabled. * + * \note This is also necessary to handle client reconnection from + * the same port as described in RFC 6347 section 4.2.8 (only + * the variant with cookies is supported currently). See + * comments on \c mbedtls_ssl_read() for details. + * * \param conf SSL configuration * \param f_cookie_write Cookie write callback * \param f_cookie_check Cookie check callback @@ -2089,29 +2095,35 @@ int mbedtls_ssl_get_session( const mbedtls_ssl_context *ssl, mbedtls_ssl_session * * \param ssl SSL context * - * \return 0 if successful, MBEDTLS_ERR_SSL_WANT_READ, - * MBEDTLS_ERR_SSL_WANT_WRITE, or a specific SSL error code. + * \return 0 if successful, or + * MBEDTLS_ERR_SSL_WANT_READ or MBEDTLS_ERR_SSL_WANT_WRITE, or + * MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED (see below), or + * a specific SSL error code. * - * \note If this function returns non-zero, then the ssl context + * \note If this function returns something other than 0 or + * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context * becomes unusable, and you should either free it or call * \c mbedtls_ssl_session_reset() on it before re-using it. - * If DTLS is in use, then you may choose to handle + * + * \note If DTLS is in use, then you may choose to handle * MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED specially for logging - * purposes, but you still need to reset/free the context. + * purposes, as it is an expected return value rather than an + * actual error, but you still need to reset/free the context. */ int mbedtls_ssl_handshake( mbedtls_ssl_context *ssl ); /** * \brief Perform a single step of the SSL handshake * - * Note: the state of the context (ssl->state) will be at + * \note The state of the context (ssl->state) will be at * the following state after execution of this function. * Do not call this function if state is MBEDTLS_SSL_HANDSHAKE_OVER. * * \param ssl SSL context * - * \return 0 if successful, MBEDTLS_ERR_SSL_WANT_READ, - * MBEDTLS_ERR_SSL_WANT_WRITE, or a specific SSL error code. + * \return 0 if successful, or + * MBEDTLS_ERR_SSL_WANT_READ or MBEDTLS_ERR_SSL_WANT_WRITE, or + * a specific SSL error code. */ int mbedtls_ssl_handshake_step( mbedtls_ssl_context *ssl ); @@ -2138,7 +2150,23 @@ int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl ); * * \return the number of bytes read, or * 0 for EOF, or - * a negative error code. + * MBEDTLS_ERR_SSL_WANT_READ or MBEDTLS_ERR_SSL_WANT_WRITE, or + * MBEDTLS_ERR_SSL_CLIENT_RECONNECT (see below), or + * another negative error code. + * + * \note When this function return MBEDTLS_ERR_SSL_CLIENT_RECONNECT + * (which can only happen server-side), it means that a client + * is initiating a new connection using the same source port. + * You can either treat that as a connection close and wait + * for the client to resend a ClientHello, or directly + * continue with \c mbedtls_ssl_handshake() with the same + * context (as it has beeen reset internally). Either way, you + * should make sure this is seen by the application as a new + * connection: application state, if any, should be reset, and + * most importantly the identity of the client must be checked + * again. WARNING: not validating the identity of the client + * again, or not transmitting the new identity to the + * application layer, would allow authentication bypass! */ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len ); diff --git a/library/error.c b/library/error.c index a7de11007..a1cf83aed 100644 --- a/library/error.c +++ b/library/error.c @@ -428,6 +428,8 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen ) mbedtls_snprintf( buf, buflen, "SSL - Connection requires a write call" ); if( use_ret == -(MBEDTLS_ERR_SSL_TIMEOUT) ) mbedtls_snprintf( buf, buflen, "SSL - The operation timed out" ); + if( use_ret == -(MBEDTLS_ERR_SSL_CLIENT_RECONNECT) ) + mbedtls_snprintf( buf, buflen, "SSL - The client initiated a reconnect from the same port" ); #endif /* MBEDTLS_SSL_TLS_C */ #if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index f1d2dd201..d9b05fd1f 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3250,6 +3250,196 @@ void mbedtls_ssl_dtls_replay_update( mbedtls_ssl_context *ssl ) } #endif /* MBEDTLS_SSL_DTLS_ANTI_REPLAY */ +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) && defined(MBEDTLS_SSL_SRV_C) +/* Forward declaration */ +static int ssl_session_reset_int( mbedtls_ssl_context *ssl, int partial ); + +/* + * Without any SSL context, check if a datagram looks like a ClientHello with + * a valid cookie, and if it doesn't, generate a HelloVerifyRequest message. + * Both input and output include full DTLS headers. + * + * - if cookie is valid, return 0 + * - if ClientHello looks superficially valid but cookie is not, + * fill obuf and set olen, then + * return MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED + * - otherwise return a specific error code + */ +static int ssl_check_dtls_clihlo_cookie( + mbedtls_ssl_cookie_write_t *f_cookie_write, + mbedtls_ssl_cookie_check_t *f_cookie_check, + void *p_cookie, + const unsigned char *cli_id, size_t cli_id_len, + const unsigned char *in, size_t in_len, + unsigned char *obuf, size_t buf_len, size_t *olen ) +{ + size_t sid_len, cookie_len; + unsigned char *p; + + if( f_cookie_write == NULL || f_cookie_check == NULL ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + + /* + * Structure of ClientHello with record and handshake headers, + * and expected values. We don't need to check a lot, more checks will be + * done when actually parsing the ClientHello - skipping those checks + * avoids code duplication and does not make cookie forging any easier. + * + * 0-0 ContentType type; copied, must be handshake + * 1-2 ProtocolVersion version; copied + * 3-4 uint16 epoch; copied, must be 0 + * 5-10 uint48 sequence_number; copied + * 11-12 uint16 length; (ignored) + * + * 13-13 HandshakeType msg_type; (ignored) + * 14-16 uint24 length; (ignored) + * 17-18 uint16 message_seq; copied + * 19-21 uint24 fragment_offset; copied, must be 0 + * 22-24 uint24 fragment_length; (ignored) + * + * 25-26 ProtocolVersion client_version; (ignored) + * 27-58 Random random; (ignored) + * 59-xx SessionID session_id; 1 byte len + sid_len content + * 60+ opaque cookie<0..2^8-1>; 1 byte len + content + * ... + * + * Minimum length is 61 bytes. + */ + if( in_len < 61 || + in[0] != MBEDTLS_SSL_MSG_HANDSHAKE || + in[3] != 0 || in[4] != 0 || + in[19] != 0 || in[20] != 0 || in[21] != 0 ) + { + return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + sid_len = in[59]; + if( sid_len > in_len - 61 ) + return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO ); + + cookie_len = in[60 + sid_len]; + if( cookie_len > in_len - 60 ) + return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO ); + + if( f_cookie_check( p_cookie, in + sid_len + 61, cookie_len, + cli_id, cli_id_len ) == 0 ) + { + /* Valid cookie */ + return( 0 ); + } + + /* + * If we get here, we've got an invalid cookie, let's prepare HVR. + * + * 0-0 ContentType type; copied + * 1-2 ProtocolVersion version; copied + * 3-4 uint16 epoch; copied + * 5-10 uint48 sequence_number; copied + * 11-12 uint16 length; olen - 13 + * + * 13-13 HandshakeType msg_type; hello_verify_request + * 14-16 uint24 length; olen - 25 + * 17-18 uint16 message_seq; copied + * 19-21 uint24 fragment_offset; copied + * 22-24 uint24 fragment_length; olen - 25 + * + * 25-26 ProtocolVersion server_version; 0xfe 0xff + * 27-27 opaque cookie<0..2^8-1>; cookie_len = olen - 27, cookie + * + * Minimum length is 28. + */ + if( buf_len < 28 ) + return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); + + /* Copy most fields and adapt others */ + memcpy( obuf, in, 25 ); + obuf[13] = MBEDTLS_SSL_HS_HELLO_VERIFY_REQUEST; + obuf[25] = 0xfe; + obuf[26] = 0xff; + + /* Generate and write actual cookie */ + p = obuf + 28; + if( f_cookie_write( p_cookie, + &p, obuf + buf_len, cli_id, cli_id_len ) != 0 ) + { + return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); + } + + *olen = p - obuf; + + /* Go back and fill length fields */ + obuf[27] = (unsigned char)( *olen - 28 ); + + obuf[14] = obuf[22] = (unsigned char)( ( *olen - 25 ) >> 16 ); + obuf[15] = obuf[23] = (unsigned char)( ( *olen - 25 ) >> 8 ); + obuf[16] = obuf[24] = (unsigned char)( ( *olen - 25 ) ); + + obuf[11] = (unsigned char)( ( *olen - 13 ) >> 8 ); + obuf[12] = (unsigned char)( ( *olen - 13 ) ); + + return( MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED ); +} + +/* + * Handle possible client reconnect with the same UDP quadruplet + * (RFC 6347 Section 4.2.8). + * + * Called by ssl_parse_record_header() in case we receive an epoch 0 record + * 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 + * - 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. + */ +static int ssl_handle_possible_reconnect( mbedtls_ssl_context *ssl ) +{ + int ret; + size_t len; + + ret = ssl_check_dtls_clihlo_cookie( + ssl->conf->f_cookie_write, + ssl->conf->f_cookie_check, + ssl->conf->p_cookie, + ssl->cli_id, ssl->cli_id_len, + ssl->in_buf, ssl->in_left, + ssl->out_buf, MBEDTLS_SSL_MAX_CONTENT_LEN, &len ); + + MBEDTLS_SSL_DEBUG_RET( 2, "ssl_check_dtls_clihlo_cookie", ret ); + + if( ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED ) + { + /* Dont 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 ); + + return( MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED ); + } + + if( ret == 0 ) + { + /* Got a valid cookie, partially reset context */ + if( ( ret = ssl_session_reset_int( ssl, 1 ) ) != 0 ) + { + MBEDTLS_SSL_DEBUG_RET( 1, "reset", ret ); + return( ret ); + } + + return( MBEDTLS_ERR_SSL_CLIENT_RECONNECT ); + } + + return( ret ); +} +#endif /* MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE && MBEDTLS_SSL_SRV_C */ + /* * ContentType type; * ProtocolVersion version; @@ -3341,13 +3531,36 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl ) if( rec_epoch != ssl->in_epoch ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "record from another epoch: " - "expected %d, received %d", - ssl->in_epoch, rec_epoch ) ); - return( MBEDTLS_ERR_SSL_INVALID_RECORD ); + "expected %d, received %d", + ssl->in_epoch, rec_epoch ) ); + +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) && defined(MBEDTLS_SSL_SRV_C) + /* + * Check for an epoch 0 ClientHello. We can't use in_msg here to + * access the first byte of record content (handshake type), as we + * have an active transform (possibly iv_len != 0), so use the + * fact that the record header len is 13 instead. + */ + if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER && + ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER && + rec_epoch == 0 && + ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE && + ssl->in_left > 13 && + ssl->in_buf[13] == MBEDTLS_SSL_HS_CLIENT_HELLO ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "possible client reconnect " + "from the same port" ) ); + return( ssl_handle_possible_reconnect( ssl ) ); + } + else +#endif /* MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE && MBEDTLS_SSL_SRV_C */ + return( MBEDTLS_ERR_SSL_INVALID_RECORD ); } #if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) - if( mbedtls_ssl_dtls_replay_check( ssl ) != 0 ) + /* Replay detection only works for the current epoch */ + if( rec_epoch == ssl->in_epoch && + mbedtls_ssl_dtls_replay_check( ssl ) != 0 ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "replayed record" ) ); return( MBEDTLS_ERR_SSL_INVALID_RECORD ); @@ -3528,7 +3741,8 @@ read_record_header: if( ( ret = ssl_parse_record_header( ssl ) ) != 0 ) { #if defined(MBEDTLS_SSL_PROTO_DTLS) - if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM ) + if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ret != MBEDTLS_ERR_SSL_CLIENT_RECONNECT ) { /* Ignore bad record and get next one; drop the whole datagram * since current header cannot be trusted to find the next record @@ -5123,8 +5337,11 @@ int mbedtls_ssl_setup( mbedtls_ssl_context *ssl, /* * Reset an initialized and used SSL context for re-use while retaining * all application-set variables, function pointers and data. + * + * If partial is non-zero, keep data in the input buffer and client ID. + * (Use when a DTLS client reconnects from the same port.) */ -int mbedtls_ssl_session_reset( mbedtls_ssl_context *ssl ) +static int ssl_session_reset_int( mbedtls_ssl_context *ssl, int partial ) { int ret; @@ -5148,7 +5365,8 @@ int mbedtls_ssl_session_reset( mbedtls_ssl_context *ssl ) ssl->in_msg = ssl->in_buf + 13; ssl->in_msgtype = 0; ssl->in_msglen = 0; - ssl->in_left = 0; + if( partial == 0 ) + ssl->in_left = 0; #if defined(MBEDTLS_SSL_PROTO_DTLS) ssl->next_record_offset = 0; ssl->in_epoch = 0; @@ -5174,7 +5392,8 @@ int mbedtls_ssl_session_reset( mbedtls_ssl_context *ssl ) ssl->transform_out = NULL; memset( ssl->out_buf, 0, MBEDTLS_SSL_BUFFER_LEN ); - memset( ssl->in_buf, 0, MBEDTLS_SSL_BUFFER_LEN ); + if( partial == 0 ) + memset( ssl->in_buf, 0, MBEDTLS_SSL_BUFFER_LEN ); #if defined(MBEDTLS_SSL_HW_RECORD_ACCEL) if( mbedtls_ssl_hw_record_reset != NULL ) @@ -5207,9 +5426,12 @@ int mbedtls_ssl_session_reset( mbedtls_ssl_context *ssl ) #endif #if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && defined(MBEDTLS_SSL_SRV_C) - mbedtls_free( ssl->cli_id ); - ssl->cli_id = NULL; - ssl->cli_id_len = 0; + if( partial == 0 ) + { + mbedtls_free( ssl->cli_id ); + ssl->cli_id = NULL; + ssl->cli_id_len = 0; + } #endif if( ( ret = ssl_handshake_init( ssl ) ) != 0 ) @@ -5218,6 +5440,15 @@ int mbedtls_ssl_session_reset( mbedtls_ssl_context *ssl ) return( 0 ); } +/* + * Reset an initialized and used SSL context for re-use while retaining + * all application-set variables, function pointers and data. + */ +int mbedtls_ssl_session_reset( mbedtls_ssl_context *ssl ) +{ + return( ssl_session_reset_int( ssl, 0 ) ); +} + /* * SSL set accessors */ diff --git a/library/version_features.c b/library/version_features.c index c2f30f21c..196b93c88 100644 --- a/library/version_features.c +++ b/library/version_features.c @@ -369,6 +369,9 @@ static const char *features[] = { #if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) "MBEDTLS_SSL_DTLS_HELLO_VERIFY", #endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY */ +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) + "MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE", +#endif /* MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE */ #if defined(MBEDTLS_SSL_DTLS_BADMAC_LIMIT) "MBEDTLS_SSL_DTLS_BADMAC_LIMIT", #endif /* MBEDTLS_SSL_DTLS_BADMAC_LIMIT */ diff --git a/programs/pkey/pk_sign.c b/programs/pkey/pk_sign.c index 606fc93d7..322e8aff0 100644 --- a/programs/pkey/pk_sign.c +++ b/programs/pkey/pk_sign.c @@ -40,7 +40,7 @@ int main( void ) { mbedtls_printf("MBEDTLS_BIGNUM_C and/or MBEDTLS_ENTROPY_C and/or " - "MBEDTLS_SHA256_C and/or MBEDLTS_MD_C and/or " + "MBEDTLS_SHA256_C and/or MBEDTLS_MD_C and/or " "MBEDTLS_PK_PARSE_C and/or MBEDTLS_FS_IO and/or " "MBEDTLS_CTR_DRBG_C not defined.\n"); return( 0 ); diff --git a/programs/pkey/rsa_sign.c b/programs/pkey/rsa_sign.c index 469ad9f5d..e897c6519 100644 --- a/programs/pkey/rsa_sign.c +++ b/programs/pkey/rsa_sign.c @@ -40,7 +40,7 @@ int main( void ) { mbedtls_printf("MBEDTLS_BIGNUM_C and/or MBEDTLS_RSA_C and/or " - "MBEDLTS_MD_C and/or " + "MBEDTLS_MD_C and/or " "MBEDTLS_SHA256_C and/or MBEDTLS_FS_IO not defined.\n"); return( 0 ); } diff --git a/programs/pkey/rsa_verify.c b/programs/pkey/rsa_verify.c index 9d48a18ee..ade36dc83 100644 --- a/programs/pkey/rsa_verify.c +++ b/programs/pkey/rsa_verify.c @@ -39,7 +39,7 @@ int main( void ) { mbedtls_printf("MBEDTLS_BIGNUM_C and/or MBEDTLS_RSA_C and/or " - "MBEDLTS_MD_C and/or " + "MBEDTLS_MD_C and/or " "MBEDTLS_SHA256_C and/or MBEDTLS_FS_IO not defined.\n"); return( 0 ); } diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 99c2d2a5e..c090db909 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -90,6 +90,7 @@ int main( void ) #define DFL_DHMLEN -1 #define DFL_RECONNECT 0 #define DFL_RECO_DELAY 0 +#define DFL_RECONNECT_HARD 0 #define DFL_TICKETS MBEDTLS_SSL_SESSION_TICKETS_ENABLED #define DFL_ALPN_STRING NULL #define DFL_TRANSPORT MBEDTLS_SSL_TRANSPORT_STREAM @@ -222,7 +223,7 @@ int main( void ) " debug_level=%%d default: 0 (disabled)\n" \ " nbio=%%d default: 0 (blocking I/O)\n" \ " options: 1 (non-blocking), 2 (added delays)\n" \ - " read_timeout=%%d default: 0 (no timeout)\n" \ + " read_timeout=%%d default: 0 ms (no timeout)\n" \ " max_resend=%%d default: 0 (no resend on timeout)\n" \ "\n" \ USAGE_DTLS \ @@ -238,6 +239,7 @@ int main( void ) " exchanges=%%d default: 1\n" \ " reconnect=%%d default: 0 (disabled)\n" \ " reco_delay=%%d default: 0 seconds\n" \ + " reconnect_hard=%%d default: 0 (disabled)\n" \ USAGE_TICKETS \ USAGE_MAX_FRAG_LEN \ USAGE_TRUNC_HMAC \ @@ -293,6 +295,7 @@ struct options int dhmlen; /* minimum DHM params len in bits */ int reconnect; /* attempt to resume session */ int reco_delay; /* delay in seconds before resuming session */ + int reconnect_hard; /* unexpectedly reconnect from the same port */ int tickets; /* enable / disable session tickets */ const char *alpn_string; /* ALPN supported protocols */ int transport; /* TLS or DTLS? */ @@ -481,6 +484,7 @@ int main( int argc, char *argv[] ) opt.dhmlen = DFL_DHMLEN; opt.reconnect = DFL_RECONNECT; opt.reco_delay = DFL_RECO_DELAY; + opt.reconnect_hard = DFL_RECONNECT_HARD; opt.tickets = DFL_TICKETS; opt.alpn_string = DFL_ALPN_STRING; opt.transport = DFL_TRANSPORT; @@ -603,6 +607,12 @@ int main( int argc, char *argv[] ) if( opt.reco_delay < 0 ) goto usage; } + else if( strcmp( p, "reconnect_hard" ) == 0 ) + { + opt.reconnect_hard = atoi( q ); + if( opt.reconnect_hard < 0 || opt.reconnect_hard > 1 ) + goto usage; + } else if( strcmp( p, "tickets" ) == 0 ) { opt.tickets = atoi( q ); @@ -1479,7 +1489,38 @@ send_request: } /* - * 7b. Continue doing data exchanges? + * 7b. Simulate hard reset and reconnect from same port? + */ + if( opt.reconnect_hard != 0 ) + { + opt.reconnect_hard = 0; + + mbedtls_printf( " . Restarting connection from same port..." ); + fflush( stdout ); + + if( ( ret = mbedtls_ssl_session_reset( &ssl ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n", -ret ); + goto exit; + } + + while( ( ret = mbedtls_ssl_handshake( &ssl ) ) != 0 ) + { + if( ret != MBEDTLS_ERR_SSL_WANT_READ && + ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", -ret ); + goto exit; + } + } + + mbedtls_printf( " ok\n" ); + + goto send_request; + } + + /* + * 7c. Continue doing data exchanges? */ if( --opt.exchanges > 0 ) goto send_request; @@ -1489,6 +1530,7 @@ send_request: */ close_notify: mbedtls_printf( " . Closing the connection..." ); + fflush( stdout ); /* No error checking, the connection might be closed already */ do ret = mbedtls_ssl_close_notify( &ssl ); @@ -1513,7 +1555,6 @@ reconnect: #endif mbedtls_printf( " . Reconnecting with saved session..." ); - fflush( stdout ); if( ( ret = mbedtls_ssl_session_reset( &ssl ) ) != 0 ) { diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index f169291eb..bd4d1d1b4 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -301,7 +301,7 @@ int main( void ) " debug_level=%%d default: 0 (disabled)\n" \ " nbio=%%d default: 0 (blocking I/O)\n" \ " options: 1 (non-blocking), 2 (added delays)\n" \ - " read_timeout=%%d default: 0 (no timeout)\n" \ + " read_timeout=%%d default: 0 ms (no timeout)\n" \ "\n" \ USAGE_DTLS \ USAGE_COOKIES \ @@ -1838,6 +1838,12 @@ reset: } #endif + if( ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT ) + { + mbedtls_printf( " ! Client initiated reconnection from same port\n" ); + goto handshake; + } + #ifdef MBEDTLS_ERROR_C if( ret != 0 ) { @@ -1903,6 +1909,7 @@ reset: /* * 4. Handshake */ +handshake: mbedtls_printf( " . Performing the SSL/TLS handshake..." ); fflush( stdout ); diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 77db588db..e49441301 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -2867,6 +2867,40 @@ run_test "DTLS cookie: enabled, nbio" \ -s "hello verification requested" \ -S "SSL - The requested feature is not available" +# Tests for client reconnecting from the same port with DTLS + +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" \ + 0 \ + -C "resend" \ + -S "The operation timed out" \ + -S "Client initiated reconnection from same port" + +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" \ + 0 \ + -C "resend" \ + -S "The operation timed out" \ + -s "Client initiated reconnection from same port" + +run_test "DTLS client reconnect from same port: reconnect, nbio" \ + "$P_SRV dtls=1 exchanges=2 read_timeout=1000 nbio=2" \ + "$P_CLI dtls=1 exchanges=2 debug_level=2 hs_timeout=500-1000 reconnect_hard=1" \ + 0 \ + -S "The operation timed out" \ + -s "Client initiated reconnection from same port" + +run_test "DTLS client reconnect from same port: no cookies" \ + "$P_SRV dtls=1 exchanges=2 read_timeout=1000 cookies=0" \ + "$P_CLI dtls=1 exchanges=2 debug_level=2 hs_timeout=500-8000 reconnect_hard=1" \ + 0 \ + -s "The operation timed out" \ + -S "Client initiated reconnection from same port" + # Tests for various cases of client authentication with DTLS # (focused on handshake flows and message parsing)