diff options
author | Kurt Roeckx <kurt@roeckx.be> | 2015-12-06 17:56:41 +0100 |
---|---|---|
committer | Viktor Dukhovni <openssl-users@dukhovni.org> | 2016-01-02 10:47:52 -0500 |
commit | 7946ab33cecce60afcc00afc8fc18f31f9e66bff (patch) | |
tree | fa178fbc42a649e87e201820cc11796dc3c7d6de /ssl | |
parent | 1e0784ff95cd69090e26e2205bfec6305038db56 (diff) |
Add support for minimum and maximum protocol version
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Diffstat (limited to 'ssl')
-rw-r--r-- | ssl/d1_lib.c | 16 | ||||
-rw-r--r-- | ssl/ssl_conf.c | 81 | ||||
-rw-r--r-- | ssl/ssl_lib.c | 16 | ||||
-rw-r--r-- | ssl/ssl_locl.h | 9 | ||||
-rw-r--r-- | ssl/statem/statem_clnt.c | 64 | ||||
-rw-r--r-- | ssl/statem/statem_srvr.c | 41 |
6 files changed, 204 insertions, 23 deletions
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index 0fdfd51091..a510b5bebe 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c @@ -259,14 +259,24 @@ long dtls1_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 == 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). + * 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/ssl_conf.c b/ssl/ssl_conf.c index 21aa2652fc..1e14a4497e 100644 --- a/ssl/ssl_conf.c +++ b/ssl/ssl_conf.c @@ -142,6 +142,10 @@ struct ssl_conf_ctx_st { uint32_t *pcert_flags; /* Pointer to SSL or SSL_CTX verify_mode or NULL if none */ uint32_t *pvfy_flags; + /* Pointer to SSL or SSL_CTX min_version field or NULL if none */ + int *min_version; + /* Pointer to SSL or SSL_CTX max_version field or NULL if none */ + int *max_version; /* Current flag table being worked on */ const ssl_flag_tbl *tbl; /* Size of table */ @@ -307,13 +311,78 @@ static int cmd_Protocol(SSL_CONF_CTX *cctx, const char *value) SSL_FLAG_TBL_INV("SSLv3", SSL_OP_NO_SSLv3), SSL_FLAG_TBL_INV("TLSv1", SSL_OP_NO_TLSv1), SSL_FLAG_TBL_INV("TLSv1.1", SSL_OP_NO_TLSv1_1), - SSL_FLAG_TBL_INV("TLSv1.2", SSL_OP_NO_TLSv1_2) + SSL_FLAG_TBL_INV("TLSv1.2", SSL_OP_NO_TLSv1_2), + SSL_FLAG_TBL_INV("DTLSv1", SSL_OP_NO_DTLSv1), + SSL_FLAG_TBL_INV("DTLSv1.2", SSL_OP_NO_DTLSv1_2) }; cctx->tbl = ssl_protocol_list; cctx->ntbl = OSSL_NELEM(ssl_protocol_list); return CONF_parse_list(value, ',', 1, ssl_set_option_list, cctx); } +/* + * protocol_from_string - converts a protocol version string to a number + * + * Returns -1 on failure or the version on success + */ +static int protocol_from_string(const char *value) +{ + struct protocol_versions { + const char *name; + int version; + }; + static const struct protocol_versions versions[] = { + {"SSLv3", SSL3_VERSION}, + {"TLSv1", TLS1_VERSION}, + {"TLSv1.1", TLS1_1_VERSION}, + {"TLSv1.2", TLS1_2_VERSION}, + {"DTLSv1", DTLS1_VERSION}, + {"DTLSv1.2", DTLS1_2_VERSION}}; + size_t i; + size_t n = OSSL_NELEM(versions); + + for (i = 0; i < n; i++) + if (strcmp(versions[i].name, value) == 0) + return versions[i].version; + return -1; +} + +/* + * cmd_MinProtocol - Set min protocol version + * @cctx: config structure to save settings in + * @value: The min protocol version in string form + * + * Returns 1 on success and 0 on failure. + */ +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; +} + +/* + * cmd_MaxProtocol - Set max protocol version + * @cctx: config structure to save settings in + * @value: The max protocol version in string form + * + * Returns 1 on success and 0 on failure. + */ +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; +} + static int cmd_Options(SSL_CONF_CTX *cctx, const char *value) { static const ssl_flag_tbl ssl_option_list[] = { @@ -527,6 +596,8 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = { #endif SSL_CONF_CMD_STRING(CipherString, "cipher", 0), SSL_CONF_CMD_STRING(Protocol, NULL, 0), + SSL_CONF_CMD_STRING(MinProtocol, "min_protocol", SSL_CONF_FLAG_SERVER | SSL_CONF_FLAG_CLIENT), + SSL_CONF_CMD_STRING(MaxProtocol, "max_protocol", SSL_CONF_FLAG_SERVER | SSL_CONF_FLAG_CLIENT), SSL_CONF_CMD_STRING(Options, NULL, 0), SSL_CONF_CMD_STRING(VerifyMode, NULL, 0), SSL_CONF_CMD(Certificate, "cert", SSL_CONF_FLAG_CERTIFICATE, @@ -831,10 +902,14 @@ void SSL_CONF_CTX_set_ssl(SSL_CONF_CTX *cctx, SSL *ssl) cctx->ctx = NULL; if (ssl) { cctx->poptions = &ssl->options; + cctx->min_version = &ssl->min_proto_version; + cctx->max_version = &ssl->max_proto_version; cctx->pcert_flags = &ssl->cert->cert_flags; cctx->pvfy_flags = &ssl->verify_mode; } else { cctx->poptions = NULL; + cctx->min_version = NULL; + cctx->max_version = NULL; cctx->pcert_flags = NULL; cctx->pvfy_flags = NULL; } @@ -846,10 +921,14 @@ void SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *cctx, SSL_CTX *ctx) cctx->ssl = NULL; if (ctx) { cctx->poptions = &ctx->options; + cctx->min_version = &ctx->min_proto_version; + cctx->max_version = &ctx->max_proto_version; cctx->pcert_flags = &ctx->cert->cert_flags; cctx->pvfy_flags = &ctx->verify_mode; } else { cctx->poptions = NULL; + cctx->min_version = NULL; + cctx->max_version = NULL; cctx->pcert_flags = NULL; cctx->pvfy_flags = NULL; } diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 7f4bca058b..cfc73de9ca 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -293,6 +293,8 @@ SSL *SSL_new(SSL_CTX *ctx) RECORD_LAYER_init(&s->rlayer, s); s->options = ctx->options; + s->min_proto_version = ctx->min_proto_version; + s->max_proto_version = ctx->max_proto_version; s->mode = ctx->mode; s->max_cert_list = ctx->max_cert_list; s->references = 1; @@ -1198,6 +1200,12 @@ long SSL_ctrl(SSL *s, int cmd, long larg, void *parg) return 1; else return 0; + 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)); } @@ -1314,6 +1322,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)); } @@ -1794,6 +1808,8 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) goto err; ret->method = meth; + ret->min_proto_version = 0; + ret->max_proto_version = 0; ret->session_cache_mode = SSL_SESS_CACHE_SERVER; ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT; /* We take the system default. */ diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 4db32a9294..9482fc91ad 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -264,6 +264,11 @@ c[1]=(unsigned char)(((l)>> 8)&0xff), \ c[2]=(unsigned char)(((l) )&0xff)),c+=3) +#define DTLS_VERSION_GT(v1, v2) ((v1) < (v2)) +#define DTLS_VERSION_GE(v1, v2) ((v1) <= (v2)) +#define DTLS_VERSION_LT(v1, v2) ((v1) > (v2)) +#define DTLS_VERSION_LE(v1, v2) ((v1) >= (v2)) + /* LOCAL STUFF */ # define SSL_DECRYPT 0 @@ -796,6 +801,8 @@ struct ssl_ctx_st { uint32_t options; uint32_t mode; + int min_proto_version; + int max_proto_version; long max_cert_list; struct cert_st /* CERT */ *cert; @@ -1066,6 +1073,8 @@ struct ssl_st { uint32_t options; /* API behaviour */ uint32_t mode; + int min_proto_version; + int max_proto_version; long max_cert_list; int first_packet; /* what was passed, used for SSLv3/TLS rollback check */ diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index d168b19cb8..26acdc5488 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -852,6 +852,14 @@ static int ssl_set_version(SSL *s) 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); @@ -864,9 +872,17 @@ static int ssl_set_version(SSL *s) } } else if (s->method->version == DTLS_ANY_VERSION) { - /* Determine which DTLS 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 1.2 disabled correct the version number */ - if (options & SSL_OP_NO_DTLSv1_2) { + 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); @@ -875,7 +891,8 @@ static int ssl_set_version(SSL *s) /* * Disabling all versions is silly: return an error. */ - if (options & SSL_OP_NO_DTLSv1) { + 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; } @@ -888,7 +905,8 @@ static int ssl_set_version(SSL *s) /* * We only support one version: update method */ - if (options & SSL_OP_NO_DTLSv1) + 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; } @@ -1129,6 +1147,10 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) 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; @@ -1140,7 +1162,9 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) #error Code needs updating for new TLS version #endif #ifndef OPENSSL_NO_SSL3 - if ((sversion == SSL3_VERSION) && !(s->options & SSL_OP_NO_SSLv3)) { + 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); @@ -1150,13 +1174,19 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) s->method = SSLv3_client_method(); } else #endif - if ((sversion == TLS1_VERSION) && !(s->options & SSL_OP_NO_TLSv1)) { + 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->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->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); @@ -1165,7 +1195,8 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) } s->session->ssl_version = s->version = s->method->version; - if (!ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL)) { + 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; @@ -1174,6 +1205,13 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) /* 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; @@ -1182,7 +1220,9 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) } options = s->options; - if (hversion == DTLS1_2_VERSION && !(options & SSL_OP_NO_DTLSv1_2)) + 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, @@ -1190,7 +1230,9 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) s->version = hversion; al = SSL_AD_PROTOCOL_VERSION; goto f_err; - } else if (hversion == DTLS1_VERSION && !(options & SSL_OP_NO_DTLSv1)) + } 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); diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 66ff3e61e2..a1163ed986 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -1044,10 +1044,17 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) 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)) { + 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; @@ -1055,7 +1062,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) } /* Deliberately fall through */ case TLS1_1_VERSION: - if(!(s->options & SSL_OP_NO_TLSv1_1)) { + 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; @@ -1063,7 +1072,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) } /* Deliberately fall through */ case TLS1_VERSION: - if(!(s->options & SSL_OP_NO_TLSv1)) { + 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; @@ -1072,7 +1083,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) /* Deliberately fall through */ case SSL3_VERSION: #ifndef OPENSSL_NO_SSL3 - if(!(s->options & SSL_OP_NO_SSLv3)) { + 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; @@ -1254,8 +1267,18 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) } if (s->method->version == DTLS_ANY_VERSION) { /* Select version to use */ - if (s->client_version <= DTLS1_2_VERSION && - !(s->options & SSL_OP_NO_DTLSv1_2)) { + 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)) { @@ -1264,8 +1287,10 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) s->version = s->client_version; al = SSL_AD_PROTOCOL_VERSION; goto f_err; - } else if (s->client_version <= DTLS1_VERSION && - !(s->options & SSL_OP_NO_DTLSv1)) { + } 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 { |