[svn-commits] branch oej/securertp r8895 - /team/oej/securertp/res/res_srtp.c

svn-commits at lists.digium.com svn-commits at lists.digium.com
Tue Jan 31 14:20:48 MST 2006


Author: oej
Date: Mon Jan 30 06:52:00 2006
New Revision: 8895

URL: http://svn.digium.com/view/asterisk?rev=8895&view=rev
Log:
Actually add the SRTP support. Thanks mikma!

Added:
    team/oej/securertp/res/res_srtp.c

Added: team/oej/securertp/res/res_srtp.c
URL: http://svn.digium.com/view/asterisk/team/oej/securertp/res/res_srtp.c?rev=8895&view=auto
==============================================================================
--- team/oej/securertp/res/res_srtp.c (added)
+++ team/oej/securertp/res/res_srtp.c Mon Jan 30 06:52:00 2006
@@ -1,0 +1,566 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Mikael Magnusson
+ *
+ * Mikael Magnusson <mikma at users.sourceforge.net>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ * 
+ * Builds on libSRTP http://srtp.sourceforge.net
+ */
+
+
+
+/*! \file res_srtp.c 
+ *
+ * \brief Secure RTP (SRTP)
+ * 
+ * Secure RTP (SRTP) 
+ * Specified in RFC 3711.
+ */
+
+/* The SIP channel will automatically use sdescriptions if received in a SDP offer, 
+   and res_srtp is loaded. SRTP with sdescriptions key exchange can be activated 
+  in outgoing offers by setting _SIP_SRTP_SDES=1 in extension.conf before executing Dial
+
+  The dial fails if the callee doesn't support SRTP and sdescriptions.
+
+  exten => 2345,1,Set(_SIP_SRTP_SDES=1)
+  exten => 2345,2,Dial(SIP/1001)
+
+  NOTE: Since chan_sip does not support TLS, this is just a first step
+  towards a secure channel. At this moment, all key exchange will be sent
+  in clear text, making it easy to eavesdrop.
+*/
+
+#include <srtp/srtp.h>
+#include "asterisk.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+#include "asterisk/rtp.h"
+
+struct ast_srtp {
+	struct ast_rtp *rtp;
+	srtp_t session;
+	const struct ast_srtp_cb *cb;
+	void *data;
+	unsigned char buf[8192 + AST_FRIENDLY_OFFSET];
+};
+
+struct ast_srtp_policy {
+	srtp_policy_t sp;
+};
+
+static const char desc[] = "Secure RTP (SRTP)";
+static int srtpdebug = 1;
+static int g_initialized = 0;
+
+/* Exported functions */
+int load_module(void);
+int unload_module(void);
+int usecount(void);
+char *description(void);
+
+/* SRTP functions */
+static int res_srtp_create(struct ast_srtp **srtp,
+			   struct ast_rtp *rtp,
+			   struct ast_srtp_policy *policy);
+static void res_srtp_destroy(struct ast_srtp *srtp);
+static int res_srtp_add_stream(struct ast_srtp *srtp,
+			       struct ast_srtp_policy *policy);
+
+static int res_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len);
+static int res_srtp_protect(struct ast_srtp *srtp, void **buf, int *len);
+static int res_srtp_get_random(unsigned char *key, size_t len);
+static void res_srtp_set_cb(struct ast_srtp *srtp,
+			    const struct ast_srtp_cb *cb, void *data);
+
+/* Policy functions */
+static struct ast_srtp_policy *res_srtp_policy_alloc(void);
+static void res_srtp_policy_destroy(struct ast_srtp_policy *policy);
+static int res_srtp_policy_set_suite(struct ast_srtp_policy *policy,
+				     enum ast_srtp_suite suite);
+static int res_srtp_policy_set_master_key(struct ast_srtp_policy *policy,
+			      const unsigned char *key, size_t key_len,
+			      const unsigned char *salt, size_t salt_len);
+static int res_srtp_policy_set_encr_alg(struct ast_srtp_policy *policy,
+					enum ast_srtp_ealg ealg);
+static int res_srtp_policy_set_auth_alg(struct ast_srtp_policy *policy,
+					enum ast_srtp_aalg aalg);
+static void res_srtp_policy_set_encr_keylen(struct ast_srtp_policy *policy,
+					    int ekeyl);
+static void res_srtp_policy_set_auth_keylen(struct ast_srtp_policy *policy,
+					    int akeyl);
+static void res_srtp_policy_set_srtp_auth_taglen(struct ast_srtp_policy *policy,
+						 int autht);
+static void res_srtp_policy_set_srtp_encr_enable(struct ast_srtp_policy *policy,
+						 int enable);
+static void res_srtp_policy_set_srtcp_encr_enable(struct ast_srtp_policy *policy,
+						  int enable);
+static void res_srtp_policy_set_srtp_auth_enable(struct ast_srtp_policy *policy,
+						 int enable);
+static void res_srtp_policy_set_ssrc(struct ast_srtp_policy *policy,
+				     unsigned long ssrc, int inbound);
+
+static struct ast_srtp_res srtp_res = {
+	.create = res_srtp_create,
+	.destroy = res_srtp_destroy,
+	.add_stream = res_srtp_add_stream,
+	.set_cb = res_srtp_set_cb,
+	.unprotect = res_srtp_unprotect,
+	.protect = res_srtp_protect,
+	.get_random = res_srtp_get_random
+};
+
+static struct ast_srtp_policy_res policy_res = {
+	.alloc = res_srtp_policy_alloc,
+	.destroy = res_srtp_policy_destroy,
+	.set_suite = res_srtp_policy_set_suite,
+	.set_master_key = res_srtp_policy_set_master_key,
+	.set_encr_alg = res_srtp_policy_set_encr_alg,
+	.set_auth_alg = res_srtp_policy_set_auth_alg,
+	.set_encr_keylen = res_srtp_policy_set_encr_keylen,
+	.set_auth_keylen = res_srtp_policy_set_auth_keylen,
+	.set_srtp_auth_taglen = res_srtp_policy_set_srtp_auth_taglen,
+	.set_srtp_encr_enable = res_srtp_policy_set_srtp_encr_enable,
+	.set_srtcp_encr_enable = res_srtp_policy_set_srtcp_encr_enable,
+	.set_srtp_auth_enable = res_srtp_policy_set_srtp_auth_enable,
+	.set_ssrc = res_srtp_policy_set_ssrc
+};
+
+static const char *srtp_errstr(int err)
+{
+	switch(err) {
+	case err_status_ok:
+		return "nothing to report";
+	case err_status_fail:
+		return "unspecified failure";
+	case err_status_bad_param:
+		return "unsupported parameter";
+	case err_status_alloc_fail:
+		return "couldn't allocate memory";
+	case err_status_dealloc_fail:
+		return "couldn't deallocate properly";
+	case err_status_init_fail:
+		return "couldn't initialize";
+	case err_status_terminus:
+		return "can't process as much data as requested";
+	case err_status_auth_fail:
+		return "authentication failure";
+	case err_status_cipher_fail:
+		return "cipher failure";
+	case err_status_replay_fail:
+		return "replay check failed (bad index)";
+	case err_status_replay_old:
+		return "replay check failed (index too old)";
+	case err_status_algo_fail:
+		return "algorithm failed test routine";
+	case err_status_no_such_op:
+		return "unsupported operation";
+	case err_status_no_ctx:
+		return "no appropriate context found";
+	case err_status_cant_check:
+		return "unable to perform desired validation";
+	case err_status_key_expired:
+		return "can't use key any more";
+	default:
+		return "unknown";
+	}
+}
+
+static struct ast_srtp *res_srtp_new(void)
+{
+	struct ast_srtp *srtp = malloc(sizeof(*srtp));
+	memset(srtp, 0, sizeof(*srtp));
+	return srtp;
+}
+
+/*
+  struct ast_srtp_policy
+*/
+static void srtp_event_cb(srtp_event_data_t *data)
+{
+	switch (data->event) {
+	case event_ssrc_collision: {
+		if (option_debug || srtpdebug) {
+			ast_log(LOG_DEBUG, "SSRC collision ssrc:%u dir:%d\n",
+				ntohl(data->stream->ssrc),
+				data->stream->direction);
+		}
+		break;
+	}
+	case event_key_soft_limit: {
+		if (option_debug || srtpdebug) {
+			ast_log(LOG_DEBUG, "event_key_soft_limit\n");
+		}
+		break;
+	}
+	case event_key_hard_limit: {
+		if (option_debug || srtpdebug) {
+			ast_log(LOG_DEBUG, "event_key_hard_limit\n");
+		}
+		break;
+	}
+	case event_packet_index_limit: {
+		if (option_debug || srtpdebug) {
+			ast_log(LOG_DEBUG, "event_packet_index_limit\n");
+		}
+		break;
+	}
+	}
+}
+
+static void res_srtp_policy_set_ssrc(struct ast_srtp_policy *policy,
+				     unsigned long ssrc, int inbound)
+{
+	if (ssrc) {
+		policy->sp.ssrc.type = ssrc_specific;
+		policy->sp.ssrc.value = ssrc;
+	} else {
+		policy->sp.ssrc.type =
+			inbound ? ssrc_any_inbound : ssrc_any_outbound;
+	}
+}
+
+static struct ast_srtp_policy *res_srtp_policy_alloc()
+{
+	struct ast_srtp_policy *tmp = malloc(sizeof(*tmp));
+
+	memset(tmp, 0, sizeof(*tmp));
+	return tmp;
+}
+
+static void
+res_srtp_policy_destroy(struct ast_srtp_policy *policy)
+{
+	if (policy->sp.key) {
+		free(policy->sp.key);
+		policy->sp.key = NULL;
+	}
+	free(policy);
+}
+
+static int policy_set_suite(crypto_policy_t *p, enum ast_srtp_suite suite)
+{
+	switch (suite) {
+	case AST_AES_CM_128_HMAC_SHA1_80:
+		p->cipher_type = AES_128_ICM;
+		p->cipher_key_len = 30;
+		p->auth_type = HMAC_SHA1;
+		p->auth_key_len = 20;
+		p->auth_tag_len = 10;
+		p->sec_serv = sec_serv_conf_and_auth;
+		return 0;
+
+	case AST_AES_CM_128_HMAC_SHA1_32:
+		p->cipher_type = AES_128_ICM;
+		p->cipher_key_len = 30;
+		p->auth_type = HMAC_SHA1;
+		p->auth_key_len = 20;
+		p->auth_tag_len = 4;
+		p->sec_serv = sec_serv_conf_and_auth;
+		return 0;
+
+	default:
+		ast_log(LOG_ERROR, "Invalid crypto suite: %d\n", suite);
+		return -1;
+	}
+}
+
+static int
+res_srtp_policy_set_suite(struct ast_srtp_policy *policy,
+			  enum ast_srtp_suite suite)
+{
+	int res = policy_set_suite(&policy->sp.rtp, suite) |
+		policy_set_suite(&policy->sp.rtcp, suite);
+
+	return res;
+}
+
+static int
+res_srtp_policy_set_master_key(struct ast_srtp_policy *policy,
+			       const unsigned char *key, size_t key_len,
+			       const unsigned char *salt, size_t salt_len)
+{
+	size_t size = key_len + salt_len;
+	unsigned char *master_key = NULL;
+
+	if (policy->sp.key) {
+		free(policy->sp.key);
+		policy->sp.key = NULL;
+	}
+
+	master_key = malloc(size);
+
+	memcpy(master_key, key, key_len);
+	memcpy(master_key + key_len, salt, salt_len);
+
+	policy->sp.key = master_key;
+	return 0;
+}
+
+static int
+res_srtp_policy_set_encr_alg(struct ast_srtp_policy *policy,
+			     enum ast_srtp_ealg ealg)
+{
+	int type = -1;
+
+	switch (ealg) {
+	case AST_MIKEY_SRTP_EALG_NULL:
+		type = NULL_CIPHER;
+		break;
+	case AST_MIKEY_SRTP_EALG_AESCM:
+		type = AES_128_ICM;
+		break;
+	default:
+		return -1;
+	}
+
+	policy->sp.rtp.cipher_type = type;
+	policy->sp.rtcp.cipher_type = type;
+	return 0;
+}
+
+static int
+res_srtp_policy_set_auth_alg(struct ast_srtp_policy *policy,
+			     enum ast_srtp_aalg aalg)
+{
+	int type = -1;
+
+	switch (aalg) {
+	case AST_MIKEY_SRTP_AALG_NULL:
+		type = NULL_AUTH;
+		break;
+	case AST_MIKEY_SRTP_AALG_SHA1HMAC:
+		type = HMAC_SHA1;
+		break;
+	default:
+		return -1;
+	}
+
+	policy->sp.rtp.auth_type = type;
+	policy->sp.rtcp.auth_type = type;
+	return 0;
+}
+
+static void
+res_srtp_policy_set_encr_keylen(struct ast_srtp_policy *policy, int ekeyl)
+{
+	policy->sp.rtp.cipher_key_len = ekeyl;
+	policy->sp.rtcp.cipher_key_len = ekeyl;
+}
+
+static void
+res_srtp_policy_set_auth_keylen(struct ast_srtp_policy *policy, int akeyl)
+{
+	policy->sp.rtp.auth_key_len = akeyl;
+	policy->sp.rtcp.auth_key_len = akeyl;
+}
+
+static void
+res_srtp_policy_set_srtp_auth_taglen(struct ast_srtp_policy *policy, int autht)
+{
+	policy->sp.rtp.auth_tag_len = autht;
+	policy->sp.rtcp.auth_tag_len = autht;
+	
+}
+
+static void
+res_srtp_policy_set_srtp_encr_enable(struct ast_srtp_policy *policy, int enable)
+{
+	int serv = enable ? sec_serv_conf : sec_serv_none;
+	policy->sp.rtp.sec_serv = 
+		(policy->sp.rtp.sec_serv & ~sec_serv_conf) | serv;
+}
+
+static void
+res_srtp_policy_set_srtcp_encr_enable(struct ast_srtp_policy *policy, int enable)
+{
+	int serv = enable ? sec_serv_conf : sec_serv_none;
+	policy->sp.rtcp.sec_serv = 
+		(policy->sp.rtcp.sec_serv & ~sec_serv_conf) | serv;
+}
+
+static void
+res_srtp_policy_set_srtp_auth_enable(struct ast_srtp_policy *policy, int enable)
+{
+	int serv = enable ? sec_serv_auth : sec_serv_none;
+	policy->sp.rtp.sec_serv = 
+		(policy->sp.rtp.sec_serv & ~sec_serv_auth) | serv;
+}
+
+
+static int res_srtp_get_random(unsigned char *key, size_t len)
+{
+	int res = crypto_get_random(key, len);
+
+	return res != err_status_ok ? -1: 0;
+}
+
+static void res_srtp_set_cb(struct ast_srtp *srtp,
+			    const struct ast_srtp_cb *cb, void *data)
+{
+	if (!srtp)
+		return;
+	
+	srtp->cb = cb;
+	srtp->data = data;
+}
+
+
+/* Vtable functions */
+
+static int
+res_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len)
+{
+	int res = 0;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		srtp_hdr_t *header = buf;
+		
+		res = srtp_unprotect(srtp->session, buf, len);
+		if (res != err_status_no_ctx)
+			break;
+		
+		if (srtp->cb && srtp->cb->no_ctx) {
+			if (srtp->cb->no_ctx(srtp->rtp, ntohl(header->ssrc), srtp->data) < 0) {
+				break;
+			}
+			
+		} else {
+			break;
+		}
+	}
+	
+	if (res != err_status_ok) {
+		if (option_debug || srtpdebug) {
+			ast_log(LOG_DEBUG, "SRTP unprotect: %s\n",
+				 srtp_errstr(res));
+		}
+		return -1;
+	}
+
+	return *len;
+}
+
+static int
+res_srtp_protect(struct ast_srtp *srtp, void **buf, int *len)
+{
+	int res = 0;
+
+	if ((*len + SRTP_MAX_TRAILER_LEN) > sizeof(srtp->buf))
+		return -1;
+
+	memcpy(srtp->buf, *buf, *len);
+
+	res = srtp_protect(srtp->session, srtp->buf, len);
+
+	if (res != err_status_ok) {
+		if (option_debug || srtpdebug) {
+			ast_log(LOG_DEBUG, "SRTP protect: %s\n",
+				 srtp_errstr(res));
+		}
+		return -1;
+	}
+
+	*buf = srtp->buf;
+	return *len;
+}
+
+static int
+res_srtp_create(struct ast_srtp **srtp, struct ast_rtp *rtp,
+		struct ast_srtp_policy *policy)
+{
+	int res;
+	struct ast_srtp *temp = res_srtp_new();
+
+	res = srtp_create(&temp->session, &policy->sp);
+	if (res != err_status_ok) {
+		return -1;
+	}
+	
+	temp->rtp = rtp;
+	*srtp = temp;
+
+	return 0;
+}
+
+static void
+res_srtp_destroy(struct ast_srtp *srtp)
+{
+	if (srtp->session) {
+		srtp_dealloc(srtp->session);
+	}
+
+	free(srtp);
+}
+
+static int
+res_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *policy)
+{
+	int res;
+	
+	res = srtp_add_stream(srtp->session, &policy->sp);
+	if (res != err_status_ok)
+		return -1;
+
+	return 0;
+}
+
+static int res_srtp_init(void)
+{
+	int res;
+
+	if (g_initialized)
+		return 0;
+
+	res = srtp_init();
+	if (res != err_status_ok)
+		return -1;
+	
+	srtp_install_event_handler(srtp_event_cb);
+
+	return ast_rtp_register_srtp(&srtp_res, &policy_res);
+}
+
+
+/*
+ * Exported functions
+ */
+
+int load_module(void)
+{
+	return  res_srtp_init();
+}
+
+int unload_module(void)
+{
+	return ast_rtp_unregister_srtp(&srtp_res, &policy_res);
+}
+
+int usecount(void)
+{
+	return 1;
+}
+
+char *description(void)
+{
+	return (char *)desc;
+}
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}



More information about the svn-commits mailing list