X-Git-Url: https://git.openssl.org/gitweb/?a=blobdiff_plain;f=ssl%2Fstatem%2Fstatem_lib.c;h=a24060e47ff1d69b52991ecfb47e0bc161ab309d;hb=2b8fa1d56cd3a41d666994a1b2ed9df0f5e5d1ec;hp=b0df7d26362ed97f9cb5da3cd65b7e8dd1c6b543;hpb=e6575156204dfd50a63f6afbe98f6714d0799764;p=openssl.git diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index b0df7d2636..a24060e47f 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -1,4 +1,3 @@ -/* ssl/statem/statem_lib.c */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -229,7 +228,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 @@ -405,9 +404,6 @@ WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst) s->new_session = 0; if (s->server) { - s->renegotiate = 0; - s->new_session = 0; - ssl_update_cache(s, SSL_SESS_CACHE_SERVER); s->ctx->stats.sess_accept_good++; @@ -603,47 +599,32 @@ int tls_get_message_body(SSL *s, unsigned long *len) return 1; } -int ssl_cert_type(X509 *x, EVP_PKEY *pkey) +int ssl_cert_type(X509 *x, EVP_PKEY *pk) { - EVP_PKEY *pk; - int ret = -1, i; - - if (pkey == NULL) - pk = X509_get_pubkey(x); - else - pk = pkey; - if (pk == NULL) - goto err; - - i = pk->type; - if (i == EVP_PKEY_RSA) { - ret = SSL_PKEY_RSA_ENC; - } else if (i == EVP_PKEY_DSA) { - ret = SSL_PKEY_DSA_SIGN; - } + if (pk == NULL && + (pk = X509_get0_pubkey(x)) == NULL) + return -1; + + switch (EVP_PKEY_id(pk)) { + default: + return -1; + case EVP_PKEY_RSA: + return SSL_PKEY_RSA_ENC; + case EVP_PKEY_DSA: + return SSL_PKEY_DSA_SIGN; #ifndef OPENSSL_NO_EC - else if (i == EVP_PKEY_EC) { - ret = SSL_PKEY_ECC; - } + case EVP_PKEY_EC: + return SSL_PKEY_ECC; +#endif +#ifndef OPENSSL_NO_GOST + case NID_id_GostR3410_2001: + return SSL_PKEY_GOST01; + case NID_id_GostR3410_2012_256: + return SSL_PKEY_GOST12_256; + case NID_id_GostR3410_2012_512: + return SSL_PKEY_GOST12_512; #endif - else if (i == NID_id_GostR3410_2001) { - ret = SSL_PKEY_GOST01; - } else if (x && (i == EVP_PKEY_DH || i == EVP_PKEY_DHX)) { - /* - * For DH two cases: DH certificate signed with RSA and DH - * certificate signed with DSA. - */ - i = X509_certificate_type(x, pk); - if (i & EVP_PKS_RSA) - ret = SSL_PKEY_DH_RSA; - else if (i & EVP_PKS_DSA) - ret = SSL_PKEY_DH_DSA; } - - err: - if (!pkey) - EVP_PKEY_free(pk); - return (ret); } int ssl_verify_alarm_type(long type) @@ -711,3 +692,438 @@ int ssl_allow_compression(SSL *s) return 0; return ssl_security(s, SSL_SECOP_COMPRESSION, 0, 0, NULL); } + +static int version_cmp(const 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[] = { +#ifndef OPENSSL_NO_TLS1_2 + { TLS1_2_VERSION, tlsv1_2_client_method, tlsv1_2_server_method }, +#else + { TLS1_2_VERSION, NULL, NULL }, +#endif +#ifndef OPENSSL_NO_TLS1_1 + { TLS1_1_VERSION, tlsv1_1_client_method, tlsv1_1_server_method }, +#else + { TLS1_1_VERSION, NULL, NULL }, +#endif +#ifndef OPENSSL_NO_TLS1 + { TLS1_VERSION, tlsv1_client_method, tlsv1_server_method }, +#else + { TLS1_VERSION, NULL, NULL }, +#endif +#ifndef OPENSSL_NO_SSL3 + { SSL3_VERSION, sslv3_client_method, sslv3_server_method }, +#else + { SSL3_VERSION, NULL, NULL }, +#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[] = { +#ifndef OPENSSL_NO_DTLS1_2 + { DTLS1_2_VERSION, dtlsv1_2_client_method, dtlsv1_2_server_method }, +#else + { DTLS1_2_VERSION, NULL, NULL }, +#endif +#ifndef OPENSSL_NO_DTLS1 + { DTLS1_VERSION, dtlsv1_client_method, dtlsv1_server_method }, +#else + { DTLS1_VERSION, NULL, NULL }, +#endif + { 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(const 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) +{ + if (version == 0) { + *bound = version; + return 1; + } + + /*- + * 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_get_client_min_max_version - get minimum and maximum client version + * @s: The SSL connection + * @min_version: The minimum supported version + * @max_version: The maximum supported 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. + * + * Returns 0 on success or an SSL error reason number on failure. On failure + * min_version and max_version will also be set to 0. + */ +int ssl_get_client_min_max_version(const SSL *s, int *min_version, int *max_version) +{ + 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) + */ + *min_version = *max_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. + */ + *min_version = 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; + *min_version = method->version; + } else { + version = (single = method)->version; + *min_version = version; + hole = 0; + } + } + + *max_version = version; + + /* Fail if everything is disabled */ + if (version == 0) + return SSL_R_NO_PROTOCOLS_AVAILABLE; + + return 0; +} + +/* + * ssl_set_client_hello_version - Work out what version we should be using for + * the initial ClientHello. + * + * @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 ver_min, ver_max, ret; + + ret = ssl_get_client_min_max_version(s, &ver_min, &ver_max); + + if (ret != 0) + return ret; + + s->client_version = s->version = ver_max; + return 0; +}