Add DTLS-SRTP support to chan_pjsip
authorKinsey Moore <kmoore@digium.com>
Tue, 23 Jul 2013 13:52:06 +0000 (13:52 +0000)
committerKinsey Moore <kmoore@digium.com>
Tue, 23 Jul 2013 13:52:06 +0000 (13:52 +0000)
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

channels/chan_gulp.c
channels/chan_sip.c
include/asterisk/res_sip.h
include/asterisk/res_sip_session.h
res/res_sip.c
res/res_sip/sip_configuration.c
res/res_sip_sdp_rtp.c
res/res_sip_session.c

index 1aa6a37..e3d3ed0 100644 (file)
@@ -384,6 +384,10 @@ static enum ast_rtp_glue_result gulp_get_rtp_peer(struct ast_channel *chan, stru
        ao2_ref(*instance, +1);
 
        ast_assert(endpoint != NULL);
+       if (endpoint->media_encryption != AST_SIP_MEDIA_ENCRYPT_NONE) {
+               return AST_RTP_GLUE_RESULT_FORBID;
+       }
+
        if (endpoint->direct_media) {
                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 gulp_pvt *pvt = channel->pvt;
+       struct ast_sip_endpoint *endpoint;
 
        if (!pvt || !channel->session || !pvt->media[SIP_MEDIA_VIDEO]->rtp) {
                return AST_RTP_GLUE_RESULT_FORBID;
        }
 
+       endpoint = channel->session->endpoint;
+
        *instance = pvt->media[SIP_MEDIA_VIDEO]->rtp;
        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;
 }
 
index 62afeb0..ca4b25b 100644 (file)
@@ -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")) {
                                        secure_audio = 1;
 
+                                       processed_crypto = 1;
                                        if (p->srtp) {
                                                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")) {
                                        secure_video = 1;
 
+                                       processed_crypto = 1;
                                        if (p->vsrtp || (p->vsrtp = ast_sdp_srtp_alloc())) {
                                                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)
 {
        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;
        }
 
-       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) {
                return NULL;
        }
index 7342d9d..01d50bf 100644 (file)
@@ -34,6 +34,8 @@
 #include "asterisk/endpoints.h"
 /* Needed for pj_sockaddr */
 #include <pjlib.h>
+/* Needed for ast_rtp_dtls_cfg struct */
+#include "asterisk/rtp_engine.h"
 
 /* Forward declarations of PJSIP stuff */
 struct pjsip_rx_data;
@@ -446,6 +448,10 @@ struct ast_sip_endpoint {
        unsigned int allowsubscribe;
        /*! The minimum allowed expiration for subscriptions from endpoint */
        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;
 };
 
 /*!
index 26299eb..fb482db 100644 (file)
@@ -26,7 +26,7 @@
 #include "asterisk/channel.h"
 /* Needed for ast_sockaddr struct */
 #include "asterisk/netsock.h"
-/* Neeed for ast_sdp_srtp struct */
+/* Needed for ast_sdp_srtp struct */
 #include "asterisk/sdp_srtp.h"
 
 /* Forward declarations */
@@ -42,7 +42,6 @@ struct pjsip_rx_data;
 struct ast_party_id;
 struct pjmedia_sdp_media;
 struct pjmedia_sdp_session;
-struct ast_rtp_instance;
 struct ast_dsp;
 
 struct ast_sip_session_sdp_handler;
index f44a8bc..d904165 100644 (file)
                                                                transport should be used in conjunction with this option to prevent
                                                                exposure of media encryption keys.
                                                        </para></enum>
+                                                       <enum name="dtls"><para>
+                                                               res_sip will offer DTLS-SRTP setup.
+                                                       </para></enum>
                                                </enumlist>
                                        </description>
                                </configOption>
                                <configOption name="fromdomain">
                                        <synopsis>Domain to user in From header for requests to this endpoint.</synopsis>
                                </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 name="auth">
                                <synopsis>Authentication type</synopsis>
index 5911edb..809430a 100644 (file)
@@ -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;
        } else if (!strcasecmp("sdes", var->value)) {
                endpoint->media_encryption = AST_SIP_MEDIA_ENCRYPT_SDES;
-       /*} else if (!strcasecmp("dtls", var->value)) {
-               endpoint->media_encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;*/
+       } else if (!strcasecmp("dtls", var->value)) {
+               endpoint->media_encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;
+               ast_rtp_dtls_cfg_parse(&endpoint->dtls_cfg, "dtlsenable", "yes");
        } else {
                return -1;
        }
@@ -518,6 +519,14 @@ static int named_groups_handler(const struct aco_option *opt,
        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)
 {
        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", "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_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)) {
                ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
index b299ed7..6e99411 100644 (file)
@@ -525,10 +525,6 @@ static enum ast_sip_session_media_encryption check_endpoint_media_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) {
                return incoming_encryption;
@@ -537,6 +533,108 @@ static enum ast_sip_session_media_encryption check_endpoint_media_transport(
        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,
        const struct pjmedia_sdp_media *stream)
 {
@@ -557,18 +655,8 @@ static int setup_sdes_srtp(struct ast_sip_session_media *session_media,
                        return -1;
                }
 
-               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;
-                       }
+               if (setup_srtp(session_media)) {
+                       return -1;
                }
 
                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;
 }
 
+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 */
 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)
@@ -590,7 +704,6 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
        char host[NI_MAXHOST];
        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_sip_session_media_encryption incoming_encryption;
 
        /* If no type formats have been configured reject this stream */
        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 */
-       incoming_encryption = check_endpoint_media_transport(session->endpoint, stream);
-       if (incoming_encryption == AST_SIP_MEDIA_TRANSPORT_INVALID) {
+       if (check_endpoint_media_transport(session->endpoint, stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) {
                return -1;
        }
 
@@ -616,8 +728,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
                return -1;
        }
 
-       if (incoming_encryption == AST_SIP_MEDIA_ENCRYPT_SDES
-                       && setup_sdes_srtp(session_media, stream)) {
+       if (setup_media_encryption(session, session_media, stream)) {
                return -1;
        }
 
@@ -631,24 +742,95 @@ static int add_crypto_to_stream(struct ast_sip_session *session,
        pj_str_t stmp;
        pjmedia_sdp_attr *attr;
        const char *crypto_attribute;
-
-       if (!session_media->srtp && session->endpoint->media_encryption != AST_SIP_MEDIA_ENCRYPT_NONE) {
-               session_media->srtp = ast_sdp_srtp_alloc();
+       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 };
+
+       switch (session->endpoint->media_encryption) {
+       case AST_SIP_MEDIA_ENCRYPT_NONE:
+       case AST_SIP_MEDIA_TRANSPORT_INVALID:
+               break;
+       case AST_SIP_MEDIA_ENCRYPT_SDES:
                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;
                }
-       }
 
-       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;
+               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;
        }
 
-       attr = pjmedia_sdp_attr_create(pool, "crypto", pj_cstr(&stmp, crypto_attribute));
-       media->attr[media->attr_count++] = attr;
        return 0;
 }
 
@@ -672,7 +854,6 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
        struct ast_format format;
        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);
-       int crypto_res;
 
        int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&
                !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;
        }
 
-       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.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 */
        if (direct_media_enabled) {
@@ -823,11 +1007,20 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
                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 */
        if (!session_media->rtp && create_rtp(session, session_media, session->endpoint->rtp_ipv6)) {
                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));
 
        /* Ensure that the address provided is valid */
index 7028e0d..38a0604 100644 (file)
@@ -854,6 +854,7 @@ static void session_destructor(void *obj)
        ast_taskprocessor_unreference(session->serializer);
        ao2_cleanup(session->datastores);
        ao2_cleanup(session->media);
+
        AST_LIST_HEAD_DESTROY(&session->supplements);
        while ((delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next))) {
                ast_free(delay);