Merge remote branch 'origin/sr_3.0'
[sip-router] / modules_k / uac / auth.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2005 Voice Sistem SRL
5  *
6  * This file is part of Kamailio, a free SIP server.
7  *
8  * UAC Kamailio-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 Kamailio-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  */
28
29
30 #include <ctype.h>
31 #include <string.h>
32
33 #include "../../lib/kcore/cmpapi.h"
34 #include "../../dprint.h"
35 #include "../../pvar.h"
36 #include "../../data_lump.h"
37 #include "../../mem/mem.h"
38 #include "../../hashes.h"
39 #include "../../modules/tm/tm_load.h"
40
41 #include "auth.h"
42 #include "auth_alg.h"
43 #include "auth_hdr.h"
44
45
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;
50
51
52 static struct uac_credential *crd_list = 0;
53
54
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)
66
67
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)
74
75 static str nc = {"00000001", 8};
76 static str cnonce = {"o", 1};
77
78 int has_credentials(void)
79 {
80         return (crd_list!=0)?1:0;
81 }
82
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 }
96
97
98 int add_credential( unsigned int type, void *val)
99 {
100         struct uac_credential *crd;
101         char *p;
102         str foo;
103
104         p = (char*)val;
105         crd = 0;
106
107         if (p==0 || *p==0)
108                 goto error;
109
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));
117
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);
128
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;
137
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);
148
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;
157
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);
168
169         /* end of string */
170         while (*p && isspace((int)*p)) p++;
171         if (*p!=0)
172                 goto parse_error;
173
174         /* link the new cred struct */
175         crd->next = crd_list;
176         crd_list = crd;
177
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 }
188
189
190 void destroy_credentials(void)
191 {
192         struct uac_credential *foo;
193
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 }
202
203
204 struct hdr_field *get_autenticate_hdr(struct sip_msg *rpl, int rpl_code)
205 {
206         struct hdr_field *hdr;
207         str hdr_name;
208
209         /* what hdr should we look for */
210         if (rpl_code==WWW_AUTH_CODE)
211         {
212                 hdr_name.s = WWW_AUTH_HDR;
213                 hdr_name.len = WWW_AUTH_HDR_LEN;
214         } else if (rpl_code==PROXY_AUTH_CODE) {
215                 hdr_name.s = PROXY_AUTH_HDR;
216                 hdr_name.len = PROXY_AUTH_HDR_LEN;
217         } else {
218                 LM_ERR("reply is not an "
219                         "auth request\n");
220                 goto error;
221         }
222
223         LM_DBG("looking for header \"%.*s\"\n",
224                 hdr_name.len, hdr_name.s);
225
226         /* search the auth hdr, but first parse them all */
227         if (parse_headers( rpl, HDR_EOH_F, 0)<0)
228         {
229                 LM_ERR("failed to parse reply\n");
230                 goto error;
231         }
232         for( hdr=rpl->headers ; hdr ; hdr=hdr->next )
233         {
234                 if ( rpl_code==WWW_AUTH_CODE && hdr->type==HDR_WWW_AUTHENTICATE_T )
235                         return hdr;
236                 if ( rpl_code==PROXY_AUTH_CODE && hdr->type==HDR_PROXY_AUTHENTICATE_T )
237                         return hdr;
238         }
239
240         LM_ERR("reply has no "
241                 "auth hdr (%.*s)\n", hdr_name.len, hdr_name.s);
242 error:
243         return 0;
244 }
245
246
247 static inline struct uac_credential *lookup_realm( str *realm)
248 {
249         struct uac_credential *crd;
250
251         for( crd=crd_list ; crd ; crd=crd->next )
252                 if (realm->len==crd->realm.len &&
253                 strncmp( realm->s, crd->realm.s, realm->len)==0 )
254                         return crd;
255         return 0;
256 }
257
258
259 static inline struct uac_credential *get_avp_credential(struct sip_msg *msg,
260                                                                                                                                 str *realm)
261 {
262         static struct uac_credential crd;
263         pv_value_t pv_val;
264
265         if(pv_get_spec_value( msg, &auth_realm_spec, &pv_val)!=0
266         || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
267                 return 0;
268         
269         crd.realm = pv_val.rs;
270         /* is it the domain we are looking for? */
271         if (realm->len!=crd.realm.len ||
272         strncmp( realm->s, crd.realm.s, realm->len)!=0 )
273                 return 0;
274
275         /* get username and password */
276         if(pv_get_spec_value( msg, &auth_username_spec, &pv_val)!=0
277         || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
278                 return 0;
279         crd.user = pv_val.rs;
280
281         if(pv_get_spec_value( msg, &auth_password_spec, &pv_val)!=0
282         || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
283                 return 0;
284         crd.passwd = pv_val.rs;
285
286         return &crd;
287 }
288
289
290 void do_uac_auth(str *method, str *uri,
291                 struct uac_credential *crd,
292                 struct authenticate_body *auth,
293                 HASHHEX response)
294 {
295         HASHHEX ha1;
296         HASHHEX ha2;
297
298         if((auth->flags&QOP_AUTH) || (auth->flags&QOP_AUTH_INT))
299         {
300                 /* if qop generate nonce-count and cnonce */
301                 cnonce.s = int2str(get_hash1_raw(auth->nonce.s, auth->nonce.len), 
302                                                    &cnonce.len);
303
304                 /* do authentication */
305                 uac_calc_HA1( crd, auth, &cnonce, ha1);
306                 uac_calc_HA2( method, uri,
307                         auth, 0/*hentity*/, ha2 );
308
309                 uac_calc_response( ha1, ha2, auth, &nc, &cnonce, response);
310                 auth->nc = &nc;
311                 auth->cnonce = &cnonce;
312         } else {
313                 /* do authentication */
314                 uac_calc_HA1( crd, auth, 0/*cnonce*/, ha1);
315                 uac_calc_HA2( method, uri,
316                         auth, 0/*hentity*/, ha2 );
317
318                 uac_calc_response( ha1, ha2, auth, 0/*nc*/, 0/*cnonce*/, response);
319         }
320 }
321
322
323 static inline int apply_urihdr_changes( struct sip_msg *req,
324                                                                                                         str *uri, str *hdr)
325 {
326         struct lump* anchor;
327
328         /* add the uri - move it to branch directly FIXME (bogdan)*/
329         if (req->new_uri.s)
330         {
331                 pkg_free(req->new_uri.s);
332                 req->new_uri.len=0;
333         }
334         req->parsed_uri_ok=0;
335         req->new_uri.s = (char*)pkg_malloc(uri->len+1);
336         if (req->new_uri.s==0)
337         {
338                 LM_ERR("no more pkg\n");
339                 goto error;
340         }
341         memcpy( req->new_uri.s, uri->s, uri->len);
342         req->new_uri.s[uri->len]=0;
343         req->new_uri.len=uri->len;
344
345         /* add the header */
346         if (parse_headers(req, HDR_EOH_F, 0) == -1)
347         {
348                 LM_ERR("failed to parse message\n");
349                 goto error;
350         }
351
352         anchor = anchor_lump(req, req->unparsed - req->buf, 0, 0);
353         if (anchor==0)
354         {
355                 LM_ERR("failed to get anchor\n");
356                 goto error;
357         }
358
359         if (insert_new_lump_before(anchor, hdr->s, hdr->len, 0) == 0)
360         {
361                 LM_ERR("faield to insert lump\n");
362                 goto error;
363         }
364
365         return 0;
366 error:
367         pkg_free( hdr->s );
368         return -1;
369 }
370
371
372
373 int uac_auth( struct sip_msg *msg)
374 {
375         static struct authenticate_body auth;
376         struct uac_credential *crd;
377         int code, branch;
378         struct sip_msg *rpl;
379         struct cell *t;
380         struct hdr_field *hdr;
381         HASHHEX response;
382         str *new_hdr;
383
384         /* get transaction */
385         t = uac_tmb.t_gett();
386         if (t==T_UNDEFINED || t==T_NULL_CELL)
387         {
388                 LM_CRIT("no current transaction found\n");
389                 goto error;
390         }
391
392         /* get the selected branch */
393         branch = uac_tmb.t_get_picked_branch();
394         if (branch<0) {
395                 LM_CRIT("no picked branch (%d)\n",branch);
396                 goto error;
397         }
398
399         rpl = t->uac[branch].reply;
400         code = t->uac[branch].last_received;
401         LM_DBG("picked reply is %p, code %d\n",rpl,code);
402
403         if (rpl==0)
404         {
405                 LM_CRIT("empty reply on picked branch\n");
406                 goto error;
407         }
408         if (rpl==FAKED_REPLY)
409         {
410                 LM_ERR("cannot process a FAKED reply\n");
411                 goto error;
412         }
413
414         hdr = get_autenticate_hdr( rpl, code);
415         if (hdr==0)
416         {
417                 LM_ERR("failed to extract authenticate hdr\n");
418                 goto error;
419         }
420
421         LM_DBG("header found; body=<%.*s>\n",
422                 hdr->body.len, hdr->body.s);
423
424         if (parse_authenticate_body( &hdr->body, &auth)<0)
425         {
426                 LM_ERR("failed to parse auth hdr body\n");
427                 goto error;
428         }
429
430         /* can we authenticate this realm? */
431         crd = 0;
432         /* first look into AVP, if set */
433         if ( auth_realm_spec.type==PVT_AVP )
434                 crd = get_avp_credential( msg, &auth.realm );
435         /* if not found, look into predefined credentials */
436         if (crd==0)
437                 crd = lookup_realm( &auth.realm );
438         /* found? */
439         if (crd==0)
440         {
441                 LM_DBG("no credential for realm \"%.*s\"\n",
442                         auth.realm.len, auth.realm.s);
443                 goto error;
444         }
445
446         /* do authentication */
447         do_uac_auth( &msg->first_line.u.request.method,
448                         &t->uac[branch].uri, crd, &auth, response);
449
450         /* build the authorization header */
451         new_hdr = build_authorization_hdr( code, &t->uac[branch].uri,
452                 crd, &auth, response);
453         if (new_hdr==0)
454         {
455                 LM_ERR("failed to build authorization hdr\n");
456                 goto error;
457         }
458
459         /* so far, so good -> add the header and set the proper RURI */
460         if ( apply_urihdr_changes( msg, &t->uac[branch].uri, new_hdr)<0 )
461         {
462                 LM_ERR("failed to apply changes\n");
463                 goto error;
464         }
465
466         /* increas the Cseq nr */
467
468
469         return 0;
470 error:
471         return -1;
472 }
473
474
475