michael@16: /* michael@16: * $Id: uac.c 6481 2009-12-29 13:49:37Z bogdan_iancu $ michael@16: * michael@16: * Copyright (C) 2005-2009 Voice Sistem SRL michael@16: * michael@16: * This file is part of opensips, a free SIP server. michael@16: * michael@16: * UAC OpenSIPS-module is free software; you can redistribute it and/or michael@16: * modify it under the terms of the GNU General Public License michael@16: * as published by the Free Software Foundation; either version 2 michael@16: * of the License, or (at your option) any later version. michael@16: * michael@16: * UAC OpenSIPS-module is distributed in the hope that it will be useful, michael@16: * but WITHOUT ANY WARRANTY; without even the implied warranty of michael@16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the michael@16: * GNU General Public License for more details. michael@16: * michael@16: * You should have received a copy of the GNU General Public License michael@16: * along with this program; if not, write to the Free Software michael@16: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. michael@16: * michael@16: * michael@16: * History: michael@16: * --------- michael@16: * 2005-01-31 first version (ramona) michael@16: * 2005-08-12 some TM callbacks replaced with RR callback - more efficient; michael@16: * (bogdan) michael@16: * 2006-03-02 UAC authentication looks first in AVPs for credential (bogdan) michael@16: * 2006-03-03 the RR parameter is encrypted via XOR with a password michael@16: * (bogdan) michael@16: * 2009-08-22 TO header replacement added (bogdan) michael@18: * 2010-01-18 UAC is able to construct credentials from request (msvb) michael@16: michael@16: */ michael@16: michael@16: michael@16: #include michael@16: #include michael@16: #include michael@16: michael@16: #include "../../sr_module.h" michael@16: #include "../../dprint.h" michael@16: #include "../../error.h" michael@16: #include "../../pvar.h" michael@16: #include "../../mem/mem.h" michael@16: #include "../../parser/parse_from.h" michael@16: #include "../tm/tm_load.h" michael@16: #include "../tm/t_hooks.h" michael@16: #include "../rr/api.h" michael@16: michael@16: #include "replace.h" michael@16: #include "auth.h" michael@16: michael@16: michael@16: michael@16: michael@16: michael@16: /* local variable used for init */ michael@16: static char* restore_mode_str = NULL; michael@16: static char* auth_username_avp = NULL; michael@16: static char* auth_realm_avp = NULL; michael@16: static char* auth_password_avp = NULL; michael@16: michael@16: /* global param variables */ michael@16: str rr_from_param = str_init("vsf"); michael@16: str rr_to_param = str_init("vst"); michael@16: str uac_passwd = str_init(""); michael@16: int restore_mode = UAC_AUTO_RESTORE; michael@16: struct tm_binds uac_tmb; michael@16: struct rr_binds uac_rrb; michael@16: pv_spec_t auth_username_spec; michael@16: pv_spec_t auth_realm_spec; michael@16: pv_spec_t auth_password_spec; michael@16: michael@16: static int w_replace_from(struct sip_msg* msg, char* p1, char* p2); michael@16: static int w_restore_from(struct sip_msg* msg); michael@16: michael@16: static int w_replace_to(struct sip_msg* msg, char* p1, char* p2); michael@16: static int w_restore_to(struct sip_msg* msg); michael@16: michael@16: static int w_uac_auth(struct sip_msg* msg, char* str, char* str2); michael@16: static int fixup_replace_uri(void** param, int param_no); michael@16: static int fixup_replace_disp_uri(void** param, int param_no); michael@16: static int mod_init(void); michael@16: static void mod_destroy(void); michael@16: michael@16: michael@16: /* Exported functions */ michael@16: static cmd_export_t cmds[]={ michael@16: {"uac_replace_from", (cmd_function)w_replace_from, 2, michael@16: fixup_replace_disp_uri, 0, michael@16: REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE }, michael@16: {"uac_replace_from", (cmd_function)w_replace_from, 1, michael@16: fixup_replace_uri, 0, michael@16: REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE }, michael@16: {"uac_restore_from", (cmd_function)w_restore_from, 0, michael@16: 0, 0, michael@16: REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE }, michael@16: {"uac_replace_to", (cmd_function)w_replace_to, 2, michael@16: fixup_replace_disp_uri, 0, michael@16: REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE }, michael@16: {"uac_replace_to", (cmd_function)w_replace_to, 1, michael@16: fixup_replace_uri, 0, michael@16: REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE }, michael@16: {"uac_restore_to", (cmd_function)w_restore_to, 0, michael@16: 0, 0, michael@16: REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE }, michael@16: {"uac_auth", (cmd_function)w_uac_auth, 0, michael@16: 0, 0, michael@18: REQUEST_ROUTE|FAILURE_ROUTE }, michael@16: {0,0,0,0,0,0} michael@16: }; michael@16: michael@16: michael@16: michael@16: /* Exported parameters */ michael@16: static param_export_t params[] = { michael@16: {"rr_from_store_param", STR_PARAM, &rr_from_param.s }, michael@16: {"rr_to_store_param", STR_PARAM, &rr_to_param.s }, michael@16: {"restore_mode", STR_PARAM, &restore_mode_str }, michael@16: {"restore_passwd", STR_PARAM, &uac_passwd.s }, michael@16: {"credential", STR_PARAM|USE_FUNC_PARAM, (void*)&add_credential }, michael@16: {"auth_username_avp", STR_PARAM, &auth_username_avp }, michael@16: {"auth_realm_avp", STR_PARAM, &auth_realm_avp }, michael@16: {"auth_password_avp", STR_PARAM, &auth_password_avp }, michael@16: {0, 0, 0} michael@16: }; michael@16: michael@16: michael@16: michael@16: struct module_exports exports= { michael@16: "uac", michael@16: MODULE_VERSION, michael@16: DEFAULT_DLFLAGS, /* dlopen flags */ michael@16: cmds, /* exported functions */ michael@16: params, /* param exports */ michael@16: 0, /* exported statistics */ michael@16: 0, /* exported MI functions */ michael@16: 0, /* exported pseudo-variables */ michael@16: 0, /* extra processes */ michael@16: mod_init, /* module initialization function */ michael@16: (response_function) 0, michael@16: mod_destroy, michael@16: 0 /* per-child init function */ michael@16: }; michael@16: michael@16: michael@16: inline static int parse_auth_avp( char *avp_spec, pv_spec_t *avp, char *txt) michael@16: { michael@16: str s; michael@16: s.s = avp_spec; s.len = strlen(s.s); michael@16: if (pv_parse_spec(&s, avp)==NULL) { michael@16: LM_ERR("malformed or non AVP %s AVP definition\n",txt); michael@16: return -1; michael@16: } michael@16: return 0; michael@16: } michael@16: michael@16: michael@16: static int mod_init(void) michael@16: { michael@16: LM_INFO("initializing...\n"); michael@16: michael@16: if (restore_mode_str && *restore_mode_str) { michael@16: if (strcasecmp(restore_mode_str,"none")==0) { michael@16: restore_mode = UAC_NO_RESTORE; michael@16: } else if (strcasecmp(restore_mode_str,"manual")==0) { michael@16: restore_mode = UAC_MANUAL_RESTORE; michael@16: } else if (strcasecmp(restore_mode_str,"auto")==0) { michael@16: restore_mode = UAC_AUTO_RESTORE; michael@16: } else { michael@16: LM_ERR("unsupported value '%s' for restore_mode\n", michael@16: restore_mode_str); michael@16: goto error; michael@16: } michael@16: } michael@16: michael@16: rr_from_param.len = strlen(rr_from_param.s); michael@16: rr_to_param.len = strlen(rr_to_param.s); michael@16: if ( (rr_from_param.len==0 || rr_to_param.len==0) && michael@16: restore_mode!=UAC_NO_RESTORE) michael@16: { michael@16: LM_ERR("rr_store_param cannot be empty if FROM is restoreable\n"); michael@16: goto error; michael@16: } michael@16: michael@16: uac_passwd.len = strlen(uac_passwd.s); michael@16: michael@16: /* parse the auth AVP spesc, if any */ michael@16: if ( auth_username_avp || auth_password_avp || auth_realm_avp) { michael@16: if (!auth_username_avp || !auth_password_avp || !auth_realm_avp) { michael@16: LM_ERR("partial definition of auth AVP!"); michael@16: goto error; michael@16: } michael@16: if ( parse_auth_avp(auth_realm_avp, &auth_realm_spec, "realm")<0 michael@16: || parse_auth_avp(auth_username_avp, &auth_username_spec, "username")<0 michael@16: || parse_auth_avp(auth_password_avp, &auth_password_spec, "password")<0 michael@16: ) { michael@16: goto error; michael@16: } michael@16: } else { michael@16: memset( &auth_realm_spec, 0, sizeof(pv_spec_t)); michael@16: memset( &auth_password_spec, 0, sizeof(pv_spec_t)); michael@16: memset( &auth_username_spec, 0, sizeof(pv_spec_t)); michael@16: } michael@16: michael@16: /* load the TM API - FIXME it should be loaded only michael@16: * if NO_RESTORE and AUTH */ michael@16: if (load_tm_api(&uac_tmb)!=0) { michael@16: LM_ERR("can't load TM API\n"); michael@16: goto error; michael@16: } michael@16: michael@16: if (restore_mode!=UAC_NO_RESTORE) { michael@16: /* load the RR API */ michael@16: if (load_rr_api(&uac_rrb)!=0) { michael@16: LM_ERR("can't load RR API\n"); michael@16: goto error; michael@16: } michael@16: michael@16: if (restore_mode==UAC_AUTO_RESTORE) { michael@16: /* we need the append_fromtag on in RR */ michael@16: if (!uac_rrb.append_fromtag) { michael@16: LM_ERR("'append_fromtag' RR param is not enabled!" michael@16: " - required by AUTO restore mode\n"); michael@16: goto error; michael@16: } michael@16: /* get all requests doing loose route */ michael@16: if (uac_rrb.register_rrcb( rr_checker, 0)!=0) { michael@16: LM_ERR("failed to install RR callback\n"); michael@16: goto error; michael@16: } michael@16: } michael@16: } michael@16: michael@16: init_from_replacer(); michael@16: michael@16: return 0; michael@16: error: michael@16: return -1; michael@16: } michael@16: michael@16: michael@16: static void mod_destroy(void) michael@16: { michael@16: destroy_credentials(); michael@16: } michael@16: michael@16: michael@16: michael@16: /************************** fixup functions ******************************/ michael@16: michael@16: static int fixup_replace_uri(void** param, int param_no) michael@16: { michael@16: pv_elem_t *model; michael@16: str s; michael@16: michael@16: model=NULL; michael@16: s.s = (char*)(*param); s.len = strlen(s.s); michael@16: if(pv_parse_format(&s, &model)<0) michael@16: { michael@16: LM_ERR("wrong format[%s]!\n",(char*)(*param)); michael@16: return E_UNSPEC; michael@16: } michael@16: if (model==NULL) michael@16: { michael@16: LM_ERR("empty parameter!\n"); michael@16: return E_UNSPEC; michael@16: } michael@16: *param = (void*)model; michael@16: michael@16: return 0; michael@16: } michael@16: michael@16: michael@16: static int fixup_replace_disp_uri(void** param, int param_no) michael@16: { michael@16: pv_elem_t *model; michael@16: char *p; michael@16: str s; michael@16: michael@16: /* convert to str */ michael@16: s.s = (char*)*param; michael@16: s.len = strlen(s.s); michael@16: michael@16: model=NULL; michael@16: if (param_no==1 && s.len) { michael@16: /* put " around display name */ michael@16: p = (char*)pkg_malloc(s.len+3); michael@16: if (p==0) { michael@16: LM_CRIT("no more pkg mem\n"); michael@16: return E_OUT_OF_MEM; michael@16: } michael@16: p[0] = '\"'; michael@16: memcpy(p+1, s.s, s.len); michael@16: p[s.len+1] = '\"'; michael@16: p[s.len+2] = '\0'; michael@16: pkg_free(s.s); michael@16: s.s = p; michael@16: s.len += 2; michael@16: } michael@16: if(pv_parse_format(&s ,&model)<0) { michael@16: LM_ERR("wrong format [%s] for param no %d!\n", s.s, param_no); michael@16: pkg_free(s.s); michael@16: return E_UNSPEC; michael@16: } michael@16: *param = (void*)model; michael@16: michael@16: return 0; michael@16: } michael@16: michael@16: michael@16: michael@16: /************************** wrapper functions ******************************/ michael@16: michael@16: static int w_restore_from(struct sip_msg *msg) michael@16: { michael@16: /* safety checks - must be a request */ michael@16: if (msg->first_line.type!=SIP_REQUEST) { michael@16: LM_ERR("called for something not request\n"); michael@16: return -1; michael@16: } michael@16: michael@16: return (restore_uri(msg,&rr_from_param,1)==0)?1:-1; michael@16: } michael@16: michael@16: michael@16: static int w_replace_from(struct sip_msg* msg, char* p1, char* p2) michael@16: { michael@16: str uri_s; michael@16: str dsp_s; michael@16: str *uri; michael@16: str *dsp; michael@16: michael@16: if (p2==NULL) { michael@16: p2 = p1; michael@16: p1 = NULL; michael@16: dsp = NULL; michael@16: } michael@16: michael@16: /* p1 dispaly , p2 uri */ michael@16: michael@16: if ( p1!=NULL ) { michael@16: if(pv_printf_s( msg, (pv_elem_p)p1, &dsp_s)!=0) michael@16: return -1; michael@16: dsp = &dsp_s; michael@16: } michael@16: michael@16: /* compute the URI string; if empty string -> make it NULL */ michael@16: if (pv_printf_s( msg, (pv_elem_p)p2, &uri_s)!=0) michael@16: return -1; michael@16: uri = uri_s.len?&uri_s:NULL; michael@16: michael@16: if (parse_from_header(msg)<0 ) { michael@16: LM_ERR("failed to find/parse FROM hdr\n"); michael@16: return -1; michael@16: } michael@16: michael@16: LM_DBG("dsp=%p (len=%d) , uri=%p (len=%d)\n", michael@16: dsp,dsp?dsp->len:0,uri,uri?uri->len:0); michael@16: michael@16: return (replace_uri(msg, dsp, uri, msg->from, &rr_from_param)==0)?1:-1; michael@16: } michael@16: michael@16: michael@16: static int w_restore_to(struct sip_msg *msg) michael@16: { michael@16: /* safety checks - must be a request */ michael@16: if (msg->first_line.type!=SIP_REQUEST) { michael@16: LM_ERR("called for something not request\n"); michael@16: return -1; michael@16: } michael@16: michael@16: return (restore_uri(msg,&rr_to_param,0)==0)?1:-1; michael@16: } michael@16: michael@16: michael@16: static int w_replace_to(struct sip_msg* msg, char* p1, char* p2) michael@16: { michael@16: str uri_s; michael@16: str dsp_s; michael@16: str *uri; michael@16: str *dsp; michael@16: michael@16: if (p2==NULL) { michael@16: p2 = p1; michael@16: p1 = NULL; michael@16: dsp = NULL; michael@16: } michael@16: michael@16: /* p1 dispaly , p2 uri */ michael@16: michael@16: if ( p1!=NULL ) { michael@16: if(pv_printf_s( msg, (pv_elem_p)p1, &dsp_s)!=0) michael@16: return -1; michael@16: dsp = &dsp_s; michael@16: } michael@16: michael@16: /* compute the URI string; if empty string -> make it NULL */ michael@16: if (pv_printf_s( msg, (pv_elem_p)p2, &uri_s)!=0) michael@16: return -1; michael@16: uri = uri_s.len?&uri_s:NULL; michael@16: michael@16: /* parse TO hdr */ michael@16: if ( msg->to==0 && (parse_headers(msg,HDR_TO_F,0)!=0 || msg->to==0) ) { michael@16: LM_ERR("failed to parse TO hdr\n"); michael@16: return -1; michael@16: } michael@16: michael@16: return (replace_uri(msg, dsp, uri, msg->to, &rr_to_param)==0)?1:-1; michael@16: } michael@16: michael@16: michael@16: michael@16: michael@16: static int w_uac_auth(struct sip_msg* msg, char* str, char* str2) michael@16: { michael@16: return (uac_auth(msg)==0)?1:-1; michael@16: } michael@16: michael@16: