Export SSL_bytes_to_cipher_list()
authorBenjamin Kaduk <bkaduk@akamai.com>
Mon, 30 Jan 2017 17:24:17 +0000 (11:24 -0600)
committerRichard Levitte <levitte@openssl.org>
Thu, 23 Feb 2017 18:40:25 +0000 (19:40 +0100)
Move ssl_bytes_to_cipher_list() to ssl_lib.c and create a public
wrapper around it.  This lets application early callbacks easily get
SSL_CIPHER objects from the raw ciphers bytes without having to
reimplement the parsing code.  In particular, they do not need to
know the details of the sslv2 format ClientHello's ciphersuite
specifications.

Document the new public function, including the arguably buggy behavior
of modifying the supplied SSL object.  On the face of it, such a function
should be able to be pure, just a direct translation of wire octets to
internal data structures.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2279)

doc/man3/SSL_get_ciphers.pod
include/openssl/ssl.h
ssl/ssl_err.c
ssl/ssl_lib.c
ssl/ssl_locl.h
ssl/statem/statem_srvr.c
util/libssl.num

index ce0be6e6c1b139fabd7c63e6a9e3b8dbbf23ceda..5933bf584973bc8065177d879617507e1de765c5 100644 (file)
@@ -3,7 +3,8 @@
 =head1 NAME
 
 SSL_get1_supported_ciphers, SSL_get_client_ciphers,
-SSL_get_ciphers, SSL_CTX_get_ciphers, SSL_get_cipher_list
+SSL_get_ciphers, SSL_CTX_get_ciphers,
+SSL_bytes_to_cipher_list, SSL_get_cipher_list
 - get list of available SSL_CIPHERs
 
 =head1 SYNOPSIS
@@ -14,6 +15,9 @@ SSL_get_ciphers, SSL_CTX_get_ciphers, SSL_get_cipher_list
  STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx);
  STACK_OF(SSL_CIPHER) *SSL_get1_supported_ciphers(SSL *s);
  STACK_OF(SSL_CIPHER) *SSL_get_client_ciphers(const SSL *ssl);
+ STACK_OF(SSL_CIPHER) *SSL_bytes_to_cipher_list(SSL *s,
+                                                const unsigned char *bytes,
+                                                size_t len, int isv2format)
  const char *SSL_get_cipher_list(const SSL *ssl, int priority);
 
 =head1 DESCRIPTION
@@ -41,6 +45,13 @@ SSL_get_client_ciphers() returns the stack of available SSL_CIPHERs matching the
 list received from the client on B<ssl>. If B<ssl> is NULL, no ciphers are
 available, or B<ssl> is not operating in server mode, NULL is returned.
 
+SSL_bytes_to_cipher_list() treats the supplied B<len> octets in B<bytes>
+as a wire-protocol cipher suite specification (in the three-octet-per-cipher
+SSLv2 wire format if B<isv2format> is nonzero; otherwise the two-octet
+SSLv3/TLS wire format), and parses the cipher suites supported by the library
+into the returned stack of SSL_CIPHER objects.  Unsupported cipher suites
+are ignored, and NULL is returned on error.
+
 SSL_get_cipher_list() returns a pointer to the name of the SSL_CIPHER
 listed for B<ssl> with B<priority>. If B<ssl> is NULL, no ciphers are
 available, or there are less ciphers than B<priority> available, NULL
@@ -63,10 +74,19 @@ free the return value itself.
 The stack returned by SSL_get1_supported_ciphers() should be freed using
 sk_SSL_CIPHER_free().
 
+The stack returned by SSL_bytes_to_cipher_list() should be freed using
+sk_SSL_CIPHER_free().
+
 =head1 RETURN VALUES
 
 See DESCRIPTION
 
+=head1 BUGS
+
+The implementation of SSL_bytes_to_cipher_list() mutates state in the
+supplied SSL object B<s>; SSL_bytes_to_cipher_list() should not be called
+on a server SSL object after that server has processed the received ClientHello.
+
 =head1 SEE ALSO
 
 L<ssl(7)>, L<SSL_CTX_set_cipher_list(3)>,
index 1c58dcc85591dbbe4cd2e2a58a06bf6fbd19934a..3716922b42ad8bd2de3f53c11e7239e642ab7896 100644 (file)
@@ -1820,6 +1820,9 @@ __owur int SSL_COMP_add_compression_method(int id, COMP_METHOD *cm);
 const SSL_CIPHER *SSL_CIPHER_find(SSL *ssl, const unsigned char *ptr);
 int SSL_CIPHER_get_cipher_nid(const SSL_CIPHER *c);
 int SSL_CIPHER_get_digest_nid(const SSL_CIPHER *c);
+STACK_OF(SSL_CIPHER) *SSL_bytes_to_cipher_list(SSL *s,
+                                               const unsigned char *bytes,
+                                               size_t len, int isv2format);
 
 /* TLS extensions functions */
 __owur int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len);
@@ -2087,6 +2090,7 @@ int ERR_load_SSL_strings(void);
 /* Function codes. */
 # define SSL_F_ADD_CLIENT_KEY_SHARE_EXT                   438
 # define SSL_F_ADD_KEY_SHARE                              512
+# define SSL_F_BYTES_TO_CIPHER_LIST                       519
 # define SSL_F_CHECK_SUITEB_CIPHER_LIST                   331
 # define SSL_F_CT_MOVE_SCTS                               345
 # define SSL_F_CT_STRICT                                  349
index 341712cdb71469bf7267f71b1cb0813f0ae9946c..81b3a678529f5891d3f481722c9c6566e0cb5b82 100644 (file)
@@ -21,6 +21,7 @@
 static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_ADD_CLIENT_KEY_SHARE_EXT), "add_client_key_share_ext"},
     {ERR_FUNC(SSL_F_ADD_KEY_SHARE), "add_key_share"},
+    {ERR_FUNC(SSL_F_BYTES_TO_CIPHER_LIST), "bytes_to_cipher_list"},
     {ERR_FUNC(SSL_F_CHECK_SUITEB_CIPHER_LIST), "check_suiteb_cipher_list"},
     {ERR_FUNC(SSL_F_CT_MOVE_SCTS), "ct_move_scts"},
     {ERR_FUNC(SSL_F_CT_STRICT), "ct_strict"},
@@ -116,7 +117,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
      "ssl_add_serverhello_use_srtp_ext"},
     {ERR_FUNC(SSL_F_SSL_BAD_METHOD), "ssl_bad_method"},
     {ERR_FUNC(SSL_F_SSL_BUILD_CERT_CHAIN), "ssl_build_cert_chain"},
-    {ERR_FUNC(SSL_F_SSL_BYTES_TO_CIPHER_LIST), "ssl_bytes_to_cipher_list"},
+    {ERR_FUNC(SSL_F_SSL_BYTES_TO_CIPHER_LIST), "SSL_bytes_to_cipher_list"},
     {ERR_FUNC(SSL_F_SSL_CERT_ADD0_CHAIN_CERT), "ssl_cert_add0_chain_cert"},
     {ERR_FUNC(SSL_F_SSL_CERT_DUP), "ssl_cert_dup"},
     {ERR_FUNC(SSL_F_SSL_CERT_NEW), "ssl_cert_new"},
index dea2dac05b976680c486b42a163420b40a0c73bf..56c6a2442c9995255aa7777c06166d702f00edc9 100644 (file)
@@ -4402,3 +4402,162 @@ int ssl_log_secret(SSL *ssl,
                           secret_len);
 }
 
+
+STACK_OF(SSL_CIPHER) *SSL_bytes_to_cipher_list(SSL *s,
+                                               const unsigned char *bytes,
+                                               size_t len, int isv2format)
+{
+    int alert;
+    PACKET pkt;
+
+    if (!PACKET_buf_init(&pkt, bytes, len))
+        return 0;
+    return bytes_to_cipher_list(s, &pkt, NULL, isv2format, &alert);
+}
+
+#define SSLV2_CIPHER_LEN    3
+
+STACK_OF(SSL_CIPHER) *bytes_to_cipher_list(SSL *s,
+                                           PACKET *cipher_suites,
+                                           STACK_OF(SSL_CIPHER) **skp,
+                                           int sslv2format, int *al)
+{
+    const SSL_CIPHER *c;
+    STACK_OF(SSL_CIPHER) *sk;
+    int n;
+    /* 3 = SSLV2_CIPHER_LEN > TLS_CIPHER_LEN = 2. */
+    unsigned char cipher[SSLV2_CIPHER_LEN];
+
+    s->s3->send_connection_binding = 0;
+
+    n = sslv2format ? SSLV2_CIPHER_LEN : TLS_CIPHER_LEN;
+
+    if (PACKET_remaining(cipher_suites) == 0) {
+        SSLerr(SSL_F_BYTES_TO_CIPHER_LIST, SSL_R_NO_CIPHERS_SPECIFIED);
+        *al = SSL_AD_ILLEGAL_PARAMETER;
+        return NULL;
+    }
+
+    if (PACKET_remaining(cipher_suites) % n != 0) {
+        SSLerr(SSL_F_BYTES_TO_CIPHER_LIST,
+               SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST);
+        *al = SSL_AD_DECODE_ERROR;
+        return NULL;
+    }
+
+    sk = sk_SSL_CIPHER_new_null();
+    if (sk == NULL) {
+        SSLerr(SSL_F_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
+        *al = SSL_AD_INTERNAL_ERROR;
+        return NULL;
+    }
+
+    OPENSSL_free(s->s3->tmp.ciphers_raw);
+    s->s3->tmp.ciphers_raw = NULL;
+    s->s3->tmp.ciphers_rawlen = 0;
+
+    if (sslv2format) {
+        size_t numciphers = PACKET_remaining(cipher_suites) / n;
+        PACKET sslv2ciphers = *cipher_suites;
+        unsigned int leadbyte;
+        unsigned char *raw;
+
+        /*
+         * We store the raw ciphers list in SSLv3+ format so we need to do some
+         * preprocessing to convert the list first. If there are any SSLv2 only
+         * ciphersuites with a non-zero leading byte then we are going to
+         * slightly over allocate because we won't store those. But that isn't a
+         * problem.
+         */
+        raw = OPENSSL_malloc(numciphers * TLS_CIPHER_LEN);
+        s->s3->tmp.ciphers_raw = raw;
+        if (raw == NULL) {
+            *al = SSL_AD_INTERNAL_ERROR;
+            goto err;
+        }
+        for (s->s3->tmp.ciphers_rawlen = 0;
+             PACKET_remaining(&sslv2ciphers) > 0;
+             raw += TLS_CIPHER_LEN) {
+            if (!PACKET_get_1(&sslv2ciphers, &leadbyte)
+                    || (leadbyte == 0
+                        && !PACKET_copy_bytes(&sslv2ciphers, raw,
+                                              TLS_CIPHER_LEN))
+                    || (leadbyte != 0
+                        && !PACKET_forward(&sslv2ciphers, TLS_CIPHER_LEN))) {
+                *al = SSL_AD_INTERNAL_ERROR;
+                OPENSSL_free(s->s3->tmp.ciphers_raw);
+                s->s3->tmp.ciphers_raw = NULL;
+                s->s3->tmp.ciphers_rawlen = 0;
+                goto err;
+            }
+            if (leadbyte == 0)
+                s->s3->tmp.ciphers_rawlen += TLS_CIPHER_LEN;
+        }
+    } else if (!PACKET_memdup(cipher_suites, &s->s3->tmp.ciphers_raw,
+                           &s->s3->tmp.ciphers_rawlen)) {
+        *al = SSL_AD_INTERNAL_ERROR;
+        goto err;
+    }
+
+    while (PACKET_copy_bytes(cipher_suites, cipher, n)) {
+        /*
+         * SSLv3 ciphers wrapped in an SSLv2-compatible ClientHello have the
+         * first byte set to zero, while true SSLv2 ciphers have a non-zero
+         * first byte. We don't support any true SSLv2 ciphers, so skip them.
+         */
+        if (sslv2format && cipher[0] != '\0')
+            continue;
+
+        /* Check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV */
+        if ((cipher[n - 2] == ((SSL3_CK_SCSV >> 8) & 0xff)) &&
+            (cipher[n - 1] == (SSL3_CK_SCSV & 0xff))) {
+            /* SCSV fatal if renegotiating */
+            if (s->renegotiate) {
+                SSLerr(SSL_F_BYTES_TO_CIPHER_LIST,
+                       SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING);
+                *al = SSL_AD_HANDSHAKE_FAILURE;
+                goto err;
+            }
+            s->s3->send_connection_binding = 1;
+            continue;
+        }
+
+        /* Check for TLS_FALLBACK_SCSV */
+        if ((cipher[n - 2] == ((SSL3_CK_FALLBACK_SCSV >> 8) & 0xff)) &&
+            (cipher[n - 1] == (SSL3_CK_FALLBACK_SCSV & 0xff))) {
+            /*
+             * The SCSV indicates that the client previously tried a higher
+             * version. Fail if the current version is an unexpected
+             * downgrade.
+             */
+            if (!ssl_check_version_downgrade(s)) {
+                SSLerr(SSL_F_BYTES_TO_CIPHER_LIST,
+                       SSL_R_INAPPROPRIATE_FALLBACK);
+                *al = SSL_AD_INAPPROPRIATE_FALLBACK;
+                goto err;
+            }
+            continue;
+        }
+
+        /* For SSLv2-compat, ignore leading 0-byte. */
+        c = ssl_get_cipher_by_char(s, sslv2format ? &cipher[1] : cipher, 1);
+        if (c != NULL) {
+            if (!sk_SSL_CIPHER_push(sk, c)) {
+                SSLerr(SSL_F_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
+                *al = SSL_AD_INTERNAL_ERROR;
+                goto err;
+            }
+        }
+    }
+    if (PACKET_remaining(cipher_suites) > 0) {
+        *al = SSL_AD_INTERNAL_ERROR;
+        SSLerr(SSL_F_BYTES_TO_CIPHER_LIST, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    *skp = sk;
+    return sk;
+ err:
+    sk_SSL_CIPHER_free(sk);
+    return NULL;
+}
index ff1f598fb434d94c72f0d6310c413751929b769d..59605f59835c3a5d10e61fc3fb5f9900558a9f43 100644 (file)
@@ -1991,6 +1991,11 @@ __owur STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *meth,
                                                     **sorted,
                                                     const char *rule_str,
                                                     CERT *c);
+__owur STACK_OF(SSL_CIPHER) *bytes_to_cipher_list(SSL *s,
+                                                  PACKET *cipher_suites,
+                                                  STACK_OF(SSL_CIPHER)
+                                                  **skp, int sslv2format,
+                                                  int *al);
 void ssl_update_cache(SSL *s, int mode);
 __owur int ssl_cipher_get_evp(const SSL_SESSION *s, const EVP_CIPHER **enc,
                               const EVP_MD **md, int *mac_pkey_type,
index 2cbc219a3a694b050bcc7044c0dbb979954be183..00e69a671e44ab3281b633ac3a07cc26193d1c1c 100644 (file)
 
 static int tls_construct_encrypted_extensions(SSL *s, WPACKET *pkt);
 static int tls_construct_hello_retry_request(SSL *s, WPACKET *pkt);
-static STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s,
-                                                      PACKET *cipher_suites,
-                                                      STACK_OF(SSL_CIPHER)
-                                                      **skp, int sslv2format,
-                                                      int *al);
 
 /*
  * ossl_statem_server13_read_transition() encapsulates the logic for the allowed
@@ -1551,8 +1546,8 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
         }
     }
 
-    if (ssl_bytes_to_cipher_list(s, &clienthello.ciphersuites, &ciphers,
-                                 clienthello.isv2, &al) == NULL) {
+    if (bytes_to_cipher_list(s, &clienthello.ciphersuites, &ciphers,
+                             clienthello.isv2, &al) == NULL) {
         goto f_err;
     }
 
@@ -3508,153 +3503,6 @@ static int tls_construct_encrypted_extensions(SSL *s, WPACKET *pkt)
     return 1;
 }
 
-#define SSLV2_CIPHER_LEN    3
-
-STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s,
-                                               PACKET *cipher_suites,
-                                               STACK_OF(SSL_CIPHER) **skp,
-                                               int sslv2format, int *al)
-{
-    const SSL_CIPHER *c;
-    STACK_OF(SSL_CIPHER) *sk;
-    int n;
-    /* 3 = SSLV2_CIPHER_LEN > TLS_CIPHER_LEN = 2. */
-    unsigned char cipher[SSLV2_CIPHER_LEN];
-
-    s->s3->send_connection_binding = 0;
-
-    n = sslv2format ? SSLV2_CIPHER_LEN : TLS_CIPHER_LEN;
-
-    if (PACKET_remaining(cipher_suites) == 0) {
-        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, SSL_R_NO_CIPHERS_SPECIFIED);
-        *al = SSL_AD_ILLEGAL_PARAMETER;
-        return NULL;
-    }
-
-    if (PACKET_remaining(cipher_suites) % n != 0) {
-        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST,
-               SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST);
-        *al = SSL_AD_DECODE_ERROR;
-        return NULL;
-    }
-
-    sk = sk_SSL_CIPHER_new_null();
-    if (sk == NULL) {
-        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
-        *al = SSL_AD_INTERNAL_ERROR;
-        return NULL;
-    }
-
-    OPENSSL_free(s->s3->tmp.ciphers_raw);
-    s->s3->tmp.ciphers_raw = NULL;
-    s->s3->tmp.ciphers_rawlen = 0;
-
-    if (sslv2format) {
-        size_t numciphers = PACKET_remaining(cipher_suites) / n;
-        PACKET sslv2ciphers = *cipher_suites;
-        unsigned int leadbyte;
-        unsigned char *raw;
-
-        /*
-         * We store the raw ciphers list in SSLv3+ format so we need to do some
-         * preprocessing to convert the list first. If there are any SSLv2 only
-         * ciphersuites with a non-zero leading byte then we are going to
-         * slightly over allocate because we won't store those. But that isn't a
-         * problem.
-         */
-        raw = OPENSSL_malloc(numciphers * TLS_CIPHER_LEN);
-        s->s3->tmp.ciphers_raw = raw;
-        if (raw == NULL) {
-            *al = SSL_AD_INTERNAL_ERROR;
-            goto err;
-        }
-        for (s->s3->tmp.ciphers_rawlen = 0;
-             PACKET_remaining(&sslv2ciphers) > 0;
-             raw += TLS_CIPHER_LEN) {
-            if (!PACKET_get_1(&sslv2ciphers, &leadbyte)
-                    || (leadbyte == 0
-                        && !PACKET_copy_bytes(&sslv2ciphers, raw,
-                                              TLS_CIPHER_LEN))
-                    || (leadbyte != 0
-                        && !PACKET_forward(&sslv2ciphers, TLS_CIPHER_LEN))) {
-                *al = SSL_AD_INTERNAL_ERROR;
-                OPENSSL_free(s->s3->tmp.ciphers_raw);
-                s->s3->tmp.ciphers_raw = NULL;
-                s->s3->tmp.ciphers_rawlen = 0;
-                goto err;
-            }
-            if (leadbyte == 0)
-                s->s3->tmp.ciphers_rawlen += TLS_CIPHER_LEN;
-        }
-    } else if (!PACKET_memdup(cipher_suites, &s->s3->tmp.ciphers_raw,
-                           &s->s3->tmp.ciphers_rawlen)) {
-        *al = SSL_AD_INTERNAL_ERROR;
-        goto err;
-    }
-
-    while (PACKET_copy_bytes(cipher_suites, cipher, n)) {
-        /*
-         * SSLv3 ciphers wrapped in an SSLv2-compatible ClientHello have the
-         * first byte set to zero, while true SSLv2 ciphers have a non-zero
-         * first byte. We don't support any true SSLv2 ciphers, so skip them.
-         */
-        if (sslv2format && cipher[0] != '\0')
-            continue;
-
-        /* Check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV */
-        if ((cipher[n - 2] == ((SSL3_CK_SCSV >> 8) & 0xff)) &&
-            (cipher[n - 1] == (SSL3_CK_SCSV & 0xff))) {
-            /* SCSV fatal if renegotiating */
-            if (s->renegotiate) {
-                SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST,
-                       SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING);
-                *al = SSL_AD_HANDSHAKE_FAILURE;
-                goto err;
-            }
-            s->s3->send_connection_binding = 1;
-            continue;
-        }
-
-        /* Check for TLS_FALLBACK_SCSV */
-        if ((cipher[n - 2] == ((SSL3_CK_FALLBACK_SCSV >> 8) & 0xff)) &&
-            (cipher[n - 1] == (SSL3_CK_FALLBACK_SCSV & 0xff))) {
-            /*
-             * The SCSV indicates that the client previously tried a higher
-             * version. Fail if the current version is an unexpected
-             * downgrade.
-             */
-            if (!ssl_check_version_downgrade(s)) {
-                SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST,
-                       SSL_R_INAPPROPRIATE_FALLBACK);
-                *al = SSL_AD_INAPPROPRIATE_FALLBACK;
-                goto err;
-            }
-            continue;
-        }
-
-        /* For SSLv2-compat, ignore leading 0-byte. */
-        c = ssl_get_cipher_by_char(s, sslv2format ? &cipher[1] : cipher, 0);
-        if (c != NULL) {
-            if (!sk_SSL_CIPHER_push(sk, c)) {
-                SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
-                *al = SSL_AD_INTERNAL_ERROR;
-                goto err;
-            }
-        }
-    }
-    if (PACKET_remaining(cipher_suites) > 0) {
-        *al = SSL_AD_INTERNAL_ERROR;
-        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, ERR_R_INTERNAL_ERROR);
-        goto err;
-    }
-
-    *skp = sk;
-    return sk;
- err:
-    sk_SSL_CIPHER_free(sk);
-    return NULL;
-}
-
 static int tls_construct_hello_retry_request(SSL *s, WPACKET *pkt)
 {
     int al = SSL_AD_INTERNAL_ERROR;
index 32b5f7654960fb7579390d96555133e3a1eb8cec..7f5100290b6bb81ece355dea37f2f76107db2a4a 100644 (file)
@@ -415,3 +415,4 @@ SSL_CTX_get_keylog_callback             415 1_1_1   EXIST::FUNCTION:
 SSL_get_peer_signature_type_nid         416    1_1_1   EXIST::FUNCTION:
 SSL_key_update                          417    1_1_1   EXIST::FUNCTION:
 SSL_get_key_update_type                 418    1_1_1   EXIST::FUNCTION:
+SSL_bytes_to_cipher_list                419    1_1_1   EXIST::FUNCTION: