diff --git a/include/re_srtp.h b/include/re_srtp.h index 2d34d4a..dae6051 100644 --- a/include/re_srtp.h +++ b/include/re_srtp.h @@ -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); diff --git a/include/re_tls.h b/include/re_tls.h index 3bde6c9..51e8f8a 100644 --- a/include/re_tls.h +++ b/include/re_tls.h @@ -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); diff --git a/mk/re.mk b/mk/re.mk index 3158df1..efd3a5a 100644 --- a/mk/re.mk +++ b/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)" diff --git a/src/bfcp/bfcp.h b/src/bfcp/bfcp.h index 9e84718..7e76e0d 100644 --- a/src/bfcp/bfcp.h +++ b/src/bfcp/bfcp.h @@ -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; diff --git a/src/bfcp/conn.c b/src/bfcp/conn.c index 22c48ee..6e4051d 100644 --- a/src/bfcp/conn.c +++ b/src/bfcp/conn.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #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: diff --git a/src/http/client.c b/src/http/client.c index 2e1787d..2bac92e 100644 --- a/src/http/client.c +++ b/src/http/client.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/src/http/server.c b/src/http/server.c index db63c06..6abd879 100644 --- a/src/http/server.c +++ b/src/http/server.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/src/main/init.c b/src/main/init.c index 772d2a9..d957aea 100644 --- a/src/main/init.c +++ b/src/main/init.c @@ -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 } diff --git a/src/main/main.h b/src/main/main.h index df633d8..5c0b688 100644 --- a/src/main/main.h +++ b/src/main/main.h @@ -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 diff --git a/src/main/mod.mk b/src/main/mod.mk index 91a6285..7adffae 100644 --- a/src/main/mod.mk +++ b/src/main/mod.mk @@ -11,3 +11,7 @@ SRCS += main/method.c ifneq ($(HAVE_EPOLL),) SRCS += main/epoll.c endif + +ifneq ($(USE_OPENSSL),) +SRCS += main/openssl.c +endif diff --git a/src/main/openssl.c b/src/main/openssl.c new file mode 100644 index 0000000..4140dce --- /dev/null +++ b/src/main/openssl.c @@ -0,0 +1,140 @@ +/** + * @file openssl.c OpenSSL initialisation and multi-threading routines + * + * Copyright (C) 2010 Creytiv.com + */ +#ifdef HAVE_SIGNAL +#include +#endif +#ifdef HAVE_PTHREAD +#include +#endif +#include +#include +#include +#include +#include +#include +#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 #include #include +#include #include #include #include diff --git a/src/srtp/srtp.h b/src/srtp/srtp.h index 80ecca4..9cb94a3 100644 --- a/src/srtp/srtp.h +++ b/src/srtp/srtp.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 */ diff --git a/src/tls/mod.mk b/src/tls/mod.mk index 299b6f6..e3ab31c 100644 --- a/src/tls/mod.mk +++ b/src/tls/mod.mk @@ -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 diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index c5b64ff..253120a 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -4,7 +4,6 @@ * Copyright (C) 2010 Creytiv.com */ #include -#include #define OPENSSL_NO_KRB5 1 #include #include @@ -15,6 +14,8 @@ #include #include #include +#include +#include #include #include #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 } diff --git a/src/tls/openssl/tls.h b/src/tls/openssl/tls.h index 4ad44fc..ee53262 100644 --- a/src/tls/openssl/tls.h +++ b/src/tls/openssl/tls.h @@ -7,5 +7,6 @@ struct tls { SSL_CTX *ctx; + X509 *cert; char *pass; /* password for private key */ }; diff --git a/src/tls/openssl/tls_tcp.c b/src/tls/openssl/tls_tcp.c index 0f4327c..215168d 100644 --- a/src/tls/openssl/tls_tcp.c +++ b/src/tls/openssl/tls_tcp.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #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; } diff --git a/src/tls/openssl/tls_udp.c b/src/tls/openssl/tls_udp.c index d7c4209..ca6197d 100644 --- a/src/tls/openssl/tls_udp.c +++ b/src/tls/openssl/tls_udp.c @@ -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 #include #include -#include #include -#include +#include #include +#include #include #include "tls.h" -#define DEBUG_MODULE "tls_udp" +#define DEBUG_MODULE "dtls" #define DEBUG_LEVEL 5 #include -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; } diff --git a/src/websock/websock.c b/src/websock/websock.c index 5861f3b..e5edffe 100644 --- a/src/websock/websock.c +++ b/src/websock/websock.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include