summaryrefslogtreecommitdiff
path: root/modules/dtls_srtp
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2014-02-09 11:50:07 +0100
committerAlfred E. Heggestad <aeh@db.org>2014-02-09 11:50:07 +0100
commit98bf08bdcf2edd9d397f32650a8bfe62186fbecf (patch)
treeebc6ec71f44bff8c42e4eefced61948623df02fc /modules/dtls_srtp
parente6ad5cf4401b860ba402d4b7b3c7c254bc87a019 (diff)
baresip 0.4.10
Diffstat (limited to 'modules/dtls_srtp')
-rw-r--r--modules/dtls_srtp/dtls.c234
-rw-r--r--modules/dtls_srtp/dtls_srtp.c403
-rw-r--r--modules/dtls_srtp/dtls_srtp.h59
-rw-r--r--modules/dtls_srtp/module.mk11
-rw-r--r--modules/dtls_srtp/srtp.c232
-rw-r--r--modules/dtls_srtp/tls_udp.c391
6 files changed, 1330 insertions, 0 deletions
diff --git a/modules/dtls_srtp/dtls.c b/modules/dtls_srtp/dtls.c
new file mode 100644
index 0000000..f787cd2
--- /dev/null
+++ b/modules/dtls_srtp/dtls.c
@@ -0,0 +1,234 @@
+/**
+ * @file dtls.c DTLS functions
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#define OPENSSL_NO_KRB5 1
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <re.h>
+#include <baresip.h>
+#include "dtls_srtp.h"
+
+
+#define DEBUG_MODULE "dtls_srtp"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/* note: shadow struct in libre's tls module */
+struct tls {
+ SSL_CTX *ctx;
+ char *pass; /* password for private key */
+ /* ... */
+ EVP_PKEY *key;
+ X509 *x;
+};
+
+
+static void destructor(void *data)
+{
+ struct tls *tls = data;
+
+ if (tls->ctx)
+ SSL_CTX_free(tls->ctx);
+
+ if (tls->x)
+ X509_free(tls->x);
+ if (tls->key)
+ EVP_PKEY_free(tls->key);
+
+ mem_deref(tls->pass);
+}
+
+
+static int cert_generate(X509 *x, EVP_PKEY *privkey, const char *aor,
+ int expire_days)
+{
+ X509_EXTENSION *ext;
+ X509_NAME *subj;
+ int ret;
+ int err = ENOMEM;
+
+ subj = X509_NAME_new();
+ if (!subj)
+ goto out;
+
+ X509_set_version(x, 2);
+
+ ASN1_INTEGER_set(X509_get_serialNumber(x), rand_u32());
+
+ ret = X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
+ (unsigned char *)aor,
+ (int)strlen(aor), -1, 0);
+ if (!ret)
+ goto out;
+
+ if (!X509_set_issuer_name(x, subj) ||
+ !X509_set_subject_name(x, subj))
+ goto out;
+
+ X509_gmtime_adj(X509_get_notBefore(x), 0);
+ X509_gmtime_adj(X509_get_notAfter(x), 60*60*24*expire_days);
+
+ if (!X509_set_pubkey(x, privkey))
+ goto out;
+
+ ext = X509V3_EXT_conf_nid(NULL, NULL,
+ NID_basic_constraints, "CA:FALSE");
+ if (1 != X509_add_ext(x, ext, -1))
+ goto out;
+ X509_EXTENSION_free(ext);
+
+ err = 0;
+
+ out:
+ if (subj)
+ X509_NAME_free(subj);
+
+ return err;
+}
+
+
+static int tls_gen_selfsigned_cert(struct tls *tls, const char *aor)
+{
+ RSA *rsa;
+ int err = ENOMEM;
+
+ rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL);
+ if (!rsa)
+ goto out;
+
+ tls->key = EVP_PKEY_new();
+ if (!tls->key)
+ goto out;
+ if (!EVP_PKEY_set1_RSA(tls->key, rsa))
+ goto out;
+
+ tls->x = X509_new();
+ if (!tls->x)
+ goto out;
+
+ if (cert_generate(tls->x, tls->key, aor, 365))
+ goto out;
+
+ /* Sign the certificate */
+ if (!X509_sign(tls->x, tls->key, EVP_sha1()))
+ goto out;
+
+ err = 0;
+
+ out:
+ if (rsa)
+ RSA_free(rsa);
+
+ return err;
+}
+
+
+int dtls_alloc_selfsigned(struct tls **tlsp, const char *aor,
+ const char *srtp_profiles)
+{
+ struct tls *tls;
+ int r, err;
+
+ if (!tlsp || !aor)
+ return EINVAL;
+
+ tls = mem_zalloc(sizeof(*tls), destructor);
+ if (!tls)
+ return ENOMEM;
+
+ SSL_library_init();
+
+ tls->ctx = SSL_CTX_new(DTLSv1_method());
+ if (!tls->ctx) {
+ err = ENOMEM;
+ goto out;
+ }
+
+#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
+ SSL_CTX_set_verify_depth(tls->ctx, 1);
+#endif
+
+ SSL_CTX_set_read_ahead(tls->ctx, 1);
+
+ /* Generate self-signed certificate */
+ err = tls_gen_selfsigned_cert(tls, aor);
+ if (err) {
+ DEBUG_WARNING("failed to generate certificate (%s): %m\n",
+ aor, err);
+ goto out;
+ }
+
+ r = SSL_CTX_use_certificate(tls->ctx, tls->x);
+ if (r != 1) {
+ err = EINVAL;
+ goto out;
+ }
+
+ r = SSL_CTX_use_PrivateKey(tls->ctx, tls->key);
+ if (r != 1) {
+ err = EINVAL;
+ goto out;
+ }
+
+ if (0 != SSL_CTX_set_tlsext_use_srtp(tls->ctx, srtp_profiles)) {
+ DEBUG_WARNING("could not enable SRTP for profiles '%s'\n",
+ srtp_profiles);
+ err = ENOSYS;
+ goto out;
+ }
+
+ err = 0;
+ out:
+ if (err)
+ mem_deref(tls);
+ else
+ *tlsp = tls;
+
+ return err;
+}
+
+
+int dtls_print_sha1_fingerprint(struct re_printf *pf, const struct tls *tls)
+{
+ uint8_t md[64];
+ unsigned int i, len;
+ int err = 0;
+
+ if (!pf || !tls)
+ return EINVAL;
+
+ len = sizeof(md);
+ if (1 != X509_digest(tls->x, EVP_sha1(), md, &len))
+ return ENOENT;
+
+ for (i=0; i<len; i++) {
+ err |= re_hprintf(pf, "%s%02x", i==0?"":":", md[i]);
+ }
+
+ return err;
+}
+
+
+int dtls_print_sha256_fingerprint(struct re_printf *pf, const struct tls *tls)
+{
+ uint8_t md[64];
+ unsigned int i, len;
+ int err = 0;
+
+ if (!pf || !tls)
+ return EINVAL;
+
+ len = sizeof(md);
+ if (1 != X509_digest(tls->x, EVP_sha256(), md, &len))
+ return ENOENT;
+
+ for (i=0; i<len; i++) {
+ err |= re_hprintf(pf, "%s%02x", i==0?"":":", md[i]);
+ }
+
+ return err;
+}
diff --git a/modules/dtls_srtp/dtls_srtp.c b/modules/dtls_srtp/dtls_srtp.c
new file mode 100644
index 0000000..b25c143
--- /dev/null
+++ b/modules/dtls_srtp/dtls_srtp.c
@@ -0,0 +1,403 @@
+/**
+ * @file dtls_srtp.c DTLS-SRTP media encryption
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#if defined (__GNUC__) && !defined (asm)
+#define asm __asm__ /* workaround */
+#endif
+#include <srtp/srtp.h>
+#include <re.h>
+#include <baresip.h>
+#include <string.h>
+#include "dtls_srtp.h"
+
+
+/*
+ * STACK Diagram:
+ *
+ * application
+ * |
+ * |
+ * [DTLS] [SRTP]
+ * \ /
+ * \ /
+ * \ /
+ * \/
+ * ( TURN/ICE )
+ * |
+ * |
+ * socket
+ *
+ */
+
+struct menc_sess {
+ struct sdp_session *sdp;
+ bool offerer;
+ menc_error_h *errorh;
+ void *arg;
+};
+
+/* media */
+struct dtls_srtp {
+ struct sock sockv[2];
+ const struct menc_sess *sess;
+ struct sdp_media *sdpm;
+ struct tmr tmr;
+ bool started;
+ bool active;
+ bool mux;
+};
+
+static struct tls *tls;
+static const char* srtp_profiles =
+ "SRTP_AES128_CM_SHA1_80:"
+ "SRTP_AES128_CM_SHA1_32";
+
+
+static void sess_destructor(void *arg)
+{
+ struct menc_sess *sess = arg;
+
+ mem_deref(sess->sdp);
+}
+
+
+static void destructor(void *arg)
+{
+ struct dtls_srtp *st = arg;
+ size_t i;
+
+ tmr_cancel(&st->tmr);
+
+ for (i=0; i<2; i++) {
+ struct sock *s = &st->sockv[i];
+
+ mem_deref(s->uh_srtp);
+ mem_deref(s->dtls);
+ mem_deref(s->app_sock); /* must be freed last */
+ mem_deref(s->tx);
+ mem_deref(s->rx);
+ }
+
+ mem_deref(st->sdpm);
+}
+
+
+static bool verify_fingerprint(const struct sdp_session *sess,
+ const struct sdp_media *media,
+ struct dtls_flow *tc)
+{
+ struct pl hash;
+ char hashstr[32];
+ uint8_t md_sdp[64];
+ size_t sz_sdp = sizeof(md_sdp);
+ struct tls_fingerprint tls_fp;
+
+ if (sdp_fingerprint_decode(sdp_rattr(sess, media, "fingerprint"),
+ &hash, md_sdp, &sz_sdp))
+ return false;
+
+ pl_strcpy(&hash, hashstr, sizeof(hashstr));
+
+ if (dtls_get_remote_fingerprint(tc, hashstr, &tls_fp)) {
+ warning("dtls_srtp: could not get DTLS fingerprint\n");
+ return false;
+ }
+
+ if (sz_sdp != tls_fp.len || 0 != memcmp(md_sdp, tls_fp.md, sz_sdp)) {
+ warning("dtls_srtp: %s fingerprint mismatch\n", hashstr);
+ info("DTLS: %w\n", tls_fp.md, (size_t)tls_fp.len);
+ info("SDP: %w\n", md_sdp, sz_sdp);
+ return false;
+ }
+
+ info("dtls_srtp: verified %s fingerprint OK\n", hashstr);
+
+ return true;
+}
+
+
+static void dtls_established_handler(int err, struct dtls_flow *flow,
+ const char *profile,
+ const struct key *client_key,
+ const struct key *server_key,
+ void *arg)
+{
+ struct sock *sock = arg;
+ const struct dtls_srtp *ds = sock->ds;
+
+ if (!verify_fingerprint(ds->sess->sdp, ds->sdpm, flow)) {
+ warning("dtls_srtp: could not verify remote fingerprint\n");
+ if (ds->sess->errorh)
+ ds->sess->errorh(EPIPE, ds->sess->arg);
+ return;
+ }
+
+ sock->negotiated = true;
+
+ info("dtls_srtp: ---> DTLS-SRTP complete (%s/%s) Profile=%s\n",
+ sdp_media_name(ds->sdpm),
+ sock->is_rtp ? "RTP" : "RTCP", profile);
+
+ err |= srtp_stream_add(&sock->tx, profile,
+ ds->active ? client_key : server_key,
+ true);
+
+ err |= srtp_stream_add(&sock->rx, profile,
+ ds->active ? server_key : client_key,
+ false);
+
+ err |= srtp_install(sock);
+ if (err) {
+ warning("dtls_srtp: srtp_install: %m\n", err);
+ }
+}
+
+
+static int session_alloc(struct menc_sess **sessp,
+ struct sdp_session *sdp, bool offerer,
+ menc_error_h *errorh, void *arg)
+{
+ struct menc_sess *sess;
+ int err;
+
+ if (!sessp || !sdp)
+ return EINVAL;
+
+ sess = mem_zalloc(sizeof(*sess), sess_destructor);
+ if (!sess)
+ return ENOMEM;
+
+ sess->sdp = mem_ref(sdp);
+ sess->offerer = offerer;
+ sess->errorh = errorh;
+ sess->arg = arg;
+
+ /* RFC 4145 */
+ err = sdp_session_set_lattr(sdp, true, "setup",
+ offerer ? "actpass" : "active");
+ if (err)
+ goto out;
+
+ /* RFC 4572 */
+ err = sdp_session_set_lattr(sdp, true, "fingerprint", "SHA-1 %H",
+ dtls_print_sha1_fingerprint, tls);
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(sess);
+ else
+ *sessp = sess;
+
+ return err;
+}
+
+
+static int media_start_sock(struct sock *sock, struct sdp_media *sdpm)
+{
+ struct sa raddr;
+ int err = 0;
+
+ if (!sock->app_sock || sock->negotiated || sock->dtls)
+ return 0;
+
+ if (sock->is_rtp)
+ raddr = *sdp_media_raddr(sdpm);
+ else
+ sdp_media_raddr_rtcp(sdpm, &raddr);
+
+ if (sa_isset(&raddr, SA_ALL)) {
+
+ err = dtls_flow_alloc(&sock->dtls, tls, sock->app_sock,
+ dtls_established_handler, sock);
+ if (err)
+ return err;
+
+ err = dtls_flow_start(sock->dtls, &raddr, sock->ds->active);
+ }
+
+ return err;
+}
+
+
+static int media_start(struct dtls_srtp *st, struct sdp_media *sdpm)
+{
+ int err = 0;
+
+ if (st->started)
+ return 0;
+
+ debug("dtls_srtp: media_start: '%s' mux=%d, active=%d\n",
+ sdp_media_name(sdpm), st->mux, st->active);
+
+ if (!sdp_media_has_media(sdpm))
+ return 0;
+
+ err = media_start_sock(&st->sockv[0], sdpm);
+
+ if (!st->mux)
+ err |= media_start_sock(&st->sockv[1], sdpm);
+
+ if (err)
+ return err;
+
+ st->started = true;
+
+ return 0;
+}
+
+
+static void timeout(void *arg)
+{
+ struct dtls_srtp *st = arg;
+
+ media_start(st, st->sdpm);
+}
+
+
+static int media_alloc(struct menc_media **mp, struct menc_sess *sess,
+ struct rtp_sock *rtp, int proto,
+ void *rtpsock, void *rtcpsock,
+ struct sdp_media *sdpm)
+{
+ struct dtls_srtp *st;
+ const char *setup, *fingerprint;
+ int err = 0;
+ unsigned i;
+ (void)rtp;
+
+ if (!mp || !sess || proto != IPPROTO_UDP)
+ return EINVAL;
+
+ st = (struct dtls_srtp *)*mp;
+ if (st)
+ goto setup;
+
+ st = mem_zalloc(sizeof(*st), destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->sess = sess;
+ st->sdpm = mem_ref(sdpm);
+ st->sockv[0].app_sock = mem_ref(rtpsock);
+ st->sockv[1].app_sock = mem_ref(rtcpsock);
+
+ for (i=0; i<2; i++)
+ st->sockv[i].ds = st;
+
+ st->sockv[0].is_rtp = true;
+ st->sockv[1].is_rtp = false;
+
+ if (err) {
+ mem_deref(st);
+ return err;
+ }
+ else
+ *mp = (struct menc_media *)st;
+
+ setup:
+ st->mux = (rtpsock == rtcpsock);
+
+ setup = sdp_rattr(st->sess->sdp, st->sdpm, "setup");
+ if (setup) {
+ st->active = !(0 == str_casecmp(setup, "active"));
+
+ /* note: we need to wait for ICE to settle ... */
+ tmr_start(&st->tmr, 100, timeout, st);
+ }
+
+ /* SDP offer/answer on fingerprint attribute */
+ fingerprint = sdp_rattr(st->sess->sdp, st->sdpm, "fingerprint");
+ if (fingerprint) {
+
+ struct pl hash;
+
+ err = sdp_fingerprint_decode(fingerprint, &hash, NULL, NULL);
+ if (err)
+ return err;
+
+ if (0 == pl_strcasecmp(&hash, "SHA-1")) {
+ err = sdp_media_set_lattr(st->sdpm, true,
+ "fingerprint", "SHA-1 %H",
+ dtls_print_sha1_fingerprint,
+ tls);
+ }
+ else if (0 == pl_strcasecmp(&hash, "SHA-256")) {
+ err = sdp_media_set_lattr(st->sdpm, true,
+ "fingerprint", "SHA-256 %H",
+ dtls_print_sha256_fingerprint,
+ tls);
+ }
+ else {
+ info("dtls_srtp: unsupported fingerprint hash `%r'\n",
+ &hash);
+ return EPROTO;
+ }
+ }
+
+ return err;
+}
+
+
+static struct menc dtls_srtp = {
+ LE_INIT, "dtls_srtp", "UDP/TLS/RTP/SAVP", session_alloc, media_alloc
+};
+
+static struct menc dtls_srtpf = {
+ LE_INIT, "dtls_srtpf", "UDP/TLS/RTP/SAVPF", session_alloc, media_alloc
+};
+
+static struct menc dtls_srtp2 = {
+ /* note: temp for Webrtc interop */
+ LE_INIT, "srtp-mandf", "RTP/SAVPF", session_alloc, media_alloc
+};
+
+
+static int module_init(void)
+{
+ err_status_t ret;
+ int err;
+
+ crypto_kernel_shutdown();
+ ret = srtp_init();
+ if (err_status_ok != ret) {
+ warning("dtls_srtp: srtp_init() failed: ret=%d\n", ret);
+ return ENOSYS;
+ }
+
+ err = dtls_alloc_selfsigned(&tls, "dtls@baresip", srtp_profiles);
+ if (err)
+ return err;
+
+ menc_register(&dtls_srtpf);
+ menc_register(&dtls_srtp);
+ menc_register(&dtls_srtp2);
+
+ debug("DTLS-SRTP ready with profiles %s\n", srtp_profiles);
+
+ return 0;
+}
+
+
+static int module_close(void)
+{
+ menc_unregister(&dtls_srtp);
+ menc_unregister(&dtls_srtpf);
+ menc_unregister(&dtls_srtp2);
+ tls = mem_deref(tls);
+ crypto_kernel_shutdown();
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(dtls_srtp) = {
+ "dtls_srtp",
+ "menc",
+ module_init,
+ module_close
+};
diff --git a/modules/dtls_srtp/dtls_srtp.h b/modules/dtls_srtp/dtls_srtp.h
new file mode 100644
index 0000000..6f85bf3
--- /dev/null
+++ b/modules/dtls_srtp/dtls_srtp.h
@@ -0,0 +1,59 @@
+/**
+ * @file dtls_srtp.h DTLS-SRTP Internal api
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+enum {
+ LAYER_SRTP = 20,
+ LAYER_DTLS = 20, /* must be above zero */
+};
+
+struct sock {
+ const struct dtls_srtp *ds;
+ struct dtls_flow *dtls;
+ struct srtp_stream *tx;
+ struct srtp_stream *rx;
+ struct udp_helper *uh_srtp;
+ void *app_sock;
+ bool negotiated;
+ bool is_rtp;
+};
+
+struct key {
+ uint8_t key[256];
+ size_t key_len;
+ uint8_t salt[256];
+ size_t salt_len;
+};
+
+
+/* dtls.c */
+int dtls_alloc_selfsigned(struct tls **tlsp, const char *aor,
+ const char *srtp_profile);
+int dtls_print_sha1_fingerprint(struct re_printf *pf, const struct tls *tls);
+int dtls_print_sha256_fingerprint(struct re_printf *pf, const struct tls *tls);
+
+
+/* srtp.c */
+int srtp_stream_add(struct srtp_stream **sp, const char *profile,
+ const struct key *key, bool tx);
+int srtp_install(struct sock *sock);
+
+
+/* tls_udp.c */
+struct dtls_flow;
+
+typedef void (dtls_estab_h)(int err, struct dtls_flow *tc,
+ const char *profile,
+ const struct key *client_key,
+ const struct key *server_key,
+ void *arg);
+
+int dtls_flow_alloc(struct dtls_flow **flowp, struct tls *tls,
+ struct udp_sock *us, dtls_estab_h *estabh, void *arg);
+int dtls_flow_start(struct dtls_flow *flow, const struct sa *peer,
+ bool active);
+int dtls_get_remote_fingerprint(const struct dtls_flow *flow, const char *type,
+ struct tls_fingerprint *fp);
diff --git a/modules/dtls_srtp/module.mk b/modules/dtls_srtp/module.mk
new file mode 100644
index 0000000..87dc952
--- /dev/null
+++ b/modules/dtls_srtp/module.mk
@@ -0,0 +1,11 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+MOD := dtls_srtp
+$(MOD)_SRCS += dtls_srtp.c dtls.c srtp.c tls_udp.c
+$(MOD)_LFLAGS += -lsrtp
+
+include mk/mod.mk
diff --git a/modules/dtls_srtp/srtp.c b/modules/dtls_srtp/srtp.c
new file mode 100644
index 0000000..7f33ccc
--- /dev/null
+++ b/modules/dtls_srtp/srtp.c
@@ -0,0 +1,232 @@
+/**
+ * @file dtls_srtp/srtp.c Secure RTP
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#if defined (__GNUC__) && !defined (asm)
+#define asm __asm__ /* workaround */
+#endif
+#include <srtp/srtp.h>
+#include <re.h>
+#include <baresip.h>
+#include "dtls_srtp.h"
+
+
+#define DEBUG_MODULE "dtls_srtp"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+struct srtp_stream {
+ srtp_policy_t policy;
+ srtp_t srtp;
+ uint8_t key[SRTP_MAX_KEY_LEN];
+};
+
+
+/*
+ * See RFC 5764 figure 3:
+ *
+ * +----------------+
+ * | 127 < B < 192 -+--> forward to RTP
+ * | |
+ * packet --> | 19 < B < 64 -+--> forward to DTLS
+ * | |
+ * | B < 2 -+--> forward to STUN
+ * +----------------+
+ *
+ */
+static inline bool is_rtp_or_rtcp(const struct mbuf *mb)
+{
+ uint8_t b;
+
+ if (mbuf_get_left(mb) < 1)
+ return false;
+
+ b = mbuf_buf(mb)[0];
+
+ return 127 < b && b < 192;
+}
+
+
+static inline bool is_rtcp_packet(const struct mbuf *mb)
+{
+ uint8_t pt;
+
+ if (mbuf_get_left(mb) < 2)
+ return false;
+
+ pt = mbuf_buf(mb)[1] & 0x7f;
+
+ return 64 <= pt && pt <= 95;
+}
+
+
+static int errstatus_print(struct re_printf *pf, err_status_t e)
+{
+ const char *s;
+
+ switch (e) {
+
+ case err_status_ok: s = "ok"; break;
+ case err_status_fail: s = "fail"; break;
+ case err_status_auth_fail: s = "auth_fail"; break;
+ case err_status_cipher_fail: s = "cipher_fail"; break;
+ case err_status_replay_fail: s = "replay_fail"; break;
+
+ default:
+ return re_hprintf(pf, "err=%d", e);
+ }
+
+ return re_hprintf(pf, "%s", s);
+}
+
+
+static void destructor(void *arg)
+{
+ struct srtp_stream *s = arg;
+
+ if (s->srtp)
+ srtp_dealloc(s->srtp);
+}
+
+
+static bool send_handler(int *err, struct sa *dst, struct mbuf *mb, void *arg)
+{
+ struct sock *sock = arg;
+ err_status_t e;
+ int len;
+ (void)dst;
+
+ if (!is_rtp_or_rtcp(mb))
+ return false;
+
+ len = (int)mbuf_get_left(mb);
+
+ if (mbuf_get_space(mb) < ((size_t)len + SRTP_MAX_TRAILER_LEN)) {
+ *err = mbuf_resize(mb, mb->pos + len + SRTP_MAX_TRAILER_LEN);
+ if (*err)
+ return true;
+ }
+
+ if (is_rtcp_packet(mb)) {
+ e = srtp_protect_rtcp(sock->tx->srtp, mbuf_buf(mb), &len);
+ }
+ else {
+ e = srtp_protect(sock->tx->srtp, mbuf_buf(mb), &len);
+ }
+
+ if (err_status_ok != e) {
+ DEBUG_WARNING("send: failed to protect %s-packet"
+ " with %d bytes (%H)\n",
+ is_rtcp_packet(mb) ? "RTCP" : "RTP",
+ len, errstatus_print, e);
+ *err = EPROTO;
+ return false;
+ }
+
+ mbuf_set_end(mb, mb->pos + len);
+
+ return false; /* continue processing */
+}
+
+
+static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg)
+{
+ struct sock *sock = arg;
+ err_status_t e;
+ int len;
+ (void)src;
+
+ if (!is_rtp_or_rtcp(mb))
+ return false;
+
+ len = (int)mbuf_get_left(mb);
+
+ if (is_rtcp_packet(mb)) {
+ e = srtp_unprotect_rtcp(sock->rx->srtp, mbuf_buf(mb), &len);
+ }
+ else {
+ e = srtp_unprotect(sock->rx->srtp, mbuf_buf(mb), &len);
+ }
+
+ if (e != err_status_ok) {
+ DEBUG_WARNING("recv: failed to unprotect %s-packet"
+ " with %d bytes (%H)\n",
+ is_rtcp_packet(mb) ? "RTCP" : "RTP",
+ len, errstatus_print, e);
+ return true; /* error - drop packet */
+ }
+
+ mbuf_set_end(mb, mb->pos + len);
+
+ return false; /* continue processing */
+}
+
+
+int srtp_stream_add(struct srtp_stream **sp, const char *profile,
+ const struct key *key, bool tx)
+{
+ struct srtp_stream *s;
+ err_status_t e;
+ int err = 0;
+
+ if (!sp || !key || key->key_len > SRTP_MAX_KEY_LEN)
+ return EINVAL;
+
+ s = mem_zalloc(sizeof(*s), destructor);
+ if (!s)
+ return ENOMEM;
+
+ memcpy(s->key, key->key, key->key_len);
+ append_salt_to_key(s->key, (unsigned int)key->key_len,
+ (unsigned char *)key->salt,
+ (unsigned int)key->salt_len);
+
+ /* note: policy and key must be on the heap */
+
+ if (0 == str_casecmp(profile, "SRTP_AES128_CM_SHA1_80")) {
+ crypto_policy_set_aes_cm_128_hmac_sha1_80(&s->policy.rtp);
+ crypto_policy_set_aes_cm_128_hmac_sha1_80(&s->policy.rtcp);
+ }
+ else if (0 == str_casecmp(profile, "SRTP_AES128_CM_SHA1_32")) {
+ crypto_policy_set_aes_cm_128_hmac_sha1_32(&s->policy.rtp);
+ crypto_policy_set_aes_cm_128_hmac_sha1_32(&s->policy.rtcp);
+ }
+ else {
+ DEBUG_WARNING("unsupported profile: %s\n", profile);
+ err = ENOSYS;
+ goto out;
+ }
+
+ s->policy.ssrc.type = tx ? ssrc_any_outbound : ssrc_any_inbound;
+ s->policy.key = s->key;
+ s->policy.next = NULL;
+
+ e = srtp_create(&s->srtp, &s->policy);
+ if (err_status_ok != e) {
+ s->srtp = NULL;
+ DEBUG_WARNING("srtp_create() failed. e=%d\n", e);
+ err = ENOMEM;
+ goto out;
+ }
+
+ out:
+ if (err)
+ mem_deref(s);
+ else
+ *sp = s;
+
+ return err;
+}
+
+
+int srtp_install(struct sock *sock)
+{
+ return udp_register_helper(&sock->uh_srtp, sock->app_sock,
+ LAYER_SRTP,
+ send_handler,
+ recv_handler,
+ sock);
+}
diff --git a/modules/dtls_srtp/tls_udp.c b/modules/dtls_srtp/tls_udp.c
new file mode 100644
index 0000000..ada5310
--- /dev/null
+++ b/modules/dtls_srtp/tls_udp.c
@@ -0,0 +1,391 @@
+/**
+ * @file dtls_srtp/tls_udp.c DTLS socket for DTLS-SRTP
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#define OPENSSL_NO_KRB5 1
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <re.h>
+#include "dtls_srtp.h"
+
+
+#define DEBUG_MODULE "tls_udp"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/* note: shadow struct in dtls.c */
+struct tls {
+ SSL_CTX *ctx;
+};
+
+struct dtls_flow {
+ struct udp_helper *uh;
+ struct udp_sock *us;
+ struct tls *tls;
+ struct tmr tmr;
+ struct sa peer;
+ SSL *ssl;
+ BIO *sbio_out;
+ BIO *sbio_in;
+ bool up;
+ dtls_estab_h *estabh;
+ void *arg;
+};
+
+
+static void check_timer(struct dtls_flow *flow);
+
+
+static int bio_create(BIO *b)
+{
+ b->init = 1;
+ b->num = 0;
+ b->ptr = NULL;
+ b->flags = 0;
+
+ return 1;
+}
+
+
+static int bio_destroy(BIO *b)
+{
+ if (!b)
+ return 0;
+
+ b->ptr = NULL;
+ b->init = 0;
+ b->flags = 0;
+
+ return 1;
+}
+
+
+static int bio_write(BIO *b, const char *buf, int len)
+{
+ struct dtls_flow *tc = b->ptr;
+ struct mbuf *mb;
+ enum {SPACE = 4}; /* sizeof TURN channel header */
+ int err;
+
+ mb = mbuf_alloc(SPACE + len);
+ if (!mb)
+ return -1;
+
+ (void)mbuf_fill(mb, 0x00, SPACE);
+ (void)mbuf_write_mem(mb, (void *)buf, len);
+
+ mb->pos = SPACE;
+
+ err = udp_send_helper(tc->us, &tc->peer, mb, tc->uh);
+ if (err) {
+ DEBUG_WARNING("udp_send_helper: %m\n", err);
+ }
+
+ mem_deref(mb);
+
+ return err ? -1 : len;
+}
+
+
+static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+ (void)b;
+ (void)num;
+ (void)ptr;
+
+ if (cmd == BIO_CTRL_FLUSH) {
+ /* The OpenSSL library needs this */
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static struct bio_method_st bio_udp_send = {
+ BIO_TYPE_SOURCE_SINK,
+ "udp_send",
+ bio_write,
+ 0,
+ 0,
+ 0,
+ bio_ctrl,
+ bio_create,
+ bio_destroy,
+ 0
+};
+
+
+static int verify_callback(int ok, X509_STORE_CTX *ctx)
+{
+ (void)ok;
+ (void)ctx;
+ return 1; /* We trust the certificate from peer */
+}
+
+
+#if defined (DTLS_CTRL_HANDLE_TIMEOUT) && defined(DTLS_CTRL_GET_TIMEOUT)
+static void timeout(void *arg)
+{
+ struct dtls_flow *tc = arg;
+
+ DTLSv1_handle_timeout(tc->ssl);
+
+ check_timer(tc);
+}
+#endif
+
+
+static void check_timer(struct dtls_flow *tc)
+{
+#if defined (DTLS_CTRL_HANDLE_TIMEOUT) && defined (DTLS_CTRL_GET_TIMEOUT)
+ struct timeval tv = {0, 0};
+ long x;
+
+ x = DTLSv1_get_timeout(tc->ssl, &tv);
+
+ if (x) {
+ uint64_t delay = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+ tmr_start(&tc->tmr, delay, timeout, tc);
+ }
+
+#else
+ (void)tc;
+#endif
+}
+
+
+static int get_srtp_key_info(const struct dtls_flow *tc, char *name, size_t sz,
+ struct key *client_key, struct key *server_key)
+{
+ SRTP_PROTECTION_PROFILE *sel;
+ const char *keymatexportlabel = "EXTRACTOR-dtls_srtp";
+ uint8_t exportedkeymat[1024], *p;
+ int keymatexportlen;
+ size_t kl = 128, sl = 112;
+
+ sel = SSL_get_selected_srtp_profile(tc->ssl);
+ if (!sel)
+ return ENOENT;
+
+ str_ncpy(name, sel->name, sz);
+
+ kl /= 8;
+ sl /= 8;
+
+ keymatexportlen = (int)(kl + sl)*2;
+ if (keymatexportlen != 60) {
+ DEBUG_WARNING("expected 60 bits, but keying material is %d\n",
+ keymatexportlen);
+ return EINVAL;
+ }
+
+ if (!SSL_export_keying_material(tc->ssl, exportedkeymat,
+ keymatexportlen,
+ keymatexportlabel,
+ strlen(keymatexportlabel),
+ NULL, 0, 0)) {
+ return ENOENT;
+ }
+
+ p = exportedkeymat;
+
+ memcpy(client_key->key, p, kl); p += kl;
+ memcpy(server_key->key, p, kl); p += kl;
+ memcpy(client_key->salt, p, sl); p += sl;
+ memcpy(server_key->salt, p, sl); p += sl;
+
+ client_key->key_len = server_key->key_len = kl;
+ client_key->salt_len = server_key->salt_len = sl;
+
+ return 0;
+}
+
+
+static void destructor(void *arg)
+{
+ struct dtls_flow *flow = arg;
+
+ if (flow->ssl) {
+ (void)SSL_shutdown(flow->ssl);
+ SSL_free(flow->ssl);
+ }
+
+ mem_deref(flow->uh);
+ mem_deref(flow->us);
+
+ tmr_cancel(&flow->tmr);
+}
+
+
+static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg)
+{
+ struct dtls_flow *flow = arg;
+ uint8_t b;
+ int r;
+
+ if (mbuf_get_left(mb) < 1)
+ return false;
+
+ /* ignore non-DTLS packets */
+ b = mb->buf[mb->pos];
+ if (b < 20 || b > 63)
+ return false;
+
+ if (!sa_cmp(src, &flow->peer, SA_ALL))
+ return false;
+
+ /* feed SSL data to the BIO */
+ r = BIO_write(flow->sbio_in, mbuf_buf(mb), (int)mbuf_get_left(mb));
+ if (r <= 0)
+ return true;
+
+ SSL_read(flow->ssl, mbuf_buf(mb), (int)mbuf_get_space(mb));
+
+ if (!flow->up && SSL_state(flow->ssl) == SSL_ST_OK) {
+
+ struct key client_key, server_key;
+ char profile[256];
+ int err;
+
+ flow->up = true;
+
+ err = get_srtp_key_info(flow, profile, sizeof(profile),
+ &client_key, &server_key);
+ if (err) {
+ DEBUG_WARNING("SRTP key info: %m\n", err);
+ return true;
+ }
+
+ flow->estabh(0, flow, profile,
+ &client_key, &server_key, flow->arg);
+ }
+
+ return true;
+}
+
+
+int dtls_flow_alloc(struct dtls_flow **flowp, struct tls *tls,
+ struct udp_sock *us, dtls_estab_h *estabh, void *arg)
+{
+ struct dtls_flow *flow;
+ int err = ENOMEM;
+
+ if (!flowp || !tls || !us || !estabh)
+ return EINVAL;
+
+ flow = mem_zalloc(sizeof(*flow), destructor);
+ if (!flow)
+ return ENOMEM;
+
+ flow->tls = tls;
+ flow->us = mem_ref(us);
+ flow->estabh = estabh;
+ flow->arg = arg;
+
+ err = udp_register_helper(&flow->uh, us, LAYER_DTLS, NULL,
+ recv_handler, flow);
+ if (err)
+ goto out;
+
+ flow->ssl = SSL_new(tls->ctx);
+ if (!flow->ssl)
+ goto out;
+
+ flow->sbio_in = BIO_new(BIO_s_mem());
+ if (!flow->sbio_in)
+ goto out;
+
+ flow->sbio_out = BIO_new(&bio_udp_send);
+ if (!flow->sbio_out) {
+ BIO_free(flow->sbio_in);
+ goto out;
+ }
+ flow->sbio_out->ptr = flow;
+
+ SSL_set_bio(flow->ssl, flow->sbio_in, flow->sbio_out);
+
+ tmr_init(&flow->tmr);
+
+ err = 0;
+
+ out:
+ if (err)
+ mem_deref(flow);
+ else
+ *flowp = flow;
+
+ return err;
+}
+
+
+int dtls_flow_start(struct dtls_flow *flow, const struct sa *peer, bool active)
+{
+ int r, err = 0;
+
+ if (!flow || !peer)
+ return EINVAL;
+
+ flow->peer = *peer;
+
+ if (active) {
+ r = SSL_connect(flow->ssl);
+ if (r < 0) {
+ int ssl_err = SSL_get_error(flow->ssl, r);
+
+ ERR_clear_error();
+
+ if (ssl_err != SSL_ERROR_WANT_READ) {
+ DEBUG_WARNING("SSL_connect() failed"
+ " (err=%d)\n", ssl_err);
+ }
+ }
+
+ check_timer(flow);
+ }
+ else {
+ SSL_set_accept_state(flow->ssl);
+
+ SSL_set_verify_depth(flow->ssl, 0);
+ SSL_set_verify(flow->ssl,
+ SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
+ verify_callback);
+ }
+
+ return err;
+}
+
+
+static const EVP_MD *type2evp(const char *type)
+{
+ 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 dtls_get_remote_fingerprint(const struct dtls_flow *flow, const char *type,
+ struct tls_fingerprint *fp)
+{
+ X509 *x;
+
+ if (!flow || !fp)
+ return EINVAL;
+
+ x = SSL_get_peer_certificate(flow->ssl);
+ if (!x)
+ return EPROTO;
+
+ fp->len = sizeof(fp->md);
+ if (1 != X509_digest(x, type2evp(type), fp->md, &fp->len))
+ return ENOENT;
+
+ return 0;
+}