summaryrefslogtreecommitdiff
path: root/channels/chan_sip.c
diff options
context:
space:
mode:
Diffstat (limited to 'channels/chan_sip.c')
-rw-r--r--channels/chan_sip.c247
1 files changed, 232 insertions, 15 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index a1bfcfe7e..b79f35a1a 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -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)
{