diff --git a/ChangeLog b/ChangeLog index 1e2eda21b..8529872d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,13 @@ API Changes * ssl_set_bio() now requires that p_send == p_recv. * ssl_set_bio() is deprecated in favor of ssl_set_bio_timeout(). += 1.3 branch + +Reminder: bump SONAME for ABI change (FALLBACK_SCSV) + +Features + * Add support for FALLBACK_SCSV (draft-ietf-tls-downgrade-scsv) + = PolarSSL 1.3.9 released 2014-10-20 Security * Lowest common hash was selected from signature_algorithms extension in diff --git a/include/polarssl/config.h b/include/polarssl/config.h index 6e9050ae5..69c61324d 100644 --- a/include/polarssl/config.h +++ b/include/polarssl/config.h @@ -811,6 +811,23 @@ */ //#define POLARSSL_SSL_DEBUG_ALL +/** + * \def POLARSSL_SSL_FALLBACK_SCSV + * + * Enable support for FALLBACK_SCSV (draft-ietf-tls-downgrade-scsv-00). + * + * For servers, it is recommended to always enable this, unless you support + * only one version of TLS, or know for sure that none of your clients + * implements a fallback strategy. + * + * For clients, you only need this if you're using a fallback strategy, which + * is not recommended in the first place, unless you absolutely need it to + * interoperate with buggy (version-intolerant) servers. + * + * Comment this macro to disable support for FALLBACK_SCSV + */ +#define POLARSSL_SSL_FALLBACK_SCSV + /** * \def POLARSSL_SSL_HW_RECORD_ACCEL * diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index da95a9ff1..45b753933 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -215,6 +215,9 @@ #define SSL_IS_CLIENT 0 #define SSL_IS_SERVER 1 +#define SSL_IS_NOT_FALLBACK 0 +#define SSL_IS_FALLBACK 1 + #define SSL_COMPRESS_NULL 0 #define SSL_COMPRESS_DEFLATE 1 @@ -340,6 +343,7 @@ * Signaling ciphersuite values (SCSV) */ #define SSL_EMPTY_RENEGOTIATION_INFO 0xFF /**< renegotiation info ext */ +#define SSL_FALLBACK_SCSV 0x5600 /**< draft-ietf-tls-downgrade-scsv-00 */ /* * Supported Signature and Hash algorithms (For TLS 1.2) @@ -397,6 +401,7 @@ #define SSL_ALERT_MSG_PROTOCOL_VERSION 70 /* 0x46 */ #define SSL_ALERT_MSG_INSUFFICIENT_SECURITY 71 /* 0x47 */ #define SSL_ALERT_MSG_INTERNAL_ERROR 80 /* 0x50 */ +#define SSL_ALERT_MSG_INAPROPRIATE_FALLBACK 86 /* 0x56 */ #define SSL_ALERT_MSG_USER_CANCELED 90 /* 0x5A */ #define SSL_ALERT_MSG_NO_RENEGOTIATION 100 /* 0x64 */ #define SSL_ALERT_MSG_UNSUPPORTED_EXT 110 /* 0x6E */ @@ -779,6 +784,10 @@ struct _ssl_context unsigned badmac_seen; /*!< records with a bad MAC received */ #endif +#if defined(POLARSSL_SSL_FALLBACK_SCSV) && defined(POLARSSL_SSL_CLI_C) + char fallback; /*!< flag for fallback connections */ +#endif + /* * Callbacks (RNG, debug, I/O, verification) */ @@ -1695,7 +1704,6 @@ const char *ssl_get_alpn_protocol( const ssl_context *ssl ); */ int ssl_set_max_version( ssl_context *ssl, int major, int minor ); - /** * \brief Set the minimum accepted SSL/TLS protocol version * (Default: SSL_MIN_MAJOR_VERSION, SSL_MIN_MINOR_VERSION) @@ -1712,6 +1720,29 @@ int ssl_set_max_version( ssl_context *ssl, int major, int minor ); */ int ssl_set_min_version( ssl_context *ssl, int major, int minor ); +#if defined(POLARSSL_SSL_FALLBACK_SCSV) && defined(POLARSSL_SSL_CLI_C) +/** + * \brief Set the fallback flag (client-side only). + * (Default: SSL_IS_NOT_FALLBACK). + * + * \note Set to SSL_IS_FALLBACK when preparing a fallback + * connection, that is a connection with max_version set to a + * lower value than the value you're willing to use. Such + * fallback connections are not recommended but are sometimes + * necessary to interoperate with buggy (version-intolerant) + * servers. + * + * \warning You should NOT set this to SSL_IS_FALLBACK for + * non-fallback connections! This would appear to work for a + * while, then cause failures when the server is upgraded to + * support a newer TLS version. + * + * \param ssl SSL context + * \param fallback SSL_IS_NOT_FALLBACK or SSL_IS_FALLBACK + */ +void ssl_set_fallback( ssl_context *ssl, char fallback ); +#endif /* POLARSSL_SSL_FALLBACK_SCSV && POLARSSL_SSL_CLI_C */ + #if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) /** * \brief Set the maximum fragment length to emit and/or negotiate diff --git a/library/ssl_cli.c b/library/ssl_cli.c index c83e6ac95..78572ebf5 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -658,6 +658,17 @@ static int ssl_write_client_hello( ssl_context *ssl ) *p++ = (unsigned char)( ciphersuites[i] ); } + /* Some versions of OpenSSL don't handle it correctly if not at end */ +#if defined(POLARSSL_SSL_FALLBACK_SCSV) + if( ssl->fallback == SSL_IS_FALLBACK ) + { + SSL_DEBUG_MSG( 3, ( "adding FALLBACK_SCSV" ) ); + *p++ = (unsigned char)( SSL_FALLBACK_SCSV >> 8 ); + *p++ = (unsigned char)( SSL_FALLBACK_SCSV ); + n++; + } +#endif + *q++ = (unsigned char)( n >> 7 ); *q++ = (unsigned char)( n << 1 ); diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 32f107282..809a93009 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -1088,6 +1088,30 @@ static int ssl_parse_client_hello_v2( ssl_context *ssl ) } } +#if defined(POLARSSL_SSL_FALLBACK_SCSV) + for( i = 0, p = buf + 6; i < ciph_len; i += 3, p += 3 ) + { + if( p[0] == 0 && + p[1] == (unsigned char)( ( SSL_FALLBACK_SCSV >> 8 ) & 0xff ) && + p[2] == (unsigned char)( ( SSL_FALLBACK_SCSV ) & 0xff ) ) + { + SSL_DEBUG_MSG( 3, ( "received FALLBACK_SCSV" ) ); + + if( ssl->minor_ver < ssl->max_minor_ver ) + { + SSL_DEBUG_MSG( 1, ( "inapropriate fallback" ) ); + + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_INAPROPRIATE_FALLBACK ); + + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + break; + } + } +#endif /* POLARSSL_SSL_FALLBACK_SCSV */ + ciphersuites = ssl->ciphersuite_list[ssl->minor_ver]; ciphersuite_info = NULL; #if defined(POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE) @@ -1719,6 +1743,29 @@ read_record_header: } } +#if defined(POLARSSL_SSL_FALLBACK_SCSV) + for( i = 0, p = buf + 41 + sess_len; i < ciph_len; i += 2, p += 2 ) + { + if( p[0] == (unsigned char)( ( SSL_FALLBACK_SCSV >> 8 ) & 0xff ) && + p[1] == (unsigned char)( ( SSL_FALLBACK_SCSV ) & 0xff ) ) + { + SSL_DEBUG_MSG( 0, ( "received FALLBACK_SCSV" ) ); + + if( ssl->minor_ver < ssl->max_minor_ver ) + { + SSL_DEBUG_MSG( 0, ( "inapropriate fallback" ) ); + + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_INAPROPRIATE_FALLBACK ); + + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + break; + } + } +#endif /* POLARSSL_SSL_FALLBACK_SCSV */ + /* * Check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 454916135..f7856efab 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -5411,6 +5411,13 @@ int ssl_set_min_version( ssl_context *ssl, int major, int minor ) return( 0 ); } +#if defined(POLARSSL_SSL_FALLBACK_SCSV) && defined(POLARSSL_SSL_CLI_C) +void ssl_set_fallback( ssl_context *ssl, char fallback ) +{ + ssl->fallback = fallback; +} +#endif + #if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) int ssl_set_max_frag_len( ssl_context *ssl, unsigned char mfl_code ) { diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 152ec4e33..9e790db99 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -100,6 +100,7 @@ int main( int argc, char *argv[] ) #define DFL_TRANSPORT SSL_TRANSPORT_STREAM #define DFL_HS_TO_MIN 0 #define DFL_HS_TO_MAX 0 +#define DFL_FALLBACK -1 #define GET_REQUEST "GET %s HTTP/1.0\r\nExtra-header: " #define GET_REQUEST_END "\r\n\r\n" @@ -142,6 +143,7 @@ struct options int transport; /* TLS or DTLS? */ uint32_t hs_to_min; /* Initial value of DTLS handshake timer */ uint32_t hs_to_max; /* Max value of DTLS handshake timer */ + int fallback; /* is this a fallback connection? */ } opt; static void my_debug( void *ctx, int level, const char *str ) @@ -303,6 +305,13 @@ static int my_verify( void *data, x509_crt *crt, int depth, int *flags ) #define USAGE_DTLS "" #endif +#if defined(POLARSSL_SSL_FALLBACK_SCSV) +#define USAGE_FALLBACK \ + " fallback=0/1 default: (library default: off)\n" +#else +#define USAGE_FALLBACK "" +#endif + #define USAGE \ "\n usage: ssl_client2 param=<>...\n" \ "\n acceptable parameters:\n" \ @@ -336,6 +345,7 @@ static int my_verify( void *data, x509_crt *crt, int depth, int *flags ) USAGE_MAX_FRAG_LEN \ USAGE_TRUNC_HMAC \ USAGE_ALPN \ + USAGE_FALLBACK \ "\n" \ " min_version=%%s default: \"\" (ssl3)\n" \ " max_version=%%s default: \"\" (tls1_2)\n" \ @@ -441,6 +451,7 @@ int main( int argc, char *argv[] ) opt.transport = DFL_TRANSPORT; opt.hs_to_min = DFL_HS_TO_MIN; opt.hs_to_max = DFL_HS_TO_MAX; + opt.fallback = DFL_FALLBACK; for( i = 1; i < argc; i++ ) { @@ -565,6 +576,15 @@ int main( int argc, char *argv[] ) { opt.alpn_string = q; } + else if( strcmp( p, "fallback" ) == 0 ) + { + switch( atoi( q ) ) + { + case 0: opt.fallback = SSL_IS_NOT_FALLBACK; break; + case 1: opt.fallback = SSL_IS_FALLBACK; break; + default: goto usage; + } + } else if( strcmp( p, "min_version" ) == 0 ) { if( strcmp( q, "ssl3" ) == 0 ) @@ -1070,6 +1090,11 @@ int main( int argc, char *argv[] ) } } +#if defined(POLARSSL_SSL_FALLBACK_SCSV) + if( opt.fallback != DFL_FALLBACK ) + ssl_set_fallback( &ssl, opt.fallback ); +#endif + printf( " ok\n" ); /* diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index bf5d4d099..fcac03b96 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -82,6 +82,21 @@ requires_openssl_with_sslv2() { fi } +# skip next test if OpenSSL doesn't support FALLBACK_SCSV +requires_openssl_with_fallback_scsv() { + if [ -z "${OPENSSL_HAS_FBSCSV:-}" ]; then + if $OPENSSL_CMD s_client -help 2>&1 | grep fallback_scsv >/dev/null + then + OPENSSL_HAS_FBSCSV="YES" + else + OPENSSL_HAS_FBSCSV="NO" + fi + fi + if [ "$OPENSSL_HAS_FBSCSV" = "NO" ]; then + SKIP_NEXT="YES" + fi +} + # skip next test if GnuTLS isn't available requires_gnutls() { if [ -z "${GNUTLS_AVAILABLE:-}" ]; then @@ -552,6 +567,84 @@ run_test "Truncated HMAC: actual test" \ 0 \ -s "dumping 'computed mac' (10 bytes)" +# Tests for FALLBACK_SCSV + +run_test "Fallback SCSV: default" \ + "$P_SRV" \ + "$P_CLI debug_level=3 force_version=tls1_1" \ + 0 \ + -C "adding FALLBACK_SCSV" \ + -S "received FALLBACK_SCSV" \ + -S "inapropriate fallback" \ + -C "is a fatal alert message (msg 86)" + +run_test "Fallback SCSV: explicitly disabled" \ + "$P_SRV" \ + "$P_CLI debug_level=3 force_version=tls1_1 fallback=0" \ + 0 \ + -C "adding FALLBACK_SCSV" \ + -S "received FALLBACK_SCSV" \ + -S "inapropriate fallback" \ + -C "is a fatal alert message (msg 86)" + +run_test "Fallback SCSV: enabled" \ + "$P_SRV" \ + "$P_CLI debug_level=3 force_version=tls1_1 fallback=1" \ + 1 \ + -c "adding FALLBACK_SCSV" \ + -s "received FALLBACK_SCSV" \ + -s "inapropriate fallback" \ + -c "is a fatal alert message (msg 86)" + +run_test "Fallback SCSV: enabled, max version" \ + "$P_SRV" \ + "$P_CLI debug_level=3 fallback=1" \ + 0 \ + -c "adding FALLBACK_SCSV" \ + -s "received FALLBACK_SCSV" \ + -S "inapropriate fallback" \ + -C "is a fatal alert message (msg 86)" + +requires_openssl_with_fallback_scsv +run_test "Fallback SCSV: default, openssl server" \ + "$O_SRV" \ + "$P_CLI debug_level=3 force_version=tls1_1 fallback=0" \ + 0 \ + -C "adding FALLBACK_SCSV" \ + -C "is a fatal alert message (msg 86)" + +requires_openssl_with_fallback_scsv +run_test "Fallback SCSV: enabled, openssl server" \ + "$O_SRV" \ + "$P_CLI debug_level=3 force_version=tls1_1 fallback=1" \ + 1 \ + -c "adding FALLBACK_SCSV" \ + -c "is a fatal alert message (msg 86)" + +requires_openssl_with_fallback_scsv +run_test "Fallback SCSV: disabled, openssl client" \ + "$P_SRV" \ + "$O_CLI -tls1_1" \ + 0 \ + -S "received FALLBACK_SCSV" \ + -S "inapropriate fallback" + +requires_openssl_with_fallback_scsv +run_test "Fallback SCSV: enabled, openssl client" \ + "$P_SRV" \ + "$O_CLI -tls1_1 -fallback_scsv" \ + 1 \ + -s "received FALLBACK_SCSV" \ + -s "inapropriate fallback" + +requires_openssl_with_fallback_scsv +run_test "Fallback SCSV: enabled, max version, openssl client" \ + "$P_SRV" \ + "$O_CLI -fallback_scsv" \ + 0 \ + -s "received FALLBACK_SCSV" \ + -S "inapropriate fallback" + # Tests for Session Tickets run_test "Session resume using tickets: basic" \