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 }