This is an initial patch to support being able to set the minimum
and maximum protocol version.  The patch is currently untested,
that will happen as I rewrite other things.  But I'm looking for
feedback.


Kurt

diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index ab8730c..6a016f0 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -294,12 +294,23 @@ long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg)
                break;
        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). */
+                * 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 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
diff --git a/ssl/s23_clnt.c b/ssl/s23_clnt.c
index 42c3d68..9b9ee00 100644
--- a/ssl/s23_clnt.c
+++ b/ssl/s23_clnt.c
@@ -375,6 +375,14 @@ static int ssl23_client_hello(SSL *s)
                }
 #endif
 
+       if (s->max_proto_version != 0 && SSL_VERSION_GT(version, 
s->max_proto_version))
+               version = s->max_proto_version;
+       if (SSL_VERSION_LT(version, s->min_proto_version))
+               {
+               SSLerr(SSL_F_SSL23_CLIENT_HELLO,SSL_R_NO_PROTOCOLS_AVAILABLE);
+               return -1;
+               }
+
        buf=(unsigned char *)s->init_buf->data;
        if (s->state == SSL23_ST_CW_CLNT_HELLO_A)
                {
@@ -760,7 +768,8 @@ static int ssl23_get_server_hello(SSL *s)
                /* ensure that TLS_MAX_VERSION is up-to-date */
                OPENSSL_assert(s->version <= TLS_MAX_VERSION);
 
-               if (!ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL))
+               if (SSL_VERSION_LT(s->version, s->min_proto_version) ||
+                       !ssl_security(s, SSL_SECOP_VERSION, 0, s->version, 
NULL))
                        {
                        
SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,SSL_R_VERSION_TOO_LOW);
                        goto err;
diff --git a/ssl/s23_srvr.c b/ssl/s23_srvr.c
index 858420d..4bc16cc 100644
--- a/ssl/s23_srvr.c
+++ b/ssl/s23_srvr.c
@@ -259,6 +259,10 @@ int ssl23_get_client_hello(SSL *s)
        int n=0,j;
        int type=0;
        int v[2];
+       int max_version = TLS_MAX_VERSION;
+
+       if (s->max_proto_version != 0)
+               max_version = s->max_proto_version;
 
        if (s->state == SSL23_ST_SR_CLNT_HELLO_A)
                {
@@ -293,25 +297,29 @@ int ssl23_get_client_hello(SSL *s)
                                if (p[4] >= TLS1_VERSION_MINOR)
                                        {
                                        if (p[4] >= TLS1_2_VERSION_MINOR &&
+                                          max_version >= TLS1_2_VERSION &&
                                           !(s->options & SSL_OP_NO_TLSv1_2))
                                                {
                                                s->version=TLS1_2_VERSION;
                                                
s->state=SSL23_ST_SR_CLNT_HELLO_B;
                                                }
                                        else if (p[4] >= TLS1_1_VERSION_MINOR &&
+                                          max_version >= TLS1_1_VERSION &&
                                           !(s->options & SSL_OP_NO_TLSv1_1))
                                                {
                                                s->version=TLS1_1_VERSION;
                                                /* type=2; */ /* done later to 
survive restarts */
                                                
s->state=SSL23_ST_SR_CLNT_HELLO_B;
                                                }
-                                       else if (!(s->options & 
SSL_OP_NO_TLSv1))
+                                       else if (!(s->options & 
SSL_OP_NO_TLSv1) &&
+                                          max_version >= TLS1_VERSION)
                                                {
                                                s->version=TLS1_VERSION;
                                                /* type=2; */ /* done later to 
survive restarts */
                                                
s->state=SSL23_ST_SR_CLNT_HELLO_B;
                                                }
-                                       else if (!(s->options & 
SSL_OP_NO_SSLv3))
+                                       else if (!(s->options & 
SSL_OP_NO_SSLv3) &&
+                                          max_version >= SSL3_VERSION)
                                                {
                                                s->version=SSL3_VERSION;
                                                /* type=2; */
@@ -322,7 +330,8 @@ int ssl23_get_client_hello(SSL *s)
                                                type=1;
                                                }
                                        }
-                               else if (!(s->options & SSL_OP_NO_SSLv3))
+                               else if (!(s->options & SSL_OP_NO_SSLv3) &&
+                                  max_version >= SSL3_VERSION)
                                        {
                                        s->version=SSL3_VERSION;
                                        /* type=2; */
@@ -369,23 +378,27 @@ int ssl23_get_client_hello(SSL *s)
                        if (v[1] >= TLS1_VERSION_MINOR)
                                {
                                if (v[1] >= TLS1_2_VERSION_MINOR &&
+                                       max_version >= TLS1_2_VERSION &&
                                        !(s->options & SSL_OP_NO_TLSv1_2))
                                        {
                                        s->version=TLS1_2_VERSION;
                                        type=3;
                                        }
                                else if (v[1] >= TLS1_1_VERSION_MINOR &&
+                                       max_version >= TLS1_1_VERSION &&
                                        !(s->options & SSL_OP_NO_TLSv1_1))
                                        {
                                        s->version=TLS1_1_VERSION;
                                        type=3;
                                        }
-                               else if (!(s->options & SSL_OP_NO_TLSv1))
+                               else if (!(s->options & SSL_OP_NO_TLSv1) &&
+                                       max_version >= TLS1_VERSION)
                                        {
                                        s->version=TLS1_VERSION;
                                        type=3;
                                        }
-                               else if (!(s->options & SSL_OP_NO_SSLv3))
+                               else if (!(s->options & SSL_OP_NO_SSLv3) &&
+                                       max_version >= SSL3_VERSION)
                                        {
                                        s->version=SSL3_VERSION;
                                        type=3;
@@ -394,7 +407,8 @@ int ssl23_get_client_hello(SSL *s)
                        else
                                {
                                /* client requests SSL 3.0 */
-                               if (!(s->options & SSL_OP_NO_SSLv3))
+                               if (!(s->options & SSL_OP_NO_SSLv3) &&
+                                       max_version >= SSL3_VERSION)
                                        {
                                        s->version=SSL3_VERSION;
                                        type=3;
@@ -442,7 +456,8 @@ int ssl23_get_client_hello(SSL *s)
                }
 #endif
 
-       if (!ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL))
+       if (SSL_VERSION_LT(s->version, s->min_proto_version) ||
+               !ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL))
                {
                SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,SSL_R_VERSION_TOO_LOW);
                goto err;
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index ee0493f..9136ea1 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -673,8 +673,14 @@ int ssl3_client_hello(SSL *s)
                        {
                        /* Determine which DTLS version to use */
                        int options = s->options;
+                       int max_version = DTLS_MAX_VERSION;
+
+                       if (s->max_proto_version != 0)
+                               max_version = s->max_proto_version;
+
                        /* If DTLS 1.2 disabled correct the version number */
-                       if (options & SSL_OP_NO_DTLSv1_2)
+                       if (options & SSL_OP_NO_DTLSv1_2 ||
+                               SSL_VERSION_GT(DTLS1_2_VERSION, max_version))
                                {
                                if (tls1_suiteb(s))
                                        {
@@ -684,7 +690,8 @@ int ssl3_client_hello(SSL *s)
                                /* Disabling all versions is silly: return an
                                 * error.
                                 */
-                               if (options & SSL_OP_NO_DTLSv1)
+                               if (options & SSL_OP_NO_DTLSv1
+                                       || SSL_VERSION_GT(s->min_proto_version, 
DTLS1_VERSION))
                                        {
                                        
SSLerr(SSL_F_SSL3_CLIENT_HELLO,SSL_R_WRONG_SSL_VERSION);
                                        goto err;
@@ -698,7 +705,8 @@ int ssl3_client_hello(SSL *s)
                        else
                                {
                                /* We only support one version: update method */
-                               if (options & SSL_OP_NO_DTLSv1)
+                               if (options & SSL_OP_NO_DTLSv1 ||
+                                       SSL_VERSION_GE(s->min_proto_version, 
DTLS1_2_VERSION))
                                        s->method = DTLSv1_2_client_method();
                                s->version = DTLS1_2_VERSION;
                                }
@@ -927,7 +935,14 @@ int ssl3_get_server_hello(SSL *s)
                /* Work out correct protocol version to use */
                int hversion = (p[0] << 8)|p[1];
                int options = s->options;
+               int max_version = DTLS_MAX_VERSION;
+
+               if (s->max_proto_version != 0)
+                       max_version = s->max_proto_version;
+
                if (hversion == DTLS1_2_VERSION
+                       && SSL_VERSION_LE(DTLS1_2_VERSION, max_version)
+                       && SSL_VERSION_GE(DTLS1_2_VERSION, s->min_proto_version)
                        && !(options & SSL_OP_NO_DTLSv1_2))
                        s->method = DTLSv1_2_client_method();
                else if (tls1_suiteb(s))
@@ -938,6 +953,8 @@ int ssl3_get_server_hello(SSL *s)
                        goto f_err;
                        }
                else if (hversion == DTLS1_VERSION
+                       && SSL_VERSION_LE(DTLS1_VERSION, max_version)
+                       && SSL_VERSION_GE(DTLS1_VERSION, s->min_proto_version)
                        && !(options & SSL_OP_NO_DTLSv1))
                        s->method = DTLSv1_client_method();
                else
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index d670ff0..661e063 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -3927,12 +3927,23 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
 
        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). */
+                * is the highest enabled version. */
+               if (s->max_proto_version == 0 && s->version == TLS_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 at its highest protocol version, not limited by
+                * max_proto_version). */
                if (s->ctx->method->version == SSLv23_method()->version)
                        {
 #if TLS_MAX_VERSION != TLS1_2_VERSION
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index f95f9c7..251370c 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -1129,8 +1129,15 @@ int ssl3_get_client_hello(SSL *s)
                p += cookie_len;
                if (s->method->version == DTLS_ANY_VERSION)
                        {
+                       int max_version = DTLS_MAX_VERSION;
+
+                       if (s->max_proto_version != 0)
+                               max_version = s->max_proto_version;
+
                        /* Select version to use */
-                       if (s->client_version <= DTLS1_2_VERSION &&
+                       if (SSL_VERSION_GE(s->client_version, DTLS1_2_VERSION) 
&&
+                               SSL_VERSION_LE(DTLS1_2_VERSION, max_version) &&
+                               SSL_VERSION_GE(DTLS1_2_VERSION, 
s->min_proto_version) &&
                                !(s->options & SSL_OP_NO_DTLSv1_2))
                                {
                                s->version = DTLS1_2_VERSION;
@@ -1143,7 +1150,9 @@ int ssl3_get_client_hello(SSL *s)
                                al = SSL_AD_PROTOCOL_VERSION;
                                goto f_err;
                                }
-                       else if (s->client_version <= DTLS1_VERSION &&
+                       else if (SSL_VERSION_GE(s->client_version, 
DTLS1_VERSION) &&
+                               SSL_VERSION_LE(DTLS1_VERSION, max_version) &&
+                               SSL_VERSION_GE(DTLS1_VERSION, 
s->min_proto_version) &&
                                !(s->options & SSL_OP_NO_DTLSv1))
                                {
                                s->version = DTLS1_VERSION;
diff --git a/ssl/ssl.h b/ssl/ssl.h
index bc4cd0d..3fa18c0 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -998,6 +998,8 @@ struct ssl_ctx_st
 
        unsigned long options;
        unsigned long mode;
+       int min_proto_version;
+       int max_proto_version;
        long max_cert_list;
 
        struct cert_st /* CERT */ *cert;
@@ -1460,6 +1462,8 @@ struct ssl_st
        int first_packet;
        int client_version;     /* what was passed, used for
                                 * SSLv3/TLS rollback check */
+       int min_proto_version;
+       int max_proto_version;
        unsigned int max_send_fragment;
 #ifndef OPENSSL_NO_TLSEXT
        /* TLS extension debug callback */
@@ -1844,6 +1848,8 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 
 #define SSL_CTRL_SET_DH_AUTO                   118
 #define SSL_CTRL_CHECK_PROTO_VERSION           119
+#define SSL_CTRL_SET_MIN_PROTO_VERSION         120
+#define SSL_CTRL_SET_MAX_PROTO_VERSION         121
 
 
 #define SSL_CERT_SET_FIRST                     1
@@ -2011,6 +2017,15 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 #define SSL_get0_ec_point_formats(s, plst) \
        SSL_ctrl(s,SSL_CTRL_GET_EC_POINT_FORMATS,0,plst)
 
+#define SSL_CTX_set_min_proto_version(ctx, version) \
+       SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, NULL)
+#define SSL_CTX_set_max_proto_version(ctx, version) \
+       SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, NULL)
+#define SSL_set_min_proto_version(s, version) \
+       SSL_ctrl(s, SSL_CTRL_SET_MIN_PROTO_VERSION, version, NULL)
+#define SSL_set_max_proto_version(s, version) \
+       SSL_ctrl(s, SSL_CTRL_SET_MAX_PROTO_VERSION, version, NULL)
+
 #ifndef OPENSSL_NO_BIO
 BIO_METHOD *BIO_f_ssl(void);
 BIO *BIO_new_ssl(SSL_CTX *ctx,int client);
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 43204de..656f5b3 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -297,6 +297,8 @@ SSL *SSL_new(SSL_CTX *ctx)
 
        s->options=ctx->options;
        s->mode=ctx->mode;
+       s->min_proto_version=ctx->min_proto_version;
+       s->max_proto_version=ctx->max_proto_version;
        s->max_cert_list=ctx->max_cert_list;
 
        if (ctx->cert != NULL)
@@ -1165,6 +1167,12 @@ long SSL_ctrl(SSL *s,int cmd,long larg,void *parg)
                        }
                else
                        return ssl_put_cipher_by_char(s,NULL,NULL);
+       case SSL_CTRL_SET_MIN_PROTO_VERSION:
+               s->min_proto_version = larg;
+               return 1;
+       case SSL_CTRL_SET_MAX_PROTO_VERSION:
+               s->max_proto_version = larg;
+               return 1;
        default:
                return(s->method->ssl_ctrl(s,cmd,larg,parg));
                }
@@ -1282,6 +1290,12 @@ long SSL_CTX_ctrl(SSL_CTX *ctx,int cmd,long larg,void 
*parg)
                return(ctx->cert->cert_flags|=larg);
        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;
+       case SSL_CTRL_SET_MAX_PROTO_VERSION:
+               ctx->max_proto_version = larg;
+               return 1;
        default:
                return(ctx->method->ssl_ctx_ctrl(ctx,cmd,larg,parg));
                }
@@ -1932,6 +1946,8 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
        memset(ret,0,sizeof(SSL_CTX));
 
        ret->method=meth;
+       ret->min_proto_version = 0;
+       ret->max_proto_version = 0;
 
        ret->cert_store=NULL;
        ret->session_cache_mode=SSL_SESS_CACHE_SERVER;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 60f8107..863931a 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -259,6 +259,13 @@
                          c[1]=(unsigned char)(((l)>> 8)&0xff), \
                          c[2]=(unsigned char)(((l)    )&0xff)),c+=3)
 
+#define SSL_VERSION_GT(v1, v2) (((v1) < 0x8000 && (v1) > (v2)) || \
+                               ((v1) > 0x8000 && (v1) < (v2)))
+#define SSL_VERSION_GE(v1, v2) (((v1) < 0x8000 && (v1) >= (v2)) || \
+                               ((v1) > 0x8000 && (v1) <= (v2)))
+#define SSL_VERSION_LT(v1, v2) SSL_VERSION_GT((v2), (v1))
+#define SSL_VERSION_LE(v1, v2) SSL_VERSION_GE((v2), (v1))
+
 /* LOCAL STUFF */
 
 #define SSL_DECRYPT    0
-- 
2.1.3

______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
Development Mailing List                       openssl-dev@openssl.org
Automated List Manager                           majord...@openssl.org

Reply via email to