|
- /* Copyright (c) 2015, Google Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
-
- #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"
-
-
- namespace bssl {
-
- // BIO uses int instead of size_t. No lengths will exceed uint16_t, so this will
- // not overflow.
- static_assert(0xffff <= 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");
-
- void SSLBuffer::Clear() {
- free(buf_); // Allocated with malloc().
- buf_ = nullptr;
- offset_ = 0;
- size_ = 0;
- cap_ = 0;
- }
-
- bool SSLBuffer::EnsureCap(size_t header_len, size_t new_cap) {
- if (new_cap > 0xffff) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return false;
- }
-
- if (cap_ >= new_cap) {
- return true;
- }
-
- // 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.
- uint8_t *new_buf = (uint8_t *)malloc(new_cap + SSL3_ALIGN_PAYLOAD - 1);
- if (new_buf == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return false;
- }
-
- // Offset the buffer such that the record body is aligned.
- size_t new_offset =
- (0 - header_len - (uintptr_t)new_buf) & (SSL3_ALIGN_PAYLOAD - 1);
-
- if (buf_ != NULL) {
- OPENSSL_memcpy(new_buf + new_offset, buf_ + offset_, size_);
- free(buf_); // Allocated with malloc().
- }
-
- buf_ = new_buf;
- offset_ = new_offset;
- cap_ = new_cap;
- 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_) {
- abort();
- }
- offset_ += (uint16_t)len;
- size_ -= (uint16_t)len;
- cap_ -= (uint16_t)len;
- }
-
- void SSLBuffer::DiscardConsumed() {
- if (size_ == 0) {
- Clear();
- }
- }
-
- 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, buf->data(), static_cast<int>(buf->cap()));
- if (ret <= 0) {
- ssl->s3->rwstate = SSL_READING;
- 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.
- int ret = BIO_read(ssl->rbio, buf->data() + buf->size(),
- static_cast<int>(len - buf->size()));
- if (ret <= 0) {
- ssl->s3->rwstate = SSL_READING;
- 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();
-
- if (SSL_is_dtls(ssl)) {
- static_assert(
- DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH <= 0xffff,
- "DTLS read buffer is too large");
-
- // The |len| parameter is ignored in DTLS.
- len = DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
- }
-
- if (!ssl->s3->read_buffer.EnsureCap(ssl_record_prefix_len(ssl), len)) {
- return -1;
- }
-
- if (ssl->rbio == NULL) {
- 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:
- 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 <=
- 0xffff,
- "maximum TLS write buffer is too large");
-
- static_assert(DTLS1_RT_HEADER_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD +
- SSL3_RT_MAX_PLAIN_LENGTH <=
- 0xffff,
- "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, buf->data(), buf->size());
- if (ret <= 0) {
- ssl->s3->rwstate = SSL_WRITING;
- 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, buf->data(), buf->size());
- if (ret <= 0) {
- ssl->s3->rwstate = SSL_WRITING;
- // 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 == NULL) {
- 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);
- }
- }
-
- } // namespace bssl
|