diff options
author | Robert Sloan <varomodt@google.com> | 2017-10-23 10:28:39 -0700 |
---|---|---|
committer | Robert Sloan <varomodt@google.com> | 2017-10-23 10:28:45 -0700 |
commit | 36272964d1ab48276cf98c4cfad1130e5483e692 (patch) | |
tree | b94f031d7e41beb57b3cfe7949357e08598c6a96 /src | |
parent | 921ef2c09591ea1f04625e01b800ac4493a8916d (diff) | |
download | boringssl-36272964d1ab48276cf98c4cfad1130e5483e692.tar.gz |
external/boringssl: Sync to 7f8c553d7f4db0a6ce727f2986d41bf8fe8ec4bf.
This includes the following changes:
https://boringssl.googlesource.com/boringssl/+log/f8de2af7e319f83ba88579fbf127ba5fafc26c71..7f8c553d7f4db0a6ce727f2986d41bf8fe8ec4bf
Test: BoringSSL CTS Presubmits.
Change-Id: I1cc1bdabc0fa7ba83731e7fc01eaf3dd80e0aa5b
Diffstat (limited to 'src')
-rw-r--r-- | src/ssl/d1_both.cc | 163 | ||||
-rw-r--r-- | src/ssl/d1_lib.cc | 22 | ||||
-rw-r--r-- | src/ssl/d1_pkt.cc | 135 | ||||
-rw-r--r-- | src/ssl/dtls_method.cc | 22 | ||||
-rw-r--r-- | src/ssl/dtls_record.cc | 11 | ||||
-rw-r--r-- | src/ssl/handshake.cc | 48 | ||||
-rw-r--r-- | src/ssl/internal.h | 200 | ||||
-rw-r--r-- | src/ssl/s3_both.cc | 227 | ||||
-rw-r--r-- | src/ssl/s3_lib.cc | 12 | ||||
-rw-r--r-- | src/ssl/s3_pkt.cc | 255 | ||||
-rw-r--r-- | src/ssl/ssl_buffer.cc | 40 | ||||
-rw-r--r-- | src/ssl/ssl_lib.cc | 149 | ||||
-rw-r--r-- | src/ssl/ssl_test.cc | 74 | ||||
-rw-r--r-- | src/ssl/test/bssl_shim.cc | 37 | ||||
-rw-r--r-- | src/ssl/test/runner/runner.go | 94 | ||||
-rw-r--r-- | src/ssl/tls13_client.cc | 7 | ||||
-rw-r--r-- | src/ssl/tls_method.cc | 26 | ||||
-rw-r--r-- | src/ssl/tls_record.cc | 35 |
18 files changed, 844 insertions, 713 deletions
diff --git a/src/ssl/d1_both.cc b/src/ssl/d1_both.cc index 3a31a029..798deb09 100644 --- a/src/ssl/d1_both.cc +++ b/src/ssl/d1_both.cc @@ -259,9 +259,9 @@ static void dtls1_hm_fragment_mark(hm_fragment *frag, size_t start, frag->reassembly = NULL; } -// dtls1_is_current_message_complete returns one if the current handshake -// message is complete and zero otherwise. -static int dtls1_is_current_message_complete(const SSL *ssl) { +// dtls1_is_current_message_complete returns whether the current handshake +// message is complete. +static bool dtls1_is_current_message_complete(const SSL *ssl) { hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT]; return frag != NULL && frag->reassembly == NULL; @@ -272,9 +272,10 @@ static int dtls1_is_current_message_complete(const SSL *ssl) { // queue. Otherwise, it checks |msg_hdr| is consistent with the existing one. It // returns NULL on failure. The caller does not take ownership of the result. static hm_fragment *dtls1_get_incoming_message( - SSL *ssl, const struct hm_header_st *msg_hdr) { + SSL *ssl, uint8_t *out_alert, const struct hm_header_st *msg_hdr) { if (msg_hdr->seq < ssl->d1->handshake_read_seq || msg_hdr->seq - ssl->d1->handshake_read_seq >= SSL_MAX_HANDSHAKE_FLIGHT) { + *out_alert = SSL_AD_INTERNAL_ERROR; return NULL; } @@ -287,7 +288,7 @@ static hm_fragment *dtls1_get_incoming_message( if (frag->type != msg_hdr->type || frag->msg_len != msg_hdr->msg_len) { OPENSSL_PUT_ERROR(SSL, SSL_R_FRAGMENT_MISMATCH); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; return NULL; } return frag; @@ -296,81 +297,76 @@ static hm_fragment *dtls1_get_incoming_message( // This is the first fragment from this message. frag = dtls1_hm_fragment_new(msg_hdr); if (frag == NULL) { + *out_alert = SSL_AD_INTERNAL_ERROR; return NULL; } ssl->d1->incoming_messages[idx] = frag; return frag; } -int dtls1_read_message(SSL *ssl) { - SSL3_RECORD *rr = &ssl->s3->rrec; - if (rr->length == 0) { - int ret = dtls1_get_record(ssl); - if (ret <= 0) { - return ret; - } +ssl_open_record_t dtls1_open_handshake(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, Span<uint8_t> in) { + uint8_t type; + Span<uint8_t> record; + auto ret = dtls_open_record(ssl, &type, &record, out_consumed, out_alert, in); + if (ret != ssl_open_record_success) { + return ret; } - switch (rr->type) { + switch (type) { case SSL3_RT_APPLICATION_DATA: // Unencrypted application data records are always illegal. if (ssl->s3->aead_read_ctx->is_null_cipher()) { - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); - return -1; + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } // Out-of-order application data may be received between ChangeCipherSpec // and finished. Discard it. - rr->length = 0; - ssl_read_buffer_discard(ssl); - return 1; + return ssl_open_record_discard; case SSL3_RT_CHANGE_CIPHER_SPEC: // We do not support renegotiation, so encrypted ChangeCipherSpec records // are illegal. if (!ssl->s3->aead_read_ctx->is_null_cipher()) { - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); - return -1; + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } - if (rr->length != 1 || rr->data[0] != SSL3_MT_CCS) { + if (record.size() != 1u || record[0] != SSL3_MT_CCS) { OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); - return -1; + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return ssl_open_record_error; } // Flag the ChangeCipherSpec for later. ssl->d1->has_change_cipher_spec = true; ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC, - MakeSpan(rr->data, rr->length)); - - rr->length = 0; - ssl_read_buffer_discard(ssl); - return 1; + record); + return ssl_open_record_success; case SSL3_RT_HANDSHAKE: // Break out to main processing. break; default: - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); - return -1; + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } CBS cbs; - CBS_init(&cbs, rr->data, rr->length); - + CBS_init(&cbs, record.data(), record.size()); while (CBS_len(&cbs) > 0) { // Read a handshake fragment. struct hm_header_st msg_hdr; CBS body; if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) { OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - return -1; + *out_alert = SSL_AD_DECODE_ERROR; + return ssl_open_record_error; } const size_t frag_off = msg_hdr.frag_off; @@ -380,15 +376,15 @@ int dtls1_read_message(SSL *ssl) { frag_off + frag_len > msg_len || msg_len > ssl_max_handshake_message_len(ssl)) { OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); - return -1; + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return ssl_open_record_error; } // The encrypted epoch in DTLS has only one handshake message. if (ssl->d1->r_epoch == 1 && msg_hdr.seq != ssl->d1->handshake_read_seq) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - return -1; + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } if (msg_hdr.seq < ssl->d1->handshake_read_seq || @@ -398,9 +394,9 @@ int dtls1_read_message(SSL *ssl) { continue; } - hm_fragment *frag = dtls1_get_incoming_message(ssl, &msg_hdr); + hm_fragment *frag = dtls1_get_incoming_message(ssl, out_alert, &msg_hdr); if (frag == NULL) { - return -1; + return ssl_open_record_error; } assert(frag->msg_len == msg_len); @@ -416,9 +412,7 @@ int dtls1_read_message(SSL *ssl) { dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len); } - rr->length = 0; - ssl_read_buffer_discard(ssl); - return 1; + return ssl_open_record_success; } bool dtls1_get_message(SSL *ssl, SSLMessage *out) { @@ -480,8 +474,8 @@ bool dtls_has_unprocessed_handshake_data(const SSL *ssl) { return false; } -int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr, - CBS *out_body) { +bool dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr, + CBS *out_body) { OPENSSL_memset(out_hdr, 0x00, sizeof(struct hm_header_st)); if (!CBS_get_u8(cbs, &out_hdr->type) || @@ -490,23 +484,27 @@ int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr, !CBS_get_u24(cbs, &out_hdr->frag_off) || !CBS_get_u24(cbs, &out_hdr->frag_len) || !CBS_get_bytes(cbs, out_body, out_hdr->frag_len)) { - return 0; + return false; } - return 1; + return true; } -int dtls1_read_change_cipher_spec(SSL *ssl) { - // Process handshake records until there is a ChangeCipherSpec. - while (!ssl->d1->has_change_cipher_spec) { - int ret = dtls1_read_message(ssl); - if (ret <= 0) { +ssl_open_record_t dtls1_open_change_cipher_spec(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, + Span<uint8_t> in) { + if (!ssl->d1->has_change_cipher_spec) { + // dtls1_open_handshake processes both handshake and ChangeCipherSpec. + auto ret = dtls1_open_handshake(ssl, out_consumed, out_alert, in); + if (ret != ssl_open_record_success) { return ret; } } - - ssl->d1->has_change_cipher_spec = false; - return 1; + if (ssl->d1->has_change_cipher_spec) { + ssl->d1->has_change_cipher_spec = false; + return ssl_open_record_success; + } + return ssl_open_record_discard; } @@ -524,7 +522,7 @@ void dtls_clear_outgoing_messages(SSL *ssl) { ssl->d1->flight_has_reply = false; } -int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) { +bool 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) || @@ -532,31 +530,29 @@ int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) { !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 false; } - return 1; + return true; } -int dtls1_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg) { +bool dtls1_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg) { if (!CBBFinishArray(cbb, out_msg) || out_msg->size() < DTLS1_HM_HEADER_LENGTH) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return 0; + return false; } // Fix up the header. Copy the fragment length into the total message // length. OPENSSL_memcpy(out_msg->data() + 1, out_msg->data() + DTLS1_HM_HEADER_LENGTH - 3, 3); - return 1; + return true; } // add_outgoing adds a new handshake message or ChangeCipherSpec to the current -// outgoing flight. It returns one on success and zero on error. In both cases, -// it takes ownership of |data| and releases it with |OPENSSL_free| when -// done. -static int add_outgoing(SSL *ssl, int is_ccs, Array<uint8_t> data) { +// outgoing flight. It returns true on success and false on error. +static bool add_outgoing(SSL *ssl, int is_ccs, Array<uint8_t> data) { if (ssl->d1->outgoing_messages_complete) { // If we've begun writing a new flight, we received the peer flight. Discard // the timer and the our flight. @@ -569,9 +565,9 @@ static int add_outgoing(SSL *ssl, int is_ccs, Array<uint8_t> data) { "outgoing_messages_len is too small"); if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT || data.size() > 0xffffffff) { - assert(0); + assert(false); OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return 0; + return false; } if (!is_ccs) { @@ -580,7 +576,7 @@ static int add_outgoing(SSL *ssl, int is_ccs, Array<uint8_t> data) { if (ssl->s3->hs != NULL && !ssl->s3->hs->transcript.Update(data)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return 0; + return false; } ssl->d1->handshake_write_seq++; } @@ -594,24 +590,24 @@ static int add_outgoing(SSL *ssl, int is_ccs, Array<uint8_t> data) { msg->is_ccs = is_ccs; ssl->d1->outgoing_messages_len++; - return 1; + return true; } -int dtls1_add_message(SSL *ssl, Array<uint8_t> data) { +bool dtls1_add_message(SSL *ssl, Array<uint8_t> data) { return add_outgoing(ssl, 0 /* handshake */, std::move(data)); } -int dtls1_add_change_cipher_spec(SSL *ssl) { +bool dtls1_add_change_cipher_spec(SSL *ssl) { return add_outgoing(ssl, 1 /* ChangeCipherSpec */, Array<uint8_t>()); } -int dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc) { +bool dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc) { // The |add_alert| path is only used for warning alerts for now, which DTLS // never sends. This will be implemented later once closure alerts are // converted. - assert(0); + assert(false); OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return 0; + return false; } // dtls1_update_mtu updates the current MTU from the BIO, ensuring it is above @@ -740,9 +736,9 @@ static enum seal_result_t seal_next_message(SSL *ssl, uint8_t *out, // seal_next_packet writes as much of the next flight as possible to |out| and // advances |ssl->d1->outgoing_written| and |ssl->d1->outgoing_offset| as // appropriate. -static int seal_next_packet(SSL *ssl, uint8_t *out, size_t *out_len, - size_t max_out) { - int made_progress = 0; +static bool seal_next_packet(SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out) { + bool made_progress = false; size_t total = 0; assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len); for (; ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len; @@ -753,7 +749,7 @@ static int seal_next_packet(SSL *ssl, uint8_t *out, size_t *out_len, enum seal_result_t ret = seal_next_message(ssl, out, &len, max_out, msg); switch (ret) { case seal_error: - return 0; + return false; case seal_no_progress: goto packet_full; @@ -763,7 +759,7 @@ static int seal_next_packet(SSL *ssl, uint8_t *out, size_t *out_len, out += len; max_out -= len; total += len; - made_progress = 1; + made_progress = true; if (ret == seal_partial) { goto packet_full; @@ -776,14 +772,19 @@ packet_full: // The MTU was too small to make any progress. if (!made_progress) { OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL); - return 0; + return false; } *out_len = total; - return 1; + return true; } static int send_flight(SSL *ssl) { + if (ssl->s3->write_shutdown != ssl_shutdown_none) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + } + dtls1_update_mtu(ssl); int ret = -1; diff --git a/src/ssl/d1_lib.cc b/src/ssl/d1_lib.cc index ec5d4705..38bc5bec 100644 --- a/src/ssl/d1_lib.cc +++ b/src/ssl/d1_lib.cc @@ -78,14 +78,14 @@ namespace bssl { // before failing the DTLS handshake. #define DTLS1_MAX_TIMEOUTS 12 -int dtls1_new(SSL *ssl) { +bool dtls1_new(SSL *ssl) { if (!ssl3_new(ssl)) { - return 0; + return false; } DTLS1_STATE *d1 = (DTLS1_STATE *)OPENSSL_malloc(sizeof *d1); if (d1 == NULL) { ssl3_free(ssl); - return 0; + return false; } OPENSSL_memset(d1, 0, sizeof *d1); @@ -97,7 +97,7 @@ int dtls1_new(SSL *ssl) { // protocol version, and implement this pre-negotiation quirk in |SSL_version| // at the API boundary rather than in internal state. ssl->version = DTLS1_2_VERSION; - return 1; + return true; } void dtls1_free(SSL *ssl) { @@ -133,21 +133,21 @@ void dtls1_start_timer(SSL *ssl) { } } -int dtls1_is_timer_expired(SSL *ssl) { +bool dtls1_is_timer_expired(SSL *ssl) { struct timeval timeleft; // Get time left until timeout, return false if no timer running if (!DTLSv1_get_timeout(ssl, &timeleft)) { - return 0; + return false; } // Return false if timer is not expired yet if (timeleft.tv_sec > 0 || timeleft.tv_usec > 0) { - return 0; + return false; } // Timer expired, so return true - return 1; + return true; } static void dtls1_double_timeout(SSL *ssl) { @@ -163,7 +163,7 @@ void dtls1_stop_timer(SSL *ssl) { ssl->d1->timeout_duration_ms = ssl->initial_timeout_duration_ms; } -int dtls1_check_timeout_num(SSL *ssl) { +bool dtls1_check_timeout_num(SSL *ssl) { ssl->d1->num_timeouts++; // Reduce MTU after 2 unsuccessful retransmissions @@ -178,10 +178,10 @@ int dtls1_check_timeout_num(SSL *ssl) { if (ssl->d1->num_timeouts > DTLS1_MAX_TIMEOUTS) { // fail the connection, enough alerts have been sent OPENSSL_PUT_ERROR(SSL, SSL_R_READ_TIMEOUT_EXPIRED); - return 0; + return false; } - return 1; + return true; } } // namespace bssl diff --git a/src/ssl/d1_pkt.cc b/src/ssl/d1_pkt.cc index 5b1cce5a..c6be93d5 100644 --- a/src/ssl/d1_pkt.cc +++ b/src/ssl/d1_pkt.cc @@ -128,84 +128,29 @@ namespace bssl { -int dtls1_get_record(SSL *ssl) { - for (;;) { - Span<uint8_t> body; - uint8_t type, alert; - size_t consumed; - enum ssl_open_record_t open_ret = dtls_open_record( - ssl, &type, &body, &consumed, &alert, ssl_read_buffer(ssl)); - if (open_ret != ssl_open_record_partial) { - ssl_read_buffer_consume(ssl, consumed); - } - switch (open_ret) { - case ssl_open_record_partial: { - assert(ssl_read_buffer(ssl).empty()); - int read_ret = ssl_read_buffer_extend_to(ssl, 0 /* unused */); - if (read_ret <= 0) { - return read_ret; - } - continue; - } - - case ssl_open_record_success: { - if (body.size() > 0xffff) { - OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); - return -1; - } - - SSL3_RECORD *rr = &ssl->s3->rrec; - rr->type = type; - rr->length = static_cast<uint16_t>(body.size()); - rr->data = body.data(); - return 1; - } - - case ssl_open_record_discard: - continue; - - case ssl_open_record_close_notify: - return 0; - - case ssl_open_record_error: - if (alert != 0) { - ssl_send_alert(ssl, SSL3_AL_FATAL, alert); - } - return -1; - } - - assert(0); - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return -1; - } -} - -int dtls1_read_app_data(SSL *ssl, bool *out_got_handshake, uint8_t *buf, - int len, int peek) { +ssl_open_record_t dtls1_open_app_data(SSL *ssl, Span<uint8_t> *out, + size_t *out_consumed, uint8_t *out_alert, + Span<uint8_t> in) { assert(!SSL_in_init(ssl)); - *out_got_handshake = false; - SSL3_RECORD *rr = &ssl->s3->rrec; - -again: - if (rr->length == 0) { - int ret = dtls1_get_record(ssl); - if (ret <= 0) { - return ret; - } + uint8_t type; + Span<uint8_t> record; + auto ret = dtls_open_record(ssl, &type, &record, out_consumed, out_alert, in); + if (ret != ssl_open_record_success) { + return ret; } - if (rr->type == SSL3_RT_HANDSHAKE) { + if (type == SSL3_RT_HANDSHAKE) { // Parse the first fragment header to determine if this is a pre-CCS or // post-CCS handshake record. DTLS resets handshake message numbers on each // handshake, so renegotiations and retransmissions are ambiguous. CBS cbs, body; struct hm_header_st msg_hdr; - CBS_init(&cbs, rr->data, rr->length); + CBS_init(&cbs, record.data(), record.size()); if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) { - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD); - return -1; + *out_alert = SSL_AD_DECODE_ERROR; + return ssl_open_record_error; } if (msg_hdr.type == SSL3_MT_FINISHED && @@ -215,62 +160,31 @@ again: // Finished, they may not have received ours. Only do this for the // first fragment, in case the Finished was fragmented. if (!dtls1_check_timeout_num(ssl)) { - return -1; + *out_alert = 0; // TODO(davidben): Send an alert? + return ssl_open_record_error; } dtls1_retransmit_outgoing_messages(ssl); } - - rr->length = 0; - goto again; + return ssl_open_record_discard; } // Otherwise, this is a pre-CCS handshake message from an unsupported // renegotiation attempt. Fall through to the error path. } - if (rr->type != SSL3_RT_APPLICATION_DATA) { - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + if (type != SSL3_RT_APPLICATION_DATA) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); - return -1; - } - - // Discard empty records. - if (rr->length == 0) { - goto again; - } - - if (len <= 0) { - return len; - } - - if ((unsigned)len > rr->length) { - len = rr->length; + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } - OPENSSL_memcpy(buf, rr->data, len); - if (!peek) { - // TODO(davidben): Should the record be truncated instead? This is a - // datagram transport. See https://crbug.com/boringssl/65. - rr->length -= len; - rr->data += len; - if (rr->length == 0) { - // The record has been consumed, so we may now clear the buffer. - ssl_read_buffer_discard(ssl); - } + if (record.empty()) { + return ssl_open_record_discard; } - return len; -} - -void dtls1_read_close_notify(SSL *ssl) { - // Bidirectional shutdown doesn't make sense for an unordered transport. DTLS - // alerts also aren't delivered reliably, so we may even time out because the - // peer never received our close_notify. Report to the caller that the channel - // has fully shut down. - if (ssl->s3->read_shutdown == ssl_shutdown_none) { - ssl->s3->read_shutdown = ssl_shutdown_close_notify; - } + *out = record; + return ssl_open_record_success; } int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake, @@ -278,6 +192,11 @@ int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake, assert(!SSL_in_init(ssl)); *out_needs_handshake = false; + if (ssl->s3->write_shutdown != ssl_shutdown_none) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + } + if (len > SSL3_RT_MAX_PLAIN_LENGTH) { OPENSSL_PUT_ERROR(SSL, SSL_R_DTLS_MESSAGE_TOO_BIG); return -1; diff --git a/src/ssl/dtls_method.cc b/src/ssl/dtls_method.cc index daaec2df..fab19be4 100644 --- a/src/ssl/dtls_method.cc +++ b/src/ssl/dtls_method.cc @@ -68,7 +68,7 @@ using namespace bssl; -static int dtls1_supports_cipher(const SSL_CIPHER *cipher) { +static bool dtls1_supports_cipher(const SSL_CIPHER *cipher) { return cipher->algorithm_enc != SSL_eNULL; } @@ -82,12 +82,12 @@ static void dtls1_on_handshake_complete(SSL *ssl) { } } -static int dtls1_set_read_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) { +static bool dtls1_set_read_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) { // Cipher changes are forbidden if the current epoch has leftover data. if (dtls_has_unprocessed_handshake_data(ssl)) { OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFERED_MESSAGES_ON_CIPHER_CHANGE); ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - return 0; + return false; } ssl->d1->r_epoch++; @@ -96,10 +96,11 @@ static int dtls1_set_read_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) { Delete(ssl->s3->aead_read_ctx); ssl->s3->aead_read_ctx = aead_ctx.release(); - return 1; + return true; } -static int dtls1_set_write_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) { +static bool dtls1_set_write_state(SSL *ssl, + UniquePtr<SSLAEADContext> aead_ctx) { ssl->d1->w_epoch++; OPENSSL_memcpy(ssl->d1->last_write_sequence, ssl->s3->write_sequence, sizeof(ssl->s3->write_sequence)); @@ -108,19 +109,18 @@ static int dtls1_set_write_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) { Delete(ssl->d1->last_aead_write_ctx); ssl->d1->last_aead_write_ctx = ssl->s3->aead_write_ctx; ssl->s3->aead_write_ctx = aead_ctx.release(); - return 1; + return true; } static const SSL_PROTOCOL_METHOD kDTLSProtocolMethod = { - 1 /* is_dtls */, + true /* is_dtls */, dtls1_new, dtls1_free, dtls1_get_message, - dtls1_read_message, dtls1_next_message, - dtls1_read_app_data, - dtls1_read_change_cipher_spec, - dtls1_read_close_notify, + dtls1_open_handshake, + dtls1_open_change_cipher_spec, + dtls1_open_app_data, dtls1_write_app_data, dtls1_dispatch_alert, dtls1_supports_cipher, diff --git a/src/ssl/dtls_record.cc b/src/ssl/dtls_record.cc index 2bf1d426..a7466405 100644 --- a/src/ssl/dtls_record.cc +++ b/src/ssl/dtls_record.cc @@ -179,15 +179,8 @@ enum ssl_open_record_t dtls_open_record(SSL *ssl, uint8_t *out_type, size_t *out_consumed, uint8_t *out_alert, Span<uint8_t> in) { *out_consumed = 0; - switch (ssl->s3->read_shutdown) { - case ssl_shutdown_none: - break; - case ssl_shutdown_fatal_alert: - OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); - *out_alert = 0; - return ssl_open_record_error; - case ssl_shutdown_close_notify: - return ssl_open_record_close_notify; + if (ssl->s3->read_shutdown == ssl_shutdown_close_notify) { + return ssl_open_record_close_notify; } if (in.empty()) { diff --git a/src/ssl/handshake.cc b/src/ssl/handshake.cc index 80b64f38..8531ca43 100644 --- a/src/ssl/handshake.cc +++ b/src/ssl/handshake.cc @@ -160,25 +160,25 @@ SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl) { void ssl_handshake_free(SSL_HANDSHAKE *hs) { Delete(hs); } -int ssl_check_message_type(SSL *ssl, const SSLMessage &msg, int type) { +bool ssl_check_message_type(SSL *ssl, const SSLMessage &msg, int type) { if (msg.type != type) { ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); ERR_add_error_dataf("got type %d, wanted type %d", msg.type, type); - return 0; + return false; } - return 1; + return true; } -int ssl_add_message_cbb(SSL *ssl, CBB *cbb) { +bool ssl_add_message_cbb(SSL *ssl, CBB *cbb) { Array<uint8_t> msg; if (!ssl->method->finish_message(ssl, cbb, &msg) || !ssl->method->add_message(ssl, std::move(msg))) { - return 0; + return false; } - return 1; + return true; } size_t ssl_max_handshake_message_len(const SSL *ssl) { @@ -498,12 +498,22 @@ int ssl_run_handshake(SSL_HANDSHAKE *hs, bool *out_early_return) { } case ssl_hs_read_server_hello: - case ssl_hs_read_message: { - int ret = ssl->method->read_message(ssl); - if (ret <= 0) { + case ssl_hs_read_message: + case ssl_hs_read_change_cipher_spec: { + uint8_t alert = SSL_AD_DECODE_ERROR; + size_t consumed = 0; + ssl_open_record_t ret; + if (hs->wait == ssl_hs_read_change_cipher_spec) { + ret = ssl_open_change_cipher_spec(ssl, &consumed, &alert, + ssl_read_buffer(ssl)); + } else { + ret = + ssl_open_handshake(ssl, &consumed, &alert, ssl_read_buffer(ssl)); + } + if (ret == ssl_open_record_error && + hs->wait == ssl_hs_read_server_hello) { uint32_t err = ERR_peek_error(); - if (hs->wait == ssl_hs_read_server_hello && - ERR_GET_LIB(err) == ERR_LIB_SSL && + if (ERR_GET_LIB(err) == ERR_LIB_SSL && ERR_GET_REASON(err) == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE) { // Add a dedicated error code to the queue for a handshake_failure // alert in response to ClientHello. This matches NSS's client @@ -514,16 +524,16 @@ int ssl_run_handshake(SSL_HANDSHAKE *hs, bool *out_early_return) { // See https://crbug.com/446505. OPENSSL_PUT_ERROR(SSL, SSL_R_HANDSHAKE_FAILURE_ON_CLIENT_HELLO); } - return ret; } - break; - } - - case ssl_hs_read_change_cipher_spec: { - int ret = ssl->method->read_change_cipher_spec(ssl); - if (ret <= 0) { - return ret; + bool retry; + int bio_ret = ssl_handle_open_record(ssl, &retry, ret, consumed, alert); + if (bio_ret <= 0) { + return bio_ret; + } + if (retry) { + continue; } + ssl_read_buffer_discard(ssl); break; } diff --git a/src/ssl/internal.h b/src/ssl/internal.h index edbf4eb5..174445ae 100644 --- a/src/ssl/internal.h +++ b/src/ssl/internal.h @@ -1001,6 +1001,10 @@ size_t ssl_max_handshake_message_len(const SSL *ssl); // dtls_clear_incoming_messages releases all buffered incoming messages. void dtls_clear_incoming_messages(SSL *ssl); +// tls_can_accept_handshake_data returns whether |ssl| is able to accept more +// data into handshake buffer. +bool tls_can_accept_handshake_data(const SSL *ssl, uint8_t *out_alert); + // tls_has_unprocessed_handshake_data returns whether there is buffered // handshake data that has not been consumed by |get_message|. bool tls_has_unprocessed_handshake_data(const SSL *ssl); @@ -1058,6 +1062,12 @@ void ssl_read_buffer_discard(SSL *ssl); // zero-initializes it. void ssl_read_buffer_clear(SSL *ssl); +// ssl_handle_open_record handles the result of passing |ssl_read_buffer| to a +// record-processing function. If |ret| is a success or if the caller should +// retry, it returns one and sets |*out_retry|. Otherwise, it returns <= 0. +int ssl_handle_open_record(SSL *ssl, bool *out_retry, ssl_open_record_t ret, + size_t consumed, uint8_t alert); + // ssl_write_buffer_is_pending returns one if the write buffer has pending data // and zero if is empty. int ssl_write_buffer_is_pending(const SSL *ssl); @@ -1465,7 +1475,7 @@ void ssl_handshake_free(SSL_HANDSHAKE *hs); // ssl_check_message_type checks if |msg| has type |type|. If so it returns // one. Otherwise, it sends an alert and returns zero. -int ssl_check_message_type(SSL *ssl, const SSLMessage &msg, int type); +bool ssl_check_message_type(SSL *ssl, const SSLMessage &msg, int type); // ssl_run_handshake runs the TLS handshake. It returns one on success and <= 0 // on error. It sets |out_early_return| to one if we've completed the handshake @@ -1718,64 +1728,83 @@ struct CERT { // |SSL_PROTOCOL_METHOD| abstracts between TLS and DTLS. struct SSL_PROTOCOL_METHOD { - // is_dtls is one if the protocol is DTLS and zero otherwise. - char is_dtls; - int (*ssl_new)(SSL *ssl); + bool is_dtls; + bool (*ssl_new)(SSL *ssl); void (*ssl_free)(SSL *ssl); // get_message sets |*out| to the current handshake message and returns true // if one has been received. It returns false if more input is needed. bool (*get_message)(SSL *ssl, SSLMessage *out); - // read_message reads additional handshake data for |get_message|. On success, - // it returns one. Otherwise, it returns <= 0. - int (*read_message)(SSL *ssl); // next_message is called to release the current handshake message. void (*next_message)(SSL *ssl); - // read_app_data reads up to |len| bytes of application data into |buf|. On - // success, it returns the number of bytes read. Otherwise, it returns <= 0 - // and sets |*out_got_handshake| to whether the failure was due to a - // post-handshake handshake message. If so, any handshake messages consumed - // may be read with |get_message|. - int (*read_app_data)(SSL *ssl, bool *out_got_handshake, uint8_t *buf, int len, - int peek); - int (*read_change_cipher_spec)(SSL *ssl); - void (*read_close_notify)(SSL *ssl); + // Use the |ssl_open_handshake| wrapper. + ssl_open_record_t (*open_handshake)(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, Span<uint8_t> in); + // Use the |ssl_open_change_cipher_spec| wrapper. + ssl_open_record_t (*open_change_cipher_spec)(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, + Span<uint8_t> in); + // Use the |ssl_open_app_data| wrapper. + ssl_open_record_t (*open_app_data)(SSL *ssl, Span<uint8_t> *out, + size_t *out_consumed, uint8_t *out_alert, + Span<uint8_t> in); int (*write_app_data)(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf, int len); int (*dispatch_alert)(SSL *ssl); - // supports_cipher returns one if |cipher| is supported by this protocol and - // zero otherwise. - int (*supports_cipher)(const SSL_CIPHER *cipher); + // supports_cipher returns whether |cipher| is supported by this protocol. + bool (*supports_cipher)(const SSL_CIPHER *cipher); // 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); + // the caller should write to. It returns true on success and false on error. + bool (*init_message)(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); // finish_message finishes a handshake message. It sets |*out_msg| to the - // serialized message. It returns one on success and zero on error. - int (*finish_message)(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg); - // add_message adds a handshake message to the pending flight. It returns one - // on success and zero on error. - int (*add_message)(SSL *ssl, Array<uint8_t> msg); + // serialized message. It returns true on success and false on error. + bool (*finish_message)(SSL *ssl, CBB *cbb, bssl::Array<uint8_t> *out_msg); + // add_message adds a handshake message to the pending flight. It returns + // true on success and false on error. + bool (*add_message)(SSL *ssl, bssl::Array<uint8_t> msg); // add_change_cipher_spec adds a ChangeCipherSpec record to the pending - // flight. It returns one on success and zero on error. - int (*add_change_cipher_spec)(SSL *ssl); - // add_alert adds an alert to the pending flight. It returns one on success - // and zero on error. - int (*add_alert)(SSL *ssl, uint8_t level, uint8_t desc); + // flight. It returns true on success and false on error. + bool (*add_change_cipher_spec)(SSL *ssl); + // add_alert adds an alert to the pending flight. It returns true on success + // and false on error. + bool (*add_alert)(SSL *ssl, uint8_t level, uint8_t desc); // flush_flight flushes the pending flight to the transport. It returns one on // success and <= 0 on error. int (*flush_flight)(SSL *ssl); // on_handshake_complete is called when the handshake is complete. void (*on_handshake_complete)(SSL *ssl); // set_read_state sets |ssl|'s read cipher state to |aead_ctx|. It returns - // one on success and zero if changing the read state is forbidden at this + // true on success and false if changing the read state is forbidden at this // point. - int (*set_read_state)(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx); + bool (*set_read_state)(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx); // set_write_state sets |ssl|'s write cipher state to |aead_ctx|. It returns - // one on success and zero if changing the write state is forbidden at this + // true on success and false if changing the write state is forbidden at this // point. - int (*set_write_state)(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx); + bool (*set_write_state)(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx); }; +// The following wrappers call |open_*| but handle |read_shutdown| correctly. + +// ssl_open_handshake processes a record from |in| for reading a handshake +// message. +ssl_open_record_t ssl_open_handshake(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, Span<uint8_t> in); + +// ssl_open_change_cipher_spec processes a record from |in| for reading a +// ChangeCipherSpec. +ssl_open_record_t ssl_open_change_cipher_spec(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, + Span<uint8_t> in); + +// ssl_open_app_data processes a record from |in| for reading application data. +// On success, it returns |ssl_open_record_success| and sets |*out| to the +// input. If it encounters a post-handshake message, it returns +// |ssl_open_record_discard|. The caller should then retry, after processing any +// messages received with |get_message|. +ssl_open_record_t ssl_open_app_data(SSL *ssl, Span<uint8_t> *out, + size_t *out_consumed, uint8_t *out_alert, + Span<uint8_t> in); + // ssl_crypto_x509_method provides the |SSL_X509_METHOD| functions using // crypto/x509. extern const SSL_X509_METHOD ssl_crypto_x509_method; @@ -2105,15 +2134,6 @@ struct SSLContext { bool ed25519_enabled:1; }; -struct SSL3_RECORD { - // type is the record type. - uint8_t type; - // length is the number of unconsumed bytes in the record. - uint16_t length; - // data is a non-owning pointer to the first unconsumed byte of the record. - uint8_t *data; -}; - struct SSL3_BUFFER { // buf is the memory allocated for this buffer. uint8_t *buf; @@ -2130,7 +2150,7 @@ struct SSL3_BUFFER { enum ssl_shutdown_t { ssl_shutdown_none = 0, ssl_shutdown_close_notify = 1, - ssl_shutdown_fatal_alert = 2, + ssl_shutdown_error = 2, }; struct SSL3_STATE { @@ -2145,7 +2165,9 @@ struct SSL3_STATE { // write_buffer holds data to be written to the transport. SSL3_BUFFER write_buffer; - SSL3_RECORD rrec; // each decoded record goes in here + // pending_app_data is the unconsumed application data. It points into + // |read_buffer|. + Span<uint8_t> pending_app_data; // partial write - check the numbers match unsigned int wnum; // number of bytes sent so far @@ -2160,6 +2182,10 @@ struct SSL3_STATE { // write_shutdown is the shutdown state for the write half of the connection. enum ssl_shutdown_t write_shutdown; + // read_error, if |read_shutdown| is |ssl_shutdown_error|, is the error for + // the receive half of the connection. + ERR_SAVE_STATE *read_error; + int alert_dispatch; int total_renegotiations; @@ -2684,55 +2710,51 @@ void ssl_update_cache(SSL_HANDSHAKE *hs, int mode); int ssl_send_alert(SSL *ssl, int level, int desc); bool ssl3_get_message(SSL *ssl, SSLMessage *out); -int ssl3_read_message(SSL *ssl); +ssl_open_record_t ssl3_open_handshake(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, Span<uint8_t> in); void ssl3_next_message(SSL *ssl); int ssl3_dispatch_alert(SSL *ssl); -int ssl3_read_app_data(SSL *ssl, bool *out_got_handshake, uint8_t *buf, int len, - int peek); -int ssl3_read_change_cipher_spec(SSL *ssl); -void ssl3_read_close_notify(SSL *ssl); -// ssl3_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 -// more data is needed. -int ssl3_get_record(SSL *ssl); +ssl_open_record_t ssl3_open_app_data(SSL *ssl, Span<uint8_t> *out, + size_t *out_consumed, uint8_t *out_alert, + Span<uint8_t> in); +ssl_open_record_t ssl3_open_change_cipher_spec(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, + Span<uint8_t> in); int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf, int len); -int ssl3_new(SSL *ssl); +bool ssl3_new(SSL *ssl); void ssl3_free(SSL *ssl); -int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); -int ssl3_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg); -int ssl3_add_message(SSL *ssl, Array<uint8_t> msg); -int ssl3_add_change_cipher_spec(SSL *ssl); -int ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc); +bool ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); +bool ssl3_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg); +bool ssl3_add_message(SSL *ssl, Array<uint8_t> msg); +bool ssl3_add_change_cipher_spec(SSL *ssl); +bool ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc); int ssl3_flush_flight(SSL *ssl); -int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); -int dtls1_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg); -int dtls1_add_message(SSL *ssl, Array<uint8_t> msg); -int dtls1_add_change_cipher_spec(SSL *ssl); -int dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc); +bool dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); +bool dtls1_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg); +bool dtls1_add_message(SSL *ssl, Array<uint8_t> msg); +bool dtls1_add_change_cipher_spec(SSL *ssl); +bool dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc); int dtls1_flush_flight(SSL *ssl); // ssl_add_message_cbb finishes the handshake message in |cbb| and adds it to -// the pending flight. It returns one on success and zero on error. -int ssl_add_message_cbb(SSL *ssl, CBB *cbb); +// the pending flight. It returns true on success and false on error. +bool ssl_add_message_cbb(SSL *ssl, CBB *cbb); -// ssl_hash_message incorporates |msg| into the handshake hash. It returns one -// on success and zero on allocation failure. +// ssl_hash_message incorporates |msg| into the handshake hash. It returns true +// on success and false on allocation failure. bool ssl_hash_message(SSL_HANDSHAKE *hs, const SSLMessage &msg); -// 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 -// more data is needed. -int dtls1_get_record(SSL *ssl); - -int dtls1_read_app_data(SSL *ssl, bool *out_got_handshake, uint8_t *buf, - int len, int peek); -int dtls1_read_change_cipher_spec(SSL *ssl); -void dtls1_read_close_notify(SSL *ssl); +ssl_open_record_t dtls1_open_app_data(SSL *ssl, Span<uint8_t> *out, + size_t *out_consumed, uint8_t *out_alert, + Span<uint8_t> in); +ssl_open_record_t dtls1_open_change_cipher_spec(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, + Span<uint8_t> in); int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf, int len); @@ -2742,26 +2764,22 @@ int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake, int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len, enum dtls1_use_epoch_t use_epoch); -int dtls1_send_finished(SSL *ssl, int a, int b, const char *sender, int slen); int dtls1_retransmit_outgoing_messages(SSL *ssl); -void dtls1_clear_record_buffer(SSL *ssl); -int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr, +bool dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr, CBS *out_body); -int dtls1_check_timeout_num(SSL *ssl); -int dtls1_handshake_write(SSL *ssl); +bool dtls1_check_timeout_num(SSL *ssl); void dtls1_start_timer(SSL *ssl); void dtls1_stop_timer(SSL *ssl); -int dtls1_is_timer_expired(SSL *ssl); +bool dtls1_is_timer_expired(SSL *ssl); unsigned int dtls1_min_mtu(void); -int dtls1_new(SSL *ssl); -int dtls1_accept(SSL *ssl); -int dtls1_connect(SSL *ssl); +bool dtls1_new(SSL *ssl); void dtls1_free(SSL *ssl); bool dtls1_get_message(SSL *ssl, SSLMessage *out); -int dtls1_read_message(SSL *ssl); +ssl_open_record_t dtls1_open_handshake(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, Span<uint8_t> in); void dtls1_next_message(SSL *ssl); int dtls1_dispatch_alert(SSL *ssl); @@ -2858,6 +2876,10 @@ void ssl_ctx_get_current_time(const SSL_CTX *ctx, // ssl_reset_error_state resets state for |SSL_get_error|. void ssl_reset_error_state(SSL *ssl); +// ssl_set_read_error sets |ssl|'s read half into an error state, saving the +// current state of the error queue. +void ssl_set_read_error(SSL* ssl); + } // namespace bssl diff --git a/src/ssl/s3_both.cc b/src/ssl/s3_both.cc index 1c6882ed..14590857 100644 --- a/src/ssl/s3_both.cc +++ b/src/ssl/s3_both.cc @@ -132,15 +132,15 @@ namespace bssl { -static int add_record_to_flight(SSL *ssl, uint8_t type, - Span<const uint8_t> in) { +static bool add_record_to_flight(SSL *ssl, uint8_t type, + Span<const uint8_t> in) { // We'll never add a flight while in the process of writing it out. assert(ssl->s3->pending_flight_offset == 0); if (ssl->s3->pending_flight == NULL) { ssl->s3->pending_flight = BUF_MEM_new(); if (ssl->s3->pending_flight == NULL) { - return 0; + return false; } } @@ -148,7 +148,7 @@ static int add_record_to_flight(SSL *ssl, uint8_t type, size_t new_cap = ssl->s3->pending_flight->length + max_out; if (max_out < in.size() || new_cap < max_out) { OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); - return 0; + return false; } size_t len; @@ -157,31 +157,31 @@ static int add_record_to_flight(SSL *ssl, uint8_t type, (uint8_t *)ssl->s3->pending_flight->data + ssl->s3->pending_flight->length, &len, max_out, type, in.data(), in.size())) { - return 0; + return false; } ssl->s3->pending_flight->length += len; - return 1; + return true; } -int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) { +bool ssl3_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_length_prefixed(cbb, body)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); CBB_cleanup(cbb); - return 0; + return false; } - return 1; + return true; } -int ssl3_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg) { +bool ssl3_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg) { return CBBFinishArray(cbb, out_msg); } -int ssl3_add_message(SSL *ssl, Array<uint8_t> msg) { +bool ssl3_add_message(SSL *ssl, Array<uint8_t> msg) { // Add the message to the current flight, splitting into several records if // needed. Span<const uint8_t> rest = msg; @@ -190,7 +190,7 @@ int ssl3_add_message(SSL *ssl, Array<uint8_t> msg) { rest = rest.subspan(chunk.size()); if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, chunk)) { - return 0; + return false; } } while (!rest.empty()); @@ -199,33 +199,33 @@ int ssl3_add_message(SSL *ssl, Array<uint8_t> msg) { // hs. if (ssl->s3->hs != NULL && !ssl->s3->hs->transcript.Update(msg)) { - return 0; + return false; } - return 1; + return true; } -int ssl3_add_change_cipher_spec(SSL *ssl) { +bool ssl3_add_change_cipher_spec(SSL *ssl) { static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS}; if (!add_record_to_flight(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec)) { - return 0; + return false; } ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec); - return 1; + return true; } -int ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc) { +bool ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc) { uint8_t alert[2] = {level, desc}; if (!add_record_to_flight(ssl, SSL3_RT_ALERT, alert)) { - return 0; + return false; } ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, alert); ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, ((int)level << 8) | desc); - return 1; + return true; } int ssl3_flush_flight(SSL *ssl) { @@ -233,6 +233,11 @@ int ssl3_flush_flight(SSL *ssl) { return 1; } + if (ssl->s3->write_shutdown != ssl_shutdown_none) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + } + if (ssl->s3->pending_flight->length > 0xffffffff || ssl->s3->pending_flight->length > INT_MAX) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); @@ -274,55 +279,28 @@ int ssl3_flush_flight(SSL *ssl) { return 1; } -static int read_v2_client_hello(SSL *ssl) { - // Read the first 5 bytes, the size of the TLS record header. This is - // sufficient to detect a V2ClientHello and ensures that we never read beyond - // the first record. - int ret = ssl_read_buffer_extend_to(ssl, SSL3_RT_HEADER_LENGTH); - if (ret <= 0) { - return ret; - } - const uint8_t *p = ssl_read_buffer(ssl).data(); - - // Some dedicated error codes for protocol mixups should the application wish - // to interpret them differently. (These do not overlap with ClientHello or - // V2ClientHello.) - if (strncmp("GET ", (const char *)p, 4) == 0 || - strncmp("POST ", (const char *)p, 5) == 0 || - strncmp("HEAD ", (const char *)p, 5) == 0 || - strncmp("PUT ", (const char *)p, 4) == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_R_HTTP_REQUEST); - return -1; - } - if (strncmp("CONNE", (const char *)p, 5) == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_R_HTTPS_PROXY_REQUEST); - return -1; - } - - if ((p[0] & 0x80) == 0 || p[2] != SSL2_MT_CLIENT_HELLO || - p[3] != SSL3_VERSION_MAJOR) { - // Not a V2ClientHello. - return 1; - } - +static ssl_open_record_t read_v2_client_hello(SSL *ssl, size_t *out_consumed, + Span<const uint8_t> in) { + *out_consumed = 0; + assert(in.size() >= SSL3_RT_HEADER_LENGTH); // Determine the length of the V2ClientHello. - size_t msg_length = ((p[0] & 0x7f) << 8) | p[1]; + size_t msg_length = ((in[0] & 0x7f) << 8) | in[1]; if (msg_length > (1024 * 4)) { OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE); - return -1; + return ssl_open_record_error; } if (msg_length < SSL3_RT_HEADER_LENGTH - 2) { // Reject lengths that are too short early. We have already read // |SSL3_RT_HEADER_LENGTH| bytes, so we should not attempt to process an // (invalid) V2ClientHello which would be shorter than that. OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_LENGTH_MISMATCH); - return -1; + return ssl_open_record_error; } - // Read the remainder of the V2ClientHello. - ret = ssl_read_buffer_extend_to(ssl, 2 + msg_length); - if (ret <= 0) { - return ret; + // Ask for the remainder of the V2ClientHello. + if (in.size() < 2 + msg_length) { + *out_consumed = 2 + msg_length; + return ssl_open_record_partial; } CBS v2_client_hello = CBS(ssl_read_buffer(ssl).subspan(2, msg_length)); @@ -330,7 +308,7 @@ static int read_v2_client_hello(SSL *ssl) { // hash. This is only ever called at the start of the handshake, so hs is // guaranteed to be non-NULL. if (!ssl->s3->hs->transcript.Update(v2_client_hello)) { - return -1; + return ssl_open_record_error; } ssl_do_msg_callback(ssl, 0 /* read */, 0 /* V2ClientHello */, @@ -349,7 +327,7 @@ static int read_v2_client_hello(SSL *ssl) { !CBS_get_bytes(&v2_client_hello, &challenge, challenge_length) || CBS_len(&v2_client_hello) != 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); - return -1; + return ssl_open_record_error; } // msg_type has already been checked. @@ -385,7 +363,7 @@ static int read_v2_client_hello(SSL *ssl) { !CBB_add_u8(&hello_body, 0) || !CBB_add_u16_length_prefixed(&hello_body, &cipher_suites)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); - return -1; + return ssl_open_record_error; } // Copy the cipher suites. @@ -393,7 +371,7 @@ static int read_v2_client_hello(SSL *ssl) { uint32_t cipher_spec; if (!CBS_get_u24(&cipher_specs, &cipher_spec)) { OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); - return -1; + return ssl_open_record_error; } // Skip SSLv2 ciphers. @@ -402,7 +380,7 @@ static int read_v2_client_hello(SSL *ssl) { } if (!CBB_add_u16(&cipher_suites, cipher_spec)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return -1; + return ssl_open_record_error; } } @@ -411,15 +389,12 @@ static int read_v2_client_hello(SSL *ssl) { !CBB_add_u8(&hello_body, 0) || !CBB_finish(client_hello.get(), NULL, &ssl->init_buf->length)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return -1; + return ssl_open_record_error; } - // Consume and discard the V2ClientHello. - ssl_read_buffer_consume(ssl, 2 + msg_length); - ssl_read_buffer_discard(ssl); - + *out_consumed = 2 + msg_length; ssl->s3->is_v2_hello = true; - return 1; + return ssl_open_record_success; } static bool parse_message(const SSL *ssl, SSLMessage *out, @@ -464,6 +439,26 @@ bool ssl3_get_message(SSL *ssl, SSLMessage *out) { return true; } +bool tls_can_accept_handshake_data(const SSL *ssl, uint8_t *out_alert) { + // If there is a complete message, the caller must have consumed it first. + SSLMessage msg; + size_t bytes_needed; + if (parse_message(ssl, &msg, &bytes_needed)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + *out_alert = SSL_AD_INTERNAL_ERROR; + return false; + } + + // Enforce the limit so the peer cannot force us to buffer 16MB. + if (bytes_needed > 4 + ssl_max_handshake_message_len(ssl)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return false; + } + + return true; +} + bool tls_has_unprocessed_handshake_data(const SSL *ssl) { size_t msg_len = 0; if (ssl->s3->has_message) { @@ -477,72 +472,92 @@ bool tls_has_unprocessed_handshake_data(const SSL *ssl) { return ssl->init_buf != NULL && ssl->init_buf->length > msg_len; } -int ssl3_read_message(SSL *ssl) { - SSLMessage msg; - size_t bytes_needed; - if (parse_message(ssl, &msg, &bytes_needed)) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return -1; - } - - // Enforce the limit so the peer cannot force us to buffer 16MB. - if (bytes_needed > 4 + ssl_max_handshake_message_len(ssl)) { - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); - OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); - return -1; - } - +ssl_open_record_t ssl3_open_handshake(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, Span<uint8_t> in) { + *out_consumed = 0; // Re-create the handshake buffer if needed. if (ssl->init_buf == NULL) { ssl->init_buf = BUF_MEM_new(); if (ssl->init_buf == NULL) { - return -1; + *out_alert = SSL_AD_INTERNAL_ERROR; + return ssl_open_record_error; } } // Bypass the record layer for the first message to handle V2ClientHello. if (ssl->server && !ssl->s3->v2_hello_done) { - int ret = read_v2_client_hello(ssl); - if (ret > 0) { - ssl->s3->v2_hello_done = true; + // Ask for the first 5 bytes, the size of the TLS record header. This is + // sufficient to detect a V2ClientHello and ensures that we never read + // beyond the first record. + if (in.size() < SSL3_RT_HEADER_LENGTH) { + *out_consumed = SSL3_RT_HEADER_LENGTH; + return ssl_open_record_partial; } - return ret; - } - SSL3_RECORD *rr = &ssl->s3->rrec; - // Get new packet if necessary. - if (rr->length == 0) { - int ret = ssl3_get_record(ssl); - if (ret <= 0) { + // Some dedicated error codes for protocol mixups should the application + // wish to interpret them differently. (These do not overlap with + // ClientHello or V2ClientHello.) + const char *str = reinterpret_cast<const char*>(in.data()); + if (strncmp("GET ", str, 4) == 0 || + strncmp("POST ", str, 5) == 0 || + strncmp("HEAD ", str, 5) == 0 || + strncmp("PUT ", str, 4) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_HTTP_REQUEST); + *out_alert = 0; + return ssl_open_record_error; + } + if (strncmp("CONNE", str, 5) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_HTTPS_PROXY_REQUEST); + *out_alert = 0; + return ssl_open_record_error; + } + + // Check for a V2ClientHello. + if ((in[0] & 0x80) != 0 && in[2] == SSL2_MT_CLIENT_HELLO && + in[3] == SSL3_VERSION_MAJOR) { + auto ret = read_v2_client_hello(ssl, out_consumed, in); + if (ret == ssl_open_record_error) { + *out_alert = 0; + } else if (ret == ssl_open_record_success) { + ssl->s3->v2_hello_done = true; + } return ret; } + + ssl->s3->v2_hello_done = true; + } + + uint8_t type; + Span<uint8_t> body; + auto ret = tls_open_record(ssl, &type, &body, out_consumed, out_alert, in); + if (ret != ssl_open_record_success) { + return ret; } // WatchGuard's TLS 1.3 interference bug is very distinctive: they drop the // ServerHello and send the remaining encrypted application data records // as-is. This manifests as an application data record when we expect // handshake. Report a dedicated error code for this case. - if (!ssl->server && rr->type == SSL3_RT_APPLICATION_DATA && + if (!ssl->server && type == SSL3_RT_APPLICATION_DATA && ssl->s3->aead_read_ctx->is_null_cipher()) { OPENSSL_PUT_ERROR(SSL, SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - return -1; + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } - if (rr->type != SSL3_RT_HANDSHAKE) { + if (type != SSL3_RT_HANDSHAKE) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - return -1; + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } // Append the entire handshake record to the buffer. - if (!BUF_MEM_append(ssl->init_buf, rr->data, rr->length)) { - return -1; + if (!BUF_MEM_append(ssl->init_buf, body.data(), body.size())) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return ssl_open_record_error; } - rr->length = 0; - ssl_read_buffer_discard(ssl); - return 1; + return ssl_open_record_success; } void ssl3_next_message(SSL *ssl) { diff --git a/src/ssl/s3_lib.cc b/src/ssl/s3_lib.cc index 3df8e1b3..f3f99fa4 100644 --- a/src/ssl/s3_lib.cc +++ b/src/ssl/s3_lib.cc @@ -164,25 +164,25 @@ namespace bssl { -int ssl3_new(SSL *ssl) { +bool ssl3_new(SSL *ssl) { UniquePtr<SSLAEADContext> aead_read_ctx = SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl)); UniquePtr<SSLAEADContext> aead_write_ctx = SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl)); if (!aead_read_ctx || !aead_write_ctx) { - return 0; + return false; } SSL3_STATE *s3 = (SSL3_STATE *)OPENSSL_malloc(sizeof *s3); if (s3 == NULL) { - return 0; + return false; } OPENSSL_memset(s3, 0, sizeof *s3); s3->hs = ssl_handshake_new(ssl); if (s3->hs == NULL) { OPENSSL_free(s3); - return 0; + return false; } s3->aead_read_ctx = aead_read_ctx.release(); @@ -195,7 +195,7 @@ int ssl3_new(SSL *ssl) { // protocol version, and implement this pre-negotiation quirk in |SSL_version| // at the API boundary rather than in internal state. ssl->version = TLS1_2_VERSION; - return 1; + return true; } void ssl3_free(SSL *ssl) { @@ -206,6 +206,7 @@ void ssl3_free(SSL *ssl) { ssl_read_buffer_clear(ssl); ssl_write_buffer_clear(ssl); + ERR_SAVE_STATE_free(ssl->s3->read_error); SSL_SESSION_free(ssl->s3->established_session); ssl_handshake_free(ssl->s3->hs); OPENSSL_free(ssl->s3->next_proto_negotiated); @@ -215,7 +216,6 @@ void ssl3_free(SSL *ssl) { Delete(ssl->s3->aead_write_ctx); BUF_MEM_free(ssl->s3->pending_flight); - OPENSSL_cleanse(ssl->s3, sizeof *ssl->s3); OPENSSL_free(ssl->s3); ssl->s3 = NULL; } diff --git a/src/ssl/s3_pkt.cc b/src/ssl/s3_pkt.cc index 71e1a089..7966e6f2 100644 --- a/src/ssl/s3_pkt.cc +++ b/src/ssl/s3_pkt.cc @@ -126,57 +126,6 @@ namespace bssl { static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len); -int ssl3_get_record(SSL *ssl) { - for (;;) { - Span<uint8_t> body; - uint8_t type, alert = SSL_AD_DECODE_ERROR; - size_t consumed; - enum ssl_open_record_t open_ret = tls_open_record( - ssl, &type, &body, &consumed, &alert, ssl_read_buffer(ssl)); - if (open_ret != ssl_open_record_partial) { - ssl_read_buffer_consume(ssl, consumed); - } - switch (open_ret) { - case ssl_open_record_partial: { - int read_ret = ssl_read_buffer_extend_to(ssl, consumed); - if (read_ret <= 0) { - return read_ret; - } - continue; - } - - case ssl_open_record_success: { - if (body.size() > 0xffff) { - OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); - return -1; - } - - SSL3_RECORD *rr = &ssl->s3->rrec; - rr->type = type; - rr->length = static_cast<uint16_t>(body.size()); - rr->data = body.data(); - return 1; - } - - case ssl_open_record_discard: - continue; - - case ssl_open_record_close_notify: - return 0; - - case ssl_open_record_error: - if (alert != 0) { - ssl_send_alert(ssl, SSL3_AL_FATAL, alert); - } - return -1; - } - - assert(0); - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return -1; - } -} - int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf, int len) { assert(ssl_can_write(ssl)); @@ -184,6 +133,11 @@ int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf, *out_needs_handshake = false; + if (ssl->s3->write_shutdown != ssl_shutdown_none) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + } + unsigned tot, n, nw; assert(ssl->s3->wnum <= INT_MAX); @@ -333,156 +287,107 @@ static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len) { return ssl3_write_pending(ssl, type, buf, len); } -static int consume_record(SSL *ssl, uint8_t *out, int len, int peek) { - SSL3_RECORD *rr = &ssl->s3->rrec; +ssl_open_record_t ssl3_open_app_data(SSL *ssl, Span<uint8_t> *out, + size_t *out_consumed, uint8_t *out_alert, + Span<uint8_t> in) { + assert(ssl_can_read(ssl)); + assert(!ssl->s3->aead_read_ctx->is_null_cipher()); - if (len <= 0) { - return len; + uint8_t type; + Span<uint8_t> body; + auto ret = tls_open_record(ssl, &type, &body, out_consumed, out_alert, in); + if (ret != ssl_open_record_success) { + return ret; } - if (len > (int)rr->length) { - len = (int)rr->length; - } + const bool is_early_data_read = ssl->server && SSL_in_early_data(ssl); - OPENSSL_memcpy(out, rr->data, len); - if (!peek) { - rr->length -= len; - rr->data += len; - if (rr->length == 0) { - // The record has been consumed, so we may now clear the buffer. - ssl_read_buffer_discard(ssl); + if (type == SSL3_RT_HANDSHAKE) { + // If reading 0-RTT data, reject handshake data. 0-RTT data is terminated + // by an alert. + if (is_early_data_read) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } - } - return len; -} - -int ssl3_read_app_data(SSL *ssl, bool *out_got_handshake, uint8_t *buf, int len, - int peek) { - assert(ssl_can_read(ssl)); - assert(!ssl->s3->aead_read_ctx->is_null_cipher()); - *out_got_handshake = false; - - SSL3_RECORD *rr = &ssl->s3->rrec; - for (;;) { - // A previous iteration may have read a partial handshake message. Do not - // allow more app data in that case. - int has_hs_data = ssl->init_buf != NULL && ssl->init_buf->length > 0; - - // Get new packet if necessary. - if (rr->length == 0 && !has_hs_data) { - int ret = ssl3_get_record(ssl); - if (ret <= 0) { - return ret; - } + // Post-handshake data prior to TLS 1.3 is always renegotiation, which we + // never accept as a server. Otherwise |ssl3_get_message| will send + // |SSL_R_EXCESSIVE_MESSAGE_SIZE|. + if (ssl->server && ssl_protocol_version(ssl) < TLS1_3_VERSION) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION); + *out_alert = SSL_AD_NO_RENEGOTIATION; + return ssl_open_record_error; } - if (has_hs_data || rr->type == SSL3_RT_HANDSHAKE) { - // If reading 0-RTT data, reject handshake data. 0-RTT data is terminated - // by an alert. - if (SSL_in_init(ssl)) { - OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - return -1; - } - - // Post-handshake data prior to TLS 1.3 is always renegotiation, which we - // never accept as a server. Otherwise |ssl3_get_message| will send - // |SSL_R_EXCESSIVE_MESSAGE_SIZE|. - if (ssl->server && ssl_protocol_version(ssl) < TLS1_3_VERSION) { - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION); - OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION); - return -1; - } - - // Parse post-handshake handshake messages. - int ret = ssl3_read_message(ssl); - if (ret <= 0) { - return ret; - } - *out_got_handshake = true; - return -1; + if (ssl->init_buf == NULL) { + ssl->init_buf = BUF_MEM_new(); } - - const int is_early_data_read = ssl->server && - ssl->s3->hs != NULL && - ssl->s3->hs->can_early_read && - ssl_protocol_version(ssl) >= TLS1_3_VERSION; - - // Handle the end_of_early_data alert. - if (rr->type == SSL3_RT_ALERT && - rr->length == 2 && - rr->data[0] == SSL3_AL_WARNING && - rr->data[1] == TLS1_AD_END_OF_EARLY_DATA && - is_early_data_read) { - // Consume the record. - rr->length = 0; - ssl_read_buffer_discard(ssl); - // Stop accepting early data. - ssl->s3->hs->can_early_read = false; - *out_got_handshake = true; - return -1; + if (ssl->init_buf == NULL || + !BUF_MEM_append(ssl->init_buf, body.data(), body.size())) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return ssl_open_record_error; } + return ssl_open_record_discard; + } - if (rr->type != SSL3_RT_APPLICATION_DATA) { - OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - return -1; - } + // Handle the end_of_early_data alert. + static const uint8_t kEndOfEarlyData[2] = {SSL3_AL_WARNING, + TLS1_AD_END_OF_EARLY_DATA}; + if (is_early_data_read && type == SSL3_RT_ALERT && body == kEndOfEarlyData) { + // Stop accepting early data. + ssl->s3->hs->can_early_read = false; + return ssl_open_record_discard; + } - if (is_early_data_read) { - if (rr->length > kMaxEarlyDataAccepted - ssl->s3->hs->early_data_read) { - OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MUCH_READ_EARLY_DATA); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL3_AD_UNEXPECTED_MESSAGE); - return -1; - } + if (type != SSL3_RT_APPLICATION_DATA) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; + } - ssl->s3->hs->early_data_read += rr->length; + if (is_early_data_read) { + if (body.size() > kMaxEarlyDataAccepted - ssl->s3->hs->early_data_read) { + OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MUCH_READ_EARLY_DATA); + *out_alert = SSL3_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } - if (rr->length != 0) { - return consume_record(ssl, buf, len, peek); - } + ssl->s3->hs->early_data_read += body.size(); + } - // Discard empty records and loop again. + if (body.empty()) { + return ssl_open_record_discard; } + + *out = body; + return ssl_open_record_success; } -int ssl3_read_change_cipher_spec(SSL *ssl) { - SSL3_RECORD *rr = &ssl->s3->rrec; - if (rr->length == 0) { - int ret = ssl3_get_record(ssl); - if (ret <= 0) { - return ret; - } +ssl_open_record_t ssl3_open_change_cipher_spec(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, + Span<uint8_t> in) { + uint8_t type; + Span<uint8_t> body; + auto ret = tls_open_record(ssl, &type, &body, out_consumed, out_alert, in); + if (ret != ssl_open_record_success) { + return ret; } - if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC || - tls_has_unprocessed_handshake_data(ssl)) { - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + if (type != SSL3_RT_CHANGE_CIPHER_SPEC) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); - return -1; + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } - if (rr->length != 1 || rr->data[0] != SSL3_MT_CCS) { + if (body.size() != 1 || body[0] != SSL3_MT_CCS) { OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); - return -1; + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return ssl_open_record_error; } - ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC, - MakeSpan(rr->data, rr->length)); - - rr->length = 0; - ssl_read_buffer_discard(ssl); - return 1; -} - -void ssl3_read_close_notify(SSL *ssl) { - // Read records until an error or close_notify. - while (ssl3_get_record(ssl) > 0) { - ; - } + ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC, body); + return ssl_open_record_success; } int ssl_send_alert(SSL *ssl, int level, int desc) { @@ -497,7 +402,7 @@ int ssl_send_alert(SSL *ssl, int level, int desc) { } else { assert(level == SSL3_AL_FATAL); assert(desc != SSL_AD_CLOSE_NOTIFY); - ssl->s3->write_shutdown = ssl_shutdown_fatal_alert; + ssl->s3->write_shutdown = ssl_shutdown_error; } ssl->s3->alert_dispatch = 1; diff --git a/src/ssl/ssl_buffer.cc b/src/ssl/ssl_buffer.cc index 79f0caeb..412df736 100644 --- a/src/ssl/ssl_buffer.cc +++ b/src/ssl/ssl_buffer.cc @@ -202,6 +202,46 @@ void ssl_read_buffer_clear(SSL *ssl) { clear_buffer(&ssl->s3->read_buffer); } +int ssl_handle_open_record(SSL *ssl, bool *out_retry, ssl_open_record_t ret, + size_t consumed, uint8_t alert) { + *out_retry = false; + if (ret != ssl_open_record_partial) { + ssl_read_buffer_consume(ssl, consumed); + } + if (ret != ssl_open_record_success) { + // Nothing was returned to the caller, so discard anything marked consumed. + ssl_read_buffer_discard(ssl); + } + switch (ret) { + case ssl_open_record_success: + return 1; + + case ssl_open_record_partial: { + int read_ret = ssl_read_buffer_extend_to(ssl, consumed); + if (read_ret <= 0) { + return read_ret; + } + *out_retry = true; + return 1; + } + + case ssl_open_record_discard: + *out_retry = true; + return 1; + + case ssl_open_record_close_notify: + return 0; + + case ssl_open_record_error: + if (alert != 0) { + ssl_send_alert(ssl, SSL3_AL_FATAL, alert); + } + return -1; + } + assert(0); + return -1; +} + int ssl_write_buffer_is_pending(const SSL *ssl) { return ssl->s3->write_buffer.len > 0; diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc index c975337d..e3f8a88f 100644 --- a/src/ssl/ssl_lib.cc +++ b/src/ssl/ssl_lib.cc @@ -206,6 +206,20 @@ void ssl_reset_error_state(SSL *ssl) { ERR_clear_system_error(); } +void ssl_set_read_error(SSL* ssl) { + ssl->s3->read_shutdown = ssl_shutdown_error; + ERR_SAVE_STATE_free(ssl->s3->read_error); + ssl->s3->read_error = ERR_save_state(); +} + +static bool check_read_error(const SSL *ssl) { + if (ssl->s3->read_shutdown == ssl_shutdown_error) { + ERR_restore_state(ssl->s3->read_error); + return false; + } + return true; +} + int ssl_can_write(const SSL *ssl) { return !SSL_in_init(ssl) || ssl->s3->hs->can_early_write; } @@ -214,6 +228,51 @@ int ssl_can_read(const SSL *ssl) { return !SSL_in_init(ssl) || ssl->s3->hs->can_early_read; } +ssl_open_record_t ssl_open_handshake(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, Span<uint8_t> in) { + *out_consumed = 0; + if (!check_read_error(ssl)) { + *out_alert = 0; + return ssl_open_record_error; + } + auto ret = ssl->method->open_handshake(ssl, out_consumed, out_alert, in); + if (ret == ssl_open_record_error) { + ssl_set_read_error(ssl); + } + return ret; +} + +ssl_open_record_t ssl_open_change_cipher_spec(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, + Span<uint8_t> in) { + *out_consumed = 0; + if (!check_read_error(ssl)) { + *out_alert = 0; + return ssl_open_record_error; + } + auto ret = + ssl->method->open_change_cipher_spec(ssl, out_consumed, out_alert, in); + if (ret == ssl_open_record_error) { + ssl_set_read_error(ssl); + } + return ret; +} + +ssl_open_record_t ssl_open_app_data(SSL *ssl, Span<uint8_t> *out, + size_t *out_consumed, uint8_t *out_alert, + Span<uint8_t> in) { + *out_consumed = 0; + if (!check_read_error(ssl)) { + *out_alert = 0; + return ssl_open_record_error; + } + auto ret = ssl->method->open_app_data(ssl, out, out_consumed, out_alert, in); + if (ret == ssl_open_record_error) { + ssl_set_read_error(ssl); + } + return ret; +} + void ssl_cipher_preference_list_free( struct ssl_cipher_preference_list_st *cipher_list) { if (cipher_list == NULL) { @@ -878,7 +937,8 @@ static int ssl_do_post_handshake(SSL *ssl, const SSLMessage &msg) { // protocol, namely in HTTPS, just before reading the HTTP response. Require // the record-layer be idle and avoid complexities of sending a handshake // record while an application_data record is being written. - if (ssl_write_buffer_is_pending(ssl)) { + if (ssl_write_buffer_is_pending(ssl) || + ssl->s3->write_shutdown != ssl_shutdown_none) { goto no_renegotiation; } @@ -896,12 +956,12 @@ static int ssl_do_post_handshake(SSL *ssl, const SSLMessage &msg) { return 1; no_renegotiation: - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION); OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION); + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION); return 0; } -static int ssl_read_impl(SSL *ssl, void *buf, int num, int peek) { +static int ssl_read_impl(SSL *ssl) { ssl_reset_error_state(ssl); if (ssl->do_handshake == NULL) { @@ -909,7 +969,12 @@ static int ssl_read_impl(SSL *ssl, void *buf, int num, int peek) { return -1; } - for (;;) { + // Replay post-handshake message errors. + if (!check_read_error(ssl)) { + return -1; + } + + while (ssl->s3->pending_app_data.empty()) { // Complete the current handshake, if any. False Start will cause // |SSL_do_handshake| to return mid-handshake, so this may require multiple // iterations. @@ -929,31 +994,58 @@ static int ssl_read_impl(SSL *ssl, void *buf, int num, int peek) { if (ssl->method->get_message(ssl, &msg)) { // Handle the post-handshake message and try again. if (!ssl_do_post_handshake(ssl, msg)) { + ssl_set_read_error(ssl); return -1; } ssl->method->next_message(ssl); continue; // Loop again. We may have begun a new handshake. } - bool got_handshake = false; - int ret = ssl->method->read_app_data(ssl, &got_handshake, (uint8_t *)buf, - num, peek); - if (got_handshake) { - continue; // Loop again to process the handshake data. + uint8_t alert = SSL_AD_DECODE_ERROR; + size_t consumed = 0; + auto ret = ssl_open_app_data(ssl, &ssl->s3->pending_app_data, &consumed, + &alert, ssl_read_buffer(ssl)); + bool retry; + int bio_ret = ssl_handle_open_record(ssl, &retry, ret, consumed, alert); + if (bio_ret <= 0) { + return bio_ret; } - if (ret > 0) { + if (!retry) { + assert(!ssl->s3->pending_app_data.empty()); ssl->s3->key_update_count = 0; } - return ret; } + + return 1; } int SSL_read(SSL *ssl, void *buf, int num) { - return ssl_read_impl(ssl, buf, num, 0 /* consume bytes */); + int ret = SSL_peek(ssl, buf, num); + if (ret <= 0) { + return ret; + } + // TODO(davidben): In DTLS, should the rest of the record be discarded? DTLS + // is not a stream. See https://crbug.com/boringssl/65. + ssl->s3->pending_app_data = + ssl->s3->pending_app_data.subspan(static_cast<size_t>(ret)); + if (ssl->s3->pending_app_data.empty()) { + ssl_read_buffer_discard(ssl); + } + return ret; } int SSL_peek(SSL *ssl, void *buf, int num) { - return ssl_read_impl(ssl, buf, num, 1 /* peek */); + int ret = ssl_read_impl(ssl); + if (ret <= 0) { + return ret; + } + if (num <= 0) { + return num; + } + size_t todo = + std::min(ssl->s3->pending_app_data.size(), static_cast<size_t>(num)); + OPENSSL_memcpy(buf, ssl->s3->pending_app_data.data(), todo); + return static_cast<int>(todo); } int SSL_write(SSL *ssl, const void *buf, int num) { @@ -1027,10 +1119,28 @@ int SSL_shutdown(SSL *ssl) { return -1; } } else if (ssl->s3->read_shutdown != ssl_shutdown_close_notify) { - // Wait for the peer's close_notify. - ssl->method->read_close_notify(ssl); - if (ssl->s3->read_shutdown != ssl_shutdown_close_notify) { - return -1; + if (SSL_is_dtls(ssl)) { + // Bidirectional shutdown doesn't make sense for an unordered + // transport. DTLS alerts also aren't delivered reliably, so we may even + // time out because the peer never received our close_notify. Report to + // the caller that the channel has fully shut down. + if (ssl->s3->read_shutdown == ssl_shutdown_error) { + ERR_restore_state(ssl->s3->read_error); + return -1; + } + ssl->s3->read_shutdown = ssl_shutdown_close_notify; + } else { + // Keep discarding data until we see a close_notify. + for (;;) { + ssl->s3->pending_app_data = Span<uint8_t>(); + int ret = ssl_read_impl(ssl); + if (ret <= 0) { + break; + } + } + if (ssl->s3->read_shutdown != ssl_shutdown_close_notify) { + return -1; + } } } @@ -1461,10 +1571,7 @@ void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) { } void SSL_set_read_ahead(SSL *ssl, int yes) { } int SSL_pending(const SSL *ssl) { - if (ssl->s3->rrec.type != SSL3_RT_APPLICATION_DATA) { - return 0; - } - return ssl->s3->rrec.length; + return static_cast<int>(ssl->s3->pending_app_data.size()); } // Fix this so it checks all the valid key/cert options diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc index 4da109d1..5e9cde14 100644 --- a/src/ssl/ssl_test.cc +++ b/src/ssl/ssl_test.cc @@ -3760,47 +3760,6 @@ TEST_P(SSLVersionTest, SessionVersion) { EXPECT_EQ(version(), SSL_SESSION_get_protocol_version(session.get())); } -// Test that a handshake-level errors are sticky. -TEST_P(SSLVersionTest, StickyErrorHandshake_ParseClientHello) { - UniquePtr<SSL_CTX> ctx = CreateContext(); - ASSERT_TRUE(ctx); - UniquePtr<SSL> ssl(SSL_new(ctx.get())); - ASSERT_TRUE(ssl); - SSL_set_accept_state(ssl.get()); - - // Pass in an empty ClientHello. - if (is_dtls()) { - static const uint8_t kBadClientHello[] = { - 0x16, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - SSL_set0_rbio(ssl.get(), - BIO_new_mem_buf(kBadClientHello, sizeof(kBadClientHello))); - } else { - static const uint8_t kBadClientHello[] = {0x16, 0x03, 0x01, 0x00, 0x04, - 0x01, 0x00, 0x00, 0x00}; - SSL_set0_rbio(ssl.get(), - BIO_new_mem_buf(kBadClientHello, sizeof(kBadClientHello))); - } - - SSL_set0_wbio(ssl.get(), BIO_new(BIO_s_mem())); - - // The handshake logic should reject the ClientHello. - int ret = SSL_do_handshake(ssl.get()); - EXPECT_NE(1, ret); - EXPECT_EQ(SSL_ERROR_SSL, SSL_get_error(ssl.get(), ret)); - EXPECT_EQ(ERR_LIB_SSL, ERR_GET_LIB(ERR_peek_error())); - EXPECT_EQ(SSL_R_DECODE_ERROR, ERR_GET_REASON(ERR_peek_error())); - ERR_clear_error(); - - // If we drive the handshake again, the error is replayed. - ret = SSL_do_handshake(ssl.get()); - EXPECT_NE(1, ret); - EXPECT_EQ(SSL_ERROR_SSL, SSL_get_error(ssl.get(), ret)); - EXPECT_EQ(ERR_LIB_SSL, ERR_GET_LIB(ERR_peek_error())); - EXPECT_EQ(SSL_R_DECODE_ERROR, ERR_GET_REASON(ERR_peek_error())); -} - TEST_P(SSLVersionTest, SSLPending) { UniquePtr<SSL> ssl(SSL_new(client_ctx_.get())); ASSERT_TRUE(ssl); @@ -3827,6 +3786,39 @@ TEST_P(SSLVersionTest, SSLPending) { EXPECT_EQ(3, SSL_pending(client_.get())); } +// Test that post-handshake tickets consumed by |SSL_shutdown| are ignored. +TEST(SSLTest, ShutdownIgnoresTickets) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + ASSERT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), TLS1_3_VERSION)); + ASSERT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_VERSION)); + + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + ASSERT_TRUE(cert); + ASSERT_TRUE(key); + ASSERT_TRUE(SSL_CTX_use_certificate(ctx.get(), cert.get())); + ASSERT_TRUE(SSL_CTX_use_PrivateKey(ctx.get(), key.get())); + + SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_BOTH); + + bssl::UniquePtr<SSL> client, server; + ASSERT_TRUE(ConnectClientAndServer(&client, &server, ctx.get(), ctx.get())); + + SSL_CTX_sess_set_new_cb(ctx.get(), [](SSL *ssl, SSL_SESSION *session) -> int { + ADD_FAILURE() << "New session callback called during SSL_shutdown"; + return 0; + }); + + // Send close_notify. + EXPECT_EQ(0, SSL_shutdown(server.get())); + EXPECT_EQ(0, SSL_shutdown(client.get())); + + // Receive close_notify. + EXPECT_EQ(1, SSL_shutdown(server.get())); + EXPECT_EQ(1, SSL_shutdown(client.get())); +} + // TODO(davidben): Convert this file to GTest properly. TEST(SSLTest, AllTests) { if (!TestSSL_SESSIONEncoding(kOpenSSLSession) || diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc index 5f839105..1d7b996a 100644 --- a/src/ssl/test/bssl_shim.cc +++ b/src/ssl/test/bssl_shim.cc @@ -56,6 +56,7 @@ OPENSSL_MSVC_PRAGMA(comment(lib, "Ws2_32.lib")) #include <openssl/ssl.h> #include <openssl/x509.h> +#include <functional> #include <memory> #include <string> #include <vector> @@ -1408,6 +1409,32 @@ static bool RetryAsync(SSL *ssl, int ret) { } } +// CheckIdempotentError runs |func|, an operation on |ssl|, ensuring that +// errors are idempotent. +static int CheckIdempotentError(const char *name, SSL *ssl, + std::function<int()> func) { + int ret = func(); + int ssl_err = SSL_get_error(ssl, ret); + uint32_t err = ERR_peek_error(); + if (ssl_err == SSL_ERROR_SSL || ssl_err == SSL_ERROR_ZERO_RETURN) { + int ret2 = func(); + int ssl_err2 = SSL_get_error(ssl, ret2); + uint32_t err2 = ERR_peek_error(); + if (ret != ret2 || ssl_err != ssl_err2 || err != err2) { + fprintf(stderr, "Repeating %s did not replay the error.\n", name); + char buf[256]; + ERR_error_string_n(err, buf, sizeof(buf)); + fprintf(stderr, "Wanted: %d %d %s\n", ret, ssl_err, buf); + ERR_error_string_n(err2, buf, sizeof(buf)); + fprintf(stderr, "Got: %d %d %s\n", ret2, ssl_err2, buf); + // runner treats exit code 90 as always failing. Otherwise, it may + // accidentally consider the result an expected protocol failure. + exit(90); + } + } + return ret; +} + // DoRead reads from |ssl|, resolving any asynchronous operations. It returns // the result value of the final |SSL_read| call. static int DoRead(SSL *ssl, uint8_t *out, size_t max_out) { @@ -1421,8 +1448,10 @@ static int DoRead(SSL *ssl, uint8_t *out, size_t max_out) { // trigger a retransmit, so disconnect the write quota. AsyncBioEnforceWriteQuota(test_state->async_bio, false); } - ret = config->peek_then_read ? SSL_peek(ssl, out, max_out) - : SSL_read(ssl, out, max_out); + ret = CheckIdempotentError("SSL_peek/SSL_read", ssl, [&]() -> int { + return config->peek_then_read ? SSL_peek(ssl, out, max_out) + : SSL_read(ssl, out, max_out); + }); if (config->async) { AsyncBioEnforceWriteQuota(test_state->async_bio, true); } @@ -2163,7 +2192,9 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, SSL *ssl, int ret; if (!config->implicit_handshake) { do { - ret = SSL_do_handshake(ssl); + ret = CheckIdempotentError("SSL_do_handshake", ssl, [&]() -> int { + return SSL_do_handshake(ssl); + }); } while (config->async && RetryAsync(ssl, ret)); if (ret != 1 || !CheckHandshakeProperties(ssl, is_resume, config)) { diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go index a57362d5..e666404f 100644 --- a/src/ssl/test/runner/runner.go +++ b/src/ssl/test/runner/runner.go @@ -1173,13 +1173,15 @@ func runTest(test *testCase, shimPath string, mallocNumToFail int64) error { shimKilledLock.Unlock() } - var isValgrindError bool + var isValgrindError, mustFail bool if exitError, ok := childErr.(*exec.ExitError); ok { switch exitError.Sys().(syscall.WaitStatus).ExitStatus() { case 88: return errMoreMallocs case 89: return errUnimplemented + case 90: + mustFail = true case 99: isValgrindError = true } @@ -1209,7 +1211,7 @@ func runTest(test *testCase, shimPath string, mallocNumToFail int64) error { correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError) } - if failed != test.shouldFail || failed && !correctFailure { + if failed != test.shouldFail || failed && !correctFailure || mustFail { childError := "none" if childErr != nil { childError = childErr.Error() @@ -1223,6 +1225,8 @@ func runTest(test *testCase, shimPath string, mallocNumToFail int64) error { msg = "unexpected success" case failed && !correctFailure: msg = "bad error (wanted '" + expectedError + "' / '" + test.expectedLocalError + "')" + case mustFail: + msg = "test failure" default: panic("internal error") } @@ -4894,6 +4898,92 @@ read alert 1 0 sendWarningAlerts: 1, flags: []string{"-check-close-notify"}, }) + + // Test that SSL_shutdown still processes KeyUpdate. + tests = append(tests, testCase{ + name: "Shutdown-Shim-KeyUpdate", + config: Config{ + MinVersion: VersionTLS13, + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + ExpectCloseNotify: true, + }, + }, + shimShutsDown: true, + sendKeyUpdates: 1, + keyUpdateRequest: keyUpdateRequested, + flags: []string{"-check-close-notify"}, + }) + + // Test that SSL_shutdown processes HelloRequest + // correctly. + tests = append(tests, testCase{ + name: "Shutdown-Shim-HelloRequest-Ignore", + config: Config{ + MinVersion: VersionTLS12, + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendHelloRequestBeforeEveryAppDataRecord: true, + ExpectCloseNotify: true, + }, + }, + shimShutsDown: true, + flags: []string{ + "-renegotiate-ignore", + "-check-close-notify", + }, + }) + tests = append(tests, testCase{ + name: "Shutdown-Shim-HelloRequest-Reject", + config: Config{ + MinVersion: VersionTLS12, + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendHelloRequestBeforeEveryAppDataRecord: true, + ExpectCloseNotify: true, + }, + }, + shimShutsDown: true, + shouldFail: true, + expectedError: ":NO_RENEGOTIATION:", + flags: []string{"-check-close-notify"}, + }) + tests = append(tests, testCase{ + name: "Shutdown-Shim-HelloRequest-CannotHandshake", + config: Config{ + MinVersion: VersionTLS12, + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendHelloRequestBeforeEveryAppDataRecord: true, + ExpectCloseNotify: true, + }, + }, + shimShutsDown: true, + shouldFail: true, + expectedError: ":NO_RENEGOTIATION:", + flags: []string{ + "-check-close-notify", + "-renegotiate-freely", + }, + }) + + tests = append(tests, testCase{ + testType: serverTest, + name: "Shutdown-Shim-Renegotiate-Server-Forbidden", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + ExpectCloseNotify: true, + }, + }, + shimShutsDown: true, + renegotiate: 1, + shouldFail: true, + expectedError: ":NO_RENEGOTIATION:", + flags: []string{ + "-check-close-notify", + }, + }) } } else { // TODO(davidben): DTLS 1.3 will want a similar thing for diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc index e75d976f..a03c5815 100644 --- a/src/ssl/tls13_client.cc +++ b/src/ssl/tls13_client.cc @@ -774,6 +774,13 @@ const char *tls13_client_handshake_state(SSL_HANDSHAKE *hs) { } int tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg) { + if (ssl->s3->write_shutdown != ssl_shutdown_none) { + // Ignore tickets on shutdown. Callers tend to indiscriminately call + // |SSL_shutdown| before destroying an |SSL|, at which point calling the new + // session callback may be confusing. + return 1; + } + UniquePtr<SSL_SESSION> session(SSL_SESSION_dup(ssl->s3->established_session, SSL_SESSION_INCLUDE_NONAUTH)); if (!session) { diff --git a/src/ssl/tls_method.cc b/src/ssl/tls_method.cc index b4d08a07..94b9e205 100644 --- a/src/ssl/tls_method.cc +++ b/src/ssl/tls_method.cc @@ -67,7 +67,7 @@ namespace bssl { -static int ssl3_supports_cipher(const SSL_CIPHER *cipher) { return 1; } +static bool ssl3_supports_cipher(const SSL_CIPHER *cipher) { return true; } static void ssl3_on_handshake_complete(SSL *ssl) { // The handshake should have released its final message. @@ -84,42 +84,38 @@ static void ssl3_on_handshake_complete(SSL *ssl) { } } -static int ssl3_set_read_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) { +static bool ssl3_set_read_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) { // Cipher changes are forbidden if the current epoch has leftover data. - // - // TODO(davidben): ssl->s3->rrec.length should be impossible now. Remove it - // once it is only used for application data. - if (ssl->s3->rrec.length != 0 || tls_has_unprocessed_handshake_data(ssl)) { + if (tls_has_unprocessed_handshake_data(ssl)) { OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFERED_MESSAGES_ON_CIPHER_CHANGE); ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - return 0; + return false; } OPENSSL_memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence)); Delete(ssl->s3->aead_read_ctx); ssl->s3->aead_read_ctx = aead_ctx.release(); - return 1; + return true; } -static int ssl3_set_write_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) { +static bool ssl3_set_write_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) { OPENSSL_memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence)); Delete(ssl->s3->aead_write_ctx); ssl->s3->aead_write_ctx = aead_ctx.release(); - return 1; + return true; } static const SSL_PROTOCOL_METHOD kTLSProtocolMethod = { - 0 /* is_dtls */, + false /* is_dtls */, ssl3_new, ssl3_free, ssl3_get_message, - ssl3_read_message, ssl3_next_message, - ssl3_read_app_data, - ssl3_read_change_cipher_spec, - ssl3_read_close_notify, + ssl3_open_handshake, + ssl3_open_change_cipher_spec, + ssl3_open_app_data, ssl3_write_app_data, ssl3_dispatch_alert, ssl3_supports_cipher, diff --git a/src/ssl/tls_record.cc b/src/ssl/tls_record.cc index 44a04d91..bc641fa7 100644 --- a/src/ssl/tls_record.cc +++ b/src/ssl/tls_record.cc @@ -191,15 +191,14 @@ enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type, Span<uint8_t> *out, size_t *out_consumed, uint8_t *out_alert, Span<uint8_t> in) { *out_consumed = 0; - switch (ssl->s3->read_shutdown) { - case ssl_shutdown_none: - break; - case ssl_shutdown_fatal_alert: - OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); - *out_alert = 0; - return ssl_open_record_error; - case ssl_shutdown_close_notify: - return ssl_open_record_close_notify; + if (ssl->s3->read_shutdown == ssl_shutdown_close_notify) { + return ssl_open_record_close_notify; + } + + // If there is an unprocessed handshake message or we are already buffering + // too much, stop before decrypting another handshake record. + if (!tls_can_accept_handshake_data(ssl, out_alert)) { + return ssl_open_record_error; } CBS cbs = CBS(in); @@ -331,6 +330,14 @@ enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type, return ssl_process_alert(ssl, out_alert, *out); } + // Handshake messages may not interleave with any other record type. + if (type != SSL3_RT_HANDSHAKE && + tls_has_unprocessed_handshake_data(ssl)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; + } + ssl->s3->warning_alert_count = 0; *out_type = type; @@ -449,8 +456,8 @@ static bool tls_seal_scatter_suffix_len(const SSL *ssl, size_t *out_suffix_len, // |tls_seal_scatter_record| implements TLS 1.0 CBC 1/n-1 record splitting and // may write two records concatenated. static int tls_seal_scatter_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out, - uint8_t *out_suffix, uint8_t type, - const uint8_t *in, size_t in_len) { + uint8_t *out_suffix, uint8_t type, + const uint8_t *in, size_t in_len) { if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 && ssl_needs_record_splitting(ssl)) { assert(ssl->s3->aead_write_ctx->ExplicitNonceLen() == 0); @@ -567,12 +574,8 @@ enum ssl_open_record_t ssl_process_alert(SSL *ssl, uint8_t *out_alert, } if (alert_level == SSL3_AL_FATAL) { - ssl->s3->read_shutdown = ssl_shutdown_fatal_alert; - - char tmp[16]; OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr); - BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr); - ERR_add_error_data(2, "SSL alert number ", tmp); + ERR_add_error_dataf("SSL alert number %d", alert_descr); *out_alert = 0; // No alert to send back to the peer. return ssl_open_record_error; } |