Fix various style issues in the extension parsing refactor
[openssl.git] / ssl / statem / statem_lib.c
index 990510a06bde6c73391966ba5e425fe7e2da2e29..120b2779321dba6d5404c6646761db5b02c33b2a 100644 (file)
@@ -152,6 +152,97 @@ static void ssl3_take_mac(SSL *s)
 }
 #endif
 
+static int compare_extensions(const void *p1, const void *p2)
+{
+    const RAW_EXTENSION *e1 = (const RAW_EXTENSION *)p1;
+    const RAW_EXTENSION *e2 = (const RAW_EXTENSION *)p2;
+
+    if (e1->type < e2->type)
+        return -1;
+    else if (e1->type > e2->type)
+        return 1;
+
+    return 0;
+}
+
+/*
+ * Gather a list of all the extensions. We don't actually process the content
+ * of the extensions yet, except to check their types.
+ *
+ * Per http://tools.ietf.org/html/rfc5246#section-7.4.1.4, there may not be
+ * more than one extension of the same type in a ClientHello or ServerHello.
+ * This function returns 1 if all extensions are unique and we have parsed their
+ * types, and 0 if the extensions contain duplicates, could not be successfully
+ * parsed, or an internal error occurred.
+ */
+int tls_collect_extensions(PACKET *packet, RAW_EXTENSION **res,
+                             size_t *numfound, int *ad)
+{
+    PACKET extensions = *packet;
+    size_t num_extensions = 0, i = 0;
+    RAW_EXTENSION *raw_extensions = NULL;
+
+    /* First pass: count the extensions. */
+    while (PACKET_remaining(&extensions) > 0) {
+        unsigned int type;
+        PACKET extension;
+
+        if (!PACKET_get_net_2(&extensions, &type) ||
+            !PACKET_get_length_prefixed_2(&extensions, &extension)) {
+            *ad = SSL_AD_DECODE_ERROR;
+            goto err;
+        }
+        num_extensions++;
+    }
+
+    if (num_extensions > 0) {
+        raw_extensions = OPENSSL_malloc(sizeof(*raw_extensions)
+                                        * num_extensions);
+        if (raw_extensions == NULL) {
+            *ad = SSL_AD_INTERNAL_ERROR;
+            SSLerr(SSL_F_TLS_PARSE_RAW_EXTENSIONS, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        /* Second pass: gather the extension types. */
+        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);
+                goto err;
+            }
+        }
+
+        if (PACKET_remaining(packet) != 0) {
+            *ad = SSL_AD_DECODE_ERROR;
+            SSLerr(SSL_F_TLS_PARSE_RAW_EXTENSIONS, SSL_R_LENGTH_MISMATCH);
+            goto err;
+        }
+        /* Sort the extensions and make sure there are no duplicates. */
+        qsort(raw_extensions, num_extensions, sizeof(*raw_extensions),
+              compare_extensions);
+        for (i = 1; i < num_extensions; i++) {
+            if (raw_extensions[i - 1].type == raw_extensions[i].type) {
+                *ad = SSL_AD_DECODE_ERROR;
+                goto err;
+            }
+        }
+    }
+
+    *res = raw_extensions;
+    *numfound = num_extensions;
+    return 1;
+
+ err:
+    OPENSSL_free(raw_extensions);
+    return 0;
+}
+
+
+
 MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL *s, PACKET *pkt)
 {
     int al;
@@ -391,6 +482,7 @@ int tls_get_message_header(SSL *s, int *mt)
                 }
                 s->s3->tmp.message_type = *mt = SSL3_MT_CHANGE_CIPHER_SPEC;
                 s->init_num = readbytes - 1;
+                s->init_msg = s->init_buf->data;
                 s->s3->tmp.message_size = readbytes;
                 return 1;
             } else if (recvd_type != SSL3_RT_HANDSHAKE) {
@@ -874,7 +966,7 @@ int ssl_set_version_bound(int method_version, int version, int *bound)
  *
  * Returns 0 on success or an SSL error reason number on failure.
  */
-int ssl_choose_server_version(SSL *s)
+int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello)
 {
     /*-
      * With version-flexible methods we have an initial state with:
@@ -886,11 +978,13 @@ int ssl_choose_server_version(SSL *s)
      * handle version.
      */
     int server_version = s->method->version;
-    int client_version = s->client_version;
+    int client_version = hello->version;
     const version_info *vent;
     const version_info *table;
     int disabled = 0;
 
+    s->client_version = client_version;
+
     switch (server_version) {
     default:
         if (version_cmp(s, client_version, s->version) < 0)