This patch includes multiple TLS fixes:

- General TLS improvements
- New DTLS API
- OpenSSL multi-threading support
This commit is contained in:
Richard Aas 2014-06-12 10:51:53 +00:00
parent 47c951506d
commit 1be9aa335c
19 changed files with 1085 additions and 306 deletions

View file

@ -24,3 +24,5 @@ int srtp_encrypt(struct srtp *srtp, struct mbuf *mb);
int srtp_decrypt(struct srtp *srtp, struct mbuf *mb);
int srtcp_encrypt(struct srtp *srtp, struct mbuf *mb);
int srtcp_decrypt(struct srtp *srtp, struct mbuf *mb);
const char *srtp_suite_name(enum srtp_suite suite);

View file

@ -7,7 +7,6 @@
struct tls;
struct tls_conn;
struct tls_sock;
struct tcp_conn;
struct udp_sock;
@ -18,23 +17,54 @@ enum tls_method {
TLS_METHOD_DTLSV1,
};
struct tls_fingerprint {
uint8_t md[64];
unsigned int len;
enum tls_fingerprint {
TLS_FINGERPRINT_SHA1,
TLS_FINGERPRINT_SHA256,
};
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_set_selfsigned(struct tls *tls, const char *cn);
void tls_set_verify_client(struct tls *tls);
int tls_set_srtp(struct tls *tls, const char *suites);
int tls_fingerprint(const struct tls *tls, enum tls_fingerprint type,
uint8_t *md, size_t size);
int tls_get_remote_fingerprint(const struct tls_conn *tc, const char *type,
struct tls_fingerprint *fp);
int tls_peer_fingerprint(const struct tls_conn *tc, enum tls_fingerprint type,
uint8_t *md, size_t size);
int tls_peer_common_name(const struct tls_conn *tc, char *cn, size_t size);
int tls_peer_verify(const struct tls_conn *tc);
int tls_srtp_keyinfo(const struct tls_conn *tc, enum srtp_suite *suite,
uint8_t *cli_key, size_t cli_key_size,
uint8_t *srv_key, size_t srv_key_size);
/* TCP */
int tls_start_tcp(struct tls_conn **ptc, struct tls *tls,
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);
/* UDP (DTLS) */
typedef void (dtls_conn_h)(const struct sa *peer, void *arg);
typedef void (dtls_estab_h)(void *arg);
typedef void (dtls_recv_h)(struct mbuf *mb, void *arg);
typedef void (dtls_close_h)(int err, void *arg);
struct dtls_sock;
int dtls_listen(struct dtls_sock **sockp, const struct sa *laddr,
struct udp_sock *us, uint32_t htsize, int layer,
dtls_conn_h *connh, void *arg);
int dtls_connect(struct tls_conn **ptc, struct tls *tls,
struct dtls_sock *sock, const struct sa *peer,
dtls_estab_h *estabh, dtls_recv_h *recvh,
dtls_close_h *closeh, void *arg);
int dtls_accept(struct tls_conn **ptc, struct tls *tls,
struct dtls_sock *sock,
dtls_estab_h *estabh, dtls_recv_h *recvh,
dtls_close_h *closeh, void *arg);
int dtls_send(struct tls_conn *tc, struct mbuf *mb);

View file

@ -611,6 +611,8 @@ info:
@echo " LIBRE_INC: $(LIBRE_INC)"
@echo " LIBRE_SO: $(LIBRE_SO)"
@echo " USE_OPENSSL: $(USE_OPENSSL)"
@echo " USE_OPENSSL_DTLS: $(USE_OPENSSL_DTLS)"
@echo " USE_OPENSSL_SRTP: $(USE_OPENSSL_SRTP)"
@echo " USE_ZLIB: $(USE_ZLIB)"
@echo " GCOV: $(GCOV)"
@echo " GPROF: $(GPROF)"

View file

@ -17,7 +17,6 @@ struct bfcp_conn {
struct tmr tmr1;
struct tmr tmr2;
struct udp_sock *us;
struct tls_sock *ss;
struct mbuf *mb;
bfcp_recv_h *recvh;
void *arg;

View file

@ -11,7 +11,6 @@
#include <re_list.h>
#include <re_sa.h>
#include <re_udp.h>
#include <re_tls.h>
#include <re_tmr.h>
#include <re_bfcp.h>
#include "bfcp.h"
@ -24,7 +23,6 @@ static void destructor(void *arg)
list_flush(&bc->ctransl);
tmr_cancel(&bc->tmr1);
tmr_cancel(&bc->tmr2);
mem_deref(bc->ss);
mem_deref(bc->us);
mem_deref(bc->mb);
}
@ -94,6 +92,7 @@ int bfcp_listen(struct bfcp_conn **bcp, enum bfcp_transp tp, struct sa *laddr,
{
struct bfcp_conn *bc;
int err;
(void)tls;
if (!bcp)
return EINVAL;
@ -109,7 +108,6 @@ int bfcp_listen(struct bfcp_conn **bcp, enum bfcp_transp tp, struct sa *laddr,
switch (bc->tp) {
case BFCP_UDP:
case BFCP_DTLS:
err = udp_listen(&bc->us, laddr, udp_recv_handler, bc);
if (err)
goto out;
@ -126,18 +124,6 @@ int bfcp_listen(struct bfcp_conn **bcp, enum bfcp_transp tp, struct sa *laddr,
goto out;
}
if (bc->tp == BFCP_DTLS) {
#ifdef USE_OPENSSL_DTLS
err = tls_start_udp(&bc->ss, tls, bc->us, 0, 4);
#else
(void)tls;
err = ENOSYS;
#endif
if (err)
goto out;
}
out:
if (err)
mem_deref(bc);
@ -156,7 +142,6 @@ int bfcp_send(struct bfcp_conn *bc, const struct sa *dst, struct mbuf *mb)
switch (bc->tp) {
case BFCP_UDP:
case BFCP_DTLS:
return udp_send(bc->us, dst, mb);
default:

View file

@ -12,6 +12,7 @@
#include <re_list.h>
#include <re_fmt.h>
#include <re_tmr.h>
#include <re_srtp.h>
#include <re_tcp.h>
#include <re_tls.h>
#include <re_dns.h>

View file

@ -11,6 +11,7 @@
#include <re_list.h>
#include <re_fmt.h>
#include <re_tmr.h>
#include <re_srtp.h>
#include <re_tcp.h>
#include <re_tls.h>
#include <re_msg.h>

View file

@ -19,12 +19,32 @@
*/
int libre_init(void)
{
int err;
#ifdef HAVE_ACTSCHED
actsched_init();
#endif
rand_init();
return net_sock_init();
#ifdef USE_OPENSSL
err = openssl_init();
if (err)
goto out;
#endif
err = net_sock_init();
if (err)
goto out;
out:
if (err) {
net_sock_close();
#ifdef USE_OPENSSL
openssl_close();
#endif
}
return err;
}
@ -34,4 +54,8 @@ int libre_init(void)
void libre_close(void)
{
(void)fd_setsize(0);
net_sock_close();
#ifdef USE_OPENSSL
openssl_close();
#endif
}

View file

@ -21,6 +21,11 @@ void actsched_stop(void);
void actsched_restart_timer(void);
#endif
#ifdef USE_OPENSSL
int openssl_init(void);
void openssl_close(void);
#endif
#ifdef __cplusplus
}
#endif

View file

@ -11,3 +11,7 @@ SRCS += main/method.c
ifneq ($(HAVE_EPOLL),)
SRCS += main/epoll.c
endif
ifneq ($(USE_OPENSSL),)
SRCS += main/openssl.c
endif

140
src/main/openssl.c Normal file
View file

@ -0,0 +1,140 @@
/**
* @file openssl.c OpenSSL initialisation and multi-threading routines
*
* Copyright (C) 2010 Creytiv.com
*/
#ifdef HAVE_SIGNAL
#include <signal.h>
#endif
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <re_types.h>
#include <re_lock.h>
#include <re_mem.h>
#include "main.h"
#ifdef HAVE_PTHREAD
static pthread_mutex_t *lockv;
static unsigned long threadid_handler(void)
{
#ifdef DARWIN
return (unsigned long)(void *)pthread_self();
#else
return (unsigned long)pthread_self();
#endif
}
static void locking_handler(int mode, int type, const char *file, int line)
{
(void)file;
(void)line;
if (mode & CRYPTO_LOCK)
(void)pthread_mutex_lock(&lockv[type]);
else
(void)pthread_mutex_unlock(&lockv[type]);
}
#endif
static struct CRYPTO_dynlock_value *dynlock_create_handler(const char *file,
int line)
{
struct lock *lock;
(void)file;
(void)line;
if (lock_alloc(&lock))
return NULL;
return (struct CRYPTO_dynlock_value *)lock;
}
static void dynlock_lock_handler(int mode, struct CRYPTO_dynlock_value *l,
const char *file, int line)
{
struct lock *lock = (struct lock *)l;
(void)file;
(void)line;
if (mode & CRYPTO_LOCK)
lock_write_get(lock);
else
lock_rel(lock);
}
static void dynlock_destroy_handler(struct CRYPTO_dynlock_value *l,
const char *file, int line)
{
(void)file;
(void)line;
mem_deref(l);
}
#ifdef SIGPIPE
static void sigpipe_handler(int x)
{
(void)x;
(void)signal(SIGPIPE, sigpipe_handler);
}
#endif
int openssl_init(void)
{
#ifdef HAVE_PTHREAD
int err, i;
lockv = mem_zalloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks(), NULL);
if (!lockv)
return ENOMEM;
for (i=0; i<CRYPTO_num_locks(); i++) {
err = pthread_mutex_init(&lockv[i], NULL);
if (err) {
lockv = mem_deref(lockv);
return err;
}
}
CRYPTO_set_id_callback(threadid_handler);
CRYPTO_set_locking_callback(locking_handler);
#endif
CRYPTO_set_dynlock_create_callback(dynlock_create_handler);
CRYPTO_set_dynlock_lock_callback(dynlock_lock_handler);
CRYPTO_set_dynlock_destroy_callback(dynlock_destroy_handler);
#ifdef SIGPIPE
(void)signal(SIGPIPE, sigpipe_handler);
#endif
SSL_library_init();
SSL_load_error_strings();
return 0;
}
void openssl_close(void)
{
ERR_free_strings();
#ifdef HAVE_PTHREAD
lockv = mem_deref(lockv);
#endif
}

View file

@ -16,6 +16,7 @@
#include <re_tmr.h>
#include <re_udp.h>
#include <re_stun.h>
#include <re_srtp.h>
#include <re_tcp.h>
#include <re_tls.h>
#include <re_msg.h>

View file

@ -60,7 +60,6 @@ int srtp_derive(uint8_t *out, size_t out_len, uint8_t label,
void srtp_iv_calc(union vect128 *iv, const union vect128 *k_s,
uint32_t ssrc, uint64_t ix);
uint64_t srtp_get_index(uint32_t roc, uint16_t s_l, uint16_t seq);
const char *srtp_suite_name(enum srtp_suite suite);
/* Replay protection */

View file

@ -7,14 +7,22 @@
ifneq ($(USE_OPENSSL),)
SRCS += tls/openssl/tls.c
SRCS += tls/openssl/tls_tcp.c
SRCS += tls/openssl/tls_udp.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")
USE_OPENSSL_SRTP := $(shell [ -f $(SYSROOT)/include/openssl/srtp.h ] || \
[ -f $(SYSROOT)/local/include/openssl/srtp.h ] || \
[ -f $(SYSROOT_ALT)/include/openssl/srtp.h ] && echo "yes")
ifneq ($(USE_OPENSSL_DTLS),)
CFLAGS += -DUSE_OPENSSL_DTLS=1
SRCS += tls/openssl/tls_udp.c
endif
ifneq ($(USE_OPENSSL_SRTP),)
CFLAGS += -DUSE_OPENSSL_SRTP=1
endif
endif

View file

@ -4,7 +4,6 @@
* Copyright (C) 2010 Creytiv.com
*/
#include <string.h>
#include <signal.h>
#define OPENSSL_NO_KRB5 1
#include <openssl/ssl.h>
#include <openssl/err.h>
@ -15,6 +14,8 @@
#include <re_main.h>
#include <re_sa.h>
#include <re_net.h>
#include <re_srtp.h>
#include <re_sys.h>
#include <re_tcp.h>
#include <re_tls.h>
#include "tls.h"
@ -31,21 +32,6 @@ struct tls_conn {
};
static struct {
uint32_t tlsc;
bool up;
} tlsg;
#ifdef SIGPIPE
static void sigpipe_handle(int x)
{
(void)x;
(void)signal(SIGPIPE, sigpipe_handle);
}
#endif
static void destructor(void *data)
{
struct tls *tls = data;
@ -53,12 +39,10 @@ static void destructor(void *data)
if (tls->ctx)
SSL_CTX_free(tls->ctx);
mem_deref(tls->pass);
if (tls->cert)
X509_free(tls->cert);
if (--tlsg.tlsc == 0) {
DEBUG_INFO("error strings freed\n");
ERR_free_strings();
}
mem_deref(tls->pass);
}
@ -103,21 +87,6 @@ int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile,
if (!tls)
return ENOMEM;
if (!tlsg.up) {
#ifdef SIGPIPE
/* Set up a SIGPIPE handler */
(void)signal(SIGPIPE, sigpipe_handle);
#endif
SSL_library_init();
tlsg.up = true;
}
if (tlsg.tlsc++ == 0) {
DEBUG_INFO("error strings loaded\n");
SSL_load_error_strings();
}
switch (method) {
case TLS_METHOD_SSLV23:
@ -217,84 +186,375 @@ int tls_add_ca(struct tls *tls, const char *capath)
/**
* Verify peer certificate of a TLS connection
* Generate and set selfsigned certificate on TLS context
*
* @param tc TLS Connection
* @param cn Returned common name
* @param cn_size Size of cn string
* @param tls TLS Context
* @param cn Common Name
*
* @return 0 if success, otherwise errorcode
*/
int tls_verify_cert(struct tls_conn *tc, char *cn, size_t cn_size)
int tls_set_selfsigned(struct tls *tls, const char *cn)
{
X509 *peer;
int n;
X509_NAME *subj = NULL;
EVP_PKEY *key = NULL;
X509 *cert = NULL;
RSA *rsa = NULL;
int r, err = ENOMEM;
if (!tc || !cn || !cn_size)
if (!tls || !cn)
return EINVAL;
/* Check the cert chain. The chain length
is automatically checked by OpenSSL when
we set the verify depth in the ctx */
rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL);
if (!rsa)
goto out;
peer = SSL_get_peer_certificate(tc->ssl);
if (!peer) {
DEBUG_WARNING("Unable to get peer certificate\n");
return EPROTO;
key = EVP_PKEY_new();
if (!key)
goto out;
if (!EVP_PKEY_set1_RSA(key, rsa))
goto out;
cert = X509_new();
if (!cert)
goto out;
if (!X509_set_version(cert, 2))
goto out;
if (!ASN1_INTEGER_set(X509_get_serialNumber(cert), rand_u32()))
goto out;
subj = X509_NAME_new();
if (!subj)
goto out;
if (!X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
(unsigned char *)cn,
(int)strlen(cn), -1, 0))
goto out;
if (!X509_set_issuer_name(cert, subj) ||
!X509_set_subject_name(cert, subj))
goto out;
if (!X509_gmtime_adj(X509_get_notBefore(cert), -3600*24*365) ||
!X509_gmtime_adj(X509_get_notAfter(cert), 3600*24*365*10))
goto out;
if (!X509_set_pubkey(cert, key))
goto out;
if (!X509_sign(cert, key, EVP_sha1()))
goto out;
r = SSL_CTX_use_certificate(tls->ctx, cert);
if (r != 1)
goto out;
r = SSL_CTX_use_PrivateKey(tls->ctx, key);
if (r != 1)
goto out;
if (tls->cert)
X509_free(tls->cert);
tls->cert = cert;
cert = NULL;
err = 0;
out:
if (subj)
X509_NAME_free(subj);
if (cert)
X509_free(cert);
if (key)
EVP_PKEY_free(key);
if (rsa)
RSA_free(rsa);
if (err)
ERR_clear_error();
return err;
}
static int verify_handler(int ok, X509_STORE_CTX *ctx)
{
(void)ok;
(void)ctx;
return 1; /* We trust the certificate from peer */
}
/**
* Set TLS server context to request certificate from client
*
* @param tls TLS Context
*/
void tls_set_verify_client(struct tls *tls)
{
if (!tls)
return;
SSL_CTX_set_verify_depth(tls->ctx, 0);
SSL_CTX_set_verify(tls->ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE,
verify_handler);
}
/**
* Set SRTP suites on TLS context
*
* @param tls TLS Context
* @param suites Secure-RTP Profiles
*
* @return 0 if success, otherwise errorcode
*/
int tls_set_srtp(struct tls *tls, const char *suites)
{
#ifdef USE_OPENSSL_SRTP
if (!tls || !suites)
return EINVAL;
if (0 != SSL_CTX_set_tlsext_use_srtp(tls->ctx, suites)) {
ERR_clear_error();
return ENOSYS;
}
/* Get the common name */
n = X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
NID_commonName, cn, (int)cn_size);
if (n < 0)
cn[0] = '\0';
return 0;
#else
(void)tls;
(void)suites;
/* todo get valid start/end date */
return ENOSYS;
#endif
}
X509_free(peer);
if (SSL_get_verify_result(tc->ssl) != X509_V_OK) {
DEBUG_WARNING("Certificate doesn't verify\n");
return EPROTO;
static int cert_fingerprint(X509 *cert, enum tls_fingerprint type,
uint8_t *md, size_t size)
{
unsigned int len = (unsigned int)size;
int n;
switch (type) {
case TLS_FINGERPRINT_SHA1:
if (size < 20)
return EOVERFLOW;
n = X509_digest(cert, EVP_sha1(), md, &len);
break;
case TLS_FINGERPRINT_SHA256:
if (size < 32)
return EOVERFLOW;
n = X509_digest(cert, EVP_sha256(), md, &len);
break;
default:
return ENOSYS;
}
if (n != 1) {
ERR_clear_error();
return ENOENT;
}
return 0;
}
static const EVP_MD *type2evp(const char *type)
/**
* Get fingerprint of local certificate
*
* @param tls TLS Context
* @param type Digest type
* @param md Buffer for fingerprint digest
* @param size Buffer size
*
* @return 0 if success, otherwise errorcode
*/
int tls_fingerprint(const struct tls *tls, enum tls_fingerprint type,
uint8_t *md, size_t size)
{
if (0 == str_casecmp(type, "SHA-1"))
return EVP_sha1();
else if (0 == str_casecmp(type, "SHA-256"))
return EVP_sha256();
else
return NULL;
}
int tls_get_remote_fingerprint(const struct tls_conn *tc, const char *type,
struct tls_fingerprint *fp)
{
const EVP_MD *evp;
X509 *x;
int n;
if (!tc || !fp)
if (!tls || !tls->cert || !md)
return EINVAL;
evp = type2evp(type);
if (!evp)
return ENOTSUP;
x = SSL_get_peer_certificate(tc->ssl);
if (!x)
return EPROTO;
fp->len = sizeof(fp->md);
n = X509_digest(x, evp, fp->md, &fp->len);
X509_free(x);
return (n == 1) ? 0 : ENOENT;
return cert_fingerprint(tls->cert, type, md, size);
}
/**
* Get fingerprint of peer certificate of a TLS connection
*
* @param tc TLS Connection
* @param type Digest type
* @param md Buffer for fingerprint digest
* @param size Buffer size
*
* @return 0 if success, otherwise errorcode
*/
int tls_peer_fingerprint(const struct tls_conn *tc, enum tls_fingerprint type,
uint8_t *md, size_t size)
{
X509 *cert;
int err;
if (!tc || !md)
return EINVAL;
cert = SSL_get_peer_certificate(tc->ssl);
if (!cert)
return ENOENT;
err = cert_fingerprint(cert, type, md, size);
X509_free(cert);
return err;
}
/**
* Get common name of peer certificate of a TLS connection
*
* @param tc TLS Connection
* @param cn Returned common name
* @param size Size of common name
*
* @return 0 if success, otherwise errorcode
*/
int tls_peer_common_name(const struct tls_conn *tc, char *cn, size_t size)
{
X509 *cert;
int n;
if (!tc || !cn || !size)
return EINVAL;
cert = SSL_get_peer_certificate(tc->ssl);
if (!cert)
return ENOENT;
n = X509_NAME_get_text_by_NID(X509_get_subject_name(cert),
NID_commonName, cn, (int)size);
X509_free(cert);
if (n < 0) {
ERR_clear_error();
return ENOENT;
}
return 0;
}
/**
* Verify peer certificate of a TLS connection
*
* @param tc TLS Connection
*
* @return 0 if verified, otherwise errorcode
*/
int tls_peer_verify(const struct tls_conn *tc)
{
if (!tc)
return EINVAL;
if (SSL_get_verify_result(tc->ssl) != X509_V_OK)
return EAUTH;
return 0;
}
/**
* Get SRTP suite and keying material of a TLS connection
*
* @param tc TLS Connection
* @param suite Returned SRTP suite
* @param cli_key Client key
* @param cli_key_size Client key size
* @param srv_key Server key
* @param srv_key_size Server key size
*
* @return 0 if success, otherwise errorcode
*/
int tls_srtp_keyinfo(const struct tls_conn *tc, enum srtp_suite *suite,
uint8_t *cli_key, size_t cli_key_size,
uint8_t *srv_key, size_t srv_key_size)
{
#ifdef USE_OPENSSL_SRTP
static const char *label = "EXTRACTOR-dtls_srtp";
size_t key_size, salt_size, size;
SRTP_PROTECTION_PROFILE *sel;
uint8_t keymat[256], *p;
if (!tc || !suite || !cli_key || !srv_key)
return EINVAL;
sel = SSL_get_selected_srtp_profile(tc->ssl);
if (!sel)
return ENOENT;
switch (sel->id) {
case SRTP_AES128_CM_SHA1_80:
*suite = SRTP_AES_CM_128_HMAC_SHA1_80;
key_size = 16;
salt_size = 14;
break;
case SRTP_AES128_CM_SHA1_32:
*suite = SRTP_AES_CM_128_HMAC_SHA1_32;
key_size = 16;
salt_size = 14;
break;
default:
return ENOSYS;
}
size = key_size + salt_size;
if (cli_key_size < size || srv_key_size < size)
return EOVERFLOW;
if (sizeof(keymat) < 2*size)
return EOVERFLOW;
if (1 != SSL_export_keying_material(tc->ssl, keymat, 2*size, label,
strlen(label), NULL, 0, 0)) {
ERR_clear_error();
return ENOENT;
}
p = keymat;
memcpy(cli_key, p, key_size); p += key_size;
memcpy(srv_key, p, key_size); p += key_size;
memcpy(cli_key + key_size, p, salt_size); p += salt_size;
memcpy(srv_key + key_size, p, salt_size);
return 0;
#else
(void)tc;
(void)suite;
(void)cli_key;
(void)cli_key_size;
(void)srv_key;
(void)srv_key_size;
return ENOSYS;
#endif
}

View file

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

View file

@ -13,6 +13,7 @@
#include <re_main.h>
#include <re_sa.h>
#include <re_net.h>
#include <re_srtp.h>
#include <re_tcp.h>
#include <re_tls.h>
#include "tls.h"
@ -126,6 +127,8 @@ static int tls_connect(struct tls_conn *tc)
{
int err = 0, r;
ERR_clear_error();
r = SSL_connect(tc->ssl);
if (r <= 0) {
const int ssl_err = SSL_get_error(tc->ssl, r);
@ -153,6 +156,8 @@ static int tls_accept(struct tls_conn *tc)
{
int err = 0, r;
ERR_clear_error();
r = SSL_accept(tc->ssl);
if (r <= 0) {
const int ssl_err = SSL_get_error(tc->ssl, r);
@ -201,6 +206,7 @@ static bool recv_handler(int *err, struct mbuf *mb, bool *estab, void *arg)
r = BIO_write(tc->sbio_in, mbuf_buf(mb), (int)mbuf_get_left(mb));
if (r <= 0) {
DEBUG_WARNING("recv: BIO_write %d\n", r);
ERR_clear_error();
*err = ENOMEM;
return true;
}
@ -240,6 +246,8 @@ static bool recv_handler(int *err, struct mbuf *mb, bool *estab, void *arg)
return true;
}
ERR_clear_error();
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);
@ -251,6 +259,10 @@ static bool recv_handler(int *err, struct mbuf *mb, bool *estab, void *arg)
case SSL_ERROR_WANT_READ:
break;
case SSL_ERROR_ZERO_RETURN:
*err = ECONNRESET;
return true;
default:
*err = EPROTO;
return true;
@ -274,6 +286,8 @@ static bool send_handler(int *err, struct mbuf *mb, void *arg)
struct tls_conn *tc = arg;
int r;
ERR_clear_error();
r = SSL_write(tc->ssl, mbuf_buf(mb), (int)mbuf_get_left(mb));
if (r <= 0) {
DEBUG_WARNING("SSL_write: %d\n", SSL_get_error(tc->ssl, r));
@ -328,12 +342,14 @@ int tls_start_tcp(struct tls_conn **ptc, struct tls *tls, struct tcp_conn *tcp,
tc->sbio_in = BIO_new(BIO_s_mem());
if (!tc->sbio_in) {
DEBUG_WARNING("alloc: BIO_new() failed\n");
ERR_clear_error();
goto out;
}
tc->sbio_out = BIO_new(&bio_tcp_send);
if (!tc->sbio_out) {
DEBUG_WARNING("alloc: BIO_new_socket() failed\n");
ERR_clear_error();
BIO_free(tc->sbio_in);
goto out;
}

View file

@ -1,5 +1,5 @@
/**
* @file openssl/tls_udp.c DTLS/UDP backend using OpenSSL
* @file openssl/tls_udp.c DTLS backend using OpenSSL
*
* Copyright (C) 2010 Creytiv.com
*/
@ -12,41 +12,48 @@
#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_srtp.h>
#include <re_udp.h>
#include <re_tmr.h>
#include <re_tls.h>
#include "tls.h"
#define DEBUG_MODULE "tls_udp"
#define DEBUG_MODULE "dtls"
#define DEBUG_LEVEL 5
#include <re_dbg.h>
struct tls_sock {
struct dtls_sock {
struct sa peer;
struct udp_helper *uh;
struct udp_sock *us;
struct hash *ht_conn;
struct tls *tls;
struct hash *ht;
struct mbuf *mb;
dtls_conn_h *connh;
void *arg;
};
/* NOTE: shadow struct defined in tls_*.c */
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;
struct sa peer;
struct le he;
struct dtls_sock *sock;
dtls_estab_h *estabh;
dtls_recv_h *recvh;
dtls_close_h *closeh;
void *arg;
bool active;
bool up;
};
static void check_timer(struct tls_conn *conn);
static int bio_create(BIO *b)
{
b->init = 1;
@ -81,9 +88,11 @@ static int bio_write(BIO *b, const char *buf, int len)
mb.pos = 0;
mb.end = mb.size = len;
err = udp_send_helper(tc->ts->us, &tc->peer, &mb, tc->ts->uh);
if (err)
err = udp_send_helper(tc->sock->us, &tc->peer, &mb, tc->sock->uh);
if (err) {
DEBUG_WARNING("udp send error: %m\n", err);
return -1;
}
return len;
}
@ -118,41 +127,19 @@ static struct bio_method_st bio_udp_send = {
};
#if defined (DTLS_CTRL_HANDLE_TIMEOUT) && defined(DTLS_CTRL_GET_TIMEOUT)
static void timeout(void *arg)
static void tls_close(struct tls_conn *tc)
{
struct tls_conn *tc = arg;
int r;
DTLSv1_handle_timeout(tc->ssl);
check_timer(tc);
}
#endif
if (!tc->ssl)
return;
r = SSL_shutdown(tc->ssl);
if (r <= 0)
ERR_clear_error();
static void check_timer(struct tls_conn *tc)
{
#if defined (DTLS_CTRL_HANDLE_TIMEOUT) && defined (DTLS_CTRL_GET_TIMEOUT)
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);
}
#else
(void)tc;
#endif
}
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);
SSL_free(tc->ssl);
tc->ssl = NULL;
}
@ -162,128 +149,186 @@ static void conn_destructor(void *arg)
hash_unlink(&tc->he);
tmr_cancel(&tc->tmr);
if (tc->ssl) {
(void)SSL_shutdown(tc->ssl);
SSL_free(tc->ssl);
}
tls_close(tc);
mem_deref(tc->sock);
}
static bool hash_cmp_handler(struct le *le, void *arg)
static void conn_close(struct tls_conn *tc, int err)
{
const struct tls_conn *tc = le->data;
tmr_cancel(&tc->tmr);
tls_close(tc);
tc->up = false;
return sa_cmp(&tc->peer, arg, SA_ALL);
if (tc->closeh)
tc->closeh(err, tc->arg);
}
static struct tls_conn *conn_alloc(struct tls_sock *ts, const struct sa *peer)
#if defined (DTLS_CTRL_HANDLE_TIMEOUT) && defined(DTLS_CTRL_GET_TIMEOUT)
static void check_timer(struct tls_conn *tc);
static void timeout(void *arg)
{
struct tls_conn *tc;
struct tls_conn *tc = arg;
tc = mem_zalloc(sizeof(*tc), conn_destructor);
if (!tc)
return NULL;
DEBUG_INFO("timeout\n");
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);
if (0 <= DTLSv1_handle_timeout(tc->ssl)) {
check_timer(tc);
}
else {
ERR_clear_error();
conn_close(tc, ETIMEDOUT);
}
}
r = SSL_write(tc->ssl, mbuf_buf(mb), (int)mbuf_get_left(mb));
if (r < 0) {
switch (SSL_get_error(tc->ssl, r)) {
static void check_timer(struct tls_conn *tc)
{
struct timeval tv = {0, 0};
if (1 == DTLSv1_get_timeout(tc->ssl, &tv)) {
tmr_start(&tc->tmr, tv.tv_sec * 1000 + tv.tv_usec / 1000,
timeout, tc);
}
else {
tmr_cancel(&tc->tmr);
}
}
#else
static void check_timer(struct tls_conn *tc)
{
(void)tc;
}
#endif
static int tls_connect(struct tls_conn *tc)
{
int r;
ERR_clear_error();
r = SSL_connect(tc->ssl);
if (r <= 0) {
const int ssl_err = SSL_get_error(tc->ssl, r);
ERR_clear_error();
switch (ssl_err) {
case SSL_ERROR_WANT_READ:
break;
default:
DEBUG_WARNING("SSL_write: %d\n",
SSL_get_error(tc->ssl, r));
*err = EPROTO;
return true;
DEBUG_WARNING("connect error: %i\n", ssl_err);
return EPROTO;
}
}
return true;
check_timer(tc);
return 0;
}
static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg)
static int tls_accept(struct tls_conn *tc)
{
struct tls_sock *ts = arg;
struct tls_conn *tc;
int r;
tc = tls_udp_conn(ts, src);
if (!tc) {
ERR_clear_error();
/* No connection found, assuming Server role */
r = SSL_accept(tc->ssl);
if (r <= 0) {
const int ssl_err = SSL_get_error(tc->ssl, r);
tc = conn_alloc(ts, src);
if (!tc)
return true;
ERR_clear_error();
SSL_set_verify(tc->ssl, 0, 0);
SSL_set_accept_state(tc->ssl);
switch (ssl_err) {
case SSL_ERROR_WANT_READ:
break;
default:
DEBUG_WARNING("accept error: %i\n", ssl_err);
return EPROTO;
}
}
check_timer(tc);
return 0;
}
static void conn_recv(struct tls_conn *tc, struct mbuf *mb)
{
int err, r;
if (!tc->ssl)
return;
/* 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;
if (r <= 0) {
DEBUG_WARNING("receive bio write error: %i\n", r);
ERR_clear_error();
conn_close(tc, ENOMEM);
return;
}
check_timer(tc);
if (SSL_state(tc->ssl) != SSL_ST_OK) {
if (tc->up) {
conn_close(tc, EPROTO);
return;
}
if (tc->active) {
err = tls_connect(tc);
}
else {
err = tls_accept(tc);
}
if (err) {
conn_close(tc, err);
return;
}
DEBUG_INFO("%s: state=0x%04x\n",
tc->active ? "client" : "server",
SSL_state(tc->ssl));
/* TLS connection is established */
if (SSL_state(tc->ssl) != SSL_ST_OK)
return;
tc->up = true;
if (tc->estabh) {
uint32_t nrefs;
mem_ref(tc);
tc->estabh(tc->arg);
nrefs = mem_nrefs(tc);
mem_deref(tc);
/* check if connection was deref'd from handler */
if (nrefs == 1)
return;
}
}
mbuf_set_pos(mb, 0);
@ -291,102 +336,357 @@ static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg)
int n;
if (mbuf_get_space(mb) < 4096) {
if (mbuf_resize(mb, mb->size + 8192))
return true;
err = mbuf_resize(mb, mb->size + 8192);
if (err) {
conn_close(tc, err);
return;
}
}
ERR_clear_error();
n = SSL_read(tc->ssl, mbuf_buf(mb), (int)mbuf_get_space(mb));
if (n < 0) {
if (n <= 0) {
const int ssl_err = SSL_get_error(tc->ssl, n);
ERR_clear_error();
switch (ssl_err) {
case SSL_ERROR_WANT_READ:
break;
case SSL_ERROR_ZERO_RETURN:
conn_close(tc, ECONNRESET);
return;
default:
return true;
DEBUG_WARNING("read error: %i\n", ssl_err);
conn_close(tc, EPROTO);
return;
}
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;
if (tc->recvh && mbuf_get_left(mb) > 0)
tc->recvh(mb, tc->arg);
return;
}
/**
* 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)
static int conn_alloc(struct tls_conn **ptc, struct tls *tls,
struct dtls_sock *sock, const struct sa *peer,
dtls_estab_h *estabh, dtls_recv_h *recvh,
dtls_close_h *closeh, void *arg)
{
struct tls_sock *ts;
int err;
struct tls_conn *tc;
int err = 0;
if (!tsp || !tls || !us)
return EINVAL;
ts = mem_zalloc(sizeof(*ts), destructor);
if (!ts)
tc = mem_zalloc(sizeof(*tc), conn_destructor);
if (!tc)
return ENOMEM;
err = hash_alloc(&ts->ht_conn, bsize ? bsize : 4);
if (err)
goto out;
hash_append(sock->ht, sa_hash(peer, SA_ALL), &tc->he, tc);
err = udp_register_helper(&ts->uh, us, layer, send_handler,
recv_handler, ts);
if (err)
goto out;
tc->sock = mem_ref(sock);
tc->peer = *peer;
tc->estabh = estabh;
tc->recvh = recvh;
tc->closeh = closeh;
tc->arg = arg;
ts->us = mem_ref(us);
ts->tls = mem_ref(tls);
/* Connect the SSL socket */
tc->ssl = SSL_new(tls->ctx);
if (!tc->ssl) {
DEBUG_WARNING("ssl new failed: %i\n",
ERR_GET_REASON(ERR_get_error()));
ERR_clear_error();
err = ENOMEM;
goto out;
}
tc->sbio_in = BIO_new(BIO_s_mem());
if (!tc->sbio_in) {
ERR_clear_error();
err = ENOMEM;
goto out;
}
tc->sbio_out = BIO_new(&bio_udp_send);
if (!tc->sbio_out) {
ERR_clear_error();
BIO_free(tc->sbio_in);
err = ENOMEM;
goto out;
}
tc->sbio_out->ptr = tc;
SSL_set_bio(tc->ssl, tc->sbio_in, tc->sbio_out);
out:
if (err)
mem_deref(ts);
mem_deref(tc);
else
*tsp = ts;
*ptc = tc;
return err;
}
/**
* Get the TLS Connection for a given peer
* DTLS Connect
*
* @param ts TLS Socket
* @param peer Network address of peer
* @param ptc Pointer to allocated DTLS connection
* @param tls TLS Context
* @param sock DTLS Socket
* @param peer Peer address
* @param estabh Establish handler
* @param recvh Receive handler
* @param closeh Close handler
* @param arg Handler argument
*
* @return TLS Connection if found, NULL if not found
* @return 0 if success, otherwise errorcode
*/
struct tls_conn *tls_udp_conn(const struct tls_sock *ts, const struct sa *peer)
int dtls_connect(struct tls_conn **ptc, struct tls *tls,
struct dtls_sock *sock, const struct sa *peer,
dtls_estab_h *estabh, dtls_recv_h *recvh,
dtls_close_h *closeh, void *arg)
{
if (!ts)
return NULL;
struct tls_conn *tc;
int err;
return list_ledata(hash_lookup(ts->ht_conn, sa_hash(peer, SA_ALL),
hash_cmp_handler, (void *)peer));
if (!ptc || !tls || !sock || !peer)
return EINVAL;
err = conn_alloc(&tc, tls, sock, peer, estabh, recvh, closeh, arg);
if (err)
return err;
tc->active = true;
err = tls_connect(tc);
if (err)
goto out;
out:
if (err)
mem_deref(tc);
else
*ptc = tc;
return err;
}
/**
* DTLS Accept
*
* @param ptc Pointer to allocated DTLS connection
* @param tls TLS Context
* @param sock DTLS Socket
* @param estabh Establish handler
* @param recvh Receive handler
* @param closeh Close handler
* @param arg Handler argument
*
* @return 0 if success, otherwise errorcode
*/
int dtls_accept(struct tls_conn **ptc, struct tls *tls,
struct dtls_sock *sock,
dtls_estab_h *estabh, dtls_recv_h *recvh,
dtls_close_h *closeh, void *arg)
{
struct tls_conn *tc;
int err, r;
if (!ptc || !tls || !sock || !sock->mb)
return EINVAL;
err = conn_alloc(&tc, tls, sock, &sock->peer, estabh, recvh, closeh,
arg);
if (err)
return err;
tc->active = false;
r = BIO_write(tc->sbio_in, mbuf_buf(sock->mb),
(int)mbuf_get_left(sock->mb));
if (r <= 0) {
DEBUG_WARNING("accept bio write error: %i\n", r);
ERR_clear_error();
err = ENOMEM;
goto out;
}
err = tls_accept(tc);
if (err)
goto out;
sock->mb = mem_deref(sock->mb);
out:
if (err)
mem_deref(tc);
else
*ptc = tc;
return err;
}
/**
* Send data on a DTLS connection
*
* @param tc DTLS connection
* @param mb Buffer to send
*
* @return 0 if success, otherwise errorcode
*/
int dtls_send(struct tls_conn *tc, struct mbuf *mb)
{
int r;
if (!tc || !mb)
return EINVAL;
if (!tc->up || !tc->ssl)
return ENOTCONN;
ERR_clear_error();
r = SSL_write(tc->ssl, mbuf_buf(mb), (int)mbuf_get_left(mb));
if (r <= 0) {
DEBUG_WARNING("write error: %i\n", SSL_get_error(tc->ssl, r));
ERR_clear_error();
return EPROTO;
}
return 0;
}
static void sock_destructor(void *arg)
{
struct dtls_sock *sock = arg;
hash_clear(sock->ht);
mem_deref(sock->uh);
mem_deref(sock->us);
mem_deref(sock->ht);
mem_deref(sock->mb);
}
static bool cmp_handler(struct le *le, void *arg)
{
struct tls_conn *tc = le->data;
return sa_cmp(&tc->peer, arg, SA_ALL);
}
static struct tls_conn *conn_lookup(struct dtls_sock *sock,
const struct sa *peer)
{
return list_ledata(hash_lookup(sock->ht, sa_hash(peer, SA_ALL),
cmp_handler, (void *)peer));
}
static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg)
{
struct dtls_sock *sock = arg;
struct tls_conn *tc;
uint8_t b;
if (mbuf_get_left(mb) < 1)
return false;
b = mb->buf[mb->pos];
if (b < 20 || b > 63)
return false;
tc = conn_lookup(sock, src);
if (tc) {
conn_recv(tc, mb);
return true;
}
if (sock->connh) {
mem_deref(sock->mb);
sock->mb = mem_ref(mb);
sock->peer = *src;
sock->connh(src, sock->arg);
}
return true;
}
/**
* Create DTLS Socket
*
* @param sockp Pointer to returned DTLS Socket
* @param laddr Local listen address (optional)
* @param us External UDP socket (optional)
* @param htsize Connection hash table size
* @param layer UDP protocol layer
* @param connh Connect handler
* @param arg Handler argument
*
* @return 0 if success, otherwise errorcode
*/
int dtls_listen(struct dtls_sock **sockp, const struct sa *laddr,
struct udp_sock *us, uint32_t htsize, int layer,
dtls_conn_h *connh, void *arg)
{
struct dtls_sock *sock;
int err;
if (!sockp)
return EINVAL;
sock = mem_zalloc(sizeof(*sock), sock_destructor);
if (!sock)
return ENOMEM;
if (us) {
sock->us = mem_ref(us);
}
else {
err = udp_listen(&sock->us, laddr, NULL, NULL);
if (err)
goto out;
}
err = udp_register_helper(&sock->uh, sock->us, layer,
NULL, recv_handler, sock);
if (err)
goto out;
err = hash_alloc(&sock->ht, hash_valid_size(htsize));
if (err)
goto out;
sock->connh = connh;
sock->arg = arg;
out:
if (err)
mem_deref(sock);
else
*sockp = sock;
return err;
}

View file

@ -11,6 +11,7 @@
#include <re_sa.h>
#include <re_list.h>
#include <re_tmr.h>
#include <re_srtp.h>
#include <re_tcp.h>
#include <re_tls.h>
#include <re_msg.h>