Wed, 10 Feb 2010 21:25:01 +0100
Extend uac_auth() of the UAC module to workaround CSEQ problems.
This logic is meant to complement that of changeset 17, which
added rich authentication credentials to the gw table and its
associated logic in the LCR module.
michael@16 | 1 | /* |
michael@16 | 2 | * $Id: auth.c 5901 2009-07-21 07:45:05Z bogdan_iancu $ |
michael@16 | 3 | * |
michael@16 | 4 | * Copyright (C) 2005 Voice Sistem SRL |
michael@16 | 5 | * |
michael@16 | 6 | * This file is part of opensips, a free SIP server. |
michael@16 | 7 | * |
michael@16 | 8 | * UAC OpenSIPS-module is free software; you can redistribute it and/or |
michael@16 | 9 | * modify it under the terms of the GNU General Public License |
michael@16 | 10 | * as published by the Free Software Foundation; either version 2 |
michael@16 | 11 | * of the License, or (at your option) any later version. |
michael@16 | 12 | * |
michael@16 | 13 | * UAC OpenSIPS-module is distributed in the hope that it will be useful, |
michael@16 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
michael@16 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
michael@16 | 16 | * GNU General Public License for more details. |
michael@16 | 17 | * |
michael@16 | 18 | * You should have received a copy of the GNU General Public License |
michael@16 | 19 | * along with this program; if not, write to the Free Software |
michael@16 | 20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
michael@16 | 21 | * |
michael@16 | 22 | * |
michael@16 | 23 | * History: |
michael@16 | 24 | * --------- |
michael@16 | 25 | * 2005-01-31 first version (ramona) |
michael@16 | 26 | * 2006-03-02 UAC authentication looks first in AVPs for credential (bogdan) |
michael@18 | 27 | * 2010-01-18 UAC replaces proxy-auth entries with new credentials (msvb) |
michael@16 | 28 | */ |
michael@16 | 29 | |
michael@16 | 30 | |
michael@16 | 31 | #include <ctype.h> |
michael@16 | 32 | #include <string.h> |
michael@16 | 33 | |
michael@16 | 34 | #include "../../str.h" |
michael@16 | 35 | #include "../../dprint.h" |
michael@16 | 36 | #include "../../pvar.h" |
michael@16 | 37 | #include "../../data_lump.h" |
michael@16 | 38 | #include "../../mem/mem.h" |
michael@16 | 39 | #include "../tm/tm_load.h" |
michael@16 | 40 | |
michael@16 | 41 | #include "auth.h" |
michael@16 | 42 | #include "auth_alg.h" |
michael@16 | 43 | #include "auth_hdr.h" |
michael@16 | 44 | |
michael@16 | 45 | |
michael@16 | 46 | extern struct tm_binds uac_tmb; |
michael@16 | 47 | extern pv_spec_t auth_username_spec; |
michael@16 | 48 | extern pv_spec_t auth_realm_spec; |
michael@16 | 49 | extern pv_spec_t auth_password_spec; |
michael@16 | 50 | |
michael@16 | 51 | |
michael@16 | 52 | static struct uac_credential *crd_list = 0; |
michael@16 | 53 | |
michael@16 | 54 | |
michael@16 | 55 | #define duplicate_str(_strd, _strs, _error) \ |
michael@16 | 56 | do { \ |
michael@16 | 57 | _strd.s = (char*)pkg_malloc(_strs.len); \ |
michael@16 | 58 | if (_strd.s==0) \ |
michael@16 | 59 | { \ |
michael@16 | 60 | LM_ERR("no more pkg memory\n");\ |
michael@16 | 61 | goto _error; \ |
michael@16 | 62 | } \ |
michael@16 | 63 | memcpy( _strd.s, _strs.s, _strs.len); \ |
michael@16 | 64 | _strd.len = _strs.len; \ |
michael@16 | 65 | }while(0) |
michael@16 | 66 | |
michael@16 | 67 | |
michael@16 | 68 | #define WWW_AUTH_CODE 401 |
michael@16 | 69 | #define WWW_AUTH_HDR "WWW-Authenticate" |
michael@16 | 70 | #define WWW_AUTH_HDR_LEN (sizeof(WWW_AUTH_HDR)-1) |
michael@16 | 71 | #define PROXY_AUTH_CODE 407 |
michael@16 | 72 | #define PROXY_AUTH_HDR "Proxy-Authenticate" |
michael@16 | 73 | #define PROXY_AUTH_HDR_LEN (sizeof(PROXY_AUTH_HDR)-1) |
michael@16 | 74 | |
michael@16 | 75 | static str nc = {"00000001", 8}; |
michael@16 | 76 | static str cnonce = {"o", 1}; |
michael@16 | 77 | |
michael@16 | 78 | int has_credentials(void) |
michael@16 | 79 | { |
michael@16 | 80 | return (crd_list!=0)?1:0; |
michael@16 | 81 | } |
michael@16 | 82 | |
michael@16 | 83 | void free_credential(struct uac_credential *crd) |
michael@16 | 84 | { |
michael@16 | 85 | if (crd) |
michael@16 | 86 | { |
michael@16 | 87 | if (crd->realm.s) |
michael@16 | 88 | pkg_free(crd->realm.s); |
michael@16 | 89 | if (crd->user.s) |
michael@16 | 90 | pkg_free(crd->user.s); |
michael@16 | 91 | if (crd->passwd.s) |
michael@16 | 92 | pkg_free(crd->passwd.s); |
michael@16 | 93 | pkg_free(crd); |
michael@16 | 94 | } |
michael@16 | 95 | } |
michael@16 | 96 | |
michael@16 | 97 | |
michael@16 | 98 | int add_credential( unsigned int type, void *val) |
michael@16 | 99 | { |
michael@16 | 100 | struct uac_credential *crd; |
michael@16 | 101 | char *p; |
michael@16 | 102 | str foo; |
michael@16 | 103 | |
michael@16 | 104 | p = (char*)val; |
michael@16 | 105 | crd = 0; |
michael@16 | 106 | |
michael@16 | 107 | if (p==0 || *p==0) |
michael@16 | 108 | goto error; |
michael@16 | 109 | |
michael@16 | 110 | crd = (struct uac_credential*)pkg_malloc(sizeof(struct uac_credential)); |
michael@16 | 111 | if (crd==0) |
michael@16 | 112 | { |
michael@16 | 113 | LM_ERR("no more pkg mem\n"); |
michael@16 | 114 | goto error; |
michael@16 | 115 | } |
michael@16 | 116 | memset( crd, 0, sizeof(struct uac_credential)); |
michael@16 | 117 | |
michael@16 | 118 | /*parse the user */ |
michael@16 | 119 | while (*p && isspace((int)*p)) p++; |
michael@16 | 120 | foo.s = p; |
michael@16 | 121 | while (*p && *p!=':' && !isspace((int)*p)) p++; |
michael@16 | 122 | if (foo.s==p || *p==0) |
michael@16 | 123 | /* missing or empty user */ |
michael@16 | 124 | goto parse_error; |
michael@16 | 125 | foo.len = p - foo.s; |
michael@16 | 126 | /* dulicate it */ |
michael@16 | 127 | duplicate_str( crd->user, foo, error); |
michael@16 | 128 | |
michael@16 | 129 | /* parse the ':' separator */ |
michael@16 | 130 | while (*p && isspace((int)*p)) p++; |
michael@16 | 131 | if (*p!=':') |
michael@16 | 132 | goto parse_error; |
michael@16 | 133 | p++; |
michael@16 | 134 | while (*p && isspace((int)*p)) p++; |
michael@16 | 135 | if (*p==0) |
michael@16 | 136 | goto parse_error; |
michael@16 | 137 | |
michael@16 | 138 | /*parse the realm */ |
michael@16 | 139 | while (*p && isspace((int)*p)) p++; |
michael@16 | 140 | foo.s = p; |
michael@16 | 141 | while (*p && *p!=':' && !isspace((int)*p)) p++; |
michael@16 | 142 | if (foo.s==p || *p==0) |
michael@16 | 143 | /* missing or empty realm */ |
michael@16 | 144 | goto parse_error; |
michael@16 | 145 | foo.len = p - foo.s; |
michael@16 | 146 | /* dulicate it */ |
michael@16 | 147 | duplicate_str( crd->realm, foo, error); |
michael@16 | 148 | |
michael@16 | 149 | /* parse the ':' separator */ |
michael@16 | 150 | while (*p && isspace((int)*p)) p++; |
michael@16 | 151 | if (*p!=':') |
michael@16 | 152 | goto parse_error; |
michael@16 | 153 | p++; |
michael@16 | 154 | while (*p && isspace((int)*p)) p++; |
michael@16 | 155 | if (*p==0) |
michael@16 | 156 | goto parse_error; |
michael@16 | 157 | |
michael@16 | 158 | /*parse the passwd */ |
michael@16 | 159 | while (*p && isspace((int)*p)) p++; |
michael@16 | 160 | foo.s = p; |
michael@16 | 161 | while (*p && !isspace((int)*p)) p++; |
michael@16 | 162 | if (foo.s==p) |
michael@16 | 163 | /* missing or empty passwd */ |
michael@16 | 164 | goto parse_error; |
michael@16 | 165 | foo.len = p - foo.s; |
michael@16 | 166 | /* dulicate it */ |
michael@16 | 167 | duplicate_str( crd->passwd, foo, error); |
michael@16 | 168 | |
michael@16 | 169 | /* end of string */ |
michael@16 | 170 | while (*p && isspace((int)*p)) p++; |
michael@16 | 171 | if (*p!=0) |
michael@16 | 172 | goto parse_error; |
michael@16 | 173 | |
michael@16 | 174 | /* link the new cred struct */ |
michael@16 | 175 | crd->next = crd_list; |
michael@16 | 176 | crd_list = crd; |
michael@16 | 177 | |
michael@16 | 178 | pkg_free(val); |
michael@16 | 179 | return 0; |
michael@16 | 180 | parse_error: |
michael@16 | 181 | LM_ERR("parse error in <%s> " |
michael@16 | 182 | "around %ld\n", (char*)val, (long)(p-(char*)val)); |
michael@16 | 183 | error: |
michael@16 | 184 | if (crd) |
michael@16 | 185 | free_credential(crd); |
michael@16 | 186 | return -1; |
michael@16 | 187 | } |
michael@16 | 188 | |
michael@16 | 189 | |
michael@16 | 190 | void destroy_credentials(void) |
michael@16 | 191 | { |
michael@16 | 192 | struct uac_credential *foo; |
michael@16 | 193 | |
michael@16 | 194 | while (crd_list) |
michael@16 | 195 | { |
michael@16 | 196 | foo = crd_list; |
michael@16 | 197 | crd_list = crd_list->next; |
michael@16 | 198 | free_credential(foo); |
michael@16 | 199 | } |
michael@16 | 200 | crd_list = 0; |
michael@16 | 201 | } |
michael@16 | 202 | |
michael@16 | 203 | |
michael@16 | 204 | static inline struct hdr_field *get_autenticate_hdr(struct sip_msg *rpl, |
michael@16 | 205 | int rpl_code) |
michael@16 | 206 | { |
michael@16 | 207 | struct hdr_field *hdr; |
michael@16 | 208 | str hdr_name; |
michael@16 | 209 | |
michael@16 | 210 | /* what hdr should we look for */ |
michael@16 | 211 | if (rpl_code==WWW_AUTH_CODE) |
michael@16 | 212 | { |
michael@16 | 213 | hdr_name.s = WWW_AUTH_HDR; |
michael@16 | 214 | hdr_name.len = WWW_AUTH_HDR_LEN; |
michael@16 | 215 | } else if (rpl_code==PROXY_AUTH_CODE) { |
michael@16 | 216 | hdr_name.s = PROXY_AUTH_HDR; |
michael@16 | 217 | hdr_name.len = PROXY_AUTH_HDR_LEN; |
michael@16 | 218 | } else { |
michael@16 | 219 | LM_ERR("reply is not an " |
michael@16 | 220 | "auth request\n"); |
michael@16 | 221 | goto error; |
michael@16 | 222 | } |
michael@16 | 223 | |
michael@16 | 224 | LM_DBG("looking for header \"%.*s\"\n", |
michael@16 | 225 | hdr_name.len, hdr_name.s); |
michael@16 | 226 | |
michael@16 | 227 | /* search the auth hdr, but first parse them all */ |
michael@16 | 228 | if (parse_headers( rpl, HDR_EOH_F, 0)<0) |
michael@16 | 229 | { |
michael@16 | 230 | LM_ERR("failed to parse reply\n"); |
michael@16 | 231 | goto error; |
michael@16 | 232 | } |
michael@16 | 233 | hdr = get_header_by_name( rpl , hdr_name.s, hdr_name.len); |
michael@16 | 234 | if (hdr) |
michael@16 | 235 | return hdr; |
michael@16 | 236 | |
michael@16 | 237 | LM_ERR("reply has no " |
michael@16 | 238 | "auth hdr (%.*s)\n", hdr_name.len, hdr_name.s); |
michael@16 | 239 | error: |
michael@16 | 240 | return 0; |
michael@16 | 241 | } |
michael@16 | 242 | |
michael@16 | 243 | |
michael@16 | 244 | static inline struct uac_credential *lookup_realm( str *realm) |
michael@16 | 245 | { |
michael@16 | 246 | struct uac_credential *crd; |
michael@16 | 247 | |
michael@16 | 248 | for( crd=crd_list ; crd ; crd=crd->next ) |
michael@16 | 249 | if (realm->len==crd->realm.len && |
michael@16 | 250 | strncmp( realm->s, crd->realm.s, realm->len)==0 ) |
michael@16 | 251 | return crd; |
michael@16 | 252 | return 0; |
michael@16 | 253 | } |
michael@16 | 254 | |
michael@16 | 255 | |
michael@16 | 256 | static inline struct uac_credential *get_avp_credential(struct sip_msg *msg, |
michael@16 | 257 | str *realm) |
michael@16 | 258 | { |
michael@16 | 259 | static struct uac_credential crd; |
michael@16 | 260 | pv_value_t pv_val; |
michael@16 | 261 | |
michael@16 | 262 | if(pv_get_spec_value( msg, &auth_realm_spec, &pv_val)!=0 |
michael@16 | 263 | || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) |
michael@16 | 264 | return 0; |
michael@16 | 265 | |
michael@16 | 266 | crd.realm = pv_val.rs; |
michael@16 | 267 | /* is it the domain we are looking for? */ |
michael@16 | 268 | if (realm->len!=crd.realm.len || |
michael@16 | 269 | strncmp( realm->s, crd.realm.s, realm->len)!=0 ) |
michael@16 | 270 | return 0; |
michael@16 | 271 | |
michael@16 | 272 | /* get username and password */ |
michael@16 | 273 | if(pv_get_spec_value( msg, &auth_username_spec, &pv_val)!=0 |
michael@16 | 274 | || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) |
michael@16 | 275 | return 0; |
michael@16 | 276 | crd.user = pv_val.rs; |
michael@16 | 277 | |
michael@16 | 278 | if(pv_get_spec_value( msg, &auth_password_spec, &pv_val)!=0 |
michael@16 | 279 | || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) |
michael@16 | 280 | return 0; |
michael@16 | 281 | crd.passwd = pv_val.rs; |
michael@16 | 282 | |
michael@16 | 283 | return &crd; |
michael@16 | 284 | } |
michael@16 | 285 | |
michael@16 | 286 | |
michael@16 | 287 | static inline void do_uac_auth(struct sip_msg *req, str *uri, |
michael@16 | 288 | struct uac_credential *crd, struct authenticate_body *auth, |
michael@16 | 289 | HASHHEX response) |
michael@16 | 290 | { |
michael@16 | 291 | HASHHEX ha1; |
michael@16 | 292 | HASHHEX ha2; |
michael@16 | 293 | |
michael@16 | 294 | if((auth->flags&QOP_AUTH) || (auth->flags&QOP_AUTH_INT)) |
michael@16 | 295 | { |
michael@16 | 296 | /* if qop generate nonce-count and cnonce */ |
michael@16 | 297 | cnonce.s = int2str(core_hash(&auth->nonce, 0, 0),&cnonce.len); |
michael@16 | 298 | |
michael@16 | 299 | /* do authentication */ |
michael@16 | 300 | uac_calc_HA1( crd, auth, &cnonce, ha1); |
michael@16 | 301 | uac_calc_HA2( &req->first_line.u.request.method, uri, |
michael@16 | 302 | auth, 0/*hentity*/, ha2 ); |
michael@16 | 303 | |
michael@16 | 304 | uac_calc_response( ha1, ha2, auth, &nc, &cnonce, response); |
michael@16 | 305 | auth->nc = &nc; |
michael@16 | 306 | auth->cnonce = &cnonce; |
michael@16 | 307 | } else { |
michael@16 | 308 | /* do authentication */ |
michael@16 | 309 | uac_calc_HA1( crd, auth, 0/*cnonce*/, ha1); |
michael@16 | 310 | uac_calc_HA2( &req->first_line.u.request.method, uri, |
michael@16 | 311 | auth, 0/*hentity*/, ha2 ); |
michael@16 | 312 | |
michael@16 | 313 | uac_calc_response( ha1, ha2, auth, 0/*nc*/, 0/*cnonce*/, response); |
michael@16 | 314 | } |
michael@16 | 315 | } |
michael@16 | 316 | |
michael@16 | 317 | |
michael@16 | 318 | static inline int apply_urihdr_changes( struct sip_msg *req, |
michael@16 | 319 | str *uri, str *hdr) |
michael@16 | 320 | { |
michael@16 | 321 | struct lump* anchor; |
michael@16 | 322 | |
michael@16 | 323 | /* add the uri - move it to branch directly FIXME (bogdan)*/ |
michael@16 | 324 | if (req->new_uri.s) |
michael@16 | 325 | { |
michael@16 | 326 | pkg_free(req->new_uri.s); |
michael@16 | 327 | req->new_uri.len=0; |
michael@16 | 328 | } |
michael@16 | 329 | req->parsed_uri_ok=0; |
michael@16 | 330 | req->new_uri.s = (char*)pkg_malloc(uri->len+1); |
michael@16 | 331 | if (req->new_uri.s==0) |
michael@16 | 332 | { |
michael@16 | 333 | LM_ERR("no more pkg\n"); |
michael@16 | 334 | goto error; |
michael@16 | 335 | } |
michael@16 | 336 | memcpy( req->new_uri.s, uri->s, uri->len); |
michael@16 | 337 | req->new_uri.s[uri->len]=0; |
michael@16 | 338 | req->new_uri.len=uri->len; |
michael@16 | 339 | |
michael@16 | 340 | /* add the header */ |
michael@16 | 341 | if (parse_headers(req, HDR_EOH_F, 0) == -1) |
michael@16 | 342 | { |
michael@16 | 343 | LM_ERR("failed to parse message\n"); |
michael@16 | 344 | goto error; |
michael@16 | 345 | } |
michael@16 | 346 | |
michael@16 | 347 | anchor = anchor_lump(req, req->unparsed - req->buf, 0, 0); |
michael@16 | 348 | if (anchor==0) |
michael@16 | 349 | { |
michael@16 | 350 | LM_ERR("failed to get anchor\n"); |
michael@16 | 351 | goto error; |
michael@16 | 352 | } |
michael@16 | 353 | |
michael@16 | 354 | if (insert_new_lump_before(anchor, hdr->s, hdr->len, 0) == 0) |
michael@16 | 355 | { |
michael@16 | 356 | LM_ERR("faield to insert lump\n"); |
michael@16 | 357 | goto error; |
michael@16 | 358 | } |
michael@16 | 359 | |
michael@16 | 360 | return 0; |
michael@16 | 361 | error: |
michael@16 | 362 | pkg_free( hdr->s ); |
michael@16 | 363 | return -1; |
michael@16 | 364 | } |
michael@16 | 365 | |
michael@16 | 366 | |
michael@16 | 367 | |
michael@16 | 368 | int uac_auth( struct sip_msg *msg) |
michael@16 | 369 | { |
michael@16 | 370 | static struct authenticate_body auth; |
michael@16 | 371 | struct uac_credential *crd; |
michael@16 | 372 | int code, branch; |
michael@16 | 373 | struct sip_msg *rpl; |
michael@16 | 374 | struct cell *t; |
michael@16 | 375 | struct hdr_field *hdr; |
michael@16 | 376 | HASHHEX response; |
michael@16 | 377 | str *new_hdr; |
michael@16 | 378 | |
michael@18 | 379 | /* pretransact */ |
michael@18 | 380 | int nret = 0; |
michael@18 | 381 | pv_value_t pv_val; |
michael@18 | 382 | str *newuri = 0; |
michael@18 | 383 | struct uac_credential *tst = 0; |
michael@18 | 384 | struct hdr_field *tmp_hdr = 0; |
michael@18 | 385 | struct hdr_field *del_hdr = 0; |
michael@18 | 386 | |
michael@18 | 387 | |
michael@18 | 388 | /* Goes something like this... */ |
michael@18 | 389 | /* HA1 = echo -n 'username:realm:password' | md5sum */ |
michael@18 | 390 | /* echo -n 'itsme:mydom.com:stupidpass' | md5sum */ |
michael@18 | 391 | /* HA2 = echo -n 'message:uri' | md5sum */ |
michael@18 | 392 | /* echo -n 'INVITE:sip:danc@ing.fool.es' | md5sum */ |
michael@18 | 393 | /* Response = echo -n 'HA1:nonce:HA2' | md5sum */ |
michael@16 | 394 | /* get transaction */ |
michael@16 | 395 | t = uac_tmb.t_gett(); |
michael@18 | 396 | if (t==T_UNDEFINED || t==T_NULL_CELL) { |
michael@18 | 397 | /* begin without any transaction */ |
michael@18 | 398 | /* set relevant structure variables */ |
michael@18 | 399 | crd = 0; |
michael@18 | 400 | crd = pkg_malloc(sizeof(struct uac_credential)); |
michael@18 | 401 | if (!crd) { |
michael@18 | 402 | LM_ERR("no more pkg memory\n"); |
michael@18 | 403 | goto error; |
michael@18 | 404 | } |
michael@16 | 405 | |
michael@18 | 406 | /* set the realm from existing UAC message */ |
michael@18 | 407 | tmp_hdr = msg->proxy_auth; |
michael@18 | 408 | del_hdr = 0; |
michael@18 | 409 | while (tmp_hdr) { |
michael@18 | 410 | crd->realm.s = strchr(strstr(tmp_hdr->body.s, "realm="), '"') + 1; |
michael@18 | 411 | crd->realm.len = strchr(crd->realm.s, '"') - crd->realm.s; |
michael@18 | 412 | if(pv_get_spec_value(msg, &auth_realm_spec, &pv_val)==0 \ |
michael@18 | 413 | && pv_val.rs.len>0) /* ensure realm is the desired one */ |
michael@18 | 414 | if (strncmp(crd->realm.s, pv_val.rs.s, crd->realm.len)==0) |
michael@18 | 415 | del_hdr = tmp_hdr; |
michael@18 | 416 | tmp_hdr = tmp_hdr->sibling; |
michael@18 | 417 | } |
michael@18 | 418 | if (del_hdr) |
michael@18 | 419 | crd->realm = pv_val.rs; /* success */ |
michael@18 | 420 | else |
michael@18 | 421 | nret++; /* failure */ |
michael@18 | 422 | |
michael@18 | 423 | /* set username from new AVP proxy values */ |
michael@18 | 424 | if(pv_get_spec_value(msg, &auth_username_spec, &pv_val)!=0 \ |
michael@18 | 425 | || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) |
michael@18 | 426 | nret++; /* signal failure with nonzero value */ |
michael@18 | 427 | else |
michael@18 | 428 | crd->user = pv_val.rs; |
michael@18 | 429 | |
michael@18 | 430 | /* set password from new AVP proxy values */ |
michael@18 | 431 | if(pv_get_spec_value(msg, &auth_password_spec, &pv_val)!=0 \ |
michael@18 | 432 | || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) |
michael@18 | 433 | nret++; /* signal failure with nonzero value */ |
michael@18 | 434 | else |
michael@18 | 435 | crd->passwd = pv_val.rs; |
michael@18 | 436 | |
michael@18 | 437 | if (nret) { /* if not found, look into predefined credentials */ |
michael@18 | 438 | tst = lookup_realm(&crd->realm); |
michael@18 | 439 | |
michael@18 | 440 | if (tst==0) { /* found? */ |
michael@18 | 441 | LM_DBG("no credential for realm \"%.*s\"\n", \ |
michael@18 | 442 | crd->realm.len, crd->realm.s); |
michael@18 | 443 | pkg_free(crd); |
michael@18 | 444 | goto error; |
michael@18 | 445 | } |
michael@18 | 446 | |
michael@18 | 447 | crd = tst; /* use predefined credentials */ |
michael@18 | 448 | /* set the realm from existing UAC message */ |
michael@18 | 449 | tmp_hdr = msg->proxy_auth; |
michael@18 | 450 | del_hdr = 0; |
michael@18 | 451 | while (tmp_hdr) { |
michael@18 | 452 | if(pv_get_spec_value(msg, &auth_realm_spec, &pv_val)==0 \ |
michael@18 | 453 | && pv_val.rs.len>0) /* ensure realm is the desired one */ |
michael@18 | 454 | if (strncmp(crd->realm.s, pv_val.rs.s, crd->realm.len)==0) |
michael@18 | 455 | del_hdr = tmp_hdr; |
michael@18 | 456 | tmp_hdr = tmp_hdr->sibling; |
michael@18 | 457 | } |
michael@18 | 458 | if (del_hdr == 0) { /* proxy-auth header matching realm not found */ |
michael@18 | 459 | LM_DBG("no credential for realm \"%.*s\"\n", \ |
michael@18 | 460 | crd->realm.len, crd->realm.s); |
michael@18 | 461 | pkg_free(crd); |
michael@18 | 462 | goto error; |
michael@18 | 463 | } |
michael@18 | 464 | } |
michael@18 | 465 | |
michael@18 | 466 | /* set the uri from existing UAC message */ |
michael@18 | 467 | newuri = pkg_malloc(sizeof(str)); |
michael@18 | 468 | if (!newuri) { |
michael@18 | 469 | LM_ERR("no more pkg memory\n"); |
michael@18 | 470 | goto error; |
michael@18 | 471 | } |
michael@18 | 472 | newuri->s = pkg_malloc(msg->new_uri.len); |
michael@18 | 473 | if (!newuri->s) { |
michael@18 | 474 | LM_ERR("no more pkg memory\n"); |
michael@18 | 475 | pkg_free(newuri); |
michael@18 | 476 | goto error; |
michael@18 | 477 | } |
michael@18 | 478 | newuri->len = msg->new_uri.len; |
michael@18 | 479 | strncpy(newuri->s, msg->new_uri.s, msg->new_uri.len); |
michael@18 | 480 | if (!newuri->s) { |
michael@18 | 481 | LM_DBG("failed to retrieve URI from UAC message\n"); |
michael@18 | 482 | pkg_free(newuri->s); |
michael@18 | 483 | pkg_free(newuri); |
michael@18 | 484 | goto error; |
michael@18 | 485 | } |
michael@18 | 486 | |
michael@18 | 487 | /* set the nonce from existing UAC message */ |
michael@18 | 488 | tmp_hdr = msg->proxy_auth; |
michael@18 | 489 | auth.nonce.len = 0; |
michael@18 | 490 | auth.nonce.s = 0; |
michael@18 | 491 | while (tmp_hdr) { |
michael@18 | 492 | if(pv_get_spec_value(msg, &auth_realm_spec, &pv_val)==0 \ |
michael@18 | 493 | && pv_val.rs.len>0) /* ensure realm is the desired one */ |
michael@18 | 494 | if (strncmp(crd->realm.s, pv_val.rs.s, crd->realm.len)==0) { |
michael@18 | 495 | auth.nonce.s = strchr(strstr(tmp_hdr->body.s, "nonce="), '"') + 1; |
michael@18 | 496 | auth.nonce.len = strchr(auth.nonce.s, '"') - auth.nonce.s; |
michael@18 | 497 | } |
michael@18 | 498 | tmp_hdr = tmp_hdr->sibling; |
michael@18 | 499 | } |
michael@18 | 500 | if (auth.nonce.s == 0) { |
michael@18 | 501 | LM_DBG("failed to retrieve nonce from UAC message\n"); |
michael@18 | 502 | pkg_free(crd); |
michael@18 | 503 | goto error; |
michael@18 | 504 | } |
michael@18 | 505 | |
michael@18 | 506 | /* do authentication */ |
michael@18 | 507 | do_uac_auth(msg, newuri, crd, &auth, response); |
michael@18 | 508 | if (response==0) { |
michael@18 | 509 | LM_ERR("failed to calculate challenge response\n"); |
michael@18 | 510 | pkg_free(crd); |
michael@18 | 511 | goto error; |
michael@18 | 512 | } |
michael@18 | 513 | |
michael@18 | 514 | /* build the authorization header */ |
michael@18 | 515 | new_hdr = build_authorization_hdr(407, newuri, crd, &auth, response); |
michael@18 | 516 | if (new_hdr==0) { |
michael@18 | 517 | LM_ERR("failed to build authorization hdr\n"); |
michael@18 | 518 | pkg_free(crd); |
michael@18 | 519 | goto error; |
michael@18 | 520 | } |
michael@18 | 521 | |
michael@18 | 522 | /* remove the old proxy-auth header and relink message index */ |
michael@18 | 523 | /* before updating the authorization credentials of the message */ |
michael@18 | 524 | if (del_hdr) { /* updated a record and must remove the old one */ |
michael@18 | 525 | if (del_lump(msg, del_hdr->name.s - msg->buf, del_hdr->len, 0)==0) { |
michael@18 | 526 | LM_ERR("can't remove credentials\n"); |
michael@18 | 527 | pkg_free(crd); |
michael@18 | 528 | goto error; |
michael@18 | 529 | } |
michael@18 | 530 | } |
michael@18 | 531 | |
michael@18 | 532 | /* so far, so good -> add the header and set the proper RURI */ |
michael@18 | 533 | if (apply_urihdr_changes(msg, newuri, new_hdr)<0) |
michael@18 | 534 | { |
michael@18 | 535 | LM_ERR("failed to apply changes\n"); |
michael@18 | 536 | pkg_free(crd); |
michael@18 | 537 | goto error; |
michael@18 | 538 | } |
michael@18 | 539 | |
michael@18 | 540 | pkg_free(crd); /* finished calculating new response string, success */ |
michael@18 | 541 | return 0; |
michael@18 | 542 | } /* if (t==T_UNDEFINED || t==T_NULL_CELL) */ |
michael@18 | 543 | |
michael@18 | 544 | /* begin with transaction reply */ |
michael@16 | 545 | /* get the selected branch */ |
michael@16 | 546 | branch = uac_tmb.t_get_picked(); |
michael@16 | 547 | if (branch<0) { |
michael@16 | 548 | LM_CRIT("no picked branch (%d)\n",branch); |
michael@16 | 549 | goto error; |
michael@16 | 550 | } |
michael@16 | 551 | |
michael@16 | 552 | rpl = t->uac[branch].reply; |
michael@16 | 553 | code = t->uac[branch].last_received; |
michael@16 | 554 | LM_DBG("picked reply is %p, code %d\n",rpl,code); |
michael@16 | 555 | |
michael@16 | 556 | if (rpl==0) |
michael@16 | 557 | { |
michael@16 | 558 | LM_CRIT("empty reply on picked branch\n"); |
michael@16 | 559 | goto error; |
michael@16 | 560 | } |
michael@16 | 561 | if (rpl==FAKED_REPLY) |
michael@16 | 562 | { |
michael@16 | 563 | LM_ERR("cannot process a FAKED reply\n"); |
michael@16 | 564 | goto error; |
michael@16 | 565 | } |
michael@16 | 566 | |
michael@16 | 567 | hdr = get_autenticate_hdr( rpl, code); |
michael@16 | 568 | if (hdr==0) |
michael@16 | 569 | { |
michael@16 | 570 | LM_ERR("failed to extract authenticate hdr\n"); |
michael@16 | 571 | goto error; |
michael@16 | 572 | } |
michael@16 | 573 | |
michael@16 | 574 | LM_DBG("header found; body=<%.*s>\n", |
michael@16 | 575 | hdr->body.len, hdr->body.s); |
michael@16 | 576 | |
michael@16 | 577 | if (parse_authenticate_body( &hdr->body, &auth)<0) |
michael@16 | 578 | { |
michael@16 | 579 | LM_ERR("failed to parse auth hdr body\n"); |
michael@16 | 580 | goto error; |
michael@16 | 581 | } |
michael@16 | 582 | |
michael@16 | 583 | /* can we authenticate this realm? */ |
michael@16 | 584 | crd = 0; |
michael@16 | 585 | /* first look into AVP, if set */ |
michael@16 | 586 | if ( auth_realm_spec.type==PVT_AVP ) |
michael@16 | 587 | crd = get_avp_credential( msg, &auth.realm ); |
michael@16 | 588 | /* if not found, look into predefined credentials */ |
michael@16 | 589 | if (crd==0) |
michael@16 | 590 | crd = lookup_realm( &auth.realm ); |
michael@16 | 591 | /* found? */ |
michael@16 | 592 | if (crd==0) |
michael@16 | 593 | { |
michael@16 | 594 | LM_DBG("no credential for realm \"%.*s\"\n", |
michael@16 | 595 | auth.realm.len, auth.realm.s); |
michael@16 | 596 | goto error; |
michael@16 | 597 | } |
michael@16 | 598 | |
michael@16 | 599 | /* do authentication */ |
michael@16 | 600 | do_uac_auth( msg, &t->uac[branch].uri, crd, &auth, response); |
michael@16 | 601 | |
michael@16 | 602 | /* build the authorization header */ |
michael@16 | 603 | new_hdr = build_authorization_hdr( code, &t->uac[branch].uri, |
michael@16 | 604 | crd, &auth, response); |
michael@16 | 605 | if (new_hdr==0) |
michael@16 | 606 | { |
michael@16 | 607 | LM_ERR("failed to build authorization hdr\n"); |
michael@16 | 608 | goto error; |
michael@16 | 609 | } |
michael@16 | 610 | |
michael@16 | 611 | /* so far, so good -> add the header and set the proper RURI */ |
michael@16 | 612 | if ( apply_urihdr_changes( msg, &t->uac[branch].uri, new_hdr)<0 ) |
michael@16 | 613 | { |
michael@16 | 614 | LM_ERR("failed to apply changes\n"); |
michael@16 | 615 | goto error; |
michael@16 | 616 | } |
michael@16 | 617 | |
michael@16 | 618 | /* increas the Cseq nr */ |
michael@16 | 619 | |
michael@16 | 620 | |
michael@16 | 621 | return 0; |
michael@16 | 622 | error: |
michael@16 | 623 | return -1; |
michael@16 | 624 | } |
michael@16 | 625 | |
michael@16 | 626 | |
michael@16 | 627 |