From ff23e0862d881ee74478dfcaa5d8fbb56dc875b0 Mon Sep 17 00:00:00 2001 From: Thomas Ries Date: Mon, 22 Mar 2004 18:44:34 +0000 Subject: [PATCH] - deal with wildcard Contact header for unREGISTER --- ChangeLog | 2 + src/register.c | 278 +++++++++++++++++++++++++++---------------------- src/security.c | 152 ++++++++++++++++++++++++--- 3 files changed, 293 insertions(+), 139 deletions(-) diff --git a/ChangeLog b/ChangeLog index e79745a..7d80bcb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,8 @@ 0.5.5 ===== + 22-Mar-2004: - deal with wildcard Contact header for unREGISTER 21-Mar-2004: - added ./autogen.sh + - security_check_sip: check existence of mandatory headers 19-Mar-2004: - proxy_rewrite_invitation_body: check success of starting RTP relay before rewriting SDP body - proxy_rewrite_invitation_body: don't fail on diff --git a/src/register.c b/src/register.c index 8e1b039..12f47de 100644 --- a/src/register.c +++ b/src/register.c @@ -171,7 +171,7 @@ int register_client(osip_message_t *my_msg, int force_lcl_masq) { int expires; time_t time_now; osip_uri_t *url1_to, *url1_contact; - osip_uri_t *url2_to, *url2_contact; + osip_uri_t *url2_to; osip_header_t *expires_hdr; osip_uri_param_t *expires_param=NULL; @@ -205,6 +205,9 @@ int register_client(osip_message_t *my_msg, int force_lcl_masq) { To: -> address to be registered Contact: -> host is reachable there + Note: in case of un-REGISTER, the contact header may + contain '*' only - which means "all registrations + made by this UA" => Mapping is To: <1--n> Contact @@ -246,134 +249,159 @@ int register_client(osip_message_t *my_msg, int force_lcl_masq) { (url1_contact->host) ? url1_contact->host : "*NULL*", expires); -/* Update registration. There are two possibilities: - * - already registered, then update the existing record - * - not registered, then create a new record - */ - j=-1; - for (i=0; i %s@%s at " - "slot=%i, exp=%li", - (url1_contact->username) ? url1_contact->username : "*NULL*", - (url1_contact->host) ? url1_contact->host : "*NULL*", - (url2_to->username) ? url2_to->username : "*NULL*", - (url2_to->host) ? url2_to->host : "*NULL*", - i, urlmap[i].expires-time_now); - break; - } - } - - if ( (j < 0) && (i >= URLMAP_SIZE) ) { - /* oops, no free entries left... */ - ERROR("URLMAP is full - registration failed"); - return STS_FAILURE; - } - - if (i >= URLMAP_SIZE) { - /* entry not existing, create new one */ - i=j; - - /* write entry */ - urlmap[i].active=1; - /* Contact: field */ - osip_uri_clone( ((osip_contact_t*)(my_msg->contacts->node->element))->url, - &urlmap[i].true_url); - /* To: field */ - osip_uri_clone( my_msg->to->url, - &urlmap[i].reg_url); - - DEBUGC(DBCLASS_REG,"create new entry for %s@%s <-> %s@%s at slot=%i", - (url1_contact->username) ? url1_contact->username : "*NULL*", - (url1_contact->host) ? url1_contact->host : "*NULL*", - (urlmap[i].reg_url->username) ? urlmap[i].reg_url->username : "*NULL*", - (urlmap[i].reg_url->host) ? urlmap[i].reg_url->host : "*NULL*", - i); - - /* - * try to figure out if we ought to do some masquerading - */ - osip_uri_clone( my_msg->to->url, - &urlmap[i].masq_url); - - n=configuration.mask_host.used; - if (n != configuration.masked_host.used) { - ERROR("# of mask_host is not equal to # of masked_host in config!"); - n=0; - } - - DEBUG("%i entries in MASK config table", n); - for (j=0; j [%s]",configuration.mask_host.string[j], - my_msg->to->url->host); - if (strcmp(configuration.mask_host.string[j], - my_msg->to->url->host)==0) - break; - } - if (jusername) ? url1_contact->username : "*NULL*", - (url1_contact->host) ? url1_contact->host : "*NULL*", - (url1_contact->username) ? url1_contact->username : "*NULL*", - configuration.masked_host.string[j]); - urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host, - strlen(configuration.masked_host.string[j])+1); - strcpy(urlmap[i].masq_url->host, configuration.masked_host.string[j]); - } - - /* - * for transparent proxying: force device to be masqueraded - * as with the outbound IP - */ - if (force_lcl_masq) { - struct in_addr addr; - char *addrstr; - sts = get_ip_by_ifname(configuration.outbound_if,&addr); - addrstr = utils_inet_ntoa(addr); - DEBUGC(DBCLASS_REG,"masquerading UA %s@%s local %s@%s", - (url1_contact->username) ? url1_contact->username : "*NULL*", - (url1_contact->host) ? url1_contact->host : "*NULL*", - (url1_contact->username) ? url1_contact->username : "*NULL*", - addrstr); - urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host, - strlen(addrstr)+1); - strcpy(urlmap[i].masq_url->host, addrstr); - } - - /* remember the VIA for later use */ -// osip_via_clone( ((osip_via_t*)(my_msg->vias->node->element)), -// &urlmap[i].via); - } else { /* if new entry */ /* - * Some phones (like BudgeTones *may* dynamically grab a SIP port - * so we might want to update the true_url and reg_url each time - * we get an REGISTER + * REGISTER */ - /* Contact: field */ - osip_uri_free(urlmap[i].true_url); - osip_uri_clone( ((osip_contact_t*)(my_msg->contacts->node->element))->url, - &urlmap[i].true_url); - /* To: field */ - osip_uri_free(urlmap[i].reg_url); - osip_uri_clone( my_msg->to->url, - &urlmap[i].reg_url); - } - /* give some safety margin for the next update */ - if (expires > 0) expires+=30; + if (expires > 0) { + /* Update registration. There are two possibilities: + * - already registered, then update the existing record + * - not registered, then create a new record + */ - /* update registration timeout */ - urlmap[i].expires=time_now+expires; + j=-1; + for (i=0; i %s@%s at " + "slot=%i, exp=%li", + (url1_contact->username) ? url1_contact->username : "*NULL*", + (url1_contact->host) ? url1_contact->host : "*NULL*", + (url2_to->username) ? url2_to->username : "*NULL*", + (url2_to->host) ? url2_to->host : "*NULL*", + i, urlmap[i].expires-time_now); + break; + } + } + + if ( (j < 0) && (i >= URLMAP_SIZE) ) { + /* oops, no free entries left... */ + ERROR("URLMAP is full - registration failed"); + return STS_FAILURE; + } + + if (i >= URLMAP_SIZE) { + /* entry not existing, create new one */ + i=j; + + /* write entry */ + urlmap[i].active=1; + /* Contact: field */ + osip_uri_clone( ((osip_contact_t*)(my_msg->contacts->node->element))->url, + &urlmap[i].true_url); + /* To: field */ + osip_uri_clone( my_msg->to->url, + &urlmap[i].reg_url); + + DEBUGC(DBCLASS_REG,"create new entry for %s@%s <-> %s@%s at slot=%i", + (url1_contact->username) ? url1_contact->username : "*NULL*", + (url1_contact->host) ? url1_contact->host : "*NULL*", + (urlmap[i].reg_url->username) ? urlmap[i].reg_url->username : "*NULL*", + (urlmap[i].reg_url->host) ? urlmap[i].reg_url->host : "*NULL*", + i); + + /* + * try to figure out if we ought to do some masquerading + */ + osip_uri_clone( my_msg->to->url, + &urlmap[i].masq_url); + + n=configuration.mask_host.used; + if (n != configuration.masked_host.used) { + ERROR("# of mask_host is not equal to # of masked_host in config!"); + n=0; + } + + DEBUG("%i entries in MASK config table", n); + for (j=0; j [%s]",configuration.mask_host.string[j], + my_msg->to->url->host); + if (strcmp(configuration.mask_host.string[j], + my_msg->to->url->host)==0) + break; + } + if (jusername) ? url1_contact->username : "*NULL*", + (url1_contact->host) ? url1_contact->host : "*NULL*", + (url1_contact->username) ? url1_contact->username : "*NULL*", + configuration.masked_host.string[j]); + urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host, + strlen(configuration.masked_host.string[j])+1); + strcpy(urlmap[i].masq_url->host, configuration.masked_host.string[j]); + } + + /* + * for transparent proxying: force device to be masqueraded + * as with the outbound IP + */ + if (force_lcl_masq) { + struct in_addr addr; + char *addrstr; + sts = get_ip_by_ifname(configuration.outbound_if,&addr); + addrstr = utils_inet_ntoa(addr); + DEBUGC(DBCLASS_REG,"masquerading UA %s@%s local %s@%s", + (url1_contact->username) ? url1_contact->username : "*NULL*", + (url1_contact->host) ? url1_contact->host : "*NULL*", + (url1_contact->username) ? url1_contact->username : "*NULL*", + addrstr); + urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host, + strlen(addrstr)+1); + strcpy(urlmap[i].masq_url->host, addrstr); + } + + } else { /* if new entry */ + /* + * Some phones (like BudgeTones *may* dynamically grab a SIP port + * so we might want to update the true_url and reg_url each time + * we get an REGISTER + */ + /* Contact: field */ + osip_uri_free(urlmap[i].true_url); + osip_uri_clone( ((osip_contact_t*)(my_msg->contacts->node->element))->url, + &urlmap[i].true_url); + /* To: field */ + osip_uri_free(urlmap[i].reg_url); + osip_uri_clone( my_msg->to->url, + &urlmap[i].reg_url); + } + /* give some safety margin for the next update */ + if (expires > 0) expires+=30; + + /* update registration timeout */ + urlmap[i].expires=time_now+expires; + + /* + * un-REGISTER + */ + } else { /* expires > 0 */ + /* + * Remove registration + * Siproxd will ALWAYS remove ALL bindings for a given + * address-of-record + */ + for (i=0; iusername) ? url2_to->username : "*NULL*", + (url2_to->host) ? url2_to->host : "*NULL*", i); + urlmap[i].expires=0; + break; + } + } + } return STS_SUCCESS; } diff --git a/src/security.c b/src/security.c index 63ed84c..5fca122 100644 --- a/src/security.c +++ b/src/security.c @@ -83,29 +83,153 @@ int security_check_sip(osip_message_t *sip){ } } + /* + * Check existence of mandatory headers + * - /* check for existing To: header */ - if ((sip->to==NULL)||(sip->to->url==NULL)||(sip->to->url->host==NULL)) { - ERROR("security check failed: NULL To Header"); +Rosenberg, et. al. Standards Track [Page 161] + +RFC 3261 SIP: Session Initiation Protocol June 2002 + + Header field where proxy ACK BYE CAN INV OPT REG + ___________________________________________________________ + Accept R - o - o m* o + Accept 2xx - - - o m* o + Accept 415 - c - c c c + Accept-Encoding R - o - o o o + Accept-Encoding 2xx - - - o m* o + Accept-Encoding 415 - c - c c c + Accept-Language R - o - o o o + Accept-Language 2xx - - - o m* o + Accept-Language 415 - c - c c c + Alert-Info R ar - - - o - - + Alert-Info 180 ar - - - o - - + Allow R - o - o o o + Allow 2xx - o - m* m* o + Allow r - o - o o o + Allow 405 - m - m m m + Authentication-Info 2xx - o - o o o + Authorization R o o o o o o + Call-ID c r m m m m m m + Call-Info ar - - - o o o + Contact R o - - m o o + Contact 1xx - - - o - - + Contact 2xx - - - m o o + Contact 3xx d - o - o o o + Contact 485 - o - o o o + Content-Disposition o o - o o o + Content-Encoding o o - o o o + Content-Language o o - o o o + Content-Length ar t t t t t t + Content-Type * * - * * * + CSeq c r m m m m m m + Date a o o o o o o + Error-Info 300-699 a - o o o o o + Expires - - - o - o + From c r m m m m m m + In-Reply-To R - - - o - - + Max-Forwards R amr m m m m m m + Min-Expires 423 - - - - - m + MIME-Version o o - o o o + Organization ar - - - o o o + + Table 2: Summary of header fields, A--O + + + + + + +Rosenberg, et. al. Standards Track [Page 162] + +RFC 3261 SIP: Session Initiation Protocol June 2002 + + + Header field where proxy ACK BYE CAN INV OPT REG + ___________________________________________________________________ + Priority R ar - - - o - - + Proxy-Authenticate 407 ar - m - m m m + Proxy-Authenticate 401 ar - o o o o o + Proxy-Authorization R dr o o - o o o + Proxy-Require R ar - o - o o o + Record-Route R ar o o o o o - + Record-Route 2xx,18x mr - o o o o - + Reply-To - - - o - - + Require ar - c - c c c + Retry-After 404,413,480,486 - o o o o o + 500,503 - o o o o o + 600,603 - o o o o o + Route R adr c c c c c c + Server r - o o o o o + Subject R - - - o - - + Supported R - o o m* o o + Supported 2xx - o o m* m* o + Timestamp o o o o o o + To c(1) r m m m m m m + Unsupported 420 - m - m m m + User-Agent o o o o o o + Via R amr m m m m m m + Via rc dr m m m m m m + Warning r - o o o o o + WWW-Authenticate 401 ar - m - m m m + WWW-Authenticate 407 ar - o - o o o + +*/ + + + /* + * => Mandatory for ALL requests and responses + * Call-ID c r m m m m m m + * CSeq c r m m m m m m + * From c r m m m m m m + * To c(1) r m m m m m m + * Via R amr m m m m m m + */ + + /* check for existing Call-ID header */ + if ((sip->call_id==NULL)|| + ((sip->call_id->number==NULL)&&(sip->call_id->host==NULL))) { + ERROR("security check failed: NULL Call-Id Header"); return STS_FAILURE; } - /* check for existing FROM */ - if ((sip->from==NULL)||(sip->from->url==NULL)||(sip->from->url->host==NULL)) { - ERROR("security check failed: NULL From Header"); - return STS_FAILURE; - } - - /* check for existing CSEQ header */ - if ((sip->cseq==NULL)||(sip->cseq->method==NULL)||(sip->cseq->number==NULL)) { + /* check for existing CSeq header */ + if ((sip->cseq==NULL)|| + (sip->cseq->method==NULL)||(sip->cseq->number==NULL)) { ERROR("security check failed: NULL CSeq Header"); return STS_FAILURE; } + /* check for existing To: header */ + if ((sip->to==NULL)|| + (sip->to->url==NULL)||(sip->to->url->host==NULL)) { + ERROR("security check failed: NULL To Header"); + return STS_FAILURE; + } -/* - check the RFC and implement tests for ALL mandatory headers here -*/ + /* check for existing From: header */ + if ((sip->from==NULL)|| + (sip->from->url==NULL)||(sip->from->url->host==NULL)) { + ERROR("security check failed: NULL From Header"); + return STS_FAILURE; + } + + /* check for existing Via: header list */ + if (sip->vias==NULL) { + ERROR("security check failed: No Via Headers"); + return STS_FAILURE; + } + + /* + * check for existing Contact: header + * according to RFC3261 not mandatory, but siproxd relies on it... + */ + if ((sip->contacts==NULL)|| + (sip->contacts->node==NULL)||(sip->contacts->node->element==NULL)|| + ((osip_contact_t*)(sip->contacts->node->element))->url==NULL) { + ERROR("security check failed: NULL Contact Header"); + return STS_FAILURE; + } /* TODO: still way to go here ... */