opensips/modules/uac/auth.c

Wed, 10 Feb 2010 21:25:01 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 10 Feb 2010 21:25:01 +0100
changeset 18
8ec65b8f6e2c
parent 16
c5c55937e44c
permissions
-rw-r--r--

Extend uac_auth() of the UAC module to workaround CSEQ problems.
This logic is meant to complement that of changeset 17, which
added rich authentication credentials to the gw table and its
associated logic in the LCR module.

     1 /*
     2  * $Id: auth.c 5901 2009-07-21 07:45:05Z bogdan_iancu $
     3  *
     4  * Copyright (C) 2005 Voice Sistem SRL
     5  *
     6  * This file is part of opensips, a free SIP server.
     7  *
     8  * UAC OpenSIPS-module is free software; you can redistribute it and/or
     9  * modify it under the terms of the GNU General Public License
    10  * as published by the Free Software Foundation; either version 2
    11  * of the License, or (at your option) any later version.
    12  *
    13  * UAC OpenSIPS-module is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  *
    18  * You should have received a copy of the GNU General Public License
    19  * along with this program; if not, write to the Free Software
    20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    21  *
    22  *
    23  * History:
    24  * ---------
    25  *  2005-01-31  first version (ramona)
    26  *  2006-03-02  UAC authentication looks first in AVPs for credential (bogdan)
    27  *  2010-01-18  UAC replaces proxy-auth entries with new credentials (msvb)
    28  */
    31 #include <ctype.h>
    32 #include <string.h>
    34 #include "../../str.h"
    35 #include "../../dprint.h"
    36 #include "../../pvar.h"
    37 #include "../../data_lump.h"
    38 #include "../../mem/mem.h"
    39 #include "../tm/tm_load.h"
    41 #include "auth.h"
    42 #include "auth_alg.h"
    43 #include "auth_hdr.h"
    46 extern struct tm_binds uac_tmb;
    47 extern pv_spec_t auth_username_spec;
    48 extern pv_spec_t auth_realm_spec;
    49 extern pv_spec_t auth_password_spec;
    52 static struct uac_credential *crd_list = 0;
    55 #define  duplicate_str(_strd, _strs, _error) \
    56 	do { \
    57 		_strd.s = (char*)pkg_malloc(_strs.len); \
    58 		if (_strd.s==0) \
    59 		{ \
    60 			LM_ERR("no more pkg memory\n");\
    61 			goto _error; \
    62 		} \
    63 		memcpy( _strd.s, _strs.s, _strs.len); \
    64 		_strd.len = _strs.len; \
    65 	}while(0)
    68 #define WWW_AUTH_CODE       401
    69 #define WWW_AUTH_HDR        "WWW-Authenticate"
    70 #define WWW_AUTH_HDR_LEN    (sizeof(WWW_AUTH_HDR)-1)
    71 #define PROXY_AUTH_CODE     407
    72 #define PROXY_AUTH_HDR      "Proxy-Authenticate"
    73 #define PROXY_AUTH_HDR_LEN  (sizeof(PROXY_AUTH_HDR)-1)
    75 static str nc = {"00000001", 8};
    76 static str cnonce = {"o", 1};
    78 int has_credentials(void)
    79 {
    80 	return (crd_list!=0)?1:0;
    81 }
    83 void free_credential(struct uac_credential *crd)
    84 {
    85 	if (crd)
    86 	{
    87 		if (crd->realm.s)
    88 			pkg_free(crd->realm.s);
    89 		if (crd->user.s)
    90 			pkg_free(crd->user.s);
    91 		if (crd->passwd.s)
    92 			pkg_free(crd->passwd.s);
    93 		pkg_free(crd);
    94 	}
    95 }
    98 int add_credential( unsigned int type, void *val)
    99 {
   100 	struct uac_credential *crd;
   101 	char *p;
   102 	str foo;
   104 	p = (char*)val;
   105 	crd = 0;
   107 	if (p==0 || *p==0)
   108 		goto error;
   110 	crd = (struct uac_credential*)pkg_malloc(sizeof(struct uac_credential));
   111 	if (crd==0)
   112 	{
   113 		LM_ERR("no more pkg mem\n");
   114 		goto error;
   115 	}
   116 	memset( crd, 0, sizeof(struct uac_credential));
   118 	/*parse the user */
   119 	while (*p && isspace((int)*p)) p++;
   120 	foo.s = p;
   121 	while (*p && *p!=':' && !isspace((int)*p)) p++;
   122 	if (foo.s==p || *p==0)
   123 		/* missing or empty user */
   124 		goto parse_error;
   125 	foo.len = p - foo.s;
   126 	/* dulicate it */
   127 	duplicate_str( crd->user, foo, error);
   129 	/* parse the ':' separator */
   130 	while (*p && isspace((int)*p)) p++;
   131 	if (*p!=':')
   132 		goto parse_error;
   133 	p++;
   134 	while (*p && isspace((int)*p)) p++;
   135 	if (*p==0)
   136 		goto parse_error;
   138 	/*parse the realm */
   139 	while (*p && isspace((int)*p)) p++;
   140 	foo.s = p;
   141 	while (*p && *p!=':' && !isspace((int)*p)) p++;
   142 	if (foo.s==p || *p==0)
   143 		/* missing or empty realm */
   144 		goto parse_error;
   145 	foo.len = p - foo.s;
   146 	/* dulicate it */
   147 	duplicate_str( crd->realm, foo, error);
   149 	/* parse the ':' separator */
   150 	while (*p && isspace((int)*p)) p++;
   151 	if (*p!=':')
   152 		goto parse_error;
   153 	p++;
   154 	while (*p && isspace((int)*p)) p++;
   155 	if (*p==0)
   156 		goto parse_error;
   158 	/*parse the passwd */
   159 	while (*p && isspace((int)*p)) p++;
   160 	foo.s = p;
   161 	while (*p && !isspace((int)*p)) p++;
   162 	if (foo.s==p)
   163 		/* missing or empty passwd */
   164 		goto parse_error;
   165 	foo.len = p - foo.s;
   166 	/* dulicate it */
   167 	duplicate_str( crd->passwd, foo, error);
   169 	/* end of string */
   170 	while (*p && isspace((int)*p)) p++;
   171 	if (*p!=0)
   172 		goto parse_error;
   174 	/* link the new cred struct */
   175 	crd->next = crd_list;
   176 	crd_list = crd;
   178 	pkg_free(val);
   179 	return 0;
   180 parse_error:
   181 		LM_ERR("parse error in <%s> "
   182 		"around %ld\n", (char*)val, (long)(p-(char*)val));
   183 error:
   184 	if (crd)
   185 		free_credential(crd);
   186 	return -1;
   187 }
   190 void destroy_credentials(void)
   191 {
   192 	struct uac_credential *foo;
   194 	while (crd_list)
   195 	{
   196 		foo = crd_list;
   197 		crd_list = crd_list->next;
   198 		free_credential(foo);
   199 	}
   200 	crd_list = 0;
   201 }
   204 static inline struct hdr_field *get_autenticate_hdr(struct sip_msg *rpl,
   205 																int rpl_code)
   206 {
   207 	struct hdr_field *hdr;
   208 	str hdr_name;
   210 	/* what hdr should we look for */
   211 	if (rpl_code==WWW_AUTH_CODE)
   212 	{
   213 		hdr_name.s = WWW_AUTH_HDR;
   214 		hdr_name.len = WWW_AUTH_HDR_LEN;
   215 	} else if (rpl_code==PROXY_AUTH_CODE) {
   216 		hdr_name.s = PROXY_AUTH_HDR;
   217 		hdr_name.len = PROXY_AUTH_HDR_LEN;
   218 	} else {
   219 		LM_ERR("reply is not an "
   220 			"auth request\n");
   221 		goto error;
   222 	}
   224 	LM_DBG("looking for header \"%.*s\"\n",
   225 		hdr_name.len, hdr_name.s);
   227 	/* search the auth hdr, but first parse them all */
   228 	if (parse_headers( rpl, HDR_EOH_F, 0)<0)
   229 	{
   230 		LM_ERR("failed to parse reply\n");
   231 		goto error;
   232 	}
   233 	hdr = get_header_by_name( rpl , hdr_name.s, hdr_name.len);
   234 	if (hdr)
   235 		return hdr;
   237 	LM_ERR("reply has no "
   238 		"auth hdr (%.*s)\n", hdr_name.len, hdr_name.s);
   239 error:
   240 	return 0;
   241 }
   244 static inline struct uac_credential *lookup_realm( str *realm)
   245 {
   246 	struct uac_credential *crd;
   248 	for( crd=crd_list ; crd ; crd=crd->next )
   249 		if (realm->len==crd->realm.len &&
   250 		strncmp( realm->s, crd->realm.s, realm->len)==0 )
   251 			return crd;
   252 	return 0;
   253 }
   256 static inline struct uac_credential *get_avp_credential(struct sip_msg *msg,
   257 																str *realm)
   258 {
   259 	static struct uac_credential crd;
   260 	pv_value_t pv_val;
   262 	if(pv_get_spec_value( msg, &auth_realm_spec, &pv_val)!=0
   263 	|| pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
   264 		return 0;
   266 	crd.realm = pv_val.rs;
   267 	/* is it the domain we are looking for? */
   268 	if (realm->len!=crd.realm.len ||
   269 	strncmp( realm->s, crd.realm.s, realm->len)!=0 )
   270 		return 0;
   272 	/* get username and password */
   273 	if(pv_get_spec_value( msg, &auth_username_spec, &pv_val)!=0
   274 	|| pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
   275 		return 0;
   276 	crd.user = pv_val.rs;
   278 	if(pv_get_spec_value( msg, &auth_password_spec, &pv_val)!=0
   279 	|| pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
   280 		return 0;
   281 	crd.passwd = pv_val.rs;
   283 	return &crd;
   284 }
   287 static inline void do_uac_auth(struct sip_msg *req, str *uri,
   288 		struct uac_credential *crd, struct authenticate_body *auth,
   289 		HASHHEX response)
   290 {
   291 	HASHHEX ha1;
   292 	HASHHEX ha2;
   294 	if((auth->flags&QOP_AUTH) || (auth->flags&QOP_AUTH_INT))
   295 	{
   296 		/* if qop generate nonce-count and cnonce */
   297 		cnonce.s = int2str(core_hash(&auth->nonce, 0, 0),&cnonce.len);
   299 		/* do authentication */
   300 		uac_calc_HA1( crd, auth, &cnonce, ha1);
   301 		uac_calc_HA2( &req->first_line.u.request.method, uri,
   302 			auth, 0/*hentity*/, ha2 );
   304 		uac_calc_response( ha1, ha2, auth, &nc, &cnonce, response);
   305 		auth->nc = &nc;
   306 		auth->cnonce = &cnonce;
   307 	} else {
   308 		/* do authentication */
   309 		uac_calc_HA1( crd, auth, 0/*cnonce*/, ha1);
   310 		uac_calc_HA2( &req->first_line.u.request.method, uri,
   311 			auth, 0/*hentity*/, ha2 );
   313 		uac_calc_response( ha1, ha2, auth, 0/*nc*/, 0/*cnonce*/, response);
   314 	}
   315 }
   318 static inline int apply_urihdr_changes( struct sip_msg *req,
   319 													str *uri, str *hdr)
   320 {
   321 	struct lump* anchor;
   323 	/* add the uri - move it to branch directly FIXME (bogdan)*/
   324 	if (req->new_uri.s)
   325 	{
   326 		pkg_free(req->new_uri.s);
   327 		req->new_uri.len=0;
   328 	}
   329 	req->parsed_uri_ok=0;
   330 	req->new_uri.s = (char*)pkg_malloc(uri->len+1);
   331 	if (req->new_uri.s==0)
   332 	{
   333 		LM_ERR("no more pkg\n");
   334 		goto error;
   335 	}
   336 	memcpy( req->new_uri.s, uri->s, uri->len);
   337 	req->new_uri.s[uri->len]=0;
   338 	req->new_uri.len=uri->len;
   340 	/* add the header */
   341 	if (parse_headers(req, HDR_EOH_F, 0) == -1)
   342 	{
   343 		LM_ERR("failed to parse message\n");
   344 		goto error;
   345 	}
   347 	anchor = anchor_lump(req, req->unparsed - req->buf, 0, 0);
   348 	if (anchor==0)
   349 	{
   350 		LM_ERR("failed to get anchor\n");
   351 		goto error;
   352 	}
   354 	if (insert_new_lump_before(anchor, hdr->s, hdr->len, 0) == 0)
   355 	{
   356 		LM_ERR("faield to insert lump\n");
   357 		goto error;
   358 	}
   360 	return 0;
   361 error:
   362 	pkg_free( hdr->s );
   363 	return -1;
   364 }
   368 int uac_auth( struct sip_msg *msg)
   369 {
   370 	static struct authenticate_body auth;
   371 	struct uac_credential *crd;
   372 	int code, branch;
   373 	struct sip_msg *rpl;
   374 	struct cell *t;
   375 	struct hdr_field *hdr;
   376 	HASHHEX response;
   377 	str *new_hdr;
   379 	/* pretransact */
   380 	int nret = 0;
   381 	pv_value_t pv_val;
   382 	str *newuri = 0;
   383 	struct uac_credential *tst = 0;
   384 	struct hdr_field *tmp_hdr = 0;
   385 	struct hdr_field *del_hdr = 0;
   388 	/* Goes something like this...                          */
   389 	/* HA1 = echo -n 'username:realm:password' | md5sum     */
   390 	/*       echo -n 'itsme:mydom.com:stupidpass' | md5sum  */
   391 	/* HA2 = echo -n 'message:uri' | md5sum                 */
   392 	/*       echo -n 'INVITE:sip:danc@ing.fool.es' | md5sum */
   393 	/* Response = echo -n 'HA1:nonce:HA2' | md5sum          */
   394 	/* get transaction */
   395 	t = uac_tmb.t_gett();
   396 	if (t==T_UNDEFINED || t==T_NULL_CELL) {
   397 		/* begin without any transaction */
   398 		/* set relevant structure variables */
   399 		crd = 0;
   400 		crd = pkg_malloc(sizeof(struct uac_credential));
   401 		if (!crd) {
   402 			LM_ERR("no more pkg memory\n");
   403 			goto error;
   404 		}
   406 		/* set the realm from existing UAC message */
   407 		tmp_hdr = msg->proxy_auth;
   408 		del_hdr = 0;
   409 		while (tmp_hdr) {
   410 			crd->realm.s = strchr(strstr(tmp_hdr->body.s, "realm="), '"') + 1;
   411 			crd->realm.len = strchr(crd->realm.s, '"') - crd->realm.s;
   412 			if(pv_get_spec_value(msg, &auth_realm_spec, &pv_val)==0 \
   413 				&& pv_val.rs.len>0) /* ensure realm is the desired one */
   414 				if (strncmp(crd->realm.s, pv_val.rs.s, crd->realm.len)==0)
   415 					del_hdr = tmp_hdr;
   416 			tmp_hdr = tmp_hdr->sibling;
   417 		}
   418 		if (del_hdr)
   419 			crd->realm = pv_val.rs;	/* success */
   420 		else
   421 			nret++;					/* failure */
   423 		/* set username from new AVP proxy values */
   424 		if(pv_get_spec_value(msg, &auth_username_spec, &pv_val)!=0 \
   425 			|| pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
   426 			nret++; /* signal failure with nonzero value */
   427 		else
   428 			crd->user = pv_val.rs;
   430 		/* set password from new AVP proxy values */
   431 		if(pv_get_spec_value(msg, &auth_password_spec, &pv_val)!=0 \
   432 			|| pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
   433 			nret++; /* signal failure with nonzero value */
   434 		else
   435 			crd->passwd = pv_val.rs;
   437 		if (nret) { /* if not found, look into predefined credentials */
   438 			tst = lookup_realm(&crd->realm);
   440 			if (tst==0) { /* found? */
   441 				LM_DBG("no credential for realm \"%.*s\"\n", \
   442 					crd->realm.len, crd->realm.s);
   443 				pkg_free(crd);
   444 				goto error;
   445 			}
   447 			crd = tst; /* use predefined credentials */
   448 			/* set the realm from existing UAC message */
   449 			tmp_hdr = msg->proxy_auth;
   450 			del_hdr = 0;
   451 			while (tmp_hdr) {
   452 				if(pv_get_spec_value(msg, &auth_realm_spec, &pv_val)==0 \
   453 					&& pv_val.rs.len>0) /* ensure realm is the desired one */
   454 					if (strncmp(crd->realm.s, pv_val.rs.s, crd->realm.len)==0)
   455 						del_hdr = tmp_hdr;
   456 				tmp_hdr = tmp_hdr->sibling;
   457 			}
   458 			if (del_hdr == 0) { /* proxy-auth header matching realm not found */
   459 				LM_DBG("no credential for realm \"%.*s\"\n", \
   460 					crd->realm.len, crd->realm.s);
   461 				pkg_free(crd);
   462 				goto error;
   463 			}
   464 		}
   466 		/* set the uri from existing UAC message */
   467 		newuri = pkg_malloc(sizeof(str));
   468 		if (!newuri) {
   469 			LM_ERR("no more pkg memory\n");
   470 			goto error;
   471 		}
   472 		newuri->s = pkg_malloc(msg->new_uri.len);
   473 		if (!newuri->s) {
   474 			LM_ERR("no more pkg memory\n");
   475 			pkg_free(newuri);
   476 			goto error;
   477 		}
   478 		newuri->len = msg->new_uri.len;
   479 		strncpy(newuri->s, msg->new_uri.s, msg->new_uri.len);
   480 		if (!newuri->s) {
   481 			LM_DBG("failed to retrieve URI from UAC message\n");
   482 			pkg_free(newuri->s);
   483 			pkg_free(newuri);
   484 			goto error;
   485 		}
   487 		/* set the nonce from existing UAC message */
   488 		tmp_hdr = msg->proxy_auth;
   489 		auth.nonce.len = 0;
   490 		auth.nonce.s = 0;
   491 		while (tmp_hdr) {
   492 			if(pv_get_spec_value(msg, &auth_realm_spec, &pv_val)==0 \
   493 				&& pv_val.rs.len>0) /* ensure realm is the desired one */
   494 				if (strncmp(crd->realm.s, pv_val.rs.s, crd->realm.len)==0) {
   495 					auth.nonce.s = strchr(strstr(tmp_hdr->body.s, "nonce="), '"') + 1;
   496 					auth.nonce.len = strchr(auth.nonce.s, '"') - auth.nonce.s;
   497 				}
   498 			tmp_hdr = tmp_hdr->sibling;
   499 		}
   500 		if (auth.nonce.s == 0) {
   501 			LM_DBG("failed to retrieve nonce from UAC message\n");
   502 			pkg_free(crd);
   503 			goto error;
   504 		}
   506 		/* do authentication */
   507 		do_uac_auth(msg, newuri, crd, &auth, response);
   508 		if (response==0) {
   509 			LM_ERR("failed to calculate challenge response\n");
   510 			pkg_free(crd);
   511 			goto error;
   512 		}
   514 		/* build the authorization header */
   515 		new_hdr = build_authorization_hdr(407, newuri, crd, &auth, response);
   516 		if (new_hdr==0) {
   517 			LM_ERR("failed to build authorization hdr\n");
   518 			pkg_free(crd);
   519 			goto error;
   520 		}
   522 		/* remove the old proxy-auth header and relink message index    */
   523 		/* before updating the authorization credentials of the message */
   524 		if (del_hdr) { /* updated a record and must remove the old one  */
   525 			if (del_lump(msg, del_hdr->name.s - msg->buf, del_hdr->len, 0)==0) {
   526 				LM_ERR("can't remove credentials\n");
   527 				pkg_free(crd);
   528 				goto error;
   529 			}
   530 		}
   532 		/* so far, so good -> add the header and set the proper RURI */
   533 		if (apply_urihdr_changes(msg, newuri, new_hdr)<0)
   534 		{
   535 			LM_ERR("failed to apply changes\n");
   536 			pkg_free(crd);
   537 			goto error;
   538 		}
   540 		pkg_free(crd); /* finished calculating new response string, success */
   541 		return 0;
   542 	} /* if (t==T_UNDEFINED || t==T_NULL_CELL) */
   544 	/* begin with transaction reply */
   545 	/* get the selected branch */
   546 	branch = uac_tmb.t_get_picked();
   547 	if (branch<0) {
   548 		LM_CRIT("no picked branch (%d)\n",branch);
   549 		goto error;
   550 	}
   552 	rpl = t->uac[branch].reply;
   553 	code = t->uac[branch].last_received;
   554 	LM_DBG("picked reply is %p, code %d\n",rpl,code);
   556 	if (rpl==0)
   557 	{
   558 		LM_CRIT("empty reply on picked branch\n");
   559 		goto error;
   560 	}
   561 	if (rpl==FAKED_REPLY)
   562 	{
   563 		LM_ERR("cannot process a FAKED reply\n");
   564 		goto error;
   565 	}
   567 	hdr = get_autenticate_hdr( rpl, code);
   568 	if (hdr==0)
   569 	{
   570 		LM_ERR("failed to extract authenticate hdr\n");
   571 		goto error;
   572 	}
   574 	LM_DBG("header found; body=<%.*s>\n",
   575 		hdr->body.len, hdr->body.s);
   577 	if (parse_authenticate_body( &hdr->body, &auth)<0)
   578 	{
   579 		LM_ERR("failed to parse auth hdr body\n");
   580 		goto error;
   581 	}
   583 	/* can we authenticate this realm? */
   584 	crd = 0;
   585 	/* first look into AVP, if set */
   586 	if ( auth_realm_spec.type==PVT_AVP )
   587 		crd = get_avp_credential( msg, &auth.realm );
   588 	/* if not found, look into predefined credentials */
   589 	if (crd==0)
   590 		crd = lookup_realm( &auth.realm );
   591 	/* found? */
   592 	if (crd==0)
   593 	{
   594 		LM_DBG("no credential for realm \"%.*s\"\n",
   595 			auth.realm.len, auth.realm.s);
   596 		goto error;
   597 	}
   599 	/* do authentication */
   600 	do_uac_auth( msg, &t->uac[branch].uri, crd, &auth, response);
   602 	/* build the authorization header */
   603 	new_hdr = build_authorization_hdr( code, &t->uac[branch].uri,
   604 		crd, &auth, response);
   605 	if (new_hdr==0)
   606 	{
   607 		LM_ERR("failed to build authorization hdr\n");
   608 		goto error;
   609 	}
   611 	/* so far, so good -> add the header and set the proper RURI */
   612 	if ( apply_urihdr_changes( msg, &t->uac[branch].uri, new_hdr)<0 )
   613 	{
   614 		LM_ERR("failed to apply changes\n");
   615 		goto error;
   616 	}
   618 	/* increas the Cseq nr */
   621 	return 0;
   622 error:
   623 	return -1;
   624 }

mercurial