Files
cli/vendor/aws-lc-sys/aws-lc/ssl/ssl_buffer.cc

738 lines
23 KiB
C++
Raw Normal View History

// Copyright (c) 2015, Google Inc.
// SPDX-License-Identifier: ISC
#include <openssl/ssl.h>
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include "../crypto/internal.h"
#include "internal.h"
BSSL_NAMESPACE_BEGIN
// BIO uses int instead of size_t. No lengths will exceed SSLBUFFER_MAX_CAPACITY
// (uint16_t), so this will not overflow.
static_assert(SSLBUFFER_MAX_CAPACITY <= INT_MAX,
"uint16_t does not fit in int");
static_assert((SSL3_ALIGN_PAYLOAD & (SSL3_ALIGN_PAYLOAD - 1)) == 0,
"SSL3_ALIGN_PAYLOAD must be a power of 2");
static OPENSSL_INLINE size_t compute_buffer_size(size_t capacity) {
return capacity + SSL3_ALIGN_PAYLOAD - 1;
}
static OPENSSL_INLINE size_t compute_buffer_offset(size_t header_len,
uint8_t *buffer) {
return (0 - header_len - (uintptr_t)buffer) & (SSL3_ALIGN_PAYLOAD - 1);
}
void SSLBuffer::Clear() {
if (buf_allocated_) {
free(buf_); // Allocated with malloc().
}
buf_ = nullptr;
buf_allocated_ = false;
buf_cap_ = 0;
buf_size_ = 0;
header_len_ = 0;
offset_ = 0;
size_ = 0;
cap_ = 0;
max_serialization_version_ = kSSLBufferMaxSerDeVersion;
}
bool SSLBuffer::EnsureCap(size_t header_len, size_t new_cap) {
if (new_cap > SSLBUFFER_MAX_CAPACITY) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return false;
}
if ((size_t)cap_ >= new_cap) {
return true;
}
uint8_t *new_buf;
bool new_buf_allocated;
size_t new_offset;
if (new_cap <= sizeof(inline_buf_)) {
// This function is called twice per TLS record, first for the five-byte
// header. To avoid allocating twice, use an inline buffer for short inputs.
new_buf = inline_buf_;
new_buf_allocated = false;
new_offset = 0;
buf_size_ = sizeof(inline_buf_);
} else {
// Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment.
//
// Since this buffer gets allocated quite frequently and doesn't contain any
// sensitive data, we allocate with malloc rather than |OPENSSL_malloc| and
// avoid zeroing on free.
buf_size_ = compute_buffer_size(new_cap);
new_buf = (uint8_t *)malloc(buf_size_);
if (new_buf == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return false;
}
new_buf_allocated = true;
// Offset the buffer such that the record body is aligned.
new_offset = compute_buffer_offset(header_len, new_buf);
}
// Note if the both old and new buffer are inline, the source and destination
// may alias.
OPENSSL_memmove(new_buf + new_offset, buf_ + offset_, size_);
if (buf_allocated_) {
free(buf_); // Allocated with malloc().
}
buf_ = new_buf;
buf_allocated_ = new_buf_allocated;
header_len_ = header_len;
offset_ = new_offset;
buf_cap_ = cap_ = new_cap;
max_serialization_version_ = kSSLBufferMaxSerDeVersion;
return true;
}
void SSLBuffer::DidWrite(size_t new_size) {
if (new_size > cap() - size()) {
abort();
}
size_ += new_size;
}
void SSLBuffer::Consume(size_t len) {
if (len > (size_t)size_) {
abort();
}
offset_ += (int)len;
size_ -= (int)len;
cap_ -= (int)len;
}
void SSLBuffer::DiscardConsumed() {
if (size_ == 0) {
Clear();
}
}
// An SSLBuffer is serialized as the following ASN.1 structure:
//
// -- The V2 Serialization
// SSLBuffer ::= SEQUENCE {
// version INTEGER (2), -- SSLBuffer structure
// bufAllocated BOOLEAN,
// bufferCapacity INTEGER,
// headerLength INTEGER,
// relativeOffset INTEGER,
// remainingCapacity INTEGER,
// buf OCTET STRING,
// }
//
// -- The V1 Serialization
// SSLBuffer ::= SEQUENCE {
// version INTEGER (1), -- SSLBuffer structure
// bufAllocated BOOLEAN,
// offset INTEGER,
// size INTEGER,
// cap INTEGER,
// buf OCTET STRING,
// }
bool SSLBuffer::DoSerializationV1(CBB &seq) {
if (!CBB_add_asn1_uint64(&seq, SSL_BUFFER_SERDE_VERSION_ONE) ||
!CBB_add_asn1_bool(&seq, (buf_allocated_ ? 1 : 0)) ||
!CBB_add_asn1_uint64(&seq, offset_) ||
!CBB_add_asn1_uint64(&seq, size_) || !CBB_add_asn1_uint64(&seq, cap_) ||
(buf_allocated_ && !CBB_add_asn1_octet_string(&seq, buf_, buf_size_)) ||
(!buf_allocated_ &&
!CBB_add_asn1_octet_string(&seq, inline_buf_, SSL3_RT_HEADER_LENGTH))) {
return false;
}
return true;
}
bool SSLBuffer::DoSerializationV2(CBB &seq) {
size_t align_offset = 0;
if (buf_allocated_) {
align_offset = compute_buffer_offset(header_len_, buf_);
}
size_t rel_offset = offset_ - align_offset;
if (!CBB_add_asn1_uint64(&seq, SSL_BUFFER_SERDE_VERSION_TWO) ||
!CBB_add_asn1_bool(&seq, (buf_allocated_ ? 1 : 0)) ||
!CBB_add_asn1_uint64(&seq, buf_cap_) ||
!CBB_add_asn1_uint64(&seq, header_len_) ||
!CBB_add_asn1_uint64(&seq, rel_offset) ||
!CBB_add_asn1_uint64(&seq, cap_) ||
(buf_allocated_ && !CBB_add_asn1_octet_string(&seq, buf_ + align_offset,
rel_offset + size_)) ||
(!buf_allocated_ &&
!CBB_add_asn1_octet_string(&seq, inline_buf_ + align_offset,
rel_offset + size_))) {
return false;
}
return true;
}
bool SSLBuffer::DoSerialization(CBB &cbb) {
CBB seq;
if (!CBB_add_asn1(&cbb, &seq, CBS_ASN1_SEQUENCE)) {
return false;
}
switch (max_serialization_version_) {
case SSL_BUFFER_SERDE_VERSION_TWO:
if (!DoSerializationV2(seq)) {
return false;
}
break;
case SSL_BUFFER_SERDE_VERSION_ONE:
if (!DoSerializationV1(seq)) {
return false;
}
break;
default:
// Only possible with a programming error
abort();
}
return CBB_flush(&cbb) == 1;
}
bool SSLBuffer::ValidateBuffersState() {
const uint8_t *start_ptr = buf_;
const uint8_t *end_ptr = buf_ + buf_size_;
const uint8_t *data_start_ = buf_ + offset_;
const uint8_t *data_end_ptr = data_start_ + size_;
const uint8_t *remaining_ptr = data_end_ptr + (cap_ - size_);
if (data_start_ > end_ptr || data_start_ < start_ptr ||
data_end_ptr > end_ptr || data_end_ptr < start_ptr || size_ > cap_ ||
remaining_ptr > end_ptr || remaining_ptr < start_ptr) {
return false;
}
return true;
}
bool SSLBuffer::DoDeserialization(CBS &cbs) {
CBS seq;
uint64_t version = 0;
if (!CBS_get_asn1(&cbs, &seq, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1_uint64(&seq, &version) ||
version > kSSLBufferMaxSerDeVersion) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
switch (version) {
case SSL_BUFFER_SERDE_VERSION_TWO:
return DoDeserializationV2(seq);
case SSL_BUFFER_SERDE_VERSION_ONE:
return DoDeserializationV1(seq);
default:
return false;
}
}
bool SSLBuffer::DoDeserializationV1(CBS &cbs) {
CBS buf;
int buf_allocated_int = 0;
uint64_t offset = 0, size = 0, cap = 0;
if (!CBS_get_asn1_bool(&cbs, &buf_allocated_int) ||
!CBS_get_asn1_uint64(&cbs, &offset) ||
!CBS_get_asn1_uint64(&cbs, &size) || !CBS_get_asn1_uint64(&cbs, &cap) ||
!CBS_get_asn1(&cbs, &buf, CBS_ASN1_OCTETSTRING) || CBS_len(&cbs) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
if (offset > INT_MAX || size > INT_MAX || cap > INT_MAX) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
// In the event that deserialization happens into an existing buffer.
Clear();
bool buf_allocated = !!buf_allocated_int;
if (buf_allocated) {
// When buf_allocated, CBS_len(&buf) should be larger than
// sizeof(inline_buf_). This is ensured in |EnsureCap|.
if (CBS_len(&buf) <= sizeof(inline_buf_)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
buf_allocated_ = true;
buf_ = (uint8_t *)malloc(CBS_len(&buf));
if (buf_ == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return false;
}
buf_size_ = CBS_len(&buf);
OPENSSL_memcpy(buf_, CBS_data(&buf), CBS_len(&buf));
} else {
if (CBS_len(&buf) != sizeof(inline_buf_)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
buf_allocated_ = false;
buf_ = inline_buf_;
buf_size_ = sizeof(inline_buf_);
OPENSSL_memcpy(inline_buf_, CBS_data(&buf), CBS_len(&buf));
}
buf_cap_ = header_len_ = 0; // V1 was lossy :(
offset_ = (int)offset;
size_ = (int)size;
cap_ = (int)cap;
// As we restored from a V1 format we can only serialize as V1 until the next
// |EnsureCap| call.
max_serialization_version_ = SSL_BUFFER_SERDE_VERSION_ONE;
// Final sanity check
if(!ValidateBuffersState()) {
Clear();
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
return true;
}
bool SSLBuffer::DoDeserializationV2(CBS &cbs) {
CBS buf;
int buf_allocated_int = 0;
uint64_t header_len = 0, buf_cap = 0, rel_offset = 0, cap = 0;
size_t align_offset = 0;
if (!CBS_get_asn1_bool(&cbs, &buf_allocated_int) ||
!CBS_get_asn1_uint64(&cbs, &buf_cap) ||
!CBS_get_asn1_uint64(&cbs, &header_len) ||
!CBS_get_asn1_uint64(&cbs, &rel_offset) ||
!CBS_get_asn1_uint64(&cbs, &cap) ||
!CBS_get_asn1(&cbs, &buf, CBS_ASN1_OCTETSTRING) || CBS_len(&cbs) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
if (buf_cap > SSLBUFFER_MAX_CAPACITY || rel_offset > INT_MAX ||
rel_offset > CBS_len(&buf) || cap > INT_MAX) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
// In the event that deserialization happens into an existing buffer.
Clear();
bool buf_allocated = !!buf_allocated_int;
if (buf_allocated) {
buf_allocated_ = true;
buf_size_ = compute_buffer_size(buf_cap);
buf_ = (uint8_t *)malloc(buf_size_);
if (buf_ == NULL) {
Clear();
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return false;
}
align_offset = compute_buffer_offset(header_len, buf_);
if (rel_offset > INT_MAX - align_offset ||
CBS_len(&buf) - rel_offset > INT_MAX ||
CBS_len(&buf) > buf_size_ - align_offset) {
Clear();
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
OPENSSL_memcpy(buf_ + align_offset, CBS_data(&buf), CBS_len(&buf));
} else {
buf_allocated_ = false;
buf_ = inline_buf_;
buf_size_ = sizeof(inline_buf_);
// We could relax this and just allocate a in-memory buffer with correct
// alignment. But this value is not configurable (unlike
// SSL3_ALIGN_PAYLOAD), so we can adjust this in the future if we modify
// sizeof(inline_buf_);
if (CBS_len(&buf) > sizeof(inline_buf_) || buf_cap > sizeof(inline_buf_)) {
Clear();
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
OPENSSL_memcpy(buf_, CBS_data(&buf), CBS_len(&buf));
}
buf_cap_ = (size_t)buf_cap;
header_len_ = (size_t)header_len;
offset_ = (int)(align_offset + rel_offset);
size_ = (int)(CBS_len(&buf) - rel_offset);
cap_ = (int)cap;
max_serialization_version_ = SSL_BUFFER_SERDE_VERSION_TWO;
// Final sanity check
if (!ValidateBuffersState()) {
Clear();
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
return true;
}
static const unsigned kBufferViewOffsetFromDataPtr =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0;
bool SSLBuffer::SerializeBufferView(CBB &cbb, Span<uint8_t> &view) {
// View must be a span that points into this buffer.
if (!buf_ptr() || !view.data() ||
view.data() <
buf_ptr() || // does the start of the view fall before the buffer
(view.data() + view.size()) <
buf_ptr() || // does the end of the view fall before the buffer
(buf_ptr() + buf_size()) <
view.data() || // does the start of the view fall after the end of
// the buffer
(buf_ptr() + buf_size()) <
(view.data() + view.size()) // does the end of of the view fall after
// the end of the buffer
) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
// Important: The offset should not be relative to
// |buf_ptr()|, as the alignment can change on restore.
// It should be relative to |data()|, which can mean it may
// currently point towards data before the current offset and thus be
// negative.
//
// Ideally for simplicity we would make this relative to read buffer's
// alignment offset, but we might not know this in the V1 case if the buffer
// had been restored from a V1 format :\. So this is the better option at this
// time.
ptrdiff_t offset = view.data() - data();
CBB child, child2;
if (!CBB_add_asn1(&cbb, &child, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&child, &child2, kBufferViewOffsetFromDataPtr) ||
!CBB_add_asn1_int64(&child2, offset) ||
!CBB_add_asn1_uint64(&child, view.size())) {
return false;
}
return CBB_flush(&cbb) == 1;
}
static bool deserialize_buffer_view_from_buf_ptr_offset(CBS &cbs,
SSLBuffer *buffer,
Span<uint8_t> &view) {
if (!buffer) {
abort();
}
uint64_t offset = 0, size = 0;
if (!CBS_get_asn1_uint64(&cbs, &offset) ||
!CBS_get_asn1_uint64(&cbs, &size)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return 0;
}
uint8_t *view_ptr = buffer->buf_ptr() + offset;
if (view_ptr < buffer->buf_ptr() || // does the start of the view fall before
// the buffer
view_ptr > (buffer->buf_ptr() +
buffer->buf_size()) || // does the the start of the view fall
// after the end of the buffer
(view_ptr + size) <
buffer->buf_ptr() || // does the end of the view fall
// before the start of the buffer
(view_ptr + size) > (buffer->buf_ptr() +
buffer->buf_size()) // does the end of the view fall
// after the end of the buffer
) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
view = MakeSpan(view_ptr, size);
return true;
}
static int fits_in_ptrdiff_int64(int64_t x) {
#if PTRDIFF_MAX > INT64_MAX || PTRDIFF_MAX == INT64_MAX
// ptrdiff_t is equal to or wider than int64_t — all int64_t fit
(void)x;
return 1;
#else
// ptrdiff_t is narrower than int64_t — cast to int64_t for safe compare
return x >= (int64_t)PTRDIFF_MIN && x <= (int64_t)PTRDIFF_MAX;
#endif
}
static bool deserialize_buffer_view_from_data_offset(CBS &cbs,
SSLBuffer *buffer,
Span<uint8_t> &view) {
if (!buffer) {
abort();
}
CBS child;
int64_t offset = 0;
uint64_t size = 0;
if (!CBS_get_asn1(&cbs, &child, kBufferViewOffsetFromDataPtr) ||
!CBS_get_asn1_int64(&child, &offset) ||
!CBS_get_asn1_uint64(&cbs, &size)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return 0;
}
if (!fits_in_ptrdiff_int64(offset)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return 0;
}
uint8_t *view_ptr = buffer->data() + (ptrdiff_t)offset;
if (view_ptr <
buffer->buf_ptr() || // does the view start fall before the buffer
view_ptr >
(buffer->buf_ptr() +
buffer->buf_size()) || // does the view start fall after the buffer
(view_ptr + size) < buffer->buf_ptr() || // does the end of the view fall
// before the buffer start
(view_ptr + size) > (buffer->buf_ptr() +
buffer->buf_size() // does the end of the view fall
// after the end of the buffer
)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return 0;
}
view = MakeSpan(view_ptr, size);
return true;
}
bool SSLBuffer::DeserializeBufferView(CBS &cbs, Span<uint8_t> &view) {
CBS app_seq;
if (!CBS_get_asn1(&cbs, &app_seq, CBS_ASN1_SEQUENCE)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
if (CBS_peek_asn1_tag(&app_seq, CBS_ASN1_INTEGER)) {
return deserialize_buffer_view_from_buf_ptr_offset(app_seq, this, view);
} else if (CBS_peek_asn1_tag(&app_seq, kBufferViewOffsetFromDataPtr)) {
return deserialize_buffer_view_from_data_offset(app_seq, this, view);
} else {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERIALIZATION_INVALID_SSL_BUFFER);
return false;
}
return true;
}
static int dtls_read_buffer_next_packet(SSL *ssl) {
SSLBuffer *buf = &ssl->s3->read_buffer;
if (!buf->empty()) {
// It is an error to call |dtls_read_buffer_extend| when the read buffer is
// not empty.
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return -1;
}
// Read a single packet from |ssl->rbio|. |buf->cap()| must fit in an int.
int ret =
BIO_read(ssl->rbio.get(), buf->data(), static_cast<int>(buf->cap()));
if (ret <= 0) {
ssl->s3->rwstate = SSL_ERROR_WANT_READ;
return ret;
}
buf->DidWrite(static_cast<size_t>(ret));
return 1;
}
static int tls_read_buffer_extend_to(SSL *ssl, size_t len) {
SSLBuffer *buf = &ssl->s3->read_buffer;
if (len > buf->cap()) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
return -1;
}
// Read until the target length is reached.
while (buf->size() < len) {
// The amount of data to read is bounded by |buf->cap|, which must fit in an
// int.
// If enable_read_ahead we want to attempt to fill the entire buffer, but
// the while loop will bail once we have at least the requested len amount
// of data. If not enable_read_ahead, only read as much to get to len bytes,
// at this point we know len is less than the overall size of the buffer.
assert(buf->cap() >= buf->size());
size_t read_amount =
ssl->enable_read_ahead ? buf->cap() - buf->size() : len - buf->size();
assert(read_amount <= buf->cap() - buf->size());
int ret = BIO_read(ssl->rbio.get(), buf->data() + buf->size(),
static_cast<int>(read_amount));
if (ret <= 0) {
ssl->s3->rwstate = SSL_ERROR_WANT_READ;
return ret;
}
buf->DidWrite(static_cast<size_t>(ret));
}
return 1;
}
int ssl_read_buffer_extend_to(SSL *ssl, size_t len) {
// |ssl_read_buffer_extend_to| implicitly discards any consumed data.
ssl->s3->read_buffer.DiscardConsumed();
size_t buffer_size = len;
if (SSL_is_dtls(ssl)) {
static_assert(DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH <=
SSLBUFFER_MAX_CAPACITY,
"DTLS read buffer is too large");
// The |len| parameter is ignored in DTLS.
len = DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
buffer_size = len;
} else {
if (ssl->ctx.get()->enable_read_ahead) {
// If we're reading ahead allocate read_ahead_buffer size or the requested
// len whichever is bigger
buffer_size = std::max(len, ssl->read_ahead_buffer_size);
}
}
if (!ssl->s3->read_buffer.EnsureCap(ssl_record_prefix_len(ssl),
buffer_size)) {
return -1;
}
if (ssl->rbio == nullptr) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET);
return -1;
}
int ret;
if (SSL_is_dtls(ssl)) {
// |len| is ignored for a datagram transport.
ret = dtls_read_buffer_next_packet(ssl);
} else {
ret = tls_read_buffer_extend_to(ssl, len);
}
if (ret <= 0) {
// If the buffer was empty originally and remained empty after attempting to
// extend it, release the buffer until the next attempt.
ssl->s3->read_buffer.DiscardConsumed();
}
return ret;
}
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->s3->read_buffer.Consume(consumed);
}
if (ret != ssl_open_record_success) {
// Nothing was returned to the caller, so discard anything marked consumed.
ssl->s3->read_buffer.DiscardConsumed();
}
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:
ssl->s3->rwstate = SSL_ERROR_ZERO_RETURN;
return 0;
case ssl_open_record_error:
if (alert != 0) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
}
return -1;
}
assert(0);
return -1;
}
static_assert(SSL3_RT_HEADER_LENGTH * 2 +
SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD * 2 +
SSL3_RT_MAX_PLAIN_LENGTH <=
SSLBUFFER_MAX_CAPACITY,
"maximum TLS write buffer is too large");
static_assert(DTLS1_RT_HEADER_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD +
SSL3_RT_MAX_PLAIN_LENGTH <=
SSLBUFFER_MAX_CAPACITY,
"maximum DTLS write buffer is too large");
static int tls_write_buffer_flush(SSL *ssl) {
SSLBuffer *buf = &ssl->s3->write_buffer;
while (!buf->empty()) {
int ret = BIO_write(ssl->wbio.get(), buf->data(), buf->size());
if (ret <= 0) {
ssl->s3->rwstate = SSL_ERROR_WANT_WRITE;
return ret;
}
buf->Consume(static_cast<size_t>(ret));
}
buf->Clear();
return 1;
}
static int dtls_write_buffer_flush(SSL *ssl) {
SSLBuffer *buf = &ssl->s3->write_buffer;
if (buf->empty()) {
return 1;
}
int ret = BIO_write(ssl->wbio.get(), buf->data(), buf->size());
if (ret <= 0) {
ssl->s3->rwstate = SSL_ERROR_WANT_WRITE;
// If the write failed, drop the write buffer anyway. Datagram transports
// can't write half a packet, so the caller is expected to retry from the
// top.
buf->Clear();
return ret;
}
buf->Clear();
return 1;
}
int ssl_write_buffer_flush(SSL *ssl) {
if (ssl->wbio == nullptr) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET);
return -1;
}
if (SSL_is_dtls(ssl)) {
return dtls_write_buffer_flush(ssl);
} else {
return tls_write_buffer_flush(ssl);
}
}
BSSL_NAMESPACE_END