Merged revisions 205804 via svnmerge from
authorDavid Vossel <dvossel@digium.com>
Fri, 10 Jul 2009 16:42:04 +0000 (16:42 +0000)
committerDavid Vossel <dvossel@digium.com>
Fri, 10 Jul 2009 16:42:04 +0000 (16:42 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
  r205804 | dvossel | 2009-07-10 11:23:59 -0500 (Fri, 10 Jul 2009) | 31 lines

  SIP registration auth loop caused by stale nonce

  If an endpoint sends two registration requests in a very short
  period of time with the same nonce, both receive 401 responses
  from Asterisk, each with a different nonce (the second 401
  containing the current nonce and the first one being stale).
  If the endpoint responds to the first 401, it does not match
  the current nonce so Asterisk sends a third 401 with a newly
  generated nonce (which updates the current nonce)... Now if
  the endpoint responds to the second 401, it does not match the
  current nonce either and Asterisk sends a fourth 401 with a
  newly generated nonce... This loop goes on and on.

  There appears to be a simple fix for this.  If the nonce from
  the request does not match our nonce, but is a good response
  to a previous nonce, instead of sending a 401 with a newly
  generated nonce, use the current one instead.  This breaks
  the loop as the nonce is not updated until a response is
  received. Additional logic has been added to make sure no
  nonce can be responded to twice though.

  (closes issue #15102)
  Reported by: Jamuel
  Patches:
        patch-bug_0015102 uploaded by Jamuel (license 809)
        nonce_sip.diff uploaded by dvossel (license 671)
  Tested by: Jamuel

  Review: https://reviewboard.asterisk.org/r/289/
........

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

channels/chan_sip.c

index ed12bf2..e81796a 100644 (file)
@@ -1809,6 +1809,7 @@ struct sip_pvt {
        struct ast_variable *notify_headers;    /*!< Custom notify type */
        struct sip_auth *peerauth;              /*!< Realm authentication */
        int noncecount;                         /*!< Nonce-count */
+       unsigned int stalenonce:1;      /*!< Marks the current nonce as responded too */
        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) */
@@ -12505,6 +12506,20 @@ static void build_route(struct sip_pvt *p, struct sip_request *req, int backward
                list_route(p->route);
 }
 
+/*! \brief builds the sip_pvt's randdata field which is used for the nonce
+ *  challenge.  When forceupdate is not set, the nonce is only updated if
+ *  the current one is stale.  In this case, a stalenonce is one which
+ *  has already received a response, if a nonce has not received a response
+ *  it is not always necessary or beneficial to create a new one. */
+
+static void set_nonce_randdata(struct sip_pvt *p, int forceupdate)
+{
+       if (p->stalenonce || forceupdate || ast_strlen_zero(p->randdata)) {
+               ast_string_field_build(p, randdata, "%08lx", ast_random());     /* Create nonce for challenge */
+               p->stalenonce = 0;
+       }
+}
+
 AST_THREADSTORAGE(check_auth_buf);
 #define CHECK_AUTH_BUF_INITLEN   256
 
@@ -12570,7 +12585,7 @@ static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *
                return AUTH_CHALLENGE_SENT;
        } else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
                /* We have no auth, so issue challenge and request authentication */
-               ast_string_field_build(p, randdata, "%08lx", ast_random());     /* Create nonce for challenge */
+               set_nonce_randdata(p, 1); /* Create nonce for challenge */
                transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
                /* Schedule auto destroy in 32 seconds */
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
@@ -12621,10 +12636,13 @@ static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *
                return AUTH_USERNAME_MISMATCH;
        }
 
-       /* Verify nonce from request matches our nonce.  If not, send 401 with new nonce */
-       if (strcasecmp(p->randdata, keys[K_NONCE].s)) { /* XXX it was 'n'casecmp ? */
+       /* Verify nonce from request matches our nonce, and the nonce has not already been responded to.
+        * If this check fails, send 401 with new nonce */
+       if (strcasecmp(p->randdata, keys[K_NONCE].s) || p->stalenonce) { /* XXX it was 'n'casecmp ? */
                wrongnonce = TRUE;
                usednonce = keys[K_NONCE].s;
+       } else {
+               p->stalenonce = 1; /* now, since the nonce has a response, mark it as stale so it can't be sent or responded to again */
        }
 
        if (!ast_strlen_zero(md5secret))
@@ -12655,14 +12673,14 @@ static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *
                        if (sipdebug)
                                ast_log(LOG_NOTICE, "Correct auth, but based on stale nonce received from '%s'\n", get_header(req, "To"));
                        /* We got working auth token, based on stale nonce . */
-                       ast_string_field_build(p, randdata, "%08lx", ast_random());
+                       set_nonce_randdata(p, 0);
                        transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, TRUE);
                } else {
                        /* Everything was wrong, so give the device one more try with a new challenge */
                        if (!req->ignore) {
                                if (sipdebug)
                                        ast_log(LOG_NOTICE, "Bad authentication received from '%s'\n", get_header(req, "To"));
-                               ast_string_field_build(p, randdata, "%08lx", ast_random());
+                               set_nonce_randdata(p, 1);
                        } else {
                                if (sipdebug)
                                        ast_log(LOG_NOTICE, "Duplicate authentication received from '%s'\n", get_header(req, "To"));
@@ -12795,7 +12813,7 @@ static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct
                return;
        } else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
                /* We have no auth, so issue challenge and request authentication */
-               ast_string_field_build(p, randdata, "%08lx", ast_random());     /* Create nonce for challenge */
+               set_nonce_randdata(p, 1);
                transmit_response_with_auth(p, response, req, p->randdata, 0, respheader, 0);
                /* Schedule auto destroy in 32 seconds */
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
@@ -12840,7 +12858,7 @@ static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct
        /* Verify nonce from request matches our nonce.  If not, send 401 with new nonce */
        if (strcasecmp(p->randdata, keys[K_NONCE].s)) {
                if (!req->ignore) {
-                       ast_string_field_build(p, randdata, "%08lx", ast_random());
+                       set_nonce_randdata(p, 1);
                }
                transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, FALSE);