]> jfr.im git - solanum.git/blobdiff - librb/src/mbedtls.c
mbedtls backend: indicate reason for TLS session termination
[solanum.git] / librb / src / mbedtls.c
index cbedbb9baed864d64d363a87b3c241ee4e3b82aa..a8471fdcc3ab6eeed7683009678a7010715d2141 100644 (file)
@@ -1,10 +1,11 @@
 /*
  *  librb: 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 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
 #include "mbedtls/dhm.h"
 #include "mbedtls/version.h"
 
-static mbedtls_x509_crt x509;
-static mbedtls_pk_context serv_pk;
-static mbedtls_dhm_context dh_params;
-static mbedtls_ctr_drbg_context ctr_drbg;
-static mbedtls_entropy_context entropy;
-static mbedtls_ssl_config serv_config;
-static mbedtls_ssl_config client_config;
+#include "mbedtls_embedded_data.h"
 
-#define SSL_P(x) ((mbedtls_ssl_context *)F->ssl)
+typedef struct
+{
+       mbedtls_x509_crt         crt;
+       mbedtls_pk_context       key;
+       mbedtls_dhm_context      dhp;
+       mbedtls_ssl_config       server_cfg;
+       mbedtls_ssl_config       client_cfg;
+       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;
+
+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);
+       snprintf(errbuf, sizeof errbuf, "(-0x%x) %s", -err, mbed_errbuf);
+#else
+       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_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);
+
+       return cfg;
+}
 
 void
 rb_ssl_shutdown(rb_fde_t *F)
 {
-       int i;
        if(F == NULL || F->ssl == NULL)
                return;
-       for(i = 0; i < 4; i++)
+
+       if(SSL_P(F) != NULL)
        {
-               int r = mbedtls_ssl_close_notify(SSL_P(F));
-               if(r != MBEDTLS_ERR_SSL_WANT_READ && r != MBEDTLS_ERR_SSL_WANT_WRITE)
-                       break;
+               for(int i = 0; i < 4; i++)
+               {
+                       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);
 }
 
@@ -87,50 +208,46 @@ rb_ssl_timeout(rb_fde_t *F, void *notused)
        F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data);
 }
 
-
 static int
 do_ssl_handshake(rb_fde_t *F, PF * callback, void *data)
 {
-       int ret;
-       int flags;
+       int ret = mbedtls_ssl_handshake(SSL_P(F));
 
-       ret = mbedtls_ssl_handshake(SSL_P(F));
-       if(ret < 0)
+       if(ret == 0)
        {
-               if (ret == -1 && rb_ignore_errno(errno))
-                       ret = MBEDTLS_ERR_SSL_WANT_READ;
+               F->handshake_count++;
+               return 1;
+       }
 
-               if((ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE))
-               {
-                       if(ret == MBEDTLS_ERR_SSL_WANT_READ)
-                               flags = RB_SELECT_READ;
-                       else
-                               flags = RB_SELECT_WRITE;
-                       rb_setselect(F, flags, callback, data);
-                       return 0;
-               }
+       if(ret == -1 && rb_ignore_errno(errno))
+               ret = MBEDTLS_ERR_SSL_WANT_READ;
 
+       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;
                return -1;
        }
-       return 1;               /* handshake is finished..go about life */
 }
 
 static void
 rb_ssl_tryaccept(rb_fde_t *F, void *data)
 {
-       int ret;
-       struct acceptdata *ad;
-
        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 */
        if(ret == 0)
                return;
 
-       ad = F->accept;
+       struct acceptdata *ad = F->accept;
        F->accept = NULL;
        rb_settimeout(F, 0, NULL, NULL);
        rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
@@ -146,11 +263,10 @@ rb_ssl_tryaccept(rb_fde_t *F, void *data)
 static int
 rb_ssl_read_cb(void *opaque, unsigned char *buf, size_t size)
 {
-       int ret;
        rb_fde_t *F = opaque;
 
-       ret = read(F->fd, buf, size);
-       if (ret < 0 && rb_ignore_errno(errno))
+       int ret = (int) read(F->fd, buf, size);
+       if(ret < 0 && rb_ignore_errno(errno))
                return MBEDTLS_ERR_SSL_WANT_READ;
 
        return ret;
@@ -160,52 +276,69 @@ static int
 rb_ssl_write_cb(void *opaque, const unsigned char *buf, size_t size)
 {
        rb_fde_t *F = opaque;
-       int ret;
 
-       ret = write(F->fd, buf, size);
-       if (ret < 0 && rb_ignore_errno(errno))
+       int ret = (int) write(F->fd, buf, size);
+       if(ret < 0 && rb_ignore_errno(errno))
                return MBEDTLS_ERR_SSL_WANT_WRITE;
 
        return ret;
 }
 
 static void
-rb_ssl_setup_srv_context(rb_fde_t *F, mbedtls_ssl_context *ssl)
+rb_ssl_setup_mbed_context(rb_fde_t *F, bool is_server)
 {
+       rb_mbedtls_ssl_context *mbed_ssl_ctx;
+       mbedtls_ssl_config *mbed_config;
        int ret;
 
-       mbedtls_ssl_init(ssl);
-       if ((ret = mbedtls_ssl_setup(ssl, &serv_config)) != 0)
+       if((mbed_ssl_ctx = rb_malloc(sizeof(rb_mbedtls_ssl_context))) == NULL)
+       {
+               rb_lib_log("rb_ssl_setup_mbed_context: rb_malloc: allocation failure");
+               rb_close(F);
+               return;
+       }
+
+       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_srv_context: failed to set up ssl context: -0x%x", -ret);
+               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;
        }
 
-       mbedtls_ssl_set_bio(ssl, F, rb_ssl_write_cb, rb_ssl_read_cb, NULL);
+       mbed_ssl_ctx->cfg = rb_mbedtls_cfg;
+       rb_mbedtls_cfg_incref(mbed_ssl_ctx->cfg);
+       F->ssl = mbed_ssl_ctx;
 }
 
 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;
-       new_F->type |= RB_FD_SSL;
-       ssl = new_F->ssl = rb_malloc(sizeof(mbedtls_ssl_context));
-       new_F->accept = rb_malloc(sizeof(struct acceptdata));
+       F->type |= RB_FD_SSL;
+       F->accept = rb_malloc(sizeof(struct acceptdata));
 
-       new_F->accept->callback = cb;
-       new_F->accept->data = data;
-       rb_settimeout(new_F, timeout, rb_ssl_timeout, NULL);
+       F->accept->callback = cb;
+       F->accept->data = data;
+       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);
-       if(do_ssl_handshake(new_F, rb_ssl_tryaccept, NULL))
+       rb_ssl_setup_mbed_context(F, true);
+       if(do_ssl_handshake(F, rb_ssl_tryaccept, NULL))
        {
-               struct acceptdata *ad = new_F->accept;
-               new_F->accept = NULL;
-
-               ad->callback(new_F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
+               struct acceptdata *ad = F->accept;
+               F->accept = NULL;
+               ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
                rb_free(ad);
        }
 }
@@ -214,39 +347,38 @@ void
 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->ssl = rb_malloc(sizeof(mbedtls_ssl_context));
        new_F->accept = rb_malloc(sizeof(struct acceptdata));
 
        new_F->accept->callback = F->accept->callback;
        new_F->accept->data = F->accept->data;
        rb_settimeout(new_F, 10, rb_ssl_timeout, NULL);
+
        memcpy(&new_F->accept->S, st, addrlen);
        new_F->accept->addrlen = addrlen;
 
-       rb_ssl_setup_srv_context(new_F, new_F->ssl);
+       rb_ssl_setup_mbed_context(new_F, true);
        if(do_ssl_handshake(F, rb_ssl_tryaccept, NULL))
        {
                struct acceptdata *ad = F->accept;
                F->accept = NULL;
-
                ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
                rb_free(ad);
        }
 }
 
 static ssize_t
-rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size_t count)
+rb_ssl_read_or_write(bool do_read, rb_fde_t *F, void *rbuf, const void *wbuf, size_t count)
 {
        ssize_t ret;
 
-       if(r_or_w == 0)
-               ret = mbedtls_ssl_read(F->ssl, rbuf, count);
+       if(do_read)
+               ret = mbedtls_ssl_read(SSL_P(F), rbuf, count);
        else
-               ret = mbedtls_ssl_write(F->ssl, wbuf, count);
+               ret = mbedtls_ssl_write(SSL_P(F), wbuf, count);
 
        if(ret < 0)
        {
-               switch (ret)
+               switch(ret)
                {
                case MBEDTLS_ERR_SSL_WANT_READ:
                        return RB_RW_SSL_NEED_READ;
@@ -255,7 +387,7 @@ rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size
                default:
                        F->ssl_errno = ret;
                        errno = EIO;
-                       return RB_RW_IO_ERROR;
+                       return RB_RW_SSL_ERROR;
                }
        }
 
@@ -265,13 +397,13 @@ rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size
 ssize_t
 rb_ssl_read(rb_fde_t *F, void *buf, size_t count)
 {
-       return rb_ssl_read_or_write(0, F, buf, NULL, count);
+       return rb_ssl_read_or_write(true, F, buf, NULL, count);
 }
 
 ssize_t
 rb_ssl_write(rb_fde_t *F, const void *buf, size_t count)
 {
-       return rb_ssl_read_or_write(1, F, NULL, buf, count);
+       return rb_ssl_read_or_write(false, F, NULL, buf, count);
 }
 
 int
@@ -279,112 +411,112 @@ rb_init_ssl(void)
 {
        int ret;
 
-       mbedtls_entropy_init(&entropy);
-       mbedtls_ctr_drbg_init(&ctr_drbg);
-
-       if((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0)) != 0)
-       {
-               rb_lib_log("rb_init_prng: unable to initialize PRNG, mbedtls_ctr_drbg_seed() returned -0x%x", -ret);
-               return 0;
-       }
-
-       mbedtls_ssl_config_init(&serv_config);
+       mbedtls_ctr_drbg_init(&ctr_drbg_ctx);
+       mbedtls_entropy_init(&entropy_ctx);
 
-       if ((ret = mbedtls_ssl_config_defaults(&serv_config,
-               MBEDTLS_SSL_IS_SERVER,
-               MBEDTLS_SSL_TRANSPORT_STREAM,
-               MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
+       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: unable to initialize default SSL parameters for server context: -0x%x", -ret);
+               rb_lib_log("rb_init_ssl: ctr_drbg_seed: %s",
+                          rb_get_ssl_strerror_internal(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)
+       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: unable to initialize default SSL parameters for client context: -0x%x", -ret);
+               rb_lib_log("rb_init_ssl: x509_crt_parse_der (Dummy CA): %s",
+                          rb_get_ssl_strerror_internal(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;
 }
 
 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;
 
-       mbedtls_x509_crt_init(&x509);
-       ret = mbedtls_x509_crt_parse_file(&x509, cert);
-       if (ret != 0)
+       if(certfile == NULL)
        {
-               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;
        }
 
-       mbedtls_pk_init(&serv_pk);
-       ret = mbedtls_pk_parse_keyfile(&serv_pk, keyfile, NULL);
-       if (ret != 0)
+       if(keyfile == NULL)
+               keyfile = certfile;
+
+       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;
        }
 
-       mbedtls_dhm_init(&dh_params);
-       ret = mbedtls_dhm_parse_dhmfile(&dh_params, dhfile);
-       if (ret != 0)
+       if((ret = mbedtls_x509_crt_parse_file(&newcfg->crt, certfile)) != 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;
        }
-
-       ret = mbedtls_ssl_conf_dh_param_ctx(&serv_config, &dh_params);
-       if (ret != 0)
+       if((ret = mbedtls_pk_parse_keyfile(&newcfg->key, keyfile, NULL)) != 0)
        {
-               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: pk_parse_keyfile ('%s'): %s",
+                          keyfile, rb_get_ssl_strerror_internal(ret));
+               rb_mbedtls_cfg_decref(newcfg);
                return 0;
        }
 
-       if (x509.next)
+       /* 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
        {
-               mbedtls_ssl_conf_ca_chain(&serv_config, x509.next, NULL);
-               mbedtls_ssl_conf_ca_chain(&client_config, x509.next, NULL);
+               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 ((ret = mbedtls_ssl_conf_own_cert(&serv_config, &x509, &serv_pk)) != 0)
+       if((ret = mbedtls_ssl_conf_own_cert(&newcfg->server_cfg, &newcfg->crt, &newcfg->key)) != 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 (server): %s",
+                          rb_get_ssl_strerror_internal(ret));
+               rb_mbedtls_cfg_decref(newcfg);
                return 0;
        }
-
-       if ((ret = mbedtls_ssl_conf_own_cert(&client_config, &x509, &serv_pk)) != 0)
+       if((ret = mbedtls_ssl_conf_own_cert(&newcfg->client_cfg, &newcfg->crt, &newcfg->key)) != 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;
        }
 
        /* XXX support cipher lists when added to mbedtls */
 
+       rb_mbedtls_cfg_decref(rb_mbedtls_cfg);
+       rb_mbedtls_cfg = newcfg;
+
        return 1;
 }
 
 int
 rb_ssl_listen(rb_fde_t *F, int backlog, int defer_accept)
 {
-       int result;
-
-       result = rb_listen(F, backlog, defer_accept);
+       int result = rb_listen(F, backlog, defer_accept);
        F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
 
        return result;
@@ -415,99 +547,72 @@ rb_ssl_tryconn_timeout_cb(rb_fde_t *F, void *data)
 static void
 rb_ssl_tryconn_cb(rb_fde_t *F, void *data)
 {
-       struct ssl_connect *sconn = data;
-       int ret;
+       int ret = do_ssl_handshake(F, rb_ssl_tryconn_cb, data);
 
-       ret = do_ssl_handshake(F, rb_ssl_tryconn_cb, (void *)sconn);
-
-       switch (ret)
+       switch(ret)
        {
        case -1:
-               rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
+               rb_ssl_connect_realcb(F, RB_ERROR_SSL, data);
                break;
        case 0:
                /* do_ssl_handshake does the rb_setselect stuff */
                return;
        default:
                break;
-
-
        }
-       rb_ssl_connect_realcb(F, RB_OK, sconn);
-}
-
-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);
+       rb_ssl_connect_realcb(F, RB_OK, data);
 }
 
 static void
 rb_ssl_tryconn(rb_fde_t *F, int status, void *data)
 {
-       struct ssl_connect *sconn = data;
        if(status != RB_OK)
        {
-               rb_ssl_connect_realcb(F, status, sconn);
+               rb_ssl_connect_realcb(F, status, data);
                return;
        }
 
        F->type |= RB_FD_SSL;
 
-
-       rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
-       F->ssl = rb_malloc(sizeof(mbedtls_ssl_context));
-       rb_ssl_setup_client_context(F, F->ssl);
-
-       do_ssl_handshake(F, rb_ssl_tryconn_cb, (void *)sconn);
+       rb_ssl_setup_mbed_context(F, false);
+       rb_settimeout(F, ((struct ssl_connect *)data)->timeout, rb_ssl_tryconn_timeout_cb, data);
+       do_ssl_handshake(F, rb_ssl_tryconn_cb, data);
 }
 
 void
 rb_connect_tcp_ssl(rb_fde_t *F, struct sockaddr *dest,
                   struct sockaddr *clocal, CNCB * callback, void *data, int timeout)
 {
-       struct ssl_connect *sconn;
        if(F == NULL)
                return;
 
-       sconn = rb_malloc(sizeof(struct ssl_connect));
+       struct ssl_connect *sconn = rb_malloc(sizeof(struct ssl_connect));
        sconn->data = data;
        sconn->callback = callback;
        sconn->timeout = timeout;
+
        rb_connect_tcp(F, dest, clocal, rb_ssl_tryconn, sconn, timeout);
 }
 
 void
 rb_ssl_start_connected(rb_fde_t *F, CNCB * callback, void *data, int timeout)
 {
-       struct ssl_connect *sconn;
        if(F == NULL)
                return;
 
-       sconn = rb_malloc(sizeof(struct ssl_connect));
+       struct ssl_connect *sconn = rb_malloc(sizeof(struct ssl_connect));
        sconn->data = data;
        sconn->callback = callback;
        sconn->timeout = timeout;
+
        F->connect = rb_malloc(sizeof(struct conndata));
        F->connect->callback = callback;
        F->connect->data = data;
        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, false);
        rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
-
-       do_ssl_handshake(F, rb_ssl_tryconn_cb, (void *)sconn);
+       do_ssl_handshake(F, rb_ssl_tryconn_cb, sconn);
 }
 
 int
@@ -519,89 +624,74 @@ rb_init_prng(const char *path, prng_seed_t seed_type)
 int
 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 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
-}
-
 static size_t
-make_certfp(const mbedtls_x509_crt *peer_cert, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
+rb_make_certfp(const mbedtls_x509_crt *peer_cert, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
 {
        const mbedtls_md_info_t *md_info;
        mbedtls_md_type_t md_type;
        bool spki = false;
+       size_t hashlen;
        int ret;
-       int len;
 
-       switch (method)
+       uint8_t der_pubkey[8192];
+       void* data = peer_cert->raw.p;
+       size_t datalen = peer_cert->raw.len;
+
+       switch(method)
        {
        case RB_SSL_CERTFP_METH_CERT_SHA1:
                md_type = MBEDTLS_MD_SHA1;
-               len = RB_SSL_CERTFP_LEN_SHA1;
+               hashlen = RB_SSL_CERTFP_LEN_SHA1;
                break;
+
        case RB_SSL_CERTFP_METH_SPKI_SHA256:
                spki = true;
        case RB_SSL_CERTFP_METH_CERT_SHA256:
                md_type = MBEDTLS_MD_SHA256;
-               len = RB_SSL_CERTFP_LEN_SHA256;
+               hashlen = RB_SSL_CERTFP_LEN_SHA256;
                break;
+
        case RB_SSL_CERTFP_METH_SPKI_SHA512:
                spki = true;
        case RB_SSL_CERTFP_METH_CERT_SHA512:
                md_type = MBEDTLS_MD_SHA512;
-               len = RB_SSL_CERTFP_LEN_SHA512;
+               hashlen = RB_SSL_CERTFP_LEN_SHA512;
                break;
+
        default:
                return 0;
        }
 
-       md_info = mbedtls_md_info_from_type(md_type);
-       if (md_info == NULL)
+       if((md_info = mbedtls_md_info_from_type(md_type)) == NULL)
                return 0;
 
-       if (!spki)
+       if(spki)
        {
-               if ((ret = mbedtls_md(md_info, peer_cert->raw.p, peer_cert->raw.len, certfp)) != 0)
+               if ((ret = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)&peer_cert->pk,
+                                                      der_pubkey, sizeof(der_pubkey))) < 0)
                {
-                       rb_lib_log("rb_get_ssl_certfp: unable to calculate certfp: -0x%x", -ret);
-                       len = 0;
+                       rb_lib_log("rb_get_ssl_certfp: pk_write_pubkey_der: %s",
+                                  rb_get_ssl_strerror_internal(ret));
+                       return 0;
                }
+               data = der_pubkey + (sizeof(der_pubkey) - ret);
+               datalen = ret;
        }
-       else
-       {
-               const size_t der_pubkey_bufsz = 4096;
-               void *der_pubkey = rb_malloc(der_pubkey_bufsz);
-               int der_pubkey_len;
 
-               der_pubkey_len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)&peer_cert->pk, der_pubkey, der_pubkey_bufsz);
-               if (der_pubkey_len < 0)
-               {
-                       rb_lib_log("rb_get_ssl_certfp: unable to retrieve pubkey: -0x%x", -der_pubkey_len);
-                       len = 0;
-               }
-               else if ((ret = mbedtls_md(md_info, der_pubkey+(der_pubkey_bufsz-der_pubkey_len), der_pubkey_len, certfp)) != 0)
-               {
-                       rb_lib_log("rb_get_ssl_certfp: unable to calculate certfp: -0x%x", -ret);
-                       len = 0;
-               }
-
-               rb_free(der_pubkey);
+       if((ret = mbedtls_md(md_info, data, datalen, certfp)) != 0)
+       {
+               rb_lib_log("rb_get_ssl_certfp: mbedtls_md: %s",
+                          rb_get_ssl_strerror_internal(ret));
+               return 0;
        }
 
-       return len;
+       return hashlen;
 }
 
 int
@@ -609,11 +699,10 @@ rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
 {
        const mbedtls_x509_crt *peer_cert;
 
-       peer_cert = mbedtls_ssl_get_peer_cert(SSL_P(F));
-       if (peer_cert == NULL)
+       if ((peer_cert = mbedtls_ssl_get_peer_cert(SSL_P(F))) == NULL)
                return 0;
 
-       return make_certfp(peer_cert, certfp, method);
+       return (int) rb_make_certfp(peer_cert, certfp, method);
 }
 
 int
@@ -624,11 +713,14 @@ rb_get_ssl_certfp_file(const char *filename, uint8_t certfp[RB_SSL_CERTFP_LEN],
 
        mbedtls_x509_crt_init(&cert);
 
-       ret = mbedtls_x509_crt_parse_file(&cert, filename);
-       if (ret != 0)
+       if ((ret = mbedtls_x509_crt_parse_file(&cert, filename)) != 0)
                return -1;
 
-       return make_certfp(&cert, certfp, method);
+       size_t len = rb_make_certfp(&cert, certfp, method);
+
+       mbedtls_x509_crt_free(&cert);
+
+       return (int) len;
 }
 
 int
@@ -643,16 +735,16 @@ rb_get_ssl_info(char *buf, size_t len)
        char version_str[512];
        mbedtls_version_get_string(version_str);
 
-       snprintf(buf, len, "MBEDTLS: compiled (%s), library(%s)",
-                   MBEDTLS_VERSION_STRING, version_str);
+       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 *F)
 {
-       if(F == NULL || F->ssl == NULL)
+       if(F == NULL || F->ssl == NULL || SSL_P(F) == NULL)
                return NULL;
        return mbedtls_ssl_get_ciphersuite(SSL_P(F));
 }
 
-#endif /* HAVE_GNUTLS */
+#endif /* HAVE_MBEDTLS */