1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/opensips/modules/uac/auth.c Wed Feb 10 21:14:04 2010 +0100 1.3 @@ -0,0 +1,468 @@ 1.4 +/* 1.5 + * $Id: auth.c 5901 2009-07-21 07:45:05Z bogdan_iancu $ 1.6 + * 1.7 + * Copyright (C) 2005 Voice Sistem SRL 1.8 + * 1.9 + * This file is part of opensips, a free SIP server. 1.10 + * 1.11 + * UAC OpenSIPS-module is free software; you can redistribute it and/or 1.12 + * modify it under the terms of the GNU General Public License 1.13 + * as published by the Free Software Foundation; either version 2 1.14 + * of the License, or (at your option) any later version. 1.15 + * 1.16 + * UAC OpenSIPS-module is distributed in the hope that it will be useful, 1.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.19 + * GNU General Public License for more details. 1.20 + * 1.21 + * You should have received a copy of the GNU General Public License 1.22 + * along with this program; if not, write to the Free Software 1.23 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 1.24 + * 1.25 + * 1.26 + * History: 1.27 + * --------- 1.28 + * 2005-01-31 first version (ramona) 1.29 + * 2006-03-02 UAC authentication looks first in AVPs for credential (bogdan) 1.30 + */ 1.31 + 1.32 + 1.33 +#include <ctype.h> 1.34 +#include <string.h> 1.35 + 1.36 +#include "../../str.h" 1.37 +#include "../../dprint.h" 1.38 +#include "../../pvar.h" 1.39 +#include "../../data_lump.h" 1.40 +#include "../../mem/mem.h" 1.41 +#include "../tm/tm_load.h" 1.42 + 1.43 +#include "auth.h" 1.44 +#include "auth_alg.h" 1.45 +#include "auth_hdr.h" 1.46 + 1.47 + 1.48 +extern struct tm_binds uac_tmb; 1.49 +extern pv_spec_t auth_username_spec; 1.50 +extern pv_spec_t auth_realm_spec; 1.51 +extern pv_spec_t auth_password_spec; 1.52 + 1.53 + 1.54 +static struct uac_credential *crd_list = 0; 1.55 + 1.56 + 1.57 +#define duplicate_str(_strd, _strs, _error) \ 1.58 + do { \ 1.59 + _strd.s = (char*)pkg_malloc(_strs.len); \ 1.60 + if (_strd.s==0) \ 1.61 + { \ 1.62 + LM_ERR("no more pkg memory\n");\ 1.63 + goto _error; \ 1.64 + } \ 1.65 + memcpy( _strd.s, _strs.s, _strs.len); \ 1.66 + _strd.len = _strs.len; \ 1.67 + }while(0) 1.68 + 1.69 + 1.70 +#define WWW_AUTH_CODE 401 1.71 +#define WWW_AUTH_HDR "WWW-Authenticate" 1.72 +#define WWW_AUTH_HDR_LEN (sizeof(WWW_AUTH_HDR)-1) 1.73 +#define PROXY_AUTH_CODE 407 1.74 +#define PROXY_AUTH_HDR "Proxy-Authenticate" 1.75 +#define PROXY_AUTH_HDR_LEN (sizeof(PROXY_AUTH_HDR)-1) 1.76 + 1.77 +static str nc = {"00000001", 8}; 1.78 +static str cnonce = {"o", 1}; 1.79 + 1.80 +int has_credentials(void) 1.81 +{ 1.82 + return (crd_list!=0)?1:0; 1.83 +} 1.84 + 1.85 +void free_credential(struct uac_credential *crd) 1.86 +{ 1.87 + if (crd) 1.88 + { 1.89 + if (crd->realm.s) 1.90 + pkg_free(crd->realm.s); 1.91 + if (crd->user.s) 1.92 + pkg_free(crd->user.s); 1.93 + if (crd->passwd.s) 1.94 + pkg_free(crd->passwd.s); 1.95 + pkg_free(crd); 1.96 + } 1.97 +} 1.98 + 1.99 + 1.100 +int add_credential( unsigned int type, void *val) 1.101 +{ 1.102 + struct uac_credential *crd; 1.103 + char *p; 1.104 + str foo; 1.105 + 1.106 + p = (char*)val; 1.107 + crd = 0; 1.108 + 1.109 + if (p==0 || *p==0) 1.110 + goto error; 1.111 + 1.112 + crd = (struct uac_credential*)pkg_malloc(sizeof(struct uac_credential)); 1.113 + if (crd==0) 1.114 + { 1.115 + LM_ERR("no more pkg mem\n"); 1.116 + goto error; 1.117 + } 1.118 + memset( crd, 0, sizeof(struct uac_credential)); 1.119 + 1.120 + /*parse the user */ 1.121 + while (*p && isspace((int)*p)) p++; 1.122 + foo.s = p; 1.123 + while (*p && *p!=':' && !isspace((int)*p)) p++; 1.124 + if (foo.s==p || *p==0) 1.125 + /* missing or empty user */ 1.126 + goto parse_error; 1.127 + foo.len = p - foo.s; 1.128 + /* dulicate it */ 1.129 + duplicate_str( crd->user, foo, error); 1.130 + 1.131 + /* parse the ':' separator */ 1.132 + while (*p && isspace((int)*p)) p++; 1.133 + if (*p!=':') 1.134 + goto parse_error; 1.135 + p++; 1.136 + while (*p && isspace((int)*p)) p++; 1.137 + if (*p==0) 1.138 + goto parse_error; 1.139 + 1.140 + /*parse the realm */ 1.141 + while (*p && isspace((int)*p)) p++; 1.142 + foo.s = p; 1.143 + while (*p && *p!=':' && !isspace((int)*p)) p++; 1.144 + if (foo.s==p || *p==0) 1.145 + /* missing or empty realm */ 1.146 + goto parse_error; 1.147 + foo.len = p - foo.s; 1.148 + /* dulicate it */ 1.149 + duplicate_str( crd->realm, foo, error); 1.150 + 1.151 + /* parse the ':' separator */ 1.152 + while (*p && isspace((int)*p)) p++; 1.153 + if (*p!=':') 1.154 + goto parse_error; 1.155 + p++; 1.156 + while (*p && isspace((int)*p)) p++; 1.157 + if (*p==0) 1.158 + goto parse_error; 1.159 + 1.160 + /*parse the passwd */ 1.161 + while (*p && isspace((int)*p)) p++; 1.162 + foo.s = p; 1.163 + while (*p && !isspace((int)*p)) p++; 1.164 + if (foo.s==p) 1.165 + /* missing or empty passwd */ 1.166 + goto parse_error; 1.167 + foo.len = p - foo.s; 1.168 + /* dulicate it */ 1.169 + duplicate_str( crd->passwd, foo, error); 1.170 + 1.171 + /* end of string */ 1.172 + while (*p && isspace((int)*p)) p++; 1.173 + if (*p!=0) 1.174 + goto parse_error; 1.175 + 1.176 + /* link the new cred struct */ 1.177 + crd->next = crd_list; 1.178 + crd_list = crd; 1.179 + 1.180 + pkg_free(val); 1.181 + return 0; 1.182 +parse_error: 1.183 + LM_ERR("parse error in <%s> " 1.184 + "around %ld\n", (char*)val, (long)(p-(char*)val)); 1.185 +error: 1.186 + if (crd) 1.187 + free_credential(crd); 1.188 + return -1; 1.189 +} 1.190 + 1.191 + 1.192 +void destroy_credentials(void) 1.193 +{ 1.194 + struct uac_credential *foo; 1.195 + 1.196 + while (crd_list) 1.197 + { 1.198 + foo = crd_list; 1.199 + crd_list = crd_list->next; 1.200 + free_credential(foo); 1.201 + } 1.202 + crd_list = 0; 1.203 +} 1.204 + 1.205 + 1.206 +static inline struct hdr_field *get_autenticate_hdr(struct sip_msg *rpl, 1.207 + int rpl_code) 1.208 +{ 1.209 + struct hdr_field *hdr; 1.210 + str hdr_name; 1.211 + 1.212 + /* what hdr should we look for */ 1.213 + if (rpl_code==WWW_AUTH_CODE) 1.214 + { 1.215 + hdr_name.s = WWW_AUTH_HDR; 1.216 + hdr_name.len = WWW_AUTH_HDR_LEN; 1.217 + } else if (rpl_code==PROXY_AUTH_CODE) { 1.218 + hdr_name.s = PROXY_AUTH_HDR; 1.219 + hdr_name.len = PROXY_AUTH_HDR_LEN; 1.220 + } else { 1.221 + LM_ERR("reply is not an " 1.222 + "auth request\n"); 1.223 + goto error; 1.224 + } 1.225 + 1.226 + LM_DBG("looking for header \"%.*s\"\n", 1.227 + hdr_name.len, hdr_name.s); 1.228 + 1.229 + /* search the auth hdr, but first parse them all */ 1.230 + if (parse_headers( rpl, HDR_EOH_F, 0)<0) 1.231 + { 1.232 + LM_ERR("failed to parse reply\n"); 1.233 + goto error; 1.234 + } 1.235 + hdr = get_header_by_name( rpl , hdr_name.s, hdr_name.len); 1.236 + if (hdr) 1.237 + return hdr; 1.238 + 1.239 + LM_ERR("reply has no " 1.240 + "auth hdr (%.*s)\n", hdr_name.len, hdr_name.s); 1.241 +error: 1.242 + return 0; 1.243 +} 1.244 + 1.245 + 1.246 +static inline struct uac_credential *lookup_realm( str *realm) 1.247 +{ 1.248 + struct uac_credential *crd; 1.249 + 1.250 + for( crd=crd_list ; crd ; crd=crd->next ) 1.251 + if (realm->len==crd->realm.len && 1.252 + strncmp( realm->s, crd->realm.s, realm->len)==0 ) 1.253 + return crd; 1.254 + return 0; 1.255 +} 1.256 + 1.257 + 1.258 +static inline struct uac_credential *get_avp_credential(struct sip_msg *msg, 1.259 + str *realm) 1.260 +{ 1.261 + static struct uac_credential crd; 1.262 + pv_value_t pv_val; 1.263 + 1.264 + if(pv_get_spec_value( msg, &auth_realm_spec, &pv_val)!=0 1.265 + || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) 1.266 + return 0; 1.267 + 1.268 + crd.realm = pv_val.rs; 1.269 + /* is it the domain we are looking for? */ 1.270 + if (realm->len!=crd.realm.len || 1.271 + strncmp( realm->s, crd.realm.s, realm->len)!=0 ) 1.272 + return 0; 1.273 + 1.274 + /* get username and password */ 1.275 + if(pv_get_spec_value( msg, &auth_username_spec, &pv_val)!=0 1.276 + || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) 1.277 + return 0; 1.278 + crd.user = pv_val.rs; 1.279 + 1.280 + if(pv_get_spec_value( msg, &auth_password_spec, &pv_val)!=0 1.281 + || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) 1.282 + return 0; 1.283 + crd.passwd = pv_val.rs; 1.284 + 1.285 + return &crd; 1.286 +} 1.287 + 1.288 + 1.289 +static inline void do_uac_auth(struct sip_msg *req, str *uri, 1.290 + struct uac_credential *crd, struct authenticate_body *auth, 1.291 + HASHHEX response) 1.292 +{ 1.293 + HASHHEX ha1; 1.294 + HASHHEX ha2; 1.295 + 1.296 + if((auth->flags&QOP_AUTH) || (auth->flags&QOP_AUTH_INT)) 1.297 + { 1.298 + /* if qop generate nonce-count and cnonce */ 1.299 + cnonce.s = int2str(core_hash(&auth->nonce, 0, 0),&cnonce.len); 1.300 + 1.301 + /* do authentication */ 1.302 + uac_calc_HA1( crd, auth, &cnonce, ha1); 1.303 + uac_calc_HA2( &req->first_line.u.request.method, uri, 1.304 + auth, 0/*hentity*/, ha2 ); 1.305 + 1.306 + uac_calc_response( ha1, ha2, auth, &nc, &cnonce, response); 1.307 + auth->nc = &nc; 1.308 + auth->cnonce = &cnonce; 1.309 + } else { 1.310 + /* do authentication */ 1.311 + uac_calc_HA1( crd, auth, 0/*cnonce*/, ha1); 1.312 + uac_calc_HA2( &req->first_line.u.request.method, uri, 1.313 + auth, 0/*hentity*/, ha2 ); 1.314 + 1.315 + uac_calc_response( ha1, ha2, auth, 0/*nc*/, 0/*cnonce*/, response); 1.316 + } 1.317 +} 1.318 + 1.319 + 1.320 +static inline int apply_urihdr_changes( struct sip_msg *req, 1.321 + str *uri, str *hdr) 1.322 +{ 1.323 + struct lump* anchor; 1.324 + 1.325 + /* add the uri - move it to branch directly FIXME (bogdan)*/ 1.326 + if (req->new_uri.s) 1.327 + { 1.328 + pkg_free(req->new_uri.s); 1.329 + req->new_uri.len=0; 1.330 + } 1.331 + req->parsed_uri_ok=0; 1.332 + req->new_uri.s = (char*)pkg_malloc(uri->len+1); 1.333 + if (req->new_uri.s==0) 1.334 + { 1.335 + LM_ERR("no more pkg\n"); 1.336 + goto error; 1.337 + } 1.338 + memcpy( req->new_uri.s, uri->s, uri->len); 1.339 + req->new_uri.s[uri->len]=0; 1.340 + req->new_uri.len=uri->len; 1.341 + 1.342 + /* add the header */ 1.343 + if (parse_headers(req, HDR_EOH_F, 0) == -1) 1.344 + { 1.345 + LM_ERR("failed to parse message\n"); 1.346 + goto error; 1.347 + } 1.348 + 1.349 + anchor = anchor_lump(req, req->unparsed - req->buf, 0, 0); 1.350 + if (anchor==0) 1.351 + { 1.352 + LM_ERR("failed to get anchor\n"); 1.353 + goto error; 1.354 + } 1.355 + 1.356 + if (insert_new_lump_before(anchor, hdr->s, hdr->len, 0) == 0) 1.357 + { 1.358 + LM_ERR("faield to insert lump\n"); 1.359 + goto error; 1.360 + } 1.361 + 1.362 + return 0; 1.363 +error: 1.364 + pkg_free( hdr->s ); 1.365 + return -1; 1.366 +} 1.367 + 1.368 + 1.369 + 1.370 +int uac_auth( struct sip_msg *msg) 1.371 +{ 1.372 + static struct authenticate_body auth; 1.373 + struct uac_credential *crd; 1.374 + int code, branch; 1.375 + struct sip_msg *rpl; 1.376 + struct cell *t; 1.377 + struct hdr_field *hdr; 1.378 + HASHHEX response; 1.379 + str *new_hdr; 1.380 + 1.381 + /* get transaction */ 1.382 + t = uac_tmb.t_gett(); 1.383 + if (t==T_UNDEFINED || t==T_NULL_CELL) 1.384 + { 1.385 + LM_CRIT("no current transaction found\n"); 1.386 + goto error; 1.387 + } 1.388 + 1.389 + /* get the selected branch */ 1.390 + branch = uac_tmb.t_get_picked(); 1.391 + if (branch<0) { 1.392 + LM_CRIT("no picked branch (%d)\n",branch); 1.393 + goto error; 1.394 + } 1.395 + 1.396 + rpl = t->uac[branch].reply; 1.397 + code = t->uac[branch].last_received; 1.398 + LM_DBG("picked reply is %p, code %d\n",rpl,code); 1.399 + 1.400 + if (rpl==0) 1.401 + { 1.402 + LM_CRIT("empty reply on picked branch\n"); 1.403 + goto error; 1.404 + } 1.405 + if (rpl==FAKED_REPLY) 1.406 + { 1.407 + LM_ERR("cannot process a FAKED reply\n"); 1.408 + goto error; 1.409 + } 1.410 + 1.411 + hdr = get_autenticate_hdr( rpl, code); 1.412 + if (hdr==0) 1.413 + { 1.414 + LM_ERR("failed to extract authenticate hdr\n"); 1.415 + goto error; 1.416 + } 1.417 + 1.418 + LM_DBG("header found; body=<%.*s>\n", 1.419 + hdr->body.len, hdr->body.s); 1.420 + 1.421 + if (parse_authenticate_body( &hdr->body, &auth)<0) 1.422 + { 1.423 + LM_ERR("failed to parse auth hdr body\n"); 1.424 + goto error; 1.425 + } 1.426 + 1.427 + /* can we authenticate this realm? */ 1.428 + crd = 0; 1.429 + /* first look into AVP, if set */ 1.430 + if ( auth_realm_spec.type==PVT_AVP ) 1.431 + crd = get_avp_credential( msg, &auth.realm ); 1.432 + /* if not found, look into predefined credentials */ 1.433 + if (crd==0) 1.434 + crd = lookup_realm( &auth.realm ); 1.435 + /* found? */ 1.436 + if (crd==0) 1.437 + { 1.438 + LM_DBG("no credential for realm \"%.*s\"\n", 1.439 + auth.realm.len, auth.realm.s); 1.440 + goto error; 1.441 + } 1.442 + 1.443 + /* do authentication */ 1.444 + do_uac_auth( msg, &t->uac[branch].uri, crd, &auth, response); 1.445 + 1.446 + /* build the authorization header */ 1.447 + new_hdr = build_authorization_hdr( code, &t->uac[branch].uri, 1.448 + crd, &auth, response); 1.449 + if (new_hdr==0) 1.450 + { 1.451 + LM_ERR("failed to build authorization hdr\n"); 1.452 + goto error; 1.453 + } 1.454 + 1.455 + /* so far, so good -> add the header and set the proper RURI */ 1.456 + if ( apply_urihdr_changes( msg, &t->uac[branch].uri, new_hdr)<0 ) 1.457 + { 1.458 + LM_ERR("failed to apply changes\n"); 1.459 + goto error; 1.460 + } 1.461 + 1.462 + /* increas the Cseq nr */ 1.463 + 1.464 + 1.465 + return 0; 1.466 +error: 1.467 + return -1; 1.468 +} 1.469 + 1.470 + 1.471 +