This patch includes multiple TLS fixes:
- General TLS improvements - New DTLS API - OpenSSL multi-threading support
This commit is contained in:
parent
47c951506d
commit
1be9aa335c
19 changed files with 1085 additions and 306 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
2
mk/re.mk
2
mk/re.mk
|
@ -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)"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
140
src/main/openssl.c
Normal 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
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -7,5 +7,6 @@
|
|||
|
||||
struct tls {
|
||||
SSL_CTX *ctx;
|
||||
X509 *cert;
|
||||
char *pass; /* password for private key */
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue