Protocol version selection and negotiation rewrite
authorViktor Dukhovni <openssl-users@dukhovni.org>
Tue, 29 Dec 2015 08:24:17 +0000 (03:24 -0500)
committerViktor Dukhovni <openssl-users@dukhovni.org>
Sat, 2 Jan 2016 15:49:06 +0000 (10:49 -0500)
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 <kurt@openssl.org>
16 files changed:
CHANGES
include/openssl/ssl.h
ssl/d1_lib.c
ssl/methods.c
ssl/s3_lib.c
ssl/ssl_cert.c
ssl/ssl_ciph.c
ssl/ssl_conf.c
ssl/ssl_err.c
ssl/ssl_lib.c
ssl/ssl_locl.h
ssl/statem/statem.c
ssl/statem/statem_clnt.c
ssl/statem/statem_lib.c
ssl/statem/statem_srvr.c
ssl/t1_lib.c

diff --git a/CHANGES b/CHANGES
index b0331b2..b4cc2d4 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -9,7 +9,9 @@
      SSL_set_max_proto_version(), or via the SSL_CONF's MinProtocol and
      MaxProtcol.  It's recommended to use the new APIs to disable
      protocols instead of disabling individual protocols using
-     SSL_set_options() or SSL_CONF's Protocol.
+     SSL_set_options() or SSL_CONF's Protocol.  This change also
+     removes support for disabling TLS 1.2 in the OpenSSL TLS
+     client at compile time by defining OPENSSL_NO_TLS1_2_CLIENT.
      [Kurt Roeckx]
 
   *) Support for ChaCha20 and Poly1305 added to libcrypto and libssl.
index a0d66fa..0d22ab7 100644 (file)
@@ -1217,7 +1217,6 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 # define SSL_CTRL_SELECT_CURRENT_CERT            116
 # define SSL_CTRL_SET_CURRENT_CERT               117
 # define SSL_CTRL_SET_DH_AUTO                    118
-# define SSL_CTRL_CHECK_PROTO_VERSION            119
 # define DTLS_CTRL_SET_LINK_MTU                  120
 # define DTLS_CTRL_GET_LINK_MIN_MTU              121
 # define SSL_CTRL_GET_EXTMS_SUPPORT              122
@@ -2099,7 +2098,6 @@ void ERR_load_SSL_strings(void);
 # define SSL_F_SSL_SET_SESSION_ID_CONTEXT                 218
 # define SSL_F_SSL_SET_SESSION_TICKET_EXT                 294
 # define SSL_F_SSL_SET_TRUST                              228
-# define SSL_F_SSL_SET_VERSION                            347
 # define SSL_F_SSL_SET_WFD                                196
 # define SSL_F_SSL_SHUTDOWN                               224
 # define SSL_F_SSL_SRP_CTX_INIT                           313
@@ -2170,6 +2168,8 @@ void ERR_load_SSL_strings(void);
 /* Reason codes. */
 # define SSL_R_APP_DATA_IN_HANDSHAKE                      100
 # define SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT 272
+# define SSL_R_AT_LEAST_TLS_1_0_NEEDED_IN_FIPS_MODE       143
+# define SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE     158
 # define SSL_R_BAD_ALERT_RECORD                           101
 # define SSL_R_BAD_CHANGE_CIPHER_SPEC                     103
 # define SSL_R_BAD_DATA                                   390
@@ -2323,9 +2323,6 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_NULL_SSL_METHOD_PASSED                     196
 # define SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED            197
 # define SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED 344
-# define SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE       387
-# define SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE        379
-# define SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE              297
 # define SSL_R_OPAQUE_PRF_INPUT_TOO_LONG                  327
 # define SSL_R_PACKET_LENGTH_TOO_LONG                     198
 # define SSL_R_PARSE_TLSEXT                               227
@@ -2443,6 +2440,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_UNSUPPORTED_SSL_VERSION                    259
 # define SSL_R_UNSUPPORTED_STATUS_TYPE                    329
 # define SSL_R_USE_SRTP_NOT_NEGOTIATED                    369
+# define SSL_R_VERSION_TOO_HIGH                           166
 # define SSL_R_VERSION_TOO_LOW                            396
 # define SSL_R_WRONG_CERTIFICATE_TYPE                     383
 # define SSL_R_WRONG_CIPHER_RETURNED                      261
index a510b5b..3cd4b78 100644 (file)
@@ -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;
         }
index ef20c9c..7a8bb11 100644 (file)
@@ -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,
index e3e4fd3..d307ec0 100644 (file)
@@ -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;
     }
index f01d3a7..597de0a 100644 (file)
@@ -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:
index 65a5bb6..a15248d 100644 (file)
@@ -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
index 1e14a44..9529d30 100644 (file)
@@ -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)
index 0c40b7b..d9dbf99 100644 (file)
@@ -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"},
index cfc73de..760014d 100644 (file)
@@ -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;
     }
 
index 9482fc9..7e07297 100644 (file)
  * 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,
index bc3fc54..a03fe32 100644 (file)
@@ -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) {
index 26acdc5..cfbfa5f 100644 (file)
 
 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 */
index 86d200f..bf59eb3 100644 (file)
@@ -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;
+}
index a1163ed..604b365 100644 (file)
@@ -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;
index a2a68af..980f2f5 100644 (file)
@@ -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