From 4fa52141b08fca89250805afcf2f112a2e0d3500 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Tue, 29 Dec 2015 03:24:17 -0500 Subject: Protocol version selection and negotiation rewrite The protocol selection code is now consolidated in a few consecutive short functions in a single file and is table driven. Protocol-specific constraints that influence negotiation are moved into the flags field of the method structure. The same protocol version constraints are now applied in all code paths. It is now much easier to add new protocol versions without reworking the protocol selection logic. In the presence of "holes" in the list of enabled client protocols we no longer select client protocols below the hole based on a subset of the constraints and then fail shortly after when it is found that these don't meet the remaining constraints (suiteb, FIPS, security level, ...). Ideally, with the new min/max controls users will be less likely to create "holes" in the first place. Reviewed-by: Kurt Roeckx --- ssl/d1_lib.c | 38 +---- ssl/methods.c | 54 ++++--- ssl/s3_lib.c | 27 ---- ssl/ssl_cert.c | 24 +-- ssl/ssl_ciph.c | 8 +- ssl/ssl_conf.c | 32 ++-- ssl/ssl_err.c | 14 +- ssl/ssl_lib.c | 18 +-- ssl/ssl_locl.h | 32 +++- ssl/statem/statem.c | 15 +- ssl/statem/statem_clnt.c | 246 +++--------------------------- ssl/statem/statem_lib.c | 385 ++++++++++++++++++++++++++++++++++++++++++++++- ssl/statem/statem_srvr.c | 112 ++------------ ssl/t1_lib.c | 5 +- 14 files changed, 537 insertions(+), 473 deletions(-) (limited to 'ssl') diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index a510b5bebe..3cd4b786e4 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c @@ -235,7 +235,7 @@ void dtls1_clear(SSL *s) if (s->options & SSL_OP_CISCO_ANYCONNECT) s->client_version = s->version = DTLS1_BAD_VER; else if (s->method->version == DTLS_ANY_VERSION) - s->version = DTLS1_2_VERSION; + s->version = DTLS_MAX_VERSION; else s->version = s->method->version; } @@ -256,38 +256,6 @@ long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg) case DTLS_CTRL_LISTEN: ret = dtls1_listen(s, parg); break; - case SSL_CTRL_CHECK_PROTO_VERSION: - /* - * For library-internal use; checks that the current protocol is the - * is the highest enabled version. - */ - if (s->max_proto_version == 0 && s->version == DTLS_MAX_VERSION) - return 1; - if (s->max_proto_version != 0 && s->version == s->max_proto_version) - return 1; - /* We're not limited by the max_proto_version but might still have - * other reasons why we use an older version like not using a - * version-flexible SSL_METHOD. Check s->ctx->method as version - * negotiation may have changed s->method. - * This check can be removed when we only have version-flexible - * SSL_METHODs - */ - if (s->version == s->ctx->method->version) - return 1; - /* - * Apparently we're using a version-flexible SSL_METHOD (not at its - * highest protocol version, not limited by max_proto_version). - */ - if (s->ctx->method->version == DTLS_method()->version) { -#if DTLS_MAX_VERSION != DTLS1_2_VERSION -# error Code needs update for DTLS_method() support beyond DTLS1_2_VERSION. -#endif - if (!(s->options & SSL_OP_NO_DTLSv1_2)) - return s->version == DTLS1_2_VERSION; - if (!(s->options & SSL_OP_NO_DTLSv1)) - return s->version == DTLS1_VERSION; - } - return 0; /* Unexpected state; fail closed. */ case DTLS_CTRL_SET_LINK_MTU: if (larg < (long)dtls1_link_min_mtu()) return 0; @@ -708,8 +676,8 @@ int dtls1_listen(SSL *s, struct sockaddr *client) /* * Verify client version is supported */ - if ((clientvers > (unsigned int)s->method->version && - s->method->version != DTLS_ANY_VERSION)) { + if (DTLS_VERSION_LT(clientvers, (unsigned int)s->method->version) && + s->method->version != DTLS_ANY_VERSION) { SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_WRONG_VERSION_NUMBER); goto end; } diff --git a/ssl/methods.c b/ssl/methods.c index ef20c9ca3e..7a8bb11b77 100644 --- a/ssl/methods.c +++ b/ssl/methods.c @@ -135,19 +135,23 @@ static const SSL_METHOD *tls1_get_method(int ver) return NULL; } -IMPLEMENT_tls_meth_func(TLS_ANY_VERSION, TLS_method, +IMPLEMENT_tls_meth_func(TLS_ANY_VERSION, 0, 0, + TLS_method, ossl_statem_accept, ossl_statem_connect, tls1_get_method, TLSv1_2_enc_data) -IMPLEMENT_tls_meth_func(TLS1_2_VERSION, TLSv1_2_method, +IMPLEMENT_tls_meth_func(TLS1_2_VERSION, 0, SSL_OP_NO_TLSv1_2, + TLSv1_2_method, ossl_statem_accept, ossl_statem_connect, tls1_get_method, TLSv1_2_enc_data) -IMPLEMENT_tls_meth_func(TLS1_1_VERSION, TLSv1_1_method, +IMPLEMENT_tls_meth_func(TLS1_1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_TLSv1_1, + TLSv1_1_method, ossl_statem_accept, ossl_statem_connect, tls1_get_method, TLSv1_1_enc_data) -IMPLEMENT_tls_meth_func(TLS1_VERSION, TLSv1_method, +IMPLEMENT_tls_meth_func(TLS1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_TLSv1, + TLSv1_method, ossl_statem_accept, ossl_statem_connect, tls1_get_method, TLSv1_enc_data) @@ -178,22 +182,26 @@ static const SSL_METHOD *tls1_get_server_method(int ver) return NULL; } -IMPLEMENT_tls_meth_func(TLS_ANY_VERSION, TLS_server_method, +IMPLEMENT_tls_meth_func(TLS_ANY_VERSION, 0, 0, + TLS_server_method, ossl_statem_accept, ssl_undefined_function, tls1_get_server_method, TLSv1_2_enc_data) -IMPLEMENT_tls_meth_func(TLS1_2_VERSION, TLSv1_2_server_method, +IMPLEMENT_tls_meth_func(TLS1_2_VERSION, 0, SSL_OP_NO_TLSv1_2, + TLSv1_2_server_method, ossl_statem_accept, ssl_undefined_function, tls1_get_server_method, TLSv1_2_enc_data) -IMPLEMENT_tls_meth_func(TLS1_1_VERSION, TLSv1_1_server_method, +IMPLEMENT_tls_meth_func(TLS1_1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_TLSv1_1, + TLSv1_1_server_method, ossl_statem_accept, ssl_undefined_function, tls1_get_server_method, TLSv1_1_enc_data) -IMPLEMENT_tls_meth_func(TLS1_VERSION, TLSv1_server_method, +IMPLEMENT_tls_meth_func(TLS1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_TLSv1, + TLSv1_server_method, ossl_statem_accept, ssl_undefined_function, tls1_get_server_method, TLSv1_enc_data) @@ -226,22 +234,26 @@ static const SSL_METHOD *tls1_get_client_method(int ver) return NULL; } -IMPLEMENT_tls_meth_func(TLS_ANY_VERSION, TLS_client_method, +IMPLEMENT_tls_meth_func(TLS_ANY_VERSION, 0, 0, + TLS_client_method, ssl_undefined_function, ossl_statem_connect, tls1_get_client_method, TLSv1_2_enc_data) -IMPLEMENT_tls_meth_func(TLS1_2_VERSION, TLSv1_2_client_method, +IMPLEMENT_tls_meth_func(TLS1_2_VERSION, 0, SSL_OP_NO_TLSv1_2, + TLSv1_2_client_method, ssl_undefined_function, ossl_statem_connect, tls1_get_client_method, TLSv1_2_enc_data) -IMPLEMENT_tls_meth_func(TLS1_1_VERSION, TLSv1_1_client_method, +IMPLEMENT_tls_meth_func(TLS1_1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_TLSv1_1, + TLSv1_1_client_method, ssl_undefined_function, ossl_statem_connect, tls1_get_client_method, TLSv1_1_enc_data) -IMPLEMENT_tls_meth_func(TLS1_VERSION, TLSv1_client_method, +IMPLEMENT_tls_meth_func(TLS1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_TLSv1, + TLSv1_client_method, ssl_undefined_function, ossl_statem_connect, tls1_get_client_method, TLSv1_enc_data) @@ -268,19 +280,19 @@ static const SSL_METHOD *dtls1_get_method(int ver) return NULL; } -IMPLEMENT_dtls1_meth_func(DTLS1_VERSION, +IMPLEMENT_dtls1_meth_func(DTLS1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_DTLSv1, DTLSv1_method, ossl_statem_accept, ossl_statem_connect, dtls1_get_method, DTLSv1_enc_data) -IMPLEMENT_dtls1_meth_func(DTLS1_2_VERSION, +IMPLEMENT_dtls1_meth_func(DTLS1_2_VERSION, 0, SSL_OP_NO_DTLSv1_2, DTLSv1_2_method, ossl_statem_accept, ossl_statem_connect, dtls1_get_method, DTLSv1_2_enc_data) -IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION, +IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION, 0, 0, DTLS_method, ossl_statem_accept, ossl_statem_connect, @@ -303,19 +315,19 @@ static const SSL_METHOD *dtls1_get_server_method(int ver) return NULL; } -IMPLEMENT_dtls1_meth_func(DTLS1_VERSION, +IMPLEMENT_dtls1_meth_func(DTLS1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_DTLSv1, DTLSv1_server_method, ossl_statem_accept, ssl_undefined_function, dtls1_get_server_method, DTLSv1_enc_data) -IMPLEMENT_dtls1_meth_func(DTLS1_2_VERSION, +IMPLEMENT_dtls1_meth_func(DTLS1_2_VERSION, 0, SSL_OP_NO_DTLSv1_2, DTLSv1_2_server_method, ossl_statem_accept, ssl_undefined_function, dtls1_get_server_method, DTLSv1_2_enc_data) -IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION, +IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION, 0, 0, DTLS_server_method, ossl_statem_accept, ssl_undefined_function, @@ -338,19 +350,19 @@ static const SSL_METHOD *dtls1_get_client_method(int ver) return NULL; } -IMPLEMENT_dtls1_meth_func(DTLS1_VERSION, +IMPLEMENT_dtls1_meth_func(DTLS1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_DTLSv1, DTLSv1_client_method, ssl_undefined_function, ossl_statem_connect, dtls1_get_client_method, DTLSv1_enc_data) -IMPLEMENT_dtls1_meth_func(DTLS1_2_VERSION, +IMPLEMENT_dtls1_meth_func(DTLS1_2_VERSION, 0, SSL_OP_NO_DTLSv1_2, DTLSv1_2_client_method, ssl_undefined_function, ossl_statem_connect, dtls1_get_client_method, DTLSv1_2_enc_data) -IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION, +IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION, 0, 0, DTLS_client_method, ssl_undefined_function, ossl_statem_connect, diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index e3e4fd34f8..d307ec05d6 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -3777,33 +3777,6 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) } #endif - case SSL_CTRL_CHECK_PROTO_VERSION: - /* - * For library-internal use; checks that the current protocol is the - * highest enabled version (according to s->ctx->method, as version - * negotiation may have changed s->method). - */ - if (s->version == s->ctx->method->version) - return 1; - /* - * Apparently we're using a version-flexible SSL_METHOD (not at its - * highest protocol version). - */ - if (s->ctx->method->version == TLS_method()->version) { -#if TLS_MAX_VERSION != TLS1_2_VERSION -# error Code needs update for TLS_method() support beyond TLS1_2_VERSION. -#endif - if (!(s->options & SSL_OP_NO_TLSv1_2)) - return s->version == TLS1_2_VERSION; - if (!(s->options & SSL_OP_NO_TLSv1_1)) - return s->version == TLS1_1_VERSION; - if (!(s->options & SSL_OP_NO_TLSv1)) - return s->version == TLS1_VERSION; - if (!(s->options & SSL_OP_NO_SSLv3)) - return s->version == SSL3_VERSION; - } - return 0; /* Unexpected state; fail closed. */ - default: break; } diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index f01d3a7835..597de0ad6c 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -1084,15 +1084,21 @@ static int ssl_security_default_callback(SSL *s, SSL_CTX *ctx, int op, break; } case SSL_SECOP_VERSION: - /* SSLv3 not allowed on level 2 */ - if (nid <= SSL3_VERSION && level >= 2) - return 0; - /* TLS v1.1 and above only for level 3 */ - if (nid <= TLS1_VERSION && level >= 3) - return 0; - /* TLS v1.2 only for level 4 and above */ - if (nid <= TLS1_1_VERSION && level >= 4) - return 0; + if (!SSL_IS_DTLS(s)) { + /* SSLv3 not allowed at level 2 */ + if (nid <= SSL3_VERSION && level >= 2) + return 0; + /* TLS v1.1 and above only for level 3 */ + if (nid <= TLS1_VERSION && level >= 3) + return 0; + /* TLS v1.2 only for level 4 and above */ + if (nid <= TLS1_1_VERSION && level >= 4) + return 0; + } else { + /* DTLS v1.2 only for level 4 and above */ + if (DTLS_VERSION_LT(nid, DTLS1_2_VERSION) && level >= 4) + return 0; + } break; case SSL_SECOP_COMPRESSION: diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c index 65a5bb6c96..a15248d41b 100644 --- a/ssl/ssl_ciph.c +++ b/ssl/ssl_ciph.c @@ -1349,12 +1349,8 @@ static int check_suiteb_cipher_list(const SSL_METHOD *meth, CERT *c, /* Check version: if TLS 1.2 ciphers allowed we can use Suite B */ if (!(meth->ssl3_enc->enc_flags & SSL_ENC_FLAG_TLS1_2_CIPHERS)) { - if (meth->ssl3_enc->enc_flags & SSL_ENC_FLAG_DTLS) - SSLerr(SSL_F_CHECK_SUITEB_CIPHER_LIST, - SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE); - else - SSLerr(SSL_F_CHECK_SUITEB_CIPHER_LIST, - SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE); + SSLerr(SSL_F_CHECK_SUITEB_CIPHER_LIST, + SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE); return 0; } # ifndef OPENSSL_NO_EC diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c index 1e14a4497e..9529d30842 100644 --- a/ssl/ssl_conf.c +++ b/ssl/ssl_conf.c @@ -347,6 +347,22 @@ static int protocol_from_string(const char *value) return -1; } +static int min_max_proto(SSL_CONF_CTX *cctx, const char *value, int *bound) +{ + int method_version; + int new_version; + + if (cctx->ctx != NULL) + method_version = cctx->ctx->method->version; + else if (cctx->ssl != NULL) + method_version = cctx->ssl->ctx->method->version; + else + return 0; + if ((new_version = protocol_from_string(value)) < 0) + return 0; + return ssl_set_version_bound(method_version, new_version, bound); +} + /* * cmd_MinProtocol - Set min protocol version * @cctx: config structure to save settings in @@ -356,13 +372,7 @@ static int protocol_from_string(const char *value) */ static int cmd_MinProtocol(SSL_CONF_CTX *cctx, const char *value) { - int version = protocol_from_string(value); - - if (version < 0) - return 0; - - *(cctx->min_version) = version; - return 1; + return min_max_proto(cctx, value, cctx->min_version); } /* @@ -374,13 +384,7 @@ static int cmd_MinProtocol(SSL_CONF_CTX *cctx, const char *value) */ static int cmd_MaxProtocol(SSL_CONF_CTX *cctx, const char *value) { - int version = protocol_from_string(value); - - if (version < 0) - return 0; - - *(cctx->max_version) = version; - return 1; + return min_max_proto(cctx, value, cctx->max_version); } static int cmd_Options(SSL_CONF_CTX *cctx, const char *value) diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 0c40b7b3ec..d9dbf99391 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -314,7 +314,6 @@ static ERR_STRING_DATA SSL_str_functs[] = { {ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, {ERR_FUNC(SSL_F_SSL_SET_TRUST), "SSL_set_trust"}, - {ERR_FUNC(SSL_F_SSL_SET_VERSION), "SSL_SET_VERSION"}, {ERR_FUNC(SSL_F_SSL_SET_WFD), "SSL_set_wfd"}, {ERR_FUNC(SSL_F_SSL_SHUTDOWN), "SSL_shutdown"}, {ERR_FUNC(SSL_F_SSL_SRP_CTX_INIT), "SSL_SRP_CTX_init"}, @@ -416,6 +415,10 @@ static ERR_STRING_DATA SSL_str_reasons[] = { {ERR_REASON(SSL_R_APP_DATA_IN_HANDSHAKE), "app data in handshake"}, {ERR_REASON(SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT), "attempt to reuse session in different context"}, + {ERR_REASON(SSL_R_AT_LEAST_TLS_1_0_NEEDED_IN_FIPS_MODE), + "at least TLS 1.0 needed in FIPS mode"}, + {ERR_REASON(SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE), + "at least (D)TLS 1.2 needed in Suite B mode"}, {ERR_REASON(SSL_R_BAD_ALERT_RECORD), "bad alert record"}, {ERR_REASON(SSL_R_BAD_CHANGE_CIPHER_SPEC), "bad change cipher spec"}, {ERR_REASON(SSL_R_BAD_DATA), "bad data"}, @@ -603,14 +606,6 @@ static ERR_STRING_DATA SSL_str_reasons[] = { "old session cipher not returned"}, {ERR_REASON(SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED), "old session compression algorithm not returned"}, - {ERR_REASON(SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE), - "only DTLS 1.2 allowed in Suite B mode"}, - {ERR_REASON(SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE), - "only TLS 1.2 allowed in Suite B mode"}, - {ERR_REASON(SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE), - "only tls allowed in fips mode"}, - {ERR_REASON(SSL_R_OPAQUE_PRF_INPUT_TOO_LONG), - "opaque PRF input too long"}, {ERR_REASON(SSL_R_PACKET_LENGTH_TOO_LONG), "packet length too long"}, {ERR_REASON(SSL_R_PARSE_TLSEXT), "parse tlsext"}, {ERR_REASON(SSL_R_PATH_TOO_LONG), "path too long"}, @@ -794,6 +789,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = { {ERR_REASON(SSL_R_UNSUPPORTED_SSL_VERSION), "unsupported ssl version"}, {ERR_REASON(SSL_R_UNSUPPORTED_STATUS_TYPE), "unsupported status type"}, {ERR_REASON(SSL_R_USE_SRTP_NOT_NEGOTIATED), "use srtp not negotiated"}, + {ERR_REASON(SSL_R_VERSION_TOO_HIGH), "version too high"}, {ERR_REASON(SSL_R_VERSION_TOO_LOW), "version too low"}, {ERR_REASON(SSL_R_WRONG_CERTIFICATE_TYPE), "wrong certificate type"}, {ERR_REASON(SSL_R_WRONG_CIPHER_RETURNED), "wrong cipher returned"}, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index cfc73de9ca..760014d739 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1201,11 +1201,11 @@ long SSL_ctrl(SSL *s, int cmd, long larg, void *parg) else return 0; case SSL_CTRL_SET_MIN_PROTO_VERSION: - s->min_proto_version = larg; - return 1; + return ssl_set_version_bound(s->ctx->method->version, (int)larg, + &s->min_proto_version); case SSL_CTRL_SET_MAX_PROTO_VERSION: - s->max_proto_version = larg; - return 1; + return ssl_set_version_bound(s->ctx->method->version, (int)larg, + &s->max_proto_version); default: return (s->method->ssl_ctrl(s, cmd, larg, parg)); } @@ -1323,11 +1323,11 @@ long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) case SSL_CTRL_CLEAR_CERT_FLAGS: return (ctx->cert->cert_flags &= ~larg); case SSL_CTRL_SET_MIN_PROTO_VERSION: - ctx->min_proto_version = larg; - return 1; + return ssl_set_version_bound(ctx->method->version, (int)larg, + &ctx->min_proto_version); case SSL_CTRL_SET_MAX_PROTO_VERSION: - ctx->max_proto_version = larg; - return 1; + return ssl_set_version_bound(ctx->method->version, (int)larg, + &ctx->max_proto_version); default: return (ctx->method->ssl_ctx_ctrl(ctx, cmd, larg, parg)); } @@ -1795,7 +1795,7 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) } if (FIPS_mode() && (meth->version < TLS1_VERSION)) { - SSLerr(SSL_F_SSL_CTX_NEW, SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE); + SSLerr(SSL_F_SSL_CTX_NEW, SSL_R_AT_LEAST_TLS_1_0_NEEDED_IN_FIPS_MODE); return NULL; } diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 9482fc91ad..7e07297f2f 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -471,8 +471,8 @@ * flags because it may not be set to correct version yet. */ # define SSL_CLIENT_USE_TLS1_2_CIPHERS(s) \ - ((SSL_IS_DTLS(s) && s->client_version <= DTLS1_2_VERSION) || \ - (!SSL_IS_DTLS(s) && s->client_version >= TLS1_2_VERSION)) + ((!SSL_IS_DTLS(s) && s->client_version >= TLS1_2_VERSION) || \ + (SSL_IS_DTLS(s) && DTLS_VERSION_GE(s->client_version, DTLS1_2_VERSION))) # ifdef TLSEXT_TYPE_encrypt_then_mac # define SSL_USE_ETM(s) (s->s3->flags & TLS1_FLAGS_ENCRYPT_THEN_MAC) @@ -535,6 +535,8 @@ struct ssl_cipher_st { /* Used to hold SSL/TLS functions */ struct ssl_method_st { int version; + unsigned flags; + unsigned long mask; int (*ssl_new) (SSL *s); void (*ssl_clear) (SSL *s); void (*ssl_free) (SSL *s); @@ -1685,12 +1687,20 @@ extern const SSL3_ENC_METHOD SSLv3_enc_data; extern const SSL3_ENC_METHOD DTLSv1_enc_data; extern const SSL3_ENC_METHOD DTLSv1_2_enc_data; -# define IMPLEMENT_tls_meth_func(version, func_name, s_accept, s_connect, \ - s_get_meth, enc_data) \ +/* + * Flags for SSL methods + */ +#define SSL_METHOD_NO_FIPS (1U<<0) +#define SSL_METHOD_NO_SUITEB (1U<<1) + +# define IMPLEMENT_tls_meth_func(version, flags, mask, func_name, s_accept, \ + s_connect, s_get_meth, enc_data) \ const SSL_METHOD *func_name(void) \ { \ static const SSL_METHOD func_name##_data= { \ version, \ + flags, \ + mask, \ tls1_new, \ tls1_clear, \ tls1_free, \ @@ -1727,6 +1737,8 @@ const SSL_METHOD *func_name(void) \ { \ static const SSL_METHOD func_name##_data= { \ SSL3_VERSION, \ + SSL_METHOD_NO_FIPS | SSL_METHOD_NO_SUITEB, \ + SSL_OP_NO_SSLv3, \ ssl3_new, \ ssl3_clear, \ ssl3_free, \ @@ -1758,12 +1770,14 @@ const SSL_METHOD *func_name(void) \ return &func_name##_data; \ } -# define IMPLEMENT_dtls1_meth_func(version, func_name, s_accept, s_connect, \ - s_get_meth, enc_data) \ +# define IMPLEMENT_dtls1_meth_func(version, flags, mask, func_name, s_accept, \ + s_connect, s_get_meth, enc_data) \ const SSL_METHOD *func_name(void) \ { \ static const SSL_METHOD func_name##_data= { \ version, \ + flags, \ + mask, \ dtls1_new, \ dtls1_clear, \ dtls1_free, \ @@ -1911,6 +1925,12 @@ __owur int ssl3_handshake_write(SSL *s); __owur int ssl_allow_compression(SSL *s); +__owur int ssl_set_client_hello_version(SSL *s); +__owur int ssl_check_version_downgrade(SSL *s); +__owur int ssl_set_version_bound(int method_version, int version, int *bound); +__owur int ssl_choose_server_version(SSL *s); +__owur int ssl_choose_client_version(SSL *s, int version); + __owur long tls1_default_timeout(void); __owur int dtls1_do_write(SSL *s, int type); void dtls1_set_message_header(SSL *s, diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c index bc3fc54fab..a03fe32083 100644 --- a/ssl/statem/statem.c +++ b/ssl/statem/statem.c @@ -261,7 +261,8 @@ static void (*get_callback(SSL *s))(const SSL *, int, int) * 1: Success * <=0: NBIO or error */ -static int state_machine(SSL *s, int server) { +static int state_machine(SSL *s, int server) +{ BUF_MEM *buf = NULL; unsigned long Time = (unsigned long)time(NULL); void (*cb) (const SSL *ssl, int type, int val) = NULL; @@ -336,19 +337,15 @@ static int state_machine(SSL *s, int server) { goto end; } } else { - if ((s->version >> 8) != SSL3_VERSION_MAJOR - && s->version != TLS_ANY_VERSION) { + if ((s->version >> 8) != SSL3_VERSION_MAJOR) { SSLerr(SSL_F_STATE_MACHINE, ERR_R_INTERNAL_ERROR); goto end; } } - if (!SSL_IS_DTLS(s)) { - if (s->version != TLS_ANY_VERSION && - !ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL)) { - SSLerr(SSL_F_STATE_MACHINE, SSL_R_VERSION_TOO_LOW); - goto end; - } + if (!ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL)) { + SSLerr(SSL_F_STATE_MACHINE, SSL_R_VERSION_TOO_LOW); + goto end; } if (s->init_buf == NULL) { diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index 26acdc5488..cfbfa5f8c3 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -166,7 +166,6 @@ static ossl_inline int cert_req_allowed(SSL *s); static int key_exchange_expected(SSL *s); -static int ssl_set_version(SSL *s); static int ca_dn_cmp(const X509_NAME *const *a, const X509_NAME *const *b); static int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, unsigned char *p); @@ -800,128 +799,12 @@ WORK_STATE ossl_statem_client_post_process_message(SSL *s, WORK_STATE wst) return WORK_ERROR; } -/* - * Work out what version we should be using for the initial ClientHello if - * the version is currently set to (D)TLS_ANY_VERSION. - * Returns 1 on success - * Returns 0 on error - */ -static int ssl_set_version(SSL *s) -{ - unsigned long mask, options = s->options; - - if (s->method->version == TLS_ANY_VERSION) { - /* - * SSL_OP_NO_X disables all protocols above X *if* there are - * some protocols below X enabled. This is required in order - * to maintain "version capability" vector contiguous. So - * that if application wants to disable TLS1.0 in favour of - * TLS1>=1, it would be insufficient to pass SSL_NO_TLSv1, the - * answer is SSL_OP_NO_TLSv1|SSL_OP_NO_SSLv3. - */ - mask = SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1 -#if !defined(OPENSSL_NO_SSL3) - | SSL_OP_NO_SSLv3 -#endif - ; -#if !defined(OPENSSL_NO_TLS1_2_CLIENT) - if (options & SSL_OP_NO_TLSv1_2) { - if ((options & mask) != mask) { - s->version = TLS1_1_VERSION; - } else { - SSLerr(SSL_F_SSL_SET_VERSION, SSL_R_NO_PROTOCOLS_AVAILABLE); - return 0; - } - } else { - s->version = TLS1_2_VERSION; - } -#else - if ((options & mask) == mask) { - SSLerr(SSL_F_SSL_SET_VERSION, SSL_R_NO_PROTOCOLS_AVAILABLE); - return 0; - } - s->version = TLS1_1_VERSION; -#endif - - mask &= ~SSL_OP_NO_TLSv1_1; - if ((options & SSL_OP_NO_TLSv1_1) && (options & mask) != mask) - s->version = TLS1_VERSION; - mask &= ~SSL_OP_NO_TLSv1; -#if !defined(OPENSSL_NO_SSL3) - if ((options & SSL_OP_NO_TLSv1) && (options & mask) != mask) - s->version = SSL3_VERSION; -#endif - - if (s->max_proto_version != 0 && (s->version > s->max_proto_version)) - s->version = s->max_proto_version; - if (s->version < s->min_proto_version) - { - SSLerr(SSL_F_SSL_SET_VERSION, SSL_R_NO_PROTOCOLS_AVAILABLE); - return 0; - } - - if (s->version != TLS1_2_VERSION && tls1_suiteb(s)) { - SSLerr(SSL_F_SSL_SET_VERSION, - SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE); - return 0; - } - - if (s->version == SSL3_VERSION && FIPS_mode()) { - SSLerr(SSL_F_SSL_SET_VERSION, SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE); - return 0; - } - - } else if (s->method->version == DTLS_ANY_VERSION) { - int max_version = DTLS_MAX_VERSION; - int min_version = DTLS_MIN_VERSION; - - if (s->max_proto_version != 0) - max_version = s->max_proto_version; - if (s->min_proto_version != 0) - min_version = s->min_proto_version; - - /* If DTLS 1.2 disabled correct the version number */ - if (options & SSL_OP_NO_DTLSv1_2 - || DTLS_VERSION_GT(DTLS1_2_VERSION, max_version)) { - if (tls1_suiteb(s)) { - SSLerr(SSL_F_SSL_SET_VERSION, - SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE); - return 0; - } - /* - * Disabling all versions is silly: return an error. - */ - if (options & SSL_OP_NO_DTLSv1 - || DTLS_VERSION_GT(min_version, DTLS1_VERSION)) { - SSLerr(SSL_F_SSL_SET_VERSION, SSL_R_WRONG_SSL_VERSION); - return 0; - } - /* - * Update method so we don't use any DTLS 1.2 features. - */ - s->method = DTLSv1_client_method(); - s->version = DTLS1_VERSION; - } else { - /* - * We only support one version: update method - */ - if (options & SSL_OP_NO_DTLSv1 - || DTLS_VERSION_GE(min_version, DTLS1_2_VERSION)) - s->method = DTLSv1_2_client_method(); - s->version = DTLS1_2_VERSION; - } - } - - s->client_version = s->version; - - return 1; -} - int tls_construct_client_hello(SSL *s) { unsigned char *buf; unsigned char *p, *d; int i; + int protverr; unsigned long l; int al = 0; #ifndef OPENSSL_NO_COMP @@ -933,8 +816,11 @@ int tls_construct_client_hello(SSL *s) buf = (unsigned char *)s->init_buf->data; /* Work out what SSL/TLS/DTLS version to use */ - if (ssl_set_version(s) == 0) + protverr = ssl_set_client_hello_version(s); + if (protverr != 0) { + SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, protverr); goto err; + } if ((sess == NULL) || (sess->ssl_version != s->version) || /* @@ -1141,121 +1027,23 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) unsigned char *cipherchars; int i, al = SSL_AD_INTERNAL_ERROR; unsigned int compression; + unsigned int sversion; + int protverr; #ifndef OPENSSL_NO_COMP SSL_COMP *comp; #endif - if (s->method->version == TLS_ANY_VERSION) { - unsigned int sversion; - int max_version = TLS_MAX_VERSION; - - if (s->max_proto_version != 0) - max_version = s->max_proto_version; - - if (!PACKET_get_net_2(pkt, &sversion)) { - al = SSL_AD_DECODE_ERROR; - SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); - goto f_err; - } - -#if TLS_MAX_VERSION != TLS1_2_VERSION -#error Code needs updating for new TLS version -#endif -#ifndef OPENSSL_NO_SSL3 - if ((sversion == SSL3_VERSION) && !(s->options & SSL_OP_NO_SSLv3) && - (s->min_proto_version <= SSL3_VERSION) && - (max_version >= SSL3_VERSION)) { - if (FIPS_mode()) { - SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, - SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE); - al = SSL_AD_PROTOCOL_VERSION; - goto f_err; - } - s->method = SSLv3_client_method(); - } else -#endif - if ((sversion == TLS1_VERSION) && !(s->options & SSL_OP_NO_TLSv1) && - (s->min_proto_version <= TLS1_VERSION) && - (max_version >= TLS1_VERSION)) { - s->method = TLSv1_client_method(); - } else if ((sversion == TLS1_1_VERSION) && - !(s->options & SSL_OP_NO_TLSv1_1) && - (s->min_proto_version <= TLS1_1_VERSION) && - (max_version >= TLS1_1_VERSION)) { - s->method = TLSv1_1_client_method(); - } else if ((sversion == TLS1_2_VERSION) && - !(s->options & SSL_OP_NO_TLSv1_2) && - (s->min_proto_version <= TLS1_2_VERSION) && - (max_version >= TLS1_2_VERSION)) { - s->method = TLSv1_2_client_method(); - } else { - SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_UNSUPPORTED_PROTOCOL); - al = SSL_AD_PROTOCOL_VERSION; - goto f_err; - } - s->session->ssl_version = s->version = s->method->version; - - if ((s->version < s->min_proto_version) - || !ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL)) { - SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_VERSION_TOO_LOW); - al = SSL_AD_PROTOCOL_VERSION; - goto f_err; - } - } else if (s->method->version == DTLS_ANY_VERSION) { - /* Work out correct protocol version to use */ - unsigned int hversion; - int options; - int max_version = DTLS_MAX_VERSION; - int min_version = DTLS_MIN_VERSION; - - if (s->max_proto_version != 0) - max_version = s->max_proto_version; - if (s->min_proto_version != 0) - min_version = s->min_proto_version; - - if (!PACKET_get_net_2(pkt, &hversion)) { - al = SSL_AD_DECODE_ERROR; - SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); - goto f_err; - } - - options = s->options; - if (hversion == DTLS1_2_VERSION && !(options & SSL_OP_NO_DTLSv1_2) && - DTLS_VERSION_LE(min_version, DTLS1_2_VERSION) && - DTLS_VERSION_GE(max_version, DTLS1_2_VERSION)) - s->method = DTLSv1_2_client_method(); - else if (tls1_suiteb(s)) { - SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, - SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE); - s->version = hversion; - al = SSL_AD_PROTOCOL_VERSION; - goto f_err; - } else if (hversion == DTLS1_VERSION && !(options & SSL_OP_NO_DTLSv1) && - DTLS_VERSION_LE(min_version, DTLS1_VERSION) && - DTLS_VERSION_GE(max_version, DTLS1_VERSION)) - s->method = DTLSv1_client_method(); - else { - SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_WRONG_SSL_VERSION); - s->version = hversion; - al = SSL_AD_PROTOCOL_VERSION; - goto f_err; - } - s->session->ssl_version = s->version = s->method->version; - } else { - unsigned char *vers; + if (!PACKET_get_net_2(pkt, &sversion)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); + goto f_err; + } - if (!PACKET_get_bytes(pkt, &vers, 2)) { - al = SSL_AD_DECODE_ERROR; - SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); - goto f_err; - } - if ((vers[0] != (s->version >> 8)) - || (vers[1] != (s->version & 0xff))) { - SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_WRONG_SSL_VERSION); - s->version = (s->version & 0xff00) | vers[1]; - al = SSL_AD_PROTOCOL_VERSION; - goto f_err; - } + protverr = ssl_choose_client_version(s, sversion); + if (protverr != 0) { + al = SSL_AD_PROTOCOL_VERSION; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, protverr); + goto f_err; } /* load the server hello data */ diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index 86d200f0b2..bf59eb3925 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -229,7 +229,7 @@ MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL *s, PACKET *pkt) { int al; long remain; - + remain = PACKET_remaining(pkt); /* * 'Change Cipher Spec' is just a single byte, which should already have @@ -704,3 +704,386 @@ int ssl_allow_compression(SSL *s) return 0; return ssl_security(s, SSL_SECOP_COMPRESSION, 0, 0, NULL); } + +static int version_cmp(SSL *s, int a, int b) +{ + int dtls = SSL_IS_DTLS(s); + + if (a == b) + return 0; + if (!dtls) + return a < b ? -1 : 1; + return DTLS_VERSION_LT(a, b) ? -1 : 1; +} + +typedef struct { + int version; + const SSL_METHOD *(*cmeth)(void); + const SSL_METHOD *(*smeth)(void); +} version_info; + +#if TLS_MAX_VERSION != TLS1_2_VERSION +# error Code needs update for TLS_method() support beyond TLS1_2_VERSION. +#endif + +static const version_info tls_version_table[] = { + { TLS1_2_VERSION, TLSv1_2_client_method, TLSv1_2_server_method }, + { TLS1_1_VERSION, TLSv1_1_client_method, TLSv1_1_server_method }, + { TLS1_VERSION, TLSv1_client_method, TLSv1_server_method }, +#ifndef OPENSSL_NO_SSL3 + { SSL3_VERSION, SSLv3_client_method, SSLv3_server_method }, +#endif + { 0, NULL, NULL }, +}; + +#if DTLS_MAX_VERSION != DTLS1_2_VERSION +# error Code needs update for DTLS_method() support beyond DTLS1_2_VERSION. +#endif + +static const version_info dtls_version_table[] = { + { DTLS1_2_VERSION, DTLSv1_2_client_method, DTLSv1_2_server_method }, + { DTLS1_VERSION, DTLSv1_client_method, DTLSv1_server_method }, + { 0, NULL, NULL }, +}; + +/* + * ssl_method_error - Check whether an SSL_METHOD is enabled. + * + * @s: The SSL handle for the candidate method + * @method: the intended method. + * + * Returns 0 on success, or an SSL error reason on failure. + */ +static int ssl_method_error(SSL *s, const SSL_METHOD *method) +{ + int version = method->version; + + if ((s->min_proto_version != 0 && + version_cmp(s, version, s->min_proto_version) < 0) || + ssl_security(s, SSL_SECOP_VERSION, 0, version, NULL) == 0) + return SSL_R_VERSION_TOO_LOW; + + if (s->max_proto_version != 0 && + version_cmp(s, version, s->max_proto_version) > 0) + return SSL_R_VERSION_TOO_HIGH; + + if ((s->options & method->mask) != 0) + return SSL_R_UNSUPPORTED_PROTOCOL; + if ((method->flags & SSL_METHOD_NO_SUITEB) != 0 && tls1_suiteb(s)) + return SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE; + else if ((method->flags & SSL_METHOD_NO_FIPS) != 0 && FIPS_mode()) + return SSL_R_AT_LEAST_TLS_1_0_NEEDED_IN_FIPS_MODE; + + return 0; +} + +/* + * ssl_check_version_downgrade - In response to RFC7507 SCSV version + * fallback indication from a client check whether we're using the highest + * supported protocol version. + * + * @s server SSL handle. + * + * Returns 1 when using the highest enabled version, 0 otherwise. + */ +int ssl_check_version_downgrade(SSL *s) +{ + const version_info *vent; + const version_info *table; + + /* + * Check that the current protocol is the highest enabled version + * (according to s->ctx->method, as version negotiation may have changed + * s->method). + */ + if (s->version == s->ctx->method->version) + return 1; + + /* + * Apparently we're using a version-flexible SSL_METHOD (not at its + * highest protocol version). + */ + if (s->ctx->method->version == TLS_method()->version) + table = tls_version_table; + else if (s->ctx->method->version == DTLS_method()->version) + table = dtls_version_table; + else { + /* Unexpected state; fail closed. */ + return 0; + } + + for (vent = table; vent->version != 0; ++vent) { + if (vent->smeth != NULL && + ssl_method_error(s, vent->smeth()) == 0) + return s->version == vent->version; + } + return 0; +} + +/* + * ssl_set_version_bound - set an upper or lower bound on the supported (D)TLS + * protocols, provided the initial (D)TLS method is version-flexible. This + * function sanity-checks the proposed value and makes sure the method is + * version-flexible, then sets the limit if all is well. + * + * @method_version: The version of the current SSL_METHOD. + * @version: the intended limit. + * @bound: pointer to limit to be updated. + * + * Returns 1 on success, 0 on failure. + */ +int ssl_set_version_bound(int method_version, int version, int *bound) +{ + /*- + * Restrict TLS methods to TLS protocol versions. + * Restrict DTLS methods to DTLS protocol versions. + * Note, DTLS version numbers are decreasing, use comparison macros. + * + * Note that for both lower-bounds we use explicit versions, not + * (D)TLS_MIN_VERSION. This is because we don't want to break user + * configurations. If the MIN (supported) version ever rises, the user's + * "floor" remains valid even if no longer available. We don't expect the + * MAX ceiling to ever get lower, so making that variable makes sense. + */ + switch (method_version) { + default: + /* + * XXX For fixed version methods, should we always fail and not set any + * bounds, always succeed and not set any bounds, or set the bounds and + * arrange to fail later if they are not met? At present fixed-version + * methods are not subject to controls that disable individual protocol + * versions. + */ + return 0; + + case TLS_ANY_VERSION: + if (version < SSL3_VERSION || version > TLS_MAX_VERSION) + return 0; + break; + + case DTLS_ANY_VERSION: + if (DTLS_VERSION_GT(version, DTLS_MAX_VERSION) || + DTLS_VERSION_LT(version, DTLS1_VERSION)) + return 0; + break; + } + + *bound = version; + return 1; +} + +/* + * ssl_choose_server_version - Choose server (D)TLS version. Called when the + * client HELLO is received to select the final server protocol version and + * the version specific method. + * + * @s: server SSL handle. + * + * Returns 0 on success or an SSL error reason number on failure. + */ +int ssl_choose_server_version(SSL *s) +{ + /*- + * With version-flexible methods we have an initial state with: + * + * s->method->version == (D)TLS_ANY_VERSION, + * s->version == (D)TLS_MAX_VERSION. + * + * So we detect version-flexible methods via the method version, not the + * handle version. + */ + int server_version = s->method->version; + int client_version = s->client_version; + const version_info *vent; + const version_info *table; + int disabled = 0; + + switch (server_version) { + default: + if (version_cmp(s, client_version, s->version) < 0) + return SSL_R_WRONG_SSL_VERSION; + /* + * If this SSL handle is not from a version flexible method we don't + * (and never did) check min/max FIPS or Suite B constraints. Hope + * that's OK. It is up to the caller to not choose fixed protocol + * versions they don't want. If not, then easy to fix, just return + * ssl_method_error(s, s->method) + */ + return 0; + case TLS_ANY_VERSION: + table = tls_version_table; + break; + case DTLS_ANY_VERSION: + table = dtls_version_table; + break; + } + + for (vent = table; vent->version != 0; ++vent) { + const SSL_METHOD *method; + + if (vent->smeth == NULL || + version_cmp(s, client_version, vent->version) < 0) + continue; + method = vent->smeth(); + if (ssl_method_error(s, method) == 0) { + s->version = vent->version; + s->method = method; + return 0; + } + disabled = 1; + } + return disabled ? SSL_R_UNSUPPORTED_PROTOCOL : SSL_R_VERSION_TOO_LOW; +} + +/* + * ssl_choose_client_version - Choose client (D)TLS version. Called when the + * server HELLO is received to select the final client protocol version and + * the version specific method. + * + * @s: client SSL handle. + * @version: The proposed version from the server's HELLO. + * + * Returns 0 on success or an SSL error reason number on failure. + */ +int ssl_choose_client_version(SSL *s, int version) +{ + const version_info *vent; + const version_info *table; + + switch (s->method->version) { + default: + if (version != s->version) + return SSL_R_WRONG_SSL_VERSION; + /* + * If this SSL handle is not from a version flexible method we don't + * (and never did) check min/max, FIPS or Suite B constraints. Hope + * that's OK. It is up to the caller to not choose fixed protocol + * versions they don't want. If not, then easy to fix, just return + * ssl_method_error(s, s->method) + */ + s->session->ssl_version = s->version; + return 0; + case TLS_ANY_VERSION: + table = tls_version_table; + break; + case DTLS_ANY_VERSION: + table = dtls_version_table; + break; + } + + for (vent = table; vent->version != 0; ++vent) { + const SSL_METHOD *method; + int err; + + if (version != vent->version) + continue; + if (vent->cmeth == NULL) + break; + method = vent->cmeth(); + err = ssl_method_error(s, method); + if (err != 0) + return err; + s->method = method; + s->session->ssl_version = s->version = version; + return 0; + } + + return SSL_R_UNSUPPORTED_PROTOCOL; +} + +/*- + * ssl_set_client_hello_version - Work out what version we should be using for + * the initial ClientHello if the version is initially (D)TLS_ANY_VERSION. We + * apply any explicit SSL_OP_NO_xxx options, the MinProtocol and MaxProtocol + * configuration commands, any Suite B or FIPS_mode() constraints and any floor + * imposed by the security level here, so we don't advertise the wrong protocol + * version to only reject the outcome later. + * + * Computing the right floor matters. If, e.g., TLS 1.0 and 1.2 are enabled, + * TLS 1.1 is disabled, but the security level, Suite-B and/or MinProtocol + * only allow TLS 1.2, we want to advertise TLS1.2, *not* TLS1. + * + * @s: client SSL handle. + * + * Returns 0 on success or an SSL error reason number on failure. + */ +int ssl_set_client_hello_version(SSL *s) +{ + int version; + int hole; + const SSL_METHOD *single = NULL; + const SSL_METHOD *method; + const version_info *table; + const version_info *vent; + + switch (s->method->version) { + default: + /* + * If this SSL handle is not from a version flexible method we don't + * (and never did) check min/max FIPS or Suite B constraints. Hope + * that's OK. It is up to the caller to not choose fixed protocol + * versions they don't want. If not, then easy to fix, just return + * ssl_method_error(s, s->method) + */ + s->client_version = s->version; + return 0; + case TLS_ANY_VERSION: + table = tls_version_table; + break; + case DTLS_ANY_VERSION: + table = dtls_version_table; + break; + } + + /* + * SSL_OP_NO_X disables all protocols above X *if* there are some protocols + * below X enabled. This is required in order to maintain the "version + * capability" vector contiguous. Any versions with a NULL client method + * (protocol version client is disabled at compile-time) is also a "hole". + * + * Our initial state is hole == 1, version == 0. That is, versions above + * the first version in the method table are disabled (a "hole" above + * the valid protocol entries) and we don't have a selected version yet. + * + * Whenever "hole == 1", and we hit an enabled method, its version becomes + * the selected version, and the method becomes a candidate "single" + * method. We're no longer in a hole, so "hole" becomes 0. + * + * If "hole == 0" and we hit an enabled method, then "single" is cleared, + * as we support a contiguous range of at least two methods. If we hit + * a disabled method, then hole becomes true again, but nothing else + * changes yet, because all the remaining methods may be disabled too. + * If we again hit an enabled method after the new hole, it becomes + * selected, as we start from scratch. + */ + version = 0; + hole = 1; + for (vent = table; vent->version != 0; ++vent) { + /* + * A table entry with a NULL client method is still a hole in the + * "version capability" vector. + */ + if (vent->cmeth == NULL) { + hole = 1; + continue; + } + method = vent->cmeth(); + if (ssl_method_error(s, method) != 0) { + hole = 1; + } else if (!hole) { + single = NULL; + } else { + version = (single = method)->version; + hole = 0; + } + } + + /* Fail if everything is disabled */ + if (version == 0) + return SSL_R_NO_PROTOCOLS_AVAILABLE; + + if (single != NULL) + s->method = single; + s->client_version = s->version = version; + return 0; +} diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index a1163ed986..604b36565b 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -970,7 +970,7 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) SSL_COMP *comp = NULL; #endif STACK_OF(SSL_CIPHER) *ciphers = NULL; - int protverr = 1; + int protverr; /* |cookie| will only be initialized for DTLS. */ PACKET session_id, cipher_suites, compression, extensions, cookie; int is_v2_record; @@ -1037,76 +1037,21 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) } } - /* Do SSL/TLS version negotiation if applicable */ + /* + * Do SSL/TLS version negotiation if applicable. For DTLS we just check + * versions are potentially compatible. Version negotiation comes later. + */ if (!SSL_IS_DTLS(s)) { - if (s->version != TLS_ANY_VERSION) { - if (s->client_version >= s->version) { - protverr = 0; - } - } else if (s->client_version >= SSL3_VERSION) { - int max_version = TLS_MAX_VERSION; - - if (s->max_proto_version != 0) - max_version = s->max_proto_version; - - switch(s->client_version) { - default: - case TLS1_2_VERSION: - if(!(s->options & SSL_OP_NO_TLSv1_2) && - (max_version >= TLS1_2_VERSION) && - (s->min_proto_version <= TLS1_2_VERSION)) { - s->version = TLS1_2_VERSION; - s->method = TLSv1_2_server_method(); - protverr = 0; - break; - } - /* Deliberately fall through */ - case TLS1_1_VERSION: - if(!(s->options & SSL_OP_NO_TLSv1_1) && - (max_version >= TLS1_1_VERSION) && - (s->min_proto_version <= TLS1_1_VERSION)) { - s->version = TLS1_1_VERSION; - s->method = TLSv1_1_server_method(); - protverr = 0; - break; - } - /* Deliberately fall through */ - case TLS1_VERSION: - if(!(s->options & SSL_OP_NO_TLSv1) && - (max_version >= TLS1_VERSION) && - (s->min_proto_version <= TLS1_VERSION)) { - s->version = TLS1_VERSION; - s->method = TLSv1_server_method(); - protverr = 0; - break; - } - /* Deliberately fall through */ - case SSL3_VERSION: -#ifndef OPENSSL_NO_SSL3 - if(!(s->options & SSL_OP_NO_SSLv3) && - (max_version >= SSL3_VERSION) && - (s->min_proto_version <= SSL3_VERSION)) { - s->version = SSL3_VERSION; - s->method = SSLv3_server_method(); - protverr = 0; - break; - } -#else - break; -#endif - } - } - } else if (s->client_version <= s->version - || s->method->version == DTLS_ANY_VERSION) { - /* - * For DTLS we just check versions are potentially compatible. Version - * negotiation comes later. - */ + protverr = ssl_choose_server_version(s); + } else if (s->method->version != DTLS_ANY_VERSION && + DTLS_VERSION_LT(s->client_version, s->version)) { + protverr = SSL_R_VERSION_TOO_LOW; + } else { protverr = 0; } if (protverr) { - SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL); + SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, protverr); if ((!s->enc_write_ctx && !s->write_hash)) { /* * similar to ssl3_get_record, send alert using remote version @@ -1266,36 +1211,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) s->d1->cookie_verified = 1; } if (s->method->version == DTLS_ANY_VERSION) { - /* Select version to use */ - int max_version = DTLS_MAX_VERSION; - int min_version = DTLS_MIN_VERSION; - - if (s->max_proto_version != 0) - max_version = s->max_proto_version; - if (s->min_proto_version != 0) - min_version = s->min_proto_version; - - if (DTLS_VERSION_GE(s->client_version, DTLS1_2_VERSION) && - !(s->options & SSL_OP_NO_DTLSv1_2) && - DTLS_VERSION_GE(max_version, DTLS1_2_VERSION) && - DTLS_VERSION_LE(min_version, DTLS1_2_VERSION)) { - s->version = DTLS1_2_VERSION; - s->method = DTLSv1_2_server_method(); - } else if (tls1_suiteb(s)) { - SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, - SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE); - s->version = s->client_version; - al = SSL_AD_PROTOCOL_VERSION; - goto f_err; - } else if (DTLS_VERSION_GE(s->client_version, DTLS1_VERSION) && - !(s->options & SSL_OP_NO_DTLSv1) && - DTLS_VERSION_GE(max_version, DTLS1_VERSION) && - DTLS_VERSION_LE(min_version, DTLS1_VERSION)) { - s->version = DTLS1_VERSION; - s->method = DTLSv1_server_method(); - } else { - SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, - SSL_R_WRONG_VERSION_NUMBER); + protverr = ssl_choose_server_version(s); + if (protverr != 0) { + SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, protverr); s->version = s->client_version; al = SSL_AD_PROTOCOL_VERSION; goto f_err; @@ -3303,7 +3221,7 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, * version. Fail if the current version is an unexpected * downgrade. */ - if (!SSL_ctrl(s, SSL_CTRL_CHECK_PROTO_VERSION, 0, NULL)) { + if (!ssl_check_version_downgrade(s)) { SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, SSL_R_INAPPROPRIATE_FALLBACK); *al = SSL_AD_INAPPROPRIATE_FALLBACK; diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index a2a68af6c9..980f2f5d0b 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -208,7 +208,10 @@ void tls1_free(SSL *s) void tls1_clear(SSL *s) { ssl3_clear(s); - s->version = s->method->version; + if (s->method->version == TLS_ANY_VERSION) + s->version = TLS_MAX_VERSION; + else + s->version = s->method->version; } #ifndef OPENSSL_NO_EC -- cgit v1.2.3