[mbedtls] Various fixes and improvements

* Move certificate, key, DH parameters and configuration to heap
  (Documentation states that setting new configuration, e.g.
   during a rehash, is unsupported while connections using that
   configuration are active)

  This is the same approach as the fix for #186

  Refcount these structures so as to not introduce a memory leak

  On rehash, it will use new structures only if there are no
  errors in constructing them

* Add better error-reporting (strings in addition to numbers)
  where possible

* Coalesce several connection memory allocations into one function

* Reduce boilerplate where possible (Charybdis targets C99)

* Support private key being in certificate file, and having no
  DH parameters file

* Correct erroneous closing comment
This commit is contained in:
Aaron Jones 2016-05-04 02:12:23 +00:00
parent d35caf56cb
commit fd5af836b7
No known key found for this signature in database
GPG key ID: 6E854C0FAAD4CEA4

View file

@ -1,10 +1,11 @@
/* /*
* libratbox: a library used by ircd-ratbox and other things * libratbox: a library used by ircd-ratbox and other things
* mbedtls.c: mbedtls related code * mbedtls.c: ARM mbedTLS backend
* *
* Copyright (C) 2007-2008 ircd-ratbox development team * Copyright (C) 2007-2008 ircd-ratbox development team
* Copyright (C) 2007-2008 Aaron Sethman <androsyn@ratbox.org> * Copyright (C) 2007-2008 Aaron Sethman <androsyn@ratbox.org>
* Copyright (C) 2015 William Pitcock <nenolod@dereferenced.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 * 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 * it under the terms of the GNU General Public License as published by
@ -42,29 +43,144 @@
#include "mbedtls/dhm.h" #include "mbedtls/dhm.h"
#include "mbedtls/version.h" #include "mbedtls/version.h"
static mbedtls_x509_crt x509; typedef struct
static mbedtls_pk_context serv_pk; {
static mbedtls_dhm_context dh_params; mbedtls_x509_crt crt;
static mbedtls_ctr_drbg_context ctr_drbg; mbedtls_pk_context key;
static mbedtls_entropy_context entropy; mbedtls_dhm_context dhp;
static mbedtls_ssl_config serv_config; mbedtls_ssl_config server_cfg;
static mbedtls_ssl_config client_config; mbedtls_ssl_config client_cfg;
size_t refcount;
} rb_mbedtls_cfg_context;
#define SSL_P(x) ((mbedtls_ssl_context *)F->ssl) 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 const char librb_personal_str[] = "charybdis/librb personalization string";
static mbedtls_ctr_drbg_context ctr_drbg_ctx;
static mbedtls_entropy_context entropy_ctx;
static rb_mbedtls_cfg_context *rb_mbedtls_cfg = NULL;
static const char *
rb_get_ssl_strerror_internal(int err)
{
static char errbuf[512];
#ifdef MBEDTLS_ERROR_C
char mbed_errbuf[512];
mbedtls_strerror(err, mbed_errbuf, sizeof mbed_errbuf);
rb_snprintf(errbuf, sizeof errbuf, "(-0x%x) %s", -err, mbed_errbuf);
#else
rb_snprintf(errbuf, sizeof errbuf, "-0x%x", -err);
#endif
return errbuf;
}
const char *
rb_get_ssl_strerror(rb_fde_t *F)
{
return rb_get_ssl_strerror_internal(F->ssl_errno);
}
static void rb_mbedtls_cfg_incref(rb_mbedtls_cfg_context *cfg)
{
lrb_assert(cfg->refcount > 0);
cfg->refcount++;
}
static void rb_mbedtls_cfg_decref(rb_mbedtls_cfg_context *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 rb_mbedtls_cfg_context *rb_mbedtls_cfg_new(void)
{
rb_mbedtls_cfg_context *cfg;
int ret;
if((cfg = rb_malloc(sizeof(rb_mbedtls_cfg_context))) == 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);
cfg->refcount = 1;
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_get_ssl_strerror_internal(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_get_ssl_strerror_internal(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_authmode(&cfg->client_cfg, MBEDTLS_SSL_VERIFY_NONE);
return cfg;
}
void void
rb_ssl_shutdown(rb_fde_t *F) rb_ssl_shutdown(rb_fde_t *F)
{ {
int i;
if(F == NULL || F->ssl == NULL) if(F == NULL || F->ssl == NULL)
return; return;
for(i = 0; i < 4; i++)
if(SSL_P(F) != NULL)
{ {
int r = mbedtls_ssl_close_notify(SSL_P(F)); for(int i = 0; i < 4; i++)
if(r != MBEDTLS_ERR_SSL_WANT_READ && r != MBEDTLS_ERR_SSL_WANT_WRITE) {
break; int r = mbedtls_ssl_close_notify(SSL_P(F));
if(r != MBEDTLS_ERR_SSL_WANT_READ && r != MBEDTLS_ERR_SSL_WANT_WRITE)
break;
}
mbedtls_ssl_free(SSL_P(F));
} }
mbedtls_ssl_free(SSL_P(F));
if(SSL_C(F) != NULL)
rb_mbedtls_cfg_decref(SSL_C(F));
rb_free(F->ssl); rb_free(F->ssl);
} }
@ -87,50 +203,46 @@ rb_ssl_timeout(rb_fde_t *F, void *notused)
F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data); F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data);
} }
static int static int
do_ssl_handshake(rb_fde_t *F, PF * callback, void *data) do_ssl_handshake(rb_fde_t *F, PF * callback, void *data)
{ {
int ret; int ret = mbedtls_ssl_handshake(SSL_P(F));
int flags;
ret = mbedtls_ssl_handshake(SSL_P(F)); if(ret == 0)
if(ret < 0)
{ {
if (ret == -1 && rb_ignore_errno(errno)) F->handshake_count++;
ret = MBEDTLS_ERR_SSL_WANT_READ; return 1;
}
if((ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)) if(ret == -1 && rb_ignore_errno(errno))
{ ret = MBEDTLS_ERR_SSL_WANT_READ;
if(ret == MBEDTLS_ERR_SSL_WANT_READ)
flags = RB_SELECT_READ;
else
flags = RB_SELECT_WRITE;
rb_setselect(F, flags, callback, data);
return 0;
}
switch(ret)
{
case MBEDTLS_ERR_SSL_WANT_READ:
rb_setselect(F, RB_SELECT_READ, callback, data);
return 0;
case MBEDTLS_ERR_SSL_WANT_WRITE:
rb_setselect(F, RB_SELECT_WRITE, callback, data);
return 0;
default:
F->ssl_errno = ret; F->ssl_errno = ret;
return -1; return -1;
} }
return 1; /* handshake is finished..go about life */
} }
static void static void
rb_ssl_tryaccept(rb_fde_t *F, void *data) rb_ssl_tryaccept(rb_fde_t *F, void *data)
{ {
int ret;
struct acceptdata *ad;
lrb_assert(F->accept != NULL); lrb_assert(F->accept != NULL);
ret = do_ssl_handshake(F, rb_ssl_tryaccept, NULL); int ret = do_ssl_handshake(F, rb_ssl_tryaccept, NULL);
/* do_ssl_handshake does the rb_setselect */ /* do_ssl_handshake does the rb_setselect */
if(ret == 0) if(ret == 0)
return; return;
ad = F->accept; struct acceptdata *ad = F->accept;
F->accept = NULL; F->accept = NULL;
rb_settimeout(F, 0, NULL, NULL); rb_settimeout(F, 0, NULL, NULL);
rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL); rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
@ -146,11 +258,10 @@ rb_ssl_tryaccept(rb_fde_t *F, void *data)
static int static int
rb_ssl_read_cb(void *opaque, unsigned char *buf, size_t size) rb_ssl_read_cb(void *opaque, unsigned char *buf, size_t size)
{ {
int ret;
rb_fde_t *F = opaque; rb_fde_t *F = opaque;
ret = read(F->fd, buf, size); int ret = (int) read(F->fd, buf, size);
if (ret < 0 && rb_ignore_errno(errno)) if(ret < 0 && rb_ignore_errno(errno))
return MBEDTLS_ERR_SSL_WANT_READ; return MBEDTLS_ERR_SSL_WANT_READ;
return ret; return ret;
@ -160,52 +271,69 @@ static int
rb_ssl_write_cb(void *opaque, const unsigned char *buf, size_t size) rb_ssl_write_cb(void *opaque, const unsigned char *buf, size_t size)
{ {
rb_fde_t *F = opaque; rb_fde_t *F = opaque;
int ret;
ret = write(F->fd, buf, size); int ret = (int) write(F->fd, buf, size);
if (ret < 0 && rb_ignore_errno(errno)) if(ret < 0 && rb_ignore_errno(errno))
return MBEDTLS_ERR_SSL_WANT_WRITE; return MBEDTLS_ERR_SSL_WANT_WRITE;
return ret; return ret;
} }
static void static void
rb_ssl_setup_srv_context(rb_fde_t *F, mbedtls_ssl_context *ssl) rb_ssl_setup_mbed_context(rb_fde_t *F, int is_server)
{ {
rb_mbedtls_ssl_context *mbed_ssl_ctx;
mbedtls_ssl_config *mbed_config;
int ret; int ret;
mbedtls_ssl_init(ssl); if((mbed_ssl_ctx = rb_malloc(sizeof(rb_mbedtls_ssl_context))) == NULL)
if ((ret = mbedtls_ssl_setup(ssl, &serv_config)) != 0)
{ {
rb_lib_log("rb_ssl_setup_srv_context: failed to set up ssl context: -0x%x", -ret); rb_lib_log("rb_ssl_setup_mbed_context: rb_malloc: allocation failure");
rb_close(F); rb_close(F);
return; return;
} }
mbedtls_ssl_set_bio(ssl, F, rb_ssl_write_cb, rb_ssl_read_cb, NULL); if(is_server)
mbed_config = &rb_mbedtls_cfg->server_cfg;
else
mbed_config = &rb_mbedtls_cfg->client_cfg;
mbedtls_ssl_init(&mbed_ssl_ctx->ssl);
mbedtls_ssl_set_bio(&mbed_ssl_ctx->ssl, F, rb_ssl_write_cb, rb_ssl_read_cb, NULL);
if((ret = mbedtls_ssl_setup(&mbed_ssl_ctx->ssl, mbed_config)) != 0)
{
rb_lib_log("rb_ssl_setup_mbed_context: ssl_setup: %s",
rb_get_ssl_strerror_internal(ret));
mbedtls_ssl_free(&mbed_ssl_ctx->ssl);
rb_free(mbed_ssl_ctx);
rb_close(F);
return;
}
mbed_ssl_ctx->cfg = rb_mbedtls_cfg;
rb_mbedtls_cfg_incref(mbed_ssl_ctx->cfg);
F->ssl = mbed_ssl_ctx;
} }
void void
rb_ssl_start_accepted(rb_fde_t *new_F, ACCB * cb, void *data, int timeout) rb_ssl_start_accepted(rb_fde_t *F, ACCB * cb, void *data, int timeout)
{ {
mbedtls_ssl_context *ssl; F->type |= RB_FD_SSL;
new_F->type |= RB_FD_SSL; F->accept = rb_malloc(sizeof(struct acceptdata));
ssl = new_F->ssl = rb_malloc(sizeof(mbedtls_ssl_context));
new_F->accept = rb_malloc(sizeof(struct acceptdata));
new_F->accept->callback = cb; F->accept->callback = cb;
new_F->accept->data = data; F->accept->data = data;
rb_settimeout(new_F, timeout, rb_ssl_timeout, NULL); rb_settimeout(F, timeout, rb_ssl_timeout, NULL);
new_F->accept->addrlen = 0; F->accept->addrlen = 0;
rb_ssl_setup_srv_context(new_F, ssl); rb_ssl_setup_mbed_context(F, 1);
if(do_ssl_handshake(new_F, rb_ssl_tryaccept, NULL)) if(do_ssl_handshake(F, rb_ssl_tryaccept, NULL))
{ {
struct acceptdata *ad = new_F->accept; struct acceptdata *ad = F->accept;
new_F->accept = NULL; F->accept = NULL;
ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
ad->callback(new_F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
rb_free(ad); rb_free(ad);
} }
} }
@ -214,21 +342,20 @@ void
rb_ssl_accept_setup(rb_fde_t *F, rb_fde_t *new_F, struct sockaddr *st, int addrlen) rb_ssl_accept_setup(rb_fde_t *F, rb_fde_t *new_F, struct sockaddr *st, int addrlen)
{ {
new_F->type |= RB_FD_SSL; new_F->type |= RB_FD_SSL;
new_F->ssl = rb_malloc(sizeof(mbedtls_ssl_context));
new_F->accept = rb_malloc(sizeof(struct acceptdata)); new_F->accept = rb_malloc(sizeof(struct acceptdata));
new_F->accept->callback = F->accept->callback; new_F->accept->callback = F->accept->callback;
new_F->accept->data = F->accept->data; new_F->accept->data = F->accept->data;
rb_settimeout(new_F, 10, rb_ssl_timeout, NULL); rb_settimeout(new_F, 10, rb_ssl_timeout, NULL);
memcpy(&new_F->accept->S, st, addrlen); memcpy(&new_F->accept->S, st, addrlen);
new_F->accept->addrlen = addrlen; new_F->accept->addrlen = addrlen;
rb_ssl_setup_srv_context(new_F, new_F->ssl); rb_ssl_setup_mbed_context(new_F, 1);
if(do_ssl_handshake(F, rb_ssl_tryaccept, NULL)) if(do_ssl_handshake(F, rb_ssl_tryaccept, NULL))
{ {
struct acceptdata *ad = F->accept; struct acceptdata *ad = F->accept;
F->accept = NULL; F->accept = NULL;
ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data); ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
rb_free(ad); rb_free(ad);
} }
@ -240,13 +367,13 @@ rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size
ssize_t ret; ssize_t ret;
if(r_or_w == 0) if(r_or_w == 0)
ret = mbedtls_ssl_read(F->ssl, rbuf, count); ret = mbedtls_ssl_read(SSL_P(F), rbuf, count);
else else
ret = mbedtls_ssl_write(F->ssl, wbuf, count); ret = mbedtls_ssl_write(SSL_P(F), wbuf, count);
if(ret < 0) if(ret < 0)
{ {
switch (ret) switch(ret)
{ {
case MBEDTLS_ERR_SSL_WANT_READ: case MBEDTLS_ERR_SSL_WANT_READ:
return RB_RW_SSL_NEED_READ; return RB_RW_SSL_NEED_READ;
@ -279,112 +406,109 @@ rb_init_ssl(void)
{ {
int ret; int ret;
mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_init(&ctr_drbg_ctx);
mbedtls_ctr_drbg_init(&ctr_drbg); mbedtls_entropy_init(&entropy_ctx);
if((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0)) != 0) if((ret = mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, mbedtls_entropy_func, &entropy_ctx,
(const unsigned char *)librb_personal_str, sizeof(librb_personal_str))) != 0)
{ {
rb_lib_log("rb_init_prng: unable to initialize PRNG, mbedtls_ctr_drbg_seed() returned -0x%x", -ret); rb_lib_log("rb_init_ssl: ctr_drbg_seed: %s",
rb_get_ssl_strerror_internal(ret));
return 0; return 0;
} }
mbedtls_ssl_config_init(&serv_config);
if ((ret = mbedtls_ssl_config_defaults(&serv_config,
MBEDTLS_SSL_IS_SERVER,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
{
rb_lib_log("rb_init_ssl: unable to initialize default SSL parameters for server context: -0x%x", -ret);
return 0;
}
mbedtls_ssl_conf_rng(&serv_config, mbedtls_ctr_drbg_random, &ctr_drbg);
/***************************************************************************************************************/
mbedtls_ssl_config_init(&client_config);
if ((ret = mbedtls_ssl_config_defaults(&client_config,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
{
rb_lib_log("rb_init_ssl: unable to initialize default SSL parameters for client context: -0x%x", -ret);
return 0;
}
mbedtls_ssl_conf_rng(&client_config, mbedtls_ctr_drbg_random, &ctr_drbg);
mbedtls_ssl_conf_authmode(&client_config, MBEDTLS_SSL_VERIFY_NONE);
return 1; return 1;
} }
int int
rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile, const char *cipher_list) rb_setup_ssl_server(const char *certfile, const char *keyfile, const char *dhfile, const char *cipher_list)
{ {
rb_mbedtls_cfg_context *newcfg;
int ret; int ret;
mbedtls_x509_crt_init(&x509); if(certfile == NULL)
ret = mbedtls_x509_crt_parse_file(&x509, cert);
if (ret != 0)
{ {
rb_lib_log("rb_setup_ssl_server: failed to parse certificate '%s': -0x%x", cert, -ret); rb_lib_log("rb_setup_ssl_server: no certificate file specified");
return 0; return 0;
} }
mbedtls_pk_init(&serv_pk); if(keyfile == NULL)
ret = mbedtls_pk_parse_keyfile(&serv_pk, keyfile, NULL); keyfile = certfile;
if (ret != 0)
if((newcfg = rb_mbedtls_cfg_new()) == NULL)
{ {
rb_lib_log("rb_setup_ssl_server: failed to parse private key '%s': -0x%x", keyfile, -ret); rb_lib_log("rb_setup_ssl_server: rb_mbedtls_cfg_new: allocation failed");
return 0; return 0;
} }
mbedtls_dhm_init(&dh_params); if((ret = mbedtls_x509_crt_parse_file(&newcfg->crt, certfile)) != 0)
ret = mbedtls_dhm_parse_dhmfile(&dh_params, dhfile);
if (ret != 0)
{ {
rb_lib_log("rb_setup_ssl_server: failed to parse DH parameters '%s': -0x%x", dhfile, -ret); rb_lib_log("rb_setup_ssl_server: x509_crt_parse_file ('%s'): %s",
certfile, rb_get_ssl_strerror_internal(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_get_ssl_strerror_internal(ret));
rb_mbedtls_cfg_decref(newcfg);
return 0; return 0;
} }
ret = mbedtls_ssl_conf_dh_param_ctx(&serv_config, &dh_params); /* Absense of DH parameters does not matter with mbedTLS, as it comes with its own defaults
if (ret != 0) 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: failed to set DH parameters on SSL config context: -0x%x", -ret); rb_lib_log("rb_setup_ssl_server: no DH parameters file specified");
return 0; }
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_get_ssl_strerror_internal(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_get_ssl_strerror_internal(ret));
}
} }
if (x509.next) if((ret = mbedtls_ssl_conf_own_cert(&newcfg->server_cfg, &newcfg->crt, &newcfg->key)) != 0)
{ {
mbedtls_ssl_conf_ca_chain(&serv_config, x509.next, NULL); rb_lib_log("rb_setup_ssl_server: ssl_conf_own_cert (server): %s",
mbedtls_ssl_conf_ca_chain(&client_config, x509.next, NULL); rb_get_ssl_strerror_internal(ret));
} rb_mbedtls_cfg_decref(newcfg);
if ((ret = mbedtls_ssl_conf_own_cert(&serv_config, &x509, &serv_pk)) != 0)
{
rb_lib_log("rb_setup_ssl_server: failed to set up own certificate: -0x%x", -ret);
return 0; return 0;
} }
if((ret = mbedtls_ssl_conf_own_cert(&newcfg->client_cfg, &newcfg->crt, &newcfg->key)) != 0)
if ((ret = mbedtls_ssl_conf_own_cert(&client_config, &x509, &serv_pk)) != 0)
{ {
rb_lib_log("rb_setup_ssl_server: failed to set up own certificate: -0x%x", -ret); rb_lib_log("rb_setup_ssl_server: ssl_conf_own_cert (client): %s",
rb_get_ssl_strerror_internal(ret));
rb_mbedtls_cfg_decref(newcfg);
return 0; return 0;
} }
if(newcfg->crt.next)
{
mbedtls_ssl_conf_ca_chain(&newcfg->server_cfg, newcfg->crt.next, NULL);
mbedtls_ssl_conf_ca_chain(&newcfg->client_cfg, newcfg->crt.next, NULL);
}
/* XXX support cipher lists when added to mbedtls */ /* XXX support cipher lists when added to mbedtls */
rb_mbedtls_cfg_decref(rb_mbedtls_cfg);
rb_mbedtls_cfg = newcfg;
return 1; return 1;
} }
int int
rb_ssl_listen(rb_fde_t *F, int backlog, int defer_accept) rb_ssl_listen(rb_fde_t *F, int backlog, int defer_accept)
{ {
int result; int result = rb_listen(F, backlog, defer_accept);
result = rb_listen(F, backlog, defer_accept);
F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL; F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
return result; return result;
@ -415,99 +539,72 @@ rb_ssl_tryconn_timeout_cb(rb_fde_t *F, void *data)
static void static void
rb_ssl_tryconn_cb(rb_fde_t *F, void *data) rb_ssl_tryconn_cb(rb_fde_t *F, void *data)
{ {
struct ssl_connect *sconn = data; int ret = do_ssl_handshake(F, rb_ssl_tryconn_cb, data);
int ret;
ret = do_ssl_handshake(F, rb_ssl_tryconn_cb, (void *)sconn); switch(ret)
switch (ret)
{ {
case -1: case -1:
rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn); rb_ssl_connect_realcb(F, RB_ERROR_SSL, data);
break; break;
case 0: case 0:
/* do_ssl_handshake does the rb_setselect stuff */ /* do_ssl_handshake does the rb_setselect stuff */
return; return;
default: default:
break; break;
} }
rb_ssl_connect_realcb(F, RB_OK, sconn); rb_ssl_connect_realcb(F, RB_OK, data);
}
static void
rb_ssl_setup_client_context(rb_fde_t *F, mbedtls_ssl_context *ssl)
{
int ret;
mbedtls_ssl_init(ssl);
if ((ret = mbedtls_ssl_setup(ssl, &client_config)) != 0)
{
rb_lib_log("rb_ssl_setup_client_context: failed to set up ssl context: -0x%x", -ret);
rb_close(F);
return;
}
mbedtls_ssl_set_bio(ssl, F, rb_ssl_write_cb, rb_ssl_read_cb, NULL);
} }
static void static void
rb_ssl_tryconn(rb_fde_t *F, int status, void *data) rb_ssl_tryconn(rb_fde_t *F, int status, void *data)
{ {
struct ssl_connect *sconn = data;
if(status != RB_OK) if(status != RB_OK)
{ {
rb_ssl_connect_realcb(F, status, sconn); rb_ssl_connect_realcb(F, status, data);
return; return;
} }
F->type |= RB_FD_SSL; F->type |= RB_FD_SSL;
rb_ssl_setup_mbed_context(F, 0);
rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); rb_settimeout(F, ((struct ssl_connect *)data)->timeout, rb_ssl_tryconn_timeout_cb, data);
F->ssl = rb_malloc(sizeof(mbedtls_ssl_context)); do_ssl_handshake(F, rb_ssl_tryconn_cb, data);
rb_ssl_setup_client_context(F, F->ssl);
do_ssl_handshake(F, rb_ssl_tryconn_cb, (void *)sconn);
} }
void void
rb_connect_tcp_ssl(rb_fde_t *F, struct sockaddr *dest, rb_connect_tcp_ssl(rb_fde_t *F, struct sockaddr *dest,
struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout) struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout)
{ {
struct ssl_connect *sconn;
if(F == NULL) if(F == NULL)
return; return;
sconn = rb_malloc(sizeof(struct ssl_connect)); struct ssl_connect *sconn = rb_malloc(sizeof(struct ssl_connect));
sconn->data = data; sconn->data = data;
sconn->callback = callback; sconn->callback = callback;
sconn->timeout = timeout; sconn->timeout = timeout;
rb_connect_tcp(F, dest, clocal, socklen, rb_ssl_tryconn, sconn, timeout); rb_connect_tcp(F, dest, clocal, socklen, rb_ssl_tryconn, sconn, timeout);
} }
void void
rb_ssl_start_connected(rb_fde_t *F, CNCB * callback, void *data, int timeout) rb_ssl_start_connected(rb_fde_t *F, CNCB * callback, void *data, int timeout)
{ {
struct ssl_connect *sconn;
if(F == NULL) if(F == NULL)
return; return;
sconn = rb_malloc(sizeof(struct ssl_connect)); struct ssl_connect *sconn = rb_malloc(sizeof(struct ssl_connect));
sconn->data = data; sconn->data = data;
sconn->callback = callback; sconn->callback = callback;
sconn->timeout = timeout; sconn->timeout = timeout;
F->connect = rb_malloc(sizeof(struct conndata)); F->connect = rb_malloc(sizeof(struct conndata));
F->connect->callback = callback; F->connect->callback = callback;
F->connect->data = data; F->connect->data = data;
F->type |= RB_FD_SSL; F->type |= RB_FD_SSL;
F->ssl = rb_malloc(sizeof(mbedtls_ssl_context));
rb_ssl_setup_client_context(F, F->ssl); rb_ssl_setup_mbed_context(F, 0);
rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
do_ssl_handshake(F, rb_ssl_tryconn_cb, sconn);
do_ssl_handshake(F, rb_ssl_tryconn_cb, (void *)sconn);
} }
int int
@ -519,35 +616,22 @@ rb_init_prng(const char *path, prng_seed_t seed_type)
int int
rb_get_random(void *buf, size_t length) rb_get_random(void *buf, size_t length)
{ {
if (mbedtls_ctr_drbg_random(&ctr_drbg, buf, length)) if(mbedtls_ctr_drbg_random(&ctr_drbg_ctx, buf, length))
return 0; return 0;
return 1; return 1;
} }
const char *
rb_get_ssl_strerror(rb_fde_t *F)
{
#ifdef MBEDTLS_ERROR_C
static char errbuf[512];
mbedtls_strerror(F->ssl_errno, errbuf, sizeof errbuf);
return errbuf;
#else
return "???";
#endif
}
int int
rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method) rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
{ {
const mbedtls_x509_crt *peer_cert; const mbedtls_x509_crt *peer_cert;
uint8_t hash[RB_SSL_CERTFP_LEN];
size_t hashlen;
const mbedtls_md_info_t *md_info; const mbedtls_md_info_t *md_info;
mbedtls_md_type_t md_type; mbedtls_md_type_t md_type;
int hashlen;
int ret; int ret;
switch (method) switch(method)
{ {
case RB_SSL_CERTFP_METH_SHA1: case RB_SSL_CERTFP_METH_SHA1:
md_type = MBEDTLS_MD_SHA1; md_type = MBEDTLS_MD_SHA1;
@ -565,23 +649,20 @@ rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
return 0; return 0;
} }
peer_cert = mbedtls_ssl_get_peer_cert(SSL_P(F)); if((peer_cert = mbedtls_ssl_get_peer_cert(SSL_P(F))) == NULL)
if (peer_cert == NULL)
return 0; return 0;
md_info = mbedtls_md_info_from_type(md_type); if((md_info = mbedtls_md_info_from_type(md_type)) == NULL)
if (md_info == NULL)
return 0; return 0;
if ((ret = mbedtls_md(md_info, peer_cert->raw.p, peer_cert->raw.len, hash)) != 0) if((ret = mbedtls_md(md_info, peer_cert->raw.p, peer_cert->raw.len, certfp)) != 0)
{ {
rb_lib_log("rb_get_ssl_certfp: unable to get certfp for F: %p, -0x%x", F, -ret); rb_lib_log("rb_get_ssl_certfp: mbedtls_md: %s",
rb_get_ssl_strerror_internal(ret));
return 0; return 0;
} }
memcpy(certfp, hash, hashlen); return hashlen;
return (int) hashlen;
} }
int int
@ -603,9 +684,10 @@ rb_get_ssl_info(char *buf, size_t len)
const char * const char *
rb_ssl_get_cipher(rb_fde_t *F) rb_ssl_get_cipher(rb_fde_t *F)
{ {
if(F == NULL || F->ssl == NULL) if(F == NULL || F->ssl == NULL || SSL_P(F) == NULL)
return NULL; return NULL;
return mbedtls_ssl_get_ciphersuite(SSL_P(F)); return mbedtls_ssl_get_ciphersuite(SSL_P(F));
} }
#endif /* HAVE_GNUTLS */ #endif /* HAVE_MBEDTLS */