diff options
author | Alfred E. Heggestad <aeh@db.org> | 2014-02-09 11:50:07 +0100 |
---|---|---|
committer | Alfred E. Heggestad <aeh@db.org> | 2014-02-09 11:50:07 +0100 |
commit | 98bf08bdcf2edd9d397f32650a8bfe62186fbecf (patch) | |
tree | ebc6ec71f44bff8c42e4eefced61948623df02fc /modules/dtls_srtp/dtls_srtp.c | |
parent | e6ad5cf4401b860ba402d4b7b3c7c254bc87a019 (diff) |
baresip 0.4.10
Diffstat (limited to 'modules/dtls_srtp/dtls_srtp.c')
-rw-r--r-- | modules/dtls_srtp/dtls_srtp.c | 403 |
1 files changed, 403 insertions, 0 deletions
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 +}; |