Wed, 10 Feb 2010 21:14:04 +0100
Import unmodified vendor sources 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 */
30 #include <ctype.h>
31 #include <string.h>
33 #include "../../str.h"
34 #include "../../dprint.h"
35 #include "../../pvar.h"
36 #include "../../data_lump.h"
37 #include "../../mem/mem.h"
38 #include "../tm/tm_load.h"
40 #include "auth.h"
41 #include "auth_alg.h"
42 #include "auth_hdr.h"
45 extern struct tm_binds uac_tmb;
46 extern pv_spec_t auth_username_spec;
47 extern pv_spec_t auth_realm_spec;
48 extern pv_spec_t auth_password_spec;
51 static struct uac_credential *crd_list = 0;
54 #define duplicate_str(_strd, _strs, _error) \
55 do { \
56 _strd.s = (char*)pkg_malloc(_strs.len); \
57 if (_strd.s==0) \
58 { \
59 LM_ERR("no more pkg memory\n");\
60 goto _error; \
61 } \
62 memcpy( _strd.s, _strs.s, _strs.len); \
63 _strd.len = _strs.len; \
64 }while(0)
67 #define WWW_AUTH_CODE 401
68 #define WWW_AUTH_HDR "WWW-Authenticate"
69 #define WWW_AUTH_HDR_LEN (sizeof(WWW_AUTH_HDR)-1)
70 #define PROXY_AUTH_CODE 407
71 #define PROXY_AUTH_HDR "Proxy-Authenticate"
72 #define PROXY_AUTH_HDR_LEN (sizeof(PROXY_AUTH_HDR)-1)
74 static str nc = {"00000001", 8};
75 static str cnonce = {"o", 1};
77 int has_credentials(void)
78 {
79 return (crd_list!=0)?1:0;
80 }
82 void free_credential(struct uac_credential *crd)
83 {
84 if (crd)
85 {
86 if (crd->realm.s)
87 pkg_free(crd->realm.s);
88 if (crd->user.s)
89 pkg_free(crd->user.s);
90 if (crd->passwd.s)
91 pkg_free(crd->passwd.s);
92 pkg_free(crd);
93 }
94 }
97 int add_credential( unsigned int type, void *val)
98 {
99 struct uac_credential *crd;
100 char *p;
101 str foo;
103 p = (char*)val;
104 crd = 0;
106 if (p==0 || *p==0)
107 goto error;
109 crd = (struct uac_credential*)pkg_malloc(sizeof(struct uac_credential));
110 if (crd==0)
111 {
112 LM_ERR("no more pkg mem\n");
113 goto error;
114 }
115 memset( crd, 0, sizeof(struct uac_credential));
117 /*parse the user */
118 while (*p && isspace((int)*p)) p++;
119 foo.s = p;
120 while (*p && *p!=':' && !isspace((int)*p)) p++;
121 if (foo.s==p || *p==0)
122 /* missing or empty user */
123 goto parse_error;
124 foo.len = p - foo.s;
125 /* dulicate it */
126 duplicate_str( crd->user, foo, error);
128 /* parse the ':' separator */
129 while (*p && isspace((int)*p)) p++;
130 if (*p!=':')
131 goto parse_error;
132 p++;
133 while (*p && isspace((int)*p)) p++;
134 if (*p==0)
135 goto parse_error;
137 /*parse the realm */
138 while (*p && isspace((int)*p)) p++;
139 foo.s = p;
140 while (*p && *p!=':' && !isspace((int)*p)) p++;
141 if (foo.s==p || *p==0)
142 /* missing or empty realm */
143 goto parse_error;
144 foo.len = p - foo.s;
145 /* dulicate it */
146 duplicate_str( crd->realm, foo, error);
148 /* parse the ':' separator */
149 while (*p && isspace((int)*p)) p++;
150 if (*p!=':')
151 goto parse_error;
152 p++;
153 while (*p && isspace((int)*p)) p++;
154 if (*p==0)
155 goto parse_error;
157 /*parse the passwd */
158 while (*p && isspace((int)*p)) p++;
159 foo.s = p;
160 while (*p && !isspace((int)*p)) p++;
161 if (foo.s==p)
162 /* missing or empty passwd */
163 goto parse_error;
164 foo.len = p - foo.s;
165 /* dulicate it */
166 duplicate_str( crd->passwd, foo, error);
168 /* end of string */
169 while (*p && isspace((int)*p)) p++;
170 if (*p!=0)
171 goto parse_error;
173 /* link the new cred struct */
174 crd->next = crd_list;
175 crd_list = crd;
177 pkg_free(val);
178 return 0;
179 parse_error:
180 LM_ERR("parse error in <%s> "
181 "around %ld\n", (char*)val, (long)(p-(char*)val));
182 error:
183 if (crd)
184 free_credential(crd);
185 return -1;
186 }
189 void destroy_credentials(void)
190 {
191 struct uac_credential *foo;
193 while (crd_list)
194 {
195 foo = crd_list;
196 crd_list = crd_list->next;
197 free_credential(foo);
198 }
199 crd_list = 0;
200 }
203 static inline struct hdr_field *get_autenticate_hdr(struct sip_msg *rpl,
204 int rpl_code)
205 {
206 struct hdr_field *hdr;
207 str hdr_name;
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 }
223 LM_DBG("looking for header \"%.*s\"\n",
224 hdr_name.len, hdr_name.s);
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 hdr = get_header_by_name( rpl , hdr_name.s, hdr_name.len);
233 if (hdr)
234 return hdr;
236 LM_ERR("reply has no "
237 "auth hdr (%.*s)\n", hdr_name.len, hdr_name.s);
238 error:
239 return 0;
240 }
243 static inline struct uac_credential *lookup_realm( str *realm)
244 {
245 struct uac_credential *crd;
247 for( crd=crd_list ; crd ; crd=crd->next )
248 if (realm->len==crd->realm.len &&
249 strncmp( realm->s, crd->realm.s, realm->len)==0 )
250 return crd;
251 return 0;
252 }
255 static inline struct uac_credential *get_avp_credential(struct sip_msg *msg,
256 str *realm)
257 {
258 static struct uac_credential crd;
259 pv_value_t pv_val;
261 if(pv_get_spec_value( msg, &auth_realm_spec, &pv_val)!=0
262 || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
263 return 0;
265 crd.realm = pv_val.rs;
266 /* is it the domain we are looking for? */
267 if (realm->len!=crd.realm.len ||
268 strncmp( realm->s, crd.realm.s, realm->len)!=0 )
269 return 0;
271 /* get username and password */
272 if(pv_get_spec_value( msg, &auth_username_spec, &pv_val)!=0
273 || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
274 return 0;
275 crd.user = pv_val.rs;
277 if(pv_get_spec_value( msg, &auth_password_spec, &pv_val)!=0
278 || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
279 return 0;
280 crd.passwd = pv_val.rs;
282 return &crd;
283 }
286 static inline void do_uac_auth(struct sip_msg *req, str *uri,
287 struct uac_credential *crd, struct authenticate_body *auth,
288 HASHHEX response)
289 {
290 HASHHEX ha1;
291 HASHHEX ha2;
293 if((auth->flags&QOP_AUTH) || (auth->flags&QOP_AUTH_INT))
294 {
295 /* if qop generate nonce-count and cnonce */
296 cnonce.s = int2str(core_hash(&auth->nonce, 0, 0),&cnonce.len);
298 /* do authentication */
299 uac_calc_HA1( crd, auth, &cnonce, ha1);
300 uac_calc_HA2( &req->first_line.u.request.method, uri,
301 auth, 0/*hentity*/, ha2 );
303 uac_calc_response( ha1, ha2, auth, &nc, &cnonce, response);
304 auth->nc = &nc;
305 auth->cnonce = &cnonce;
306 } else {
307 /* do authentication */
308 uac_calc_HA1( crd, auth, 0/*cnonce*/, ha1);
309 uac_calc_HA2( &req->first_line.u.request.method, uri,
310 auth, 0/*hentity*/, ha2 );
312 uac_calc_response( ha1, ha2, auth, 0/*nc*/, 0/*cnonce*/, response);
313 }
314 }
317 static inline int apply_urihdr_changes( struct sip_msg *req,
318 str *uri, str *hdr)
319 {
320 struct lump* anchor;
322 /* add the uri - move it to branch directly FIXME (bogdan)*/
323 if (req->new_uri.s)
324 {
325 pkg_free(req->new_uri.s);
326 req->new_uri.len=0;
327 }
328 req->parsed_uri_ok=0;
329 req->new_uri.s = (char*)pkg_malloc(uri->len+1);
330 if (req->new_uri.s==0)
331 {
332 LM_ERR("no more pkg\n");
333 goto error;
334 }
335 memcpy( req->new_uri.s, uri->s, uri->len);
336 req->new_uri.s[uri->len]=0;
337 req->new_uri.len=uri->len;
339 /* add the header */
340 if (parse_headers(req, HDR_EOH_F, 0) == -1)
341 {
342 LM_ERR("failed to parse message\n");
343 goto error;
344 }
346 anchor = anchor_lump(req, req->unparsed - req->buf, 0, 0);
347 if (anchor==0)
348 {
349 LM_ERR("failed to get anchor\n");
350 goto error;
351 }
353 if (insert_new_lump_before(anchor, hdr->s, hdr->len, 0) == 0)
354 {
355 LM_ERR("faield to insert lump\n");
356 goto error;
357 }
359 return 0;
360 error:
361 pkg_free( hdr->s );
362 return -1;
363 }
367 int uac_auth( struct sip_msg *msg)
368 {
369 static struct authenticate_body auth;
370 struct uac_credential *crd;
371 int code, branch;
372 struct sip_msg *rpl;
373 struct cell *t;
374 struct hdr_field *hdr;
375 HASHHEX response;
376 str *new_hdr;
378 /* get transaction */
379 t = uac_tmb.t_gett();
380 if (t==T_UNDEFINED || t==T_NULL_CELL)
381 {
382 LM_CRIT("no current transaction found\n");
383 goto error;
384 }
386 /* get the selected branch */
387 branch = uac_tmb.t_get_picked();
388 if (branch<0) {
389 LM_CRIT("no picked branch (%d)\n",branch);
390 goto error;
391 }
393 rpl = t->uac[branch].reply;
394 code = t->uac[branch].last_received;
395 LM_DBG("picked reply is %p, code %d\n",rpl,code);
397 if (rpl==0)
398 {
399 LM_CRIT("empty reply on picked branch\n");
400 goto error;
401 }
402 if (rpl==FAKED_REPLY)
403 {
404 LM_ERR("cannot process a FAKED reply\n");
405 goto error;
406 }
408 hdr = get_autenticate_hdr( rpl, code);
409 if (hdr==0)
410 {
411 LM_ERR("failed to extract authenticate hdr\n");
412 goto error;
413 }
415 LM_DBG("header found; body=<%.*s>\n",
416 hdr->body.len, hdr->body.s);
418 if (parse_authenticate_body( &hdr->body, &auth)<0)
419 {
420 LM_ERR("failed to parse auth hdr body\n");
421 goto error;
422 }
424 /* can we authenticate this realm? */
425 crd = 0;
426 /* first look into AVP, if set */
427 if ( auth_realm_spec.type==PVT_AVP )
428 crd = get_avp_credential( msg, &auth.realm );
429 /* if not found, look into predefined credentials */
430 if (crd==0)
431 crd = lookup_realm( &auth.realm );
432 /* found? */
433 if (crd==0)
434 {
435 LM_DBG("no credential for realm \"%.*s\"\n",
436 auth.realm.len, auth.realm.s);
437 goto error;
438 }
440 /* do authentication */
441 do_uac_auth( msg, &t->uac[branch].uri, crd, &auth, response);
443 /* build the authorization header */
444 new_hdr = build_authorization_hdr( code, &t->uac[branch].uri,
445 crd, &auth, response);
446 if (new_hdr==0)
447 {
448 LM_ERR("failed to build authorization hdr\n");
449 goto error;
450 }
452 /* so far, so good -> add the header and set the proper RURI */
453 if ( apply_urihdr_changes( msg, &t->uac[branch].uri, new_hdr)<0 )
454 {
455 LM_ERR("failed to apply changes\n");
456 goto error;
457 }
459 /* increas the Cseq nr */
462 return 0;
463 error:
464 return -1;
465 }