1 /*
2  * Copyright (C) 2005 Voice Sistem SRL
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * UAC Kamailio-module is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * UAC Kamailio-module is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  *
20  */
21 
22 /*!
23  * \file
24  * \brief Kamailio uac :: Authentication
25  * \ingroup uac
26  * Module: \ref uac
27  */
28 
29 #include <ctype.h>
30 #include <string.h>
31 
32 #include "../../core/strutils.h"
33 #include "../../core/dprint.h"
34 #include "../../core/pvar.h"
35 #include "../../core/data_lump.h"
36 #include "../../core/mem/mem.h"
37 #include "../../core/hashes.h"
38 #include "../../core/dset.h"
39 #include "../../core/srapi.h"
40 #include "../../core/parser/parse_cseq.h"
41 #include "../../modules/tm/tm_load.h"
42 
43 #include "auth.h"
44 #include "auth_alg.h"
45 #include "auth_hdr.h"
46 
47 
48 extern struct tm_binds uac_tmb;
49 extern pv_spec_t auth_username_spec;
50 extern pv_spec_t auth_realm_spec;
51 extern pv_spec_t auth_password_spec;
52 
53 
54 static struct uac_credential *crd_list = 0;
55 
56 
57 #define  duplicate_str(_strd, _strs, _error) \
58 	do { \
59 		_strd.s = (char*)pkg_malloc(_strs.len); \
60 		if (_strd.s==0) \
61 		{ \
62 			PKG_MEM_ERROR;\
63 			goto _error; \
64 		} \
65 		memcpy( _strd.s, _strs.s, _strs.len); \
66 		_strd.len = _strs.len; \
67 	}while(0)
68 
69 
70 #define WWW_AUTH_CODE       401
71 #define WWW_AUTH_HDR        "WWW-Authenticate"
72 #define WWW_AUTH_HDR_LEN    (sizeof(WWW_AUTH_HDR)-1)
73 #define PROXY_AUTH_CODE     407
74 #define PROXY_AUTH_HDR      "Proxy-Authenticate"
75 #define PROXY_AUTH_HDR_LEN  (sizeof(PROXY_AUTH_HDR)-1)
76 
77 static str nc = {"00000001", 8};
78 static str cnonce = {"o", 1};
79 
has_credentials(void)80 int has_credentials(void)
81 {
82 	return (crd_list!=0)?1:0;
83 }
84 
free_credential(struct uac_credential * crd)85 void free_credential(struct uac_credential *crd)
86 {
87 	if (crd)
88 	{
89 		if (crd->realm.s)
90 			pkg_free(crd->realm.s);
91 		if (crd->user.s)
92 			pkg_free(crd->user.s);
93 		if (crd->passwd.s)
94 			pkg_free(crd->passwd.s);
95 		pkg_free(crd);
96 	}
97 }
98 
99 
add_credential(unsigned int type,void * val)100 int add_credential( unsigned int type, void *val)
101 {
102 	struct uac_credential *crd;
103 	char *p;
104 	str foo;
105 
106 	p = (char*)val;
107 	crd = 0;
108 
109 	if (p==0 || *p==0)
110 		goto error;
111 
112 	crd = (struct uac_credential*)pkg_malloc(sizeof(struct uac_credential));
113 	if (crd==0)
114 	{
115 		PKG_MEM_ERROR;
116 		goto error;
117 	}
118 	memset( crd, 0, sizeof(struct uac_credential));
119 
120 	/*parse the user */
121 	while (*p && isspace((int)*p)) p++;
122 	foo.s = p;
123 	while (*p && *p!=':' && !isspace((int)*p)) p++;
124 	if (foo.s==p || *p==0)
125 		/* missing or empty user */
126 		goto parse_error;
127 	foo.len = p - foo.s;
128 	/* dulicate it */
129 	duplicate_str( crd->user, foo, error);
130 
131 	/* parse the ':' separator */
132 	while (*p && isspace((int)*p)) p++;
133 	if (*p!=':')
134 		goto parse_error;
135 	p++;
136 	while (*p && isspace((int)*p)) p++;
137 	if (*p==0)
138 		goto parse_error;
139 
140 	/*parse the realm */
141 	while (*p && isspace((int)*p)) p++;
142 	foo.s = p;
143 	while (*p && *p!=':' && !isspace((int)*p)) p++;
144 	if (foo.s==p || *p==0)
145 		/* missing or empty realm */
146 		goto parse_error;
147 	foo.len = p - foo.s;
148 	/* dulicate it */
149 	duplicate_str( crd->realm, foo, error);
150 
151 	/* parse the ':' separator */
152 	while (*p && isspace((int)*p)) p++;
153 	if (*p!=':')
154 		goto parse_error;
155 	p++;
156 	while (*p && isspace((int)*p)) p++;
157 	if (*p==0)
158 		goto parse_error;
159 
160 	/*parse the passwd */
161 	while (*p && isspace((int)*p)) p++;
162 	foo.s = p;
163 	while (*p && !isspace((int)*p)) p++;
164 	if (foo.s==p)
165 		/* missing or empty passwd */
166 		goto parse_error;
167 	foo.len = p - foo.s;
168 	/* dulicate it */
169 	duplicate_str( crd->passwd, foo, error);
170 
171 	/* end of string */
172 	while (*p && isspace((int)*p)) p++;
173 	if (*p!=0)
174 		goto parse_error;
175 
176 	/* link the new cred struct */
177 	crd->next = crd_list;
178 	crd_list = crd;
179 
180 	pkg_free(val);
181 	return 0;
182 parse_error:
183 		LM_ERR("parse error in <%s> "
184 		"around %ld\n", (char*)val, (long)(p-(char*)val));
185 error:
186 	if (crd)
187 		free_credential(crd);
188 	return -1;
189 }
190 
191 
destroy_credentials(void)192 void destroy_credentials(void)
193 {
194 	struct uac_credential *foo;
195 
196 	while (crd_list)
197 	{
198 		foo = crd_list;
199 		crd_list = crd_list->next;
200 		free_credential(foo);
201 	}
202 	crd_list = 0;
203 }
204 
205 
get_autenticate_hdr(struct sip_msg * rpl,int rpl_code)206 struct hdr_field *get_autenticate_hdr(struct sip_msg *rpl, int rpl_code)
207 {
208 	struct hdr_field *hdr;
209 	str hdr_name;
210 
211 	/* what hdr should we look for */
212 	if (rpl_code==WWW_AUTH_CODE)
213 	{
214 		hdr_name.s = WWW_AUTH_HDR;
215 		hdr_name.len = WWW_AUTH_HDR_LEN;
216 	} else if (rpl_code==PROXY_AUTH_CODE) {
217 		hdr_name.s = PROXY_AUTH_HDR;
218 		hdr_name.len = PROXY_AUTH_HDR_LEN;
219 	} else {
220 		LM_ERR("reply is not an "
221 			"auth request\n");
222 		goto error;
223 	}
224 
225 	LM_DBG("looking for header \"%.*s\"\n",
226 		hdr_name.len, hdr_name.s);
227 
228 	/* search the auth hdr, but first parse them all */
229 	if (parse_headers( rpl, HDR_EOH_F, 0)<0)
230 	{
231 		LM_ERR("failed to parse reply\n");
232 		goto error;
233 	}
234 	for( hdr=rpl->headers ; hdr ; hdr=hdr->next )
235 	{
236 		if ( rpl_code==WWW_AUTH_CODE && hdr->type==HDR_WWW_AUTHENTICATE_T )
237 			return hdr;
238 		if ( rpl_code==PROXY_AUTH_CODE && hdr->type==HDR_PROXY_AUTHENTICATE_T )
239 			return hdr;
240 	}
241 
242 	LM_ERR("reply has no "
243 		"auth hdr (%.*s)\n", hdr_name.len, hdr_name.s);
244 error:
245 	return 0;
246 }
247 
248 
lookup_realm(str * realm)249 static inline struct uac_credential *lookup_realm( str *realm)
250 {
251 	struct uac_credential *crd;
252 
253 	for( crd=crd_list ; crd ; crd=crd->next )
254 		if (realm->len==crd->realm.len &&
255 		strncmp( realm->s, crd->realm.s, realm->len)==0 )
256 			return crd;
257 	return 0;
258 }
259 
260 
get_avp_credential(struct sip_msg * msg,str * realm)261 static inline struct uac_credential *get_avp_credential(struct sip_msg *msg, str *realm)
262 {
263 	static struct uac_credential crd;
264 	pv_value_t pv_val;
265 
266 	if(pv_get_spec_value( msg, &auth_realm_spec, &pv_val)!=0)
267 		return 0;
268 
269 	if (pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) {
270 		/* if realm parameter is empty or NULL, match any realm asked for */
271 		crd.realm = *realm;
272 	} else {
273 		crd.realm = pv_val.rs;
274 		/* is it the domain we are looking for? */
275 		if (realm->len!=crd.realm.len ||
276 		  strncmp( realm->s, crd.realm.s, realm->len)!=0 ) {
277 			return 0;
278 		}
279 	}
280 
281 	/* get username and password */
282 	if(pv_get_spec_value( msg, &auth_username_spec, &pv_val)!=0
283 	|| pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
284 		return 0;
285 	crd.user = pv_val.rs;
286 
287 	if(pv_get_spec_value( msg, &auth_password_spec, &pv_val)!=0
288 	|| pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
289 		return 0;
290 	crd.passwd = pv_val.rs;
291 
292 	return &crd;
293 }
294 
295 
do_uac_auth(str * method,str * uri,struct uac_credential * crd,struct authenticate_body * auth,HASHHEX response)296 void do_uac_auth(str *method, str *uri,
297 		struct uac_credential *crd,
298 		struct authenticate_body *auth,
299 		HASHHEX response)
300 {
301 	HASHHEX ha1;
302 	HASHHEX ha2;
303 
304 	if((auth->flags&QOP_AUTH) || (auth->flags&QOP_AUTH_INT))
305 	{
306 		/* if qop generate nonce-count and cnonce */
307 		cnonce.s = int2str(get_hash1_raw(auth->nonce.s, auth->nonce.len),
308 						   &cnonce.len);
309 
310 		/* do authentication */
311 		uac_calc_HA1( crd, auth, &cnonce, ha1);
312 		uac_calc_HA2( method, uri,
313 			auth, 0/*hentity*/, ha2 );
314 
315 		uac_calc_response( ha1, ha2, auth, &nc, &cnonce, response);
316 		auth->nc = &nc;
317 		auth->cnonce = &cnonce;
318 	} else {
319 		/* do authentication */
320 		uac_calc_HA1( crd, auth, 0/*cnonce*/, ha1);
321 		uac_calc_HA2( method, uri,
322 			auth, 0/*hentity*/, ha2 );
323 
324 		uac_calc_response( ha1, ha2, auth, 0/*nc*/, 0/*cnonce*/, response);
325 	}
326 }
327 
328 
apply_urihdr_changes(struct sip_msg * req,str * uri,str * hdr)329 static inline int apply_urihdr_changes( struct sip_msg *req,
330 													str *uri, str *hdr)
331 {
332 	struct lump* anchor;
333 
334 	/* add the uri - move it to branch directly FIXME (bogdan)*/
335 	if (req->new_uri.s)
336 	{
337 		pkg_free(req->new_uri.s);
338 		req->new_uri.len=0;
339 	}
340 	req->parsed_uri_ok=0;
341 	req->new_uri.s = (char*)pkg_malloc(uri->len+1);
342 	if (req->new_uri.s==0)
343 	{
344 		PKG_MEM_ERROR;
345 		goto error;
346 	}
347 	memcpy( req->new_uri.s, uri->s, uri->len);
348 	req->new_uri.s[uri->len]=0;
349 	req->new_uri.len=uri->len;
350 	ruri_mark_new();
351 
352 	/* add the header */
353 	if (parse_headers(req, HDR_EOH_F, 0) == -1)
354 	{
355 		LM_ERR("failed to parse message\n");
356 		goto error;
357 	}
358 
359 	anchor = anchor_lump(req, req->unparsed - req->buf, 0, 0);
360 	if (anchor==0)
361 	{
362 		LM_ERR("failed to get anchor\n");
363 		goto error;
364 	}
365 
366 	if (insert_new_lump_before(anchor, hdr->s, hdr->len, 0) == 0)
367 	{
368 		LM_ERR("faield to insert lump\n");
369 		goto error;
370 	}
371 
372 	return 0;
373 error:
374 	pkg_free( hdr->s );
375 	return -1;
376 }
377 
378 /**
379  *
380  */
uac_auth_mode(sip_msg_t * msg,int mode)381 int uac_auth_mode(sip_msg_t *msg, int mode)
382 {
383 	static struct authenticate_body auth;
384 	struct uac_credential *crd;
385 	int code, branch;
386 	struct sip_msg *rpl;
387 	struct cell *t;
388 	struct hdr_field *hdr;
389 	HASHHEX response;
390 	str *new_hdr;
391 	sr_cfgenv_t *cenv = NULL;
392 
393 	/* get transaction */
394 	t = uac_tmb.t_gett();
395 	if (t==T_UNDEFINED || t==T_NULL_CELL)
396 	{
397 		LM_CRIT("no current transaction found\n");
398 		goto error;
399 	}
400 
401 	/* get the selected branch */
402 	branch = uac_tmb.t_get_picked_branch();
403 	if (branch<0) {
404 		LM_CRIT("no picked branch (%d)\n",branch);
405 		goto error;
406 	}
407 
408 	rpl = t->uac[branch].reply;
409 	code = t->uac[branch].last_received;
410 	LM_DBG("picked reply is %p, code %d\n",rpl,code);
411 
412 	if (rpl==0)
413 	{
414 		LM_CRIT("empty reply on picked branch\n");
415 		goto error;
416 	}
417 	if (rpl==FAKED_REPLY)
418 	{
419 		LM_ERR("cannot process a FAKED reply\n");
420 		goto error;
421 	}
422 
423 	hdr = get_autenticate_hdr( rpl, code);
424 	if (hdr==0)
425 	{
426 		LM_ERR("failed to extract authenticate hdr\n");
427 		goto error;
428 	}
429 
430 	LM_DBG("header found; body=<%.*s>\n",
431 		hdr->body.len, hdr->body.s);
432 
433 	if (parse_authenticate_body( &hdr->body, &auth)<0)
434 	{
435 		LM_ERR("failed to parse auth hdr body\n");
436 		goto error;
437 	}
438 
439 	/* can we authenticate this realm? */
440 	crd = 0;
441 	/* first look into AVP, if set */
442 	if ( auth_realm_spec.type!=PVT_NONE )
443 		crd = get_avp_credential( msg, &auth.realm );
444 	/* if not found, look into predefined credentials */
445 	if (crd==0)
446 		crd = lookup_realm( &auth.realm );
447 	/* found? */
448 	if (crd==0)
449 	{
450 		LM_INFO("no credential for realm \"%.*s\"\n",
451 			auth.realm.len, auth.realm.s);
452 		goto error;
453 	}
454 
455 	if(mode & UACAUTH_MODE_HA1) {
456 		crd->aflags |= UAC_FLCRED_HA1;
457 	}
458 
459 	/* do authentication */
460 	do_uac_auth( &msg->first_line.u.request.method,
461 			&t->uac[branch].uri, crd, &auth, response);
462 
463 	/* build the authorization header */
464 	new_hdr = build_authorization_hdr( code, &t->uac[branch].uri,
465 		crd, &auth, response);
466 	if (new_hdr==0)
467 	{
468 		LM_ERR("failed to build authorization hdr\n");
469 		goto error;
470 	}
471 
472 	/* so far, so good -> add the header and set the proper RURI */
473 	if ( apply_urihdr_changes( msg, &t->uac[branch].uri, new_hdr)<0 )
474 	{
475 		LM_ERR("failed to apply changes\n");
476 		goto error;
477 	}
478 
479 	/* mark request in T with uac auth for increase of cseq via dialog
480 	 * - this function is executed in failure route, msg_flags will be
481 	 *   reset afterwards by tm fake env */
482 	if(t->uas.request) {
483 		t->uas.request->msg_flags |= FL_UAC_AUTH;
484 		cenv = sr_cfgenv_get();
485 		if(cenv->cb_cseq_update != NULL) {
486 			if(cenv->cb_cseq_update(msg)<0) {
487 				goto error;
488 			}
489 		}
490 	}
491 
492 	return 0;
493 error:
494 	return -1;
495 }
496 
497 /**
498  *
499  */
uac_auth(sip_msg_t * msg)500 int uac_auth(sip_msg_t *msg)
501 {
502 	return uac_auth_mode(msg, 0);
503 }