Convert ssl3_send_client_hello to CBB.

Start converting the ones we can right now. Some of the messier ones
resize init_buf rather than assume the initial size is sufficient, so
those will probably wait until init_buf is gone and the handshake's
undergone some more invasive surgery. The async ones will also require
some thought. But some can be incrementally converted now.

BUG=468889

Change-Id: I0bc22e4dca37d9d671a488c42eba864c51933638
Reviewed-on: https://boringssl-review.googlesource.com/6190
Reviewed-by: Adam Langley <alangley@gmail.com>
This commit is contained in:
David Benjamin 2015-10-10 14:13:23 -04:00 committed by Adam Langley
parent 978f16ea08
commit e8d53508ca
8 changed files with 224 additions and 209 deletions

View File

@ -365,6 +365,55 @@ static bool TestCBBPrefixed() {
return buf_len == sizeof(kExpected) && memcmp(buf, kExpected, buf_len) == 0;
}
static bool TestCBBDiscardChild() {
ScopedCBB cbb;
CBB contents, inner_contents, inner_inner_contents;
if (!CBB_init(cbb.get(), 0) ||
!CBB_add_u8(cbb.get(), 0xaa)) {
return false;
}
// Discarding |cbb|'s children preserves the byte written.
CBB_discard_child(cbb.get());
if (!CBB_add_u8_length_prefixed(cbb.get(), &contents) ||
!CBB_add_u8_length_prefixed(cbb.get(), &contents) ||
!CBB_add_u8(&contents, 0xbb) ||
!CBB_add_u16_length_prefixed(cbb.get(), &contents) ||
!CBB_add_u16(&contents, 0xcccc) ||
!CBB_add_u24_length_prefixed(cbb.get(), &contents) ||
!CBB_add_u24(&contents, 0xdddddd) ||
!CBB_add_u8_length_prefixed(cbb.get(), &contents) ||
!CBB_add_u8(&contents, 0xff) ||
!CBB_add_u8_length_prefixed(&contents, &inner_contents) ||
!CBB_add_u8(&inner_contents, 0x42) ||
!CBB_add_u16_length_prefixed(&inner_contents, &inner_inner_contents) ||
!CBB_add_u8(&inner_inner_contents, 0x99)) {
return false;
}
// Discard everything from |inner_contents| down.
CBB_discard_child(&contents);
uint8_t *buf;
size_t buf_len;
if (!CBB_finish(cbb.get(), &buf, &buf_len)) {
return false;
}
ScopedOpenSSLBytes scoper(buf);
static const uint8_t kExpected[] = {
0xaa,
0,
1, 0xbb,
0, 2, 0xcc, 0xcc,
0, 0, 3, 0xdd, 0xdd, 0xdd,
1, 0xff,
};
return buf_len == sizeof(kExpected) && memcmp(buf, kExpected, buf_len) == 0;
}
static bool TestCBBMisuse() {
CBB cbb, child, contents;
uint8_t *buf;
@ -670,6 +719,7 @@ int main(void) {
!TestCBBFinishChild() ||
!TestCBBMisuse() ||
!TestCBBPrefixed() ||
!TestCBBDiscardChild() ||
!TestCBBASN1() ||
!TestBerConvert() ||
!TestASN1Uint64() ||

View File

@ -360,6 +360,20 @@ int CBB_add_u24(CBB *cbb, uint32_t value) {
return cbb_buffer_add_u(cbb->base, value, 3);
}
void CBB_discard_child(CBB *cbb) {
if (cbb->child == NULL) {
return;
}
cbb->base->len = cbb->offset;
cbb->child->base = NULL;
cbb->child = NULL;
cbb->pending_len_len = 0;
cbb->pending_is_asn1 = 0;
cbb->offset = 0;
}
int CBB_add_asn1_uint64(CBB *cbb, uint64_t value) {
CBB child;
size_t i;

View File

@ -117,6 +117,7 @@ using ScopedX509_SIG = ScopedOpenSSLType<X509_SIG, X509_SIG_free>;
using ScopedX509Stack = ScopedOpenSSLStack<STACK_OF(X509), X509, X509_free>;
using ScopedCBB = ScopedOpenSSLContext<CBB, void, CBB_zero, CBB_cleanup>;
using ScopedEVP_AEAD_CTX = ScopedOpenSSLContext<EVP_AEAD_CTX, void,
EVP_AEAD_CTX_zero,
EVP_AEAD_CTX_cleanup>;

View File

@ -344,6 +344,10 @@ OPENSSL_EXPORT int CBB_add_u16(CBB *cbb, uint16_t value);
* returns one on success and zero otherwise. */
OPENSSL_EXPORT int CBB_add_u24(CBB *cbb, uint32_t value);
/* CBB_discard_child discards the current unflushed child of |cbb|. Neither the
* child's contents nor the length prefix will be included in the output. */
OPENSSL_EXPORT void CBB_discard_child(CBB *cbb);
/* CBB_add_asn1_uint64 writes an ASN.1 INTEGER into |cbb| using |CBB_add_asn1|
* and writes |value| in its contents. It returns one on success and zero on
* error. */

View File

@ -976,7 +976,6 @@ enum ssl_session_result_t ssl_get_prev_session(
const struct ssl_early_callback_ctx *ctx);
STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs);
int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, uint8_t *p);
struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_dup(
struct ssl_cipher_preference_list_st *cipher_list);
void ssl_cipher_preference_list_free(
@ -1104,7 +1103,7 @@ unsigned int dtls1_min_mtu(void);
void dtls1_hm_fragment_free(hm_fragment *frag);
/* some client-only functions */
int ssl3_send_client_hello(SSL *s);
int ssl3_send_client_hello(SSL *ssl);
int ssl3_get_server_hello(SSL *s);
int ssl3_get_certificate_request(SSL *s);
int ssl3_get_new_session_ticket(SSL *s);
@ -1208,8 +1207,13 @@ int tls1_check_ec_tmp_key(SSL *s);
int tls1_shared_list(SSL *s, const uint8_t *l1, size_t l1len, const uint8_t *l2,
size_t l2len, int nmatch);
uint8_t *ssl_add_clienthello_tlsext(SSL *s, uint8_t *const buf,
uint8_t *const limit, size_t header_len);
/* ssl_add_clienthello_tlsext writes ClientHello extensions to |out|. It
* returns one on success and zero on failure. The |header_len| argument is the
* length of the ClientHello written so far and is used to compute the padding
* length. (It does not include the record header.) */
int ssl_add_clienthello_tlsext(SSL *ssl, CBB *out, size_t header_len);
uint8_t *ssl_add_serverhello_tlsext(SSL *s, uint8_t *const buf,
uint8_t *const limit);
int ssl_parse_clienthello_tlsext(SSL *s, CBS *cbs);

View File

@ -589,140 +589,139 @@ end:
return ret;
}
int ssl3_send_client_hello(SSL *s) {
uint8_t *buf, *p, *d;
int i;
unsigned long l;
static int ssl3_write_client_cipher_list(SSL *ssl, CBB *out) {
/* Prepare disabled cipher masks. */
ssl_set_client_disabled(ssl);
buf = (uint8_t *)s->init_buf->data;
if (s->state == SSL3_ST_CW_CLNT_HELLO_A) {
if (!s->s3->have_version) {
uint16_t max_version = ssl3_get_max_client_version(s);
CBB child;
if (!CBB_add_u16_length_prefixed(out, &child)) {
return 0;
}
STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl);
int any_enabled = 0;
size_t i;
for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
/* Skip disabled ciphers */
if (cipher->algorithm_ssl & ssl->cert->mask_ssl ||
cipher->algorithm_mkey & ssl->cert->mask_k ||
cipher->algorithm_auth & ssl->cert->mask_a) {
continue;
}
any_enabled = 1;
if (!CBB_add_u16(&child, ssl_cipher_get_value(cipher))) {
return 0;
}
}
/* If all ciphers were disabled, return the error to the caller. */
if (!any_enabled) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_AVAILABLE);
return 0;
}
/* For SSLv3, the SCSV is added. Otherwise the renegotiation extension is
* added. */
if (ssl->client_version == SSL3_VERSION &&
!ssl->s3->initial_handshake_complete) {
if (!CBB_add_u16(&child, SSL3_CK_SCSV & 0xffff)) {
return 0;
}
/* The renegotiation extension is required to be at index zero. */
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;
}
return CBB_flush(out);
}
int ssl3_send_client_hello(SSL *ssl) {
if (ssl->state == SSL3_ST_CW_CLNT_HELLO_B) {
return ssl_do_write(ssl);
}
CBB cbb;
CBB_zero(&cbb);
assert(ssl->state == SSL3_ST_CW_CLNT_HELLO_A);
if (!ssl->s3->have_version) {
uint16_t max_version = ssl3_get_max_client_version(ssl);
/* Disabling all versions is silly: return an error. */
if (max_version == 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION);
goto err;
}
s->version = max_version;
s->client_version = max_version;
ssl->version = max_version;
/* Only set |ssl->client_version| on the initial handshake. Renegotiations,
* although locked to a version, reuse the value. When using the plain RSA
* key exchange, the ClientHello version is checked in the premaster secret.
* Some servers fail when this value changes. */
ssl->client_version = max_version;
}
/* If the configured session was created at a version higher than our
* maximum version, drop it. */
if (s->session &&
(s->session->session_id_length == 0 || s->session->not_resumable ||
(!SSL_IS_DTLS(s) && s->session->ssl_version > s->version) ||
(SSL_IS_DTLS(s) && s->session->ssl_version < s->version))) {
SSL_set_session(s, NULL);
if (ssl->session != NULL &&
(ssl->session->session_id_length == 0 || ssl->session->not_resumable ||
(!SSL_IS_DTLS(ssl) && ssl->session->ssl_version > ssl->version) ||
(SSL_IS_DTLS(ssl) && ssl->session->ssl_version < ssl->version))) {
SSL_set_session(ssl, NULL);
}
/* else use the pre-loaded session */
p = s->s3->client_random;
/* If resending the ClientHello in DTLS after a HelloVerifyRequest, don't
* renegerate the client_random. The random must be reused. */
if ((!SSL_IS_DTLS(s) || !s->d1->send_cookie) &&
!ssl_fill_hello_random(p, sizeof(s->s3->client_random),
0 /* client */)) {
if ((!SSL_IS_DTLS(ssl) || !ssl->d1->send_cookie) &&
!ssl_fill_hello_random(ssl->s3->client_random,
sizeof(ssl->s3->client_random), 0 /* client */)) {
goto err;
}
/* Do the message type and length last. Note: the final argument to
* ssl_add_clienthello_tlsext below depends on the size of this prefix. */
d = p = ssl_handshake_start(s);
/* version indicates the negotiated version: for example from an SSLv2/v3
* compatible client hello). The client_version field is the maximum
* version we permit and it is also used in RSA encrypted premaster
* secrets. Some servers can choke if we initially report a higher version
* then renegotiate to a lower one in the premaster secret. This didn't
* happen with TLS 1.0 as most servers supported it but it can with TLS 1.1
* or later if the server only supports 1.0.
*
* Possible scenario with previous logic:
* 1. Client hello indicates TLS 1.2
* 2. Server hello says TLS 1.0
* 3. RSA encrypted premaster secret uses 1.2.
* 4. Handhaked proceeds using TLS 1.0.
* 5. Server sends hello request to renegotiate.
* 6. Client hello indicates TLS v1.0 as we now
* know that is maximum server supports.
* 7. Server chokes on RSA encrypted premaster secret
* containing version 1.0.
*
* For interoperability it should be OK to always use the maximum version
* we support in client hello and then rely on the checking of version to
* ensure the servers isn't being inconsistent: for example initially
* negotiating with TLS 1.0 and renegotiating with TLS 1.2. We do this by
* using client_version in client hello and not resetting it to the
* negotiated version. */
*(p++) = s->client_version >> 8;
*(p++) = s->client_version & 0xff;
/* Random stuff */
memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE);
p += SSL3_RANDOM_SIZE;
/* Session ID */
if (s->s3->initial_handshake_complete || s->session == NULL) {
/* Renegotiations do not participate in session resumption. */
i = 0;
} else {
i = s->session->session_id_length;
}
*(p++) = i;
if (i != 0) {
if (i > (int)sizeof(s->session->session_id)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
memcpy(p, s->session->session_id, i);
p += i;
}
int has_session = ssl->session != NULL &&
!ssl->s3->initial_handshake_complete;
/* cookie stuff for DTLS */
if (SSL_IS_DTLS(s)) {
if (s->d1->cookie_len > sizeof(s->d1->cookie)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
*(p++) = s->d1->cookie_len;
memcpy(p, s->d1->cookie, s->d1->cookie_len);
p += s->d1->cookie_len;
}
/* Ciphers supported */
i = ssl_cipher_list_to_bytes(s, SSL_get_ciphers(s), &p[2]);
if (i == 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_AVAILABLE);
goto err;
}
s2n(i, p);
p += i;
/* COMPRESSION */
*(p++) = 1;
*(p++) = 0; /* Add the NULL method */
/* TLS extensions*/
p = ssl_add_clienthello_tlsext(s, p, buf + SSL3_RT_MAX_PLAIN_LENGTH,
p - buf);
if (p == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB child;
if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) ||
!CBB_add_u16(&cbb, ssl->client_version) ||
!CBB_add_bytes(&cbb, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
!CBB_add_u8_length_prefixed(&cbb, &child) ||
(has_session &&
!CBB_add_bytes(&child, ssl->session->session_id,
ssl->session->session_id_length))) {
goto err;
}
l = p - d;
if (!ssl_set_handshake_header(s, SSL3_MT_CLIENT_HELLO, l)) {
if (SSL_IS_DTLS(ssl)) {
if (!CBB_add_u8_length_prefixed(&cbb, &child) ||
!CBB_add_bytes(&child, ssl->d1->cookie, ssl->d1->cookie_len)) {
goto err;
}
s->state = SSL3_ST_CW_CLNT_HELLO_B;
}
/* SSL3_ST_CW_CLNT_HELLO_B */
return ssl_do_write(s);
size_t length;
if (!ssl3_write_client_cipher_list(ssl, &cbb) ||
!CBB_add_u8(&cbb, 1 /* one compression method */) ||
!CBB_add_u8(&cbb, 0 /* null compression */) ||
!ssl_add_clienthello_tlsext(ssl, &cbb,
CBB_len(&cbb) + SSL_HM_HEADER_LENGTH(ssl)) ||
!CBB_finish(&cbb, NULL, &length) ||
!ssl_set_handshake_header(ssl, SSL3_MT_CLIENT_HELLO, length)) {
goto err;
}
ssl->state = SSL3_ST_CW_CLNT_HELLO_B;
return ssl_do_write(ssl);
err:
CBB_cleanup(&cbb);
return -1;
}

View File

@ -1498,51 +1498,6 @@ int SSL_set_cipher_list(SSL *ssl, const char *str) {
return 1;
}
int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, uint8_t *p) {
size_t i;
const SSL_CIPHER *c;
CERT *ct = s->cert;
uint8_t *q;
/* Set disabled masks for this session */
ssl_set_client_disabled(s);
if (sk == NULL) {
return 0;
}
q = p;
for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
c = sk_SSL_CIPHER_value(sk, i);
/* Skip disabled ciphers */
if (c->algorithm_ssl & ct->mask_ssl ||
c->algorithm_mkey & ct->mask_k ||
c->algorithm_auth & ct->mask_a) {
continue;
}
s2n(ssl_cipher_get_value(c), p);
}
/* If all ciphers were disabled, return the error to the caller. */
if (p == q) {
return 0;
}
/* For SSLv3, the SCSV is added. Otherwise the renegotiation extension is
* added. */
if (s->client_version == SSL3_VERSION &&
!s->s3->initial_handshake_complete) {
s2n(SSL3_CK_SCSV & 0xffff, p);
/* The renegotiation extension is required to be at index zero. */
s->s3->tmp.extensions.sent |= (1u << 0);
}
if (s->mode & SSL_MODE_SEND_FALLBACK_SCSV) {
s2n(SSL3_CK_FALLBACK_SCSV & 0xffff, p);
}
return p - q;
}
STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs) {
CBS cipher_suites = *cbs;
const SSL_CIPHER *c;

View File

@ -2228,52 +2228,48 @@ int SSL_extension_supported(unsigned extension_value) {
tls_extension_find(&index, extension_value) != NULL;
}
/* header_len is the length of the ClientHello header written so far, used to
* compute padding. It does not include the record header. Pass 0 if no padding
* is to be done. */
uint8_t *ssl_add_clienthello_tlsext(SSL *s, uint8_t *const buf,
uint8_t *const limit, size_t header_len) {
int ssl_add_clienthello_tlsext(SSL *ssl, CBB *out, size_t header_len) {
/* don't add extensions for SSLv3 unless doing secure renegotiation */
if (s->client_version == SSL3_VERSION && !s->s3->send_connection_binding) {
return buf;
if (ssl->client_version == SSL3_VERSION &&
!ssl->s3->send_connection_binding) {
return 1;
}
CBB cbb, extensions;
CBB_zero(&cbb);
if (!CBB_init_fixed(&cbb, buf, limit - buf) ||
!CBB_add_u16_length_prefixed(&cbb, &extensions)) {
size_t orig_len = CBB_len(out);
CBB extensions;
if (!CBB_add_u16_length_prefixed(out, &extensions)) {
goto err;
}
s->s3->tmp.extensions.sent = 0;
s->s3->tmp.custom_extensions.sent = 0;
ssl->s3->tmp.extensions.sent = 0;
ssl->s3->tmp.custom_extensions.sent = 0;
size_t i;
for (i = 0; i < kNumExtensions; i++) {
if (kExtensions[i].init != NULL) {
kExtensions[i].init(s);
kExtensions[i].init(ssl);
}
}
for (i = 0; i < kNumExtensions; i++) {
const size_t len_before = CBB_len(&extensions);
if (!kExtensions[i].add_clienthello(s, &extensions)) {
if (!kExtensions[i].add_clienthello(ssl, &extensions)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
goto err;
}
if (CBB_len(&extensions) != len_before) {
s->s3->tmp.extensions.sent |= (1u << i);
ssl->s3->tmp.extensions.sent |= (1u << i);
}
}
if (!custom_ext_add_clienthello(s, &extensions)) {
if (!custom_ext_add_clienthello(ssl, &extensions)) {
goto err;
}
if (header_len > 0) {
header_len += CBB_len(&extensions);
if (!SSL_IS_DTLS(ssl)) {
header_len += CBB_len(&extensions) - orig_len;
if (header_len > 0xff && header_len < 0x200) {
/* Add padding to workaround bugs in F5 terminators. See
* https://tools.ietf.org/html/draft-agl-tls-padding-03
@ -2301,26 +2297,18 @@ uint8_t *ssl_add_clienthello_tlsext(SSL *s, uint8_t *const buf,
}
}
if (!CBB_flush(&cbb)) {
goto err;
}
uint8_t *ret = buf;
const size_t cbb_len = CBB_len(&cbb);
/* If only two bytes have been written then the extensions are actually empty
* and those two bytes are the zero length. In that case, we don't bother
* sending the extensions length. */
if (cbb_len > 2) {
ret += cbb_len;
if (CBB_len(&extensions) - orig_len == 2) {
CBB_discard_child(out);
}
CBB_cleanup(&cbb);
return ret;
return CBB_flush(out);
err:
CBB_cleanup(&cbb);
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return NULL;
return 0;
}
uint8_t *ssl_add_serverhello_tlsext(SSL *s, uint8_t *const buf,