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>kris/onging/CECPQ3_patch15
@@ -272,9 +272,10 @@ static bool 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) { | |||
@@ -496,17 +490,21 @@ bool dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr, | |||
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; | |||
} | |||
@@ -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 @@ 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; | |||
*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, | |||
@@ -117,11 +117,11 @@ static const SSL_PROTOCOL_METHOD kDTLSProtocolMethod = { | |||
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, | |||
@@ -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->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 @@ 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; | |||
} | |||
@@ -1062,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); | |||
@@ -1728,20 +1734,31 @@ struct SSL_PROTOCOL_METHOD { | |||
// 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 @@ 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; | |||
@@ -2147,7 +2155,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 | |||
@@ -2690,18 +2700,19 @@ 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); | |||
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 @@ bool ssl_add_message_cbb(SSL *ssl, CBB *cbb); | |||
// 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 @@ 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); | |||
@@ -274,55 +274,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 +303,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 +322,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 +358,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 +366,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 +375,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 +384,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, | |||
@@ -497,58 +467,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) { | |||
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) { | |||
@@ -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)); | |||
@@ -333,152 +282,120 @@ 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 (;;) { | |||
// Get new packet if necessary. | |||
if (rr->length == 0) { | |||
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; | |||
} | |||
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; | |||
if (ssl->init_buf == NULL) { | |||
ssl->init_buf = BUF_MEM_new(); | |||
} | |||
// 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) { | |||
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; | |||
ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC, 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_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) { | |||
@@ -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; | |||
@@ -907,7 +907,7 @@ no_renegotiation: | |||
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 @@ static int ssl_read_impl(SSL *ssl, void *buf, int num, int peek) { | |||
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 @@ 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); | |||
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_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 | |||
@@ -86,10 +86,7 @@ static void ssl3_on_handshake_complete(SSL *ssl) { | |||
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 @@ static const SSL_PROTOCOL_METHOD kTLSProtocolMethod = { | |||
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, | |||