Add support for DTLS-SRTP to res_rtp_asterisk and chan_sip.
authorJoshua Colp <jcolp@digium.com>
Thu, 20 Sep 2012 18:27:28 +0000 (18:27 +0000)
committerJoshua Colp <jcolp@digium.com>
Thu, 20 Sep 2012 18:27:28 +0000 (18:27 +0000)
As mentioned on the review for this, WebRTC has moved towards choosing
DTLS-SRTP as the mechanism for key exchange for SRTP. This commit adds
support for this but makes it available for normal SIP clients as well.

Testing has been done to ensure that this introduces no regressions with
existing behavior and also that it functions as expected.

Review: https://reviewboard.asterisk.org/r/2113/
........

Merged revisions 373229 from http://svn.asterisk.org/svn/asterisk/branches/11

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@373234 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_sip.c
channels/sip/include/sip.h
configs/sip.conf.sample
configure
configure.ac
include/asterisk/autoconfig.h.in
include/asterisk/rtp_engine.h
main/rtp_engine.c
res/res_rtp_asterisk.c

index dd64867..a4a6da9 100644 (file)
@@ -1387,11 +1387,13 @@ static int process_sdp_o(const char *o, struct sip_pvt *p);
 static int process_sdp_c(const char *c, struct ast_sockaddr *addr);
 static int process_sdp_a_sendonly(const char *a, int *sendonly);
 static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
+static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
 static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec);
 static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newvideortp, int *last_rtpmap_codec);
 static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, int *last_rtpmap_codec);
 static int process_sdp_a_image(const char *a, struct sip_pvt *p);
 static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
+static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
 static void start_ice(struct ast_rtp_instance *instance);
 static void add_codec_to_sdp(const struct sip_pvt *p, struct ast_format *codec,
                             struct ast_str **m_buf, struct ast_str **a_buf,
@@ -4928,6 +4930,8 @@ static void sip_destroy_peer(struct sip_peer *peer)
        ast_string_field_free_memory(peer);
 
        peer->caps = ast_format_cap_destroy(peer->caps);
+
+       ast_rtp_dtls_cfg_free(&peer->dtls_cfg);
 }
 
 /*! \brief Update peer data in database (if used) */
@@ -5515,6 +5519,29 @@ static void copy_socket_data(struct sip_socket *to_sock, const struct sip_socket
        *to_sock = *from_sock;
 }
 
+/*! \brief Initialize DTLS-SRTP support on an RTP instance */
+static int dialog_initialize_dtls_srtp(const struct sip_pvt *dialog, struct ast_rtp_instance *rtp, struct sip_srtp **srtp)
+{
+       struct ast_rtp_engine_dtls *dtls;
+
+       if (!dialog->dtls_cfg.enabled) {
+               return 0;
+       }
+
+       if (!ast_rtp_engine_srtp_is_registered()) {
+               ast_log(LOG_ERROR, "No SRTP module loaded, can't setup SRTP session.\n");
+               return -1;
+       }
+
+       if (!(dtls = ast_rtp_instance_get_dtls(rtp)) ||
+           dtls->set_configuration(rtp, &dialog->dtls_cfg) ||
+           !(*srtp = sip_srtp_alloc())) {
+               return -1;
+       }
+
+       return 0;
+}
+
 /*! \brief Initialize RTP portion of a dialog
  * \return -1 on failure, 0 on success
  */
@@ -5536,6 +5563,10 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog)
                ice->stop(dialog->rtp);
        }
 
+       if (dialog_initialize_dtls_srtp(dialog, dialog->rtp, &dialog->srtp)) {
+               return -1;
+       }
+
        if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS) ||
                        (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) && (ast_format_cap_has_type(dialog->caps, AST_FORMAT_TYPE_VIDEO)))) {
                if (!(dialog->vrtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr_tmp, NULL))) {
@@ -5546,6 +5577,10 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog)
                        ice->stop(dialog->vrtp);
                }
 
+               if (dialog_initialize_dtls_srtp(dialog, dialog->vrtp, &dialog->vsrtp)) {
+                       return -1;
+               }
+
                ast_rtp_instance_set_timeout(dialog->vrtp, dialog->rtptimeout);
                ast_rtp_instance_set_hold_timeout(dialog->vrtp, dialog->rtpholdtimeout);
                ast_rtp_instance_set_keepalive(dialog->vrtp, dialog->rtpkeepalive);
@@ -5562,6 +5597,10 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog)
                        ice->stop(dialog->trtp);
                }
 
+               if (dialog_initialize_dtls_srtp(dialog, dialog->trtp, &dialog->tsrtp)) {
+                       return -1;
+               }
+
                /* Do not timeout text as its not constant*/
                ast_rtp_instance_set_keepalive(dialog->trtp, dialog->rtpkeepalive);
 
@@ -5618,6 +5657,8 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
 
        ast_string_field_set(dialog, engine, peer->engine);
 
+       ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &dialog->dtls_cfg);
+
        dialog->rtptimeout = peer->rtptimeout;
        dialog->rtpholdtimeout = peer->rtpholdtimeout;
        dialog->rtpkeepalive = peer->rtpkeepalive;
@@ -6263,6 +6304,8 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
        p->redircaps = ast_format_cap_destroy(p->redircaps);
        p->prefcaps = ast_format_cap_destroy(p->prefcaps);
 
+       ast_rtp_dtls_cfg_free(&p->dtls_cfg);
+
        if (p->last_device_state_info) {
                ao2_ref(p->last_device_state_info, -1);
                p->last_device_state_info = NULL;
@@ -9577,6 +9620,16 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                processed = TRUE;
                        }
 
+                       if (process_sdp_a_dtls(value, p, p->rtp)) {
+                               processed = TRUE;
+                       }
+                       if (process_sdp_a_dtls(value, p, p->vrtp)) {
+                               processed = TRUE;
+                       }
+                       if (process_sdp_a_dtls(value, p, p->trtp)) {
+                               processed = TRUE;
+                       }
+
                        break;
                }
 
@@ -9594,8 +9647,9 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                int image = FALSE;
                int text = FALSE;
                int processed_crypto = FALSE;
-               char protocol[6] = {0,};
+               char protocol[18] = {0,};
                int x;
+               struct ast_rtp_engine_dtls *dtls;
 
                numberofports = 0;
                len = -1;
@@ -9614,50 +9668,60 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 
                /* Check for 'audio' media offer */
                if (strncmp(m, "audio ", 6) == 0) {
-                       if ((sscanf(m, "audio %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
-                           (sscanf(m, "audio %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
+                       if ((sscanf(m, "audio %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+                           (sscanf(m, "audio %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
                                codecs = m + len;
                                /* produce zero-port m-line since it may be needed later
-                                * length is "m=audio 0 RTP/" + protocol + " " + codecs + "\0" */
-                               if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+                                * length is "m=audio 0 " + protocol + " " + codecs + "\0" */
+                               if (!(offer->decline_m_line = ast_malloc(10 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
                                        ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
                                        res = -1;
                                        goto process_sdp_cleanup;
                                }
                                /* guaranteed to be exactly the right length */
-                               sprintf(offer->decline_m_line, "m=audio 0 RTP/%s %s", protocol, codecs);
+                               sprintf(offer->decline_m_line, "m=audio 0 %s %s", protocol, codecs);
 
                                if (x == 0) {
                                        ast_log(LOG_WARNING, "Ignoring audio media offer because port number is zero\n");
                                        continue;
                                }
 
+                               if (has_media_stream(p, SDP_AUDIO)) {
+                                       ast_log(LOG_WARNING, "Declining non-primary audio stream: %s\n", m);
+                                       continue;
+                               }
+
                                /* Check number of ports offered for stream */
                                if (numberofports > 1) {
                                        ast_log(LOG_WARNING, "%d ports offered for audio media, not supported by Asterisk. Will try anyway...\n", numberofports);
                                }
 
-                               if (!strcmp(protocol, "SAVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+                               if ((!strcmp(protocol, "RTP/SAVPF") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
                                        ast_log(LOG_WARNING, "Received SAVPF profle in audio offer but AVPF is not enabled: %s\n", m);
                                        continue;
-                               } else if (!strcmp(protocol, "SAVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+                               } else if ((!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVP")) && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
                                        ast_log(LOG_WARNING, "Received SAVP profile in audio offer but AVPF is enabled: %s\n", m);
                                        continue;
-                               } else if (!strcmp(protocol, "SAVP") || !strcmp(protocol, "SAVPF")) {
+                               } else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
                                        secure_audio = 1;
-                               } else if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+
+                                       if (p->srtp) {
+                                               ast_set_flag(p->srtp, SRTP_CRYPTO_OFFER_OK);
+                                       }
+                               } else if (!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "RTP/SAVPF")) {
+                                       secure_audio = 1;
+                               } else if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
                                        ast_log(LOG_WARNING, "Received AVPF profile in audio offer but AVPF is not enabled: %s\n", m);
                                        continue;
-                               } else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+                               } else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
                                        ast_log(LOG_WARNING, "Received AVP profile in audio offer but AVPF is enabled: %s\n", m);
                                        continue;
-                               } else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
-                                       ast_log(LOG_WARNING, "Unknown RTP profile in audio offer: %s\n", m);
+                               } else if ((!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) &&
+                                          (!(dtls = ast_rtp_instance_get_dtls(p->rtp)) || !dtls->active(p->rtp))) {
+                                       ast_log(LOG_WARNING, "Received UDP/TLS in audio offer but DTLS is not enabled: %s\n", m);
                                        continue;
-                               }
-
-                               if (has_media_stream(p, SDP_AUDIO)) {
-                                       ast_log(LOG_WARNING, "Declining non-primary audio stream: %s\n", m);
+                               } else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
+                                       ast_log(LOG_WARNING, "Unknown RTP profile in audio offer: %s\n", m);
                                        continue;
                                }
 
@@ -9686,18 +9750,18 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                }
                /* Check for 'video' media offer */
                else if (strncmp(m, "video ", 6) == 0) {
-                       if ((sscanf(m, "video %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
-                           (sscanf(m, "video %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
+                       if ((sscanf(m, "video %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+                           (sscanf(m, "video %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
                                codecs = m + len;
                                /* produce zero-port m-line since it may be needed later
-                                * length is "m=video 0 RTP/" + protocol + " " + codecs + "\0" */
-                               if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+                                * length is "m=video 0 " + protocol + " " + codecs + "\0" */
+                               if (!(offer->decline_m_line = ast_malloc(10 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
                                        ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
                                        res = -1;
                                        goto process_sdp_cleanup;
                                }
                                /* guaranteed to be exactly the right length */
-                               sprintf(offer->decline_m_line, "m=video 0 RTP/%s %s", protocol, codecs);
+                               sprintf(offer->decline_m_line, "m=video 0 %s %s", protocol, codecs);
 
                                if (x == 0) {
                                        ast_log(LOG_WARNING, "Ignoring video stream offer because port number is zero\n");
@@ -9709,30 +9773,36 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                        ast_log(LOG_WARNING, "%d ports offered for video stream, not supported by Asterisk. Will try anyway...\n", numberofports);
                                }
 
-                               if (!strcmp(protocol, "SAVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+                               if (has_media_stream(p, SDP_VIDEO)) {
+                                       ast_log(LOG_WARNING, "Declining non-primary video stream: %s\n", m);
+                                       continue;
+                               }
+
+                               if ((!strcmp(protocol, "RTP/SAVPF") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
                                        ast_log(LOG_WARNING, "Received SAVPF profle in video offer but AVPF is not enabled: %s\n", m);
                                        continue;
-                               } else if (!strcmp(protocol, "SAVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+                               } else if ((!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVP")) && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
                                        ast_log(LOG_WARNING, "Received SAVP profile in video offer but AVPF is enabled: %s\n", m);
                                        continue;
-                               } else if (!strcmp(protocol, "SAVP") || !strcmp(protocol, "SAVPF")) {
+                               } else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
+                                       secure_video = 1;
+
+                                       if (p->vsrtp || (p->vsrtp = sip_srtp_alloc())) {
+                                               ast_set_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK);
+                                       }
+                               } else if (!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "RTP/SAVPF")) {
                                        secure_video = 1;
-                               } else if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+                               } else if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
                                        ast_log(LOG_WARNING, "Received AVPF profile in video offer but AVPF is not enabled: %s\n", m);
                                        continue;
-                               } else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+                               } else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
                                        ast_log(LOG_WARNING, "Received AVP profile in video offer but AVPF is enabled: %s\n", m);
                                        continue;
-                               } else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
+                               } else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
                                        ast_log(LOG_WARNING, "Unknown RTP profile in video offer: %s\n", m);
                                        continue;
                                }
 
-                               if (has_media_stream(p, SDP_VIDEO)) {
-                                       ast_log(LOG_WARNING, "Declining non-primary video stream: %s\n", m);
-                                       continue;
-                               }
-
                                video = TRUE;
                                p->novideo = FALSE;
                                offer->type = SDP_VIDEO;
@@ -9758,18 +9828,18 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                }
                /* Check for 'text' media offer */
                else if (strncmp(m, "text ", 5) == 0) {
-                       if ((sscanf(m, "text %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
-                           (sscanf(m, "text %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
+                       if ((sscanf(m, "text %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+                           (sscanf(m, "text %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
                                codecs = m + len;
                                /* produce zero-port m-line since it may be needed later
-                                * length is "m=text 0 RTP/" + protocol + " " + codecs + "\0" */
-                               if (!(offer->decline_m_line = ast_malloc(13 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+                                * length is "m=text 0 " + protocol + " " + codecs + "\0" */
+                               if (!(offer->decline_m_line = ast_malloc(9 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
                                        ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
                                        res = -1;
                                        goto process_sdp_cleanup;
                                }
                                /* guaranteed to be exactly the right length */
-                               sprintf(offer->decline_m_line, "m=text 0 RTP/%s %s", protocol, codecs);
+                               sprintf(offer->decline_m_line, "m=text 0 %s %s", protocol, codecs);
 
                                if (x == 0) {
                                        ast_log(LOG_WARNING, "Ignoring text stream offer because port number is zero\n");
@@ -9781,13 +9851,13 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                        ast_log(LOG_WARNING, "%d ports offered for text stream, not supported by Asterisk. Will try anyway...\n", numberofports);
                                }
 
-                               if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+                               if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
                                        ast_log(LOG_WARNING, "Received AVPF profile in text offer but AVPF is not enabled: %s\n", m);
                                        continue;
-                               } else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+                               } else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
                                        ast_log(LOG_WARNING, "Received AVP profile in text offer but AVPF is enabled: %s\n", m);
                                        continue;
-                               } else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
+                               } else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
                                        ast_log(LOG_WARNING, "Unknown RTP profile in text offer: %s\n", m);
                                        continue;
                                }
@@ -9923,6 +9993,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                if (audio) {
                                        if (process_sdp_a_ice(value, p, p->rtp)) {
                                                processed = TRUE;
+                                       } else if (process_sdp_a_dtls(value, p, p->rtp)) {
+                                               processed = TRUE;
                                        } else if (process_sdp_a_sendonly(value, &sendonly)) {
                                                processed = TRUE;
                                        } else if (!processed_crypto && process_crypto(p, p->rtp, &p->srtp, value)) {
@@ -9936,6 +10008,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                else if (video) {
                                        if (process_sdp_a_ice(value, p, p->vrtp)) {
                                                processed = TRUE;
+                                       } else if (process_sdp_a_dtls(value, p, p->vrtp)) {
+                                               processed = TRUE;
                                        } else if (!processed_crypto && process_crypto(p, p->vrtp, &p->vsrtp, value)) {
                                                processed_crypto = TRUE;
                                                processed = TRUE;
@@ -9947,7 +10021,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                else if (text) {
                                        if (process_sdp_a_ice(value, p, p->trtp)) {
                                                processed = TRUE;
-                                       } if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec)) {
+                                       } else if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec)) {
                                                processed = TRUE;
                                        } else if (!processed_crypto && process_crypto(p, p->trtp, &p->tsrtp, value)) {
                                                processed_crypto = TRUE;
@@ -10466,6 +10540,56 @@ static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_in
        return found;
 }
 
+static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance)
+{
+       struct ast_rtp_engine_dtls *dtls;
+       int found = FALSE;
+       char value[256], hash[6];
+
+       if (!instance || !p->dtls_cfg.enabled || !(dtls = ast_rtp_instance_get_dtls(instance))) {
+               return found;
+       }
+
+       if (sscanf(a, "setup: %255s", value) == 1) {
+               found = TRUE;
+
+               if (!strcasecmp(value, "active")) {
+                       dtls->set_setup(instance, AST_RTP_DTLS_SETUP_ACTIVE);
+               } else if (!strcasecmp(value, "passive")) {
+                       dtls->set_setup(instance, AST_RTP_DTLS_SETUP_PASSIVE);
+               } else if (!strcasecmp(value, "actpass")) {
+                       dtls->set_setup(instance, AST_RTP_DTLS_SETUP_ACTPASS);
+               } else if (!strcasecmp(value, "holdconn")) {
+                       dtls->set_setup(instance, AST_RTP_DTLS_SETUP_HOLDCONN);
+               } else {
+                       ast_log(LOG_WARNING, "Unsupported setup attribute value '%s' received on dialog '%s'\n",
+                               value, p->callid);
+               }
+       } else if (sscanf(a, "connection: %255s", value) == 1) {
+               found = TRUE;
+
+               if (!strcasecmp(value, "new")) {
+                       dtls->reset(instance);
+               } else if (!strcasecmp(value, "existing")) {
+                       /* Since they want to just use what already exists we go on as if nothing happened */
+               } else {
+                       ast_log(LOG_WARNING, "Unsupported connection attribute value '%s' received on dialog '%s'\n",
+                               value, p->callid);
+               }
+       } else if (sscanf(a, "fingerprint: %5s %255s", hash, value) == 2) {
+               found = TRUE;
+
+               if (!strcasecmp(hash, "sha-1")) {
+                       dtls->set_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1, value);
+               } else {
+                       ast_log(LOG_WARNING, "Unsupported fingerprint hash type '%s' received on dialog '%s'\n",
+                               hash, p->callid);
+               }
+       }
+
+       return found;
+}
+
 static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec)
 {
        int found = FALSE;
@@ -12053,6 +12177,49 @@ static void start_ice(struct ast_rtp_instance *instance)
        ice->start(instance);
 }
 
+/*! \brief Add DTLS attributes to SDP */
+static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf)
+{
+       struct ast_rtp_engine_dtls *dtls;
+       const char *fingerprint;
+
+       if (!instance || !(dtls = ast_rtp_instance_get_dtls(instance)) || !dtls->active(instance)) {
+               return;
+       }
+
+       switch (dtls->get_connection(instance)) {
+       case AST_RTP_DTLS_CONNECTION_NEW:
+               ast_str_append(a_buf, 0, "a=connection:new\r\n");
+               break;
+       case AST_RTP_DTLS_CONNECTION_EXISTING:
+               ast_str_append(a_buf, 0, "a=connection:existing\r\n");
+               break;
+       default:
+               break;
+       }
+
+       switch (dtls->get_setup(instance)) {
+       case AST_RTP_DTLS_SETUP_ACTIVE:
+               ast_str_append(a_buf, 0, "a=setup:active\r\n");
+               break;
+       case AST_RTP_DTLS_SETUP_PASSIVE:
+               ast_str_append(a_buf, 0, "a=setup:passive\r\n");
+               break;
+       case AST_RTP_DTLS_SETUP_ACTPASS:
+               ast_str_append(a_buf, 0, "a=setup:actpass\r\n");
+               break;
+       case AST_RTP_DTLS_SETUP_HOLDCONN:
+               ast_str_append(a_buf, 0, "a=setup:holdconn\r\n");
+               break;
+       default:
+               break;
+       }
+
+       if ((fingerprint = dtls->get_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1))) {
+               ast_str_append(a_buf, 0, "a=fingerprint:SHA-1 %s\r\n", fingerprint);
+       }
+}
+
 /*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
 static void add_codec_to_sdp(const struct sip_pvt *p,
        struct ast_format *format,
@@ -12323,6 +12490,11 @@ static void get_crypto_attrib(struct sip_pvt *p, struct sip_srtp *srtp, const ch
                        srtp->crypto = sdp_crypto_setup();
                }
 
+               if (p->dtls_cfg.enabled) {
+                       /* If DTLS-SRTP is enabled the key details will be pulled from TLS */
+                       return;
+               }
+
                /* set the key length based on INVITE or settings */
                if (ast_test_flag(srtp, SRTP_CRYPTO_TAG_80)) {
                        taglen = 80;
@@ -12341,12 +12513,18 @@ static void get_crypto_attrib(struct sip_pvt *p, struct sip_srtp *srtp, const ch
        }
 }
 
-static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure)
+static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure, struct ast_rtp_instance *instance)
 {
-       if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
-               return secure ? "SAVPF" : "AVPF";
+       struct ast_rtp_engine_dtls *dtls;
+
+       if ((dtls = ast_rtp_instance_get_dtls(instance)) && dtls->active(instance)) {
+               return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "UDP/TLS/RTP/SAVPF" : "UDP/TLS/RTP/SAVP";
        } else {
-               return secure ? "SAVP" : "AVP";
+               if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+                       return secure ? "RTP/SAVPF" : "RTP/AVPF";
+               } else {
+                       return secure ? "RTP/SAVP" : "RTP/AVP";
+               }
        }
 }
 
@@ -12509,8 +12687,8 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                   Video is handled differently than audio since we can not transcode. */
                if (needvideo) {
                        get_crypto_attrib(p, p->vsrtp, &v_a_crypto);
-                       ast_str_append(&m_video, 0, "m=video %d RTP/%s", ast_sockaddr_port(&vdest),
-                                      get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
+                       ast_str_append(&m_video, 0, "m=video %d %s", ast_sockaddr_port(&vdest),
+                                      get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->vrtp));
 
                        /* Build max bitrate string */
                        if (p->maxcallbitrate)
@@ -12519,8 +12697,12 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                                ast_verbose("Video is at %s\n", ast_sockaddr_stringify(&vdest));
                        }
 
-                       if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
-                               add_ice_to_sdp(p->vrtp, &a_video);
+                       if (!doing_directmedia) {
+                               if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
+                                       add_ice_to_sdp(p->vrtp, &a_video);
+                               }
+
+                               add_dtls_to_sdp(p->vrtp, &a_video);
                        }
                }
 
@@ -12530,14 +12712,18 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                        if (sipdebug_text)
                                ast_verbose("Lets set up the text sdp\n");
                        get_crypto_attrib(p, p->tsrtp, &t_a_crypto);
-                       ast_str_append(&m_text, 0, "m=text %d RTP/%s", ast_sockaddr_port(&tdest),
-                                      get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
+                       ast_str_append(&m_text, 0, "m=text %d %s", ast_sockaddr_port(&tdest),
+                                      get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->trtp));
                        if (debug) {  /* XXX should I use tdest below ? */
                                ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&taddr));
                        }
 
-                       if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
-                               add_ice_to_sdp(p->trtp, &a_text);
+                       if (!doing_directmedia) {
+                               if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
+                                       add_ice_to_sdp(p->trtp, &a_text);
+                               }
+
+                               add_dtls_to_sdp(p->trtp, &a_text);
                        }
                }
 
@@ -12547,8 +12733,8 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                   peer doesn't have to ast_gethostbyname() us */
 
                get_crypto_attrib(p, p->srtp, &a_crypto);
-               ast_str_append(&m_audio, 0, "m=audio %d RTP/%s", ast_sockaddr_port(&dest),
-                              get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
+               ast_str_append(&m_audio, 0, "m=audio %d %s", ast_sockaddr_port(&dest),
+                              get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->rtp));
 
                /* Now, start adding audio codecs. These are added in this order:
                   - First what was requested by the calling channel
@@ -12635,8 +12821,12 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                if (min_text_packet_size)
                        ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size);
 
-               if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
-                       add_ice_to_sdp(p->rtp, &a_audio);
+               if (!doing_directmedia) {
+                       if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
+                               add_ice_to_sdp(p->rtp, &a_audio);
+                       }
+
+                       add_dtls_to_sdp(p->rtp, &a_audio);
                }
 
                if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 ||
@@ -17354,6 +17544,8 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
                set_t38_capabilities(p);
        }
 
+       ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &p->dtls_cfg);
+
        /* Copy SIP extensions profile to peer */
        /* XXX is this correct before a successful auth ? */
        if (p->sipoptions)
@@ -17400,6 +17592,8 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
                ast_string_field_set(p, peername, peer->name);
                ast_string_field_set(p, authname, peer->name);
 
+               ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &p->dtls_cfg);
+
                if (sipmethod == SIP_INVITE) {
                        /* destroy old channel vars and copy in new ones. */
                        ast_variables_destroy(p->chanvars);
@@ -30009,9 +30203,14 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                                ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_USE_AVPF);
                        } else if (!strcasecmp(v->name, "icesupport")) {
                                ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_ICE_SUPPORT);
+                       } else {
+                               ast_rtp_dtls_cfg_parse(&peer->dtls_cfg, v->name, v->value);
                        }
                }
 
+               /* Apply the encryption tag length to the DTLS configuration, in case DTLS is in use */
+               peer->dtls_cfg.suite = (ast_test_flag(&peer->flags[2], SIP_PAGE3_SRTP_TAG_32) ? AST_AES_CM_128_HMAC_SHA1_32 : AST_AES_CM_128_HMAC_SHA1_80);
+
                /* These apply to devstate lookups */
                if (realtime && !strcasecmp(v->name, "lastms")) {
                        sscanf(v->value, "%30d", &peer->lastms);
@@ -32201,6 +32400,8 @@ 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)
 {
+       struct ast_rtp_engine_dtls *dtls;
+
        /* If no RTP instance exists for this media stream don't bother processing the crypto line */
        if (!rtp) {
                ast_debug(3, "Received offer with crypto line for media stream that is not enabled\n");
@@ -32231,6 +32432,11 @@ static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struc
 
        ast_set_flag(*srtp, SRTP_CRYPTO_OFFER_OK);
 
+       if ((dtls = ast_rtp_instance_get_dtls(rtp))) {
+               dtls->stop(rtp);
+               p->dtls_cfg.enabled = 0;
+       }
+
        return TRUE;
 }
 
index 047310f..48a5a3b 100644 (file)
@@ -36,6 +36,7 @@
 #include "asterisk/security_events.h"
 #include "asterisk/features.h"
 #include "asterisk/http_websocket.h"
+#include "asterisk/rtp_engine.h"
 
 #ifndef FALSE
 #define FALSE    0
@@ -1212,6 +1213,8 @@ struct sip_pvt {
        struct ast_cc_config_params *cc_params;
        struct sip_epa_entry *epa_entry;
        int fromdomainport;                 /*!< Domain port to show in from field */
+
+       struct ast_rtp_dtls_cfg dtls_cfg;
 };
 
 /*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission
@@ -1361,6 +1364,8 @@ struct sip_peer {
        enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */
        unsigned int disallowed_methods;
        struct ast_cc_config_params *cc_params;
+
+       struct ast_rtp_dtls_cfg dtls_cfg;
 };
 
 /*!
index 81ca998..6c3df58 100644 (file)
@@ -1240,6 +1240,38 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ; maxforwards
 ; encryption
 ; description          ; Used to provide a description of the peer in console output
+; dtlsenable
+; dtlsverify
+; dtlsrekey
+; dtlscertfile
+; dtlsprivatekey
+; dtlscipher
+; dtlscafile
+; dtlscapath
+; dtlssetup
+;
+
+;------------------------------------------------------------------------------
+; DTLS-SRTP CONFIGURATION
+;
+; DTLS-SRTP support is available if the underlying RTP engine in use supports it.
+;
+; dtlsenable = yes                   ; Enable or disable DTLS-SRTP support
+; dtlsverify = yes                   ; Verify that the provided peer certificate is valid
+; dtlsrekey = 60                     ; Interval at which to renegotiate the TLS session and rekey the SRTP session
+;                                    ; If this is not set or the value provided is 0 rekeying will be disabled
+; dtlscertfile = file                ; Path to certificate file to present
+; dtlsprivatekey = file              ; Path to private key for certificate file
+; dtlscipher = <SSL cipher string>   ; Cipher to use for TLS negotiation
+;                                    ; A list of valid SSL cipher strings can be found at:
+;                                    ; http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
+; dtlscafile = file                  ; Path to certificate authority certificate
+; dtlscapath = path                  ; Path to a directory containing certificate authority certificates
+; dtlssetup = actpass                ; Whether we are willing to accept connections, connect to the other party, or both.
+;                                    ; Valid options are active (we want to connect to the other party), passive (we want to
+;                                    ; accept connections only), and actpass (we will do both). This value will be used in
+;                                    ; the outgoing SDP when offering and for incoming SDP offers when the remote party sends
+;                                    ; actpass
 
 ;[sip_proxy]
 ; For incoming calls only. Example: FWD (Free World Dialup)
index d6d2636..db14c6d 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 371030 .
+# From configure.ac Revision: 373120 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.68 for asterisk trunk.
 #
@@ -1018,6 +1018,10 @@ PBX_DAHDI
 DAHDI_DIR
 DAHDI_INCLUDE
 DAHDI_LIB
+PBX_OPENSSL_SRTP
+OPENSSL_SRTP_DIR
+OPENSSL_SRTP_INCLUDE
+OPENSSL_SRTP_LIB
 PBX_CRYPTO
 CRYPTO_DIR
 CRYPTO_INCLUDE
@@ -8277,6 +8281,18 @@ fi
 
 
 
+OPENSSL_SRTP_DESCRIP="OpenSSL SRTP Extension Support"
+OPENSSL_SRTP_OPTION=crypto
+OPENSSL_SRTP_DIR=${CRYPTO_DIR}
+
+PBX_OPENSSL_SRTP=0
+
+
+
+
+
+
+
     DAHDI_DESCRIP="DAHDI"
     DAHDI_OPTION="dahdi"
     PBX_DAHDI=0
@@ -27448,6 +27464,110 @@ $as_echo "no" >&6; }
                fi
        fi
 
+
+if test "x${PBX_OPENSSL_SRTP}" != "x1" -a "${USE_OPENSSL_SRTP}" != "no"; then
+   pbxlibdir=""
+   # if --with-OPENSSL_SRTP=DIR has been specified, use it.
+   if test "x${OPENSSL_SRTP_DIR}" != "x"; then
+      if test -d ${OPENSSL_SRTP_DIR}/lib; then
+        pbxlibdir="-L${OPENSSL_SRTP_DIR}/lib"
+      else
+        pbxlibdir="-L${OPENSSL_SRTP_DIR}"
+      fi
+   fi
+   pbxfuncname="SSL_CTX_set_tlsext_use_srtp"
+   if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
+      AST_OPENSSL_SRTP_FOUND=yes
+   else
+      ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+      CFLAGS="${CFLAGS} "
+      as_ac_Lib=`$as_echo "ac_cv_lib_ssl_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lssl" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lssl... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lssl ${pbxlibdir} -lcrypto $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$as_ac_Lib=yes"
+else
+  eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+  AST_OPENSSL_SRTP_FOUND=yes
+else
+  AST_OPENSSL_SRTP_FOUND=no
+fi
+
+      CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+   fi
+
+   # now check for the header.
+   if test "${AST_OPENSSL_SRTP_FOUND}" = "yes"; then
+      OPENSSL_SRTP_LIB="${pbxlibdir} -lssl -lcrypto"
+      # if --with-OPENSSL_SRTP=DIR has been specified, use it.
+      if test "x${OPENSSL_SRTP_DIR}" != "x"; then
+         OPENSSL_SRTP_INCLUDE="-I${OPENSSL_SRTP_DIR}/include"
+      fi
+      OPENSSL_SRTP_INCLUDE="${OPENSSL_SRTP_INCLUDE} "
+      if test "xopenssl/ssl.h" = "x" ; then    # no header, assume found
+         OPENSSL_SRTP_HEADER_FOUND="1"
+      else                             # check for the header
+         ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+         CPPFLAGS="${CPPFLAGS} ${OPENSSL_SRTP_INCLUDE}"
+         ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_ssl_h" = xyes; then :
+  OPENSSL_SRTP_HEADER_FOUND=1
+else
+  OPENSSL_SRTP_HEADER_FOUND=0
+fi
+
+
+         CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+      fi
+      if test "x${OPENSSL_SRTP_HEADER_FOUND}" = "x0" ; then
+         OPENSSL_SRTP_LIB=""
+         OPENSSL_SRTP_INCLUDE=""
+      else
+         if test "x${pbxfuncname}" = "x" ; then                # only checking headers -> no library
+            OPENSSL_SRTP_LIB=""
+         fi
+         PBX_OPENSSL_SRTP=1
+         cat >>confdefs.h <<_ACEOF
+#define HAVE_OPENSSL_SRTP 1
+_ACEOF
+
+      fi
+   fi
+fi
+
+
 fi
 
 
index 5d56dfa..b6fe5ee 100644 (file)
@@ -387,6 +387,7 @@ AST_EXT_LIB_SETUP([COROSYNC], [Corosync], [cpg])
 AST_EXT_LIB_SETUP_OPTIONAL([COROSYNC_CFG_STATE_TRACK], [A callback only in corosync 1.x], [COROSYNC], [cfg])
 AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
 AST_EXT_LIB_SETUP([CRYPTO], [OpenSSL Cryptography], [crypto])
+AST_EXT_LIB_SETUP_OPTIONAL([OPENSSL_SRTP], [OpenSSL SRTP Extension Support], [CRYPTO], [crypto])
 AST_EXT_LIB_SETUP([DAHDI], [DAHDI], [dahdi])
 AST_EXT_LIB_SETUP([FFMPEG], [Ffmpeg and avcodec], [avcodec])
 AST_EXT_LIB_SETUP([GSM], [External GSM], [gsm], [, use 'internal' GSM otherwise])
@@ -2103,7 +2104,8 @@ fi
 
 if test "$PBX_OPENSSL" = "1";
 then
-    AST_CHECK_OSPTK([4], [0], [0])
+        AST_CHECK_OSPTK([4], [0], [0])
+        AST_EXT_LIB_CHECK([OPENSSL_SRTP], [ssl], [SSL_CTX_set_tlsext_use_srtp], [openssl/ssl.h], [-lcrypto])
 fi
 
 AST_EXT_LIB_CHECK([SRTP], [srtp], [srtp_init], [srtp/srtp.h])
index 9288cce..e6835a0 100644 (file)
 /* Define to 1 if you have the OpenSSL Secure Sockets Layer library. */
 #undef HAVE_OPENSSL
 
+/* Define to 1 if CRYPTO has the OpenSSL SRTP Extension Support feature. */
+#undef HAVE_OPENSSL_SRTP
+
 /* Define this to indicate the ${OSPTK_DESCRIP} library */
 #undef HAVE_OSPTK
 
index 9820e51..293a7a7 100644 (file)
@@ -353,6 +353,61 @@ struct ast_rtp_engine_ice {
        void (*ice_lite)(struct ast_rtp_instance *instance);
 };
 
+/*! \brief DTLS setup types */
+enum ast_rtp_dtls_setup {
+       AST_RTP_DTLS_SETUP_ACTIVE,   /*!< Endpoint is willing to inititate connections */
+       AST_RTP_DTLS_SETUP_PASSIVE,  /*!< Endpoint is willing to accept connections */
+       AST_RTP_DTLS_SETUP_ACTPASS,  /*!< Endpoint is willing to both accept and initiate connections */
+       AST_RTP_DTLS_SETUP_HOLDCONN, /*!< Endpoint does not want the connection to be established right now */
+};
+
+/*! \brief DTLS connection states */
+enum ast_rtp_dtls_connection {
+        AST_RTP_DTLS_CONNECTION_NEW,      /*!< Endpoint wants to use a new connection */
+       AST_RTP_DTLS_CONNECTION_EXISTING, /*!< Endpoint wishes to use existing connection */
+};
+
+/*! \brief DTLS fingerprint hashes */
+enum ast_rtp_dtls_hash {
+       AST_RTP_DTLS_HASH_SHA1, /*!< SHA-1 fingerprint hash */
+};
+
+/*! \brief DTLS configuration structure */
+struct ast_rtp_dtls_cfg {
+       unsigned int enabled:1;                /*!< Whether DTLS support is enabled or not */
+       unsigned int verify:1;                 /*!< Whether to request and verify a client certificate when acting as server */
+       unsigned int rekey;                    /*!< Interval at which to renegotiate and rekey - defaults to 0 (off) */
+       enum ast_rtp_dtls_setup default_setup; /*!< Default setup type to use for outgoing */
+       enum ast_srtp_suite suite;             /*!< Crypto suite in use */
+       char *certfile;                        /*!< Certificate file */
+       char *pvtfile;                         /*!< Private key file */
+       char *cipher;                          /*!< Cipher to use */
+       char *cafile;                          /*!< Certificate authority file */
+       char *capath;                          /*!< Path to certificate authority */
+};
+
+/*! \brief Structure that represents the optional DTLS SRTP support within an RTP engine */
+struct ast_rtp_engine_dtls {
+       /*! Set the configuration of the DTLS support on the instance */
+       int (*set_configuration)(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg);
+       /*! Get if the DTLS SRTP support is active or not */
+       int (*active)(struct ast_rtp_instance *instance);
+       /*! Stop and terminate DTLS SRTP support */
+       void (*stop)(struct ast_rtp_instance *instance);
+       /*! Reset the connection and start fresh */
+       void (*reset)(struct ast_rtp_instance *instance);
+       /*! Get the current connection state */
+       enum ast_rtp_dtls_connection (*get_connection)(struct ast_rtp_instance *instance);
+       /*! Get the current setup state */
+       enum ast_rtp_dtls_setup (*get_setup)(struct ast_rtp_instance *instance);
+       /*! Set the remote setup state */
+       void (*set_setup)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup);
+       /*! Set the remote fingerprint */
+       void (*set_fingerprint)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint);
+       /*! Get the local fingerprint */
+       const char *(*get_fingerprint)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash);
+};
+
 /*! Structure that represents an RTP stack (engine) */
 struct ast_rtp_engine {
        /*! Name of the RTP engine, used when explicitly requested */
@@ -426,6 +481,8 @@ struct ast_rtp_engine {
        int (*sendcng)(struct ast_rtp_instance *instance, int level);
        /*! Callback to pointer for optional ICE support */
        struct ast_rtp_engine_ice *ice;
+       /*! Callback to pointer for optional DTLS SRTP support */
+       struct ast_rtp_engine_dtls *dtls;
        /*! Linked list information */
        AST_RWLIST_ENTRY(ast_rtp_engine) entry;
 };
@@ -2014,6 +2071,43 @@ int ast_rtp_engine_unload_format(const struct ast_format *format);
  */
 struct ast_rtp_engine_ice *ast_rtp_instance_get_ice(struct ast_rtp_instance *instance);
 
+/*!
+ * \brief Obtain a pointer to the DTLS support present on an RTP instance
+ *
+ * \param instance the RTP instance
+ *
+ * \retval DTLS support if present
+ * \retval NULL if no DTLS support available
+ */
+struct ast_rtp_engine_dtls *ast_rtp_instance_get_dtls(struct ast_rtp_instance *instance);
+
+/*!
+ * \brief Parse DTLS related configuration options
+ *
+ * \param dtls_cfg a DTLS configuration structure
+ * \param name name of the configuration option
+ * \param value value of the configuration option
+ *
+ * \retval 0 if handled
+ * \retval -1 if not handled
+ */
+int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, const char *value);
+
+/*!
+ * \brief Copy contents of a DTLS configuration structure
+ *
+ * \param src_cfg source DTLS configuration structure
+ * \param dst_cfg destination DTLS configuration structure
+ */
+void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rtp_dtls_cfg *dst_cfg);
+
+/*!
+ * \brief Free contents of a DTLS configuration structure
+ *
+ * \param dtls_cfg a DTLS configuration structure
+ */
+void ast_rtp_dtls_cfg_free(struct ast_rtp_dtls_cfg *dtls_cfg);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
index 90e1abb..4bcdb13 100644 (file)
@@ -2058,6 +2058,74 @@ struct ast_rtp_engine_ice *ast_rtp_instance_get_ice(struct ast_rtp_instance *ins
        return instance->engine->ice;
 }
 
+struct ast_rtp_engine_dtls *ast_rtp_instance_get_dtls(struct ast_rtp_instance *instance)
+{
+       return instance->engine->dtls;
+}
+
+int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, const char *value)
+{
+       if (!strcasecmp(name, "dtlsenable")) {
+               dtls_cfg->enabled = ast_true(value) ? 1 : 0;
+       } else if (!strcasecmp(name, "dtlsverify")) {
+               dtls_cfg->verify = ast_true(value) ? 1 : 0;
+       } else if (!strcasecmp(name, "dtlsrekey")) {
+               if (sscanf(value, "%30u", &dtls_cfg->rekey) != 1) {
+                       return -1;
+               }
+       } else if (!strcasecmp(name, "dtlscertfile")) {
+               ast_free(dtls_cfg->certfile);
+               dtls_cfg->certfile = ast_strdup(value);
+       } else if (!strcasecmp(name, "dtlsprivatekey")) {
+               ast_free(dtls_cfg->pvtfile);
+               dtls_cfg->pvtfile = ast_strdup(value);
+       } else if (!strcasecmp(name, "dtlscipher")) {
+               ast_free(dtls_cfg->cipher);
+               dtls_cfg->cipher = ast_strdup(value);
+       } else if (!strcasecmp(name, "dtlscafile")) {
+               ast_free(dtls_cfg->cafile);
+               dtls_cfg->cafile = ast_strdup(value);
+       } else if (!strcasecmp(name, "dtlscapath") || !strcasecmp(name, "dtlscadir")) {
+               ast_free(dtls_cfg->capath);
+               dtls_cfg->capath = ast_strdup(value);
+       } else if (!strcasecmp(name, "dtlssetup")) {
+               if (!strcasecmp(value, "active")) {
+                       dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_ACTIVE;
+               } else if (!strcasecmp(value, "passive")) {
+                       dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_PASSIVE;
+               } else if (!strcasecmp(value, "actpass")) {
+                       dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_ACTPASS;
+               }
+       } else {
+               return -1;
+       }
+
+       return 0;
+}
+
+void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rtp_dtls_cfg *dst_cfg)
+{
+       dst_cfg->enabled = src_cfg->enabled;
+       dst_cfg->verify = src_cfg->verify;
+       dst_cfg->rekey = src_cfg->rekey;
+       dst_cfg->suite = src_cfg->suite;
+       dst_cfg->certfile = ast_strdup(src_cfg->certfile);
+       dst_cfg->pvtfile = ast_strdup(src_cfg->pvtfile);
+       dst_cfg->cipher = ast_strdup(src_cfg->cipher);
+       dst_cfg->cafile = ast_strdup(src_cfg->cafile);
+       dst_cfg->capath = ast_strdup(src_cfg->capath);
+       dst_cfg->default_setup = src_cfg->default_setup;
+}
+
+void ast_rtp_dtls_cfg_free(struct ast_rtp_dtls_cfg *dtls_cfg)
+{
+       ast_free(dtls_cfg->certfile);
+       ast_free(dtls_cfg->pvtfile);
+       ast_free(dtls_cfg->cipher);
+       ast_free(dtls_cfg->cafile);
+       ast_free(dtls_cfg->capath);
+}
+
 static void set_next_mime_type(const struct ast_format *format, int rtp_code, char *type, char *subtype, unsigned int sample_rate)
 {
        int x = mime_types_len;
index 9283f80..958d583 100644 (file)
@@ -40,6 +40,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <signal.h>
 #include <fcntl.h>
 
+#ifdef HAVE_OPENSSL_SRTP
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#endif
+
 /* Asterisk discourages the use of bzero in favor of memset, in fact if you try to use bzero it will tell you to use memset. As a result bzero has to be undefined
  * here since it is used internally by pjlib. The only other option would be to modify pjlib... which won't happen. */
 #undef bzero
@@ -94,6 +100,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #define DEFAULT_LEARNING_MIN_SEQUENTIAL 4
 
+#define SRTP_MASTER_KEY_LEN 16
+#define SRTP_MASTER_SALT_LEN 14
+#define SRTP_MASTER_LEN (SRTP_MASTER_KEY_LEN + SRTP_MASTER_SALT_LEN)
+
 enum strict_rtp_state {
        STRICT_RTP_OPEN = 0, /*! No RTP packets should be dropped, all sources accepted */
        STRICT_RTP_LEARN,    /*! Accept next packet as source */
@@ -104,6 +114,8 @@ enum strict_rtp_state {
 #define DEFAULT_ICESUPPORT 1
 
 extern struct ast_srtp_res *res_srtp;
+extern struct ast_srtp_policy_res *res_srtp_policy;
+
 static int dtmftimeout = DEFAULT_DTMF_TIMEOUT;
 
 static int rtpstart = DEFAULT_RTP_START;                       /*!< First port for RTP sessions (set in rtp.conf) */
@@ -253,6 +265,21 @@ struct ast_rtp {
 
        struct ao2_container *local_candidates;   /*!< The local ICE candidates */
        struct ao2_container *remote_candidates;  /*!< The remote ICE candidates */
+
+#ifdef HAVE_OPENSSL_SRTP
+       SSL_CTX *ssl_ctx; /*!< SSL context */
+       SSL *ssl;         /*!< SSL session */
+       BIO *read_bio;    /*!< Memory buffer for reading */
+       BIO *write_bio;   /*!< Memory buffer for writing */
+       enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */
+       enum ast_srtp_suite suite;   /*!< SRTP crypto suite */
+       char local_fingerprint[160]; /*!< Fingerprint of our certificate */
+       unsigned char remote_fingerprint[EVP_MAX_MD_SIZE]; /*!< Fingerprint of the peer certificate */
+       enum ast_rtp_dtls_connection connection; /*!< Whether this is a new or existing connection */
+       unsigned int dtls_failure:1; /*!< Failure occurred during DTLS negotiation */
+       unsigned int rekey; /*!< Interval at which to renegotiate and rekey */
+       int rekeyid; /*!< Scheduled item id for rekeying */
+#endif
 };
 
 /*!
@@ -359,6 +386,12 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance);
 static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc);
 static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
 
+#ifdef HAVE_OPENSSL_SRTP
+static int ast_rtp_activate(struct ast_rtp_instance *instance);
+#endif
+
+static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp);
+
 /*! \brief Destructor for locally created ICE candidates */
 static void ast_rtp_ice_candidate_destroy(void *obj)
 {
@@ -657,6 +690,305 @@ static struct ast_rtp_engine_ice ast_rtp_ice = {
        .ice_lite = ast_rtp_ice_lite,
 };
 
+#ifdef HAVE_OPENSSL_SRTP
+static void dtls_info_callback(const SSL *ssl, int where, int ret)
+{
+       struct ast_rtp *rtp = SSL_get_ex_data(ssl, 0);
+
+       /* We only care about alerts */
+       if (!(where & SSL_CB_ALERT)) {
+               return;
+       }
+
+       rtp->dtls_failure = 1;
+}
+
+static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       if (!dtls_cfg->enabled) {
+               return 0;
+       }
+
+       if (!ast_rtp_engine_srtp_is_registered()) {
+               return -1;
+       }
+
+       if (!(rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method()))) {
+               return -1;
+       }
+
+       SSL_CTX_set_verify(rtp->ssl_ctx, dtls_cfg->verify ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, NULL);
+
+       if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_80) {
+               SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_80");
+       } else if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_32) {
+               SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_32");
+       } else {
+               ast_log(LOG_ERROR, "Unsupported suite specified for DTLS-SRTP on RTP instance '%p'\n", instance);
+               goto error;
+       }
+
+       if (!ast_strlen_zero(dtls_cfg->certfile)) {
+               char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;
+               BIO *certbio;
+               X509 *cert;
+               unsigned int size, i;
+               unsigned char fingerprint[EVP_MAX_MD_SIZE];
+               char *local_fingerprint = rtp->local_fingerprint;
+
+               if (!SSL_CTX_use_certificate_file(rtp->ssl_ctx, dtls_cfg->certfile, SSL_FILETYPE_PEM)) {
+                       ast_log(LOG_ERROR, "Specified certificate file '%s' for RTP instance '%p' could not be used\n",
+                               dtls_cfg->certfile, instance);
+                       goto error;
+               }
+
+               if (!SSL_CTX_use_PrivateKey_file(rtp->ssl_ctx, private, SSL_FILETYPE_PEM) ||
+                   !SSL_CTX_check_private_key(rtp->ssl_ctx)) {
+                       ast_log(LOG_ERROR, "Specified private key file '%s' for RTP instance '%p' could not be used\n",
+                               private, instance);
+                       goto error;
+               }
+
+               if (!(certbio = BIO_new(BIO_s_file()))) {
+                       ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n",
+                               instance);
+                       goto error;
+               }
+
+               if (!BIO_read_filename(certbio, dtls_cfg->certfile) ||
+                   !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL)) ||
+                   !X509_digest(cert, EVP_sha1(), fingerprint, &size) ||
+                   !size) {
+                       ast_log(LOG_ERROR, "Could not produce fingerprint from certificate '%s' for RTP instance '%p'\n",
+                               dtls_cfg->certfile, instance);
+                       BIO_free_all(certbio);
+                       goto error;
+               }
+
+               for (i = 0; i < size; i++) {
+                       sprintf(local_fingerprint, "%.2X:", fingerprint[i]);
+                       local_fingerprint += 3;
+               }
+
+               *(local_fingerprint-1) = 0;
+
+               BIO_free_all(certbio);
+       }
+
+       if (!ast_strlen_zero(dtls_cfg->cipher)) {
+               if (!SSL_CTX_set_cipher_list(rtp->ssl_ctx, dtls_cfg->cipher)) {
+                       ast_log(LOG_ERROR, "Invalid cipher specified in cipher list '%s' for RTP instance '%p'\n",
+                               dtls_cfg->cipher, instance);
+                       goto error;
+               }
+       }
+
+       if (!ast_strlen_zero(dtls_cfg->cafile) || !ast_strlen_zero(dtls_cfg->capath)) {
+               if (!SSL_CTX_load_verify_locations(rtp->ssl_ctx, S_OR(dtls_cfg->cafile, NULL), S_OR(dtls_cfg->capath, NULL))) {
+                       ast_log(LOG_ERROR, "Invalid certificate authority file '%s' or path '%s' specified for RTP instance '%p'\n",
+                               S_OR(dtls_cfg->cafile, ""), S_OR(dtls_cfg->capath, ""), instance);
+                       goto error;
+               }
+       }
+
+       rtp->rekey = dtls_cfg->rekey;
+       rtp->dtls_setup = dtls_cfg->default_setup;
+       rtp->suite = dtls_cfg->suite;
+
+       if (!(rtp->ssl = SSL_new(rtp->ssl_ctx))) {
+               ast_log(LOG_ERROR, "Failed to allocate memory for SSL context on RTP instance '%p'\n",
+                       instance);
+               goto error;
+       }
+
+       SSL_set_ex_data(rtp->ssl, 0, rtp);
+       SSL_set_info_callback(rtp->ssl, dtls_info_callback);
+
+       if (!(rtp->read_bio = BIO_new(BIO_s_mem()))) {
+               ast_log(LOG_ERROR, "Failed to allocate memory for inbound SSL traffic on RTP instance '%p'\n",
+                       instance);
+               goto error;
+       }
+       BIO_set_mem_eof_return(rtp->read_bio, -1);
+
+       if (!(rtp->write_bio = BIO_new(BIO_s_mem()))) {
+               ast_log(LOG_ERROR, "Failed to allocate memory for outbound SSL traffic on RTP instance '%p'\n",
+                       instance);
+               goto error;
+       }
+       BIO_set_mem_eof_return(rtp->write_bio, -1);
+
+       SSL_set_bio(rtp->ssl, rtp->read_bio, rtp->write_bio);
+
+       if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
+               SSL_set_accept_state(rtp->ssl);
+       } else {
+               SSL_set_connect_state(rtp->ssl);
+       }
+
+       rtp->connection = AST_RTP_DTLS_CONNECTION_NEW;
+
+       return 0;
+
+error:
+       if (rtp->read_bio) {
+               BIO_free(rtp->read_bio);
+               rtp->read_bio = NULL;
+       }
+
+       if (rtp->write_bio) {
+               BIO_free(rtp->write_bio);
+               rtp->write_bio = NULL;
+       }
+
+       if (rtp->ssl) {
+               SSL_free(rtp->ssl);
+               rtp->ssl = NULL;
+       }
+
+       SSL_CTX_free(rtp->ssl_ctx);
+       rtp->ssl_ctx = NULL;
+
+       return -1;
+}
+
+static int ast_rtp_dtls_active(struct ast_rtp_instance *instance)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       return !rtp->ssl_ctx ? 0 : 1;
+}
+
+static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       if (rtp->ssl_ctx) {
+               SSL_CTX_free(rtp->ssl_ctx);
+               rtp->ssl_ctx = NULL;
+       }
+
+       if (rtp->ssl) {
+               SSL_free(rtp->ssl);
+               rtp->ssl = NULL;
+       }
+}
+
+static void ast_rtp_dtls_reset(struct ast_rtp_instance *instance)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       /* If the SSL session is not yet finalized don't bother resetting */
+       if (!SSL_is_init_finished(rtp->ssl)) {
+               return;
+       }
+
+       SSL_shutdown(rtp->ssl);
+       rtp->connection = AST_RTP_DTLS_CONNECTION_NEW;
+}
+
+static enum ast_rtp_dtls_connection ast_rtp_dtls_get_connection(struct ast_rtp_instance *instance)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       return rtp->connection;
+}
+
+static enum ast_rtp_dtls_setup ast_rtp_dtls_get_setup(struct ast_rtp_instance *instance)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       return rtp->dtls_setup;
+}
+
+static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       enum ast_rtp_dtls_setup old = rtp->dtls_setup;
+
+       switch (setup) {
+       case AST_RTP_DTLS_SETUP_ACTIVE:
+               rtp->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE;
+               break;
+       case AST_RTP_DTLS_SETUP_PASSIVE:
+               rtp->dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE;
+               break;
+       case AST_RTP_DTLS_SETUP_ACTPASS:
+               /* We can't respond to an actpass setup with actpass ourselves... so respond with active, as we can initiate connections */
+               if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) {
+                       rtp->dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE;
+               }
+               break;
+       case AST_RTP_DTLS_SETUP_HOLDCONN:
+               rtp->dtls_setup = AST_RTP_DTLS_SETUP_HOLDCONN;
+               break;
+       default:
+               /* This should never occur... if it does exit early as we don't know what state things are in */
+               return;
+       }
+
+       /* If the setup state did not change we go on as if nothing happened */
+       if (old == rtp->dtls_setup) {
+               return;
+       }
+
+       /* If they don't want us to establish a connection wait until later */
+       if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_HOLDCONN) {
+               return;
+       }
+
+       if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) {
+               SSL_set_connect_state(rtp->ssl);
+       } else if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
+               SSL_set_accept_state(rtp->ssl);
+       } else {
+               return;
+       }
+}
+
+static void ast_rtp_dtls_set_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint)
+{
+       char *tmp = ast_strdupa(fingerprint), *value;
+       int pos = 0;
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       if (hash != AST_RTP_DTLS_HASH_SHA1) {
+               return;
+       }
+
+       while ((value = strsep(&tmp, ":")) && (pos != (EVP_MAX_MD_SIZE - 1))) {
+               sscanf(value, "%02x", (unsigned int*)&rtp->remote_fingerprint[pos++]);
+       }
+}
+
+static const char *ast_rtp_dtls_get_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       if (hash != AST_RTP_DTLS_HASH_SHA1) {
+               return NULL;
+       }
+
+       return rtp->local_fingerprint;
+}
+
+/* DTLS RTP Engine interface declaration */
+static struct ast_rtp_engine_dtls ast_rtp_dtls = {
+       .set_configuration = ast_rtp_dtls_set_configuration,
+       .active = ast_rtp_dtls_active,
+       .stop = ast_rtp_dtls_stop,
+       .reset = ast_rtp_dtls_reset,
+       .get_connection = ast_rtp_dtls_get_connection,
+       .get_setup = ast_rtp_dtls_get_setup,
+       .set_setup = ast_rtp_dtls_set_setup,
+       .set_fingerprint = ast_rtp_dtls_set_fingerprint,
+       .get_fingerprint = ast_rtp_dtls_get_fingerprint,
+};
+
+#endif
+
 /* RTP Engine Declaration */
 static struct ast_rtp_engine asterisk_rtp_engine = {
        .name = "asterisk",
@@ -685,6 +1017,10 @@ static struct ast_rtp_engine asterisk_rtp_engine = {
        .qos = ast_rtp_qos_set,
        .sendcng = ast_rtp_sendcng,
        .ice = &ast_rtp_ice,
+#ifdef HAVE_OPENSSL_SRTP
+       .dtls = &ast_rtp_dtls,
+       .activate = ast_rtp_activate,
+#endif
 };
 
 static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len)
@@ -874,6 +1210,168 @@ static inline int rtcp_debug_test_addr(struct ast_sockaddr *addr)
        return 1;
 }
 
+#ifdef HAVE_OPENSSL_SRTP
+static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp)
+{
+       size_t pending = BIO_ctrl_pending(rtp->write_bio);
+
+       if (pending > 0) {
+               char outgoing[pending];
+               size_t out;
+               struct ast_sockaddr remote_address = { {0, } };
+               int ice;
+
+               ast_rtp_instance_get_remote_address(instance, &remote_address);
+
+               /* If we do not yet know an address to send this to defer it until we do */
+               if (ast_sockaddr_isnull(&remote_address)) {
+                       return;
+               }
+
+               out = BIO_read(rtp->write_bio, outgoing, sizeof(outgoing));
+
+               __rtp_sendto(instance, outgoing, out, 0, &remote_address, 0, &ice, 0);
+       }
+}
+
+static int dtls_srtp_renegotiate(const void *data)
+{
+       struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       SSL_renegotiate(rtp->ssl);
+       SSL_do_handshake(rtp->ssl);
+       dtls_srtp_check_pending(instance, rtp);
+
+       rtp->rekeyid = -1;
+       ao2_ref(instance, -1);
+
+       return 0;
+}
+
+static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance)
+{
+       unsigned char material[SRTP_MASTER_LEN * 2];
+       unsigned char *local_key, *local_salt, *remote_key, *remote_salt;
+       struct ast_srtp_policy *local_policy, *remote_policy = NULL;
+       struct ast_rtp_instance_stats stats = { 0, };
+
+       /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
+       if (SSL_CTX_get_verify_mode(rtp->ssl_ctx) != SSL_VERIFY_NONE) {
+               X509 *certificate;
+
+               if (!(certificate = SSL_get_peer_certificate(rtp->ssl))) {
+                       ast_log(LOG_WARNING, "No certificate was provided by the peer on RTP instance '%p'\n", instance);
+                       return -1;
+               }
+
+               /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
+               if (rtp->remote_fingerprint[0]) {
+                       unsigned char fingerprint[EVP_MAX_MD_SIZE];
+                       unsigned int size;
+
+                       if (!X509_digest(certificate, EVP_sha1(), fingerprint, &size) ||
+                           !size ||
+                           memcmp(fingerprint, rtp->remote_fingerprint, size)) {
+                               X509_free(certificate);
+                               ast_log(LOG_WARNING, "Fingerprint provided by remote party does not match that of peer certificate on RTP instance '%p'\n",
+                                       instance);
+                               return -1;
+                       }
+               }
+
+               X509_free(certificate);
+       }
+
+       /* Ensure that certificate verification was successful */
+       if (SSL_get_verify_result(rtp->ssl) != X509_V_OK) {
+               ast_log(LOG_WARNING, "Peer certificate on RTP instance '%p' failed verification test\n",
+                       instance);
+               return -1;
+       }
+
+       /* Produce key information and set up SRTP */
+       if (!SSL_export_keying_material(rtp->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
+               ast_log(LOG_WARNING, "Unable to extract SRTP keying material from DTLS-SRTP negotiation on RTP instance '%p'\n",
+                       instance);
+               return -1;
+       }
+
+       /* Whether we are acting as a server or client determines where the keys/salts are */
+       if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) {
+               local_key = material;
+               remote_key = local_key + SRTP_MASTER_KEY_LEN;
+               local_salt = remote_key + SRTP_MASTER_KEY_LEN;
+               remote_salt = local_salt + SRTP_MASTER_SALT_LEN;
+       } else {
+               remote_key = material;
+               local_key = remote_key + SRTP_MASTER_KEY_LEN;
+               remote_salt = local_key + SRTP_MASTER_KEY_LEN;
+               local_salt = remote_salt + SRTP_MASTER_SALT_LEN;
+       }
+
+       if (!(local_policy = res_srtp_policy->alloc())) {
+               return -1;
+       }
+
+       if (res_srtp_policy->set_master_key(local_policy, local_key, SRTP_MASTER_KEY_LEN, local_salt, SRTP_MASTER_SALT_LEN) < 0) {
+               ast_log(LOG_WARNING, "Could not set key/salt information on local policy of '%p' when setting up DTLS-SRTP\n", rtp);
+               goto error;
+       }
+
+       if (res_srtp_policy->set_suite(local_policy, rtp->suite)) {
+               ast_log(LOG_WARNING, "Could not set suite to '%d' on local policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp);
+               goto error;
+       }
+
+       if (ast_rtp_instance_get_stats(instance, &stats, AST_RTP_INSTANCE_STAT_LOCAL_SSRC)) {
+               goto error;
+       }
+
+       res_srtp_policy->set_ssrc(local_policy, stats.local_ssrc, 0);
+
+       if (!(remote_policy = res_srtp_policy->alloc())) {
+               goto error;
+       }
+
+       if (res_srtp_policy->set_master_key(remote_policy, remote_key, SRTP_MASTER_KEY_LEN, remote_salt, SRTP_MASTER_SALT_LEN) < 0) {
+               ast_log(LOG_WARNING, "Could not set key/salt information on remote policy of '%p' when setting up DTLS-SRTP\n", rtp);
+               goto error;
+       }
+
+       if (res_srtp_policy->set_suite(remote_policy, rtp->suite)) {
+               ast_log(LOG_WARNING, "Could not set suite to '%d' on remote policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp);
+               goto error;
+       }
+
+       res_srtp_policy->set_ssrc(remote_policy, 0, 1);
+
+       if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy)) {
+               ast_log(LOG_WARNING, "Could not set policies when setting up DTLS-SRTP on '%p'\n", rtp);
+               goto error;
+       }
+
+       if (rtp->rekey) {
+               ao2_ref(instance, +1);
+               if ((rtp->rekeyid = ast_sched_add(rtp->sched, rtp->rekey * 1000, dtls_srtp_renegotiate, instance)) < 0) {
+                       ao2_ref(instance, -1);
+                       goto error;
+               }
+       }
+
+       return 0;
+
+error:
+       res_srtp_policy->destroy(local_policy);
+
+       if (remote_policy) {
+               res_srtp_policy->destroy(remote_policy);
+       }
+
+       return -1;
+}
+#endif
+
 static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp)
 {
        int len;
@@ -884,6 +1382,56 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
           return len;
        }
 
+#ifdef HAVE_OPENSSL_SRTP
+       if (!rtcp) {
+               char *in = buf;
+
+               dtls_srtp_check_pending(instance, rtp);
+
+               /* If this is an SSL packet pass it to OpenSSL for processing */
+               if ((*in >= 20) && (*in <= 64)) {
+                       int res = 0;
+
+                       /* If no SSL session actually exists terminate things */
+                       if (!rtp->ssl) {
+                               ast_log(LOG_ERROR, "Received SSL traffic on RTP instance '%p' without an SSL session\n",
+                                       instance);
+                               return -1;
+                       }
+
+                       /* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */
+                       if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) {
+                               rtp->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE;
+                               SSL_set_accept_state(rtp->ssl);
+                       }
+
+                       dtls_srtp_check_pending(instance, rtp);
+
+                       BIO_write(rtp->read_bio, buf, len);
+
+                       len = SSL_read(rtp->ssl, buf, len);
+
+                       dtls_srtp_check_pending(instance, rtp);
+
+                       if (rtp->dtls_failure) {
+                               ast_log(LOG_ERROR, "DTLS failure occurred on RTP instance '%p', terminating\n",
+                                       instance);
+                               return -1;
+                       }
+
+                       if (SSL_is_init_finished(rtp->ssl)) {
+                               /* Any further connections will be existing since this is now established */
+                               rtp->connection = AST_RTP_DTLS_CONNECTION_EXISTING;
+
+                               /* Use the keying material to set up key/salt information */
+                               res = dtls_srtp_setup(rtp, srtp, instance);
+                       }
+
+                       return res;
+               }
+       }
+#endif
+
        if (rtp->ice) {
                pj_str_t combined = pj_str(ast_sockaddr_stringify(sa));
                pj_sockaddr address;
@@ -927,7 +1475,7 @@ static int rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t siz
        return __rtp_recvfrom(instance, buf, size, flags, sa, 0);
 }
 
-static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice)
+static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp)
 {
        int len = size;
        void *temp = buf;
@@ -936,7 +1484,7 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
 
        *ice = 0;
 
-       if (res_srtp && srtp && res_srtp->protect(srtp, &temp, &len, rtcp) < 0) {
+       if (use_srtp && res_srtp && srtp && res_srtp->protect(srtp, &temp, &len, rtcp) < 0) {
                return -1;
        }
 
@@ -954,12 +1502,12 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
 
 static int rtcp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)
 {
-       return __rtp_sendto(instance, buf, size, flags, sa, 1, ice);
+       return __rtp_sendto(instance, buf, size, flags, sa, 1, ice, 1);
 }
 
 static int rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)
 {
-       return __rtp_sendto(instance, buf, size, flags, sa, 0, ice);
+       return __rtp_sendto(instance, buf, size, flags, sa, 0, ice, 1);
 }
 
 static int rtp_get_rate(struct ast_format *format)
@@ -1223,6 +1771,10 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
        /* Record any information we may need */
        rtp->sched = sched;
 
+#ifdef HAVE_OPENSSL_SRTP
+       rtp->rekeyid = -1;
+#endif
+
        return 0;
 }
 
@@ -1285,6 +1837,18 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
                ao2_ref(rtp->remote_candidates, -1);
        }
 
+#ifdef HAVE_OPENSSL_SRTP
+       /* Destroy the SSL context if present */
+       if (rtp->ssl_ctx) {
+               SSL_CTX_free(rtp->ssl_ctx);
+       }
+
+       /* Destroy the SSL session if present */
+       if (rtp->ssl) {
+               SSL_free(rtp->ssl);
+       }
+#endif
+
        /* Destroy synchronization items */
        ast_mutex_destroy(&rtp->lock);
        ast_cond_destroy(&rtp->cond);
@@ -3507,6 +4071,10 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance)
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
        struct ast_sockaddr addr = { {0,} };
 
+#ifdef HAVE_OPENSSL_SRTP
+       AST_SCHED_DEL_UNREF(rtp->sched, rtp->rekeyid, ao2_ref(instance, -1));
+#endif
+
        if (rtp->rtcp && rtp->rtcp->schedid > 0) {
                if (!ast_sched_del(rtp->sched, rtp->rtcp->schedid)) {
                        /* successfully cancelled scheduler entry. */
@@ -3586,6 +4154,23 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level)
        return res;
 }
 
+#ifdef HAVE_OPENSSL_SRTP
+static int ast_rtp_activate(struct ast_rtp_instance *instance)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       if (!rtp->ssl) {
+               return 0;
+       }
+
+       SSL_do_handshake(rtp->ssl);
+
+       dtls_srtp_check_pending(instance, rtp);
+
+       return 0;
+}
+#endif
+
 static char *rtp_do_debug_ip(struct ast_cli_args *a)
 {
        char *arg = ast_strdupa(a->argv[4]);