Mon, 16 Jan 2012 23:08:14 +0100
Inconclusively complete possibly missing fields. This change introduces
inconsistencies difficult to correct given incomplete documentation of
IPKG and OPKG packaging standards.
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 }