tls: added DTLS

This commit is contained in:
Richard Aas 2011-12-22 07:36:31 +00:00
parent 15d5274de3
commit fddf8ed5e7
8 changed files with 449 additions and 17 deletions

View file

@ -7,11 +7,26 @@
struct tls;
struct tls_conn;
struct tls_sock;
struct tcp_conn;
struct udp_sock;
int tls_alloc(struct tls **tlsp, const char *keyfile, const char *pwd);
/** Defines the TLS method */
enum tls_method {
TLS_METHOD_SSLV23,
TLS_METHOD_DTLSV1,
};
int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile,
const char *pwd);
int tls_add_ca(struct tls *tls, const char *capath);
int tls_verify_cert(struct tls_conn *tc, char *cn, size_t cn_size);
int tls_start_tcp(struct tls_conn **ptc, struct tls *tls,
struct tcp_conn *tcp);
struct tcp_conn *tcp, int layer);
int tls_start_udp(struct tls_sock **tsp, struct tls *tls,
struct udp_sock *us, int layer, uint32_t bsize);
struct tls_conn *tls_udp_conn(const struct tls_sock *ts,
const struct sa *peer);

View file

@ -184,7 +184,7 @@ static void tcp_conn_handler(const struct sa *addr, void *arg)
#ifdef USE_TLS
if (bs->transp == BFCP_TRANSP_TLS) {
if (tls_start_tcp(&conn->sc, bs->tls, conn->tc))
if (tls_start_tcp(&conn->sc, bs->tls, conn->tc, 0))
goto error;
}
#endif
@ -318,7 +318,7 @@ int bfcp_send(struct bfcp_sock *sock, const struct sa *dst, struct mbuf *mb)
if (sock->transp == BFCP_TRANSP_TLS) {
err = tls_start_tcp(&conn->sc, sock->tls,
conn->tc);
conn->tc, 0);
if (err)
goto out;
}

View file

@ -510,7 +510,7 @@ static void tcp_connect_handler(const struct sa *paddr, void *arg)
#ifdef USE_TLS
if (transp->tls) {
err = tls_start_tcp(&conn->sc, transp->tls, conn->tc);
err = tls_start_tcp(&conn->sc, transp->tls, conn->tc, 0);
if (err)
goto out;
}
@ -570,7 +570,7 @@ static int conn_send(struct sip_connqent **qentp, struct sip *sip, bool secure,
goto out;
}
err = tls_start_tcp(&conn->sc, transp->tls, conn->tc);
err = tls_start_tcp(&conn->sc, transp->tls, conn->tc, 0);
if (err)
goto out;
}

View file

@ -7,4 +7,14 @@
ifneq ($(USE_OPENSSL),)
SRCS += tls/openssl/tls.c
SRCS += tls/openssl/tls_tcp.c
USE_OPENSSL_DTLS := $(shell [ -f $(SYSROOT)/include/openssl/dtls1.h ] || \
[ -f $(SYSROOT)/local/include/openssl/dtls1.h ] || \
[ -f $(SYSROOT_ALT)/include/openssl/dtls1.h ] && echo "yes")
ifneq ($(USE_OPENSSL_DTLS),)
CFLAGS += -DUSE_OPENSSL_DTLS=1
SRCS += tls/openssl/tls_udp.c
endif
endif

View file

@ -49,7 +49,10 @@ static void destructor(void *data)
{
struct tls *tls = data;
SSL_CTX_free(tls->ctx);
if (tls->ctx)
SSL_CTX_free(tls->ctx);
mem_deref(tls->pass);
if (--tlsg.tlsc == 0) {
DEBUG_INFO("error strings freed\n");
@ -76,10 +79,11 @@ static int password_cb(char *buf, int size, int rwflag, void *userdata)
}
int tls_alloc(struct tls **tlsp, const char *keyfile, const char *pwd)
int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile,
const char *pwd)
{
int r, err = ENOMEM;
struct tls *tls;
int r, err;
if (!tlsp)
return EINVAL;
@ -103,20 +107,44 @@ int tls_alloc(struct tls **tlsp, const char *keyfile, const char *pwd)
SSL_load_error_strings();
}
tls->ctx = SSL_CTX_new(SSLv23_method());
if (!tls->ctx)
switch (method) {
case TLS_METHOD_SSLV23:
tls->ctx = SSL_CTX_new(SSLv23_method());
break;
#ifdef USE_OPENSSL_DTLS
case TLS_METHOD_DTLSV1:
tls->ctx = SSL_CTX_new(DTLSv1_method());
break;
#endif
default:
DEBUG_WARNING("tls method %d not supported\n", method);
err = ENOSYS;
goto out;
}
if (!tls->ctx) {
err = ENOMEM;
goto out;
}
#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
SSL_CTX_set_verify_depth(tls->ctx, 1);
#endif
if (method == TLS_METHOD_DTLSV1) {
SSL_CTX_set_read_ahead(tls->ctx, 1);
}
/* Load our keys and certificates */
if (keyfile) {
err = EINVAL;
if (pwd) {
tls->pass = pwd;
err = str_dup(&tls->pass, pwd);
if (err)
goto out;
SSL_CTX_set_default_passwd_cb(tls->ctx, password_cb);
SSL_CTX_set_default_passwd_cb_userdata(tls->ctx, tls);
}
@ -125,6 +153,7 @@ int tls_alloc(struct tls **tlsp, const char *keyfile, const char *pwd)
if (r <= 0) {
DEBUG_WARNING("Can't read certificate file: %s (%d)\n",
keyfile, r);
err = EINVAL;
goto out;
}
@ -133,6 +162,7 @@ int tls_alloc(struct tls **tlsp, const char *keyfile, const char *pwd)
if (r <= 0) {
DEBUG_WARNING("Can't read key file: %s (%d)\n",
keyfile, r);
err = EINVAL;
goto out;
}
}

View file

@ -7,5 +7,5 @@
struct tls {
SSL_CTX *ctx;
const char *pass; /* password for private key */
char *pass; /* password for private key */
};

View file

@ -276,10 +276,10 @@ static bool send_handler(int *err, struct mbuf *mb, void *arg)
}
int tls_start_tcp(struct tls_conn **ptc, struct tls *tls, struct tcp_conn *tcp)
int tls_start_tcp(struct tls_conn **ptc, struct tls *tls, struct tcp_conn *tcp,
int layer)
{
struct tls_conn *tc;
const int layer = 0;
int err;
if (!ptc || !tls || !tcp)

377
src/tls/openssl/tls_udp.c Normal file
View file

@ -0,0 +1,377 @@
/**
* @file openssl/tls_udp.c DTLS/UDP backend using OpenSSL
*
* Copyright (C) 2010 Creytiv.com
*/
#define OPENSSL_NO_KRB5 1
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_mem.h>
#include <re_mbuf.h>
#include <re_list.h>
#include <re_hash.h>
#include <re_tmr.h>
#include <re_sa.h>
#include <re_net.h>
#include <re_udp.h>
#include <re_tls.h>
#include "tls.h"
#define DEBUG_MODULE "tls_udp"
#define DEBUG_LEVEL 5
#include <re_dbg.h>
struct tls_sock {
struct udp_helper *uh;
struct udp_sock *us;
struct hash *ht_conn;
struct tls *tls;
};
struct tls_conn {
SSL *ssl; /* inheritance */
BIO *sbio_out;
BIO *sbio_in;
struct le he;
struct sa peer;
struct tmr tmr;
struct tls_sock *ts;
};
static void check_timer(struct tls_conn *conn);
static int bio_create(BIO *b)
{
b->init = 1;
b->num = 0;
b->ptr = NULL;
b->flags = 0;
return 1;
}
static int bio_destroy(BIO *b)
{
if (!b)
return 0;
b->ptr = NULL;
b->init = 0;
b->flags = 0;
return 1;
}
static int bio_write(BIO *b, const char *buf, int len)
{
struct tls_conn *tc = b->ptr;
struct mbuf mb;
int err;
mb.buf = (void *)buf;
mb.pos = 0;
mb.end = mb.size = len;
err = udp_send_helper(tc->ts->us, &tc->peer, &mb, tc->ts->uh);
if (err)
return -1;
return len;
}
static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
{
(void)b;
(void)num;
(void)ptr;
if (cmd == BIO_CTRL_FLUSH) {
/* The OpenSSL library needs this */
return 1;
}
return 0;
}
static struct bio_method_st bio_udp_send = {
BIO_TYPE_SOURCE_SINK,
"udp_send",
bio_write,
0,
0,
0,
bio_ctrl,
bio_create,
bio_destroy,
0
};
static void timeout(void *arg)
{
struct tls_conn *tc = arg;
DTLSv1_handle_timeout(tc->ssl);
check_timer(tc);
}
static void check_timer(struct tls_conn *tc)
{
struct timeval tv = {0, 0};
if (DTLSv1_get_timeout(tc->ssl, &tv)) {
tmr_start(&tc->tmr, tv.tv_sec * 1000 + tv.tv_usec / 1000,
timeout, tc);
}
}
static void destructor(void *arg)
{
struct tls_sock *ts = arg;
hash_flush(ts->ht_conn);
mem_deref(ts->ht_conn);
mem_deref(ts->uh);
mem_deref(ts->us);
mem_deref(ts->tls);
}
static void conn_destructor(void *arg)
{
struct tls_conn *tc = arg;
hash_unlink(&tc->he);
tmr_cancel(&tc->tmr);
if (tc->ssl) {
(void)SSL_shutdown(tc->ssl);
SSL_free(tc->ssl);
}
}
static bool hash_cmp_handler(struct le *le, void *arg)
{
const struct tls_conn *tc = le->data;
return sa_cmp(&tc->peer, arg, SA_ALL);
}
static struct tls_conn *conn_alloc(struct tls_sock *ts, const struct sa *peer)
{
struct tls_conn *tc;
tc = mem_zalloc(sizeof(*tc), conn_destructor);
if (!tc)
return NULL;
tc->ssl = SSL_new(ts->tls->ctx);
if (!tc->ssl)
goto error;
tc->sbio_in = BIO_new(BIO_s_mem());
if (!tc->sbio_in)
goto error;
tc->sbio_out = BIO_new(&bio_udp_send);
if (!tc->sbio_out) {
BIO_free(tc->sbio_in);
goto error;
}
tc->sbio_out->ptr = tc;
SSL_set_bio(tc->ssl, tc->sbio_in, tc->sbio_out);
tmr_init(&tc->tmr);
tc->peer = *peer;
tc->ts = ts;
hash_append(ts->ht_conn, sa_hash(peer, SA_ALL), &tc->he, tc);
return tc;
error:
return mem_deref(tc);
}
static bool send_handler(int *err, struct sa *dst, struct mbuf *mb, void *arg)
{
struct tls_sock *ts = arg;
struct tls_conn *tc;
int r;
tc = tls_udp_conn(ts, dst);
if (!tc) {
/* No connection found, assuming Client role */
tc = conn_alloc(ts, dst);
if (!tc) {
*err = ENOMEM;
return true;
}
SSL_set_connect_state(tc->ssl);
check_timer(tc);
}
r = SSL_write(tc->ssl, mbuf_buf(mb), (int)mbuf_get_left(mb));
if (r < 0) {
switch (SSL_get_error(tc->ssl, r)) {
case SSL_ERROR_WANT_READ:
break;
default:
DEBUG_WARNING("SSL_write: %d\n",
SSL_get_error(tc->ssl, r));
*err = EPROTO;
return true;
}
}
return true;
}
static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg)
{
struct tls_sock *ts = arg;
struct tls_conn *tc;
int r;
tc = tls_udp_conn(ts, src);
if (!tc) {
/* No connection found, assuming Server role */
tc = conn_alloc(ts, src);
if (!tc)
return true;
SSL_set_verify(tc->ssl, 0, 0);
SSL_set_accept_state(tc->ssl);
}
/* feed SSL data to the BIO */
r = BIO_write(tc->sbio_in, mbuf_buf(mb), (int)mbuf_get_left(mb));
if (r <= 0)
return true;
check_timer(tc);
mbuf_set_pos(mb, 0);
for (;;) {
int n;
if (mbuf_get_space(mb) < 4096) {
if (mbuf_resize(mb, mb->size + 8192))
return true;
}
n = SSL_read(tc->ssl, mbuf_buf(mb), (int)mbuf_get_space(mb));
if (n < 0) {
const int ssl_err = SSL_get_error(tc->ssl, n);
switch (ssl_err) {
case SSL_ERROR_WANT_READ:
break;
default:
return true;
}
break;
}
else if (n == 0)
break;
mb->pos += n;
}
if (!mb->pos)
return true;
mbuf_set_end(mb, mb->pos);
mbuf_set_pos(mb, 0);
return false;
}
/**
* Start TLS on a UDP socket (aka DTLS). The UDP socket can act as a
* client or a server, and multiple DTLS connections can be established to
* multiple peers, all from the same UDP socket.
*
* @param tsp Pointer to allocated TLS socket
* @param tls TLS context
* @param us UDP socket
* @param layer Protocol stack layer
* @param bsize Bucket size for hash table (0 for default)
*
* @return 0 if success, otherwise errorcode
*/
int tls_start_udp(struct tls_sock **tsp, struct tls *tls, struct udp_sock *us,
int layer, uint32_t bsize)
{
struct tls_sock *ts;
int err;
if (!tsp || !tls || !us)
return EINVAL;
ts = mem_zalloc(sizeof(*ts), destructor);
if (!ts)
return ENOMEM;
err = hash_alloc(&ts->ht_conn, bsize ? bsize : 4);
if (err)
goto out;
err = udp_register_helper(&ts->uh, us, layer, send_handler,
recv_handler, ts);
if (err)
goto out;
ts->us = mem_ref(us);
ts->tls = mem_ref(tls);
out:
if (err)
mem_deref(ts);
else
*tsp = ts;
return err;
}
struct tls_conn *tls_udp_conn(const struct tls_sock *ts, const struct sa *peer)
{
if (!ts)
return NULL;
return list_ledata(hash_lookup(ts->ht_conn, sa_hash(peer, SA_ALL),
hash_cmp_handler, (void *)peer));
}