Fix a variety of memory leaks
[asterisk/asterisk.git] / res / res_srtp.c
index 3e9525a..f651c40 100644 (file)
@@ -29,7 +29,8 @@
  */
 
 /*** MODULEINFO
  */
 
 /*** MODULEINFO
-         <depend>srtp</depend>
+       <depend>srtp</depend>
+       <support_level>core</support_level>
 ***/
 
 /* See https://wiki.asterisk.org/wiki/display/AST/Secure+Calling */
 ***/
 
 /* See https://wiki.asterisk.org/wiki/display/AST/Secure+Calling */
@@ -53,17 +54,21 @@ struct ast_srtp {
        srtp_t session;
        const struct ast_srtp_cb *cb;
        void *data;
        srtp_t session;
        const struct ast_srtp_cb *cb;
        void *data;
+       int warned;
        unsigned char buf[8192 + AST_FRIENDLY_OFFSET];
        unsigned char buf[8192 + AST_FRIENDLY_OFFSET];
+       unsigned char rtcpbuf[8192 + AST_FRIENDLY_OFFSET];
 };
 
 struct ast_srtp_policy {
        srtp_policy_t sp;
 };
 
 };
 
 struct ast_srtp_policy {
        srtp_policy_t sp;
 };
 
+/*! Tracks whether or not we've initialized the libsrtp library */
 static int g_initialized = 0;
 
 /* SRTP functions */
 static int ast_srtp_create(struct ast_srtp **srtp, struct ast_rtp_instance *rtp, struct ast_srtp_policy *policy);
 static int g_initialized = 0;
 
 /* SRTP functions */
 static int ast_srtp_create(struct ast_srtp **srtp, struct ast_rtp_instance *rtp, struct ast_srtp_policy *policy);
+static int ast_srtp_replace(struct ast_srtp **srtp, struct ast_rtp_instance *rtp, struct ast_srtp_policy *policy);
 static void ast_srtp_destroy(struct ast_srtp *srtp);
 static int ast_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *policy);
 static int ast_srtp_change_source(struct ast_srtp *srtp, unsigned int from_ssrc, unsigned int to_ssrc);
 static void ast_srtp_destroy(struct ast_srtp *srtp);
 static int ast_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *policy);
 static int ast_srtp_change_source(struct ast_srtp *srtp, unsigned int from_ssrc, unsigned int to_ssrc);
@@ -82,6 +87,7 @@ static void ast_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, unsigned lo
 
 static struct ast_srtp_res srtp_res = {
        .create = ast_srtp_create,
 
 static struct ast_srtp_res srtp_res = {
        .create = ast_srtp_create,
+       .replace = ast_srtp_replace,
        .destroy = ast_srtp_destroy,
        .add_stream = ast_srtp_add_stream,
        .change_source = ast_srtp_change_source,
        .destroy = ast_srtp_destroy,
        .add_stream = ast_srtp_add_stream,
        .change_source = ast_srtp_change_source,
@@ -178,6 +184,8 @@ static struct ast_srtp *res_srtp_new(void)
                ast_free(srtp);
                return NULL;
        }
                ast_free(srtp);
                return NULL;
        }
+       
+       srtp->warned = 1;
 
        return srtp;
 }
 
        return srtp;
 }
@@ -317,7 +325,7 @@ static int ast_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len, int rt
        int retry = 0;
        struct ast_rtp_instance_stats stats = {0,};
 
        int retry = 0;
        struct ast_rtp_instance_stats stats = {0,};
 
-       tryagain:
+tryagain:
 
        for (i = 0; i < 2; i++) {
                res = rtcp ? srtp_unprotect_rtcp(srtp->session, buf, len) : srtp_unprotect(srtp->session, buf, len);
 
        for (i = 0; i < 2; i++) {
                res = rtcp ? srtp_unprotect_rtcp(srtp->session, buf, len) : srtp_unprotect(srtp->session, buf, len);
@@ -338,50 +346,57 @@ static int ast_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len, int rt
        }
 
        if (retry == 0  && res == err_status_replay_old) {
        }
 
        if (retry == 0  && res == err_status_replay_old) {
-               ast_log(LOG_WARNING, "SRTP unprotect: %s\n", srtp_errstr(res));
+               ast_log(AST_LOG_NOTICE, "SRTP unprotect failed with %s, retrying\n", srtp_errstr(res));
 
                if (srtp->session) {
                        struct ast_srtp_policy *policy;
                        struct ao2_iterator it;
 
                if (srtp->session) {
                        struct ast_srtp_policy *policy;
                        struct ao2_iterator it;
-                       int policies_count = 0;
-                       
-                       // dealloc first
-                       ast_log(LOG_WARNING, "SRTP destroy before re-create\n");
+                       int policies_count;
+
+                       /* dealloc first */
+                       ast_debug(5, "SRTP destroy before re-create\n");
                        srtp_dealloc(srtp->session);
                        srtp_dealloc(srtp->session);
-                       
-                       // get the count
+
+                       /* get the count */
                        policies_count = ao2_container_count(srtp->policies);
                        policies_count = ao2_container_count(srtp->policies);
-                       
-                       // get the first to build up
+
+                       /* get the first to build up */
                        it = ao2_iterator_init(srtp->policies, 0);
                        policy = ao2_iterator_next(&it);
 
                        it = ao2_iterator_init(srtp->policies, 0);
                        policy = ao2_iterator_next(&it);
 
-                       ast_log(LOG_WARNING, "SRTP try to re-create\n");
-                       if (srtp_create(&srtp->session, &policy->sp) == err_status_ok) {
-                               ast_log(LOG_WARNING, "SRTP re-created with first policy\n");
-                               
-                               // unref first element
-                               ao2_t_ref(policy, -1, "Unreffing first policy for re-creating srtp session");
-                               
-                               // if we have more than one policy, add them afterwards 
-                               if (policies_count > 1) {
-                                       ast_log(LOG_WARNING, "Add all the other %d policies\n", policies_count-1);
-                                       while ((policy = ao2_iterator_next(&it))) {
-                                               srtp_add_stream(srtp->session, &policy->sp);
-                                               ao2_t_ref(policy, -1, "Unreffing n-th policy for re-creating srtp session");
+                       ast_debug(5, "SRTP try to re-create\n");
+                       if (policy) {
+                               if (srtp_create(&srtp->session, &policy->sp) == err_status_ok) {
+                                       ast_debug(5, "SRTP re-created with first policy\n");
+                                       ao2_t_ref(policy, -1, "Unreffing first policy for re-creating srtp session");
+
+                                       /* if we have more than one policy, add them */
+                                       if (policies_count > 1) {
+                                               ast_debug(5, "Add all the other %d policies\n",
+                                                       policies_count - 1);
+                                               while ((policy = ao2_iterator_next(&it))) {
+                                                       srtp_add_stream(srtp->session, &policy->sp);
+                                                       ao2_t_ref(policy, -1, "Unreffing n-th policy for re-creating srtp session");
+                                               }
                                        }
                                        }
+
+                                       retry++;
+                                       ao2_iterator_destroy(&it);
+                                       goto tryagain;
                                }
                                }
-                               
-                               retry++;
-                               ao2_iterator_destroy(&it);
-                               goto tryagain;
+                               ao2_t_ref(policy, -1, "Unreffing first policy after srtp_create failed");
                        }
                        ao2_iterator_destroy(&it);
                }
        }
 
        if (res != err_status_ok && res != err_status_replay_fail ) {
                        }
                        ao2_iterator_destroy(&it);
                }
        }
 
        if (res != err_status_ok && res != err_status_replay_fail ) {
-               ast_log(LOG_WARNING, "SRTP unprotect: %s\n", srtp_errstr(res));
+               if ((srtp->warned >= 10) && !((srtp->warned - 10) % 100)) {
+                       ast_log(AST_LOG_WARNING, "SRTP unprotect failed with: %s %d\n", srtp_errstr(res), srtp->warned);
+                       srtp->warned = 11;
+               } else {
+                       srtp->warned++;
+               }
                errno = EAGAIN;
                return -1;
        }
                errno = EAGAIN;
                return -1;
        }
@@ -392,19 +407,22 @@ static int ast_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len, int rt
 static int ast_srtp_protect(struct ast_srtp *srtp, void **buf, int *len, int rtcp)
 {
        int res;
 static int ast_srtp_protect(struct ast_srtp *srtp, void **buf, int *len, int rtcp)
 {
        int res;
+       unsigned char *localbuf;
 
        if ((*len + SRTP_MAX_TRAILER_LEN) > sizeof(srtp->buf)) {
                return -1;
        }
 
        if ((*len + SRTP_MAX_TRAILER_LEN) > sizeof(srtp->buf)) {
                return -1;
        }
+       
+       localbuf = rtcp ? srtp->rtcpbuf : srtp->buf;
 
 
-       memcpy(srtp->buf, *buf, *len);
+       memcpy(localbuf, *buf, *len);
 
 
-       if ((res = rtcp ? srtp_protect_rtcp(srtp->session, srtp->buf, len) : srtp_protect(srtp->session, srtp->buf, len)) != err_status_ok && res != err_status_replay_fail) {
+       if ((res = rtcp ? srtp_protect_rtcp(srtp->session, localbuf, len) : srtp_protect(srtp->session, localbuf, len)) != err_status_ok && res != err_status_replay_fail) {
                ast_log(LOG_WARNING, "SRTP protect: %s\n", srtp_errstr(res));
                return -1;
        }
 
                ast_log(LOG_WARNING, "SRTP protect: %s\n", srtp_errstr(res));
                return -1;
        }
 
-       *buf = srtp->buf;
+       *buf = localbuf;
        return *len;
 }
 
        return *len;
 }
 
@@ -415,12 +433,14 @@ static int ast_srtp_create(struct ast_srtp **srtp, struct ast_rtp_instance *rtp,
        if (!(temp = res_srtp_new())) {
                return -1;
        }
        if (!(temp = res_srtp_new())) {
                return -1;
        }
+       ast_module_ref(ast_module_info->self);
 
 
+       /* Any failures after this point can use ast_srtp_destroy to destroy the instance */
        if (srtp_create(&temp->session, &policy->sp) != err_status_ok) {
        if (srtp_create(&temp->session, &policy->sp) != err_status_ok) {
+               ast_srtp_destroy(temp);
                return -1;
        }
 
                return -1;
        }
 
-       ast_module_ref(ast_module_info->self);
        temp->rtp = rtp;
        *srtp = temp;
 
        temp->rtp = rtp;
        *srtp = temp;
 
@@ -429,6 +449,14 @@ static int ast_srtp_create(struct ast_srtp **srtp, struct ast_rtp_instance *rtp,
        return 0;
 }
 
        return 0;
 }
 
+static int ast_srtp_replace(struct ast_srtp **srtp, struct ast_rtp_instance *rtp, struct ast_srtp_policy *policy)
+{
+       if ((*srtp) != NULL) {
+               ast_srtp_destroy(*srtp);
+       }
+       return ast_srtp_create(srtp, rtp, policy);
+}
+
 static void ast_srtp_destroy(struct ast_srtp *srtp)
 {
        if (srtp->session) {
 static void ast_srtp_destroy(struct ast_srtp *srtp)
 {
        if (srtp->session) {
@@ -446,13 +474,28 @@ static int ast_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *po
 {
        struct ast_srtp_policy *match;
 
 {
        struct ast_srtp_policy *match;
 
+       /* For existing streams, replace if its an SSRC stream, or bail if its a wildcard */
        if ((match = find_policy(srtp, &policy->sp, OBJ_POINTER))) {
        if ((match = find_policy(srtp, &policy->sp, OBJ_POINTER))) {
-               ast_debug(3, "Policy already exists, not re-adding\n");
-               ao2_t_ref(match, -1, "Unreffing already existing policy");
-               return -1;
+               if (policy->sp.ssrc.type != ssrc_specific) {
+                       ast_log(AST_LOG_WARNING, "Cannot replace an existing wildcard policy\n");
+                       ao2_t_ref(match, -1, "Unreffing already existing policy");
+                       return -1;
+               } else {
+                       if (srtp_remove_stream(srtp->session, match->sp.ssrc.value) != err_status_ok) {
+                               ast_log(AST_LOG_WARNING, "Failed to remove SRTP stream for SSRC %d\n", match->sp.ssrc.value);
+                       }
+                       ao2_t_unlink(srtp->policies, match, "Remove existing match policy");
+                       ao2_t_ref(match, -1, "Unreffing already existing policy");
+               }
        }
 
        }
 
+       ast_debug(3, "Adding new policy for %s %d\n",
+               policy->sp.ssrc.type == ssrc_specific ? "SSRC" : "type",
+               policy->sp.ssrc.type == ssrc_specific ? policy->sp.ssrc.value : policy->sp.ssrc.type);
        if (srtp_add_stream(srtp->session, &policy->sp) != err_status_ok) {
        if (srtp_add_stream(srtp->session, &policy->sp) != err_status_ok) {
+               ast_log(AST_LOG_WARNING, "Failed to add SRTP stream for %s %d\n",
+                       policy->sp.ssrc.type == ssrc_specific ? "SSRC" : "type",
+                       policy->sp.ssrc.type == ssrc_specific ? policy->sp.ssrc.value : policy->sp.ssrc.type);
                return -1;
        }
 
                return -1;
        }
 
@@ -470,7 +513,7 @@ static int ast_srtp_change_source(struct ast_srtp *srtp, unsigned int from_ssrc,
        };
        err_status_t status;
 
        };
        err_status_t status;
 
-       /* If we find a mach, return and unlink it from the container so we
+       /* If we find a match, return and unlink it from the container so we
         * can change the SSRC (which is part of the hash) and then have
         * ast_srtp_add_stream link it back in if all is well */
        if ((match = find_policy(srtp, &sp, OBJ_POINTER | OBJ_UNLINK))) {
         * can change the SSRC (which is part of the hash) and then have
         * ast_srtp_add_stream link it back in if all is well */
        if ((match = find_policy(srtp, &sp, OBJ_POINTER | OBJ_UNLINK))) {
@@ -486,6 +529,13 @@ static int ast_srtp_change_source(struct ast_srtp *srtp, unsigned int from_ssrc,
        return 0;
 }
 
        return 0;
 }
 
+static void res_srtp_shutdown(void)
+{
+       srtp_install_event_handler(NULL);
+       ast_rtp_engine_unregister_srtp();
+       g_initialized = 0;
+}
+
 static int res_srtp_init(void)
 {
        if (g_initialized) {
 static int res_srtp_init(void)
 {
        if (g_initialized) {
@@ -493,12 +543,20 @@ static int res_srtp_init(void)
        }
 
        if (srtp_init() != err_status_ok) {
        }
 
        if (srtp_init() != err_status_ok) {
+               ast_log(AST_LOG_WARNING, "Failed to initialize libsrtp\n");
                return -1;
        }
 
        srtp_install_event_handler(srtp_event_cb);
 
                return -1;
        }
 
        srtp_install_event_handler(srtp_event_cb);
 
-       return ast_rtp_engine_register_srtp(&srtp_res, &policy_res);
+       if (ast_rtp_engine_register_srtp(&srtp_res, &policy_res)) {
+               ast_log(AST_LOG_WARNING, "Failed to register SRTP with rtp engine\n");
+               res_srtp_shutdown();
+               return -1;
+       }
+
+       g_initialized = 1;
+       return 0;
 }
 
 /*
 }
 
 /*
@@ -512,7 +570,7 @@ static int load_module(void)
 
 static int unload_module(void)
 {
 
 static int unload_module(void)
 {
-       ast_rtp_engine_unregister_srtp();
+       res_srtp_shutdown();
        return 0;
 }
 
        return 0;
 }