1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
Description: Possible resource leak on uncompleted re-invite transactions
Origin: http://downloads.asterisk.org/pub/security/AST-2012-010-1.8.diff
Bug-Debian: http://bugs.debian.org/680470
If Asterisk sends a re-invite and an endpoint responds to the re-invite
with a provisional response but never sends a final response, then the
SIP dialog structure is never freed and the RTP ports for the call are
never released. If an attacker has the ability to place a call, they
could create a denial of service by using all available RTP ports.
Adapted to 1.6.2.9 by Tzafrir Cohen <tzafrir@debian.org>
---
channels/chan_sip.c | 57 ++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 47 insertions(+), 10 deletions(-)
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index dc71ca0..321cffb 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1662,6 +1662,7 @@ struct sip_pvt {
struct sip_auth *peerauth; /*!< Realm authentication */
int noncecount; /*!< Nonce-count */
unsigned int stalenonce:1; /*!< Marks the current nonce as responded too */
+ unsigned int ongoing_reinvite:1; /*!< There is a reinvite in progress that might need to be cleaned up */
char lastmsg[256]; /*!< Last Message sent/received */
int amaflags; /*!< AMA Flags */
int pendinginvite; /*!< Any pending INVITE or state NOTIFY (in subscribe pvt's) ? (seqno of this) */
@@ -1674,6 +1675,7 @@ struct sip_pvt {
int initid; /*!< Auto-congest ID if appropriate (scheduler) */
int waitid; /*!< Wait ID for scheduler after 491 or other delays */
+ int reinviteid; /*!< Reinvite in case of provisional, but no final response */
int autokillid; /*!< Auto-kill ID (scheduler) */
int t38id; /*!< T.38 Response ID */
enum transfermodes allowtransfer; /*!< REFER: restriction scheme */
@@ -4081,7 +4083,7 @@ static int __sip_autodestruct(const void *data)
ast_debug(3, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>");
append_history(p, "ReliableXmit", "timeout");
if (sscanf(p->lastmsg, "Tx: %30s", method_str) == 1 || sscanf(p->lastmsg, "Rx: %30s", method_str) == 1) {
- if (method_match(SIP_CANCEL, method_str) || method_match(SIP_BYE, method_str)) {
+ if (p->ongoing_reinvite || method_match(SIP_CANCEL, method_str) || method_match(SIP_BYE, method_str)) {
pvt_set_needdestroy(p, "autodestruct");
}
}
@@ -6092,6 +6094,21 @@ static const char *hangup_cause2sip(int cause)
return 0;
}
+static int reinvite_timeout(const void *data)
+{
+ struct sip_pvt *dialog = (struct sip_pvt *) data;
+ struct ast_channel *owner;
+ sip_pvt_lock(dialog);
+ dialog->reinviteid = -1;
+ check_pendings(dialog);
+ owner = dialog->owner;
+ if (owner) {
+ ast_channel_free(owner);
+ }
+ sip_pvt_unlock(dialog);
+ dialog_unref(dialog, "unref for reinvite timeout");
+ return 0;
+}
/*! \brief sip_hangup: Hangup SIP call
* Part of PBX interface, called from ast_hangup */
@@ -6289,8 +6306,16 @@ static int sip_hangup(struct ast_channel *ast)
ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE);
AST_SCHED_DEL_UNREF(sched, p->waitid, dialog_unref(p, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
- if (sip_cancel_destroy(p))
+ if (sip_cancel_destroy(p)) {
ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
+ }
+ /* If we have an ongoing reinvite, there is a chance that we have gotten a provisional
+ * response, but something weird has happened and we will never receive a final response.
+ * So, just in case, check for pending actions after a bit of time to trigger the pending
+ * bye that we are setting above */
+ if (p->ongoing_reinvite && p->reinviteid < 0) {
+ p->reinviteid = ast_sched_add(sched, 32 * p->timer_t1, reinvite_timeout, dialog_ref(p, "ref for reinvite_timeout"));
+ }
}
}
}
@@ -7338,6 +7363,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
p->method = intended_method;
p->initid = -1;
p->waitid = -1;
+ p->reinviteid = -1;
p->autokillid = -1;
p->request_queue_sched_id = -1;
p->provisional_keepalive_sched_id = -1;
@@ -10715,7 +10741,7 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int old
initialize_initreq(p, &req);
p->lastinvite = p->ocseq;
ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Change direction of this dialog */
-
+ p->ongoing_reinvite = 1;
return send_request(p, &req, XMIT_CRITICAL, p->ocseq);
}
@@ -17736,17 +17762,20 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req)
static void check_pendings(struct sip_pvt *p)
{
if (ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
- /* if we can't BYE, then this is really a pending CANCEL */
- if (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA)
+ if (p->reinviteid > -1) {
+ /* Outstanding p->reinviteid timeout, so wait... */
+ return;
+ } else if (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA) {
+ /* if we can't BYE, then this is really a pending CANCEL */
transmit_request(p, SIP_CANCEL, p->lastinvite, XMIT_RELIABLE, FALSE);
/* Actually don't destroy us yet, wait for the 487 on our original
INVITE, but do set an autodestruct just in case we never get it. */
- else {
+ } else {
/* We have a pending outbound invite, don't send something
- new in-transaction */
- if (p->pendinginvite)
+ * new in-transaction, unless it is a pending reinvite, then
+ * by the time we are called here, we should probably just hang up. */
+ if (p->pendinginvite && !p->ongoing_reinvite)
return;
-
/* Perhaps there is an SD change INVITE outstanding */
transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE);
}
@@ -17821,9 +17850,17 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru
if (resp >= 300 && (p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA ))
p->invitestate = INV_COMPLETED;
+ if ((resp >= 200 && reinvite)) {
+ p->ongoing_reinvite = 0;
+ if (p->reinviteid > -1) {
+ AST_SCHED_DEL_UNREF(sched, p->reinviteid, dialog_unref(p, "unref dialog for reinvite timeout because of a final response"));
+ }
+ }
+
/* Final response, clear out pending invite */
- if ((resp == 200 || resp >= 300) && p->pendinginvite && seqno == p->pendinginvite)
+ if ((resp == 200 || resp >= 300) && p->pendinginvite && seqno == p->pendinginvite) {
p->pendinginvite = 0;
+ }
switch (resp) {
case 100: /* Trying */
--
1.7.10.4
|