summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Sloan <varomodt@google.com>2017-10-23 10:28:39 -0700
committerRobert Sloan <varomodt@google.com>2017-10-23 10:28:45 -0700
commit36272964d1ab48276cf98c4cfad1130e5483e692 (patch)
treeb94f031d7e41beb57b3cfe7949357e08598c6a96 /src
parent921ef2c09591ea1f04625e01b800ac4493a8916d (diff)
downloadboringssl-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.cc163
-rw-r--r--src/ssl/d1_lib.cc22
-rw-r--r--src/ssl/d1_pkt.cc135
-rw-r--r--src/ssl/dtls_method.cc22
-rw-r--r--src/ssl/dtls_record.cc11
-rw-r--r--src/ssl/handshake.cc48
-rw-r--r--src/ssl/internal.h200
-rw-r--r--src/ssl/s3_both.cc227
-rw-r--r--src/ssl/s3_lib.cc12
-rw-r--r--src/ssl/s3_pkt.cc255
-rw-r--r--src/ssl/ssl_buffer.cc40
-rw-r--r--src/ssl/ssl_lib.cc149
-rw-r--r--src/ssl/ssl_test.cc74
-rw-r--r--src/ssl/test/bssl_shim.cc37
-rw-r--r--src/ssl/test/runner/runner.go94
-rw-r--r--src/ssl/tls13_client.cc7
-rw-r--r--src/ssl/tls_method.cc26
-rw-r--r--src/ssl/tls_record.cc35
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;
}