diff -u asterisk-1.4.21.2~dfsg/debian/changelog asterisk-1.4.21.2~dfsg/debian/changelog --- asterisk-1.4.21.2~dfsg/debian/changelog +++ asterisk-1.4.21.2~dfsg/debian/changelog @@ -1,3 +1,13 @@ +asterisk (1:1.4.21.2~dfsg-1ubuntu3.1) intrepid-security; urgency=low + + * SECURITY UPDATE: information leak in IAX2 authentication + - added debian/patches/CVE-2009-0041: Adjust chan_iax2.c to fix + information leak in IAX2 authentication. Based on upstream patch. + - CVE-2009-0041 + - AST-2009-001 + + -- Brian Thomason Wed, 07 Oct 2009 14:31:11 -0400 + asterisk (1:1.4.21.2~dfsg-1ubuntu3) intrepid; urgency=low * debian/asterisk.init: Fix status action so that it returns the diff -u asterisk-1.4.21.2~dfsg/debian/patches/series asterisk-1.4.21.2~dfsg/debian/patches/series --- asterisk-1.4.21.2~dfsg/debian/patches/series +++ asterisk-1.4.21.2~dfsg/debian/patches/series @@ -83,0 +84,3 @@ + +#security fixes +CVE-2009-0041 only in patch2: unchanged: --- asterisk-1.4.21.2~dfsg.orig/debian/patches/CVE-2009-0041 +++ asterisk-1.4.21.2~dfsg/debian/patches/CVE-2009-0041 @@ -0,0 +1,261 @@ +# +# Ubuntu: https://bugs.launchpad.net/bugs/345217 +# Upstream: http://downloads.asterisk.org/pub/security/AST-2009-001.html +# Patch: http://downloads.asterisk.org/pub/security/AST-2009-001-1.4.patch +# Description: Adjust chan_iax2.c to fix information leak in IAX2 authentication +# + +--- asterisk.orig/channels/chan_iax2.c 2009-10-07 14:27:53.000000000 -0400 ++++ asterisk/channels/chan_iax2.c 2009-10-07 14:28:16.000000000 -0400 +@@ -158,6 +158,7 @@ + static int authdebug = 1; + static int autokill = 0; + static int iaxcompat = 0; ++static int last_authmethod = 0; + + static int iaxdefaultdpcache=10 * 60; /* Cache dialplan entries for 10 minutes by default */ + +@@ -231,10 +232,9 @@ + static ast_cond_t sched_cond; + + enum { +- IAX_STATE_STARTED = (1 << 0), +- IAX_STATE_AUTHENTICATED = (1 << 1), +- IAX_STATE_TBD = (1 << 2), +- IAX_STATE_UNCHANGED = (1 << 3), ++ IAX_STATE_STARTED = (1 << 0), ++ IAX_STATE_AUTHENTICATED = (1 << 1), ++ IAX_STATE_TBD = (1 << 2), + } iax2_state; + + struct iax2_context { +@@ -514,7 +514,7 @@ + /*! The jitterbuffer */ + jitterbuf *jb; + /*! active jb read scheduler id */ +- int jbid; ++ int jbid; + /*! LAG */ + int lag; + /*! Error, as discovered by the manager */ +@@ -570,7 +570,8 @@ + AST_STRING_FIELD(mohinterpret); + AST_STRING_FIELD(mohsuggest); + ); +- ++ /*! AUTHREJ all AUTHREP frames */ ++ int authrej; + /*! permitted authentication methods */ + int authmethods; + /*! permitted encryption methods */ +@@ -5289,6 +5290,18 @@ + ast_string_field_set(iaxs[callno], secret, user->secret); + res = 0; + user = user_unref(user); ++ } else { ++ /* user was not found, but we should still fake an AUTHREQ. ++ * Set authmethods to the last known authmethod used by the system ++ * Set a fake secret, it's not looked at, just required to attempt authentication. ++ * Set authrej so the AUTHREP is rejected without even looking at its contents */ ++ iaxs[callno]->authmethods = last_authmethod ? last_authmethod : (IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT); ++ ast_string_field_set(iaxs[callno], secret, "badsecret"); ++ iaxs[callno]->authrej = 1; ++ if (!ast_strlen_zero(iaxs[callno]->username)) { ++ /* only send the AUTHREQ if a username was specified. */ ++ res = 0; ++ } + } + ast_set2_flag(iaxs[callno], iax2_getpeertrunk(*sin), IAX_TRUNK); + return res; +@@ -5395,6 +5408,9 @@ + .name = p->username, + }; + ++ if (p->authrej) { ++ return res; ++ } + user = ao2_find(users, &tmp_user, OBJ_POINTER); + if (user) { + if (ast_test_flag(p, IAX_MAXAUTHREQ)) { +@@ -5472,7 +5488,7 @@ + int expire = 0; + int res = -1; + +- ast_clear_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED | IAX_STATE_UNCHANGED); ++ ast_clear_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED); + /* iaxs[callno]->peer[0] = '\0'; not necc. any more-- stringfield is pre-inited to null string */ + if (ies->username) + ast_copy_string(peer, ies->username, sizeof(peer)); +@@ -5495,8 +5511,29 @@ + p = find_peer(peer, 1); + ast_mutex_lock(&iaxsl[callno]); + if (!p || !iaxs[callno]) { ++ if (iaxs[callno]) { ++ int plaintext = ((last_authmethod & IAX_AUTH_PLAINTEXT) | (iaxs[callno]->authmethods & IAX_AUTH_PLAINTEXT)); ++ ++ ast_string_field_set(iaxs[callno], secret, "badsecret"); ++ ++ /* An AUTHREQ must be sent in response to a REGREQ of an invalid peer unless ++ * 1. A challenge already exists indicating a AUTHREQ was already sent out. ++ * 2. A plaintext secret is present in ie as result of a previous AUTHREQ requesting it. ++ * 3. A plaintext secret is present in the ie and the last_authmethod used by a peer happened ++ * to be plaintext, indicating it is an authmethod used by other peers on the system. ++ * ++ * If none of these cases exist, res will be returned as 0 without authentication indicating ++ * an AUTHREQ needs to be sent out. */ ++ ++ if (ast_strlen_zero(iaxs[callno]->challenge) && ++ !(!ast_strlen_zero(secret) && plaintext)) { ++ /* by setting res to 0, an REGAUTH will be sent */ ++ res = 0; ++ } ++ } + if (authdebug && !p) + ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr)); ++ + goto return_unref; + } + +@@ -5511,8 +5548,6 @@ + ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name); + goto return_unref; + } +- if (!inaddrcmp(&p->addr, sin)) +- ast_set_flag(&iaxs[callno]->state, IAX_STATE_UNCHANGED); + ast_string_field_set(iaxs[callno], secret, p->secret); + ast_string_field_set(iaxs[callno], inkeys, p->inkeys); + /* Check secret against what we have on file */ +@@ -5528,7 +5563,7 @@ + if (key && !ast_check_signature(key, iaxs[callno]->challenge, rsasecret)) { + ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED); + break; +- } else if (!key) ++ } else if (!key) + ast_log(LOG_WARNING, "requested inkey '%s' does not exist\n", keyn); + keyn = strsep(&stringp, ":"); + } +@@ -5546,7 +5581,7 @@ + struct MD5Context md5; + unsigned char digest[16]; + char *tmppw, *stringp; +- ++ + tmppw = ast_strdupa(p->secret); + stringp = tmppw; + while((tmppw = strsep(&stringp, ";"))) { +@@ -5556,7 +5591,7 @@ + MD5Final(digest, &md5); + for (x=0;x<16;x++) + sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */ +- if (!strcasecmp(requeststr, md5secret)) ++ if (!strcasecmp(requeststr, md5secret)) + break; + } + if (tmppw) { +@@ -5574,24 +5609,25 @@ + goto return_unref; + } else + ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED); +- } else if (!ast_strlen_zero(md5secret) || !ast_strlen_zero(secret)) { +- if (authdebug) +- ast_log(LOG_NOTICE, "Inappropriate authentication received\n"); ++ } else if (!ast_strlen_zero(iaxs[callno]->challenge) && ast_strlen_zero(md5secret) && ast_strlen_zero(rsasecret)) { ++ /* if challenge has been sent, but no challenge response if given, reject. */ + goto return_unref; + } +- ast_string_field_set(iaxs[callno], peer, peer); +- /* Choose lowest expiry number */ +- if (expire && (expire < iaxs[callno]->expiry)) +- iaxs[callno]->expiry = expire; +- + ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */ + ++ /* either Authentication has taken place, or a REGAUTH must be sent before verifying registration */ + res = 0; +- + return_unref: ++ if (iaxs[callno]) { ++ ast_string_field_set(iaxs[callno], peer, peer); ++ } ++ /* Choose lowest expiry number */ ++ if (expire && (expire < iaxs[callno]->expiry)) { ++ iaxs[callno]->expiry = expire; ++ } ++ + if (p) + peer_unref(p); +- + return res; + } + +@@ -6260,24 +6296,33 @@ + struct iax2_peer *p; + char challenge[10]; + const char *peer_name; +- int res = -1; ++ int sentauthmethod; + + peer_name = ast_strdupa(iaxs[callno]->peer); + + /* SLD: third call to find_peer in registration */ + ast_mutex_unlock(&iaxsl[callno]); +- p = find_peer(peer_name, 1); ++ if ((p = find_peer(peer_name, 1))) { ++ last_authmethod = p->authmethods; ++ } ++ + ast_mutex_lock(&iaxsl[callno]); + if (!iaxs[callno]) + goto return_unref; ++ ++ memset(&ied, 0, sizeof(ied)); ++ /* The selection of which delayed reject is sent may leak information, ++ * if it sets a static response. For example, if a host is known to only ++ * use MD5 authentication, then an RSA response would indicate that the ++ * peer does not exist, and vice-versa. ++ * Therefore, we use whatever the last peer used (which may vary over the ++ * course of a server, which should leak minimal information). */ ++ sentauthmethod = p ? p->authmethods : last_authmethod ? last_authmethod : (IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT); + if (!p) { +- ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name); +- goto return_unref; ++ iaxs[callno]->authmethods = sentauthmethod; + } +- +- memset(&ied, 0, sizeof(ied)); +- iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods); +- if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) { ++ iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, sentauthmethod); ++ if (sentauthmethod & (IAX_AUTH_RSA | IAX_AUTH_MD5)) { + /* Build the challenge */ + snprintf(challenge, sizeof(challenge), "%d", (int)ast_random()); + ast_string_field_set(iaxs[callno], challenge, challenge); +@@ -6285,12 +6330,12 @@ + } + iax_ie_append_str(&ied, IAX_IE_USERNAME, peer_name); + +- res = 0; +- + return_unref: +- peer_unref(p); ++ if (p) { ++ peer_unref(p); ++ } + +- return res ? res : send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);; ++ return iaxs[callno] ? send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1) : -1; + } + + static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_in *sin) +@@ -8222,8 +8267,9 @@ + ast_mutex_unlock(&iaxsl[fr->callno]); + return 1; + } +- if ((ast_strlen_zero(iaxs[fr->callno]->secret) && ast_strlen_zero(iaxs[fr->callno]->inkeys)) || +- ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED | IAX_STATE_UNCHANGED)) { ++ if ((ast_strlen_zero(iaxs[fr->callno]->secret) && ast_strlen_zero(iaxs[fr->callno]->inkeys)) || ++ ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED)) { ++ + if (f.subclass == IAX_COMMAND_REGREL) + memset(&sin, 0, sizeof(sin)); + if (update_registry(&sin, fr->callno, ies.devicetype, fd, ies.refresh))