Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.739 diff -u -r1.739 chan_sip.c --- channels/chan_sip.c 20 May 2005 03:14:41 -0000 1.739 +++ channels/chan_sip.c 24 May 2005 06:38:32 -0000 @@ -671,7 +671,7 @@ static struct ast_frame *sip_read(struct ast_channel *ast); static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req); static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans); -static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *rand, int reliable, char *header); +static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *rand, int reliable, char *header, int stale); static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, int reliable, int newbranch); static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int inc, int reliable, int newbranch); static int transmit_invite(struct sip_pvt *p, int sipmethod, int sendsdp, char *auth, char *authheader, char *vxml_url, char *distinctive_ring, char *osptoken, int addsipheaders, int init); @@ -3724,7 +3724,7 @@ } /* transmit_response_with_auth: Respond with authorization request */ -static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *randdata, int reliable, char *header) +static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *randdata, int reliable, char *header, int stale) { struct sip_request resp; char tmp[256]; @@ -3734,7 +3734,9 @@ ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq")); return -1; } - snprintf(tmp, sizeof(tmp), "Digest realm=\"%s\", nonce=\"%s\"", global_realm, randdata); + snprintf(tmp, sizeof(tmp), "Digest realm=\"%s\", nonce=\"%s\" %s", global_realm, randdata, stale?", stale=true":""); + /* Stale means that they sent us correct authentication, but + based it on an old challenge (nonce) */ respprep(&resp, p, msg, req); add_header(&resp, header, tmp); add_header(&resp, "Content-Length", "0"); @@ -5344,7 +5346,7 @@ if (!reliable) { /* Resend message if this was NOT a reliable delivery. Otherwise the retransmission should get it */ - transmit_response_with_auth(p, response, req, randdata, reliable, respheader); + transmit_response_with_auth(p, response, req, randdata, reliable, respheader, 0); /* Schedule auto destroy in 15 seconds */ sip_scheddestroy(p, 15000); } @@ -5352,7 +5354,7 @@ } } else if (ast_strlen_zero(randdata) || ast_strlen_zero(authtoken)) { snprintf(randdata, randlen, "%08x", rand()); - transmit_response_with_auth(p, response, req, randdata, reliable, respheader); + transmit_response_with_auth(p, response, req, randdata, reliable, respheader, 0); /* Schedule auto destroy in 15 seconds */ sip_scheddestroy(p, 15000); res = 1; @@ -5368,8 +5370,12 @@ char tmp[256] = ""; char *c; char *z; - char *response =""; + char *ua_hash =""; char *resp_uri =""; + char *nonce = ""; + char *digestusername = ""; + int wrongnonce = 0; + char *usednonce = randdata; /* Find their response among the mess that we'r sent for comparison */ ast_copy_string(tmp, authtoken, sizeof(tmp)); @@ -5382,12 +5388,12 @@ if (!strncasecmp(c, "response=", strlen("response="))) { c+= strlen("response="); if ((*c == '\"')) { - response=++c; + ua_hash=++c; if ((c = strchr(c,'\"'))) *c = '\0'; } else { - response=c; + ua_hash=c; if ((c = strchr(c,','))) *c = '\0'; } @@ -5404,32 +5410,86 @@ *c = '\0'; } + } else if (!strncasecmp(c, "username=", strlen("username="))) { + c+= strlen("username="); + if ((*c == '\"')) { + digestusername=++c; + if((c = strchr(c,'\"'))) + *c = '\0'; + } else { + digestusername=c; + if((c = strchr(c,','))) + *c = '\0'; + } + } else if (!strncasecmp(c, "nonce=", strlen("nonce="))) { + c+= strlen("nonce="); + if ((*c == '\"')) { + nonce=++c; + if ((c = strchr(c,'\"'))) + *c = '\0'; + } else { + nonce=c; + if ((c = strchr(c,','))) + *c = '\0'; + } + } else if ((z = strchr(c,' ')) || (z = strchr(c,','))) c=z; if (c) c++; } + /* Verify that digest username matches the username we auth as */ + if ( strcmp(username,digestusername) ) { + /* Oops, we're trying something here */ + return -2; + } + + /* Verify nonce from request matches our nonce. If not, send 401 with new nonce */ + if ( strncasecmp (randdata,nonce,randlen) ) { + wrongnonce = 1; + usednonce = nonce; + } + snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret); + if (!ast_strlen_zero(resp_uri)) snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text, resp_uri); else snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text, uri); + if (!ast_strlen_zero(md5secret)) - snprintf(a1_hash, sizeof(a1_hash), "%s", md5secret); + snprintf(a1_hash, sizeof(a1_hash), "%s", md5secret); else - ast_md5_hash(a1_hash, a1); + ast_md5_hash(a1_hash, a1); + ast_md5_hash(a2_hash, a2); - snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, randdata, a2_hash); + + snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash); ast_md5_hash(resp_hash, resp); - /* resp_hash now has the expected response, compare the two */ + if ( wrongnonce ) { + ast_log(LOG_NOTICE, "stale nonce received from '%s'\n", get_header(req, "To")); + + snprintf(randdata, randlen, "%08x", rand()); + if (ua_hash && !strncasecmp(ua_hash, resp_hash, strlen(resp_hash))) { + /* We got working auth token, based on stale nonce . */ + transmit_response_with_auth(p, response, req, randdata, reliable, respheader, 1); + } else { + /* Everything was wrong, so give the device one more try */ + transmit_response_with_auth(p, response, req, randdata, reliable, respheader, 0); + } - if (response && !strncasecmp(response, resp_hash, strlen(resp_hash))) { + /* Schedule auto destroy in 15 seconds */ + sip_scheddestroy(p, 15000); + return 1; + } + /* resp_hash now has the expected response, compare the two */ + if (ua_hash && !strncasecmp(ua_hash, resp_hash, strlen(resp_hash))) { /* Auth is OK */ res = 0; } - /* Assume success ;-) */ } + /* Failure */ return res; } @@ -5530,8 +5590,19 @@ if (!res) { ast_device_state_changed("SIP/%s", peer->name); } - if (res < 0) - transmit_response(p, "403 Forbidden", &p->initreq); + if (res < 0) { + switch (res) { + /* Wrong password in authentication. Go away, don't try again until you fixed it */ + case -1: transmit_response(p, "403 Forbidden", &p->initreq); + break; + /* Username and digest username does not match. + Asterisk uses the From: username for authentication. We need the + users to use the same authentication user name until we support + proper authentication by digest auth name */ + case -2: transmit_response(p, "403 Authentication user name does not match account name", &p->initreq); + break; + } + } if (peer) ASTOBJ_UNREF(peer,sip_destroy_peer); return res;