Disconnect handshake message creation from init_buf. This allows us to use CBB for all handshake messages. Now, SSL_PROTOCOL_METHOD is responsible for implementing a trio of CBB-related hooks to assemble handshake messages. Change-Id: I144d3cac4f05b6637bf45d3f838673fc5c854405 Reviewed-on: https://boringssl-review.googlesource.com/8440 Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index a768fd7..6ce9412 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h
@@ -4140,6 +4140,10 @@ * version. */ const SSL3_ENC_METHOD *enc_method; + /* pending_message is the current outgoing handshake message. */ + uint8_t *pending_message; + uint32_t pending_message_len; + /* State pertaining to the pending handshake. * * TODO(davidben): State is current spread all over the place. Move
diff --git a/ssl/d1_both.c b/ssl/d1_both.c index f021d0a..d6f4e7b 100644 --- a/ssl/d1_both.c +++ b/ssl/d1_both.c
@@ -305,9 +305,16 @@ return 1; } -int dtls1_do_handshake_write(SSL *ssl, size_t *out_offset, const uint8_t *in, - size_t offset, size_t len, - enum dtls1_use_epoch_t use_epoch) { +/* dtls1_do_handshake_write writes handshake message |in| using the given epoch, + * starting |offset| bytes into the message body. It returns one on success. On + * error, it returns <= 0 and sets |*out_offset| to the number of bytes of body + * that were successfully written. This may be used to retry the write + * later. |in| must be a reassembled handshake message with the full DTLS + * handshake header. */ +static int dtls1_do_handshake_write(SSL *ssl, size_t *out_offset, + const uint8_t *in, size_t offset, + size_t len, + enum dtls1_use_epoch_t use_epoch) { dtls1_update_mtu(ssl); int ret = -1; @@ -404,6 +411,108 @@ return ret; } +void dtls_clear_outgoing_messages(SSL *ssl) { + size_t i; + for (i = 0; i < ssl->d1->outgoing_messages_len; i++) { + OPENSSL_free(ssl->d1->outgoing_messages[i].data); + ssl->d1->outgoing_messages[i].data = NULL; + } + ssl->d1->outgoing_messages_len = 0; +} + +/* dtls1_buffer_change_cipher_spec adds a ChangeCipherSpec to the current + * handshake flight. */ +static int dtls1_buffer_change_cipher_spec(SSL *ssl) { + if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + DTLS_OUTGOING_MESSAGE *msg = + &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len]; + msg->data = NULL; + msg->len = 0; + msg->epoch = ssl->d1->w_epoch; + msg->is_ccs = 1; + + ssl->d1->outgoing_messages_len++; + return 1; +} + +static int dtls1_buffer_message(SSL *ssl, uint8_t *data, size_t len) { + if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + OPENSSL_free(data); + return 0; + } + + DTLS_OUTGOING_MESSAGE *msg = + &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len]; + msg->data = data; + msg->len = len; + msg->epoch = ssl->d1->w_epoch; + msg->is_ccs = 0; + + ssl->d1->outgoing_messages_len++; + return 1; +} + +int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) { + /* Pick a modest size hint to save most of the |realloc| calls. */ + if (!CBB_init(cbb, 64) || + !CBB_add_u8(cbb, type) || + !CBB_add_u24(cbb, 0 /* length (filled in later) */) || + !CBB_add_u16(cbb, ssl->d1->handshake_write_seq) || + !CBB_add_u24(cbb, 0 /* offset */) || + !CBB_add_u24_length_prefixed(cbb, body)) { + return 0; + } + + return 1; +} + +int dtls1_finish_message(SSL *ssl, CBB *cbb) { + uint8_t *msg = NULL; + size_t len; + if (!CBB_finish(cbb, &msg, &len) || + len > 0xffffffffu || + len < DTLS1_HM_HEADER_LENGTH) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + OPENSSL_free(msg); + return 0; + } + + /* Fix up the header. Copy the fragment length into the total message + * length. */ + memcpy(msg + 1, msg + DTLS1_HM_HEADER_LENGTH - 3, 3); + + ssl3_update_handshake_hash(ssl, msg, len); + + ssl->d1->handshake_write_seq++; + ssl->init_off = 0; + return dtls1_buffer_message(ssl, msg, len); +} + +int dtls1_write_message(SSL *ssl) { + if (ssl->d1->outgoing_messages_len == 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + const DTLS_OUTGOING_MESSAGE *msg = + &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len - 1]; + if (msg->is_ccs) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + size_t offset = ssl->init_off; + int ret = dtls1_do_handshake_write(ssl, &offset, msg->data, offset, msg->len, + dtls1_use_current_epoch); + ssl->init_off = offset; + return ret; +} + /* dtls1_is_next_message_complete returns one if the next handshake message is * complete and zero otherwise. */ static int dtls1_is_next_message_complete(SSL *ssl) { @@ -693,45 +802,6 @@ return ret; } -/* dtls1_buffer_change_cipher_spec adds a ChangeCipherSpec to the current - * handshake flight. */ -static int dtls1_buffer_change_cipher_spec(SSL *ssl) { - if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return 0; - } - - DTLS_OUTGOING_MESSAGE *msg = - &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len]; - msg->data = NULL; - msg->len = 0; - msg->epoch = ssl->d1->w_epoch; - msg->is_ccs = 1; - - ssl->d1->outgoing_messages_len++; - return 1; -} - -int dtls1_buffer_message(SSL *ssl) { - if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return 0; - } - - DTLS_OUTGOING_MESSAGE *msg = - &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len]; - msg->data = BUF_memdup(ssl->init_buf->data, ssl->init_num); - if (msg->data == NULL) { - return 0; - } - msg->len = ssl->init_num; - msg->epoch = ssl->d1->w_epoch; - msg->is_ccs = 0; - - ssl->d1->outgoing_messages_len++; - return 1; -} - int dtls1_send_change_cipher_spec(SSL *ssl, int a, int b) { if (ssl->state == a) { dtls1_buffer_change_cipher_spec(ssl); @@ -741,15 +811,6 @@ return dtls1_write_change_cipher_spec(ssl, dtls1_use_current_epoch); } -void dtls_clear_outgoing_messages(SSL *ssl) { - size_t i; - for (i = 0; i < ssl->d1->outgoing_messages_len; i++) { - OPENSSL_free(ssl->d1->outgoing_messages[i].data); - ssl->d1->outgoing_messages[i].data = NULL; - } - ssl->d1->outgoing_messages_len = 0; -} - void dtls_clear_incoming_messages(SSL *ssl) { size_t i; for (i = 0; i < SSL_MAX_HANDSHAKE_FLIGHT; i++) {
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index d738c57..e0853cf 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c
@@ -289,39 +289,6 @@ #endif } -int dtls1_set_handshake_header(SSL *ssl, int htype, unsigned long len) { - uint8_t *message = (uint8_t *)ssl->init_buf->data; - - uint16_t seq = ssl->d1->handshake_write_seq; - ssl->d1->handshake_write_seq++; - - ssl->init_num = (int)len + DTLS1_HM_HEADER_LENGTH; - ssl->init_off = 0; - - /* Serialize the message header as if it were a single fragment. */ - uint8_t *p = message; - *p++ = htype; - l2n3(len, p); - s2n(seq, p); - l2n3(0, p); - l2n3(len, p); - assert(p == message + DTLS1_HM_HEADER_LENGTH); - - /* Buffer the message to handle re-xmits. */ - dtls1_buffer_message(ssl); - - return ssl3_update_handshake_hash(ssl, message, ssl->init_num); -} - -int dtls1_handshake_write(SSL *ssl) { - size_t offset = ssl->init_off; - int ret = dtls1_do_handshake_write( - ssl, &offset, (const uint8_t *)ssl->init_buf->data, offset, ssl->init_num, - dtls1_use_current_epoch); - ssl->init_off = offset; - return ret; -} - void dtls1_expect_flight(SSL *ssl) { dtls1_start_timer(ssl); }
diff --git a/ssl/d1_meth.c b/ssl/d1_meth.c index 32a4651..78119a0 100644 --- a/ssl/d1_meth.c +++ b/ssl/d1_meth.c
@@ -70,9 +70,9 @@ dtls1_write_app_data, dtls1_dispatch_alert, dtls1_supports_cipher, - DTLS1_HM_HEADER_LENGTH, - dtls1_set_handshake_header, - dtls1_handshake_write, + dtls1_init_message, + dtls1_finish_message, + dtls1_write_message, dtls1_send_change_cipher_spec, dtls1_expect_flight, dtls1_received_flight,
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c index 3b33574..e5c6e2f 100644 --- a/ssl/handshake_client.c +++ b/ssl/handshake_client.c
@@ -611,7 +611,7 @@ static int ssl3_send_client_hello(SSL *ssl) { if (ssl->state == SSL3_ST_CW_CLNT_HELLO_B) { - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); } /* The handshake buffer is reset on every ClientHello. Notably, in DTLS, we @@ -662,12 +662,11 @@ int has_session = ssl->session != NULL && !ssl->s3->initial_handshake_complete; - 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) || + CBB body, child; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO) || + !CBB_add_u16(&body, ssl->client_version) || + !CBB_add_bytes(&body, ssl->s3->client_random, SSL3_RANDOM_SIZE) || + !CBB_add_u8_length_prefixed(&body, &child) || (has_session && !CBB_add_bytes(&child, ssl->session->session_id, ssl->session->session_id_length))) { @@ -675,25 +674,24 @@ } if (SSL_IS_DTLS(ssl)) { - if (!CBB_add_u8_length_prefixed(&cbb, &child) || + if (!CBB_add_u8_length_prefixed(&body, &child) || !CBB_add_bytes(&child, ssl->d1->cookie, ssl->d1->cookie_len)) { goto err; } } - 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)) { + size_t header_len = + SSL_IS_DTLS(ssl) ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH; + if (!ssl3_write_client_cipher_list(ssl, &body) || + !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)) || + !ssl->method->finish_message(ssl, &cbb)) { goto err; } ssl->state = SSL3_ST_CW_CLNT_HELLO_B; - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); err: CBB_cleanup(&cbb); @@ -1594,10 +1592,10 @@ return 1; } - /* In TLS, send an empty Certificate message. */ - uint8_t *p = ssl_handshake_start(ssl); - l2n3(0, p); - if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, 3)) { + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) || + !CBB_add_u24(&body, 0 /* no certificates */) || + !ssl->method->finish_message(ssl, &cbb)) { return -1; } } else if (!ssl3_output_cert_chain(ssl)) { @@ -1607,7 +1605,7 @@ } assert(ssl->state == SSL3_ST_CW_CERT_D); - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); } OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned), @@ -1615,15 +1613,15 @@ static int ssl3_send_client_key_exchange(SSL *ssl) { if (ssl->state == SSL3_ST_CW_KEY_EXCH_B) { - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); } assert(ssl->state == SSL3_ST_CW_KEY_EXCH_A); uint8_t *pms = NULL; size_t pms_len = 0; - CBB cbb; - if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl), - ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl))) { + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CLIENT_KEY_EXCHANGE)) { goto err; } @@ -1660,10 +1658,10 @@ /* Write out psk_identity. */ CBB child; - if (!CBB_add_u16_length_prefixed(&cbb, &child) || + if (!CBB_add_u16_length_prefixed(&body, &child) || !CBB_add_bytes(&child, (const uint8_t *)identity, OPENSSL_strnlen(identity, sizeof(identity))) || - !CBB_flush(&cbb)) { + !CBB_flush(&body)) { goto err; } } @@ -1698,11 +1696,11 @@ goto err; } - CBB child, *enc_pms = &cbb; + CBB child, *enc_pms = &body; size_t enc_pms_len; /* In TLS, there is a length prefix. */ if (ssl->version > SSL3_VERSION) { - if (!CBB_add_u16_length_prefixed(&cbb, &child)) { + if (!CBB_add_u16_length_prefixed(&body, &child)) { goto err; } enc_pms = &child; @@ -1715,13 +1713,13 @@ /* Log the premaster secret, if logging is enabled. */ !ssl_log_rsa_client_key_exchange(ssl, ptr, enc_pms_len, pms, pms_len) || !CBB_did_write(enc_pms, enc_pms_len) || - !CBB_flush(&cbb)) { + !CBB_flush(&body)) { goto err; } } else if (alg_k & (SSL_kECDHE|SSL_kDHE|SSL_kCECPQ1)) { /* Generate a keypair and serialize the public half. */ CBB child; - if (!SSL_ECDH_CTX_add_key(&ssl->s3->tmp.ecdh_ctx, &cbb, &child)) { + if (!SSL_ECDH_CTX_add_key(&ssl->s3->tmp.ecdh_ctx, &body, &child)) { goto err; } @@ -1733,7 +1731,7 @@ ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); goto err; } - if (!CBB_flush(&cbb)) { + if (!CBB_flush(&body)) { goto err; } @@ -1783,9 +1781,7 @@ /* The message must be added to the finished hash before calculating the * master secret. */ - size_t length; - if (!CBB_finish(&cbb, NULL, &length) || - !ssl_set_handshake_header(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE, length)) { + if (!ssl->method->finish_message(ssl, &cbb)) { goto err; } ssl->state = SSL3_ST_CW_KEY_EXCH_B; @@ -1799,8 +1795,7 @@ OPENSSL_cleanse(pms, pms_len); OPENSSL_free(pms); - /* SSL3_ST_CW_KEY_EXCH_B */ - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); err: CBB_cleanup(&cbb); @@ -1813,14 +1808,14 @@ static int ssl3_send_cert_verify(SSL *ssl) { if (ssl->state == SSL3_ST_CW_CERT_VRFY_C) { - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); } assert(ssl_has_private_key(ssl)); - CBB cbb, child; - if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl), - ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl))) { + CBB cbb, body, child; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CERTIFICATE_VERIFY)) { goto err; } @@ -1828,7 +1823,7 @@ const EVP_MD *md = NULL; if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { md = tls1_choose_signing_digest(ssl); - if (!tls12_add_sigandhash(ssl, &cbb, md)) { + if (!tls12_add_sigandhash(ssl, &body, md)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } @@ -1837,7 +1832,7 @@ /* Set aside space for the signature. */ const size_t max_sig_len = ssl_private_key_max_signature_len(ssl); uint8_t *ptr; - if (!CBB_add_u16_length_prefixed(&cbb, &child) || + if (!CBB_add_u16_length_prefixed(&body, &child) || !CBB_reserve(&child, &ptr, max_sig_len)) { goto err; } @@ -1877,15 +1872,13 @@ goto err; } - size_t length; if (!CBB_did_write(&child, sig_len) || - !CBB_finish(&cbb, NULL, &length) || - !ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_VERIFY, length)) { + !ssl->method->finish_message(ssl, &cbb)) { goto err; } ssl->state = SSL3_ST_CW_CERT_VRFY_C; - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); err: CBB_cleanup(&cbb); @@ -1894,7 +1887,7 @@ static int ssl3_send_next_proto(SSL *ssl) { if (ssl->state == SSL3_ST_CW_NEXT_PROTO_B) { - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); } assert(ssl->state == SSL3_ST_CW_NEXT_PROTO_A); @@ -1902,30 +1895,26 @@ static const uint8_t kZero[32] = {0}; size_t padding_len = 32 - ((ssl->s3->next_proto_negotiated_len + 2) % 32); - CBB cbb, child; - size_t length; - CBB_zero(&cbb); - if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl), - ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) || - !CBB_add_u8_length_prefixed(&cbb, &child) || + CBB cbb, body, child; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_NEXT_PROTO) || + !CBB_add_u8_length_prefixed(&body, &child) || !CBB_add_bytes(&child, ssl->s3->next_proto_negotiated, ssl->s3->next_proto_negotiated_len) || - !CBB_add_u8_length_prefixed(&cbb, &child) || + !CBB_add_u8_length_prefixed(&body, &child) || !CBB_add_bytes(&child, kZero, padding_len) || - !CBB_finish(&cbb, NULL, &length) || - !ssl_set_handshake_header(ssl, SSL3_MT_NEXT_PROTO, length)) { + !ssl->method->finish_message(ssl, &cbb)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); CBB_cleanup(&cbb); return -1; } ssl->state = SSL3_ST_CW_NEXT_PROTO_B; - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); } static int ssl3_send_channel_id(SSL *ssl) { if (ssl->state == SSL3_ST_CW_CHANNEL_ID_B) { - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); } assert(ssl->state == SSL3_ST_CW_CHANNEL_ID_A); @@ -1975,27 +1964,22 @@ goto err; } - CBB cbb, child; - size_t length; - CBB_zero(&cbb); - if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl), - ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) || - !CBB_add_u16(&cbb, TLSEXT_TYPE_channel_id) || - !CBB_add_u16_length_prefixed(&cbb, &child) || - !BN_bn2cbb_padded(&child, 32, x) || - !BN_bn2cbb_padded(&child, 32, y) || + CBB cbb, body, child; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS) || + !CBB_add_u16(&body, TLSEXT_TYPE_channel_id) || + !CBB_add_u16_length_prefixed(&body, &child) || + !BN_bn2cbb_padded(&child, 32, x) || !BN_bn2cbb_padded(&child, 32, y) || !BN_bn2cbb_padded(&child, 32, sig->r) || !BN_bn2cbb_padded(&child, 32, sig->s) || - !CBB_finish(&cbb, NULL, &length) || - !ssl_set_handshake_header(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS, - length)) { + !ssl->method->finish_message(ssl, &cbb)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); CBB_cleanup(&cbb); goto err; } ssl->state = SSL3_ST_CW_CHANNEL_ID_B; - ret = ssl_do_write(ssl); + ret = ssl->method->write_message(ssl); err: BN_free(x);
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c index 7f7f3b8..128bb8d 100644 --- a/ssl/handshake_server.c +++ b/ssl/handshake_server.c
@@ -1060,7 +1060,7 @@ static int ssl3_send_server_hello(SSL *ssl) { if (ssl->state == SSL3_ST_SW_SRVR_HELLO_B) { - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); } assert(ssl->state == SSL3_ST_SW_SRVR_HELLO_A); @@ -1085,71 +1085,63 @@ return -1; } - CBB cbb, session_id; - size_t length; - CBB_zero(&cbb); - if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl), - ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) || - !CBB_add_u16(&cbb, ssl->version) || - !CBB_add_bytes(&cbb, ssl->s3->server_random, SSL3_RANDOM_SIZE) || - !CBB_add_u8_length_prefixed(&cbb, &session_id) || + CBB cbb, body, session_id; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) || + !CBB_add_u16(&body, ssl->version) || + !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) || + !CBB_add_u8_length_prefixed(&body, &session_id) || !CBB_add_bytes(&session_id, ssl->session->session_id, ssl->session->session_id_length) || - !CBB_add_u16(&cbb, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) || - !CBB_add_u8(&cbb, 0 /* no compression */) || - !ssl_add_serverhello_tlsext(ssl, &cbb) || - !CBB_finish(&cbb, NULL, &length) || - !ssl_set_handshake_header(ssl, SSL3_MT_SERVER_HELLO, length)) { + !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) || + !CBB_add_u8(&body, 0 /* no compression */) || + !ssl_add_serverhello_tlsext(ssl, &body) || + !ssl->method->finish_message(ssl, &cbb)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); CBB_cleanup(&cbb); return -1; } ssl->state = SSL3_ST_SW_SRVR_HELLO_B; - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); } static int ssl3_send_server_certificate(SSL *ssl) { - if (ssl->state == SSL3_ST_SW_CERT_A) { - if (!ssl3_output_cert_chain(ssl)) { - return 0; - } - ssl->state = SSL3_ST_SW_CERT_B; + if (ssl->state == SSL3_ST_SW_CERT_B) { + return ssl->method->write_message(ssl); } - /* SSL3_ST_SW_CERT_B */ - return ssl_do_write(ssl); + if (!ssl3_output_cert_chain(ssl)) { + return 0; + } + ssl->state = SSL3_ST_SW_CERT_B; + return ssl->method->write_message(ssl); } static int ssl3_send_certificate_status(SSL *ssl) { - if (ssl->state == SSL3_ST_SW_CERT_STATUS_A) { - CBB out, ocsp_response; - size_t length; - - CBB_zero(&out); - if (!CBB_init_fixed(&out, ssl_handshake_start(ssl), - ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) || - !CBB_add_u8(&out, TLSEXT_STATUSTYPE_ocsp) || - !CBB_add_u24_length_prefixed(&out, &ocsp_response) || - !CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response, - ssl->ctx->ocsp_response_length) || - !CBB_finish(&out, NULL, &length) || - !ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_STATUS, length)) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - CBB_cleanup(&out); - return -1; - } - - ssl->state = SSL3_ST_SW_CERT_STATUS_B; + if (ssl->state == SSL3_ST_SW_CERT_STATUS_B) { + return ssl->method->write_message(ssl); } - /* SSL3_ST_SW_CERT_STATUS_B */ - return ssl_do_write(ssl); + CBB cbb, body, ocsp_response; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CERTIFICATE_STATUS) || + !CBB_add_u8(&body, TLSEXT_STATUSTYPE_ocsp) || + !CBB_add_u24_length_prefixed(&body, &ocsp_response) || + !CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response, + ssl->ctx->ocsp_response_length) || + !ssl->method->finish_message(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return -1; + } + + ssl->state = SSL3_ST_SW_CERT_STATUS_B; + return ssl->method->write_message(ssl); } static int ssl3_send_server_key_exchange(SSL *ssl) { if (ssl->state == SSL3_ST_SW_KEY_EXCH_C) { - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); } CBB cbb, child; @@ -1243,9 +1235,10 @@ } /* Assemble the message. */ - if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl), - ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) || - !CBB_add_bytes(&cbb, ssl->s3->tmp.server_params, + CBB body; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_SERVER_KEY_EXCHANGE) || + !CBB_add_bytes(&body, ssl->s3->tmp.server_params, ssl->s3->tmp.server_params_len)) { goto err; } @@ -1261,7 +1254,7 @@ const EVP_MD *md; if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { md = tls1_choose_signing_digest(ssl); - if (!tls12_add_sigandhash(ssl, &cbb, md)) { + if (!tls12_add_sigandhash(ssl, &body, md)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); goto err; @@ -1275,7 +1268,7 @@ /* Add space for the signature. */ const size_t max_sig_len = ssl_private_key_max_signature_len(ssl); uint8_t *ptr; - if (!CBB_add_u16_length_prefixed(&cbb, &child) || + if (!CBB_add_u16_length_prefixed(&body, &child) || !CBB_reserve(&child, &ptr, max_sig_len)) { goto err; } @@ -1322,9 +1315,7 @@ } } - size_t length; - if (!CBB_finish(&cbb, NULL, &length) || - !ssl_set_handshake_header(ssl, SSL3_MT_SERVER_KEY_EXCHANGE, length)) { + if (!ssl->method->finish_message(ssl, &cbb)) { goto err; } @@ -1333,91 +1324,118 @@ ssl->s3->tmp.server_params_len = 0; ssl->state = SSL3_ST_SW_KEY_EXCH_C; - return ssl_do_write(ssl); + return ssl->method->write_message(ssl); err: CBB_cleanup(&cbb); return -1; } -static int ssl3_send_certificate_request(SSL *ssl) { - uint8_t *p, *d; +static int add_cert_types(SSL *ssl, CBB *cbb) { + /* Get configured signature algorithms. */ + int have_rsa_sign = 0; + int have_ecdsa_sign = 0; + const uint8_t *sig; + size_t siglen = tls12_get_psigalgs(ssl, &sig); size_t i; - int j, nl, off, n; - STACK_OF(X509_NAME) *sk = NULL; - X509_NAME *name; - BUF_MEM *buf; + for (i = 0; i < siglen; i += 2, sig += 2) { + switch (sig[1]) { + case TLSEXT_signature_rsa: + have_rsa_sign = 1; + break; - if (ssl->state == SSL3_ST_SW_CERT_REQ_A) { - buf = ssl->init_buf; - - d = p = ssl_handshake_start(ssl); - - /* get the list of acceptable cert types */ - p++; - n = ssl3_get_req_cert_type(ssl, p); - d[0] = n; - p += n; - n++; - - if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { - const uint8_t *psigs; - nl = tls12_get_psigalgs(ssl, &psigs); - s2n(nl, p); - memcpy(p, psigs, nl); - p += nl; - n += nl + 2; + case TLSEXT_signature_ecdsa: + have_ecdsa_sign = 1; + break; } - - off = n; - p += 2; - n += 2; - - sk = SSL_get_client_CA_list(ssl); - nl = 0; - if (sk != NULL) { - for (i = 0; i < sk_X509_NAME_num(sk); i++) { - name = sk_X509_NAME_value(sk, i); - j = i2d_X509_NAME(name, NULL); - if (!BUF_MEM_grow_clean(buf, SSL_HM_HEADER_LENGTH(ssl) + n + j + 2)) { - OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); - goto err; - } - p = ssl_handshake_start(ssl) + n; - s2n(j, p); - i2d_X509_NAME(name, &p); - n += 2 + j; - nl += 2 + j; - } - } - - /* else no CA names */ - p = ssl_handshake_start(ssl) + off; - s2n(nl, p); - - if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_REQUEST, n)) { - goto err; - } - ssl->state = SSL3_ST_SW_CERT_REQ_B; } - /* SSL3_ST_SW_CERT_REQ_B */ - return ssl_do_write(ssl); + if (have_rsa_sign && !CBB_add_u8(cbb, SSL3_CT_RSA_SIGN)) { + return 0; + } + + /* ECDSA certs can be used with RSA cipher suites as well so we don't need to + * check for SSL_kECDH or SSL_kECDHE. */ + if (ssl->version >= TLS1_VERSION && have_ecdsa_sign && + !CBB_add_u8(cbb, TLS_CT_ECDSA_SIGN)) { + return 0; + } + + return 1; +} + +static int ssl3_send_certificate_request(SSL *ssl) { + if (ssl->state == SSL3_ST_SW_CERT_REQ_B) { + return ssl->method->write_message(ssl); + } + + CBB cbb, body, cert_types, sigalgs_cbb, names_cbb, name_cbb; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CERTIFICATE_REQUEST) || + !CBB_add_u8_length_prefixed(&body, &cert_types) || + !add_cert_types(ssl, &cert_types)) { + goto err; + } + + if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { + const uint8_t *sigalgs; + size_t sigalgs_len = tls12_get_psigalgs(ssl, &sigalgs); + if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) || + !CBB_add_bytes(&sigalgs_cbb, sigalgs, sigalgs_len)) { + goto err; + } + } + + STACK_OF(X509_NAME) *sk = SSL_get_client_CA_list(ssl); + if (sk != NULL) { + if (!CBB_add_u16_length_prefixed(&body, &names_cbb)) { + goto err; + } + + size_t i; + for (i = 0; i < sk_X509_NAME_num(sk); i++) { + X509_NAME *name = sk_X509_NAME_value(sk, i); + int len = i2d_X509_NAME(name, NULL); + if (len < 0) { + goto err; + } + uint8_t *ptr; + if (!CBB_add_u16_length_prefixed(&names_cbb, &name_cbb) || + !CBB_add_space(&name_cbb, &ptr, (size_t)len) || + (len > 0 && i2d_X509_NAME(name, &ptr) < 0)) { + goto err; + } + } + } + + if (!ssl->method->finish_message(ssl, &cbb)) { + goto err; + } + + ssl->state = SSL3_ST_SW_CERT_REQ_B; + return ssl->method->write_message(ssl); err: + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); return -1; } static int ssl3_send_server_hello_done(SSL *ssl) { - if (ssl->state == SSL3_ST_SW_SRVR_DONE_A) { - if (!ssl_set_handshake_header(ssl, SSL3_MT_SERVER_HELLO_DONE, 0)) { - return -1; - } - ssl->state = SSL3_ST_SW_SRVR_DONE_B; + if (ssl->state == SSL3_ST_SW_SRVR_DONE_B) { + return ssl->method->write_message(ssl); } - /* SSL3_ST_SW_SRVR_DONE_B */ - return ssl_do_write(ssl); + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO_DONE) || + !ssl->method->finish_message(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return -1; + } + + ssl->state = SSL3_ST_SW_SRVR_DONE_B; + return ssl->method->write_message(ssl); } static int ssl3_get_client_certificate(SSL *ssl) { @@ -2064,130 +2082,106 @@ return ret; } -/* send a new session ticket (not necessarily for a new session) */ static int ssl3_send_new_session_ticket(SSL *ssl) { - int ret = -1; - uint8_t *session = NULL; - size_t session_len; - EVP_CIPHER_CTX ctx; - HMAC_CTX hctx; - - EVP_CIPHER_CTX_init(&ctx); - HMAC_CTX_init(&hctx); - - if (ssl->state == SSL3_ST_SW_SESSION_TICKET_A) { - uint8_t *p, *macstart; - int len; - unsigned int hlen; - SSL_CTX *tctx = ssl->initial_ctx; - uint8_t iv[EVP_MAX_IV_LENGTH]; - uint8_t key_name[16]; - /* The maximum overhead of encrypting the session is 16 (key name) + IV + - * one block of encryption overhead + HMAC. */ - const size_t max_ticket_overhead = - 16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE; - - /* Serialize the SSL_SESSION to be encoded into the ticket. */ - if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &session, - &session_len)) { - goto err; - } - - /* If the session is too long, emit a dummy value rather than abort the - * connection. */ - if (session_len > 0xFFFF - max_ticket_overhead) { - static const char kTicketPlaceholder[] = "TICKET TOO LARGE"; - const size_t placeholder_len = strlen(kTicketPlaceholder); - - OPENSSL_free(session); - session = NULL; - - p = ssl_handshake_start(ssl); - /* Emit ticket_lifetime_hint. */ - l2n(0, p); - /* Emit ticket. */ - s2n(placeholder_len, p); - memcpy(p, kTicketPlaceholder, placeholder_len); - p += placeholder_len; - - len = p - ssl_handshake_start(ssl); - if (!ssl_set_handshake_header(ssl, SSL3_MT_NEW_SESSION_TICKET, len)) { - goto err; - } - ssl->state = SSL3_ST_SW_SESSION_TICKET_B; - return ssl_do_write(ssl); - } - - /* Grow buffer if need be: the length calculation is as follows: - * handshake_header_length + 4 (ticket lifetime hint) + 2 (ticket length) + - * max_ticket_overhead + * session_length */ - if (!BUF_MEM_grow(ssl->init_buf, SSL_HM_HEADER_LENGTH(ssl) + 6 + - max_ticket_overhead + session_len)) { - goto err; - } - p = ssl_handshake_start(ssl); - /* Initialize HMAC and cipher contexts. If callback present it does all the - * work otherwise use generated values from parent ctx. */ - if (tctx->tlsext_ticket_key_cb) { - if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx, - 1 /* encrypt */) < 0) { - goto err; - } - } else { - if (!RAND_bytes(iv, 16) || - !EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, - tctx->tlsext_tick_aes_key, iv) || - !HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(), - NULL)) { - goto err; - } - memcpy(key_name, tctx->tlsext_tick_key_name, 16); - } - - /* Ticket lifetime hint (advisory only): We leave this unspecified for - * resumed session (for simplicity), and guess that tickets for new - * sessions will live as long as their sessions. */ - l2n(ssl->hit ? 0 : ssl->session->timeout, p); - - /* Skip ticket length for now */ - p += 2; - /* Output key name */ - macstart = p; - memcpy(p, key_name, 16); - p += 16; - /* output IV */ - memcpy(p, iv, EVP_CIPHER_CTX_iv_length(&ctx)); - p += EVP_CIPHER_CTX_iv_length(&ctx); - /* Encrypt session data */ - if (!EVP_EncryptUpdate(&ctx, p, &len, session, session_len)) { - goto err; - } - p += len; - if (!EVP_EncryptFinal_ex(&ctx, p, &len)) { - goto err; - } - p += len; - - if (!HMAC_Update(&hctx, macstart, p - macstart) || - !HMAC_Final(&hctx, p, &hlen)) { - goto err; - } - - p += hlen; - /* Now write out lengths: p points to end of data written */ - /* Total length */ - len = p - ssl_handshake_start(ssl); - /* Skip ticket lifetime hint */ - p = ssl_handshake_start(ssl) + 4; - s2n(len - 6, p); - if (!ssl_set_handshake_header(ssl, SSL3_MT_NEW_SESSION_TICKET, len)) { - goto err; - } - ssl->state = SSL3_ST_SW_SESSION_TICKET_B; + if (ssl->state == SSL3_ST_SW_SESSION_TICKET_B) { + return ssl->method->write_message(ssl); } - /* SSL3_ST_SW_SESSION_TICKET_B */ - ret = ssl_do_write(ssl); + /* Serialize the SSL_SESSION to be encoded into the ticket. */ + uint8_t *session = NULL; + size_t session_len; + if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &session, + &session_len)) { + return -1; + } + + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + HMAC_CTX hctx; + HMAC_CTX_init(&hctx); + + int ret = -1; + CBB cbb, body, ticket; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_NEW_SESSION_TICKET) || + /* Ticket lifetime hint (advisory only): We leave this unspecified for + * resumed session (for simplicity), and guess that tickets for new + * sessions will live as long as their sessions. */ + !CBB_add_u32(&body, ssl->hit ? 0 : ssl->session->timeout) || + !CBB_add_u16_length_prefixed(&body, &ticket)) { + goto err; + } + + /* If the session is too long, emit a dummy value rather than abort the + * connection. */ + const size_t max_ticket_overhead = + 16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE; + if (session_len > 0xffff - max_ticket_overhead) { + static const char kTicketPlaceholder[] = "TICKET TOO LARGE"; + + if (!CBB_add_bytes(&ticket, (const uint8_t *)kTicketPlaceholder, + strlen(kTicketPlaceholder)) || + !ssl->method->finish_message(ssl, &cbb)) { + goto err; + } + + ssl->state = SSL3_ST_SW_SESSION_TICKET_B; + ret = 1; + goto err; + } + + /* Initialize HMAC and cipher contexts. If callback present it does all the + * work otherwise use generated values from parent ctx. */ + SSL_CTX *tctx = ssl->initial_ctx; + uint8_t iv[EVP_MAX_IV_LENGTH]; + uint8_t key_name[16]; + if (tctx->tlsext_ticket_key_cb != NULL) { + if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx, + 1 /* encrypt */) < 0) { + goto err; + } + } else { + if (!RAND_bytes(iv, 16) || + !EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, + tctx->tlsext_tick_aes_key, iv) || + !HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(), + NULL)) { + goto err; + } + memcpy(key_name, tctx->tlsext_tick_key_name, 16); + } + + uint8_t *ptr; + if (!CBB_add_bytes(&ticket, key_name, 16) || + !CBB_add_bytes(&ticket, iv, EVP_CIPHER_CTX_iv_length(&ctx)) || + !CBB_reserve(&ticket, &ptr, session_len + EVP_MAX_BLOCK_LENGTH)) { + goto err; + } + + int len; + size_t total = 0; + if (!EVP_EncryptUpdate(&ctx, ptr + total, &len, session, session_len)) { + goto err; + } + total += len; + if (!EVP_EncryptFinal_ex(&ctx, ptr + total, &len)) { + goto err; + } + total += len; + if (!CBB_did_write(&ticket, total)) { + goto err; + } + + unsigned hlen; + if (!HMAC_Update(&hctx, CBB_data(&ticket), CBB_len(&ticket)) || + !CBB_reserve(&ticket, &ptr, EVP_MAX_MD_SIZE) || + !HMAC_Final(&hctx, ptr, &hlen) || + !CBB_did_write(&ticket, hlen) || + !ssl->method->finish_message(ssl, &cbb)) { + goto err; + } + + ssl->state = SSL3_ST_SW_SESSION_TICKET_B; + ret = ssl->method->write_message(ssl); err: OPENSSL_free(session);
diff --git a/ssl/internal.h b/ssl/internal.h index 369bdcc..710fdd3 100644 --- a/ssl/internal.h +++ b/ssl/internal.h
@@ -723,29 +723,17 @@ void ssl_write_buffer_clear(SSL *ssl); +/* Certificate functions. */ + +/* ssl_add_cert_to_cbb adds |x509| to |cbb|. It returns one on success and zero + * on error. */ +int ssl_add_cert_to_cbb(CBB *cbb, X509 *x509); + + /* Underdocumented functions. * * Functions below here haven't been touched up and may be underdocumented. */ -#define l2n(l, c) \ - (*((c)++) = (uint8_t)(((l) >> 24) & 0xff), \ - *((c)++) = (uint8_t)(((l) >> 16) & 0xff), \ - *((c)++) = (uint8_t)(((l) >> 8) & 0xff), \ - *((c)++) = (uint8_t)(((l)) & 0xff)) - -#define s2n(s, c) \ - ((c[0] = (uint8_t)(((s) >> 8) & 0xff), \ - c[1] = (uint8_t)(((s)) & 0xff)), \ - c += 2) - -#define l2n3(l, c) \ - ((c[0] = (uint8_t)(((l) >> 16) & 0xff), \ - c[1] = (uint8_t)(((l) >> 8) & 0xff), \ - c[2] = (uint8_t)(((l)) & 0xff)), \ - c += 3) - -/* LOCAL STUFF */ - #define TLSEXT_CHANNEL_ID_SIZE 128 /* Check if an SSL structure is using DTLS */ @@ -839,12 +827,16 @@ /* supports_cipher returns one if |cipher| is supported by this protocol and * zero otherwise. */ int (*supports_cipher)(const SSL_CIPHER *cipher); - /* Handshake header length */ - unsigned int hhlen; - /* Set the handshake header */ - int (*set_handshake_header)(SSL *ssl, int type, unsigned long len); - /* Write out handshake message */ - int (*do_write)(SSL *ssl); + /* init_message begins a new handshake message of type |type|. |cbb| is the + * root CBB to be passed into |finish_message|. |*body| is set to a child CBB + * the caller should write to. It returns one on success and zero on error. */ + int (*init_message)(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); + /* finish_message finishes a handshake message and prepares it to be + * written. It returns one on success and zero on error. */ + int (*finish_message)(SSL *ssl, CBB *cbb); + /* write_message writes the next message to the transport. It returns one on + * success and <= 0 on error. */ + int (*write_message)(SSL *ssl); /* send_change_cipher_spec sends a ChangeCipherSpec message. */ int (*send_change_cipher_spec)(SSL *ssl, int a, int b); /* expect_flight is called when the handshake expects a flight of messages from @@ -870,13 +862,6 @@ int (*cert_verify_mac)(SSL *, int, uint8_t *); }; -#define SSL_HM_HEADER_LENGTH(ssl) ssl->method->hhlen -#define ssl_handshake_start(ssl) \ - (((uint8_t *)ssl->init_buf->data) + ssl->method->hhlen) -#define ssl_set_handshake_header(ssl, htype, len) \ - ssl->method->set_handshake_header(ssl, htype, len) -#define ssl_do_write(ssl) ssl->method->do_write(ssl) - /* lengths of messages */ #define DTLS1_COOKIE_LENGTH 256 @@ -988,7 +973,7 @@ int (*cb)(SSL *ssl, void *arg), void *arg); int ssl_verify_cert_chain(SSL *ssl, STACK_OF(X509) *cert_chain); -int ssl_add_cert_chain(SSL *ssl, unsigned long *l); +int ssl_add_cert_chain(SSL *ssl, CBB *cbb); void ssl_update_cache(SSL *ssl, int mode); /* ssl_get_compatible_server_ciphers determines the key exchange and @@ -1008,9 +993,7 @@ int ssl3_get_finished(SSL *ssl); int ssl3_send_change_cipher_spec(SSL *ssl, int state_a, int state_b); void ssl3_cleanup_key_block(SSL *ssl); -int ssl3_do_write(SSL *ssl, int type); int ssl3_send_alert(SSL *ssl, int level, int desc); -int ssl3_get_req_cert_type(SSL *ssl, uint8_t *p); long ssl3_get_message(SSL *ssl, int msg_type, enum ssl_hash_message_t hash_message, int *ok); @@ -1046,20 +1029,16 @@ int ssl3_accept(SSL *ssl); int ssl3_connect(SSL *ssl); -int ssl3_set_handshake_header(SSL *ssl, int htype, unsigned long len); -int ssl3_handshake_write(SSL *ssl); +int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); +int ssl3_finish_message(SSL *ssl, CBB *cbb); +int ssl3_write_message(SSL *ssl); + void ssl3_expect_flight(SSL *ssl); void ssl3_received_flight(SSL *ssl); -/* dtls1_do_handshake_write writes handshake message |in| using the give epoch, - * starting |offset| bytes into the message body. It returns one on success. On - * error, it returns <= 0 and sets |*out_offset| to the number of bytes of body - * which were successfully written. This may be used to retry the write - * later. |in| must be a reassembled handshake message with the full DTLS - * handshake header. */ -int dtls1_do_handshake_write(SSL *ssl, size_t *out_offset, const uint8_t *in, - size_t offset, size_t len, - enum dtls1_use_epoch_t use_epoch); +int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); +int dtls1_finish_message(SSL *ssl, CBB *cbb); +int dtls1_write_message(SSL *ssl); /* dtls1_get_record reads a new input record. On success, it places it in * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if @@ -1079,13 +1058,11 @@ int dtls1_send_change_cipher_spec(SSL *ssl, int a, int b); int dtls1_send_finished(SSL *ssl, int a, int b, const char *sender, int slen); -int dtls1_buffer_message(SSL *ssl); int dtls1_retransmit_buffered_messages(SSL *ssl); void dtls1_clear_record_buffer(SSL *ssl); int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr, CBS *out_body); int dtls1_check_timeout_num(SSL *ssl); -int dtls1_set_handshake_header(SSL *ssl, int type, unsigned long len); int dtls1_handshake_write(SSL *ssl); void dtls1_expect_flight(SSL *ssl); void dtls1_received_flight(SSL *ssl);
diff --git a/ssl/s3_both.c b/ssl/s3_both.c index f081066..1bdc2a2 100644 --- a/ssl/s3_both.c +++ b/ssl/s3_both.c
@@ -117,6 +117,7 @@ #include <string.h> #include <openssl/buf.h> +#include <openssl/bytestring.h> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/mem.h> @@ -132,61 +133,118 @@ /* ssl3_do_write sends |ssl->init_buf| in records of type 'type' * (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC). It returns 1 on success * and <= 0 on error. */ -int ssl3_do_write(SSL *ssl, int type) { - int ret = ssl3_write_bytes(ssl, type, ssl->init_buf->data, ssl->init_num); +static int ssl3_do_write(SSL *ssl, int type, const uint8_t *data, size_t len) { + int ret = ssl3_write_bytes(ssl, type, data, len); if (ret <= 0) { return ret; } /* ssl3_write_bytes writes the data in its entirety. */ - assert(ret == ssl->init_num); - ssl_do_msg_callback(ssl, 1 /* write */, ssl->version, type, - ssl->init_buf->data, (size_t)ssl->init_num); - ssl->init_num = 0; + assert((size_t)ret == len); + ssl_do_msg_callback(ssl, 1 /* write */, ssl->version, type, data, len); + return 1; +} + +int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) { + CBB_zero(cbb); + if (ssl->s3->pending_message != NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* Pick a modest size hint to save most of the |realloc| calls. */ + if (!CBB_init(cbb, 64) || + !CBB_add_u8(cbb, type) || + !CBB_add_u24_length_prefixed(cbb, body)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + return 1; +} + +int ssl3_finish_message(SSL *ssl, CBB *cbb) { + if (ssl->s3->pending_message != NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + uint8_t *msg = NULL; + size_t len; + if (!CBB_finish(cbb, &msg, &len) || + len > 0xffffffffu) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + OPENSSL_free(msg); + return 0; + } + + ssl3_update_handshake_hash(ssl, msg, len); + + ssl->s3->pending_message = msg; + ssl->s3->pending_message_len = (uint32_t)len; + return 1; +} + +int ssl3_write_message(SSL *ssl) { + if (ssl->s3->pending_message == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + int ret = ssl3_do_write(ssl, SSL3_RT_HANDSHAKE, ssl->s3->pending_message, + ssl->s3->pending_message_len); + if (ret <= 0) { + return ret; + } + + OPENSSL_free(ssl->s3->pending_message); + ssl->s3->pending_message = NULL; + ssl->s3->pending_message_len = 0; return 1; } int ssl3_send_finished(SSL *ssl, int a, int b) { - uint8_t *p; - int n; - - if (ssl->state == a) { - p = ssl_handshake_start(ssl); - - n = ssl->s3->enc_method->final_finish_mac(ssl, ssl->server, - ssl->s3->tmp.finish_md); - if (n == 0) { - return 0; - } - ssl->s3->tmp.finish_md_len = n; - memcpy(p, ssl->s3->tmp.finish_md, n); - - /* Log the master secret, if logging is enabled. */ - if (!ssl_log_master_secret(ssl, ssl->s3->client_random, SSL3_RANDOM_SIZE, - ssl->session->master_key, - ssl->session->master_key_length)) { - return 0; - } - - /* Copy the finished so we can use it for renegotiation checks */ - if (ssl->server) { - assert(n <= EVP_MAX_MD_SIZE); - memcpy(ssl->s3->previous_server_finished, ssl->s3->tmp.finish_md, n); - ssl->s3->previous_server_finished_len = n; - } else { - assert(n <= EVP_MAX_MD_SIZE); - memcpy(ssl->s3->previous_client_finished, ssl->s3->tmp.finish_md, n); - ssl->s3->previous_client_finished_len = n; - } - - if (!ssl_set_handshake_header(ssl, SSL3_MT_FINISHED, n)) { - return 0; - } - ssl->state = b; + if (ssl->state == b) { + return ssl->method->write_message(ssl); } - /* SSL3_ST_SEND_xxxxxx_HELLO_B */ - return ssl_do_write(ssl); + int n = ssl->s3->enc_method->final_finish_mac(ssl, ssl->server, + ssl->s3->tmp.finish_md); + if (n == 0) { + return 0; + } + ssl->s3->tmp.finish_md_len = n; + + /* Log the master secret, if logging is enabled. */ + if (!ssl_log_master_secret(ssl, ssl->s3->client_random, SSL3_RANDOM_SIZE, + ssl->session->master_key, + ssl->session->master_key_length)) { + return 0; + } + + /* Copy the finished so we can use it for renegotiation checks */ + if (ssl->server) { + assert(n <= EVP_MAX_MD_SIZE); + memcpy(ssl->s3->previous_server_finished, ssl->s3->tmp.finish_md, n); + ssl->s3->previous_server_finished_len = n; + } else { + assert(n <= EVP_MAX_MD_SIZE); + memcpy(ssl->s3->previous_client_finished, ssl->s3->tmp.finish_md, n); + ssl->s3->previous_client_finished_len = n; + } + + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) || + !CBB_add_bytes(&body, ssl->s3->tmp.finish_md, + ssl->s3->tmp.finish_md_len) || + !ssl->method->finish_message(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return -1; + } + + ssl->state = b; + return ssl->method->write_message(ssl); } /* ssl3_take_mac calculates the Finished MAC for the handshakes messages seen @@ -262,30 +320,24 @@ } int ssl3_send_change_cipher_spec(SSL *ssl, int a, int b) { - if (ssl->state == a) { - *((uint8_t *)ssl->init_buf->data) = SSL3_MT_CCS; - ssl->init_num = 1; + static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS}; - ssl->state = b; - } - - /* SSL3_ST_CW_CHANGE_B */ - return ssl3_do_write(ssl, SSL3_RT_CHANGE_CIPHER_SPEC); + ssl->state = b; + return ssl3_do_write(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec, + sizeof(kChangeCipherSpec)); } int ssl3_output_cert_chain(SSL *ssl) { - uint8_t *p; - unsigned long l = 3 + SSL_HM_HEADER_LENGTH(ssl); - - if (!ssl_add_cert_chain(ssl, &l)) { + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) || + !ssl_add_cert_chain(ssl, &body) || + !ssl->method->finish_message(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); return 0; } - l -= 3 + SSL_HM_HEADER_LENGTH(ssl); - p = ssl_handshake_start(ssl); - l2n3(l, p); - l += 3; - return ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, l); + return 1; } size_t ssl_max_handshake_message_len(const SSL *ssl) {
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index f194267..3827f15 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c
@@ -166,21 +166,6 @@ return 1; } -int ssl3_set_handshake_header(SSL *ssl, int htype, unsigned long len) { - uint8_t *p = (uint8_t *)ssl->init_buf->data; - *(p++) = htype; - l2n3(len, p); - ssl->init_num = (int)len + SSL3_HM_HEADER_LENGTH; - - /* Add the message to the handshake hash. */ - return ssl3_update_handshake_hash(ssl, (uint8_t *)ssl->init_buf->data, - ssl->init_num); -} - -int ssl3_handshake_write(SSL *ssl) { - return ssl3_do_write(ssl, SSL3_RT_HANDSHAKE); -} - void ssl3_expect_flight(SSL *ssl) {} void ssl3_received_flight(SSL *ssl) {} @@ -232,6 +217,7 @@ OPENSSL_free(ssl->s3->alpn_selected); SSL_AEAD_CTX_free(ssl->s3->aead_read_ctx); SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx); + OPENSSL_free(ssl->s3->pending_message); OPENSSL_cleanse(ssl->s3, sizeof *ssl->s3); OPENSSL_free(ssl->s3); @@ -331,40 +317,6 @@ return ret; } -int ssl3_get_req_cert_type(SSL *ssl, uint8_t *p) { - int ret = 0; - const uint8_t *sig; - size_t i, siglen; - int have_rsa_sign = 0; - int have_ecdsa_sign = 0; - - /* get configured sigalgs */ - siglen = tls12_get_psigalgs(ssl, &sig); - for (i = 0; i < siglen; i += 2, sig += 2) { - switch (sig[1]) { - case TLSEXT_signature_rsa: - have_rsa_sign = 1; - break; - - case TLSEXT_signature_ecdsa: - have_ecdsa_sign = 1; - break; - } - } - - if (have_rsa_sign) { - p[ret++] = SSL3_CT_RSA_SIGN; - } - - /* ECDSA certs can be used with RSA cipher suites as well so we don't need to - * check for SSL_kECDH or SSL_kECDHE. */ - if (ssl->version >= TLS1_VERSION && have_ecdsa_sign) { - p[ret++] = TLS_CT_ECDSA_SIGN; - } - - return ret; -} - /* If we are using default SHA1+MD5 algorithms switch to new SHA256 PRF and * handshake macs if required. */ uint32_t ssl_get_algorithm_prf(const SSL *ssl) {
diff --git a/ssl/s3_meth.c b/ssl/s3_meth.c index 8370f23..715966b 100644 --- a/ssl/s3_meth.c +++ b/ssl/s3_meth.c
@@ -70,9 +70,9 @@ ssl3_write_app_data, ssl3_dispatch_alert, ssl3_supports_cipher, - SSL3_HM_HEADER_LENGTH, - ssl3_set_handshake_header, - ssl3_handshake_write, + ssl3_init_message, + ssl3_finish_message, + ssl3_write_message, ssl3_send_change_cipher_spec, ssl3_expect_flight, ssl3_received_flight,
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c index 5ec33eb..41987f8 100644 --- a/ssl/ssl_asn1.c +++ b/ssl/ssl_asn1.c
@@ -165,22 +165,6 @@ static const int kCertChainTag = CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 19; -static int add_X509(CBB *cbb, X509 *x509) { - int len = i2d_X509(x509, NULL); - if (len < 0) { - return 0; - } - uint8_t *buf; - if (!CBB_add_space(cbb, &buf, len)) { - OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); - return 0; - } - if (buf != NULL && i2d_X509(x509, &buf) < 0) { - return 0; - } - return 1; -} - static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data, size_t *out_len, int for_ticket) { CBB cbb, session, child, child2; @@ -229,7 +213,7 @@ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } - if (!add_X509(&child, in->peer)) { + if (!ssl_add_cert_to_cbb(&child, in->peer)) { goto err; } } @@ -351,7 +335,7 @@ } size_t i; for (i = 0; i < sk_X509_num(in->cert_chain); i++) { - if (!add_X509(&child, sk_X509_value(in->cert_chain, i))) { + if (!ssl_add_cert_to_cbb(&child, sk_X509_value(in->cert_chain, i))) { goto err; } }
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 0eb0d8b..b608c73 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c
@@ -416,56 +416,58 @@ return add_client_CA(&ctx->client_CA, x509); } -/* Add a certificate to a BUF_MEM structure */ -static int ssl_add_cert_to_buf(BUF_MEM *buf, unsigned long *l, X509 *x) { - int n; - uint8_t *p; - - n = i2d_X509(x, NULL); - if (n < 0 || !BUF_MEM_grow_clean(buf, (int)(n + (*l) + 3))) { - OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); +int ssl_add_cert_to_cbb(CBB *cbb, X509 *x509) { + int len = i2d_X509(x509, NULL); + if (len < 0) { return 0; } - p = (uint8_t *)&(buf->data[*l]); - l2n3(n, p); - n = i2d_X509(x, &p); - if (n < 0) { - /* This shouldn't happen. */ - OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); - return 0; + uint8_t *buf; + if (!CBB_add_space(cbb, &buf, len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; } - *l += n + 3; - + if (buf != NULL && i2d_X509(x509, &buf) < 0) { + return 0; + } return 1; } -/* Add certificate chain to internal SSL BUF_MEM structure. */ -int ssl_add_cert_chain(SSL *ssl, unsigned long *l) { +static int ssl_add_cert_with_length(CBB *cbb, X509 *x509) { + CBB child; + return CBB_add_u24_length_prefixed(cbb, &child) && + ssl_add_cert_to_cbb(&child, x509) && + CBB_flush(cbb); +} + +int ssl_add_cert_chain(SSL *ssl, CBB *cbb) { CERT *cert = ssl->cert; - BUF_MEM *buf = ssl->init_buf; - int no_chain = 0; - size_t i; - X509 *x = cert->x509; - STACK_OF(X509) *chain = cert->chain; - if (x == NULL) { OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET); return 0; } + CBB child; + if (!CBB_add_u24_length_prefixed(cbb, &child)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + int no_chain = 0; + STACK_OF(X509) *chain = cert->chain; if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) || chain != NULL) { no_chain = 1; } if (no_chain) { - if (!ssl_add_cert_to_buf(buf, l, x)) { + if (!ssl_add_cert_to_cbb(&child, x)) { return 0; } + size_t i; for (i = 0; i < sk_X509_num(chain); i++) { x = sk_X509_value(chain, i); - if (!ssl_add_cert_to_buf(buf, l, x)) { + if (!ssl_add_cert_with_length(&child, x)) { return 0; } } @@ -479,10 +481,11 @@ X509_verify_cert(&xs_ctx); /* Don't leave errors in the queue */ ERR_clear_error(); + + size_t i; for (i = 0; i < sk_X509_num(xs_ctx.chain); i++) { x = sk_X509_value(xs_ctx.chain, i); - - if (!ssl_add_cert_to_buf(buf, l, x)) { + if (!ssl_add_cert_with_length(&child, x)) { X509_STORE_CTX_cleanup(&xs_ctx); return 0; } @@ -490,7 +493,7 @@ X509_STORE_CTX_cleanup(&xs_ctx); } - return 1; + return CBB_flush(cbb); } static int set_cert_store(X509_STORE **store_ptr, X509_STORE *new_store, int take_ref) {