Lift BIO above SSL_PROTOCOL_METHOD.
This gets us closer to exposing BIO-free APIs. The next step is probably
to make the experimental bssl::OpenRecord function call a split out core
of ssl_read_impl.
Change-Id: I4acebb43f708df8c52eb4e328da8ae3551362fb9
Reviewed-on: https://boringssl-review.googlesource.com/21865
Commit-Queue: Steven Valdez <svaldez@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/ssl/d1_both.cc b/ssl/d1_both.cc
index daf62fc..5e5fc4b 100644
--- a/ssl/d1_both.cc
+++ b/ssl/d1_both.cc
@@ -272,9 +272,10 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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) {
@@ -496,17 +490,21 @@
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;
}
diff --git a/ssl/d1_pkt.cc b/ssl/d1_pkt.cc
index 5b1cce5..7e329e3 100644
--- a/ssl/d1_pkt.cc
+++ b/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,52 @@
// 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;
+ *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
+ return ssl_open_record_error;
}
- // Discard empty records.
- if (rr->length == 0) {
- goto again;
+ if (record.empty()) {
+ return ssl_open_record_discard;
}
- if (len <= 0) {
- return len;
- }
-
- if ((unsigned)len > rr->length) {
- len = rr->length;
- }
-
- 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);
- }
- }
-
- return len;
+ *out = record;
+ return ssl_open_record_success;
}
-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;
+ssl_open_record_t dtls1_open_close_notify(SSL *ssl, size_t *out_consumed,
+ uint8_t *out_alert,
+ bssl::Span<uint8_t> in) {
+ switch (ssl->s3->read_shutdown) {
+ // 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.
+ case ssl_shutdown_none:
+ case ssl_shutdown_close_notify:
+ ssl->s3->read_shutdown = ssl_shutdown_close_notify;
+ return ssl_open_record_close_notify;
+ case ssl_shutdown_error:
+ ERR_restore_state(ssl->s3->read_error);
+ *out_alert = 0;
+ return ssl_open_record_error;
}
+ assert(0);
+ return ssl_open_record_error;
}
int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake,
diff --git a/ssl/dtls_method.cc b/ssl/dtls_method.cc
index a6156a1..ac06842 100644
--- a/ssl/dtls_method.cc
+++ b/ssl/dtls_method.cc
@@ -117,11 +117,11 @@
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_open_close_notify,
dtls1_write_app_data,
dtls1_dispatch_alert,
dtls1_supports_cipher,
diff --git a/ssl/handshake.cc b/ssl/handshake.cc
index 1e19e5c..a318ec3 100644
--- a/ssl/handshake.cc
+++ b/ssl/handshake.cc
@@ -498,12 +498,22 @@
}
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->method->open_change_cipher_spec(ssl, &consumed, &alert,
+ ssl_read_buffer(ssl));
+ } else {
+ ret = ssl->method->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 @@
// 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/ssl/internal.h b/ssl/internal.h
index 60e69f9..6bbde76 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1062,6 +1062,12 @@
// 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);
@@ -1728,20 +1734,31 @@
// 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);
+ // open_handshake processes a record from |in| for reading a handshake
+ // message.
+ ssl_open_record_t (*open_handshake)(SSL *ssl, size_t *out_consumed,
+ uint8_t *out_alert, Span<uint8_t> in);
+ // open_change_cipher_spec processes a record from |in| for reading a
+ // ChangeCipherSpec. If an out-of-order record was received in DTLS, it
+ // succeeds without consuming input.
+ ssl_open_record_t (*open_change_cipher_spec)(SSL *ssl, size_t *out_consumed,
+ uint8_t *out_alert,
+ Span<uint8_t> in);
+ // 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 (*open_app_data)(SSL *ssl, Span<uint8_t> *out,
+ size_t *out_consumed, uint8_t *out_alert,
+ Span<uint8_t> in);
+ // open_close_notify processes a record from |in| for reading close_notify.
+ // It discards all records and returns |ssl_open_record_close_notify| when it
+ // receives one.
+ ssl_open_record_t (*open_close_notify)(SSL *ssl, 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);
@@ -2107,15 +2124,6 @@
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;
@@ -2147,7 +2155,9 @@
// 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
@@ -2690,18 +2700,19 @@
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);
+ssl_open_record_t ssl3_open_close_notify(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);
@@ -2730,15 +2741,14 @@
// 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);
+ssl_open_record_t dtls1_open_close_notify(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);
@@ -2762,7 +2772,8 @@
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);
diff --git a/ssl/s3_both.cc b/ssl/s3_both.cc
index 7fc843e..b513a42 100644
--- a/ssl/s3_both.cc
+++ b/ssl/s3_both.cc
@@ -274,55 +274,28 @@
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 +303,7 @@
// 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 +322,7 @@
!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 +358,7 @@
!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 +366,7 @@
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 +375,7 @@
}
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 +384,12 @@
!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,
@@ -497,58 +467,92 @@
return ssl->init_buf != NULL && ssl->init_buf->length > msg_len;
}
-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) {
+ *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/ssl/s3_pkt.cc b/ssl/s3_pkt.cc
index 2718fde..509f8d3 100644
--- a/ssl/s3_pkt.cc
+++ b/ssl/s3_pkt.cc
@@ -126,57 +126,6 @@
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));
@@ -333,152 +282,120 @@
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;
-
- if (len <= 0) {
- return len;
- }
-
- if (len > (int)rr->length) {
- len = (int)rr->length;
- }
-
- 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);
- }
- }
- return len;
-}
-
-int ssl3_read_app_data(SSL *ssl, bool *out_got_handshake, uint8_t *buf, int len,
- int peek) {
+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());
- *out_got_handshake = false;
- SSL3_RECORD *rr = &ssl->s3->rrec;
+ 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;
+ }
- for (;;) {
- // Get new packet if necessary.
- if (rr->length == 0) {
- int ret = ssl3_get_record(ssl);
- if (ret <= 0) {
- return ret;
- }
- }
+ const bool is_early_data_read = ssl->server && SSL_in_early_data(ssl);
- const bool is_early_data_read = ssl->server && SSL_in_early_data(ssl);
-
- if (rr->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);
- 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;
- }
-
- if (ssl->init_buf == NULL) {
- ssl->init_buf = BUF_MEM_new();
- }
- if (ssl->init_buf == NULL ||
- !BUF_MEM_append(ssl->init_buf, rr->data, rr->length)) {
- return -1;
- }
- *out_got_handshake = true;
- rr->length = 0;
- ssl_read_buffer_discard(ssl);
- return -1;
- }
-
- // 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 (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;
- }
-
+ 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) {
- 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;
- }
-
- ssl->s3->hs->early_data_read += rr->length;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
+ *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
+ return ssl_open_record_error;
}
- if (rr->length != 0) {
- return consume_record(ssl, buf, len, peek);
+ // 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;
}
- // Discard empty records and loop again.
- }
-}
-
-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;
+ if (ssl->init_buf == NULL) {
+ ssl->init_buf = BUF_MEM_new();
}
+ 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_CHANGE_CIPHER_SPEC) {
- ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+ // 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 (type != SSL3_RT_APPLICATION_DATA) {
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) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
- ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- return -1;
+ 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;
+ }
+
+ ssl->s3->hs->early_data_read += body.size();
}
- ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC,
- MakeSpan(rr->data, rr->length));
+ if (body.empty()) {
+ return ssl_open_record_discard;
+ }
- rr->length = 0;
- ssl_read_buffer_discard(ssl);
- return 1;
+ *out = body;
+ return ssl_open_record_success;
}
-void ssl3_read_close_notify(SSL *ssl) {
- // Read records until an error or close_notify.
- while (ssl3_get_record(ssl) > 0) {
- ;
+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 (type != SSL3_RT_CHANGE_CIPHER_SPEC) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
+ *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
+ return ssl_open_record_error;
+ }
+
+ if (body.size() != 1 || body[0] != SSL3_MT_CCS) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+ return ssl_open_record_error;
+ }
+
+ ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC, body);
+ return ssl_open_record_success;
+}
+
+ssl_open_record_t ssl3_open_close_notify(SSL *ssl, size_t *out_consumed,
+ uint8_t *out_alert, Span<uint8_t> in) {
+ // TODO(davidben): Replace this with open_app_data so we actually process
+ // various bad behaviors.
+ 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 ssl_open_record_discard;
+ }
+ return ret;
}
int ssl_send_alert(SSL *ssl, int level, int desc) {
diff --git a/ssl/ssl_buffer.cc b/ssl/ssl_buffer.cc
index 79f0cae..412df73 100644
--- a/ssl/ssl_buffer.cc
+++ b/ssl/ssl_buffer.cc
@@ -202,6 +202,46 @@
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/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index 2f5374b..8e06c49 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -907,7 +907,7 @@
return 0;
}
-static int ssl_read_impl(SSL *ssl, void *buf, int num, int peek) {
+static int ssl_read_impl(SSL *ssl, void *buf, int num, bool peek) {
ssl_reset_error_state(ssl);
if (ssl->do_handshake == NULL) {
@@ -941,25 +941,48 @@
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.
- }
- if (ret > 0) {
+ if (ssl->s3->pending_app_data.empty()) {
+ uint8_t alert = SSL_AD_DECODE_ERROR;
+ size_t consumed = 0;
+ auto ret =
+ ssl->method->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 (retry) {
+ continue;
+ }
ssl->s3->key_update_count = 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);
+ if (!peek) {
+ // 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(todo);
+ if (ssl->s3->pending_app_data.empty()) {
+ ssl_read_buffer_discard(ssl);
+ }
+ }
+ return static_cast<int>(todo);
}
}
int SSL_read(SSL *ssl, void *buf, int num) {
- return ssl_read_impl(ssl, buf, num, 0 /* consume bytes */);
+ return ssl_read_impl(ssl, buf, num, false /* consume bytes */);
}
int SSL_peek(SSL *ssl, void *buf, int num) {
- return ssl_read_impl(ssl, buf, num, 1 /* peek */);
+ return ssl_read_impl(ssl, buf, num, true /* peek */);
}
int SSL_write(SSL *ssl, const void *buf, int num) {
@@ -1033,8 +1056,19 @@
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);
+ ssl->s3->pending_app_data = Span<uint8_t>();
+ for (;;) {
+ uint8_t alert = SSL_AD_DECODE_ERROR;
+ size_t consumed = 0;
+ auto ret = ssl->method->open_close_notify(ssl, &consumed, &alert,
+ ssl_read_buffer(ssl));
+ bool retry;
+ int bio_ret = ssl_handle_open_record(ssl, &retry, ret, consumed, alert);
+ if (bio_ret <= 0) {
+ break;
+ }
+ assert(retry); // open_close_notify never reports success.
+ }
if (ssl->s3->read_shutdown != ssl_shutdown_close_notify) {
return -1;
}
@@ -1467,10 +1501,7 @@
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/ssl/tls_method.cc b/ssl/tls_method.cc
index 9cc79b5..c7352ce 100644
--- a/ssl/tls_method.cc
+++ b/ssl/tls_method.cc
@@ -86,10 +86,7 @@
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 false;
@@ -115,11 +112,11 @@
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_open_close_notify,
ssl3_write_app_data,
ssl3_dispatch_alert,
ssl3_supports_cipher,