mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-29 18:19:30 +00:00
Add DTLS-SRTP support to chan_pjsip
This patch introduces DTLS-SRTP support to chan_pjsip and the options necessary to configure it including an option to allow choosing between 32 and 80 byte SRTP tag lengths. During the implementation and testing of this patch, three other bugs were found and their fixes are included with this patch. The two in chan_sip were a segfault relating to DTLS setup and mistaken call rejection. The third bug fix prevents chan_pjsip from attempting to perform bridge optimization between two endpoints if either of them is running any form of SRTP. Review: https://reviewboard.asterisk.org/r/2683/ (closes issue ASTERISK-21419) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395121 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -384,6 +384,10 @@ static enum ast_rtp_glue_result gulp_get_rtp_peer(struct ast_channel *chan, stru
|
|||||||
ao2_ref(*instance, +1);
|
ao2_ref(*instance, +1);
|
||||||
|
|
||||||
ast_assert(endpoint != NULL);
|
ast_assert(endpoint != NULL);
|
||||||
|
if (endpoint->media_encryption != AST_SIP_MEDIA_ENCRYPT_NONE) {
|
||||||
|
return AST_RTP_GLUE_RESULT_FORBID;
|
||||||
|
}
|
||||||
|
|
||||||
if (endpoint->direct_media) {
|
if (endpoint->direct_media) {
|
||||||
return AST_RTP_GLUE_RESULT_REMOTE;
|
return AST_RTP_GLUE_RESULT_REMOTE;
|
||||||
}
|
}
|
||||||
@@ -396,14 +400,22 @@ static enum ast_rtp_glue_result gulp_get_vrtp_peer(struct ast_channel *chan, str
|
|||||||
{
|
{
|
||||||
struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
|
struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
|
||||||
struct gulp_pvt *pvt = channel->pvt;
|
struct gulp_pvt *pvt = channel->pvt;
|
||||||
|
struct ast_sip_endpoint *endpoint;
|
||||||
|
|
||||||
if (!pvt || !channel->session || !pvt->media[SIP_MEDIA_VIDEO]->rtp) {
|
if (!pvt || !channel->session || !pvt->media[SIP_MEDIA_VIDEO]->rtp) {
|
||||||
return AST_RTP_GLUE_RESULT_FORBID;
|
return AST_RTP_GLUE_RESULT_FORBID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endpoint = channel->session->endpoint;
|
||||||
|
|
||||||
*instance = pvt->media[SIP_MEDIA_VIDEO]->rtp;
|
*instance = pvt->media[SIP_MEDIA_VIDEO]->rtp;
|
||||||
ao2_ref(*instance, +1);
|
ao2_ref(*instance, +1);
|
||||||
|
|
||||||
|
ast_assert(endpoint != NULL);
|
||||||
|
if (endpoint->media_encryption != AST_SIP_MEDIA_ENCRYPT_NONE) {
|
||||||
|
return AST_RTP_GLUE_RESULT_FORBID;
|
||||||
|
}
|
||||||
|
|
||||||
return AST_RTP_GLUE_RESULT_LOCAL;
|
return AST_RTP_GLUE_RESULT_LOCAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10193,6 +10193,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
|
|||||||
} else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
|
} else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
|
||||||
secure_audio = 1;
|
secure_audio = 1;
|
||||||
|
|
||||||
|
processed_crypto = 1;
|
||||||
if (p->srtp) {
|
if (p->srtp) {
|
||||||
ast_set_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK);
|
ast_set_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK);
|
||||||
}
|
}
|
||||||
@@ -10275,6 +10276,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
|
|||||||
} else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
|
} else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
|
||||||
secure_video = 1;
|
secure_video = 1;
|
||||||
|
|
||||||
|
processed_crypto = 1;
|
||||||
if (p->vsrtp || (p->vsrtp = ast_sdp_srtp_alloc())) {
|
if (p->vsrtp || (p->vsrtp = ast_sdp_srtp_alloc())) {
|
||||||
ast_set_flag(p->vsrtp, AST_SRTP_CRYPTO_OFFER_OK);
|
ast_set_flag(p->vsrtp, AST_SRTP_CRYPTO_OFFER_OK);
|
||||||
}
|
}
|
||||||
@@ -13036,13 +13038,17 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, int needtext
|
|||||||
static char *crypto_get_attrib(struct ast_sdp_srtp *srtp, int dtls_enabled, int default_taglen_32)
|
static char *crypto_get_attrib(struct ast_sdp_srtp *srtp, int dtls_enabled, int default_taglen_32)
|
||||||
{
|
{
|
||||||
char *a_crypto;
|
char *a_crypto;
|
||||||
char *orig_crypto;
|
const char *orig_crypto;
|
||||||
|
|
||||||
if (!srtp) {
|
if (!srtp || dtls_enabled) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
orig_crypto = ast_sdp_srtp_get_attrib(srtp, dtls_enabled, default_taglen_32);
|
||||||
|
if (ast_strlen_zero(orig_crypto)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
orig_crypto = ast_strdupa(ast_sdp_srtp_get_attrib(srtp, dtls_enabled, default_taglen_32));
|
|
||||||
if (ast_asprintf(&a_crypto, "a=crypto:%s\r\n", orig_crypto) == -1) {
|
if (ast_asprintf(&a_crypto, "a=crypto:%s\r\n", orig_crypto) == -1) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,8 @@
|
|||||||
#include "asterisk/endpoints.h"
|
#include "asterisk/endpoints.h"
|
||||||
/* Needed for pj_sockaddr */
|
/* Needed for pj_sockaddr */
|
||||||
#include <pjlib.h>
|
#include <pjlib.h>
|
||||||
|
/* Needed for ast_rtp_dtls_cfg struct */
|
||||||
|
#include "asterisk/rtp_engine.h"
|
||||||
|
|
||||||
/* Forward declarations of PJSIP stuff */
|
/* Forward declarations of PJSIP stuff */
|
||||||
struct pjsip_rx_data;
|
struct pjsip_rx_data;
|
||||||
@@ -446,6 +448,10 @@ struct ast_sip_endpoint {
|
|||||||
unsigned int allowsubscribe;
|
unsigned int allowsubscribe;
|
||||||
/*! The minimum allowed expiration for subscriptions from endpoint */
|
/*! The minimum allowed expiration for subscriptions from endpoint */
|
||||||
unsigned int subminexpiry;
|
unsigned int subminexpiry;
|
||||||
|
/*! \brief DTLS-SRTP configuration information */
|
||||||
|
struct ast_rtp_dtls_cfg dtls_cfg;
|
||||||
|
/*! Should SRTP use a 32 byte tag instead of an 80 byte tag? */
|
||||||
|
unsigned int srtp_tag_32;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
#include "asterisk/channel.h"
|
#include "asterisk/channel.h"
|
||||||
/* Needed for ast_sockaddr struct */
|
/* Needed for ast_sockaddr struct */
|
||||||
#include "asterisk/netsock.h"
|
#include "asterisk/netsock.h"
|
||||||
/* Neeed for ast_sdp_srtp struct */
|
/* Needed for ast_sdp_srtp struct */
|
||||||
#include "asterisk/sdp_srtp.h"
|
#include "asterisk/sdp_srtp.h"
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
@@ -42,7 +42,6 @@ struct pjsip_rx_data;
|
|||||||
struct ast_party_id;
|
struct ast_party_id;
|
||||||
struct pjmedia_sdp_media;
|
struct pjmedia_sdp_media;
|
||||||
struct pjmedia_sdp_session;
|
struct pjmedia_sdp_session;
|
||||||
struct ast_rtp_instance;
|
|
||||||
struct ast_dsp;
|
struct ast_dsp;
|
||||||
|
|
||||||
struct ast_sip_session_sdp_handler;
|
struct ast_sip_session_sdp_handler;
|
||||||
|
@@ -336,6 +336,9 @@
|
|||||||
transport should be used in conjunction with this option to prevent
|
transport should be used in conjunction with this option to prevent
|
||||||
exposure of media encryption keys.
|
exposure of media encryption keys.
|
||||||
</para></enum>
|
</para></enum>
|
||||||
|
<enum name="dtls"><para>
|
||||||
|
res_sip will offer DTLS-SRTP setup.
|
||||||
|
</para></enum>
|
||||||
</enumlist>
|
</enumlist>
|
||||||
</description>
|
</description>
|
||||||
</configOption>
|
</configOption>
|
||||||
@@ -476,6 +479,87 @@
|
|||||||
<configOption name="fromdomain">
|
<configOption name="fromdomain">
|
||||||
<synopsis>Domain to user in From header for requests to this endpoint.</synopsis>
|
<synopsis>Domain to user in From header for requests to this endpoint.</synopsis>
|
||||||
</configOption>
|
</configOption>
|
||||||
|
<configOption name="dtlsverify">
|
||||||
|
<synopsis>Verify that the provided peer certificate is valid</synopsis>
|
||||||
|
<description><para>
|
||||||
|
This option only applies if <replaceable>media_encryption</replaceable> is
|
||||||
|
set to <literal>dtls</literal>.
|
||||||
|
</para></description>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="dtlsrekey">
|
||||||
|
<synopsis>Interval at which to renegotiate the TLS session and rekey the SRTP session</synopsis>
|
||||||
|
<description><para>
|
||||||
|
This option only applies if <replaceable>media_encryption</replaceable> is
|
||||||
|
set to <literal>dtls</literal>.
|
||||||
|
</para><para>
|
||||||
|
If this is not set or the value provided is 0 rekeying will be disabled.
|
||||||
|
</para></description>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="dtlscertfile">
|
||||||
|
<synopsis>Path to certificate file to present to peer</synopsis>
|
||||||
|
<description><para>
|
||||||
|
This option only applies if <replaceable>media_encryption</replaceable> is
|
||||||
|
set to <literal>dtls</literal>.
|
||||||
|
</para></description>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="dtlsprivatekey">
|
||||||
|
<synopsis>Path to private key for certificate file</synopsis>
|
||||||
|
<description><para>
|
||||||
|
This option only applies if <replaceable>media_encryption</replaceable> is
|
||||||
|
set to <literal>dtls</literal>.
|
||||||
|
</para></description>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="dtlscipher">
|
||||||
|
<synopsis>Cipher to use for DTLS negotiation</synopsis>
|
||||||
|
<description><para>
|
||||||
|
This option only applies if <replaceable>media_encryption</replaceable> is
|
||||||
|
set to <literal>dtls</literal>.
|
||||||
|
</para><para>
|
||||||
|
Many options for acceptable ciphers. See link for more:
|
||||||
|
http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
|
||||||
|
</para></description>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="dtlscafile">
|
||||||
|
<synopsis>Path to certificate authority certificate</synopsis>
|
||||||
|
<description><para>
|
||||||
|
This option only applies if <replaceable>media_encryption</replaceable> is
|
||||||
|
set to <literal>dtls</literal>.
|
||||||
|
</para></description>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="dtlscapath">
|
||||||
|
<synopsis>Path to a directory containing certificate authority certificates</synopsis>
|
||||||
|
<description><para>
|
||||||
|
This option only applies if <replaceable>media_encryption</replaceable> is
|
||||||
|
set to <literal>dtls</literal>.
|
||||||
|
</para></description>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="dtlssetup">
|
||||||
|
<synopsis>Whether we are willing to accept connections, connect to the other party, or both.</synopsis>
|
||||||
|
<description>
|
||||||
|
<para>
|
||||||
|
This option only applies if <replaceable>media_encryption</replaceable> is
|
||||||
|
set to <literal>dtls</literal>.
|
||||||
|
</para>
|
||||||
|
<enumlist>
|
||||||
|
<enum name="active"><para>
|
||||||
|
res_sip will make a connection to the peer.
|
||||||
|
</para></enum>
|
||||||
|
<enum name="passive"><para>
|
||||||
|
res_sip will accept connections from the peer.
|
||||||
|
</para></enum>
|
||||||
|
<enum name="actpass"><para>
|
||||||
|
res_sip will offer and accept connections from the peer.
|
||||||
|
</para></enum>
|
||||||
|
</enumlist>
|
||||||
|
</description>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="srtp_tag_32">
|
||||||
|
<synopsis>Determines whether 32 byte tags should be used instead of 80 byte tags.</synopsis>
|
||||||
|
<description><para>
|
||||||
|
This option only applies if <replaceable>media_encryption</replaceable> is
|
||||||
|
set to <literal>sdes</literal> or <literal>dtls</literal>.
|
||||||
|
</para></description>
|
||||||
|
</configOption>
|
||||||
</configObject>
|
</configObject>
|
||||||
<configObject name="auth">
|
<configObject name="auth">
|
||||||
<synopsis>Authentication type</synopsis>
|
<synopsis>Authentication type</synopsis>
|
||||||
|
@@ -467,8 +467,9 @@ static int media_encryption_handler(const struct aco_option *opt, struct ast_var
|
|||||||
endpoint->media_encryption = AST_SIP_MEDIA_ENCRYPT_NONE;
|
endpoint->media_encryption = AST_SIP_MEDIA_ENCRYPT_NONE;
|
||||||
} else if (!strcasecmp("sdes", var->value)) {
|
} else if (!strcasecmp("sdes", var->value)) {
|
||||||
endpoint->media_encryption = AST_SIP_MEDIA_ENCRYPT_SDES;
|
endpoint->media_encryption = AST_SIP_MEDIA_ENCRYPT_SDES;
|
||||||
/*} else if (!strcasecmp("dtls", var->value)) {
|
} else if (!strcasecmp("dtls", var->value)) {
|
||||||
endpoint->media_encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;*/
|
endpoint->media_encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;
|
||||||
|
ast_rtp_dtls_cfg_parse(&endpoint->dtls_cfg, "dtlsenable", "yes");
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -518,6 +519,14 @@ static int named_groups_handler(const struct aco_option *opt,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dtls_handler(const struct aco_option *opt,
|
||||||
|
struct ast_variable *var, void *obj)
|
||||||
|
{
|
||||||
|
struct ast_sip_endpoint *endpoint = obj;
|
||||||
|
|
||||||
|
return ast_rtp_dtls_cfg_parse(&endpoint->dtls_cfg, var->name, var->value);
|
||||||
|
}
|
||||||
|
|
||||||
static void *sip_nat_hook_alloc(const char *name)
|
static void *sip_nat_hook_alloc(const char *name)
|
||||||
{
|
{
|
||||||
return ast_sorcery_generic_alloc(sizeof(struct ast_sip_nat_hook), NULL);
|
return ast_sorcery_generic_alloc(sizeof(struct ast_sip_nat_hook), NULL);
|
||||||
@@ -676,6 +685,15 @@ int ast_res_sip_initialize_configuration(void)
|
|||||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fromuser", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromuser));
|
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fromuser", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromuser));
|
||||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fromdomain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromdomain));
|
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fromdomain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromdomain));
|
||||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwifromuser", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mwi_from));
|
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwifromuser", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mwi_from));
|
||||||
|
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlsverify", "", dtls_handler, NULL, 0, 0);
|
||||||
|
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlsrekey", "", dtls_handler, NULL, 0, 0);
|
||||||
|
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlscertfile", "", dtls_handler, NULL, 0, 0);
|
||||||
|
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlsprivatekey", "", dtls_handler, NULL, 0, 0);
|
||||||
|
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlscipher", "", dtls_handler, NULL, 0, 0);
|
||||||
|
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlscafile", "", dtls_handler, NULL, 0, 0);
|
||||||
|
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlscapath", "", dtls_handler, NULL, 0, 0);
|
||||||
|
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlssetup", "", dtls_handler, NULL, 0, 0);
|
||||||
|
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "srtp_tag_32", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, srtp_tag_32));
|
||||||
|
|
||||||
if (ast_sip_initialize_sorcery_transport(sip_sorcery)) {
|
if (ast_sip_initialize_sorcery_transport(sip_sorcery)) {
|
||||||
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
|
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
|
||||||
|
@@ -525,10 +525,6 @@ static enum ast_sip_session_media_encryption check_endpoint_media_transport(
|
|||||||
}
|
}
|
||||||
|
|
||||||
incoming_encryption = get_media_encryption_type(stream->desc.transport);
|
incoming_encryption = get_media_encryption_type(stream->desc.transport);
|
||||||
if (incoming_encryption == AST_SIP_MEDIA_ENCRYPT_DTLS) {
|
|
||||||
/* DTLS not yet supported */
|
|
||||||
return AST_SIP_MEDIA_TRANSPORT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (incoming_encryption == endpoint->media_encryption) {
|
if (incoming_encryption == endpoint->media_encryption) {
|
||||||
return incoming_encryption;
|
return incoming_encryption;
|
||||||
@@ -537,6 +533,108 @@ static enum ast_sip_session_media_encryption check_endpoint_media_transport(
|
|||||||
return AST_SIP_MEDIA_TRANSPORT_INVALID;
|
return AST_SIP_MEDIA_TRANSPORT_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int setup_srtp(struct ast_sip_session_media *session_media)
|
||||||
|
{
|
||||||
|
if (!session_media->srtp) {
|
||||||
|
session_media->srtp = ast_sdp_srtp_alloc();
|
||||||
|
if (!session_media->srtp) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session_media->srtp->crypto) {
|
||||||
|
session_media->srtp->crypto = ast_sdp_crypto_alloc();
|
||||||
|
if (!session_media->srtp->crypto) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setup_dtls_srtp(struct ast_sip_session *session,
|
||||||
|
struct ast_sip_session_media *session_media)
|
||||||
|
{
|
||||||
|
struct ast_rtp_engine_dtls *dtls;
|
||||||
|
|
||||||
|
if (!session->endpoint->dtls_cfg.enabled || !session_media->rtp) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dtls = ast_rtp_instance_get_dtls(session_media->rtp);
|
||||||
|
if (!dtls) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
session->endpoint->dtls_cfg.suite = ((session->endpoint->srtp_tag_32) ? AST_AES_CM_128_HMAC_SHA1_32 : AST_AES_CM_128_HMAC_SHA1_80);
|
||||||
|
if (dtls->set_configuration(session_media->rtp, &session->endpoint->dtls_cfg)) {
|
||||||
|
ast_log(LOG_ERROR, "Attempted to set an invalid DTLS-SRTP configuration on RTP instance '%p'\n",
|
||||||
|
session_media->rtp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setup_srtp(session_media)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_dtls_attrib(struct ast_sip_session_media *session_media,
|
||||||
|
const struct pjmedia_sdp_media *stream)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct ast_rtp_engine_dtls *dtls = ast_rtp_instance_get_dtls(session_media->rtp);
|
||||||
|
|
||||||
|
for (i = 0; i < stream->attr_count; i++) {
|
||||||
|
pjmedia_sdp_attr *attr = stream->attr[i];
|
||||||
|
pj_str_t *value;
|
||||||
|
|
||||||
|
if (!attr->value.ptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = pj_strtrim(&attr->value);
|
||||||
|
|
||||||
|
if (!pj_strcmp2(&attr->name, "setup")) {
|
||||||
|
if (!pj_stricmp2(value, "active")) {
|
||||||
|
dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_ACTIVE);
|
||||||
|
} else if (!pj_stricmp2(value, "passive")) {
|
||||||
|
dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_PASSIVE);
|
||||||
|
} else if (!pj_stricmp2(value, "actpass")) {
|
||||||
|
dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_ACTPASS);
|
||||||
|
} else if (!pj_stricmp2(value, "holdconn")) {
|
||||||
|
dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_HOLDCONN);
|
||||||
|
} else {
|
||||||
|
ast_log(LOG_WARNING, "Unsupported setup attribute value '%*s'\n", (int)value->slen, value->ptr);
|
||||||
|
}
|
||||||
|
} else if (!pj_strcmp2(&attr->name, "connection")) {
|
||||||
|
if (!pj_stricmp2(value, "new")) {
|
||||||
|
dtls->reset(session_media->rtp);
|
||||||
|
} else if (!pj_stricmp2(value, "existing")) {
|
||||||
|
/* Do nothing */
|
||||||
|
} else {
|
||||||
|
ast_log(LOG_WARNING, "Unsupported connection attribute value '%*s'\n", (int)value->slen, value->ptr);
|
||||||
|
}
|
||||||
|
} else if (!pj_strcmp2(&attr->name, "fingerprint")) {
|
||||||
|
char hash_value[256], hash[6];
|
||||||
|
char fingerprint_text[value->slen + 1];
|
||||||
|
ast_copy_pj_str(fingerprint_text, value, sizeof(fingerprint_text));
|
||||||
|
|
||||||
|
if (sscanf(fingerprint_text, "%5s %255s", hash, hash_value) == 2) {
|
||||||
|
if (!strcasecmp(hash, "sha-1")) {
|
||||||
|
dtls->set_fingerprint(session_media->rtp, AST_RTP_DTLS_HASH_SHA1, hash_value);
|
||||||
|
} else {
|
||||||
|
ast_log(LOG_WARNING, "Unsupported fingerprint hash type '%s'\n",
|
||||||
|
hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast_set_flag(session_media->srtp, AST_SRTP_CRYPTO_OFFER_OK);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int setup_sdes_srtp(struct ast_sip_session_media *session_media,
|
static int setup_sdes_srtp(struct ast_sip_session_media *session_media,
|
||||||
const struct pjmedia_sdp_media *stream)
|
const struct pjmedia_sdp_media *stream)
|
||||||
{
|
{
|
||||||
@@ -557,18 +655,8 @@ static int setup_sdes_srtp(struct ast_sip_session_media *session_media,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!session_media->srtp) {
|
if (setup_srtp(session_media)) {
|
||||||
session_media->srtp = ast_sdp_srtp_alloc();
|
return -1;
|
||||||
if (!session_media->srtp) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!session_media->srtp->crypto) {
|
|
||||||
session_media->srtp->crypto = ast_sdp_crypto_alloc();
|
|
||||||
if (!session_media->srtp->crypto) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ast_sdp_crypto_process(session_media->rtp, session_media->srtp, crypto_str)) {
|
if (!ast_sdp_crypto_process(session_media->rtp, session_media->srtp, crypto_str)) {
|
||||||
@@ -583,6 +671,32 @@ static int setup_sdes_srtp(struct ast_sip_session_media *session_media,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int setup_media_encryption(struct ast_sip_session *session,
|
||||||
|
struct ast_sip_session_media *session_media,
|
||||||
|
const struct pjmedia_sdp_media *stream)
|
||||||
|
{
|
||||||
|
switch (session->endpoint->media_encryption) {
|
||||||
|
case AST_SIP_MEDIA_ENCRYPT_SDES:
|
||||||
|
if (setup_sdes_srtp(session_media, stream)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AST_SIP_MEDIA_ENCRYPT_DTLS:
|
||||||
|
if (setup_dtls_srtp(session, session_media)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (parse_dtls_attrib(session_media, stream)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AST_SIP_MEDIA_TRANSPORT_INVALID:
|
||||||
|
case AST_SIP_MEDIA_ENCRYPT_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! \brief Function which negotiates an incoming media stream */
|
/*! \brief Function which negotiates an incoming media stream */
|
||||||
static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
|
static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
|
||||||
const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
|
const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
|
||||||
@@ -590,7 +704,6 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
|
|||||||
char host[NI_MAXHOST];
|
char host[NI_MAXHOST];
|
||||||
RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
|
RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
|
||||||
enum ast_format_type media_type = stream_to_media_type(session_media->stream_type);
|
enum ast_format_type media_type = stream_to_media_type(session_media->stream_type);
|
||||||
enum ast_sip_session_media_encryption incoming_encryption;
|
|
||||||
|
|
||||||
/* If no type formats have been configured reject this stream */
|
/* If no type formats have been configured reject this stream */
|
||||||
if (!ast_format_cap_has_type(session->endpoint->codecs, media_type)) {
|
if (!ast_format_cap_has_type(session->endpoint->codecs, media_type)) {
|
||||||
@@ -598,8 +711,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure incoming transport is compatible with the endpoint's configuration */
|
/* Ensure incoming transport is compatible with the endpoint's configuration */
|
||||||
incoming_encryption = check_endpoint_media_transport(session->endpoint, stream);
|
if (check_endpoint_media_transport(session->endpoint, stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) {
|
||||||
if (incoming_encryption == AST_SIP_MEDIA_TRANSPORT_INVALID) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,8 +728,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (incoming_encryption == AST_SIP_MEDIA_ENCRYPT_SDES
|
if (setup_media_encryption(session, session_media, stream)) {
|
||||||
&& setup_sdes_srtp(session_media, stream)) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,24 +742,95 @@ static int add_crypto_to_stream(struct ast_sip_session *session,
|
|||||||
pj_str_t stmp;
|
pj_str_t stmp;
|
||||||
pjmedia_sdp_attr *attr;
|
pjmedia_sdp_attr *attr;
|
||||||
const char *crypto_attribute;
|
const char *crypto_attribute;
|
||||||
|
struct ast_rtp_engine_dtls *dtls;
|
||||||
|
static const pj_str_t STR_NEW = { "new", 3 };
|
||||||
|
static const pj_str_t STR_EXISTING = { "existing", 8 };
|
||||||
|
static const pj_str_t STR_ACTIVE = { "active", 6 };
|
||||||
|
static const pj_str_t STR_PASSIVE = { "passive", 7 };
|
||||||
|
static const pj_str_t STR_ACTPASS = { "actpass", 7 };
|
||||||
|
static const pj_str_t STR_HOLDCONN = { "holdconn", 8 };
|
||||||
|
|
||||||
if (!session_media->srtp && session->endpoint->media_encryption != AST_SIP_MEDIA_ENCRYPT_NONE) {
|
switch (session->endpoint->media_encryption) {
|
||||||
session_media->srtp = ast_sdp_srtp_alloc();
|
case AST_SIP_MEDIA_ENCRYPT_NONE:
|
||||||
|
case AST_SIP_MEDIA_TRANSPORT_INVALID:
|
||||||
|
break;
|
||||||
|
case AST_SIP_MEDIA_ENCRYPT_SDES:
|
||||||
if (!session_media->srtp) {
|
if (!session_media->srtp) {
|
||||||
|
session_media->srtp = ast_sdp_srtp_alloc();
|
||||||
|
if (!session_media->srtp) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crypto_attribute = ast_sdp_srtp_get_attrib(session_media->srtp,
|
||||||
|
0 /* DTLS running? No */,
|
||||||
|
session->endpoint->srtp_tag_32 /* 32 byte tag length? */);
|
||||||
|
if (!crypto_attribute) {
|
||||||
|
/* No crypto attribute to add, bad news */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attr = pjmedia_sdp_attr_create(pool, "crypto", pj_cstr(&stmp, crypto_attribute));
|
||||||
|
media->attr[media->attr_count++] = attr;
|
||||||
|
break;
|
||||||
|
case AST_SIP_MEDIA_ENCRYPT_DTLS:
|
||||||
|
if (setup_dtls_srtp(session, session_media)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dtls = ast_rtp_instance_get_dtls(session_media->rtp);
|
||||||
|
if (!dtls) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (dtls->get_connection(session_media->rtp)) {
|
||||||
|
case AST_RTP_DTLS_CONNECTION_NEW:
|
||||||
|
attr = pjmedia_sdp_attr_create(pool, "connection", &STR_NEW);
|
||||||
|
media->attr[media->attr_count++] = attr;
|
||||||
|
break;
|
||||||
|
case AST_RTP_DTLS_CONNECTION_EXISTING:
|
||||||
|
attr = pjmedia_sdp_attr_create(pool, "connection", &STR_EXISTING);
|
||||||
|
media->attr[media->attr_count++] = attr;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (dtls->get_setup(session_media->rtp)) {
|
||||||
|
case AST_RTP_DTLS_SETUP_ACTIVE:
|
||||||
|
attr = pjmedia_sdp_attr_create(pool, "setup", &STR_ACTIVE);
|
||||||
|
media->attr[media->attr_count++] = attr;
|
||||||
|
break;
|
||||||
|
case AST_RTP_DTLS_SETUP_PASSIVE:
|
||||||
|
attr = pjmedia_sdp_attr_create(pool, "setup", &STR_PASSIVE);
|
||||||
|
media->attr[media->attr_count++] = attr;
|
||||||
|
break;
|
||||||
|
case AST_RTP_DTLS_SETUP_ACTPASS:
|
||||||
|
attr = pjmedia_sdp_attr_create(pool, "setup", &STR_ACTPASS);
|
||||||
|
media->attr[media->attr_count++] = attr;
|
||||||
|
break;
|
||||||
|
case AST_RTP_DTLS_SETUP_HOLDCONN:
|
||||||
|
attr = pjmedia_sdp_attr_create(pool, "setup", &STR_HOLDCONN);
|
||||||
|
media->attr[media->attr_count++] = attr;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((crypto_attribute = dtls->get_fingerprint(session_media->rtp, AST_RTP_DTLS_HASH_SHA1))) {
|
||||||
|
RAII_VAR(struct ast_str *, fingerprint, ast_str_create(64), ast_free);
|
||||||
|
if (!fingerprint) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_str_set(&fingerprint, 0, "SHA-1 %s", crypto_attribute);
|
||||||
|
|
||||||
|
attr = pjmedia_sdp_attr_create(pool, "fingerprint", pj_cstr(&stmp, ast_str_buffer(fingerprint)));
|
||||||
|
media->attr[media->attr_count++] = attr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
crypto_attribute = ast_sdp_srtp_get_attrib(session_media->srtp,
|
|
||||||
0 /* DTLS can not be enabled for res_sip */,
|
|
||||||
0 /* don't prefer 32byte tag length */);
|
|
||||||
if (!crypto_attribute) {
|
|
||||||
/* No crypto attribute to add */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
attr = pjmedia_sdp_attr_create(pool, "crypto", pj_cstr(&stmp, crypto_attribute));
|
|
||||||
media->attr[media->attr_count++] = attr;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -672,7 +854,6 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
|
|||||||
struct ast_format format;
|
struct ast_format format;
|
||||||
RAII_VAR(struct ast_format_cap *, caps, NULL, ast_format_cap_destroy);
|
RAII_VAR(struct ast_format_cap *, caps, NULL, ast_format_cap_destroy);
|
||||||
enum ast_format_type media_type = stream_to_media_type(session_media->stream_type);
|
enum ast_format_type media_type = stream_to_media_type(session_media->stream_type);
|
||||||
int crypto_res;
|
|
||||||
|
|
||||||
int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&
|
int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&
|
||||||
!ast_format_cap_is_empty(session->direct_media_cap);
|
!ast_format_cap_is_empty(session->direct_media_cap);
|
||||||
@@ -694,11 +875,14 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
crypto_res = add_crypto_to_stream(session, session_media, pool, media);
|
if (add_crypto_to_stream(session, session_media, pool, media)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
media->desc.media = pj_str(session_media->stream_type);
|
media->desc.media = pj_str(session_media->stream_type);
|
||||||
media->desc.transport = pj_str(ast_sdp_get_rtp_profile(
|
media->desc.transport = pj_str(ast_sdp_get_rtp_profile(
|
||||||
!crypto_res, session_media->rtp, session->endpoint->use_avpf));
|
session->endpoint->media_encryption == AST_SIP_MEDIA_ENCRYPT_SDES,
|
||||||
|
session_media->rtp, session->endpoint->use_avpf));
|
||||||
|
|
||||||
/* Add connection level details */
|
/* Add connection level details */
|
||||||
if (direct_media_enabled) {
|
if (direct_media_enabled) {
|
||||||
@@ -823,11 +1007,20 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ensure incoming transport is compatible with the endpoint's configuration */
|
||||||
|
if (check_endpoint_media_transport(session->endpoint, remote_stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create an RTP instance if need be */
|
/* Create an RTP instance if need be */
|
||||||
if (!session_media->rtp && create_rtp(session, session_media, session->endpoint->rtp_ipv6)) {
|
if (!session_media->rtp && create_rtp(session, session_media, session->endpoint->rtp_ipv6)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setup_media_encryption(session, session_media, remote_stream)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host));
|
ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host));
|
||||||
|
|
||||||
/* Ensure that the address provided is valid */
|
/* Ensure that the address provided is valid */
|
||||||
|
@@ -854,6 +854,7 @@ static void session_destructor(void *obj)
|
|||||||
ast_taskprocessor_unreference(session->serializer);
|
ast_taskprocessor_unreference(session->serializer);
|
||||||
ao2_cleanup(session->datastores);
|
ao2_cleanup(session->datastores);
|
||||||
ao2_cleanup(session->media);
|
ao2_cleanup(session->media);
|
||||||
|
|
||||||
AST_LIST_HEAD_DESTROY(&session->supplements);
|
AST_LIST_HEAD_DESTROY(&session->supplements);
|
||||||
while ((delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next))) {
|
while ((delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next))) {
|
||||||
ast_free(delay);
|
ast_free(delay);
|
||||||
|
Reference in New Issue
Block a user