mirror of https://github.com/asterisk/asterisk
After 5 years in mantis and over a year on reviewboard, SRTP support is finally being comitted. This includes generic CHANNEL dialplan functions that work for getting the status of whether a call has secure media or signaling as defined by the underlying channel technology and for setting whether or not a new channel being bridged to a calling channel should have secure signaling or media. See doc/tex/secure-calls.tex for examples. Original patch by mikma, updated for trunk and revised by me. (closes issue #5413) Reported by: mikma Tested by: twilson, notthematrix, hemanshurpatel Review: https://reviewboard.asterisk.org/r/191/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@268894 65c4cc65-6c06-0410-ace0-fbb531ad65f3certified/1.8.6
parent
ebbf166c2d
commit
857814f435
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2006 - 2007, Mikael Magnusson
|
||||
*
|
||||
* Mikael Magnusson <mikma@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.
|
||||
*/
|
||||
|
||||
/*! \file sdp_crypto.h
|
||||
*
|
||||
* \brief SDP Security descriptions
|
||||
*
|
||||
* Specified in RFC 4568
|
||||
*
|
||||
* \author Mikael Magnusson <mikma@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
#ifndef _SDP_CRYPTO_H
|
||||
#define _SDP_CRYPTO_H
|
||||
|
||||
#include <asterisk/rtp_engine.h>
|
||||
|
||||
struct sdp_crypto;
|
||||
|
||||
/*! \brief Initialize an return an sdp_crypto struct
|
||||
*
|
||||
* \details
|
||||
* This function allocates a new sdp_crypto struct and initializes its values
|
||||
*
|
||||
* \retval NULL on failure
|
||||
* \retval a pointer to a new sdp_crypto structure
|
||||
*/
|
||||
struct sdp_crypto *sdp_crypto_setup(void);
|
||||
|
||||
/*! \brief Destroy a previously allocated sdp_crypto struct */
|
||||
void sdp_crypto_destroy(struct sdp_crypto *crypto);
|
||||
|
||||
/*! \brief Parse the a=crypto line from SDP and set appropriate values on the
|
||||
* sdp_crypto struct.
|
||||
*
|
||||
* \param p A valid sdp_crypto struct
|
||||
* \param attr the a:crypto line from SDP
|
||||
* \param rtp The rtp instance associated with the SDP being parsed
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval nonzero failure
|
||||
*/
|
||||
int sdp_crypto_process(struct sdp_crypto *p, const char *attr, struct ast_rtp_instance *rtp);
|
||||
|
||||
|
||||
/*! \brief Generate an SRTP a=crypto offer
|
||||
*
|
||||
* \details
|
||||
* The offer is stored on the sdp_crypto struct in a_crypto
|
||||
*
|
||||
* \param A valid sdp_crypto struct
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval nonzero failure
|
||||
*/
|
||||
int sdp_crypto_offer(struct sdp_crypto *p);
|
||||
|
||||
|
||||
/*! \brief Return the a_crypto value of the sdp_crypto struct
|
||||
*
|
||||
* \param p An sdp_crypto struct that has had sdp_crypto_offer called
|
||||
*
|
||||
* \retval The value of the a_crypto for p
|
||||
*/
|
||||
const char *sdp_crypto_attrib(struct sdp_crypto *p);
|
||||
|
||||
#endif /* _SDP_CRYPTO_H */
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2006 - 2007, Mikael Magnusson
|
||||
*
|
||||
* Mikael Magnusson <mikma@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.
|
||||
*/
|
||||
|
||||
/*! \file sip_srtp.h
|
||||
*
|
||||
* \brief SIP Secure RTP (SRTP)
|
||||
*
|
||||
* Specified in RFC 3711
|
||||
*
|
||||
* \author Mikael Magnusson <mikma@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
#ifndef _SIP_SRTP_H
|
||||
#define _SIP_SRTP_H
|
||||
|
||||
#include "sdp_crypto.h"
|
||||
|
||||
/* SRTP flags */
|
||||
#define SRTP_ENCR_OPTIONAL (1 << 1) /* SRTP encryption optional */
|
||||
#define SRTP_CRYPTO_ENABLE (1 << 2)
|
||||
#define SRTP_CRYPTO_OFFER_OK (1 << 3)
|
||||
|
||||
/*! \brief structure for secure RTP audio */
|
||||
struct sip_srtp {
|
||||
unsigned int flags;
|
||||
struct sdp_crypto *crypto;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief allocate a sip_srtp structure
|
||||
* \retval a new malloc'd sip_srtp structure on success
|
||||
* \retval NULL on failure
|
||||
*/
|
||||
struct sip_srtp *sip_srtp_alloc(void);
|
||||
|
||||
/*!
|
||||
* \brief free a sip_srtp structure
|
||||
* \param srtp a sip_srtp structure
|
||||
*/
|
||||
void sip_srtp_destroy(struct sip_srtp *srtp);
|
||||
|
||||
#endif /* _SIP_SRTP_H */
|
@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2006 - 2007, Mikael Magnusson
|
||||
*
|
||||
* Mikael Magnusson <mikma@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.
|
||||
*/
|
||||
|
||||
/*! \file sdp_crypto.c
|
||||
*
|
||||
* \brief SDP Security descriptions
|
||||
*
|
||||
* Specified in RFC 4568
|
||||
*
|
||||
* \author Mikael Magnusson <mikma@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "include/sdp_crypto.h"
|
||||
|
||||
#define SRTP_MASTER_LEN 30
|
||||
#define SRTP_MASTERKEY_LEN 16
|
||||
#define SRTP_MASTERSALT_LEN ((SRTP_MASTER_LEN) - (SRTP_MASTERKEY_LEN))
|
||||
#define SRTP_MASTER_LEN64 (((SRTP_MASTER_LEN) * 8 + 5) / 6 + 1)
|
||||
|
||||
extern struct ast_srtp_res *res_srtp;
|
||||
extern struct ast_srtp_policy_res *res_srtp_policy;
|
||||
|
||||
struct sdp_crypto {
|
||||
char *a_crypto;
|
||||
unsigned char local_key[SRTP_MASTER_LEN];
|
||||
char local_key64[SRTP_MASTER_LEN64];
|
||||
};
|
||||
|
||||
static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, const unsigned char *master_key, unsigned long ssrc, int inbound);
|
||||
|
||||
static struct sdp_crypto *sdp_crypto_alloc(void)
|
||||
{
|
||||
struct sdp_crypto *crypto;
|
||||
|
||||
return crypto = ast_calloc(1, sizeof(*crypto));
|
||||
}
|
||||
|
||||
void sdp_crypto_destroy(struct sdp_crypto *crypto)
|
||||
{
|
||||
ast_free(crypto->a_crypto);
|
||||
crypto->a_crypto = NULL;
|
||||
ast_free(crypto);
|
||||
}
|
||||
|
||||
struct sdp_crypto *sdp_crypto_setup(void)
|
||||
{
|
||||
struct sdp_crypto *p;
|
||||
int key_len;
|
||||
unsigned char remote_key[SRTP_MASTER_LEN];
|
||||
|
||||
if (!ast_rtp_engine_srtp_is_registered()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(p = sdp_crypto_alloc())) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (res_srtp->get_random(p->local_key, sizeof(p->local_key)) < 0) {
|
||||
sdp_crypto_destroy(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_base64encode(p->local_key64, p->local_key, SRTP_MASTER_LEN, sizeof(p->local_key64));
|
||||
|
||||
key_len = ast_base64decode(remote_key, p->local_key64, sizeof(remote_key));
|
||||
|
||||
if (key_len != SRTP_MASTER_LEN) {
|
||||
ast_log(LOG_ERROR, "base64 encode/decode bad len %d != %d\n", key_len, SRTP_MASTER_LEN);
|
||||
ast_free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (memcmp(remote_key, p->local_key, SRTP_MASTER_LEN)) {
|
||||
ast_log(LOG_ERROR, "base64 encode/decode bad key\n");
|
||||
ast_free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_debug(1 , "local_key64 %s len %zu\n", p->local_key64, strlen(p->local_key64));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, const unsigned char *master_key, unsigned long ssrc, int inbound)
|
||||
{
|
||||
const unsigned char *master_salt = NULL;
|
||||
|
||||
if (!ast_rtp_engine_srtp_is_registered()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
master_salt = master_key + SRTP_MASTERKEY_LEN;
|
||||
if (res_srtp_policy->set_master_key(policy, master_key, SRTP_MASTERKEY_LEN, master_salt, SRTP_MASTERSALT_LEN) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (res_srtp_policy->set_suite(policy, suite_val)) {
|
||||
ast_log(LOG_WARNING, "Could not set remote SRTP suite\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res_srtp_policy->set_ssrc(policy, ssrc, inbound);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdp_crypto_activate(struct sdp_crypto *p, int suite_val, unsigned char *remote_key, struct ast_rtp_instance *rtp)
|
||||
{
|
||||
struct ast_srtp_policy *local_policy = NULL;
|
||||
struct ast_srtp_policy *remote_policy = NULL;
|
||||
struct ast_rtp_instance_stats stats = {0,};
|
||||
int res = -1;
|
||||
|
||||
if (!ast_rtp_engine_srtp_is_registered()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!p) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(local_policy = res_srtp_policy->alloc())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(remote_policy = res_srtp_policy->alloc())) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ast_rtp_instance_get_stats(rtp, &stats, AST_RTP_INSTANCE_STAT_LOCAL_SSRC)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (set_crypto_policy(local_policy, suite_val, p->local_key, stats.local_ssrc, 0) < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (set_crypto_policy(remote_policy, suite_val, remote_key, 0, 1) < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* FIXME MIKMA */
|
||||
/* ^^^ I wish I knew what needed fixing... */
|
||||
if (ast_rtp_instance_add_srtp_policy(rtp, local_policy)) {
|
||||
ast_log(LOG_WARNING, "Could not set local SRTP policy\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ast_rtp_instance_add_srtp_policy(rtp, remote_policy)) {
|
||||
ast_log(LOG_WARNING, "Could not set remote SRTP policy\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ast_debug(1 , "SRTP policy activated\n");
|
||||
res = 0;
|
||||
|
||||
err:
|
||||
if (local_policy) {
|
||||
res_srtp_policy->destroy(local_policy);
|
||||
}
|
||||
|
||||
if (remote_policy) {
|
||||
res_srtp_policy->destroy(remote_policy);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sdp_crypto_process(struct sdp_crypto *p, const char *attr, struct ast_rtp_instance *rtp)
|
||||
{
|
||||
char *str = NULL;
|
||||
char *name = NULL;
|
||||
char *tag = NULL;
|
||||
char *suite = NULL;
|
||||
char *key_params = NULL;
|
||||
char *key_param = NULL;
|
||||
char *session_params = NULL;
|
||||
char *key_salt = NULL;
|
||||
char *lifetime = NULL;
|
||||
int found = 0;
|
||||
int attr_len = strlen(attr);
|
||||
int key_len = 0;
|
||||
int suite_val = 0;
|
||||
unsigned char remote_key[SRTP_MASTER_LEN];
|
||||
|
||||
if (!ast_rtp_engine_srtp_is_registered()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
str = ast_strdupa(attr);
|
||||
|
||||
name = strsep(&str, ":");
|
||||
tag = strsep(&str, " ");
|
||||
suite = strsep(&str, " ");
|
||||
key_params = strsep(&str, " ");
|
||||
session_params = strsep(&str, " ");
|
||||
|
||||
if (!tag || !suite) {
|
||||
ast_log(LOG_WARNING, "Unrecognized a=%s", attr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (session_params) {
|
||||
ast_log(LOG_WARNING, "Unsupported crypto parameters: %s", session_params);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_80")) {
|
||||
suite_val = AST_AES_CM_128_HMAC_SHA1_80;
|
||||
} else if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_32")) {
|
||||
suite_val = AST_AES_CM_128_HMAC_SHA1_32;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Unsupported crypto suite: %s\n", suite);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((key_param = strsep(&key_params, ";"))) {
|
||||
char *method = NULL;
|
||||
char *info = NULL;
|
||||
|
||||
method = strsep(&key_param, ":");
|
||||
info = strsep(&key_param, ";");
|
||||
|
||||
if (!strcmp(method, "inline")) {
|
||||
key_salt = strsep(&info, "|");
|
||||
lifetime = strsep(&info, "|");
|
||||
|
||||
if (lifetime) {
|
||||
ast_log(LOG_NOTICE, "Crypto life time unsupported: %s\n", attr);
|
||||
continue;
|
||||
}
|
||||
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
ast_log(LOG_NOTICE, "SRTP crypto offer not acceptable\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if ((key_len = ast_base64decode(remote_key, key_salt, sizeof(remote_key))) != SRTP_MASTER_LEN) {
|
||||
ast_log(LOG_WARNING, "SRTP sdescriptions key %d != %d\n", key_len, SRTP_MASTER_LEN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sdp_crypto_activate(p, suite_val, remote_key, rtp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!p->a_crypto) {
|
||||
if (!(p->a_crypto = ast_calloc(1, attr_len + 11))) {
|
||||
ast_log(LOG_ERROR, "Could not allocate memory for a_crypto\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(p->a_crypto, attr_len + 10, "a=crypto:%s %s inline:%s\r\n", tag, suite, p->local_key64);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sdp_crypto_offer(struct sdp_crypto *p)
|
||||
{
|
||||
char crypto_buf[128];
|
||||
const char *crypto_suite = "AES_CM_128_HMAC_SHA1_80"; /* Crypto offer */
|
||||
|
||||
if (p->a_crypto) {
|
||||
ast_free(p->a_crypto);
|
||||
}
|
||||
|
||||
if (snprintf(crypto_buf, sizeof(crypto_buf), "a=crypto:1 %s inline:%s\r\n", crypto_suite, p->local_key64) < 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(p->a_crypto = ast_strdup(crypto_buf))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *sdp_crypto_attrib(struct sdp_crypto *p)
|
||||
{
|
||||
return p->a_crypto;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2006 - 2007, Mikael Magnusson
|
||||
*
|
||||
* Mikael Magnusson <mikma@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.
|
||||
*/
|
||||
|
||||
/*! \file sip_srtp.c
|
||||
*
|
||||
* \brief SIP Secure RTP (SRTP)
|
||||
*
|
||||
* Specified in RFC 3711
|
||||
*
|
||||
* \author Mikael Magnusson <mikma@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/utils.h"
|
||||
#include "include/srtp.h"
|
||||
|
||||
struct sip_srtp *sip_srtp_alloc(void)
|
||||
{
|
||||
struct sip_srtp *srtp;
|
||||
|
||||
srtp = ast_calloc(1, sizeof(*srtp));
|
||||
|
||||
return srtp;
|
||||
}
|
||||
|
||||
void sip_srtp_destroy(struct sip_srtp *srtp)
|
||||
{
|
||||
if (srtp->crypto) {
|
||||
sdp_crypto_destroy(srtp->crypto);
|
||||
}
|
||||
srtp->crypto = NULL;
|
||||
ast_free(srtp);
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
\section{Introduction}
|
||||
Asterisk supports a channel-agnostic method for handling secure call requirements. Since there is no single meaning of what constitutes a "secure call," Asterisk allows the administrator the control to define "secure" for themselves via the dialplan and channel-specific configuration files.
|
||||
|
||||
\section{Channel-specific configuration}
|
||||
Currently the IAX2 and SIP channels support the call security features in Asterisk. Both channel-specific configuration files (\path{iax2.conf} and \path{sip.conf}) support the encryption=yes setting. For IAX2, this setting causes Asterisk to offer encryption when placing or receiving a call. To force encryption with IAX2, the forceencrypt=yes option is required. Due to limitations of SDP, encryption=yes in \path{sip.conf} results in a call with only a secure media offer, therefor forceencrypt=yes would be redundant in \path{sip.conf}.
|
||||
|
||||
If a peer is defined as requiring encryption but the endpoint does not support it, the call will fail with a HANGUPCAUSE of 58 (bearer capability does not exist).
|
||||
|
||||
|
||||
\section{Security-based dialplan branching}
|
||||
Each channel that supports secure signaling or media can implement a CHANNEL read callback function that specifies whether or not that channel meets the specified criteria. Currently, chan\_iax2 and chan\_sip implement these callbacks. Channels that do not support secure media or signaling will return an empty string when queried. For example, to only allow an inbound call that has both secure signaling and media, see the following example.
|
||||
|
||||
\begin{astlisting}
|
||||
\begin{verbatim}
|
||||
exten => 123,1,GotoIf("$[${CHANNEL(secure_signaling)}" = ""]?fail)
|
||||
exten => 123,n,GotoIf("$[${CHANNEL(seucre_media)}" = ""]?fail)
|
||||
exten => 123,n,Dial(SIP/123)
|
||||
exten => 123,n,Hangup
|
||||
exten => 123,n(fail),Playback(vm-goodbye)
|
||||
exten => 123,n,Hangup
|
||||
\end{verbatim}
|
||||
\end{astlisting}
|
||||
|
||||
\section{Forcing bridged channels to be secure}
|
||||
Administrators can force outbound channels that are to be bridged to a calling channel to conform to secure media and signaling policies. For example, to first make a call attempt that has both secure signaling and media, but gracefully fall back to non-secure signaling and media see the following example:
|
||||
|
||||
\begin{astlisting}
|
||||
\begin{verbatim}
|
||||
exten => 123,1,NoOp(We got a call)
|
||||
exten => 123,n,Set(CHANNEL(secure_bridge_signaling)=1)
|
||||
exten => 123,n,Set(CHANNEL(secure_bridge_media)=1)
|
||||
exten => 123,n,Dial(SIP/somebody)
|
||||
exten => 123,n,NoOp(HANGUPCAUSE=${HANGUPCAUSE})
|
||||
exten => 123,n,GotoIf($["${HANGUPCAUSE}"="58"]?encrypt_fail)
|
||||
exten => 123,n,Hangup
|
||||
|
||||
; notify user that retrying via insecure channel (user-provided prompt)
|
||||
exten => 123,n(encrypt_fail),Playback(secure-call-fail-retry)
|
||||
exten => 123,n,Set(CHANNEL(secure_bridge_signaling)=0)
|
||||
exten => 123,n,Set(CHANNEL(secure_bridge_media)=0)
|
||||
exten => 123,n,Dial(SIP/somebody)
|
||||
exten => 123,n,Hangup
|
||||
\end{verbatim}
|
||||
\end{astlisting}
|
||||
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2010 FIXME
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief SRTP resource
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RES_SRTP_H
|
||||
#define _ASTERISK_RES_SRTP_H
|
||||
|
||||
struct ast_srtp;
|
||||
struct ast_srtp_policy;
|
||||
struct ast_rtp_instance;
|
||||
|
||||
struct ast_srtp_cb {
|
||||
int (*no_ctx)(struct ast_rtp_instance *rtp, unsigned long ssrc, void *data);
|
||||
};
|
||||
|
||||
struct ast_srtp_res {
|
||||
int (*create)(struct ast_srtp **srtp, struct ast_rtp_instance *rtp, struct ast_srtp_policy *policy);
|
||||
void (*destroy)(struct ast_srtp *srtp);
|
||||
int (*add_stream)(struct ast_srtp *srtp, struct ast_srtp_policy *policy);
|
||||
void (*set_cb)(struct ast_srtp *srtp, const struct ast_srtp_cb *cb, void *data);
|
||||
int (*unprotect)(struct ast_srtp *srtp, void *buf, int *size, int rtcp);
|
||||
int (*protect)(struct ast_srtp *srtp, void **buf, int *size, int rtcp);
|
||||
int (*get_random)(unsigned char *key, size_t len);
|
||||
};
|
||||
|
||||
/* Crypto suites */
|
||||
enum ast_srtp_suite {
|
||||
AST_AES_CM_128_HMAC_SHA1_80 = 1,
|
||||
AST_AES_CM_128_HMAC_SHA1_32 = 2,
|
||||
AST_F8_128_HMAC_SHA1_80 = 3
|
||||
};
|
||||
|
||||
struct ast_srtp_policy_res {
|
||||
struct ast_srtp_policy *(*alloc)(void);
|
||||
void (*destroy)(struct ast_srtp_policy *policy);
|
||||
int (*set_suite)(struct ast_srtp_policy *policy, enum ast_srtp_suite suite);
|
||||
int (*set_master_key)(struct ast_srtp_policy *policy, const unsigned char *key, size_t key_len, const unsigned char *salt, size_t salt_len);
|
||||
void (*set_ssrc)(struct ast_srtp_policy *policy, unsigned long ssrc, int inbound);
|
||||
};
|
||||
|
||||
#endif /* _ASTERISK_RES_SRTP_H */
|
@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2005, Mikael Magnusson
|
||||
*
|
||||
* Mikael Magnusson <mikma@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.
|
||||
*
|
||||
* \author Mikael Magnusson <mikma@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>srtp</depend>
|
||||
***/
|
||||
|
||||
/* 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 _SIPSRTP_CRYPTO=enable in extension.conf before executing Dial
|
||||
|
||||
The dial fails if the callee doesn't support SRTP and sdescriptions.
|
||||
|
||||
exten => 2345,1,Set(_SIPSRTP_CRYPTO=enable)
|
||||
exten => 2345,2,Dial(SIP/1001)
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <srtp/srtp.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/sched.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/rtp_engine.h"
|
||||
|
||||
struct ast_srtp {
|
||||
struct ast_rtp_instance *rtp;
|
||||
srtp_t session;
|
||||
const struct ast_srtp_cb *cb;
|
||||
void *data;
|
||||
unsigned char buf[8192 + AST_FRIENDLY_OFFSET];
|
||||
unsigned int has_stream:1;
|
||||
};
|
||||
|
||||
struct ast_srtp_policy {
|
||||
srtp_policy_t sp;
|
||||
};
|
||||
|
||||
static int g_initialized = 0;
|
||||
|
||||
/* SRTP functions */
|
||||
static int ast_srtp_create(struct ast_srtp **srtp, struct ast_rtp_instance *rtp, struct ast_srtp_policy *policy);
|
||||
static void ast_srtp_destroy(struct ast_srtp *srtp);
|
||||
static int ast_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *policy);
|
||||
|
||||
static int ast_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len, int rtcp);
|
||||
static int ast_srtp_protect(struct ast_srtp *srtp, void **buf, int *len, int rtcp);
|
||||
static void ast_srtp_set_cb(struct ast_srtp *srtp, const struct ast_srtp_cb *cb, void *data);
|
||||
static int ast_srtp_get_random(unsigned char *key, size_t len);
|
||||
|
||||
/* Policy functions */
|
||||
static struct ast_srtp_policy *ast_srtp_policy_alloc(void);
|
||||
static void ast_srtp_policy_destroy(struct ast_srtp_policy *policy);
|
||||
static int ast_srtp_policy_set_suite(struct ast_srtp_policy *policy, enum ast_srtp_suite suite);
|
||||
static int ast_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 void ast_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, unsigned long ssrc, int inbound);
|
||||
|
||||
static struct ast_srtp_res srtp_res = {
|
||||
.create = ast_srtp_create,
|
||||
.destroy = ast_srtp_destroy,
|
||||
.add_stream = ast_srtp_add_stream,
|
||||
.set_cb = ast_srtp_set_cb,
|
||||
.unprotect = ast_srtp_unprotect,
|
||||
.protect = ast_srtp_protect,
|
||||
.get_random = ast_srtp_get_random
|
||||
};
|
||||
|
||||
static struct ast_srtp_policy_res policy_res = {
|
||||
.alloc = ast_srtp_policy_alloc,
|
||||
.destroy = ast_srtp_policy_destroy,
|
||||
.set_suite = ast_srtp_policy_set_suite,
|
||||
.set_master_key = ast_srtp_policy_set_master_key,
|
||||
.set_ssrc = ast_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;
|
||||
|
||||
if (!(srtp = ast_calloc(1, sizeof(*srtp)))) {
|
||||
ast_log(LOG_ERROR, "Unable to allocate memory for srtp\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return srtp;
|
||||
}
|
||||
|
||||
/*
|
||||
struct ast_srtp_policy
|
||||
*/
|
||||
static void srtp_event_cb(srtp_event_data_t *data)
|
||||
{
|
||||
switch (data->event) {
|
||||
case event_ssrc_collision:
|
||||
ast_debug(1, "SSRC collision\n");
|
||||
break;
|
||||
case event_key_soft_limit:
|
||||
ast_debug(1, "event_key_soft_limit\n");
|
||||
break;
|
||||
case event_key_hard_limit:
|
||||
ast_debug(1, "event_key_hard_limit\n");
|
||||
break;
|
||||
case event_packet_index_limit:
|
||||
ast_debug(1, "event_packet_index_limit\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ast_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 *ast_srtp_policy_alloc()
|
||||
{
|
||||
struct ast_srtp_policy *tmp;
|
||||
|
||||
if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
|
||||
ast_log(LOG_ERROR, "Unable to allocate memory for srtp_policy\n");
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void ast_srtp_policy_destroy(struct ast_srtp_policy *policy)
|
||||
{
|
||||
if (policy->sp.key) {
|
||||
ast_free(policy->sp.key);
|
||||
policy->sp.key = NULL;
|
||||
}
|
||||
ast_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 ast_srtp_policy_set_suite(struct ast_srtp_policy *policy, enum ast_srtp_suite suite)
|
||||
{
|
||||
return policy_set_suite(&policy->sp.rtp, suite) | policy_set_suite(&policy->sp.rtcp, suite);
|
||||
}
|
||||
|
||||
static int ast_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;
|
||||
|
||||
if (policy->sp.key) {
|
||||
ast_free(policy->sp.key);
|
||||
policy->sp.key = NULL;
|
||||
}
|
||||
|
||||
if (!(master_key = ast_calloc(1, size))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(master_key, key, key_len);
|
||||
memcpy(master_key + key_len, salt, salt_len);
|
||||
|
||||
policy->sp.key = master_key;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_srtp_get_random(unsigned char *key, size_t len)
|
||||
{
|
||||
return crypto_get_random(key, len) != err_status_ok ? -1: 0;
|
||||
}
|
||||
|
||||
static void ast_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 ast_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len, int rtcp)
|
||||
{
|
||||
int res = 0;
|
||||
int i;
|
||||
struct ast_rtp_instance_stats stats = {0,};
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
res = rtcp ? srtp_unprotect_rtcp(srtp->session, buf, len) : srtp_unprotect(srtp->session, buf, len);
|
||||
if (res != err_status_no_ctx) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (srtp->cb && srtp->cb->no_ctx) {
|
||||
if (ast_rtp_instance_get_stats(srtp->rtp, &stats, AST_RTP_INSTANCE_STAT_REMOTE_SSRC)) {
|
||||
break;
|
||||
}
|
||||
if (srtp->cb->no_ctx(srtp->rtp, stats.remote_ssrc, srtp->data) < 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res != err_status_ok && res != err_status_replay_fail ) {
|
||||
ast_debug(1, "SRTP unprotect: %s\n", srtp_errstr(res));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return *len;
|
||||
}
|
||||
|
||||
static int ast_srtp_protect(struct ast_srtp *srtp, void **buf, int *len, int rtcp)
|
||||
{
|
||||
int res;
|
||||
|
||||
if ((*len + SRTP_MAX_TRAILER_LEN) > sizeof(srtp->buf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(srtp->buf, *buf, *len);
|
||||
|
||||
if ((res = rtcp ? srtp_protect_rtcp(srtp->session, srtp->buf, len) : srtp_protect(srtp->session, srtp->buf, len)) != err_status_ok && res != err_status_replay_fail) {
|
||||
ast_debug(1, "SRTP protect: %s\n", srtp_errstr(res));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*buf = srtp->buf;
|
||||
return *len;
|
||||
}
|
||||
|
||||
static int ast_srtp_create(struct ast_srtp **srtp, struct ast_rtp_instance *rtp, struct ast_srtp_policy *policy)
|
||||
{
|
||||
struct ast_srtp *temp;
|
||||
|
||||
if (!(temp = res_srtp_new())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (srtp_create(&temp->session, &policy->sp) != err_status_ok) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
temp->rtp = rtp;
|
||||
*srtp = temp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ast_srtp_destroy(struct ast_srtp *srtp)
|
||||
{
|
||||
if (srtp->session) {
|
||||
srtp_dealloc(srtp->session);
|
||||
}
|
||||
|
||||
ast_free(srtp);
|
||||
}
|
||||
|
||||
static int ast_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *policy)
|
||||
{
|
||||
if (!srtp->has_stream && srtp_add_stream(srtp->session, &policy->sp) != err_status_ok) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
srtp->has_stream = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int res_srtp_init(void)
|
||||
{
|
||||
if (g_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (srtp_init() != err_status_ok) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
srtp_install_event_handler(srtp_event_cb);
|
||||
|
||||
return ast_rtp_engine_register_srtp(&srtp_res, &policy_res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exported functions
|
||||
*/
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return res_srtp_init();
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_rtp_engine_unregister_srtp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Secure RTP (SRTP)",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
);
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
local:
|
||||
*;
|
||||
};
|
Loading…
Reference in new issue