/* * 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_version_supported - Check that the specified `version` is supported by * `SSL *` instance * * @s: The SSL handle for the candidate method * @version: Protocol version to test against * * Returns 1 when supported, otherwise 0 */ int ssl_version_supported(const SSL *s, int version) { const version_info *vent; const version_info *table; switch (s->method->version) { default: /* Version should match method version for non-ANY method */ return version_cmp(s, version, s->version) == 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 && version_cmp(s, version, vent->version) <= 0; ++vent) { if (vent->cmeth != NULL && version_cmp(s, version, vent->version) == 0 && ssl_method_error(s, vent->cmeth()) == 0) { return 1; } } return 0; }
/* * 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; /* TODO(TLS1.3): Remove this before release */ if (version == TLS1_3_VERSION_DRAFT) version = TLS1_3_VERSION; 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) */ 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->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_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, CLIENTHELLO_MSG *hello) { /*- * 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 = hello->legacy_version; const version_info *vent; const version_info *table; int disabled = 0; RAW_EXTENSION *suppversions; s->client_version = client_version; switch (server_version) { default: /* * TODO(TLS1.3): This check will fail if someone attempts to do * renegotiation in TLS1.3 at the moment. We need to ensure we disable * renegotiation for TLS1.3 */ 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; } suppversions = tls_get_extension_by_type(hello->pre_proc_exts, hello->num_extensions, TLSEXT_TYPE_supported_versions); if (suppversions != NULL && !SSL_IS_DTLS(s)) { unsigned int candidate_vers = 0; unsigned int best_vers = 0; const SSL_METHOD *best_method = NULL; PACKET versionslist; if (!PACKET_as_length_prefixed_1(&suppversions->data, &versionslist)) { /* Trailing or invalid data? */ return SSL_R_LENGTH_MISMATCH; } while (PACKET_get_net_2(&versionslist, &candidate_vers)) { /* TODO(TLS1.3): Remove this before release */ if (candidate_vers == TLS1_3_VERSION_DRAFT) candidate_vers = TLS1_3_VERSION; /* * TODO(TLS1.3): There is some discussion on the TLS list about * wheter to ignore versions <TLS1.2 in supported_versions. At the * moment we honour them if present. To be reviewed later */ if (version_cmp(s, candidate_vers, best_vers) <= 0) continue; for (vent = table; vent->version != 0 && vent->version != (int)candidate_vers; ++vent) continue; if (vent->version != 0 && vent->smeth != NULL) { const SSL_METHOD *method; method = vent->smeth(); if (ssl_method_error(s, method) == 0) { best_vers = candidate_vers; best_method = method; } } } if (PACKET_remaining(&versionslist) != 0) { /* Trailing data? */ return SSL_R_LENGTH_MISMATCH; } if (best_vers > 0) { s->version = best_vers; s->method = best_method; return 0; } return SSL_R_UNSUPPORTED_PROTOCOL; } /* * If the supported versions extension isn't present, then the highest * version we can negotiate is TLSv1.2 */ if (version_cmp(s, client_version, TLS1_3_VERSION) >= 0) client_version = TLS1_2_VERSION; /* * No supported versions extension, so we just use the version supplied in * the ClientHello. */ 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; }