From 1cbd39dbebdb46e46a8f30d4c40292b001c147b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 20 Oct 2014 13:34:59 +0200 Subject: [PATCH] Implement FALLBACK_SCSV client-side --- include/polarssl/config.h | 17 ++++++++++++ include/polarssl/ssl.h | 32 +++++++++++++++++++++- library/ssl_cli.c | 11 ++++++++ library/ssl_tls.c | 7 +++++ programs/ssl/ssl_client2.c | 24 +++++++++++++++++ tests/ssl-opt.sh | 54 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 144 insertions(+), 1 deletion(-) diff --git a/include/polarssl/config.h b/include/polarssl/config.h index 50b4e339e..ff0ccecbd 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 194e94471..a36e74219 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -206,6 +206,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 @@ -308,6 +311,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) @@ -697,6 +701,10 @@ struct _ssl_context int min_major_ver; /*!< min. major version used */ int min_minor_ver; /*!< min. minor version used */ +#if defined(POLARSSL_SSL_FALLBACK_SCSV) && defined(POLARSSL_SSL_CLI_C) + char fallback; /*!< flag for fallback connections */ +#endif + /* * Callbacks (RNG, debug, I/O, verification) */ @@ -1367,7 +1375,6 @@ const char *ssl_get_alpn_protocol( const 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_MIN_MAJOR_VERSION, SSL_MIN_MINOR_VERSION) @@ -1383,6 +1390,29 @@ void ssl_set_max_version( ssl_context *ssl, int major, int minor ); */ void 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 27abb3efe..50ab05f2c 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -596,6 +596,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_tls.c b/library/ssl_tls.c index 5f080defe..bd4494f55 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3977,6 +3977,13 @@ void ssl_set_min_version( ssl_context *ssl, int major, int minor ) } } +#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 5b7a488c9..8ca83ebe3 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -95,6 +95,7 @@ int main( int argc, char *argv[] ) #define DFL_RECO_DELAY 0 #define DFL_TICKETS SSL_SESSION_TICKETS_ENABLED #define DFL_ALPN_STRING NULL +#define DFL_FALLBACK -1 #define GET_REQUEST "GET %s HTTP/1.0\r\nExtra-header: " #define GET_REQUEST_END "\r\n\r\n" @@ -132,6 +133,7 @@ struct options int reco_delay; /* delay in seconds before resuming session */ int tickets; /* enable / disable session tickets */ const char *alpn_string; /* ALPN supported protocols */ + int fallback; /* is this a fallback connection? */ } opt; static void my_debug( void *ctx, int level, const char *str ) @@ -284,6 +286,13 @@ static int my_verify( void *data, x509_crt *crt, int depth, int *flags ) #define USAGE_ALPN "" #endif /* POLARSSL_SSL_ALPN */ +#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" \ @@ -313,6 +322,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" \ @@ -413,6 +423,7 @@ int main( int argc, char *argv[] ) opt.reco_delay = DFL_RECO_DELAY; opt.tickets = DFL_TICKETS; opt.alpn_string = DFL_ALPN_STRING; + opt.fallback = DFL_FALLBACK; for( i = 1; i < argc; i++ ) { @@ -519,6 +530,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 ) @@ -952,6 +972,10 @@ int main( int argc, char *argv[] ) 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 ); +#if defined(POLARSSL_SSL_FALLBACK_SCSV) + if( opt.fallback != DFL_FALLBACK ) + ssl_set_fallback( &ssl, opt.fallback ); +#endif /* * 4. Handshake diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 4eafed436..ab7793a42 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -80,6 +80,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 @@ -425,6 +440,45 @@ 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" \ + -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" \ + -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" \ + 0 \ + -c "adding FALLBACK_SCSV" \ + -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)" + # Tests for Session Tickets run_test "Session resume using tickets: basic" \