|
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 */ |
|
28 |
|
29 |
|
30 #include <ctype.h> |
|
31 #include <string.h> |
|
32 |
|
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" |
|
39 |
|
40 #include "auth.h" |
|
41 #include "auth_alg.h" |
|
42 #include "auth_hdr.h" |
|
43 |
|
44 |
|
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; |
|
49 |
|
50 |
|
51 static struct uac_credential *crd_list = 0; |
|
52 |
|
53 |
|
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) |
|
65 |
|
66 |
|
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) |
|
73 |
|
74 static str nc = {"00000001", 8}; |
|
75 static str cnonce = {"o", 1}; |
|
76 |
|
77 int has_credentials(void) |
|
78 { |
|
79 return (crd_list!=0)?1:0; |
|
80 } |
|
81 |
|
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 } |
|
95 |
|
96 |
|
97 int add_credential( unsigned int type, void *val) |
|
98 { |
|
99 struct uac_credential *crd; |
|
100 char *p; |
|
101 str foo; |
|
102 |
|
103 p = (char*)val; |
|
104 crd = 0; |
|
105 |
|
106 if (p==0 || *p==0) |
|
107 goto error; |
|
108 |
|
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)); |
|
116 |
|
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); |
|
127 |
|
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; |
|
136 |
|
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); |
|
147 |
|
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; |
|
156 |
|
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); |
|
167 |
|
168 /* end of string */ |
|
169 while (*p && isspace((int)*p)) p++; |
|
170 if (*p!=0) |
|
171 goto parse_error; |
|
172 |
|
173 /* link the new cred struct */ |
|
174 crd->next = crd_list; |
|
175 crd_list = crd; |
|
176 |
|
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 } |
|
187 |
|
188 |
|
189 void destroy_credentials(void) |
|
190 { |
|
191 struct uac_credential *foo; |
|
192 |
|
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 } |
|
201 |
|
202 |
|
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; |
|
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 hdr = get_header_by_name( rpl , hdr_name.s, hdr_name.len); |
|
233 if (hdr) |
|
234 return hdr; |
|
235 |
|
236 LM_ERR("reply has no " |
|
237 "auth hdr (%.*s)\n", hdr_name.len, hdr_name.s); |
|
238 error: |
|
239 return 0; |
|
240 } |
|
241 |
|
242 |
|
243 static inline struct uac_credential *lookup_realm( str *realm) |
|
244 { |
|
245 struct uac_credential *crd; |
|
246 |
|
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 } |
|
253 |
|
254 |
|
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; |
|
260 |
|
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; |
|
264 |
|
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; |
|
270 |
|
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; |
|
276 |
|
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; |
|
281 |
|
282 return &crd; |
|
283 } |
|
284 |
|
285 |
|
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; |
|
292 |
|
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); |
|
297 |
|
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 ); |
|
302 |
|
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 ); |
|
311 |
|
312 uac_calc_response( ha1, ha2, auth, 0/*nc*/, 0/*cnonce*/, response); |
|
313 } |
|
314 } |
|
315 |
|
316 |
|
317 static inline int apply_urihdr_changes( struct sip_msg *req, |
|
318 str *uri, str *hdr) |
|
319 { |
|
320 struct lump* anchor; |
|
321 |
|
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; |
|
338 |
|
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 } |
|
345 |
|
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 } |
|
352 |
|
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 } |
|
358 |
|
359 return 0; |
|
360 error: |
|
361 pkg_free( hdr->s ); |
|
362 return -1; |
|
363 } |
|
364 |
|
365 |
|
366 |
|
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; |
|
377 |
|
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 } |
|
385 |
|
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 } |
|
392 |
|
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); |
|
396 |
|
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 } |
|
407 |
|
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 } |
|
414 |
|
415 LM_DBG("header found; body=<%.*s>\n", |
|
416 hdr->body.len, hdr->body.s); |
|
417 |
|
418 if (parse_authenticate_body( &hdr->body, &auth)<0) |
|
419 { |
|
420 LM_ERR("failed to parse auth hdr body\n"); |
|
421 goto error; |
|
422 } |
|
423 |
|
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 } |
|
439 |
|
440 /* do authentication */ |
|
441 do_uac_auth( msg, &t->uac[branch].uri, crd, &auth, response); |
|
442 |
|
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 } |
|
451 |
|
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 } |
|
458 |
|
459 /* increas the Cseq nr */ |
|
460 |
|
461 |
|
462 return 0; |
|
463 error: |
|
464 return -1; |
|
465 } |
|
466 |
|
467 |
|
468 |