Path: blob/master/modules/mbedtls/stream_peer_mbedtls.cpp
10277 views
/**************************************************************************/1/* stream_peer_mbedtls.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "stream_peer_mbedtls.h"3132#include "core/io/stream_peer_tcp.h"3334int StreamPeerMbedTLS::bio_send(void *ctx, const unsigned char *buf, size_t len) {35if (buf == nullptr || len == 0) {36return 0;37}3839StreamPeerMbedTLS *sp = static_cast<StreamPeerMbedTLS *>(ctx);4041ERR_FAIL_NULL_V(sp, 0);4243int sent;44Error err = sp->base->put_partial_data((const uint8_t *)buf, len, sent);45if (err != OK) {46return MBEDTLS_ERR_SSL_INTERNAL_ERROR;47}48if (sent == 0) {49return MBEDTLS_ERR_SSL_WANT_WRITE;50}51return sent;52}5354int StreamPeerMbedTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) {55if (buf == nullptr || len == 0) {56return 0;57}5859StreamPeerMbedTLS *sp = static_cast<StreamPeerMbedTLS *>(ctx);6061ERR_FAIL_NULL_V(sp, 0);6263int got;64Error err = sp->base->get_partial_data((uint8_t *)buf, len, got);65if (err != OK) {66return MBEDTLS_ERR_SSL_INTERNAL_ERROR;67}68if (got == 0) {69return MBEDTLS_ERR_SSL_WANT_READ;70}71return got;72}7374void StreamPeerMbedTLS::_cleanup() {75tls_ctx->clear();76base = Ref<StreamPeer>();77status = STATUS_DISCONNECTED;78}7980Error StreamPeerMbedTLS::_do_handshake() {81int ret = mbedtls_ssl_handshake(tls_ctx->get_context());82if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {83// Handshake is still in progress, will retry via poll later.84return OK;85} else if (ret != 0) {86// An error occurred.87ERR_PRINT("TLS handshake error: " + itos(ret));88TLSContextMbedTLS::print_mbedtls_error(ret);89disconnect_from_stream();90status = STATUS_ERROR;91return FAILED;92}9394status = STATUS_CONNECTED;95return OK;96}9798Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, const String &p_common_name, Ref<TLSOptions> p_options) {99ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER);100101Error err = tls_ctx->init_client(MBEDTLS_SSL_TRANSPORT_STREAM, p_common_name, p_options.is_valid() ? p_options : TLSOptions::client());102ERR_FAIL_COND_V(err != OK, err);103104base = p_base;105mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr);106107status = STATUS_HANDSHAKING;108109if (_do_handshake() != OK) {110status = STATUS_ERROR_HOSTNAME_MISMATCH;111return FAILED;112}113114return OK;115}116117Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base, Ref<TLSOptions> p_options) {118ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER);119ERR_FAIL_COND_V(p_options.is_null() || !p_options->is_server(), ERR_INVALID_PARAMETER);120121Error err = tls_ctx->init_server(MBEDTLS_SSL_TRANSPORT_STREAM, p_options);122ERR_FAIL_COND_V(err != OK, err);123124base = p_base;125126mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr);127128status = STATUS_HANDSHAKING;129130if (_do_handshake() != OK) {131return FAILED;132}133134status = STATUS_CONNECTED;135return OK;136}137138Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) {139ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);140141Error err;142int sent = 0;143144while (p_bytes > 0) {145err = put_partial_data(p_data, p_bytes, sent);146147if (err != OK) {148return err;149}150151p_data += sent;152p_bytes -= sent;153}154155return OK;156}157158Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {159ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);160161r_sent = 0;162163if (p_bytes == 0) {164return OK;165}166167do {168int ret = mbedtls_ssl_write(tls_ctx->get_context(), &p_data[r_sent], p_bytes - r_sent);169if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {170// Non blocking IO.171break;172} else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {173// Clean close174disconnect_from_stream();175return ERR_FILE_EOF;176} else if (ret <= 0) {177TLSContextMbedTLS::print_mbedtls_error(ret);178disconnect_from_stream();179return ERR_CONNECTION_ERROR;180}181r_sent += ret;182183} while (r_sent < p_bytes);184185return OK;186}187188Error StreamPeerMbedTLS::get_data(uint8_t *p_buffer, int p_bytes) {189ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);190191Error err;192193int got = 0;194while (p_bytes > 0) {195err = get_partial_data(p_buffer, p_bytes, got);196197if (err != OK) {198return err;199}200201p_buffer += got;202p_bytes -= got;203}204205return OK;206}207208Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {209ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);210211r_received = 0;212213do {214int ret = mbedtls_ssl_read(tls_ctx->get_context(), &p_buffer[r_received], p_bytes - r_received);215if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {216// Non blocking IO.217break;218} else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {219// Clean close220disconnect_from_stream();221return ERR_FILE_EOF;222} else if (ret <= 0) {223TLSContextMbedTLS::print_mbedtls_error(ret);224disconnect_from_stream();225return ERR_CONNECTION_ERROR;226}227228r_received += ret;229230} while (r_received < p_bytes);231232return OK;233}234235void StreamPeerMbedTLS::poll() {236ERR_FAIL_COND(status != STATUS_CONNECTED && status != STATUS_HANDSHAKING);237ERR_FAIL_COND(base.is_null());238239if (status == STATUS_HANDSHAKING) {240_do_handshake();241return;242}243244// We could pass nullptr as second parameter, but some behavior sanitizers don't seem to like that.245// Passing a 1 byte buffer to workaround it.246uint8_t byte;247int ret = mbedtls_ssl_read(tls_ctx->get_context(), &byte, 0);248249if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {250// Nothing to read/write (non blocking IO)251} else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {252// Clean close (disconnect)253disconnect_from_stream();254return;255} else if (ret < 0) {256TLSContextMbedTLS::print_mbedtls_error(ret);257disconnect_from_stream();258return;259}260261Ref<StreamPeerTCP> tcp = base;262if (tcp.is_valid() && tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) {263disconnect_from_stream();264return;265}266}267268int StreamPeerMbedTLS::get_available_bytes() const {269ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0);270271return mbedtls_ssl_get_bytes_avail(&(tls_ctx->tls));272}273274StreamPeerMbedTLS::StreamPeerMbedTLS() {275tls_ctx.instantiate();276}277278StreamPeerMbedTLS::~StreamPeerMbedTLS() {279disconnect_from_stream();280}281282void StreamPeerMbedTLS::disconnect_from_stream() {283if (status != STATUS_CONNECTED && status != STATUS_HANDSHAKING) {284return;285}286287Ref<StreamPeerTCP> tcp = base;288if (tcp.is_valid() && tcp->get_status() == StreamPeerTCP::STATUS_CONNECTED) {289// We are still connected on the socket, try to send close notify.290mbedtls_ssl_close_notify(tls_ctx->get_context());291}292293_cleanup();294}295296StreamPeerMbedTLS::Status StreamPeerMbedTLS::get_status() const {297return status;298}299300Ref<StreamPeer> StreamPeerMbedTLS::get_stream() const {301return base;302}303304StreamPeerTLS *StreamPeerMbedTLS::_create_func(bool p_notify_postinitialize) {305return static_cast<StreamPeerTLS *>(ClassDB::creator<StreamPeerMbedTLS>(p_notify_postinitialize));306}307308void StreamPeerMbedTLS::initialize_tls() {309_create = _create_func;310}311312void StreamPeerMbedTLS::finalize_tls() {313_create = nullptr;314}315316317