diff --git a/ChangeLog b/ChangeLog index 28f2847b9..37d32e1ff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -28,6 +28,7 @@ Features * Added predefined DHM groups from RFC 5114 * Added simple SSL session cache implementation * Added ServerName extension parsing (SNI) at server side + * Added option to add minimum accepted SSL/TLS protocol version Changes * Removed redundant POLARSSL_DEBUG_MSG define diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 1254615b9..c48a89dbc 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -90,6 +90,7 @@ #define POLARSSL_ERR_SSL_HW_ACCEL_FAILED -0x7F80 /**< Hardware acceleration function returned with error */ #define POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH -0x6F80 /**< Hardware acceleration function skipped / left alone data */ #define POLARSSL_ERR_SSL_COMPRESSION_FAILED -0x6F00 /**< Processing of the compression / decompression failed */ +#define POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION -0x6E80 /**< Handshake protocol not within min/max boundaries */ /* * Various constants @@ -389,6 +390,8 @@ struct _ssl_context int max_major_ver; /*!< max. major version from client */ int max_minor_ver; /*!< max. minor version from client */ + int min_minor_ver; /*!< min. major version accepted */ + int min_major_ver; /*!< min. minor version accepted */ /* * Callbacks (RNG, debug, I/O, verification) @@ -828,6 +831,19 @@ void ssl_set_sni( ssl_context *ssl, */ void ssl_set_max_version( ssl_context *ssl, int major, int minor ); + +/** + * \brief Set the minimum accepted SSL/TLS protocol version + * (Default: SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0) + * + * \param ssl SSL context + * \param major Major version number (only SSL_MAJOR_VERSION_3 supported) + * \param minor Minor version number (SSL_MINOR_VERSION_0, + * SSL_MINOR_VERSION_1 and SSL_MINOR_VERSION_2, + * SSL_MINOR_VERSION_3 supported) + */ +void ssl_set_min_version( ssl_context *ssl, int major, int minor ); + /** * \brief Enable / Disable renegotiation support for connection * (Default: SSL_RENEGOTIATION_ENABLED) diff --git a/library/error.c b/library/error.c index d4d8500da..a5eaaba35 100644 --- a/library/error.c +++ b/library/error.c @@ -303,6 +303,8 @@ void error_strerror( int ret, char *buf, size_t buflen ) snprintf( buf, buflen, "SSL - Hardware acceleration function skipped / left alone data" ); if( use_ret == -(POLARSSL_ERR_SSL_COMPRESSION_FAILED) ) snprintf( buf, buflen, "SSL - Processing of the compression / decompression failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION) ) + snprintf( buf, buflen, "SSL - Handshake protocol not within min/max boundaries" ); #endif /* POLARSSL_SSL_TLS_C */ #if defined(POLARSSL_X509_PARSE_C) diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 3e1b0569f..07b31d979 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -413,6 +413,18 @@ static int ssl_parse_server_hello( ssl_context *ssl ) ssl->minor_ver = buf[5]; + if( ssl->minor_ver < ssl->min_minor_ver ) + { + SSL_DEBUG_MSG( 1, ( "server only supports ssl smaller than minimum" + " [%d:%d] < [%d:%d]", ssl->major_ver, ssl->minor_ver, + buf[4], buf[5] ) ); + + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_PROTOCOL_VERSION ); + + return( POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION ); + } + #if defined(POLARSSL_DEBUG_C) t = ( (time_t) buf[6] << 24 ) | ( (time_t) buf[7] << 16 ) diff --git a/library/ssl_srv.c b/library/ssl_srv.c index da3401536..408d510fa 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -222,6 +222,18 @@ static int ssl_parse_client_hello( ssl_context *ssl ) ssl->minor_ver = ( buf[5] <= SSL_MINOR_VERSION_3 ) ? buf[5] : SSL_MINOR_VERSION_3; + if( ssl->minor_ver < ssl->min_minor_ver ) + { + SSL_DEBUG_MSG( 1, ( "client only supports ssl smaller than minimum" + " [%d:%d] < [%d:%d]", ssl->major_ver, ssl->minor_ver, + buf[4], buf[5] ) ); + + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_PROTOCOL_VERSION ); + + return( POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION ); + } + ssl->max_major_ver = buf[4]; ssl->max_minor_ver = buf[5]; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 5ae581f33..b66e04610 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2855,6 +2855,9 @@ int ssl_init( ssl_context *ssl ) ssl->rsa_sign = ssl_rsa_sign; ssl->rsa_key_len = ssl_rsa_key_len; + ssl->min_major_ver = SSL_MAJOR_VERSION_3; + ssl->min_minor_ver = SSL_MINOR_VERSION_0; + #if defined(POLARSSL_DHM_C) if( ( ret = mpi_read_string( &ssl->dhm_P, 16, POLARSSL_DHM_RFC5114_MODP_1024_P) ) != 0 || @@ -3133,6 +3136,12 @@ void ssl_set_max_version( ssl_context *ssl, int major, int minor ) ssl->max_minor_ver = minor; } +void ssl_set_min_version( ssl_context *ssl, int major, int minor ) +{ + ssl->min_major_ver = major; + ssl->min_minor_ver = minor; +} + void ssl_set_renegotiation( ssl_context *ssl, int renegotiation ) { ssl->disable_renegotiation = renegotiation; diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index a6915e2ee..ae98b1bad 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -52,6 +52,8 @@ #define DFL_FORCE_CIPHER 0 #define DFL_RENEGOTIATION SSL_RENEGOTIATION_ENABLED #define DFL_ALLOW_LEGACY SSL_LEGACY_NO_RENEGOTIATION +#define DFL_MIN_VERSION -1 +#define DFL_MAX_VERSION -1 #define GET_REQUEST "GET %s HTTP/1.0\r\n\r\n" @@ -71,6 +73,8 @@ struct options int force_ciphersuite[2]; /* protocol/ciphersuite to use, or all */ int renegotiation; /* enable / disable renegotiation */ int allow_legacy; /* allow legacy renegotiation */ + int min_version; /* minimum protocol version accepted */ + int max_version; /* maximum protocol version accepted */ } opt; void my_debug( void *ctx, int level, const char *str ) @@ -142,6 +146,12 @@ int my_verify( void *data, x509_cert *crt, int depth, int *flags ) " request_page=%%s default: \".\"\n" \ " renegotiation=%%d default: 1 (enabled)\n" \ " allow_legacy=%%d default: 0 (disabled)\n" \ + "\n" \ + " min_version=%%s default: \"\" (ssl3)\n" \ + " max_version=%%s default: \"\" (tls1_2)\n" \ + " force_version=%%s default: \"\" (none)\n" \ + " options: ssl3, tls1, tls1_1, tls1_2\n" \ + "\n" \ " force_ciphersuite= default: all enabled\n"\ " acceptable ciphersuite names:\n" @@ -215,6 +225,8 @@ int main( int argc, char *argv[] ) opt.force_ciphersuite[0]= DFL_FORCE_CIPHER; opt.renegotiation = DFL_RENEGOTIATION; opt.allow_legacy = DFL_ALLOW_LEGACY; + opt.min_version = DFL_MIN_VERSION; + opt.max_version = DFL_MAX_VERSION; for( i = 1; i < argc; i++ ) { @@ -271,6 +283,57 @@ int main( int argc, char *argv[] ) if( opt.allow_legacy < 0 || opt.allow_legacy > 1 ) goto usage; } + else if( strcmp( p, "min_version" ) == 0 ) + { + if( strcmp( q, "ssl3" ) == 0 ) + opt.min_version = SSL_MINOR_VERSION_0; + else if( strcmp( q, "tls1" ) == 0 ) + opt.min_version = SSL_MINOR_VERSION_1; + else if( strcmp( q, "tls1_1" ) == 0 ) + opt.min_version = SSL_MINOR_VERSION_2; + else if( strcmp( q, "tls1_2" ) == 0 ) + opt.min_version = SSL_MINOR_VERSION_3; + else + goto usage; + } + else if( strcmp( p, "max_version" ) == 0 ) + { + if( strcmp( q, "ssl3" ) == 0 ) + opt.max_version = SSL_MINOR_VERSION_0; + else if( strcmp( q, "tls1" ) == 0 ) + opt.max_version = SSL_MINOR_VERSION_1; + else if( strcmp( q, "tls1_1" ) == 0 ) + opt.max_version = SSL_MINOR_VERSION_2; + else if( strcmp( q, "tls1_2" ) == 0 ) + opt.max_version = SSL_MINOR_VERSION_3; + else + goto usage; + } + else if( strcmp( p, "force_version" ) == 0 ) + { + if( strcmp( q, "ssl3" ) == 0 ) + { + opt.min_version = SSL_MINOR_VERSION_0; + opt.max_version = SSL_MINOR_VERSION_0; + } + else if( strcmp( q, "tls1" ) == 0 ) + { + opt.min_version = SSL_MINOR_VERSION_1; + opt.max_version = SSL_MINOR_VERSION_1; + } + else if( strcmp( q, "tls1_1" ) == 0 ) + { + opt.min_version = SSL_MINOR_VERSION_2; + opt.max_version = SSL_MINOR_VERSION_2; + } + else if( strcmp( q, "tls1_2" ) == 0 ) + { + opt.min_version = SSL_MINOR_VERSION_3; + opt.max_version = SSL_MINOR_VERSION_3; + } + else + goto usage; + } else goto usage; } @@ -425,6 +488,11 @@ int main( int argc, char *argv[] ) ssl_set_hostname( &ssl, opt.server_name ); + if( opt.min_version != -1 ) + ssl_set_min_version( &ssl, SSL_MAJOR_VERSION_3, opt.min_version ); + if( opt.max_version != -1 ) + ssl_set_max_version( &ssl, SSL_MAJOR_VERSION_3, opt.max_version ); + /* * 4. Handshake */ diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index b98bff79c..5076be398 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -59,6 +59,7 @@ #define DFL_FORCE_CIPHER 0 #define DFL_RENEGOTIATION SSL_RENEGOTIATION_ENABLED #define DFL_ALLOW_LEGACY SSL_LEGACY_NO_RENEGOTIATION +#define DFL_MIN_VERSION -1 #define HTTP_RESPONSE \ "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" \ @@ -79,6 +80,7 @@ struct options int force_ciphersuite[2]; /* protocol/ciphersuite to use, or all */ int renegotiation; /* enable / disable renegotiation */ int allow_legacy; /* allow legacy renegotiation */ + int min_version; /* minimum protocol version accepted */ } opt; void my_debug( void *ctx, int level, const char *str ) @@ -110,6 +112,8 @@ void my_debug( void *ctx, int level, const char *str ) " request_page=%%s default: \".\"\n" \ " renegotiation=%%d default: 1 (enabled)\n" \ " allow_legacy=%%d default: 0 (disabled)\n" \ + " min_version=%%s default: \"ssl3\"\n" \ + " options: ssl3, tls1, tls1_1, tls1_2\n" \ " force_ciphersuite= default: all enabled\n"\ " acceptable ciphersuite names:\n" @@ -189,6 +193,7 @@ int main( int argc, char *argv[] ) opt.force_ciphersuite[0]= DFL_FORCE_CIPHER; opt.renegotiation = DFL_RENEGOTIATION; opt.allow_legacy = DFL_ALLOW_LEGACY; + opt.min_version = DFL_MIN_VERSION; for( i = 1; i < argc; i++ ) { @@ -241,6 +246,19 @@ int main( int argc, char *argv[] ) if( opt.allow_legacy < 0 || opt.allow_legacy > 1 ) goto usage; } + else if( strcmp( p, "min_version" ) == 0 ) + { + if( strcmp( q, "ssl3" ) == 0 ) + opt.min_version = SSL_MINOR_VERSION_0; + else if( strcmp( q, "tls1" ) == 0 ) + opt.min_version = SSL_MINOR_VERSION_1; + else if( strcmp( q, "tls1_1" ) == 0 ) + opt.min_version = SSL_MINOR_VERSION_2; + else if( strcmp( q, "tls1_2" ) == 0 ) + opt.min_version = SSL_MINOR_VERSION_3; + else + goto usage; + } else goto usage; } @@ -395,6 +413,9 @@ int main( int argc, char *argv[] ) POLARSSL_DHM_RFC5114_MODP_2048_G ); #endif + if( opt.min_version != -1 ) + ssl_set_min_version( &ssl, SSL_MAJOR_VERSION_3, opt.min_version ); + printf( " ok\n" ); reset: @@ -464,7 +485,7 @@ reset: if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE ) { printf( " failed\n ! ssl_handshake returned -0x%x\n\n", -ret ); - goto exit; + goto reset; } }