Add SSL_set_fallback_version.

Alas, we will need a version fallback for TLS 1.3 again.

This deprecates SSL_MODE_SEND_FALLBACK_SCSV. Rather than supplying a
boolean, have BoringSSL be aware of the real maximum version so we can
change the TLS 1.3 anti-downgrade logic to kick in, even when
max_version is set to 1.2.

The fallback version replaces the maximum version when it is set for
almost all purposes, except for downgrade protection purposes.

BUG=chromium:630165

Change-Id: I4c841dcbc6e55a282b223dfe169ac89c83c8a01f
Reviewed-on: https://boringssl-review.googlesource.com/8882
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 45480e5..21b8674 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -681,7 +681,9 @@
  * version; see RFC 7507 for details.
  *
  * DO NOT ENABLE THIS if your application attempts a normal handshake. Only use
- * this in explicit fallback retries, following the guidance in RFC 7507. */
+ * this in explicit fallback retries, following the guidance in RFC 7507.
+ *
+ * This flag is deprecated. Use |SSL_set_fallback_version| instead. */
 #define SSL_MODE_SEND_FALLBACK_SCSV 0x00000400L
 
 /* SSL_CTX_set_mode enables all modes set in |mode| (which should be one or more
@@ -3059,6 +3061,22 @@
 OPENSSL_EXPORT void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx,
                                                                    int enable);
 
+/* SSL_set_fallback_version, on a client, sets the effective maximum protocol
+ * version. This may be used when implementing a version
+ * fallback to work around buggy servers.
+ *
+ * For purposes of the TLS protocol itself, including assembling the ClientHello
+ * and which ServerHello versions are accepted, this value is used as the
+ * maximum version. However, if this value differs from the real maximum
+ * version, as set by |SSL_set_max_version|, TLS_FALLBACK_SCSV (see RFC 7507)
+ * will be sent. Further, the TLS 1.3 anti-downgrade logic will be conditioned
+ * on the true maximum version.
+ *
+ * For instance, a fallback from a TLS 1.3 ClientHello to a TLS 1.2 ClientHello
+ * should set this value to |TLS1_2_VERSION| and call |SSL_set_max_version| with
+ * |TLS1_3_VERSION|. */
+OPENSSL_EXPORT void SSL_set_fallback_version(SSL *ssl, uint16_t version);
+
 
 /* Deprecated functions. */
 
@@ -3960,6 +3978,11 @@
    * is normalized in DTLS. */
   uint16_t min_version;
 
+  /* fallback_version is the effective maximum acceptable protocol version for
+   * use with a version fallback, or zero if unset. Note this version is
+   * normalized in DTLS. */
+  uint16_t fallback_version;
+
   /* method is the method table corresponding to the current protocol (DTLS or
    * TLS). */
   const SSL_PROTOCOL_METHOD *method;
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index 52ff212..6f9b2fc 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -548,8 +548,10 @@
   return ret;
 }
 
-int ssl_write_client_cipher_list(SSL *ssl, CBB *out, uint16_t min_version,
-                                 uint16_t max_version) {
+static int ssl_write_client_cipher_list(SSL *ssl, CBB *out,
+                                        uint16_t min_version,
+                                        uint16_t max_version,
+                                        uint16_t real_max_version) {
   /* Prepare disabled cipher masks. */
   ssl_set_client_disabled(ssl);
 
@@ -596,17 +598,20 @@
     ssl->s3->tmp.extensions.sent |= (1u << 0);
   }
 
-  if ((ssl->mode & SSL_MODE_SEND_FALLBACK_SCSV) &&
-      !CBB_add_u16(&child, SSL3_CK_FALLBACK_SCSV & 0xffff)) {
-    return 0;
+  if ((ssl->mode & SSL_MODE_SEND_FALLBACK_SCSV) ||
+      real_max_version > max_version) {
+    if (!CBB_add_u16(&child, SSL3_CK_FALLBACK_SCSV & 0xffff)) {
+      return 0;
+    }
   }
 
   return CBB_flush(out);
 }
 
 int ssl_add_client_hello_body(SSL *ssl, CBB *body) {
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+  uint16_t min_version, max_version, real_max_version;
+  if (!ssl_get_full_version_range(ssl, &min_version, &max_version,
+                                  &real_max_version)) {
     return 0;
   }
 
@@ -633,7 +638,8 @@
 
   size_t header_len =
       SSL_IS_DTLS(ssl) ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH;
-  if (!ssl_write_client_cipher_list(ssl, body, min_version, max_version) ||
+  if (!ssl_write_client_cipher_list(ssl, body, min_version, max_version,
+                                    real_max_version) ||
       !CBB_add_u8(body, 1 /* one compression method */) ||
       !CBB_add_u8(body, 0 /* null compression */) ||
       !ssl_add_clienthello_tlsext(ssl, body, header_len + CBB_len(body))) {
@@ -791,8 +797,9 @@
 
   server_version = ssl->method->version_from_wire(server_wire_version);
 
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version) ||
+  uint16_t min_version, max_version, real_max_version;
+  if (!ssl_get_full_version_range(ssl, &min_version, &max_version,
+                                  &real_max_version) ||
       server_version < min_version || server_version > max_version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
     al = SSL_AD_PROTOCOL_VERSION;
@@ -843,7 +850,7 @@
    * settled down. */
   static const uint8_t kDowngradeTLS12[8] = {0x44, 0x4f, 0x57, 0x4e,
                                              0x47, 0x52, 0x44, 0x01};
-  if (max_version >= TLS1_3_VERSION &&
+  if (real_max_version >= TLS1_3_VERSION &&
       ssl3_protocol_version(ssl) <= TLS1_2_VERSION &&
       memcmp(ssl->s3->server_random + SSL3_RANDOM_SIZE - 8, kDowngradeTLS12,
              8) == 0) {
diff --git a/ssl/internal.h b/ssl/internal.h
index 5d14eba..f5180a7 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1238,9 +1238,6 @@
 STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *ssl);
 int ssl_verify_alarm_type(long type);
 
-int ssl_write_client_cipher_list(SSL *ssl, CBB *out, uint16_t min_version,
-                                 uint16_t max_version);
-
 int ssl3_get_finished(SSL *ssl);
 int ssl3_send_change_cipher_spec(SSL *ssl);
 void ssl3_cleanup_key_block(SSL *ssl);
@@ -1411,10 +1408,21 @@
  * |version|. */
 const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version);
 
-/* ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the
- * minimum and maximum enabled protocol versions, respectively. */
+/* ssl_get_full_version_range sets |*out_min_version|, |*out_fallback_version|,
+ * and |*out_max_version| to the minimum, fallback, and maximum enabled protocol
+ * versions, respectively. The fallback version is the effective maximium
+ * version used throughout the stack and the maximum version is the true maximum
+ * for downgrade purposes. */
+int ssl_get_full_version_range(const SSL *ssl, uint16_t *out_min_version,
+                               uint16_t *out_fallback_version,
+                               uint16_t *out_max_version);
+
+/* ssl_get_version_range sets |*out_min_version| and
+ * |*out_effective_max_version| to the minimum and maximum enabled protocol
+ * versions, respectively. Note that, if there is a fallback version set, it
+ * returns it as the maximum version. */
 int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
-                          uint16_t *out_max_version);
+                          uint16_t *out_effective_max_version);
 
 /* ssl3_protocol_version returns |ssl|'s protocol version. It is an error to
  * call this function before the version is determined. */
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 9a4e200..716a040 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -860,6 +860,10 @@
   ssl->max_version = ssl->method->version_from_wire(version);
 }
 
+void SSL_set_fallback_version(SSL *ssl, uint16_t version) {
+  ssl->fallback_version = ssl->method->version_from_wire(version);
+}
+
 uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) {
   ctx->options |= options;
   return ctx->options;
@@ -2562,8 +2566,9 @@
 
 static const size_t kVersionsLen = sizeof(kVersions) / sizeof(kVersions[0]);
 
-int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
-                          uint16_t *out_max_version) {
+int ssl_get_full_version_range(const SSL *ssl, uint16_t *out_min_version,
+                               uint16_t *out_fallback_version,
+                               uint16_t *out_max_version) {
   /* For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but
    * DTLS 1.0 should be mapped to TLS 1.1. */
   uint32_t options = ssl->options;
@@ -2623,16 +2628,32 @@
     }
   }
 
-  if (!any_enabled) {
+  uint16_t fallback_version = max_version;
+  if (ssl->fallback_version != 0 && ssl->fallback_version < fallback_version) {
+    fallback_version = ssl->fallback_version;
+  }
+
+  if (!any_enabled || fallback_version < min_version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION);
     return 0;
   }
 
   *out_min_version = min_version;
+  *out_fallback_version = fallback_version;
   *out_max_version = max_version;
   return 1;
 }
 
+int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+                          uint16_t *out_effective_max_version) {
+  /* This function returns the effective maximum version and not the fallback
+   * version. */
+  uint16_t real_max_version_unused;
+  return ssl_get_full_version_range(ssl, out_min_version,
+                                    out_effective_max_version,
+                                    &real_max_version_unused);
+}
+
 uint16_t ssl3_protocol_version(const SSL *ssl) {
   assert(ssl->s3->have_version);
   return ssl->method->version_from_wire(ssl->version);
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index d1aadb8..edf0fe0 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1316,6 +1316,9 @@
   if (config->max_version != 0) {
     SSL_set_max_version(ssl.get(), (uint16_t)config->max_version);
   }
+  if (config->fallback_version != 0) {
+    SSL_set_fallback_version(ssl.get(), (uint16_t)config->fallback_version);
+  }
   if (config->mtu != 0) {
     SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU);
     SSL_set_mtu(ssl.get(), config->mtu);
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 4997836..fd263e6 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -3864,6 +3864,51 @@
 		shouldFail:         true,
 		expectedLocalError: "tls: downgrade from TLS 1.3 detected",
 	})
+
+	// Test that FALLBACK_SCSV is sent and that the downgrade signal works
+	// behave correctly when both real maximum and fallback versions are
+	// set.
+	testCases = append(testCases, testCase{
+		name: "Downgrade-TLS12-Client-Fallback",
+		config: Config{
+			Bugs: ProtocolBugs{
+				FailIfNotFallbackSCSV: true,
+			},
+		},
+		flags: []string{
+			"-max-version", strconv.Itoa(VersionTLS13),
+			"-fallback-version", strconv.Itoa(VersionTLS12),
+		},
+		shouldFail:    true,
+		expectedError: ":DOWNGRADE_DETECTED:",
+	})
+	testCases = append(testCases, testCase{
+		name: "Downgrade-TLS12-Client-FallbackEqualsMax",
+		flags: []string{
+			"-max-version", strconv.Itoa(VersionTLS12),
+			"-fallback-version", strconv.Itoa(VersionTLS12),
+		},
+	})
+
+	// On TLS 1.2 fallback, 1.3 ServerHellos are forbidden. (We would rather
+	// just have such connections fail than risk getting confused because we
+	// didn't sent the 1.3 ClientHello.)
+	testCases = append(testCases, testCase{
+		name: "Downgrade-TLS12-Fallback-CheckVersion",
+		config: Config{
+			Bugs: ProtocolBugs{
+				NegotiateVersion:      VersionTLS13,
+				FailIfNotFallbackSCSV: true,
+			},
+		},
+		flags: []string{
+			"-max-version", strconv.Itoa(VersionTLS13),
+			"-fallback-version", strconv.Itoa(VersionTLS12),
+		},
+		shouldFail:    true,
+		expectedError: ":UNSUPPORTED_PROTOCOL:",
+	})
+
 }
 
 func addMinimumVersionTests() {
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index b1a0792..d242f7a 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -144,6 +144,7 @@
   { "-port", &TestConfig::port },
   { "-min-version", &TestConfig::min_version },
   { "-max-version", &TestConfig::max_version },
+  { "-fallback-version", &TestConfig::fallback_version },
   { "-mtu", &TestConfig::mtu },
   { "-export-keying-material", &TestConfig::export_keying_material },
   { "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations },
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 73bdc6e..690fbe7 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -66,6 +66,7 @@
   std::string expected_signed_cert_timestamps;
   int min_version = 0;
   int max_version = 0;
+  int fallback_version = 0;
   int mtu = 0;
   bool implicit_handshake = false;
   bool use_early_callback = false;