Index: chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.570 diff -u -r1.570 chan_sip.c --- chan_sip.c 18 Nov 2004 04:26:22 -0000 1.570 +++ chan_sip.c 19 Nov 2004 14:54:45 -0000 @@ -243,11 +243,14 @@ struct sip_history *next; }; +#define SIP_LEN_BUF_S 80 +#define SIP_LEN_BUF_L 256 +typedef enum { FALSE=0, TRUE=1 } BOOL; + /* SIP Authentication */ /* sip_pvt: PVT structures are used for each SIP conversation, ie. a call */ static struct sip_pvt { ast_mutex_t lock; /* Channel private lock */ char callid[80]; /* Global CallID */ - char randdata[80]; /* Random data */ unsigned int ocseq; /* Current outgoing seqno */ unsigned int icseq; /* Current incoming seqno */ unsigned int callgroup; /* Call group */ @@ -263,7 +266,6 @@ int noncodeccapability; int callingpres; /* Calling presentation */ int outgoing; /* Outgoing or incoming call? */ - int authtries; /* Times we've tried to authenticate */ int insecure; /* Don't check source port/ip */ int expiry; /* How long we take to expire */ int branch; /* One random number */ @@ -311,10 +313,20 @@ char fullcontact[128]; /* The Contact: that the UA registers with us */ char accountcode[20]; /* Account code */ char our_contact[256]; /* Our contact header */ + + int authtype; /* which method */ + char authalgorithm[SIP_LEN_BUF_S]; /* Authenication server URI */ + char authuri[SIP_LEN_BUF_S]; /* Authenication server URI */ + char authrealm[SIP_LEN_BUF_S]; /* Authorization realm */ + char nonce[SIP_LEN_BUF_S]; /* Authorization nonce */ + int noncecount; /* number of time nonce has been used */ + BOOL noncestale; + char opaque[SIP_LEN_BUF_S]; /* Opaque value */ + char qop[SIP_LEN_BUF_S]; /* Quality of Protection */ + int authtries; /* Times we've tried to authenticate */ + char randdata[SIP_LEN_BUF_S]; /* Random data */ + char realm[256]; /* Authorization realm */ - char nonce[256]; /* Authorization nonce */ - char opaque[256]; /* Opaque nonsense */ - char qop[80]; /* Quality of Protection, since SIP wasn't complicated enough yet. */ char domain[256]; /* Authorization nonce */ char lastmsg[256]; /* Last Message sent/received */ int amaflags; /* AMA Flags */ @@ -557,7 +569,7 @@ static struct sip_peer *temp_peer(char *name); static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *msg, int init); static void free_old_route(struct sip_route *route); -static int build_reply_digest(struct sip_pvt *p, char *orig_header, char *digest, int digest_len); +static void build_reply_digest(struct sip_pvt *p, char *orig_header, char *digest, int digest_len); static int update_user_counter(struct sip_pvt *fup, int event); static void prune_peers(void); static int sip_do_reload(void); @@ -6245,14 +6257,14 @@ return RESULT_SUCCESS; } -static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *digest, int digest_len); +static BOOL reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *digest, int digest_len); /*--- do_register_auth: Challenge for registration ---*/ static int do_register_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader) { char digest[1024]; p->authtries++; memset(digest,0,sizeof(digest)); - if (reply_digest(p,req, header, "REGISTER", digest, sizeof(digest))) { + if (!reply_digest(p,req, header, "REGISTER", digest, sizeof(digest))) { /* There's nothing to use for authentication */ return -1; } @@ -6264,156 +6276,195 @@ char digest[1024]; p->authtries++; memset(digest,0,sizeof(digest)); - if (reply_digest(p,req, header, msg, digest, sizeof(digest) )) { + if (!reply_digest(p,req, header, msg, digest, sizeof(digest) )) { /* No way to authenticate */ return -1; } return transmit_invite(p,msg,!strcasecmp(msg, "INVITE"),digest, respheader, NULL,NULL,NULL, init); } -/*--- reply_digest: reply to authentication for outbound registrations ---*/ -/* This is used for register= servers in sip.conf, SIP proxies we register - with for receiving calls from. */ -static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, char *orig_header, char *digest, int digest_len) { - - char tmp[512] = ""; - char *realm = ""; - char *nonce = ""; - char *domain = ""; - char *opaque = ""; - char *qop = ""; - char *c; - strncpy(tmp, get_header(req, header),sizeof(tmp) - 1); - if (ast_strlen_zero(tmp)) - return -1; - c = tmp; - c+=strlen("Digest "); + + +/* + * Construct the HTTP digest authentication reply + * cf. RFC 2617 + */ +static void +build_reply_digest(struct sip_pvt *p, char* method, char* digest, int digest_len) +{ + char* algorithm = "MD5"; /* auth algorithm, hardcoded */ + char a[SIP_LEN_BUF_L]; + char Ha1[SIP_LEN_BUF_L]; + char Ha2[SIP_LEN_BUF_L]; + char Hresp[SIP_LEN_BUF_L]; + char uri[SIP_LEN_BUF_L] = ""; + char cnonce[SIP_LEN_BUF_L]; + int n; + BOOL q = !ast_strlen_zero(p->qop) && !strcmp(p->qop, "auth"); + + /* build our uri */ + if (!strcmp(p->authuri, "/")) + p->authuri[0] = '\0'; + if (!ast_strlen_zero(p->authuri)) + strcpy(uri, p->authuri); + else if (!ast_strlen_zero(p->uri)) + strcpy(uri, p->uri); + else + sprintf(uri, "sip:%s@%s", p->username, ast_inet_ntoa(a, INET_ADDRSTRLEN, p->sa.sin_addr)); + + snprintf(cnonce, sizeof(cnonce), "%08x", rand()); + + /* Calculate SIP digest response */ + /* KD(H(a1):nonce:H(a2)) */ + + if (!ast_strlen_zero(p->peermd5secret)) { + strcpy(Ha1, p->peermd5secret); + } else { + snprintf(a, sizeof(a), "%s:%s:%s", p->authname, p->authrealm, p->peersecret); + md5_hash(Ha1, a); + } + + snprintf(a, sizeof(a), "%s:%s", method, uri); + md5_hash(Ha2, a); + + if (q) { + snprintf(a, sizeof(a), "%s:%s:%8.8x:%s:%s:%s", Ha1, p->nonce, ++p->noncecount, cnonce, p->qop, Ha2); + } else { + snprintf(a, sizeof(a), "%s:%s:%s", Ha1, p->nonce, Ha2); + } + md5_hash(Hresp, a); + + n = snprintf(digest, digest_len, + "Digest username=\"%s\", realm=\"%s\", algorithm=%s, uri=\"%s\", nonce=\"%s\", response=\"%s\"", + p->authname, p->authrealm, algorithm, uri, p->nonce, Hresp); + + if (q) { + n += snprintf(digest + n, digest_len - n, ", qop=\"%s\", cnonce=\"%s\", nc=%8.8x", p->qop, cnonce, p->noncecount); + } + + if (!ast_strlen_zero(p->opaque)) + n += snprintf(digest + n, digest_len - n, ", opaque=\"%s\"", p->opaque); + +} + +/* + * parse_parm: parse a string with syntax + * parameter="value" + * parameter=value + */ +static BOOL +parse_parm(char* parm, char* dest, char** buf) +{ + char* s, *e; + while (**buf && (**buf < 33)) *buf++; + if (!*buf || strncasecmp(*buf, parm, strlen(parm))) + return FALSE; + if (!(s = strchr(*buf, '='))) + return FALSE; + s++; + if (*s == '\"'){ /* we have quoted string */ + s++; + e = strchr(s, '\"'); + } else { + e = strchr(s, ','); + } + while (*s) { + *dest++ = *s++; + if (e && s==e) + break; + } + *dest = '\0'; + while (*s == ',' || *s == '\"' || *s == ' ') s++; + *buf = s; + return TRUE; +} + + +static BOOL +parse_auth_digest(struct sip_pvt *p, struct sip_request *req, char *header) +{ + char tmp[20] = ""; + char *c; + + c = header; + p->noncestale = FALSE; while (c) { while (*c && (*c < 33)) c++; if (!*c) break; - if (!strncasecmp(c,"realm=", strlen("realm="))) { - c+=strlen("realm="); - if ((*c == '\"')) { - realm=++c; - if ((c = strchr(c,'\"'))) - *c = '\0'; - } else { - realm = 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 (!strncasecmp(c, "opaque=", strlen("opaque="))) { - c+=strlen("opaque="); - if ((*c == '\"')) { - opaque=++c; - if ((c = strchr(c,'\"'))) - *c = '\0'; - } else { - opaque = c; - if ((c = strchr(c,','))) - *c = '\0'; - } - } else if (!strncasecmp(c, "qop=", strlen("qop="))) { - c+=strlen("qop="); - if ((*c == '\"')) { - qop=++c; - if ((c = strchr(c,'\"'))) - *c = '\0'; - } else { - qop = c; - if ((c = strchr(c,','))) - *c = '\0'; - } - } else if (!strncasecmp(c, "domain=", strlen("domain="))) { - c+=strlen("domain="); - if ((*c == '\"')) { - domain=++c; - if ((c = strchr(c,'\"'))) - *c = '\0'; - } else { - domain = c; - if ((c = strchr(c,','))) - *c = '\0'; - } - } else - c = strchr(c,','); + if (parse_parm("domain", p->authuri, &c)) { + continue; + } else if (parse_parm("realm", p->authrealm, &c)) { + continue; + } else if (parse_parm("nonce", p->nonce, &c)) { + continue; + } else if (parse_parm("opaque", p->opaque, &c)) { + continue; + } else if (parse_parm("qop", p->qop, &c)) { + continue; + } else if (parse_parm("algorithm", p->authalgorithm, &c)) { + continue; + } else if (parse_parm("stale", tmp, &c)) { + if (!strncasecmp(tmp, "TRUE", 4)) + p->noncestale = TRUE; + continue; + } else { + c = strchr(c, ','); + } if (c) c++; } - if (strlen(tmp) >= sizeof(tmp)) - ast_log(LOG_WARNING, "Buffer overflow detected! Please file a bug.\n"); - - /* copy realm and nonce for later authorization of CANCELs and BYEs */ - strncpy(p->realm, realm, sizeof(p->realm)-1); - strncpy(p->nonce, nonce, sizeof(p->nonce)-1); - strncpy(p->domain, domain, sizeof(p->domain)-1); - strncpy(p->opaque, opaque, sizeof(p->opaque)-1); - strncpy(p->qop, qop, sizeof(p->qop)-1); - build_reply_digest(p, orig_header, digest, digest_len); - return 0; + return TRUE; } -/*--- build_reply_digest: Build reply digest ---*/ -/* Build digest challenge for authentication of peers (for registration) - and users (for calls). Also used for authentication of CANCEL and BYE */ -static int build_reply_digest(struct sip_pvt *p, char* orig_header, char* digest, int digest_len) -{ - char a1[256]; - char a2[256]; - char a1_hash[256]; - char a2_hash[256]; - char resp[256]; - char resp_hash[256]; - char uri[256] = ""; - char cnonce[80]; - char iabuf[INET_ADDRSTRLEN]; - - if (!ast_strlen_zero(p->domain)) - strncpy(uri, p->domain, sizeof(uri) - 1); - else if (!ast_strlen_zero(p->uri)) - strncpy(uri, p->uri, sizeof(uri) - 1); - else - snprintf(uri, sizeof(uri), "sip:%s@%s",p->username, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr)); +enum { SIP_AUTH_NONE=0, + SIP_AUTH_BASIC, + SIP_AUTH_DIGEST, + SIP_AUTH_CERT +}; - snprintf(cnonce, sizeof(cnonce), "%08x", rand()); +static BOOL +sip_parse_authenticate(struct sip_pvt *p, struct sip_request *req, char *header) +{ + char tmp[20] = ""; + char *c; + BOOL res; - snprintf(a1,sizeof(a1),"%s:%s:%s",p->authname,p->realm,p->peersecret); - snprintf(a2,sizeof(a2),"%s:%s",orig_header,uri); - if (!ast_strlen_zero(p->peermd5secret)) - strncpy(a1_hash, p->peermd5secret, sizeof(a1_hash) - 1); - else - md5_hash(a1_hash,a1); - md5_hash(a2_hash,a2); - /* XXX We hard code the nonce-number to 1... What are the odds? Are we seriously going to keep - track of every nonce we've seen? Also we hard code to "auth"... XXX */ - if (!ast_strlen_zero(p->qop)) - snprintf(resp,sizeof(resp),"%s:%s:%s:%s:%s:%s",a1_hash,p->nonce, "00000001", cnonce, "auth", a2_hash); - else - snprintf(resp,sizeof(resp),"%s:%s:%s",a1_hash,p->nonce,a2_hash); - md5_hash(resp_hash,resp); - /* XXX We hard code our qop to "auth" for now. XXX */ - if (!ast_strlen_zero(p->qop)) - snprintf(digest,digest_len,"Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\", qop=\"%s\", cnonce=\"%s\", nc=%s",p->authname,p->realm,uri,p->nonce,resp_hash, p->opaque, "auth", cnonce, "00000001"); - else - snprintf(digest,digest_len,"Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\"",p->authname,p->realm,uri,p->nonce,resp_hash, p->opaque); + c = get_header(req, header); + if (ast_strlen_zero(c)) + return FALSE; + + if (!strncmp(c, "Digest", 6)) { + p->authtype = SIP_AUTH_DIGEST; + res = parse_auth_digest(p, req, c + 7); +#if 0 + } else if (!strncmp(c, "Certificate", 11)) { + p->authtype = SIP_AUTH_CERT; + res = parse_auth_cert(p, req, c + 12); +#endif +#if 0 + } else if (!strncmp(c, "Basic", 5)) { + p->authtype = SIP_AUTH_BASIC; + res = parse_auth_digest(p, req, c + 6); +#endif + } else { + ast_log(LOG_WARNING, "%s: We don't support %s authentication\n", type, tmp); + return FALSE; + } + return res; +} - return 0; +static BOOL +reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, char *method, char *digest, int digest_len) +{ + if (!sip_parse_authenticate(p, req, header)) + return FALSE; + build_reply_digest(p, method, digest, digest_len); + return TRUE; } - +