aboutsummaryrefslogtreecommitdiffstats
path: root/channels
diff options
context:
space:
mode:
authordvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2009-07-10 16:48:56 +0000
committerdvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2009-07-10 16:48:56 +0000
commit0c7b93faeadb2a54a5b3ee60c6784bf79fb4461e (patch)
tree8e1bd2bbc2976224b0a9a2a25c794606e5a09f74 /channels
parent227a24de8c54d6604b440841476c356dcd33e0c5 (diff)
Merged revisions 205840 via svnmerge from
https://origsvn.digium.com/svn/asterisk/trunk ................ r205840 | dvossel | 2009-07-10 11:42:04 -0500 (Fri, 10 Jul 2009) | 37 lines Merged revisions 205804 via svnmerge from 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: http://svn.digium.com/svn/asterisk/branches/1.6.0@205843 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels')
-rw-r--r--channels/chan_sip.c32
1 files changed, 25 insertions, 7 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 7d40f484f..be6a116fe 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1273,6 +1273,7 @@ struct sip_pvt {
int route_persistant; /*!< Is this the "real" route? */
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) */
@@ -10759,6 +10760,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
@@ -10824,7 +10839,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);
@@ -10875,10 +10890,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))
@@ -10909,14 +10927,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"));
@@ -11048,7 +11066,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);
@@ -11093,7 +11111,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);