Fix a variety of memory leaks
[asterisk/asterisk.git] / res / res_srtp.c
index f21ac0f..f651c40 100644 (file)
@@ -29,7 +29,8 @@
  */
 
 /*** MODULEINFO
-         <depend>srtp</depend>
+       <depend>srtp</depend>
+       <support_level>core</support_level>
 ***/
 
 /* 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;
+       int warned;
        unsigned char buf[8192 + AST_FRIENDLY_OFFSET];
+       unsigned char rtcpbuf[8192 + AST_FRIENDLY_OFFSET];
 };
 
 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 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);
@@ -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,
+       .replace = ast_srtp_replace,
        .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;
        }
+       
+       srtp->warned = 1;
 
        return srtp;
 }
@@ -314,8 +322,11 @@ static int ast_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len, int rt
 {
        int res = 0;
        int i;
+       int retry = 0;
        struct ast_rtp_instance_stats stats = {0,};
 
+tryagain:
+
        for (i = 0; i < 2; i++) {
                res = rtcp ? srtp_unprotect_rtcp(srtp->session, buf, len) : srtp_unprotect(srtp->session, buf, len);
                if (res != err_status_no_ctx) {
@@ -334,8 +345,58 @@ static int ast_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len, int rt
                }
        }
 
+       if (retry == 0  && res == err_status_replay_old) {
+               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;
+                       int policies_count;
+
+                       /* dealloc first */
+                       ast_debug(5, "SRTP destroy before re-create\n");
+                       srtp_dealloc(srtp->session);
+
+                       /* get the count */
+                       policies_count = ao2_container_count(srtp->policies);
+
+                       /* get the first to build up */
+                       it = ao2_iterator_init(srtp->policies, 0);
+                       policy = ao2_iterator_next(&it);
+
+                       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;
+                               }
+                               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 ) {
-               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;
        }
@@ -346,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;
+       unsigned char *localbuf;
 
        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;
        }
 
-       *buf = srtp->buf;
+       *buf = localbuf;
        return *len;
 }
 
@@ -369,12 +433,14 @@ static int ast_srtp_create(struct ast_srtp **srtp, struct ast_rtp_instance *rtp,
        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) {
+               ast_srtp_destroy(temp);
                return -1;
        }
 
-       ast_module_ref(ast_module_info->self);
        temp->rtp = rtp;
        *srtp = temp;
 
@@ -383,6 +449,14 @@ static int ast_srtp_create(struct ast_srtp **srtp, struct ast_rtp_instance *rtp,
        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) {
@@ -400,13 +474,28 @@ static int ast_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *po
 {
        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))) {
-               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) {
+               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;
        }
 
@@ -424,7 +513,7 @@ static int ast_srtp_change_source(struct ast_srtp *srtp, unsigned int from_ssrc,
        };
        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))) {
@@ -440,6 +529,13 @@ static int ast_srtp_change_source(struct ast_srtp *srtp, unsigned int from_ssrc,
        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) {
@@ -447,12 +543,20 @@ static int res_srtp_init(void)
        }
 
        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 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;
 }
 
 /*
@@ -466,7 +570,7 @@ static int load_module(void)
 
 static int unload_module(void)
 {
-       ast_rtp_engine_unregister_srtp();
+       res_srtp_shutdown();
        return 0;
 }