Address some review feedback comments for supported_versions
[openssl.git] / ssl / statem / statem_lib.c
index 69f1200d64dcb94c016b10de565ad53ed36d15db..2aadc77a77ffef06fb77c7a935cdddb002343d25 100644 (file)
@@ -152,6 +152,16 @@ static void ssl3_take_mac(SSL *s)
 }
 #endif
 
+/*
+ * Comparison function used in a call to qsort (see tls_collect_extensions()
+ * below.)
+ * The two arguments |p1| and |p2| are expected to be pointers to RAW_EXTENSIONs
+ *
+ * Returns:
+ *  1 if the type for p1 is greater than p2
+ *  0 if the type for p1 and p2 are the same
+ * -1 if the type for p1 is less than p2
+ */
 static int compare_extensions(const void *p1, const void *p2)
 {
     const RAW_EXTENSION *e1 = (const RAW_EXTENSION *)p1;
@@ -204,25 +214,25 @@ int tls_collect_extensions(PACKET *packet, RAW_EXTENSION **res,
                                         * num_extensions);
         if (raw_extensions == NULL) {
             *ad = SSL_AD_INTERNAL_ERROR;
-            SSLerr(SSL_F_TLS_PARSE_RAW_EXTENSIONS, ERR_R_MALLOC_FAILURE);
+            SSLerr(SSL_F_TLS_COLLECT_EXTENSIONS, ERR_R_MALLOC_FAILURE);
             goto err;
         }
 
-        /* Second pass: gather the extension types. */
+        /* Second pass: collect the extensions. */
         for (i = 0; i < num_extensions; i++) {
             if (!PACKET_get_net_2(packet, &raw_extensions[i].type) ||
                 !PACKET_get_length_prefixed_2(packet,
                                               &raw_extensions[i].data)) {
                 /* This should not happen. */
                 *ad = SSL_AD_INTERNAL_ERROR;
-                SSLerr(SSL_F_TLS_PARSE_RAW_EXTENSIONS, ERR_R_INTERNAL_ERROR);
+                SSLerr(SSL_F_TLS_COLLECT_EXTENSIONS, ERR_R_INTERNAL_ERROR);
                 goto err;
             }
         }
 
         if (PACKET_remaining(packet) != 0) {
             *ad = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_TLS_PARSE_RAW_EXTENSIONS, SSL_R_LENGTH_MISMATCH);
+            SSLerr(SSL_F_TLS_COLLECT_EXTENSIONS, SSL_R_LENGTH_MISMATCH);
             goto err;
         }
         /* Sort the extensions and make sure there are no duplicates. */
@@ -982,10 +992,11 @@ int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello)
      * handle version.
      */
     int server_version = s->method->version;
-    int client_version = hello->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;
 
@@ -1009,6 +1020,73 @@ int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello)
         break;
     }
 
+    suppversions = tls_get_extension_by_type(hello->pre_proc_exts,
+                                             hello->num_extensions,
+                                             TLSEXT_TYPE_supported_versions);
+
+    /*
+     * TODO(TLS1.3): We only look at this if our max protocol version is TLS1.3
+     * or above. Should we allow it for lower versions too?
+     */
+    if (suppversions != NULL && !SSL_IS_DTLS(s)
+            && (s->max_proto_version == 0
+                || TLS1_3_VERSION <= s->max_proto_version)) {
+        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;
+            if ((int)candidate_vers > s->client_version)
+                s->client_version = candidate_vers;
+            if (version_cmp(s, candidate_vers, best_vers) <= 0)
+                continue;
+            for (vent = table;
+                 vent->version != 0 && vent->version != (int)candidate_vers;
+                 ++vent)
+                ;
+            if (vent->version != 0) {
+                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;