Index: build_tools/menuselect-deps.in =================================================================== --- build_tools/menuselect-deps.in (revision 189271) +++ build_tools/menuselect-deps.in (working copy) @@ -42,6 +42,7 @@ SPEEX_PREPROCESS=@PBX_SPEEX_PREPROCESS@ SQLITE3=@PBX_SQLITE3@ SQLITE=@PBX_SQLITE@ +SRTP=@PBX_SRTP@ SS7=@PBX_SS7@ OPENSSL=@PBX_OPENSSL@ SUPPSERV=@PBX_SUPPSERV@ Index: channels/sip_srtp.c =================================================================== --- channels/sip_srtp.c (revision 0) +++ channels/sip_srtp.c (revision 0) @@ -0,0 +1,52 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * 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 + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "sip_srtp.h" + +struct sip_srtp *sip_srtp_alloc(void) +{ + struct sip_srtp *srtp; + + if (!(srtp = ast_calloc(1, sizeof(*srtp)))) { + ast_log(LOG_ERROR, "Out of memory, can't allocate srtp structure\n"); + } + + return srtp; +} + +void sip_srtp_destroy(struct sip_srtp *srtp) +{ + if (srtp->crypto) { + sdp_crypto_destroy(srtp->crypto); + } + srtp->crypto = NULL; + ast_free(srtp); +} Index: channels/sip_srtp.h =================================================================== --- channels/sip_srtp.h (revision 0) +++ channels/sip_srtp.h (revision 0) @@ -0,0 +1,57 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * 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 + */ + +#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 */ Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 189271) +++ channels/chan_sip.c (working copy) @@ -272,6 +272,9 @@ #include "asterisk/event.h" #include "asterisk/tcptls.h" +#include "sip_srtp.h" +#include "sdp_crypto.h" + /*** DOCUMENTATION @@ -1367,6 +1370,7 @@ #define SIP_PAGE2_RTCACHEFRIENDS (1 << 0) /*!< GP: Should we keep RT objects in memory for extended time? */ #define SIP_PAGE2_RTAUTOCLEAR (1 << 2) /*!< GP: Should we clean memory from peers after expiry? */ /* Space for addition of other realtime flags in the future */ +#define SIP_PAGE2_SRTPCAPABLE (1 << 8) /*!< DP: Are we capable of handling SRTP? */ #define SIP_PAGE2_STATECHANGEQUEUE (1 << 9) /*!< D: Unsent state pending change exists */ #define SIP_PAGE2_VIDEOSUPPORT (1 << 14) /*!< DP: Video supported if offered? */ @@ -1713,6 +1717,7 @@ (A bit unsure of this, please correct if you know more) */ struct sip_st_dlg *stimer; /*!< SIP Session-Timers */ + struct sip_srtp *srtp; /*!< Structure for Secure RTP session data */ 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) */ @@ -2571,6 +2576,10 @@ static int sip_get_codec(struct ast_channel *chan); static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect); +/*----- SRTP interface functions */ +static int setup_srtp(struct sip_pvt *p); +static int process_crypto(struct sip_pvt *p, 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); @@ -5001,6 +5010,19 @@ } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) { /* We're replacing a call. */ p->options->replaces = ast_var_value(current); + } else if (!strcasecmp(ast_var_name(current), "SIPSRTP")) { + if (ast_false(ast_var_value(current))) { + continue; + } + + if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) { + ast_debug(1, "Reinviting not possible when using SRTP, ignoring canreinvite setting\n"); + } + + if (!p->srtp && setup_srtp(p) < 0) { + ast_log(LOG_WARNING, "SRTP setup failed\n"); + return -1; + } } } @@ -5201,6 +5223,10 @@ ast_variables_destroy(p->chanvars); p->chanvars = NULL; } + if (p->srtp) { + sip_srtp_destroy(p->srtp); + p->srtp = NULL; + } ast_string_field_free_memory(p); @@ -7405,7 +7431,9 @@ int newnoncodeccapability; int numberofmediastreams = 0; int debug = sip_debug_test_pvt(p); - + int secure_audio = FALSE; + int secure_video = FALSE; + int found_rtpmap_codecs[SDP_MAX_RTPMAP_CODECS]; int last_rtpmap_codec=0; @@ -7544,11 +7572,22 @@ int audio = FALSE; int video = FALSE; int text = FALSE; + char protocol[5] = ""; numberofports = 1; len = -1; - if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || - (sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1 && len > 0)) { + if ((sscanf(m, "audio %d/%d RTP/%4s %n", &x, &numberofports, protocol, &len) == 3) || + (sscanf(m, "audio %d RTP/%4s %n", &x, protocol, &len) == 2)) { + 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; + } + if (len < 0) { + ast_log(LOG_WARNING, "Unknown SDP media type in offer: %s\n", m); + continue; + } audio = TRUE; numberofmediastreams++; /* Found audio stream in this media definition */ @@ -7563,8 +7602,18 @@ ast_verbose("Found RTP audio format %d\n", codec); ast_rtp_set_m_type(newaudiortp, codec); } - } else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || - (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1 && len >= 0)) { + } else if ((sscanf(m, "video %d/%d RTP/%4s %n", &x, &numberofports, protocol, &len) == 3) || + (sscanf(m, "video %d RTP/%4s %n", &x, protocol, &len) == 2)) { + if (!strcmp(protocol, "SAVP")) + secure_video = 1; + else if (strcmp(protocol, "AVP")) { + ast_log(LOG_WARNING, "Unknown SDP media protocol for video in offer: %s\n", protocol); + continue; + } + if (len < 0) { + ast_log(LOG_WARNING, "Unknown SDP media type for video in offer: %s\n", m); + continue; + } video = TRUE; p->novideo = FALSE; numberofmediastreams++; @@ -7731,11 +7780,6 @@ if (debug) ast_verbose("Got unsupported a:maxprate in SDP offer \n"); breakout = TRUE; - } else if (!strncasecmp(a, "crypto:", (size_t) 7)) { - /* SRTP stuff, not yet supported */ - if (debug) - ast_verbose("Got unsupported a:crypto in SDP offer \n"); - breakout = TRUE; } if (breakout) /* We have a match, skip to next header */ continue; @@ -7745,6 +7789,12 @@ if (sendonly == -1) sendonly = 1; continue; + } else if (!strncasecmp(a, "crypto:", (size_t) 7)) { + process_crypto(p, a); + continue; + } else if (!strncasecmp(a, "key-mgmt:mikey ", (size_t) 15)) { + ast_log(LOG_NOTICE, "Asterisk currently does not support MIKEY key negotiation\n"); + continue; } if (!strcasecmp(a, "inactive")) { @@ -7897,6 +7947,16 @@ } } + + 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 -2; + } + + if (!secure_audio && p->srtp) { + ast_log(LOG_WARNING, "We are requesting SRTP, but they responded without it!\n"); + return -2; + } if (udptlportno != -1) { int found = 0, x; @@ -8023,6 +8083,16 @@ change_t38_state(p, T38_DISABLED); } + if (secure_video && !(p->srtp && (ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)))) { + ast_log(LOG_WARNING, "Can't provide secure video requested in SDP offer\n"); + return -2; + } + + if (!p->novideo && !secure_video && p->srtp) { + ast_log(LOG_WARNING, "We are requesting SRTP, but they responded without it!\n"); + return -2; + } + /* Now gather all of the codecs that we are asked for: */ ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability); ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability); @@ -9215,9 +9285,12 @@ 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; int x; int capability = 0; + const char *protocol = NULL; + struct sip_srtp *srtp = p->srtp; int needaudio = FALSE; int needvideo = FALSE; int needtext = FALSE; @@ -9281,13 +9354,36 @@ ast_debug(2, "This call needs video offers, but there's no video support enabled!\n"); } + /* Set encryption properties */ + if (srtp) { + if (srtp->crypto) { + a_crypto = sdp_crypto_attrib(srtp->crypto); + } else { + 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"); + } + } + + if (a_crypto) { + protocol = "SAVP"; + } else { + protocol = "AVP"; + } + if (debug) ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port)); /* 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)); + ast_str_append(&m_video, 0, "m=video %d RTP/%s", ntohs(vdest.sin_port), protocol); /* Build max bitrate string */ if (p->maxcallbitrate) @@ -9333,8 +9429,8 @@ /* 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)); - + ast_str_append(&m_audio, 0, "m=audio %d RTP/%s", ntohs(dest.sin_port), protocol); + if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR) hold = "a=recvonly\r\n"; else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE) @@ -9489,6 +9585,10 @@ if (add_t38) len += m_modem->used + a_modem->used; + if (a_crypto) { + len += strlen(a_crypto); + } + add_header(resp, "Content-Type", "application/sdp"); add_header_contentLength(resp, len); add_line(resp, version); @@ -9517,6 +9617,9 @@ add_line(resp, m_modem->str); add_line(resp, a_modem->str); } + if (a_crypto) { + add_line(resp, a_crypto); + } /* Update lastrtprx when we send our SDP */ p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ @@ -9728,7 +9831,7 @@ ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr)); } else { /*! \todo We should not always add port here. Port is only added if it's non-standard (see code above) */ - ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr), ourport, get_transport(p->socket.type)); + ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr), get_transport(p->socket.type)); } } @@ -16351,6 +16454,8 @@ 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, "srtpcapable")) { + snprintf(buf, len, "%d", ast_test_flag(&peer->flags[1], SIP_PAGE2_SRTPCAPABLE)); } else if (!strncasecmp(colname, "chanvar[", 8)) { char *chanvar=colname + 8; struct ast_variable *v; @@ -19305,7 +19410,12 @@ ast_debug(2, "%s: This call is UP.... \n", c->name); transmit_response(p, "100 Trying", req); - + + if (p->srtp && !ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)) { + transmit_response_reliable(p, "488 Not Acceptable Here (crypto)", req); + p->invitestate = INV_TERMINATED; + break; + } if (p->t38.state == T38_PEER_REINVITE) { p->t38id = ast_sched_add(sched, 5000, sip_t38_abort, dialog_ref(p, "passing dialog ptr into sched structure based on t38id for sip_t38_abort.")); } else if (p->t38.state == T38_ENABLED) { @@ -19316,7 +19426,7 @@ 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); } - + p->invitestate = INV_TERMINATED; break; default: @@ -19933,6 +20043,12 @@ ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname); return -1; } + } else if (!strcasecmp(args.param, "secure_signalling")) { + snprintf(buf, buflen, "%d", p->socket.type == SIP_TRANSPORT_TLS ? 1 : 0); + } else if (!strcasecmp(args.param, "secure_media")) { + snprintf(buf, buflen, "%d", p->srtp ? 1 : 0); + } else if (!strcasecmp(args.param, "transport")) { + ast_copy_string(buf, get_transport(p->socket.type), buflen); } else { res = -1; } @@ -22872,6 +22988,8 @@ } else { peer->stimer.st_ref = i; } + } else if (!strcasecmp(v->name, "srtpcapable")) { + ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_SRTPCAPABLE); } } @@ -24332,6 +24450,50 @@ ); } +/* + * SRTP + */ + +static int setup_srtp(struct sip_pvt *p) +{ + if (!ast_srtp_is_registered()) { + ast_log(LOG_ERROR, "No SRTP module loaded, can't setup SRTP session.\n"); + return -1; + } + + if (!(p->srtp = sip_srtp_alloc())) { /* Allocate SRTP data structure */ + return -1; + } + + return 0; +} + +static int process_crypto(struct sip_pvt *p, const char *a) +{ + if (!p->srtp) { + if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + ast_log(LOG_WARNING, "Ignoring unexpected crypto attribute in SDP answer\n"); + return -1; + } + + if (setup_srtp(p) < 0) { + return -1; + } + } + + if (!p->srtp->crypto && !(p->srtp->crypto = sdp_crypto_setup())) { + return -1; + } + + if (sdp_crypto_process(p->srtp->crypto, a, p->rtp) < 0) { + return -1; + } + + ast_set_flag(p->srtp, SRTP_CRYPTO_OFFER_OK); + + return 0; +} + /*! \brief Send all MWI subscriptions */ static void sip_send_all_mwi_subscriptions(void) { Index: channels/Makefile =================================================================== --- channels/Makefile (revision 189271) +++ channels/Makefile (working copy) @@ -84,6 +84,8 @@ endif endif +chan_sip.so: sip_srtp.o sdp_crypto.o + chan_misdn.o: ASTCFLAGS+=-Imisdn misdn_config.o: ASTCFLAGS+=-Imisdn Index: channels/chan_iax2.c =================================================================== --- channels/chan_iax2.c (revision 189271) +++ channels/chan_iax2.c (working copy) @@ -12230,6 +12230,8 @@ 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_signalling") || !strcasecmp(args, "secure_media")) { + snprintf(buf, buflen, "%d", ast_test_flag(pvt, IAX_ENCRYPTED) ? 1 : 0); } else { res = -1; } Index: channels/sdp_crypto.c =================================================================== --- channels/sdp_crypto.c (revision 0) +++ channels/sdp_crypto.c (revision 0) @@ -0,0 +1,286 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * 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 + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/options.h" +#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) + +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; + + if (!(crypto = ast_calloc(1, sizeof(*crypto)))) { + ast_log(LOG_ERROR, "Out of memory, can't allocate crypto structure\n"); + } + + return crypto; +} + +void sdp_crypto_destroy(struct sdp_crypto *crypto) +{ + if (crypto->a_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 (!(p = sdp_crypto_alloc())) { + return NULL; + } + + if (ast_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); + } + + if (memcmp(remote_key, p->local_key, SRTP_MASTER_LEN)) { + ast_log(LOG_ERROR, "base64 encode/decode bad key\n"); + } + + 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; + + master_salt = master_key + SRTP_MASTERKEY_LEN; + if (ast_srtp_policy_set_master_key(policy, master_key, SRTP_MASTERKEY_LEN, master_salt, SRTP_MASTERSALT_LEN) < 0) { + return -1; + } + + if (ast_srtp_policy_set_suite(policy, suite_val)) { + ast_log(LOG_WARNING, "Could not set remote SRTP suite\n"); + return -1; + } + + ast_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 *rtp) +{ + struct ast_srtp_policy *local_policy = NULL; + struct ast_srtp_policy *remote_policy = NULL; + int res = -1; + + if (!p) { + return -1; + } + + if (!(local_policy = ast_srtp_policy_alloc())) { + return -1; + } + + if (!(remote_policy = ast_srtp_policy_alloc())) { + goto err; + } + + if (set_crypto_policy(local_policy, suite_val, p->local_key, ast_rtp_get_ssrc(rtp), 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_add_srtp_policy(rtp, local_policy)) { + ast_log(LOG_WARNING, "Could not set local SRTP policy\n"); + goto err; + } + + if (ast_rtp_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) { + ast_srtp_policy_destroy(local_policy); + } + + if (remote_policy) { + ast_srtp_policy_destroy(remote_policy); + } + + return res; +} + +int sdp_crypto_process(struct sdp_crypto *p, const char *attr, struct ast_rtp *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_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", 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); + } + + snprintf(crypto_buf, sizeof(crypto_buf), "a=crypto:1 %s inline:%s\r\n", crypto_suite, p->local_key64); + p->a_crypto = ast_strdup(crypto_buf); + + return 0; +} + +const char *sdp_crypto_attrib(struct sdp_crypto *p) +{ + return p->a_crypto; +} Index: channels/sdp_crypto.h =================================================================== --- channels/sdp_crypto.h (revision 0) +++ channels/sdp_crypto.h (revision 0) @@ -0,0 +1,42 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * 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 + */ + +#ifndef _SDP_CRYPTO_H +#define _SDP_CRYPTO_H + +#include + +struct sdp_crypto; + +struct sdp_crypto *sdp_crypto_setup(void); +void sdp_crypto_destroy(struct sdp_crypto *crypto); + +int sdp_crypto_process(struct sdp_crypto *p, const char *attr, struct ast_rtp *rtp); +int sdp_crypto_offer(struct sdp_crypto *p); +const char *sdp_crypto_attrib(struct sdp_crypto *p); + +#endif /* _SDP_CRYPTO_H */ Index: CREDITS =================================================================== --- CREDITS (revision 189271) +++ CREDITS (working copy) @@ -153,6 +153,9 @@ George Konstantoulakis - Support for Greek in voicemail added by InAccess Networks (work funded by HOL, www.hol.gr) gkon(AT)inaccessnetworks.com +Mikael Magnusson - Provided SRTP support in RTP, and SRTP support in the SIP channel + mikma@users.sourceforge.net + Daniel Nylander - Support for Swedish and Norwegian languages in voicemail. http://www.danielnylander.se/ Index: configure.ac =================================================================== --- configure.ac (revision 189271) +++ configure.ac (working copy) @@ -282,6 +282,7 @@ AST_EXT_LIB_SETUP([SPEEXDSP], [Speexdsp], [speexdsp]) 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([SUPPSERV], [mISDN Supplemental Services], [suppserv]) AST_EXT_LIB_SETUP([OPENSSL], [OpenSSL Secure Sockets Layer support], [ssl]) AST_EXT_LIB_SETUP([FREETDS], [FreeTDS], [tds]) @@ -1530,6 +1531,8 @@ AST_EXT_LIB_CHECK([OSPTK], [osptk], [OSPPCryptoDecrypt], [osp/osp.h], [-lcrypto -lssl]) fi +AST_EXT_LIB_CHECK([SRTP], [srtp], [srtp_init], [srtp/srtp.h]) + AST_EXT_TOOL_CHECK([GMIME], [gmime], [], [], [#include ], [gboolean q = g_mime_check_version(0,0,0);]) AST_EXT_LIB_CHECK([HOARD], [hoard], [malloc], []) Index: apps/app_dial.c =================================================================== --- apps/app_dial.c (revision 189271) +++ apps/app_dial.c (working copy) @@ -491,9 +491,11 @@ #define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 33) #define OPT_PEER_H ((uint64_t)1 << 34) #define OPT_CALLEE_GO_ON ((uint64_t)1 << 35) +#define OPT_ANNOUNCE_BOTH ((uint64_t)1 << 36) enum { OPT_ARG_ANNOUNCE = 0, + OPT_ARG_ANNOUNCE_BOTH, OPT_ARG_SENDDTMF, OPT_ARG_GOTO, OPT_ARG_DURATION_LIMIT, @@ -510,6 +512,7 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE), + AST_APP_OPTION_ARG('B', OPT_ANNOUNCE_BOTH, OPT_ARG_ANNOUNCE_BOTH), AST_APP_OPTION('C', OPT_RESETCDR), AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE), AST_APP_OPTION('d', OPT_DTMF_EXIT), @@ -1488,6 +1491,45 @@ bconfig->end_bridge_callback_data = originator; } +struct prompt_data { + struct ast_channel *chan; + const char *prompt; + int res; +}; + +static void *play_prompts_cb(void *data) +{ + struct prompt_data *pd = data; + + if (!ast_strlen_zero(pd->prompt) && !(pd->res = ast_streamfile(pd->chan, pd->prompt, pd->chan->language))) { + pd->res = ast_waitstream(pd->chan, ""); + } + + return &pd->res; +} + +static int play_prompts(struct ast_channel *chan, struct ast_channel *peer, const char *chan_prompt, const char *peer_prompt) +{ + struct prompt_data one, two; + pthread_t thread1 = AST_PTHREADT_NULL, thread2 = AST_PTHREADT_NULL; + int res; + void *ret = &res; + + one.chan = chan; + two.chan = peer; + one.prompt = chan_prompt; + two.prompt = peer_prompt; + + ast_pthread_create(&thread1, NULL, play_prompts_cb, &one); + ast_pthread_create(&thread2, NULL, play_prompts_cb, &two); + pthread_join(thread1, ret); + res = *(int *)ret; + pthread_join(thread2, ret); + res |= *(int *)ret; + + return res; +} + static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags64 *peerflags, int *continue_exec) { int res = -1; /* default: error */ @@ -1944,7 +1986,6 @@ } theapp = pbx_findapp("Macro"); - if (theapp && !res) { /* XXX why check res here ? */ /* Set peer->exten and peer->context so that MACRO_EXTEN and MACRO_CONTEXT get set */ ast_copy_string(peer->context, chan->context, sizeof(peer->context)); @@ -1968,6 +2009,7 @@ if (!res && (macro_result = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) { char *macro_transfer_dest; + char *prompt1; if (!strcasecmp(macro_result, "BUSY")) { ast_copy_string(pa.status, macro_result, sizeof(pa.status)); @@ -1995,6 +2037,24 @@ if (!ast_parseable_goto(chan, macro_transfer_dest)) ast_set_flag64(peerflags, OPT_GO_ON); } + } else if (!strncasecmp(macro_result, "PLAY_CALLER:", 12) && (prompt1 = ast_strdupa(macro_result + 12))) { + __ast_answer(chan, 0, 0); /* Macro has completed, so peer has answered */ + ast_stream_and_wait(chan, prompt1, ""); + } else if (!strncasecmp(macro_result, "PLAY_BOTH:", 10) && (prompt1 = ast_strdupa(macro_result + 10))) { + char *prompt2 = prompt1; + + prompt1 = strsep(&prompt2, "^"); + if (ast_strlen_zero(prompt2)) { + prompt2 = prompt1; + } + + __ast_answer(chan, 0, 0); + + ast_channel_unlock(peer); + if (play_prompts(chan, peer, prompt1, prompt2)) { + ast_log(LOG_WARNING, "An error occured playing prompts!\n"); + } + ast_channel_lock(peer); } } @@ -2170,6 +2230,11 @@ ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0); } + + if (ast_test_flag64(&opts, OPT_ANNOUNCE_BOTH) && !ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE_BOTH])) { + play_prompts(chan, peer, opt_args[OPT_ARG_ANNOUNCE_BOTH], opt_args[OPT_ARG_ANNOUNCE_BOTH]); + } + res = ast_bridge_call(chan, peer, &config); } Index: funcs/func_channel.c =================================================================== --- funcs/func_channel.c (revision 189271) +++ funcs/func_channel.c (working copy) @@ -130,6 +130,17 @@ R/O 1 if T38 is offered or enabled in this channel, otherwise 0 + + R/O 1 if signalling is secure on this channel, + otherwise 0 + + + R/O 1 if media is secure on this channel, + otherwise 0 + + + R/O udp or tcp or tls + R/O Get QOS information about the RTP stream This option takes two additional arguments: @@ -189,6 +200,14 @@ R/O Get the peer's username. + + R/O 1 if signalling is secure on this channel, + otherwise 0 + + + R/O 1 if media is secure on this channel, + otherwise 0 + Index: include/asterisk/rtp.h =================================================================== --- include/asterisk/rtp.h (revision 189271) +++ include/asterisk/rtp.h (working copy) @@ -126,6 +126,39 @@ double rtt; /*!< Round trip time */ }; +struct ast_srtp; + +struct ast_srtp_policy; + +struct ast_srtp_cb { + int (*no_ctx)(struct ast_rtp *rtp, unsigned long ssrc, void *data); +}; + +struct ast_srtp_res { + int (*create)(struct ast_srtp **srtp, struct ast_rtp *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 (*protect)(struct ast_srtp *srtp, void **buf, int *size); + 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); +}; + /*! RTP callback structure */ typedef int (*ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data); @@ -371,6 +404,25 @@ int ast_rtp_reload(void); /*! reload rtp configuration */ void ast_rtp_new_init(struct ast_rtp *rtp); +int ast_rtp_register_srtp(struct ast_srtp_res *srtp_res, struct ast_srtp_policy_res *policy_res); +int ast_rtp_unregister_srtp(struct ast_srtp_res *srtp_res, struct ast_srtp_policy_res *policy_res); +int ast_srtp_is_registered(void); + +unsigned int ast_rtp_get_ssrc(struct ast_rtp *rtp); +unsigned int ast_rtp_get_themssrc(struct ast_rtp *rtp); +void ast_rtp_set_srtp_cb(struct ast_rtp *rtp, const struct ast_srtp_cb *cb, void *data); +int ast_rtp_add_srtp_policy(struct ast_rtp *rtp, struct ast_srtp_policy *policy); +struct ast_srtp_policy *ast_srtp_policy_alloc(void); +int ast_srtp_policy_set_suite(struct ast_srtp_policy *policy, enum ast_srtp_suite suite); +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); +void ast_srtp_policy_set_srtp_encr_enable(struct ast_srtp_policy *policy, int enable); +void ast_srtp_policy_set_srtcp_encr_enable(struct ast_srtp_policy *policy, int enable); +void ast_srtp_policy_set_srtp_auth_enable(struct ast_srtp_policy *policy, int enable); +void ast_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, unsigned long ssrc, int inbound); + +void ast_srtp_policy_destroy(struct ast_srtp_policy *policy); +int ast_srtp_get_random(unsigned char *key, size_t len); + /*! \brief Set codec preference */ void ast_rtp_codec_setpref(struct ast_rtp *rtp, struct ast_codec_pref *prefs); Index: include/asterisk/aes_internal.h =================================================================== --- include/asterisk/aes_internal.h (revision 189271) +++ include/asterisk/aes_internal.h (working copy) @@ -115,6 +115,8 @@ #ifdef AES_ENCRYPT +#define aes_encrypt ast_aes_encrypt + typedef struct { aes_32t ks[KS_LENGTH]; } aes_encrypt_ctx; @@ -140,6 +142,8 @@ #ifdef AES_DECRYPT +#define aes_decrypt ast_aes_decrypt + typedef struct { aes_32t ks[KS_LENGTH]; } aes_decrypt_ctx; Index: include/asterisk/autoconfig.h.in =================================================================== --- include/asterisk/autoconfig.h.in (revision 189271) +++ include/asterisk/autoconfig.h.in (working copy) @@ -854,6 +854,12 @@ /* Define to indicate the ${SQRT_DESCRIP} library version */ #undef HAVE_SQRT_VERSION +/* Define this to indicate the ${SRTP_DESCRIP} library */ +#undef HAVE_SRTP + +/* Define to indicate the ${SRTP_DESCRIP} library version */ +#undef HAVE_SRTP_VERSION + /* Define this to indicate the ${SS7_DESCRIP} library */ #undef HAVE_SS7 Index: main/rtp.c =================================================================== --- main/rtp.c (revision 189271) +++ main/rtp.c (working copy) @@ -91,6 +91,9 @@ /* Uncomment this to enable more intense native bridging, but note: this is currently buggy */ /* #define P2P_INTENSE */ +struct ast_srtp_res *g_srtp_res; +struct ast_srtp_policy_res *g_policy_res; + /*! * \brief Structure representing a RTP session. * @@ -168,6 +171,7 @@ struct ast_rtcp *rtcp; struct ast_codec_pref pref; struct ast_rtp *bridged; /*!< Who we are Packet bridged to */ + struct ast_srtp *srtp; /*!< Secure RTP data */ enum strict_rtp_state strict_rtp_state; /*!< Current state that strict RTP protection is in */ struct sockaddr_in strict_rtp_address; /*!< Remote address information for strict RTP purposes */ @@ -1109,6 +1113,157 @@ return f; } +/*! \brief Register SRTP module in res_srtp to rtp core */ +int ast_rtp_register_srtp(struct ast_srtp_res *srtp_res, struct ast_srtp_policy_res *policy_res) +{ + if (g_srtp_res || g_policy_res) { + ast_log(LOG_WARNING, "Unable to register SRTP\n"); + return -1; + } + + if (!srtp_res || !policy_res) { + ast_log(LOG_WARNING, "Unable to register SRTP\n"); + return -1; + } + + g_srtp_res = srtp_res; + g_policy_res = policy_res; + + return 0; +} + +int ast_rtp_unregister_srtp(struct ast_srtp_res *srtp_res, struct ast_srtp_policy_res *policy_res) +{ + g_srtp_res = NULL; + g_policy_res = NULL; + + return 0; +} + +/*! \brief Check if there's a SRTP module registred and available */ +int ast_srtp_is_registered(void) +{ + return g_srtp_res && g_policy_res; +} + +unsigned int ast_rtp_get_ssrc(struct ast_rtp *rtp) +{ + return rtp->ssrc; +} + +unsigned int ast_rtp_get_themssrc(struct ast_rtp *rtp) +{ + return rtp->themssrc; +} + +void ast_rtp_set_srtp_cb(struct ast_rtp *rtp, const struct ast_srtp_cb *cb, + void *data) +{ + if (!g_srtp_res || !rtp->srtp) { + return; + } + + g_srtp_res->set_cb(rtp->srtp, cb, data); +} + +void ast_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, unsigned long ssrc, int inbound) +{ + if (!g_policy_res) { + return; + } + + g_policy_res->set_ssrc(policy, ssrc, inbound); +} + +int ast_rtp_add_srtp_policy(struct ast_rtp *rtp, struct ast_srtp_policy *policy) +{ + int res; + + if (!g_srtp_res) { + return -1; + } + + if (!rtp->srtp) { + res = g_srtp_res->create(&rtp->srtp, rtp, policy); + } else { + res = g_srtp_res->add_stream(rtp->srtp, policy); + } + + return res; +} + +struct ast_srtp_policy *ast_srtp_policy_alloc() +{ + if (!g_policy_res) { + return NULL; + } + + return g_policy_res->alloc(); +} + +void ast_srtp_policy_destroy(struct ast_srtp_policy *policy) +{ + if (!g_policy_res) { + return; + } + + g_policy_res->destroy(policy); +} + +int ast_srtp_policy_set_suite(struct ast_srtp_policy *policy, enum ast_srtp_suite suite) +{ + if (!g_policy_res) { + return -1; + } + + return g_policy_res->set_suite(policy, suite); +} + +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) +{ + if (!g_policy_res) { + return -1; + } + + return g_policy_res->set_master_key(policy, key, key_len, salt, salt_len); +} + +int ast_srtp_get_random(unsigned char *key, size_t len) +{ + if (!g_srtp_res) { + return -1; + } + + return g_srtp_res->get_random(key, len); +} + +static int rtp_recvfrom(struct ast_rtp *rtp, void *buf, size_t size, int flags, struct sockaddr *sa, socklen_t *salen) +{ + int len; + + if ((len = recvfrom(rtp->s, buf, size, flags, sa, salen)) < 0) { + return len; + } + + if (g_srtp_res && rtp->srtp && g_srtp_res->unprotect(rtp->srtp, buf, &len) < 0) { + return -1; + } + + return len; +} + +static int rtp_sendto(struct ast_rtp *rtp, void *buf, size_t size, int flags, struct sockaddr *sa, socklen_t salen) +{ + int len = size; + void *temp = buf; + + if (g_srtp_res && rtp->srtp && g_srtp_res->protect(rtp->srtp, &temp, &len) < 0) { + return -1; + } + + return sendto(rtp->s, temp, len, flags, sa, salen); +} + static int rtpread(int *id, int fd, short events, void *cbdata) { struct ast_rtp *rtp = cbdata; @@ -1500,7 +1655,7 @@ rtpheader[0] = htonl(reconstruct); /* Send the packet back out */ - res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&bridged->them, sizeof(bridged->them)); + res = rtp_sendto(bridged, (void *)rtpheader, len + hdrlen, 0, (struct sockaddr *)&bridged->them, sizeof(bridged->them)); if (res < 0) { if (!bridged->nat || (bridged->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(bridged->them.sin_addr), ntohs(bridged->them.sin_port), strerror(errno)); @@ -1543,7 +1698,7 @@ len = sizeof(sock_in); /* Cache where the header will go */ - res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, + res = rtp_recvfrom(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sock_in, &len); /* If strict RTP protection is enabled see if we need to learn this address or if the packet should be dropped */ @@ -3114,7 +3269,7 @@ 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 *) &rtp->them, sizeof(rtp->them)); + res = rtp_sendto(rtp, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); if (res < 0) ast_log(LOG_ERROR, "RTP Transmission error to %s:%u: %s\n", ast_inet_ntoa(rtp->them.sin_addr), @@ -3158,7 +3313,7 @@ rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); /* Transmit */ - res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); + res = rtp_sendto(rtp, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); if (res < 0) ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", ast_inet_ntoa(rtp->them.sin_addr), @@ -3214,7 +3369,7 @@ /* Send 3 termination packets */ for (i = 0; i < 3; i++) { rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); - res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); + res = rtp_sendto(rtp, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); rtp->seqno++; if (res < 0) ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", @@ -3531,7 +3686,7 @@ rtpheader[2] = htonl(rtp->ssrc); data[12] = level; if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { - res = sendto(rtp->s, (void *)rtpheader, hdrlen + 1, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); + res = rtp_sendto(rtp, (void *)rtpheader, hdrlen + 1, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); if (res <0) ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s:%d: %s\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); if (rtp_debug_test_addr(&rtp->them)) @@ -3627,7 +3782,7 @@ put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc)); if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { - res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); + res = rtp_sendto(rtp, (void *)rtpheader, f->datalen + hdrlen, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); if (res < 0) { if (!rtp->nat || (rtp->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(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); @@ -4399,6 +4554,13 @@ return AST_BRIDGE_FAILED_NOWARN; } + if (p0->srtp || p1->srtp) { + /* We can't do native briding if someone is using SRTP! */ + ast_channel_unlock(c0); + ast_channel_unlock(c1); + return AST_BRIDGE_FAILED_NOWARN; + } + /* If we need to feed DTMF frames into the core then only do a partial native bridge */ if (ast_test_flag(p0, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) { ast_set_flag(p0, FLAG_P2P_NEED_DTMF); Index: makeopts.in =================================================================== --- makeopts.in (revision 189271) +++ makeopts.in (working copy) @@ -199,6 +199,9 @@ SPEEXDSP_INCLUDE=@SPEEXDSP_INCLUDE@ SPEEXDSP_LIB=@SPEEXDSP_LIB@ +SRTP_LIB=@SRTP_LIB@ +SRTP_INCLUDE=@SRTP_INCLUDE@ + SQLITE_INCLUDE=@SQLITE_INCLUDE@ SQLITE_LIB=@SQLITE_LIB@ Index: res/res_srtp.c =================================================================== --- res/res_srtp.c (revision 0) +++ res/res_srtp.c (revision 0) @@ -0,0 +1,397 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2005, Mikael Magnusson + * + * Mikael Magnusson + * + * 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 + */ + +/*** MODULEINFO + srtp +***/ + +/* 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 + +#include "asterisk/lock.h" +#include "asterisk/module.h" +#include "asterisk/options.h" +#include "asterisk/rtp.h" + +struct ast_srtp { + struct ast_rtp *rtp; + srtp_t session; + const struct ast_srtp_cb *cb; + void *data; + unsigned char buf[8192 + AST_FRIENDLY_OFFSET]; + unsigned int has_stream:1; +}; + +struct ast_srtp_policy { + srtp_policy_t sp; +}; + +static int g_initialized = 0; + +/* SRTP functions */ +static int res_srtp_create(struct ast_srtp **srtp, struct ast_rtp *rtp, struct ast_srtp_policy *policy); +static void res_srtp_destroy(struct ast_srtp *srtp); +static int res_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *policy); + +static int res_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len); +static int res_srtp_protect(struct ast_srtp *srtp, void **buf, int *len); +static int res_srtp_get_random(unsigned char *key, size_t len); +static void res_srtp_set_cb(struct ast_srtp *srtp, const struct ast_srtp_cb *cb, void *data); + +/* Policy functions */ +static struct ast_srtp_policy *res_srtp_policy_alloc(void); +static void res_srtp_policy_destroy(struct ast_srtp_policy *policy); +static int res_srtp_policy_set_suite(struct ast_srtp_policy *policy, enum ast_srtp_suite suite); +static int res_srtp_policy_set_master_key(struct ast_srtp_policy *policy, const unsigned char *key, size_t key_len, const unsigned char *salt, size_t salt_len); +static void res_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, unsigned long ssrc, int inbound); + +static struct ast_srtp_res srtp_res = { + .create = res_srtp_create, + .destroy = res_srtp_destroy, + .add_stream = res_srtp_add_stream, + .set_cb = res_srtp_set_cb, + .unprotect = res_srtp_unprotect, + .protect = res_srtp_protect, + .get_random = res_srtp_get_random +}; + +static struct ast_srtp_policy_res policy_res = { + .alloc = res_srtp_policy_alloc, + .destroy = res_srtp_policy_destroy, + .set_suite = res_srtp_policy_set_suite, + .set_master_key = res_srtp_policy_set_master_key, + .set_ssrc = res_srtp_policy_set_ssrc +}; + +static const char *srtp_errstr(int err) +{ + switch(err) { + case err_status_ok: + return "nothing to report"; + case err_status_fail: + return "unspecified failure"; + case err_status_bad_param: + return "unsupported parameter"; + case err_status_alloc_fail: + return "couldn't allocate memory"; + case err_status_dealloc_fail: + return "couldn't deallocate properly"; + case err_status_init_fail: + return "couldn't initialize"; + case err_status_terminus: + return "can't process as much data as requested"; + case err_status_auth_fail: + return "authentication failure"; + case err_status_cipher_fail: + return "cipher failure"; + case err_status_replay_fail: + return "replay check failed (bad index)"; + case err_status_replay_old: + return "replay check failed (index too old)"; + case err_status_algo_fail: + return "algorithm failed test routine"; + case err_status_no_such_op: + return "unsupported operation"; + case err_status_no_ctx: + return "no appropriate context found"; + case err_status_cant_check: + return "unable to perform desired validation"; + case err_status_key_expired: + return "can't use key any more"; + default: + return "unknown"; + } +} + +static struct ast_srtp *res_srtp_new(void) +{ + struct ast_srtp *srtp; + + 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 res_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, + unsigned long ssrc, int inbound) +{ + if (ssrc) { + policy->sp.ssrc.type = ssrc_specific; + policy->sp.ssrc.value = ssrc; + } else { + policy->sp.ssrc.type = inbound ? ssrc_any_inbound : ssrc_any_outbound; + } +} + +static struct ast_srtp_policy *res_srtp_policy_alloc() +{ + struct ast_srtp_policy *tmp; + + if (!(tmp = ast_calloc(1, sizeof(*tmp)))) { + ast_log(LOG_ERROR, "Unable to allocate memory for srtp_policy\n"); + } + + return tmp; +} + +static void res_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 res_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 res_srtp_policy_set_master_key(struct ast_srtp_policy *policy, const unsigned char *key, size_t key_len, const unsigned char *salt, size_t salt_len) +{ + size_t size = key_len + salt_len; + unsigned char *master_key; + + 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 res_srtp_get_random(unsigned char *key, size_t len) +{ + return crypto_get_random(key, len) != err_status_ok ? -1: 0; +} + +static void res_srtp_set_cb(struct ast_srtp *srtp, const struct ast_srtp_cb *cb, void *data) +{ + if (!srtp) { + return; + } + + srtp->cb = cb; + srtp->data = data; +} + +/* Vtable functions */ +static int res_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len) +{ + int res = 0; + int i; + + for (i = 0; i < 2; i++) { + res = srtp_unprotect(srtp->session, buf, len); + if (res != err_status_no_ctx) { + break; + } + + if (srtp->cb && srtp->cb->no_ctx) { + if (srtp->cb->no_ctx(srtp->rtp, ast_rtp_get_themssrc(srtp->rtp), srtp->data) < 0) { + break; + } + } else { + break; + } + } + + if (res != err_status_ok) { + ast_debug(1, "SRTP unprotect: %s\n", srtp_errstr(res)); + return -1; + } + + return *len; +} + +static int res_srtp_protect(struct ast_srtp *srtp, void **buf, int *len) +{ + int res; + + if ((*len + SRTP_MAX_TRAILER_LEN) > sizeof(srtp->buf)) { + return -1; + } + + memcpy(srtp->buf, *buf, *len); + + if ((res = srtp_protect(srtp->session, srtp->buf, len)) != err_status_ok) { + ast_debug(1, "SRTP protect: %s\n", srtp_errstr(res)); + return -1; + } + + *buf = srtp->buf; + return *len; +} + +static int res_srtp_create(struct ast_srtp **srtp, struct ast_rtp *rtp, struct ast_srtp_policy *policy) +{ + 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 res_srtp_destroy(struct ast_srtp *srtp) +{ + if (srtp->session) { + srtp_dealloc(srtp->session); + } + + ast_free(srtp); +} + +static int res_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_register_srtp(&srtp_res, &policy_res); +} + +/* + * Exported functions + */ + +static int load_module(void) +{ + return res_srtp_init(); +} + +static int unload_module(void) +{ + return ast_rtp_unregister_srtp(&srtp_res, &policy_res); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Secure RTP (SRTP)", + .load = load_module, + .unload = unload_module, +);