solanum-vs-hackint-and-char.../libratbox/src/mbedtls.c
Aaron Jones 566f46785f
MbedTLS: Misc backend cleanups
* Add generic direction enum for negotiation setup.

* Rename a rather long wrapper function to a shorter one consistent with
  what it does.

* Rework context setup function.

* Don't check for handshake state before beginning handshaking.

  The old backend began a handshake and then stepped into the callback
  function if it was interrupted; the current one just jumps right into
  it, so there is no need to check if it has previously succeeded,
  because it hasn't been attempted yet.

* Add missing errno assignment to one of the handshake wrappers.

* Don't bother checking if SSL_P(F) is NULL when we already checked if
  F->ssl is NULL -- this should be impossible.

* Don't bother checking if SSL_C(F) is NULL -- this was a no-op.

* Change the socket send and recv functions to not peer into a foreign
  ratbox structure -- use the correct function to get the socket fd.

* Rewrap some lines and function arguments.

Other backends will be brought into line with this backend soon.

This will enable easier maintenance of the backends, by reducing the
diffs between them, which should make different behaviour easier to
spot.
2016-09-10 08:42:04 +00:00

856 lines
20 KiB
C

/*
* libratbox: a library used by ircd-ratbox and other things
* mbedtls.c: ARM MbedTLS backend
*
* Copyright (C) 2007-2008 ircd-ratbox development team
* Copyright (C) 2007-2008 Aaron Sethman <androsyn@ratbox.org>
* Copyright (C) 2015 William Pitcock <nenolod@dereferenced.org>
* Copyright (C) 2016 Aaron Jones <aaronmdjones@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*
* $Id$
*/
#include <libratbox_config.h>
#include <ratbox_lib.h>
#include <commio-int.h>
#include <commio-ssl.h>
#ifdef HAVE_MBEDTLS
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/certs.h"
#include "mbedtls/x509.h"
#include "mbedtls/ssl.h"
#include "mbedtls/net.h"
#include "mbedtls/error.h"
#include "mbedtls/debug.h"
#include "mbedtls/dhm.h"
#include "mbedtls/version.h"
#include "mbedtls_embedded_data.h"
typedef enum
{
RB_FD_TLS_DIRECTION_IN = 0,
RB_FD_TLS_DIRECTION_OUT = 1
} rb_fd_tls_direction;
#define RB_MAX_CIPHERSUITES 512
typedef struct
{
mbedtls_x509_crt crt;
mbedtls_pk_context key;
mbedtls_dhm_context dhp;
mbedtls_ssl_config server_cfg;
mbedtls_ssl_config client_cfg;
int suites[RB_MAX_CIPHERSUITES + 1];
size_t refcount;
} rb_mbedtls_cfg_context;
typedef struct
{
rb_mbedtls_cfg_context *cfg;
mbedtls_ssl_context ssl;
} rb_mbedtls_ssl_context;
#define SSL_C(x) ((rb_mbedtls_ssl_context *) (x)->ssl)->cfg
#define SSL_P(x) &((rb_mbedtls_ssl_context *) (x)->ssl)->ssl
static mbedtls_ctr_drbg_context ctr_drbg_ctx;
static mbedtls_entropy_context entropy_ctx;
static mbedtls_x509_crt dummy_ca_ctx;
static rb_mbedtls_cfg_context *rb_mbedtls_cfg = NULL;
struct ssl_connect
{
CNCB *callback;
void *data;
int timeout;
};
static const char *rb_mbedtls_strerror(int);
static void rb_ssl_connect_realcb(rb_fde_t *, int, struct ssl_connect *);
static int rb_sock_net_recv(void *, unsigned char *, size_t);
static int rb_sock_net_xmit(void *, const unsigned char *, size_t);
/*
* Internal MbedTLS-specific code
*/
static void
rb_mbedtls_cfg_incref(rb_mbedtls_cfg_context *const cfg)
{
lrb_assert(cfg->refcount > 0);
cfg->refcount++;
}
static void
rb_mbedtls_cfg_decref(rb_mbedtls_cfg_context *const cfg)
{
if(cfg == NULL)
return;
lrb_assert(cfg->refcount > 0);
if((--cfg->refcount) > 0)
return;
mbedtls_ssl_config_free(&cfg->client_cfg);
mbedtls_ssl_config_free(&cfg->server_cfg);
mbedtls_dhm_free(&cfg->dhp);
mbedtls_pk_free(&cfg->key);
mbedtls_x509_crt_free(&cfg->crt);
rb_free(cfg);
}
static void
rb_ssl_init_fd(rb_fde_t *const F, rb_fd_tls_direction dir)
{
mbedtls_ssl_config *mbed_config;
switch(dir)
{
case RB_FD_TLS_DIRECTION_IN:
mbed_config = &rb_mbedtls_cfg->server_cfg;
break;
case RB_FD_TLS_DIRECTION_OUT:
mbed_config = &rb_mbedtls_cfg->client_cfg;
break;
default:
rb_lib_log("rb_ssl_init_fd: bad direction");
abort();
return;
}
rb_mbedtls_ssl_context *const mbed_ssl_ctx = rb_malloc(sizeof *mbed_ssl_ctx);
if(mbed_ssl_ctx == NULL)
{
rb_lib_log("rb_ssl_init_fd: rb_malloc: allocation failure");
rb_close(F);
return;
}
mbedtls_ssl_init(&mbed_ssl_ctx->ssl);
mbedtls_ssl_set_bio(&mbed_ssl_ctx->ssl, F, rb_sock_net_xmit, rb_sock_net_recv, NULL);
int ret;
if((ret = mbedtls_ssl_setup(&mbed_ssl_ctx->ssl, mbed_config)) != 0)
{
rb_lib_log("rb_ssl_init_fd: ssl_setup: %s", rb_mbedtls_strerror(ret));
mbedtls_ssl_free(&mbed_ssl_ctx->ssl);
rb_free(mbed_ssl_ctx);
rb_close(F);
return;
}
rb_mbedtls_cfg_incref(rb_mbedtls_cfg);
mbed_ssl_ctx->cfg = rb_mbedtls_cfg;
F->ssl = mbed_ssl_ctx;
}
static rb_mbedtls_cfg_context *
rb_mbedtls_cfg_new(void)
{
rb_mbedtls_cfg_context *const cfg = rb_malloc(sizeof *cfg);
if(cfg == NULL)
return NULL;
mbedtls_x509_crt_init(&cfg->crt);
mbedtls_pk_init(&cfg->key);
mbedtls_dhm_init(&cfg->dhp);
mbedtls_ssl_config_init(&cfg->server_cfg);
mbedtls_ssl_config_init(&cfg->client_cfg);
(void) memset(cfg->suites, 0x00, sizeof cfg->suites);
cfg->refcount = 1;
int ret;
if((ret = mbedtls_ssl_config_defaults(&cfg->server_cfg,
MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
{
rb_lib_log("rb_mbedtls_cfg_new: ssl_config_defaults (server): %s", rb_mbedtls_strerror(ret));
rb_mbedtls_cfg_decref(cfg);
return NULL;
}
if((ret = mbedtls_ssl_config_defaults(&cfg->client_cfg,
MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
{
rb_lib_log("rb_mbedtls_cfg_new: ssl_config_defaults (client): %s", rb_mbedtls_strerror(ret));
rb_mbedtls_cfg_decref(cfg);
return NULL;
}
mbedtls_ssl_conf_rng(&cfg->server_cfg, mbedtls_ctr_drbg_random, &ctr_drbg_ctx);
mbedtls_ssl_conf_rng(&cfg->client_cfg, mbedtls_ctr_drbg_random, &ctr_drbg_ctx);
mbedtls_ssl_conf_ca_chain(&cfg->server_cfg, &dummy_ca_ctx, NULL);
mbedtls_ssl_conf_ca_chain(&cfg->client_cfg, &dummy_ca_ctx, NULL);
mbedtls_ssl_conf_authmode(&cfg->server_cfg, MBEDTLS_SSL_VERIFY_OPTIONAL);
mbedtls_ssl_conf_authmode(&cfg->client_cfg, MBEDTLS_SSL_VERIFY_NONE);
#ifdef MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE
mbedtls_ssl_conf_legacy_renegotiation(&cfg->client_cfg, MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE);
#endif
#ifdef MBEDTLS_SSL_SESSION_TICKETS_DISABLED
mbedtls_ssl_conf_session_tickets(&cfg->client_cfg, MBEDTLS_SSL_SESSION_TICKETS_DISABLED);
#endif
return cfg;
}
static void
rb_ssl_accept_common(rb_fde_t *const F, void *const data)
{
lrb_assert(F != NULL);
lrb_assert(F->accept != NULL);
lrb_assert(F->accept->callback != NULL);
lrb_assert(F->ssl != NULL);
int ret = mbedtls_ssl_handshake(SSL_P(F));
switch(ret)
{
case 0:
F->handshake_count++;
break;
case MBEDTLS_ERR_SSL_WANT_READ:
rb_setselect(F, RB_SELECT_READ, rb_ssl_accept_common, NULL);
return;
case MBEDTLS_ERR_SSL_WANT_WRITE:
rb_setselect(F, RB_SELECT_WRITE, rb_ssl_accept_common, NULL);
return;
default:
errno = EIO;
F->ssl_errno = ret;
F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data);
return;
}
rb_settimeout(F, 0, NULL, NULL);
rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
struct acceptdata *const ad = F->accept;
F->accept = NULL;
ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
rb_free(ad);
}
static void
rb_ssl_tryconn_cb(rb_fde_t *const F, void *const data)
{
lrb_assert(F != NULL);
lrb_assert(F->ssl != NULL);
int ret = mbedtls_ssl_handshake(SSL_P(F));
switch(ret)
{
case 0:
F->handshake_count++;
break;
case MBEDTLS_ERR_SSL_WANT_READ:
rb_setselect(F, RB_SELECT_READ, rb_ssl_tryconn_cb, data);
return;
case MBEDTLS_ERR_SSL_WANT_WRITE:
rb_setselect(F, RB_SELECT_WRITE, rb_ssl_tryconn_cb, data);
return;
default:
errno = EIO;
F->ssl_errno = ret;
rb_ssl_connect_realcb(F, RB_ERROR_SSL, data);
return;
}
rb_ssl_connect_realcb(F, RB_OK, data);
}
static const char *
rb_mbedtls_strerror(int err)
{
static char errbuf[512];
#ifdef MBEDTLS_ERROR_C
char mbed_errbuf[512];
mbedtls_strerror(err, mbed_errbuf, sizeof mbed_errbuf);
(void) rb_snprintf(errbuf, sizeof errbuf, "(-0x%x) %s", -err, mbed_errbuf);
#else
(void) rb_snprintf(errbuf, sizeof errbuf, "-0x%x", -err);
#endif
return errbuf;
}
/*
* External MbedTLS-specific code
*/
void
rb_ssl_shutdown(rb_fde_t *const F)
{
if(F == NULL || F->ssl == NULL)
return;
for(int i = 0; i < 4; i++)
{
int ret = mbedtls_ssl_close_notify(SSL_P(F));
if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
break;
}
mbedtls_ssl_free(SSL_P(F));
rb_mbedtls_cfg_decref(SSL_C(F));
rb_free(F->ssl);
F->ssl = NULL;
}
int
rb_init_ssl(void)
{
mbedtls_ctr_drbg_init(&ctr_drbg_ctx);
mbedtls_entropy_init(&entropy_ctx);
int ret;
if((ret = mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, mbedtls_entropy_func, &entropy_ctx,
(const unsigned char *)rb_mbedtls_personal_str, sizeof(rb_mbedtls_personal_str))) != 0)
{
rb_lib_log("rb_init_ssl: ctr_drbg_seed: %s", rb_mbedtls_strerror(ret));
return 0;
}
if((ret = mbedtls_x509_crt_parse_der(&dummy_ca_ctx, rb_mbedtls_dummy_ca_certificate,
sizeof(rb_mbedtls_dummy_ca_certificate))) != 0)
{
rb_lib_log("rb_init_ssl: x509_crt_parse_der (Dummy CA): %s", rb_mbedtls_strerror(ret));
return 0;
}
rb_lib_log("rb_init_ssl: MbedTLS backend initialised");
return 1;
}
int
rb_setup_ssl_server(const char *const certfile, const char *keyfile,
const char *const dhfile, const char *const cipherlist)
{
if(certfile == NULL)
{
rb_lib_log("rb_setup_ssl_server: no certificate file specified");
return 0;
}
if(keyfile == NULL)
keyfile = certfile;
rb_mbedtls_cfg_context *const newcfg = rb_mbedtls_cfg_new();
if(newcfg == NULL)
{
rb_lib_log("rb_setup_ssl_server: rb_mbedtls_cfg_new: allocation failed");
return 0;
}
int ret;
if((ret = mbedtls_x509_crt_parse_file(&newcfg->crt, certfile)) != 0)
{
rb_lib_log("rb_setup_ssl_server: x509_crt_parse_file ('%s'): %s", certfile, rb_mbedtls_strerror(ret));
rb_mbedtls_cfg_decref(newcfg);
return 0;
}
if((ret = mbedtls_pk_parse_keyfile(&newcfg->key, keyfile, NULL)) != 0)
{
rb_lib_log("rb_setup_ssl_server: pk_parse_keyfile ('%s'): %s", keyfile, rb_mbedtls_strerror(ret));
rb_mbedtls_cfg_decref(newcfg);
return 0;
}
/* Absense of DH parameters does not matter with mbedTLS, as it comes with its own defaults
Thus, clients can still use DHE- ciphersuites, just over a weaker, common DH group
So, we do not consider failure to parse DH parameters as fatal */
if(dhfile == NULL)
{
rb_lib_log("rb_setup_ssl_server: no DH parameters file specified");
}
else
{
if((ret = mbedtls_dhm_parse_dhmfile(&newcfg->dhp, dhfile)) != 0)
{
rb_lib_log("rb_setup_ssl_server: dhm_parse_dhmfile ('%s'): %s",
dhfile, rb_mbedtls_strerror(ret));
}
else if((ret = mbedtls_ssl_conf_dh_param_ctx(&newcfg->server_cfg, &newcfg->dhp)) != 0)
{
rb_lib_log("rb_setup_ssl_server: ssl_conf_dh_param_ctx: %s", rb_mbedtls_strerror(ret));
}
}
if((ret = mbedtls_ssl_conf_own_cert(&newcfg->server_cfg, &newcfg->crt, &newcfg->key)) != 0)
{
rb_lib_log("rb_setup_ssl_server: ssl_conf_own_cert (server): %s", rb_mbedtls_strerror(ret));
rb_mbedtls_cfg_decref(newcfg);
return 0;
}
if((ret = mbedtls_ssl_conf_own_cert(&newcfg->client_cfg, &newcfg->crt, &newcfg->key)) != 0)
{
rb_lib_log("rb_setup_ssl_server: ssl_conf_own_cert (client): %s", rb_mbedtls_strerror(ret));
rb_mbedtls_cfg_decref(newcfg);
return 0;
}
const int *rb_ciphersuites = newcfg->suites;
size_t suites_count = 0;
if(cipherlist != NULL)
{
// The cipherlist is (const char *) -- we should not modify it
char *const cipherlist_dup = strdup(cipherlist);
if(cipherlist_dup != NULL)
{
char *cipher_str = cipherlist_dup;
char *cipher_idx;
do
{
// Arbitrary, but the same separator as OpenSSL uses
cipher_idx = strchr(cipher_str, ':');
// This could legitimately be NULL (last ciphersuite in the list)
if(cipher_idx != NULL)
*cipher_idx = '\0';
size_t cipher_len = strlen(cipher_str);
int cipher_idn = 0;
// All MbedTLS ciphersuite names begin with these 4 characters
if(cipher_len > 4 && strncmp(cipher_str, "TLS-", 4) == 0)
cipher_idn = mbedtls_ssl_get_ciphersuite_id(cipher_str);
// Prevent the same ciphersuite being added multiple times
for(size_t x = 0; cipher_idn != 0 && newcfg->suites[x] != 0; x++)
if(newcfg->suites[x] == cipher_idn)
cipher_idn = 0;
// Add the suite to the list
if(cipher_idn != 0)
newcfg->suites[suites_count++] = cipher_idn;
// Advance the string to the next entry
if(cipher_idx)
cipher_str = cipher_idx + 1;
} while(cipher_idx && suites_count < RB_MAX_CIPHERSUITES);
if(suites_count == 0)
rb_lib_log("rb_setup_ssl_server: Ciphersuites provided, but could not parse any");
free(cipherlist_dup);
}
else
{
rb_lib_log("rb_setup_ssl_server: strdup: %s", strerror(errno));
}
}
else
{
rb_lib_log("rb_setup_ssl_server: No ciphersuite list provided");
}
if(suites_count == 0)
{
rb_lib_log("rb_setup_ssl_server: Using default ciphersuites");
rb_ciphersuites = rb_mbedtls_ciphersuites;
suites_count = (sizeof(rb_mbedtls_ciphersuites) / sizeof(rb_mbedtls_ciphersuites[0])) - 1;
}
mbedtls_ssl_conf_ciphersuites(&newcfg->server_cfg, rb_ciphersuites);
mbedtls_ssl_conf_ciphersuites(&newcfg->client_cfg, rb_ciphersuites);
rb_lib_log("rb_setup_ssl_server: Configured %zu ciphersuites", suites_count);
rb_mbedtls_cfg_decref(rb_mbedtls_cfg);
rb_mbedtls_cfg = newcfg;
rb_lib_log("rb_setup_ssl_server: TLS configuration successful");
return 1;
}
int
rb_init_prng(const char *const path, prng_seed_t seed_type)
{
rb_lib_log("rb_init_prng: Skipping PRNG initialisation; not required by MbedTLS backend");
return 1;
}
int
rb_get_random(void *const buf, size_t length)
{
int ret;
if((ret = mbedtls_ctr_drbg_random(&ctr_drbg_ctx, buf, length)) != 0)
{
rb_lib_log("rb_get_random: ctr_drbg_random: %s", rb_mbedtls_strerror(ret));
return 0;
}
return 1;
}
const char *
rb_get_ssl_strerror(rb_fde_t *const F)
{
return rb_mbedtls_strerror(F->ssl_errno);
}
int
rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], int method)
{
mbedtls_md_type_t md_type;
int hashlen;
switch(method)
{
case RB_SSL_CERTFP_METH_SHA1:
md_type = MBEDTLS_MD_SHA1;
hashlen = RB_SSL_CERTFP_LEN_SHA1;
break;
case RB_SSL_CERTFP_METH_SHA256:
md_type = MBEDTLS_MD_SHA256;
hashlen = RB_SSL_CERTFP_LEN_SHA256;
break;
case RB_SSL_CERTFP_METH_SHA512:
md_type = MBEDTLS_MD_SHA512;
hashlen = RB_SSL_CERTFP_LEN_SHA512;
break;
default:
return 0;
}
const mbedtls_x509_crt *const peer_cert = mbedtls_ssl_get_peer_cert(SSL_P(F));
if(peer_cert == NULL)
return 0;
const mbedtls_md_info_t *const md_info = mbedtls_md_info_from_type(md_type);
if(md_info == NULL)
return 0;
int ret;
if((ret = mbedtls_md(md_info, peer_cert->raw.p, peer_cert->raw.len, certfp)) != 0)
{
rb_lib_log("rb_get_ssl_certfp: mbedtls_md: %s", rb_mbedtls_strerror(ret));
return 0;
}
return hashlen;
}
void
rb_get_ssl_info(char *const buf, size_t len)
{
char version_str[512];
mbedtls_version_get_string(version_str);
(void) rb_snprintf(buf, len, "ARM mbedTLS: compiled (v%s), library (v%s)",
MBEDTLS_VERSION_STRING, version_str);
}
const char *
rb_ssl_get_cipher(rb_fde_t *const F)
{
if(F == NULL || F->ssl == NULL || SSL_P(F) == NULL)
return NULL;
static char buf[512];
const char *const version = mbedtls_ssl_get_version(SSL_P(F));
const char *const cipher = mbedtls_ssl_get_ciphersuite(SSL_P(F));
(void) rb_snprintf(buf, sizeof buf, "%s, %s", version, cipher);
return buf;
}
ssize_t
rb_ssl_read(rb_fde_t *const F, void *const buf, size_t count)
{
lrb_assert(F != NULL);
lrb_assert(F->ssl != NULL);
ssize_t ret = (ssize_t) mbedtls_ssl_read(SSL_P(F), buf, count);
if(ret >= 0)
return ret;
switch(ret)
{
case MBEDTLS_ERR_SSL_WANT_READ:
errno = EAGAIN;
return RB_RW_SSL_NEED_READ;
case MBEDTLS_ERR_SSL_WANT_WRITE:
errno = EAGAIN;
return RB_RW_SSL_NEED_WRITE;
default:
errno = EIO;
F->ssl_errno = ret;
return RB_RW_SSL_ERROR;
}
}
ssize_t
rb_ssl_write(rb_fde_t *const F, const void *const buf, size_t count)
{
lrb_assert(F != NULL);
lrb_assert(F->ssl != NULL);
ssize_t ret = (ssize_t) mbedtls_ssl_write(SSL_P(F), buf, count);
if(ret >= 0)
return ret;
switch(ret)
{
case MBEDTLS_ERR_SSL_WANT_READ:
errno = EAGAIN;
return RB_RW_SSL_NEED_READ;
case MBEDTLS_ERR_SSL_WANT_WRITE:
errno = EAGAIN;
return RB_RW_SSL_NEED_WRITE;
default:
errno = EIO;
F->ssl_errno = ret;
return RB_RW_SSL_ERROR;
}
}
/*
* Internal library-agnostic code
* Mostly copied from the OpenSSL backend, with some optimisations and complete const-correctness
*/
static void
rb_ssl_connect_realcb(rb_fde_t *const F, int status, struct ssl_connect *const sconn)
{
lrb_assert(F->connect != NULL);
F->connect->callback = sconn->callback;
F->connect->data = sconn->data;
rb_connect_callback(F, status);
rb_free(sconn);
}
static void
rb_ssl_timeout_cb(rb_fde_t *const F, void *const data)
{
lrb_assert(F->accept != NULL);
lrb_assert(F->accept->callback != NULL);
F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data);
}
static void
rb_ssl_tryconn_timeout_cb(rb_fde_t *const F, void *const data)
{
rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data);
}
static void
rb_ssl_tryconn(rb_fde_t *const F, int status, void *const data)
{
lrb_assert(F != NULL);
if(status != RB_OK)
{
rb_ssl_connect_realcb(F, status, data);
return;
}
F->type |= RB_FD_SSL;
struct ssl_connect *const sconn = data;
rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT);
rb_ssl_tryconn_cb(F, sconn);
}
static int
rb_sock_net_recv(void *const context_ptr, unsigned char *const buf, size_t count)
{
const int fd = rb_get_fd((rb_fde_t *)context_ptr);
int ret = (int) read(fd, buf, count);
if(ret < 0 && rb_ignore_errno(errno))
return MBEDTLS_ERR_SSL_WANT_READ;
return ret;
}
static int
rb_sock_net_xmit(void *const context_ptr, const unsigned char *const buf, size_t count)
{
const int fd = rb_get_fd((rb_fde_t *)context_ptr);
int ret = (int) write(fd, buf, count);
if(ret < 0 && rb_ignore_errno(errno))
return MBEDTLS_ERR_SSL_WANT_WRITE;
return ret;
}
/*
* External library-agnostic code
* Mostly copied from the OpenSSL backend, with some optimisations and const-correctness
*/
int
rb_supports_ssl(void)
{
return 1;
}
unsigned int
rb_ssl_handshake_count(rb_fde_t *const F)
{
return F->handshake_count;
}
void
rb_ssl_clear_handshake_count(rb_fde_t *const F)
{
F->handshake_count = 0;
}
void
rb_ssl_start_accepted(rb_fde_t *const F, ACCB *const cb, void *const data, int timeout)
{
F->type |= RB_FD_SSL;
F->accept = rb_malloc(sizeof(struct acceptdata));
F->accept->callback = cb;
F->accept->data = data;
F->accept->addrlen = 0;
(void) memset(&F->accept->S, 0x00, sizeof F->accept->S);
rb_settimeout(F, timeout, rb_ssl_timeout_cb, NULL);
rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_IN);
rb_ssl_accept_common(F, NULL);
}
void
rb_ssl_accept_setup(rb_fde_t *const srv_F, rb_fde_t *const cli_F, struct sockaddr *const st, int addrlen)
{
cli_F->type |= RB_FD_SSL;
cli_F->accept = rb_malloc(sizeof(struct acceptdata));
cli_F->accept->callback = srv_F->accept->callback;
cli_F->accept->data = srv_F->accept->data;
cli_F->accept->addrlen = addrlen;
(void) memset(&cli_F->accept->S, 0x00, sizeof cli_F->accept->S);
(void) memcpy(&cli_F->accept->S, st, addrlen);
rb_settimeout(cli_F, 10, rb_ssl_timeout_cb, NULL);
rb_ssl_init_fd(cli_F, RB_FD_TLS_DIRECTION_IN);
rb_ssl_accept_common(cli_F, NULL);
}
int
rb_ssl_listen(rb_fde_t *const F, int backlog, int defer_accept)
{
int result = rb_listen(F, backlog, defer_accept);
F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
return result;
}
void
rb_connect_tcp_ssl(rb_fde_t *const F, struct sockaddr *const dest, struct sockaddr *const clocal,
int socklen, CNCB *const callback, void *const data, int timeout)
{
if(F == NULL)
return;
struct ssl_connect *const sconn = rb_malloc(sizeof *sconn);
sconn->data = data;
sconn->callback = callback;
sconn->timeout = timeout;
rb_connect_tcp(F, dest, clocal, socklen, rb_ssl_tryconn, sconn, timeout);
}
void
rb_ssl_start_connected(rb_fde_t *const F, CNCB *const callback, void *const data, int timeout)
{
if(F == NULL)
return;
F->connect = rb_malloc(sizeof(struct conndata));
F->connect->callback = callback;
F->connect->data = data;
F->type |= RB_FD_SSL;
struct ssl_connect *const sconn = rb_malloc(sizeof *sconn);
sconn->data = data;
sconn->callback = callback;
sconn->timeout = timeout;
rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT);
rb_ssl_tryconn_cb(F, sconn);
}
#endif /* HAVE_MBEDTLS */