]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
IAX2 REGAUTH loop
authorDavid Vossel <dvossel@digium.com>
Fri, 15 May 2009 22:43:13 +0000 (22:43 +0000)
committerDavid Vossel <dvossel@digium.com>
Fri, 15 May 2009 22:43:13 +0000 (22:43 +0000)
IAX was not sending REGREJ to terminate invalid registrations.  Instead it sent another REGAUTH if the authentication challenge failed.  This caused a loop of REGREQ and REGAUTH frames.

(Related to Security fix AST-2009-001)

(closes issue #14867)
Reported by: aragon
Tested by: dvossel

(closes issue #14717)
Reported by: mobeck
Patches:
      regauth_loop_update_patch.diff uploaded by dvossel (license 671)
Tested by: dvossel

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

channels/chan_iax2.c

index da7f189617deaa5e2d6689283f7b12d1d482512f..bab2c0a38210b7950cd0b506fe3f780d2a829425 100644 (file)
@@ -229,10 +229,9 @@ AST_MUTEX_DEFINE_STATIC(sched_lock);
 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 {
@@ -520,7 +519,7 @@ struct chan_iax2_pvt {
        /*! The jitterbuffer */
         jitterbuf *jb;
        /*! active jb read scheduler id */
-        int jbid;                       
+        int jbid;
        /*! LAG */
        int lag;
        /*! Error, as discovered by the manager */
@@ -5736,7 +5735,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
        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));
@@ -5760,10 +5759,28 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
        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;
        }
 
@@ -5778,8 +5795,6 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
                        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 */
@@ -5795,7 +5810,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
                                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, ":");
                        }
@@ -5813,7 +5828,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
                struct MD5Context md5;
                unsigned char digest[16];
                char *tmppw, *stringp;
-               
+
                tmppw = ast_strdupa(p->secret);
                stringp = tmppw;
                while((tmppw = strsep(&stringp, ";"))) {
@@ -5823,7 +5838,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
                        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) {
@@ -5841,17 +5856,16 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
                        goto return_unref;
                } else
                        ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
-       } else if (!ast_strlen_zero(iaxs[callno]->secret) || !ast_strlen_zero(iaxs[callno]->inkeys)) {
-               if (authdebug &&
-                       ((!ast_strlen_zero(iaxs[callno]->secret) && (p->authmethods & IAX_AUTH_MD5) && !ast_strlen_zero(iaxs[callno]->challenge)) ||
-                        (!ast_strlen_zero(iaxs[callno]->inkeys) && (p->authmethods & IAX_AUTH_RSA) && !ast_strlen_zero(iaxs[callno]->challenge)))) {
-                       ast_log(LOG_NOTICE, "Inappropriate authentication received for '%s'\n", p->name);
-               } /* ELSE this is the first time through and no challenge exists, so it's not quite yet a failure. */
+       } 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_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);
 
@@ -5861,12 +5875,9 @@ return_unref:
                }
        }
 
-       res = 0;
-
        if (p) {
                peer_unref(p);
        }
-
        return res;
 }
 
@@ -6564,6 +6575,9 @@ static int registry_authrequest(int callno)
         * 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) {
+               iaxs[callno]->authmethods = sentauthmethod;
+       }
        iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, sentauthmethod);
        if (sentauthmethod & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
                /* Build the challenge */
@@ -8525,8 +8539,9 @@ retryowner2:
                                        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))