diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 3d6e42c802..dc2619ad04 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -122,8 +122,9 @@ #define SSL_RENEGOTIATION_ENABLED 0 #define SSL_RENEGOTIATION_DISABLED 1 -#define SSL_NO_LEGACY_RENEGOTIATION 0 -#define SSL_ALLOW_LEGACY_RENEGOTIATION 1 +#define SSL_LEGACY_NO_RENEGOTIATION 0 +#define SSL_LEGACY_ALLOW_RENEGOTIATION 1 +#define SSL_LEGACY_BREAK_HANDSHAKE 2 #define SSL_MAX_CONTENT_LEN 16384 @@ -758,10 +759,25 @@ void ssl_set_renegotiation( ssl_context *ssl, int renegotiation ); /** * \brief Prevent or allow legacy renegotiation. - * (Default: SSL_NO_LEGACY_RENEGOTIATION) - * Allowing legacy renegotiation makes the connection - * vulnerable to specific man in the middle attacks. - * (See RFC 5746) + * (Default: SSL_LEGACY_NO_RENEGOTIATION) + * + * SSL_LEGACY_NO_RENEGOTIATION allows connections to + * be established even if the peer does not support + * secure renegotiation, but does not allow renegotiation + * to take place if not secure. + * (Interoperable and secure option) + * + * SSL_LEGACY_ALLOW_RENEGOTIATION allows renegotiations + * with non-upgraded peers. Allowing legacy renegotiation + * makes the connection vulnerable to specific man in the + * middle attacks. (See RFC 5746) + * (Most interoperable and least secure option) + * + * SSL_LEGACY_BREAK_HANDSHAKE breaks off connections + * if peer does not support secure renegotiation. Results + * in interoperability issues with non-upgraded peers + * that do not support renegotiation altogether. + * (Most secure option, interoperability issues) * * \param ssl SSL context * \param allow_legacy Prevent or allow (SSL_NO_LEGACY_RENEGOTIATION or @@ -914,6 +930,8 @@ int ssl_handshake_client( ssl_context *ssl ); int ssl_handshake_server( ssl_context *ssl ); void ssl_handshake_wrapup( ssl_context *ssl ); +int ssl_send_fatal_handshake_failure( ssl_context *ssl ); + int ssl_derive_keys( ssl_context *ssl ); int ssl_read_record( ssl_context *ssl ); diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 9b821100b1..3d5008b6b2 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -328,12 +328,17 @@ static int ssl_parse_renegotiation_info( ssl_context *ssl, unsigned char *buf, size_t len ) { + int ret; + if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) { if( len != 1 || buf[0] != 0x0 ) { SSL_DEBUG_MSG( 1, ( "non-zero length renegotiated connection field" ) ); - /* TODO: Send handshake failure alert */ + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); } @@ -348,7 +353,10 @@ static int ssl_parse_renegotiation_info( ssl_context *ssl, ssl->peer_verify_data, ssl->verify_data_len ) != 0 ) { SSL_DEBUG_MSG( 1, ( "non-matching renegotiated connection field" ) ); - /* TODO: Send handshake failure alert */ + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); } } @@ -366,6 +374,7 @@ static int ssl_parse_server_hello( ssl_context *ssl ) size_t ext_len = 0; unsigned char *buf, *ext; int renegotiation_info_seen = 0; + int handshake_failure = 0; SSL_DEBUG_MSG( 2, ( "=> parse server hello" ) ); @@ -563,18 +572,39 @@ static int ssl_parse_server_hello( ssl_context *ssl ) /* * Renegotiation security checks */ - if( ssl->renegotiation == SSL_RENEGOTIATION && - ssl->secure_renegotiation == SSL_SECURE_RENEGOTIATION && - renegotiation_info_seen == 0 ) + if( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == SSL_LEGACY_BREAK_HANDSHAKE ) { - SSL_DEBUG_MSG( 1, ( "renegotiation_info extension missing" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + SSL_DEBUG_MSG( 1, ( "legacy renegotiation, breaking off handshake" ) ); + handshake_failure = 1; + } + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_SECURE_RENEGOTIATION && + renegotiation_info_seen == 0 ) + { + SSL_DEBUG_MSG( 1, ( "renegotiation_info extension missing (secure)" ) ); + handshake_failure = 1; } - - if( !ssl->allow_legacy_renegotiation && - ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION ) + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == SSL_LEGACY_NO_RENEGOTIATION ) { SSL_DEBUG_MSG( 1, ( "legacy renegotiation not allowed" ) ); + handshake_failure = 1; + } + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + renegotiation_info_seen == 1 ) + { + SSL_DEBUG_MSG( 1, ( "renegotiation_info extension present (legacy)" ) ); + handshake_failure = 1; + } + + if( handshake_failure == 1 ) + { + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); } diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 742b9126ec..209e5bdd6a 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -42,12 +42,17 @@ static int ssl_parse_renegotiation_info( ssl_context *ssl, unsigned char *buf, size_t len ) { + int ret; + if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) { if( len != 1 || buf[0] != 0x0 ) { SSL_DEBUG_MSG( 1, ( "non-zero length renegotiated connection field" ) ); - /* TODO: Send handshake failure alert */ + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); } @@ -60,7 +65,10 @@ static int ssl_parse_renegotiation_info( ssl_context *ssl, memcmp( buf + 1, ssl->peer_verify_data, ssl->verify_data_len ) != 0 ) { SSL_DEBUG_MSG( 1, ( "non-matching renegotiated connection field" ) ); - /* TODO: Send handshake failure alert */ + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); } } @@ -77,7 +85,8 @@ static int ssl_parse_client_hello( ssl_context *ssl ) unsigned int comp_len; unsigned int ext_len = 0; unsigned char *buf, *p, *ext; - int renegotiation_info_seen; + int renegotiation_info_seen = 0; + int handshake_failure = 0; SSL_DEBUG_MSG( 2, ( "=> parse client hello" ) ); @@ -277,6 +286,10 @@ static int ssl_parse_client_hello( ssl_context *ssl ) if( ssl->renegotiation == SSL_RENEGOTIATION ) { SSL_DEBUG_MSG( 1, ( "received RENEGOTIATION SCSV during renegotiation" ) ); + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); } ssl->secure_renegotiation = SSL_SECURE_RENEGOTIATION; @@ -306,7 +319,6 @@ have_ciphersuite: ssl_optimize_checksum( ssl, ssl->session_negotiate->ciphersuite ); ext = buf + 44 + sess_len + ciph_len + comp_len; - renegotiation_info_seen = 0; while( ext_len ) { @@ -349,26 +361,39 @@ have_ciphersuite: /* * Renegotiation security checks */ - if( ssl->renegotiation == SSL_RENEGOTIATION && - ssl->secure_renegotiation == SSL_SECURE_RENEGOTIATION && - renegotiation_info_seen == 0 ) + if( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == SSL_LEGACY_BREAK_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "legacy renegotiation, breaking off handshake" ) ); + handshake_failure = 1; + } + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_SECURE_RENEGOTIATION && + renegotiation_info_seen == 0 ) { SSL_DEBUG_MSG( 1, ( "renegotiation_info extension missing (secure)" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + handshake_failure = 1; } - - if( ssl->renegotiation == SSL_RENEGOTIATION && - ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && - renegotiation_info_seen == 1 ) - { - SSL_DEBUG_MSG( 1, ( "renegotiation_info extension present (legacy)" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - if( !ssl->allow_legacy_renegotiation && - ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION ) + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == SSL_LEGACY_NO_RENEGOTIATION ) { SSL_DEBUG_MSG( 1, ( "legacy renegotiation not allowed" ) ); + handshake_failure = 1; + } + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + renegotiation_info_seen == 1 ) + { + SSL_DEBUG_MSG( 1, ( "renegotiation_info extension present (legacy)" ) ); + handshake_failure = 1; + } + + if( handshake_failure == 1 ) + { + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); } diff --git a/library/ssl_tls.c b/library/ssl_tls.c index d47ec9f406..7e638cd7d1 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2020,6 +2020,20 @@ int ssl_read_record( ssl_context *ssl ) return( 0 ); } +int ssl_send_fatal_handshake_failure( ssl_context *ssl ) +{ + int ret; + + if( ( ret = ssl_send_alert_message( ssl, + SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_HANDSHAKE_FAILURE ) ) != 0 ) + { + return( ret ); + } + + return( 0 ); +} + int ssl_send_alert_message( ssl_context *ssl, unsigned char level, unsigned char message ) @@ -3513,15 +3527,28 @@ int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len ) return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); } - if( ssl->disable_renegotiation == SSL_RENEGOTIATION_DISABLED ) + if( ssl->disable_renegotiation == SSL_RENEGOTIATION_DISABLED || + ( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == SSL_LEGACY_NO_RENEGOTIATION ) ) { SSL_DEBUG_MSG( 3, ( "ignoring renegotiation, sending alert" ) ); - if( ( ret = ssl_send_alert_message( ssl, - SSL_ALERT_LEVEL_WARNING, - SSL_ALERT_MSG_NO_RENEGOTIATION ) ) != 0 ) + if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) { - return( ret ); + /* + * SSLv3 does not have a "no_renegotiation" alert + */ + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + } + else + { + if( ( ret = ssl_send_alert_message( ssl, + SSL_ALERT_LEVEL_WARNING, + SSL_ALERT_MSG_NO_RENEGOTIATION ) ) != 0 ) + { + return( ret ); + } } } else diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index df5274a13e..0c962f2107 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -51,7 +51,7 @@ #define DFL_KEY_FILE "" #define DFL_FORCE_CIPHER 0 #define DFL_RENEGOTIATION SSL_RENEGOTIATION_ENABLED -#define DFL_ALLOW_LEGACY SSL_NO_LEGACY_RENEGOTIATION +#define DFL_ALLOW_LEGACY SSL_LEGACY_NO_RENEGOTIATION #define GET_REQUEST "GET %s HTTP/1.0\r\n\r\n"