opensips/modules/uac/auth.c

Mon, 16 Jan 2012 22:56:52 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Mon, 16 Jan 2012 22:56:52 +0100
changeset 21
6bb708a2265f
parent 16
c5c55937e44c
permissions
-rw-r--r--

Import original vendor code for correction and improvement.

     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