dect
/
asterisk
Archived
13
0
Fork 0

Add SRTP support for 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: http://svn.digium.com/svn/asterisk/trunk@268894 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
twilson 2010-06-08 05:29:08 +00:00
parent 5f87b66641
commit 9b1a36a294
28 changed files with 9227 additions and 30793 deletions

13
CHANGES
View File

@ -59,6 +59,10 @@ SIP Changes
* When dialing SIP peers, a new component may be added to the end of the dialstring
to indicate that a specific remote IP address or host should be used when dialing
the particular peer. The dialstring format is SIP/peer/exten/host_or_IP.
* SRTP SDES support for encrypting calls to/from Asterisk over SIP. The
ability to selectively force bridged channels to also be encrypted is also
implemented. Branching in the dialplan can be done based on whether or not
a channel has secure media and/or signaling.
* Added directmediapermit/directmediadeny to limit which peers can send direct media
to each other
* Added the 'snom_aoc_enabled' option to turn on support for sending Advice of
@ -68,6 +72,10 @@ IAX2 Changes
-----------
* Added rtsavesysname option into iax.conf to allow the systname to be saved
on realtime updates.
* Added the ability for chan_iax2 to inform the dialplan whether or not
encryption is being used. This interoperates with the SIP SRTP implementation
so that a secure SIP call can be bridged to a secure IAX call when the
dialplan requires bridged channels to be "secure".
MGCP Changes
------------
@ -205,6 +213,11 @@ Dialplan Functions
prefixing the name of the hash at assignment with the appropriate number of
underscores, just like variables.
* GROUP_MATCH_COUNT has been improved to allow regex matching on category
* CHANNEL(secure_bridge_signaling) and CHANNEL(secure_bridge_media) to set/get
whether or not channels that are bridged to the current channel will be
required to have secure signaling and/or media.
* CHANNEL(secure_signaling) and CHANNEL(secure_media) to get whether or not
the current channel has secure signaling and/or media.
* For DAHDI/ISDN channels, the CHANNEL() dialplan function now supports the
"no_media_path" option.
Returns "0" if there is a B channel associated with the call.

View File

@ -51,6 +51,7 @@ SPEEXDSP=@PBX_SPEEXDSP@
SPEEX_PREPROCESS=@PBX_SPEEX_PREPROCESS@
SQLITE3=@PBX_SQLITE3@
SQLITE=@PBX_SQLITE@
SRTP=@PBX_SRTP@
SS7=@PBX_SS7@
OPENSSL=@PBX_OPENSSL@
SUPPSERV=@PBX_SUPPSERV@

View File

@ -1171,6 +1171,7 @@ static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data,
static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img);
static int iax2_sendtext(struct ast_channel *c, const char *text);
static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen);
static int iax2_queryoption(struct ast_channel *c, int option, void *data, int *datalen);
static int iax2_transfer(struct ast_channel *c, const char *dest);
static int iax2_write(struct ast_channel *c, struct ast_frame *f);
static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now);
@ -1218,6 +1219,7 @@ static const struct ast_channel_tech iax2_tech = {
.write_video = iax2_write,
.indicate = iax2_indicate,
.setoption = iax2_setoption,
.queryoption = iax2_queryoption,
.bridge = iax2_bridge,
.transfer = iax2_transfer,
.fixup = iax2_fixup,
@ -4903,6 +4905,11 @@ static int iax2_call(struct ast_channel *c, char *dest, int timeout)
ast_log(LOG_WARNING, "No address associated with '%s'\n", pds.peer);
return -1;
}
if (ast_test_flag64(iaxs[callno], IAX_FORCE_ENCRYPT) && !cai.encmethods) {
ast_log(LOG_WARNING, "Encryption forced for call, but not enabled\n");
c->hangupcause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
return -1;
}
if (ast_strlen_zero(cai.secret) && ast_test_flag64(iaxs[callno], IAX_FORCE_ENCRYPT)) {
ast_log(LOG_WARNING, "Call terminated. No secret given and force encrypt enabled\n");
return -1;
@ -5144,6 +5151,19 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
case AST_OPTION_OPRMODE:
errno = EINVAL;
return -1;
case AST_OPTION_SECURE_SIGNALING:
case AST_OPTION_SECURE_MEDIA:
{
unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
ast_mutex_lock(&iaxsl[callno]);
if ((*(int *) data)) {
ast_set_flag64(iaxs[callno], IAX_FORCE_ENCRYPT);
} else {
ast_clear_flag64(iaxs[callno], IAX_FORCE_ENCRYPT);
}
ast_mutex_unlock(&iaxsl[callno]);
return 0;
}
default:
{
unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
@ -5175,6 +5195,23 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
}
}
static int iax2_queryoption(struct ast_channel *c, int option, void *data, int *datalen)
{
switch (option) {
case AST_OPTION_SECURE_SIGNALING:
case AST_OPTION_SECURE_MEDIA:
{
unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
ast_mutex_lock(&iaxsl[callno]);
*((int *) data) = ast_test_flag64(iaxs[callno], IAX_FORCE_ENCRYPT) ? 1 : 0;
ast_mutex_unlock(&iaxsl[callno]);
return 0;
}
default:
return -1;
}
}
static struct ast_frame *iax2_read(struct ast_channel *c)
{
ast_log(LOG_NOTICE, "I should never be called!\n");
@ -13609,6 +13646,8 @@ static int acf_channel_read(struct ast_channel *chan, const char *funcname, char
ast_copy_string(buf, pvt->addr.sin_addr.s_addr ? ast_inet_ntoa(pvt->addr.sin_addr) : "", buflen);
} else if (!strcasecmp(args, "peername")) {
ast_copy_string(buf, pvt->username, buflen);
} else if (!strcasecmp(args, "secure_signaling") || !strcasecmp(args, "secure_media")) {
snprintf(buf, buflen, "%s", IAX_CALLENCRYPTED(pvt) ? "1" : "");
} else {
res = -1;
}

View File

@ -267,11 +267,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "sip/include/config_parser.h"
#include "sip/include/reqresp_parser.h"
#include "sip/include/sip_utils.h"
#include "sip/include/srtp.h"
#include "sip/include/sdp_crypto.h"
#include "asterisk/ccss.h"
#include "asterisk/xml.h"
#include "sip/include/dialog.h"
#include "sip/include/dialplan_functions.h"
/*** DOCUMENTATION
<application name="SIPDtmfMode" language="en_US">
<synopsis>
@ -1566,6 +1569,10 @@ static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *r
static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
/*------ SRTP Support -------- */
static int setup_srtp(struct sip_srtp **srtp);
static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_srtp **srtp, const char *a);
/*------ T38 Support --------- */
static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans);
static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan);
@ -3910,7 +3917,16 @@ static int sip_setoption(struct ast_channel *chan, int option, void *data, int d
res = 0;
}
break;
case AST_OPTION_SECURE_SIGNALING:
p->req_secure_signaling = *(unsigned int *) data;
res = 0;
break;
case AST_OPTION_SECURE_MEDIA:
ast_set2_flag(&p->flags[1], *(unsigned int *) data, SIP_PAGE2_USE_SRTP);
res = 0;
break;
default:
ast_log(LOG_NOTICE, "Unknown option: %d\n", option);
break;
}
@ -3961,6 +3977,14 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
*cp = p->dsp ? 1 : 0;
ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", chan->name);
break;
case AST_OPTION_SECURE_SIGNALING:
*((unsigned int *) data) = p->req_secure_signaling;
res = 0;
break;
case AST_OPTION_SECURE_MEDIA:
*((unsigned int *) data) = ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP) ? 1 : 0;
res = 0;
break;
case AST_OPTION_DEVICE_NAME:
if (p && p->outgoing_call) {
cp = (char *) data;
@ -5004,6 +5028,35 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
}
}
/* Check to see if we should try to force encryption */
if (p->req_secure_signaling && p->socket.type != SIP_TRANSPORT_TLS) {
ast_log(LOG_WARNING, "Encrypted signaling is required\n");
ast->hangupcause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
return -1;
}
if (ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP)) {
if (ast_test_flag(&p->flags[0], SIP_REINVITE)) {
ast_debug(1, "Direct media not possible when using SRTP, ignoring canreinvite setting\n");
ast_clear_flag(&p->flags[0], SIP_REINVITE);
}
if (p->rtp && !p->srtp && setup_srtp(&p->srtp) < 0) {
ast_log(LOG_WARNING, "SRTP audio setup failed\n");
return -1;
}
if (p->vrtp && !p->vsrtp && setup_srtp(&p->vsrtp) < 0) {
ast_log(LOG_WARNING, "SRTP video setup failed\n");
return -1;
}
if (p->trtp && !p->vsrtp && setup_srtp(&p->tsrtp) < 0) {
ast_log(LOG_WARNING, "SRTP text setup failed\n");
return -1;
}
}
res = 0;
ast_set_flag(&p->flags[0], SIP_OUTGOING);
@ -5211,6 +5264,21 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
p->chanvars = NULL;
}
if (p->srtp) {
sip_srtp_destroy(p->srtp);
p->srtp = NULL;
}
if (p->vsrtp) {
sip_srtp_destroy(p->vsrtp);
p->vsrtp = NULL;
}
if (p->tsrtp) {
sip_srtp_destroy(p->tsrtp);
p->tsrtp = NULL;
}
if (p->directmediaha) {
ast_free_ha(p->directmediaha);
p->directmediaha = NULL;
@ -7671,6 +7739,10 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
const char *codecs;
int codec;
/* SRTP */
int secure_audio = FALSE;
int secure_video = FALSE;
/* Others */
int sendonly = -1;
int vsendonly = -1;
@ -7770,6 +7842,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
int video = FALSE;
int image = FALSE;
int text = FALSE;
char protocol[5] = {0,};
int x;
numberofports = 1;
@ -7780,8 +7853,14 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
nextm = get_sdp_iterate(&next, req, "m");
/* Search for audio media definition */
if ((sscanf(m, "audio %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
(sscanf(m, "audio %30u RTP/AVP %n", &x, &len) == 1 && len > 0)) {
if ((sscanf(m, "audio %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
(sscanf(m, "audio %30u RTP/%4s %n", &x, protocol, &len) == 2 && len > 0)) {
if (!strcmp(protocol, "SAVP")) {
secure_audio = 1;
} else if (strcmp(protocol, "AVP")) {
ast_log(LOG_WARNING, "unknown SDP media protocol in offer: %s\n", protocol);
continue;
}
audio = TRUE;
p->offered_media[SDP_AUDIO].offered = TRUE;
numberofmediastreams++;
@ -7801,8 +7880,14 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
ast_rtp_codecs_payloads_set_m_type(&newaudiortp, NULL, codec);
}
/* Search for video media definition */
} else if ((sscanf(m, "video %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
(sscanf(m, "video %30u RTP/AVP %n", &x, &len) == 1 && len >= 0)) {
} else if ((sscanf(m, "video %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
(sscanf(m, "video %30u RTP/%4s %n", &x, protocol, &len) == 2 && len >= 0)) {
if (!strcmp(protocol, "SAVP")) {
secure_video = 1;
} else if (strcmp(protocol, "AVP")) {
ast_log(LOG_WARNING, "unknown SDP media protocol in offer: %s\n", protocol);
continue;
}
video = TRUE;
p->novideo = FALSE;
p->offered_media[SDP_VIDEO].offered = TRUE;
@ -7864,8 +7949,6 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
if (numberofports > 1)
ast_log(LOG_WARNING, "SDP offered %d ports for media, not supported by Asterisk. Will try anyway...\n", numberofports);
/* Media stream specific parameters */
while ((type = get_sdp_line(&iterator, next - 1, req, &value)) != '\0') {
int processed = FALSE;
@ -7899,6 +7982,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
if (audio) {
if (process_sdp_a_sendonly(value, &sendonly))
processed = TRUE;
else if (process_crypto(p, p->rtp, &p->srtp, value))
processed = TRUE;
else if (process_sdp_a_audio(value, p, &newaudiortp, &last_rtpmap_codec))
processed = TRUE;
}
@ -7906,6 +7991,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
else if (video) {
if (process_sdp_a_sendonly(value, &vsendonly))
processed = TRUE;
else if (process_crypto(p, p->vrtp, &p->vsrtp, value))
processed = TRUE;
else if (process_sdp_a_video(value, p, &newvideortp, &last_rtpmap_codec))
processed = TRUE;
}
@ -7913,6 +8000,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
else if (text) {
if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec))
processed = TRUE;
else if (process_crypto(p, p->trtp, &p->tsrtp, value))
processed = TRUE;
}
/* Image (T.38 FAX) specific scanning */
else if (image) {
@ -7937,20 +8026,48 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
return -1;
}
if (portno == -1 && vportno == -1 && udptlportno == -1 && tportno == -1)
if (portno == -1 && vportno == -1 && udptlportno == -1 && tportno == -1) {
/* No acceptable offer found in SDP - we have no ports */
/* Do not change RTP or VRTP if this is a re-invite */
ast_log(LOG_WARNING, "Failing due to no acceptable offer found\n");
return -2;
}
if (numberofmediastreams > 3)
if (numberofmediastreams > 3) {
/* We have too many fax, audio and/or video and/or text media streams, fail this offer */
ast_log(LOG_WARNING, "Faling due to too many media streams\n");
return -3;
}
if (secure_audio && !(p->srtp && (ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)))) {
ast_log(LOG_WARNING, "Can't provide secure audio requested in SDP offer\n");
return -4;
}
if (!secure_audio && p->srtp) {
ast_log(LOG_WARNING, "We are requesting SRTP, but they responded without it!\n");
return -4;
}
if (secure_video && !(p->vsrtp && (ast_test_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK)))) {
ast_log(LOG_WARNING, "Can't provide secure video requested in SDP offer\n");
return -4;
}
if (!p->novideo && !secure_video && p->vsrtp) {
ast_log(LOG_WARNING, "We are requesting SRTP, but they responded without it!\n");
return -4;
}
if (!(secure_audio || secure_video) && ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP)) {
ast_log(LOG_WARNING, "Matched device setup to use SRTP, but request was not!\n");
return -4;
}
if (udptlportno == -1) {
change_t38_state(p, T38_DISABLED);
}
/* Now gather all of the codecs that we are asked for: */
ast_rtp_codecs_payload_formats(&newaudiortp, &peercapability, &peernoncodeccapability);
ast_rtp_codecs_payload_formats(&newvideortp, &vpeercapability, &vpeernoncodeccapability);
@ -9843,6 +9960,23 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, int needtext
}
}
static void get_crypto_attrib(struct sip_srtp *srtp, const char **a_crypto)
{
/* Set encryption properties */
if (srtp) {
if (!srtp->crypto) {
srtp->crypto = sdp_crypto_setup();
}
if (srtp->crypto && (sdp_crypto_offer(srtp->crypto) >= 0)) {
*a_crypto = sdp_crypto_attrib(srtp->crypto);
}
if (!*a_crypto) {
ast_log(LOG_WARNING, "No SRTP key management enabled\n");
}
}
}
/*! \brief Add Session Description Protocol message
If oldsdp is TRUE, then the SDP version number is not incremented. This mechanism
@ -9879,6 +10013,9 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
struct ast_str *a_video = ast_str_alloca(1024); /* Attributes for video */
struct ast_str *a_text = ast_str_alloca(1024); /* Attributes for text */
struct ast_str *a_modem = ast_str_alloca(1024); /* Attributes for modem */
const char *a_crypto = NULL;
const char *v_a_crypto = NULL;
const char *t_a_crypto = NULL;
format_t x;
format_t capability = 0;
@ -9962,7 +10099,9 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
/* Ok, we need video. Let's add what we need for video and set codecs.
Video is handled differently than audio since we can not transcode. */
if (needvideo) {
ast_str_append(&m_video, 0, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
get_crypto_attrib(p->vsrtp, &v_a_crypto);
ast_str_append(&m_video, 0, "m=video %d RTP/%s", ntohs(vdest.sin_port),
v_a_crypto ? "SAVP" : "AVP");
/* Build max bitrate string */
if (p->maxcallbitrate)
@ -9976,7 +10115,9 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
if (needtext) {
if (sipdebug_text)
ast_verbose("Lets set up the text sdp\n");
ast_str_append(&m_text, 0, "m=text %d RTP/AVP", ntohs(tdest.sin_port));
get_crypto_attrib(p->tsrtp, &t_a_crypto);
ast_str_append(&m_text, 0, "m=text %d RTP/%s", ntohs(tdest.sin_port),
t_a_crypto ? "SAVP" : "AVP");
if (debug) /* XXX should I use tdest below ? */
ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(tsin.sin_port));
@ -9987,7 +10128,9 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
/* We break with the "recommendation" and send our IP, in order that our
peer doesn't have to ast_gethostbyname() us */
ast_str_append(&m_audio, 0, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
get_crypto_attrib(p->srtp, &a_crypto);
ast_str_append(&m_audio, 0, "m=audio %d RTP/%s", ntohs(dest.sin_port),
a_crypto ? "SAVP" : "AVP");
if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR)
hold = "a=recvonly\r\n";
@ -10149,7 +10292,15 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
len += m_text->used + a_text->used + strlen(hold);
if (add_t38)
len += m_modem->used + a_modem->used;
if (a_crypto) {
len += strlen(a_crypto);
}
if (v_a_crypto) {
len += strlen(v_a_crypto);
}
if (t_a_crypto) {
len += strlen(t_a_crypto);
}
add_header(resp, "Content-Type", "application/sdp");
add_header_contentLength(resp, len);
add_line(resp, version);
@ -10163,6 +10314,9 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
add_line(resp, m_audio->str);
add_line(resp, a_audio->str);
add_line(resp, hold);
if (a_crypto) {
add_line(resp, a_crypto);
}
} else if (p->offered_media[SDP_AUDIO].offered) {
snprintf(dummy_answer, sizeof(dummy_answer), "m=audio 0 RTP/AVP %s\r\n", p->offered_media[SDP_AUDIO].codecs);
add_line(resp, dummy_answer);
@ -10171,6 +10325,9 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
add_line(resp, m_video->str);
add_line(resp, a_video->str);
add_line(resp, hold); /* Repeat hold for the video stream */
if (v_a_crypto) {
add_line(resp, v_a_crypto);
}
} else if (p->offered_media[SDP_VIDEO].offered) {
snprintf(dummy_answer, sizeof(dummy_answer), "m=video 0 RTP/AVP %s\r\n", p->offered_media[SDP_VIDEO].codecs);
add_line(resp, dummy_answer);
@ -10179,6 +10336,9 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
add_line(resp, m_text->str);
add_line(resp, a_text->str);
add_line(resp, hold); /* Repeat hold for the text stream */
if (t_a_crypto) {
add_line(resp, t_a_crypto);
}
} else if (p->offered_media[SDP_TEXT].offered) {
snprintf(dummy_answer, sizeof(dummy_answer), "m=text 0 RTP/AVP %s\r\n", p->offered_media[SDP_TEXT].codecs);
add_line(resp, dummy_answer);
@ -17482,6 +17642,8 @@ static int function_sippeer(struct ast_channel *chan, const char *cmd, char *dat
ast_copy_string(buf, peer->cid_num, len);
} else if (!strcasecmp(colname, "codecs")) {
ast_getformatname_multiple(buf, len -1, peer->capability);
} else if (!strcasecmp(colname, "encryption")) {
snprintf(buf, len, "%d", ast_test_flag(&peer->flags[1], SIP_PAGE2_USE_SRTP));
} else if (!strncasecmp(colname, "chanvar[", 8)) {
char *chanvar=colname + 8;
struct ast_variable *v;
@ -21015,8 +21177,13 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
transmit_response_with_t38_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)));
} else if (p->t38.state == T38_DISABLED) {
/* If this is not a re-invite or something to ignore - it's critical */
ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)), p->session_modify == TRUE ? FALSE : TRUE, FALSE);
if (p->srtp && !ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)) {
ast_log(LOG_WARNING, "Target does not support required crypto\n");
transmit_response_reliable(p, "488 Not Acceptable Here (crypto)", req);
} else {
ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)), p->session_modify == TRUE ? FALSE : TRUE, FALSE);
}
}
p->invitestate = INV_TERMINATED;
@ -25374,6 +25541,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
ast_string_field_set(peer, unsolicited_mailbox, v->value);
} else if (!strcasecmp(v->name, "use_q850_reason")) {
ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_Q850_REASON);
} else if (!strcasecmp(v->name, "encryption")) {
ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_USE_SRTP);
} else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);
}
@ -26666,6 +26835,10 @@ static enum ast_rtp_glue_result sip_get_rtp_peer(struct ast_channel *chan, struc
res = AST_RTP_GLUE_RESULT_FORBID;
}
if (p->srtp) {
res = AST_RTP_GLUE_RESULT_FORBID;
}
sip_pvt_unlock(p);
return res;
@ -27084,6 +27257,50 @@ static void sip_send_all_mwi_subscriptions(void)
} while (0));
}
/* SRTP */
static int setup_srtp(struct sip_srtp **srtp)
{
if (!ast_rtp_engine_srtp_is_registered()) {
ast_log(LOG_ERROR, "No SRTP module loaded, can't setup SRTP session.\n");
return -1;
}
if (!(*srtp = sip_srtp_alloc())) { /* Allocate SRTP data structure */
return -1;
}
return 0;
}
static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_srtp **srtp, const char *a)
{
if (strncasecmp(a, "crypto:", 7)) {
return FALSE;
}
if (!*srtp) {
if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
ast_log(LOG_WARNING, "Ignoring unexpected crypto attribute in SDP answer\n");
return FALSE;
}
if (setup_srtp(srtp) < 0) {
return FALSE;
}
}
if (!(*srtp)->crypto && !((*srtp)->crypto = sdp_crypto_setup())) {
return FALSE;
}
if (sdp_crypto_process((*srtp)->crypto, a, rtp) < 0) {
return FALSE;
}
ast_set_flag(*srtp, SRTP_CRYPTO_OFFER_OK);
return TRUE;
}
/*! \brief Reload module */
static int sip_do_reload(enum channelreloadreason reason)
{

View File

@ -214,6 +214,10 @@ int sip_acf_channel_read(struct ast_channel *chan, const char *funcname, char *p
ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname);
return -1;
}
} else if (!strcasecmp(args.param, "secure_signaling")) {
snprintf(buf, buflen, "%s", p->socket.type == SIP_TRANSPORT_TLS ? "1" : "");
} else if (!strcasecmp(args.param, "secure_media")) {
snprintf(buf, buflen, "%s", p->srtp ? "1" : "");
} else {
res = -1;
}

View File

@ -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 */

View File

@ -307,10 +307,8 @@
#define SIP_PAGE2_RTAUTOCLEAR (1 << 1) /*!< GP: Should we clean memory from peers after expiry? */
#define SIP_PAGE2_RPID_UPDATE (1 << 2)
#define SIP_PAGE2_Q850_REASON (1 << 3) /*!< DP: Get/send cause code via Reason header */
#define SIP_PAGE2_SYMMETRICRTP (1 << 4) /*!< GDP: Whether symmetric RTP is enabled or not */
#define SIP_PAGE2_STATECHANGEQUEUE (1 << 5) /*!< D: Unsent state pending change exists */
#define SIP_PAGE2_CONNECTLINEUPDATE_PEND (1 << 6)
#define SIP_PAGE2_RPID_IMMEDIATE (1 << 7)
#define SIP_PAGE2_RPORT_PRESENT (1 << 8) /*!< Was rport received in the Via header? */
@ -345,6 +343,7 @@
#define SIP_PAGE2_UDPTL_DESTINATION (1 << 25) /*!< DP: Use source IP of RTP as destination if NAT is enabled */
#define SIP_PAGE2_VIDEOSUPPORT_ALWAYS (1 << 26) /*!< DP: Always set up video, even if endpoints don't support it */
#define SIP_PAGE2_HAVEPEERCONTEXT (1 << 27) /*< Are we associated with a configured peer context? */
#define SIP_PAGE2_USE_SRTP (1 << 28) /*!< DP: Whether we should offer (only) SRTP */
#define SIP_PAGE2_FLAGS_TO_COPY \
(SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_IGNORESDPVERSION | \
@ -352,7 +351,7 @@
SIP_PAGE2_BUGGY_MWI | SIP_PAGE2_TEXTSUPPORT | SIP_PAGE2_FAX_DETECT | \
SIP_PAGE2_UDPTL_DESTINATION | SIP_PAGE2_VIDEOSUPPORT_ALWAYS | SIP_PAGE2_PREFERRED_CODEC | \
SIP_PAGE2_RPID_IMMEDIATE | SIP_PAGE2_RPID_UPDATE | SIP_PAGE2_SYMMETRICRTP |\
SIP_PAGE2_Q850_REASON | SIP_PAGE2_HAVEPEERCONTEXT)
SIP_PAGE2_Q850_REASON | SIP_PAGE2_HAVEPEERCONTEXT | SIP_PAGE2_USE_SRTP)
#define SIP_PAGE3_SNOM_AOC (1 << 0) /*!< DPG: Allow snom aoc messages */
@ -965,6 +964,7 @@ struct sip_pvt {
* or respect the other endpoint's request for frame sizes (on)
* for incoming calls
*/
unsigned short req_secure_signaling:1;/*!< Whether we are required to have secure signaling or not */
char tag[11]; /*!< Our tag for this session */
int timer_t1; /*!< SIP timer T1, ms rtt */
int timer_b; /*!< SIP timer B, ms */
@ -1048,6 +1048,9 @@ struct sip_pvt {
AST_LIST_HEAD_NOLOCK(request_queue, sip_request) request_queue; /*!< Requests that arrived but could not be processed immediately */
struct sip_invite_param *options; /*!< Options for INVITE */
struct sip_st_dlg *stimer; /*!< SIP Session-Timers */
struct sip_srtp *srtp; /*!< Structure to hold Secure RTP session data for audio */
struct sip_srtp *vsrtp; /*!< Structure to hold Secure RTP session data for video */
struct sip_srtp *tsrtp; /*!< Structure to hold Secure RTP session data for text */
int red; /*!< T.140 RTP Redundancy */
int hangupcause; /*!< Storage of hangupcause copied from our owner before we disconnect from the AST channel (only used at hangup) */

View File

@ -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 */

310
channels/sip/sdp_crypto.c Normal file
View File

@ -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;
}

51
channels/sip/srtp.c Normal file
View File

@ -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);
}

38350
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -377,6 +377,7 @@ AST_EXT_LIB_SETUP([SPEEXDSP], [SpeexDSP], [speexdsp])
AST_EXT_LIB_SETUP_DEPENDENT([SPEEX_PREPROCESS], [speex_preprocess_ctl], [], [speex])
AST_EXT_LIB_SETUP([SQLITE], [SQLite], [sqlite])
AST_EXT_LIB_SETUP([SQLITE3], [SQLite], [sqlite3])
AST_EXT_LIB_SETUP([SRTP], [Secure RTP], [srtp])
AST_EXT_LIB_SETUP([OPENSSL], [OpenSSL Secure Sockets Layer], [ssl])
AST_EXT_LIB_SETUP([SUPPSERV], [mISDN Supplemental Services], [suppserv])
AST_EXT_LIB_SETUP([FREETDS], [FreeTDS], [tds])
@ -1748,6 +1749,8 @@ then
AST_CHECK_OSPTK([3], [6], [0])
fi
AST_EXT_LIB_CHECK([SRTP], [srtp], [srtp_init], [srtp/srtp.h])
AST_EXT_TOOL_CHECK([GMIME], [gmime-config], [], [], [#include <gmime/gmime.h>], [gboolean q = g_mime_check_version(0,0,0);])
AST_EXT_LIB_CHECK([HOARD], [hoard], [malloc], [])

View File

@ -147,6 +147,9 @@ reference purposes.
\chapter{Security Framework}
\input{security-events.tex}
\chapter{Secure Calls}
\input{secure-calls.tex}
\chapter{Call Completion Supplementary Services}
\input{ccss.tex}

45
doc/tex/secure-calls.tex Normal file
View File

@ -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}

View File

@ -38,6 +38,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/app.h"
#include "asterisk/indications.h"
#include "asterisk/stringfields.h"
#include "asterisk/global_datastores.h"
/*** DOCUMENTATION
<function name="CHANNELS" language="en_US">
@ -102,6 +103,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="rxgain">
<para>R/W set rxgain level on channel drivers that support it.</para>
</enum>
<enum name="secure_bridge_signaling">
<para>Whether or not channels bridged to this channel require secure signaling</para>
</enum>
<enum name="secure_bridge_media">
<para>Whether or not channels bridged to this channel require secure media</para>
</enum>
<enum name="state">
<para>R/O state for channel</para>
</enum>
@ -344,6 +351,18 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
char amabuf[256];
snprintf(amabuf,sizeof(amabuf), "%d", chan->amaflags);
locked_copy_string(chan, buf, amabuf, len);
} else if (!strncasecmp(data, "secure_bridge_", 14)) {
struct ast_datastore *ds;
ast_channel_lock(chan);
if ((ds = ast_channel_datastore_find(chan, &secure_call_info, NULL))) {
struct ast_secure_call_store *encrypt = ds->data;
if (!strcasecmp(data, "secure_bridge_signaling")) {
snprintf(buf, len, "%s", encrypt->signaling ? "1" : "");
} else if (!strcasecmp(data, "secure_bridge_media")) {
snprintf(buf, len, "%s", encrypt->media ? "1" : "");
}
}
ast_channel_unlock(chan);
} else if (!chan->tech || !chan->tech->func_channel_read || chan->tech->func_channel_read(chan, function, data, buf, len)) {
ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", data);
ret = -1;
@ -429,6 +448,37 @@ static int func_channel_write(struct ast_channel *chan, const char *function,
break;
}
}
} else if (!strncasecmp(data, "secure_bridge_", 14)) {
struct ast_datastore *ds;
struct ast_secure_call_store *store;
if (!chan || !value) {
return -1;
}
ast_channel_lock(chan);
if (!(ds = ast_channel_datastore_find(chan, &secure_call_info, NULL))) {
if (!(ds = ast_datastore_alloc(&secure_call_info, NULL))) {
ast_channel_unlock(chan);
return -1;
}
if (!(store = ast_calloc(1, sizeof(*store)))) {
ast_channel_unlock(chan);
ast_free(ds);
return -1;
}
ds->data = store;
ast_channel_datastore_add(chan, ds);
} else {
store = ds->data;
}
ast_channel_unlock(chan);
if (!strcasecmp(data, "secure_bridge_signaling")) {
store->signaling = ast_true(value) ? 1 : 0;
} else if (!strcasecmp(data, "secure_bridge_media")) {
store->media = ast_true(value) ? 1 : 0;
}
} else if (!chan->tech->func_channel_write
|| chan->tech->func_channel_write(chan, function, data, value)) {
ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n",

View File

@ -702,6 +702,9 @@
/* Define to 1 if you have the `sqrtl' function. */
#undef HAVE_SQRTL
/* Define to 1 if you have the Secure RTP library. */
#undef HAVE_SRTP
/* Define to 1 if you have the ISDN SS7 library. */
#undef HAVE_SS7
@ -791,7 +794,7 @@
/* Define to 1 if you have the `strtoq' function. */
#undef HAVE_STRTOQ
/* Define to 1 if `st_blksize' is member of `struct stat'. */
/* Define to 1 if `st_blksize' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_BLKSIZE
/* Define to 1 if you have the mISDN Supplemental Services library. */
@ -1059,12 +1062,12 @@
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if the C compiler supports function prototypes. */
#undef PROTOTYPES
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
#undef PTHREAD_CREATE_JOINABLE
@ -1084,11 +1087,6 @@
/* Define to the type of arg 5 for `select'. */
#undef SELECT_TYPE_ARG5
/* Define to 1 if the `setvbuf' function takes the buffering type as its
second argument and the buffer pointer as the third, as on System V before
release 3. */
#undef SETVBUF_REVERSED
/* The size of `char *', as computed by sizeof. */
#undef SIZEOF_CHAR_P
@ -1118,20 +1116,30 @@
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
/* Define to 1 if on AIX 3.
System headers sometimes define this.
We just want to avoid a redefinition error message. */
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
#undef _LARGEFILE_SOURCE
@ -1149,20 +1157,6 @@
/* Define to 1 if you need to in order for `stat' and other things to work. */
#undef _POSIX_SOURCE
/* Enable extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Define like PROTOTYPES; this can be used by system headers. */
#undef __PROTOTYPES
/* Define to empty if `const' does not conform to ANSI C. */
#undef const

View File

@ -459,6 +459,10 @@ enum ast_control_transfer {
/*! Get the CC agent type from the channel */
#define AST_OPTION_CC_AGENT_TYPE 17
/*! Get or set the security options on a channel */
#define AST_OPTION_SECURE_SIGNALING 18
#define AST_OPTION_SECURE_MEDIA 19
struct oprmode {
struct ast_channel *peer;
int mode;

View File

@ -27,10 +27,15 @@
#include "asterisk/channel.h"
extern const struct ast_datastore_info dialed_interface_info;
extern const struct ast_datastore_info secure_call_info;
struct ast_dialed_interface {
AST_LIST_ENTRY(ast_dialed_interface) list;
char interface[1];
};
struct ast_secure_call_store {
unsigned int signaling:1;
unsigned int media:1;
};
#endif

View File

@ -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 */

View File

@ -71,6 +71,9 @@ extern "C" {
#include "asterisk/astobj2.h"
#include "asterisk/frame.h"
#include "asterisk/netsock.h"
#include "asterisk/sched.h"
#include "asterisk/res_srtp.h"
/* Maximum number of payloads supported */
#define AST_RTP_MAX_PT 256
@ -458,6 +461,11 @@ int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *m
*/
int ast_rtp_engine_unregister(struct ast_rtp_engine *engine);
int ast_rtp_engine_register_srtp(struct ast_srtp_res *srtp_res, struct ast_srtp_policy_res *policy_res);
void ast_rtp_engine_unregister_srtp(void);
int ast_rtp_engine_srtp_is_registered(void);
#define ast_rtp_glue_register(glue) ast_rtp_glue_register2(glue, ast_module_info->self)
/*!
@ -1724,6 +1732,9 @@ struct ast_rtp_glue *ast_rtp_instance_get_active_glue(struct ast_rtp_instance *i
*/
struct ast_channel *ast_rtp_instance_get_chan(struct ast_rtp_instance *instance);
int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy *policy);
struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif

View File

@ -40,6 +40,9 @@
LINKER_SYMBOL_PREFIXgetloadavg;
LINKER_SYMBOL_PREFIXntohll;
LINKER_SYMBOL_PREFIXhtonll;
LINKER_SYMBOL_PREFIXres_srtp;
LINKER_SYMBOL_PREFIXres_srtp_policy;
LINKER_SYMBOL_PREFIXsecure_call_info;
local:
*;
};

View File

@ -65,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/timing.h"
#include "asterisk/autochan.h"
#include "asterisk/stringfields.h"
#include "asterisk/global_datastores.h"
#include "asterisk/data.h"
#ifdef HAVE_EPOLL
@ -4803,6 +4804,46 @@ struct ast_channel *ast_request_and_dial(const char *type, format_t format, cons
return __ast_request_and_dial(type, format, requestor, data, timeout, outstate, cidnum, cidname, NULL);
}
static int set_security_requirements(const struct ast_channel *requestor, struct ast_channel *out)
{
int ops[2][2] = {
{AST_OPTION_SECURE_SIGNALING, 0},
{AST_OPTION_SECURE_MEDIA, 0},
};
int i;
struct ast_channel *r = (struct ast_channel *) requestor; /* UGLY */
struct ast_datastore *ds;
if (!requestor || !out) {
return 0;
}
ast_channel_lock(r);
if ((ds = ast_channel_datastore_find(r, &secure_call_info, NULL))) {
struct ast_secure_call_store *encrypt = ds->data;
ops[0][1] = encrypt->signaling;
ops[1][1] = encrypt->media;
} else {
ast_channel_unlock(r);
return 0;
}
ast_channel_unlock(r);
for (i = 0; i < 2; i++) {
if (ops[i][1]) {
if (ast_channel_setoption(out, ops[i][0], &ops[i][1], sizeof(ops[i][1]), 0)) {
/* We require a security feature, but the channel won't provide it */
return -1;
}
} else {
/* We don't care if we can't clear the option on a channel that doesn't support it */
ast_channel_setoption(out, ops[i][0], &ops[i][1], sizeof(ops[i][1]), 0);
}
}
return 0;
}
struct ast_channel *ast_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
{
struct chanlist *chan;
@ -4851,6 +4892,13 @@ struct ast_channel *ast_request(const char *type, format_t format, const struct
if (!(c = chan->tech->requester(type, capabilities | videoformat | textformat, requestor, data, cause)))
return NULL;
if (set_security_requirements(requestor, c)) {
ast_log(LOG_WARNING, "Setting security requirements failed\n");
c = ast_channel_release(c);
*cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
return NULL;
}
/* no need to generate a Newchannel event here; it is done in the channel_alloc call */
return c;
}

View File

@ -84,3 +84,29 @@ const struct ast_datastore_info dialed_interface_info = {
.destroy = dialed_interface_destroy,
.duplicate = dialed_interface_duplicate,
};
static void secure_call_store_destroy(void *data)
{
struct ast_secure_call_store *store = data;
ast_free(store);
}
static void *secure_call_store_duplicate(void *data)
{
struct ast_secure_call_store *old = data;
struct ast_secure_call_store *new;
if (!(new = ast_calloc(1, sizeof(*new)))) {
return NULL;
}
new->signaling = old->signaling;
new->media = old->media;
return new;
}
const struct ast_datastore_info secure_call_info = {
.type = "encrypt-call",
.destroy = secure_call_store_destroy,
.duplicate = secure_call_store_duplicate,
};

View File

@ -39,6 +39,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/translate.h"
struct ast_srtp_res *res_srtp = NULL;
struct ast_srtp_policy_res *res_srtp_policy = NULL;
/*! Structure that represents an RTP session (instance) */
struct ast_rtp_instance {
/*! Engine that is handling this RTP instance */
@ -67,6 +70,8 @@ struct ast_rtp_instance {
struct ast_rtp_glue *glue;
/*! Channel associated with the instance */
struct ast_channel *chan;
/*! SRTP info associated with the instance */
struct ast_srtp *srtp;
};
/*! List of RTP engines that are currently registered */
@ -1670,3 +1675,47 @@ struct ast_channel *ast_rtp_instance_get_chan(struct ast_rtp_instance *instance)
{
return instance->chan;
}
int ast_rtp_engine_register_srtp(struct ast_srtp_res *srtp_res, struct ast_srtp_policy_res *policy_res)
{
if (res_srtp || res_srtp_policy) {
return -1;
}
if (!srtp_res || !policy_res) {
return -1;
}
res_srtp = srtp_res;
res_srtp_policy = policy_res;
return 0;
}
void ast_rtp_engine_unregister_srtp(void)
{
res_srtp = NULL;
res_srtp_policy = NULL;
}
int ast_rtp_engine_srtp_is_registered(void)
{
return res_srtp && res_srtp_policy;
}
int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy *policy)
{
if (!res_srtp) {
return -1;
}
if (!instance->srtp) {
return res_srtp->create(&instance->srtp, instance, policy);
} else {
return res_srtp->add_stream(instance->srtp, policy);
}
}
struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance)
{
return instance->srtp;
}

View File

@ -228,6 +228,9 @@ SQLITE_LIB=@SQLITE_LIB@
SQLITE3_INCLUDE=@SQLITE3_INCLUDE@
SQLITE3_LIB=@SQLITE3_LIB@
SRTP_LIB=@SRTP_LIB@
SRTP_INCLUDE=@SRTP_INCLUDE@
OPENSSL_INCLUDE=@OPENSSL_INCLUDE@
OPENSSL_LIB=@OPENSSL_LIB@

View File

@ -78,6 +78,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define ZFONE_PROFILE_ID 0x505a
extern struct ast_srtp_res *res_srtp;
static int dtmftimeout = DEFAULT_DTMF_TIMEOUT;
static int rtpstart = DEFAULT_RTP_START; /*!< First port for RTP sessions (set in rtp.conf) */
@ -328,6 +329,57 @@ static inline int rtcp_debug_test_addr(struct sockaddr_in *addr)
return 1;
}
static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct sockaddr *sa, socklen_t *salen, int rtcp)
{
int len;
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
if ((len = recvfrom(rtcp ? rtp->rtcp->s : rtp->s, buf, size, flags, sa, salen)) < 0) {
return len;
}
if (res_srtp && srtp && res_srtp->unprotect(srtp, buf, &len, rtcp) < 0) {
return -1;
}
return len;
}
static int rtcp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct sockaddr *sa, socklen_t *salen)
{
return __rtp_recvfrom(instance, buf, size, flags, sa, salen, 1);
}
static int rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct sockaddr *sa, socklen_t *salen)
{
return __rtp_recvfrom(instance, buf, size, flags, sa, salen, 0);
}
static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct sockaddr *sa, socklen_t salen, int rtcp)
{
int len = size;
void *temp = buf;
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
if (res_srtp && srtp && res_srtp->protect(srtp, &temp, &len, rtcp) < 0) {
return -1;
}
return sendto(rtcp ? rtp->rtcp->s : rtp->s, temp, len, flags, sa, salen);
}
static int rtcp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct sockaddr *sa, socklen_t salen)
{
return __rtp_sendto(instance, buf, size, flags, sa, salen, 1);
}
static int rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct sockaddr *sa, socklen_t salen)
{
return __rtp_sendto(instance, buf, size, flags, sa, salen, 0);
}
static int rtp_get_rate(format_t subclass)
{
return (subclass == AST_FORMAT_G722) ? 8000 : ast_format_rate(subclass);
@ -529,7 +581,7 @@ static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit)
/* Actually send the packet */
for (i = 0; i < 2; i++) {
rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration));
res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address));
res = rtp_sendto(instance, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address));
if (res < 0) {
ast_log(LOG_ERROR, "RTP Transmission error to %s:%u: %s\n",
ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), strerror(errno));
@ -575,7 +627,7 @@ static int ast_rtp_dtmf_continuation(struct ast_rtp_instance *instance)
rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno));
/* Boom, send it on out */
res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address));
res = rtp_sendto(instance, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address));
if (res < 0) {
ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n",
ast_inet_ntoa(remote_address.sin_addr),
@ -638,7 +690,7 @@ static int ast_rtp_dtmf_end(struct ast_rtp_instance *instance, char digit)
/* Send it 3 times, that's the magical number */
for (i = 0; i < 3; i++) {
res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address));
res = rtp_sendto(instance, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address));
if (res < 0) {
ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n",
ast_inet_ntoa(remote_address.sin_addr),
@ -714,9 +766,9 @@ static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw)
}
/*! \brief Send RTCP recipient's report */
static int ast_rtcp_write_rr(const void *data)
static int ast_rtcp_write_rr(struct ast_rtp_instance *instance)
{
struct ast_rtp *rtp = (struct ast_rtp *)data;
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
int res;
int len = 32;
unsigned int lost;
@ -789,7 +841,7 @@ static int ast_rtcp_write_rr(const void *data)
rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */
len += 12;
res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));
res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));
if (res < 0) {
ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted: %s\n",strerror(errno));
@ -817,9 +869,9 @@ static int ast_rtcp_write_rr(const void *data)
}
/*! \brief Send RTCP sender's report */
static int ast_rtcp_write_sr(const void *data)
static int ast_rtcp_write_sr(struct ast_rtp_instance *instance)
{
struct ast_rtp *rtp = (struct ast_rtp *)data;
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
int res;
int len = 0;
struct timeval now;
@ -889,7 +941,7 @@ static int ast_rtcp_write_sr(const void *data)
rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */
len += 12;
res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));
res =rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));
if (res < 0) {
ast_log(LOG_ERROR, "RTCP SR transmission error to %s:%d, rtcp halted %s\n",ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), strerror(errno));
AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid);
@ -947,16 +999,17 @@ static int ast_rtcp_write_sr(const void *data)
* RR is sent if we have not sent any rtp packets in the previous interval */
static int ast_rtcp_write(const void *data)
{
struct ast_rtp *rtp = (struct ast_rtp *)data;
struct ast_rtp_instance *instance = (struct ast_rtp_instance *) data;
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
int res;
if (!rtp || !rtp->rtcp)
return 0;
if (rtp->txcount > rtp->rtcp->lastsrtxcount)
res = ast_rtcp_write_sr(data);
res = ast_rtcp_write_sr(instance);
else
res = ast_rtcp_write_rr(data);
res = ast_rtcp_write_rr(instance);
return res;
}
@ -1049,7 +1102,7 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame
put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts));
put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc));
if ((res = sendto(rtp->s, (void *)rtpheader, frame->datalen + hdrlen, 0, (struct sockaddr *)&remote_address, sizeof(remote_address))) < 0) {
if ((res = rtp_sendto(instance, (void *)rtpheader, frame->datalen + hdrlen, 0, (struct sockaddr *)&remote_address, sizeof(remote_address))) < 0) {
if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) || (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
ast_debug(1, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), strerror(errno));
} else if (((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(rtp, FLAG_NAT_INACTIVE_NOWARN)) {
@ -1064,7 +1117,7 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame
if (rtp->rtcp && rtp->rtcp->schedid < 1) {
ast_debug(1, "Starting RTCP transmission on RTP instance '%p'\n", instance);
rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, instance);
}
}
@ -1564,7 +1617,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
struct ast_frame *f = &ast_null_frame;
/* Read in RTCP data from the socket */
if ((res = recvfrom(rtp->rtcp->s, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len)) < 0) {
if ((res = rtcp_recvfrom(instance, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len)) < 0) {
ast_assert(errno != EBADF);
if (errno != EAGAIN) {
ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n", strerror(errno));
@ -1857,7 +1910,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
ast_rtp_instance_get_remote_address(instance1, &remote_address);
/* Send the packet back out */
res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&remote_address, sizeof(remote_address));
res = rtp_sendto(instance1, (void *)rtpheader, len, 0, (struct sockaddr *)&remote_address, sizeof(remote_address));
if (res < 0) {
if (!ast_rtp_instance_get_prop(instance1, AST_RTP_PROPERTY_NAT) || (ast_rtp_instance_get_prop(instance1, AST_RTP_PROPERTY_NAT) && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
ast_debug(1, "RTP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), strerror(errno));
@ -1899,7 +1952,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
}
/* Actually read in the data from the socket */
if ((res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr*)&sin, &len)) < 0) {
if ((res = rtp_recvfrom(instance, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr*)&sin, &len)) < 0) {
ast_assert(errno != EBADF);
if (errno != EAGAIN) {
ast_log(LOG_WARNING, "RTP Read error: %s. Hanging up.\n", strerror(errno));
@ -2040,7 +2093,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
/* Do not schedule RR if RTCP isn't run */
if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && rtp->rtcp->schedid < 1) {
/* Schedule transmission of Receiver Report */
rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, instance);
}
if ((int)rtp->lastrxseqno - (int)seqno > 100) /* if so it would indicate that the sender cycled; allow for misordering */
rtp->cycles += RTP_SEQ_MOD;

403
res/res_srtp.c Normal file
View File

@ -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,
);

4
res/res_srtp.exports.in Normal file
View File

@ -0,0 +1,4 @@
{
local:
*;
};