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