michael@16: /* michael@16: * $Id: auth.c 5901 2009-07-21 07:45:05Z bogdan_iancu $ michael@16: * michael@16: * Copyright (C) 2005 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: * 2006-03-02 UAC authentication looks first in AVPs for credential (bogdan) michael@16: */ michael@16: michael@16: michael@16: #include michael@16: #include michael@16: michael@16: #include "../../str.h" michael@16: #include "../../dprint.h" michael@16: #include "../../pvar.h" michael@16: #include "../../data_lump.h" michael@16: #include "../../mem/mem.h" michael@16: #include "../tm/tm_load.h" michael@16: michael@16: #include "auth.h" michael@16: #include "auth_alg.h" michael@16: #include "auth_hdr.h" michael@16: michael@16: michael@16: extern struct tm_binds uac_tmb; michael@16: extern pv_spec_t auth_username_spec; michael@16: extern pv_spec_t auth_realm_spec; michael@16: extern pv_spec_t auth_password_spec; michael@16: michael@16: michael@16: static struct uac_credential *crd_list = 0; michael@16: michael@16: michael@16: #define duplicate_str(_strd, _strs, _error) \ michael@16: do { \ michael@16: _strd.s = (char*)pkg_malloc(_strs.len); \ michael@16: if (_strd.s==0) \ michael@16: { \ michael@16: LM_ERR("no more pkg memory\n");\ michael@16: goto _error; \ michael@16: } \ michael@16: memcpy( _strd.s, _strs.s, _strs.len); \ michael@16: _strd.len = _strs.len; \ michael@16: }while(0) michael@16: michael@16: michael@16: #define WWW_AUTH_CODE 401 michael@16: #define WWW_AUTH_HDR "WWW-Authenticate" michael@16: #define WWW_AUTH_HDR_LEN (sizeof(WWW_AUTH_HDR)-1) michael@16: #define PROXY_AUTH_CODE 407 michael@16: #define PROXY_AUTH_HDR "Proxy-Authenticate" michael@16: #define PROXY_AUTH_HDR_LEN (sizeof(PROXY_AUTH_HDR)-1) michael@16: michael@16: static str nc = {"00000001", 8}; michael@16: static str cnonce = {"o", 1}; michael@16: michael@16: int has_credentials(void) michael@16: { michael@16: return (crd_list!=0)?1:0; michael@16: } michael@16: michael@16: void free_credential(struct uac_credential *crd) michael@16: { michael@16: if (crd) michael@16: { michael@16: if (crd->realm.s) michael@16: pkg_free(crd->realm.s); michael@16: if (crd->user.s) michael@16: pkg_free(crd->user.s); michael@16: if (crd->passwd.s) michael@16: pkg_free(crd->passwd.s); michael@16: pkg_free(crd); michael@16: } michael@16: } michael@16: michael@16: michael@16: int add_credential( unsigned int type, void *val) michael@16: { michael@16: struct uac_credential *crd; michael@16: char *p; michael@16: str foo; michael@16: michael@16: p = (char*)val; michael@16: crd = 0; michael@16: michael@16: if (p==0 || *p==0) michael@16: goto error; michael@16: michael@16: crd = (struct uac_credential*)pkg_malloc(sizeof(struct uac_credential)); michael@16: if (crd==0) michael@16: { michael@16: LM_ERR("no more pkg mem\n"); michael@16: goto error; michael@16: } michael@16: memset( crd, 0, sizeof(struct uac_credential)); michael@16: michael@16: /*parse the user */ michael@16: while (*p && isspace((int)*p)) p++; michael@16: foo.s = p; michael@16: while (*p && *p!=':' && !isspace((int)*p)) p++; michael@16: if (foo.s==p || *p==0) michael@16: /* missing or empty user */ michael@16: goto parse_error; michael@16: foo.len = p - foo.s; michael@16: /* dulicate it */ michael@16: duplicate_str( crd->user, foo, error); michael@16: michael@16: /* parse the ':' separator */ michael@16: while (*p && isspace((int)*p)) p++; michael@16: if (*p!=':') michael@16: goto parse_error; michael@16: p++; michael@16: while (*p && isspace((int)*p)) p++; michael@16: if (*p==0) michael@16: goto parse_error; michael@16: michael@16: /*parse the realm */ michael@16: while (*p && isspace((int)*p)) p++; michael@16: foo.s = p; michael@16: while (*p && *p!=':' && !isspace((int)*p)) p++; michael@16: if (foo.s==p || *p==0) michael@16: /* missing or empty realm */ michael@16: goto parse_error; michael@16: foo.len = p - foo.s; michael@16: /* dulicate it */ michael@16: duplicate_str( crd->realm, foo, error); michael@16: michael@16: /* parse the ':' separator */ michael@16: while (*p && isspace((int)*p)) p++; michael@16: if (*p!=':') michael@16: goto parse_error; michael@16: p++; michael@16: while (*p && isspace((int)*p)) p++; michael@16: if (*p==0) michael@16: goto parse_error; michael@16: michael@16: /*parse the passwd */ michael@16: while (*p && isspace((int)*p)) p++; michael@16: foo.s = p; michael@16: while (*p && !isspace((int)*p)) p++; michael@16: if (foo.s==p) michael@16: /* missing or empty passwd */ michael@16: goto parse_error; michael@16: foo.len = p - foo.s; michael@16: /* dulicate it */ michael@16: duplicate_str( crd->passwd, foo, error); michael@16: michael@16: /* end of string */ michael@16: while (*p && isspace((int)*p)) p++; michael@16: if (*p!=0) michael@16: goto parse_error; michael@16: michael@16: /* link the new cred struct */ michael@16: crd->next = crd_list; michael@16: crd_list = crd; michael@16: michael@16: pkg_free(val); michael@16: return 0; michael@16: parse_error: michael@16: LM_ERR("parse error in <%s> " michael@16: "around %ld\n", (char*)val, (long)(p-(char*)val)); michael@16: error: michael@16: if (crd) michael@16: free_credential(crd); michael@16: return -1; michael@16: } michael@16: michael@16: michael@16: void destroy_credentials(void) michael@16: { michael@16: struct uac_credential *foo; michael@16: michael@16: while (crd_list) michael@16: { michael@16: foo = crd_list; michael@16: crd_list = crd_list->next; michael@16: free_credential(foo); michael@16: } michael@16: crd_list = 0; michael@16: } michael@16: michael@16: michael@16: static inline struct hdr_field *get_autenticate_hdr(struct sip_msg *rpl, michael@16: int rpl_code) michael@16: { michael@16: struct hdr_field *hdr; michael@16: str hdr_name; michael@16: michael@16: /* what hdr should we look for */ michael@16: if (rpl_code==WWW_AUTH_CODE) michael@16: { michael@16: hdr_name.s = WWW_AUTH_HDR; michael@16: hdr_name.len = WWW_AUTH_HDR_LEN; michael@16: } else if (rpl_code==PROXY_AUTH_CODE) { michael@16: hdr_name.s = PROXY_AUTH_HDR; michael@16: hdr_name.len = PROXY_AUTH_HDR_LEN; michael@16: } else { michael@16: LM_ERR("reply is not an " michael@16: "auth request\n"); michael@16: goto error; michael@16: } michael@16: michael@16: LM_DBG("looking for header \"%.*s\"\n", michael@16: hdr_name.len, hdr_name.s); michael@16: michael@16: /* search the auth hdr, but first parse them all */ michael@16: if (parse_headers( rpl, HDR_EOH_F, 0)<0) michael@16: { michael@16: LM_ERR("failed to parse reply\n"); michael@16: goto error; michael@16: } michael@16: hdr = get_header_by_name( rpl , hdr_name.s, hdr_name.len); michael@16: if (hdr) michael@16: return hdr; michael@16: michael@16: LM_ERR("reply has no " michael@16: "auth hdr (%.*s)\n", hdr_name.len, hdr_name.s); michael@16: error: michael@16: return 0; michael@16: } michael@16: michael@16: michael@16: static inline struct uac_credential *lookup_realm( str *realm) michael@16: { michael@16: struct uac_credential *crd; michael@16: michael@16: for( crd=crd_list ; crd ; crd=crd->next ) michael@16: if (realm->len==crd->realm.len && michael@16: strncmp( realm->s, crd->realm.s, realm->len)==0 ) michael@16: return crd; michael@16: return 0; michael@16: } michael@16: michael@16: michael@16: static inline struct uac_credential *get_avp_credential(struct sip_msg *msg, michael@16: str *realm) michael@16: { michael@16: static struct uac_credential crd; michael@16: pv_value_t pv_val; michael@16: michael@16: if(pv_get_spec_value( msg, &auth_realm_spec, &pv_val)!=0 michael@16: || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) michael@16: return 0; michael@16: michael@16: crd.realm = pv_val.rs; michael@16: /* is it the domain we are looking for? */ michael@16: if (realm->len!=crd.realm.len || michael@16: strncmp( realm->s, crd.realm.s, realm->len)!=0 ) michael@16: return 0; michael@16: michael@16: /* get username and password */ michael@16: if(pv_get_spec_value( msg, &auth_username_spec, &pv_val)!=0 michael@16: || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) michael@16: return 0; michael@16: crd.user = pv_val.rs; michael@16: michael@16: if(pv_get_spec_value( msg, &auth_password_spec, &pv_val)!=0 michael@16: || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0) michael@16: return 0; michael@16: crd.passwd = pv_val.rs; michael@16: michael@16: return &crd; michael@16: } michael@16: michael@16: michael@16: static inline void do_uac_auth(struct sip_msg *req, str *uri, michael@16: struct uac_credential *crd, struct authenticate_body *auth, michael@16: HASHHEX response) michael@16: { michael@16: HASHHEX ha1; michael@16: HASHHEX ha2; michael@16: michael@16: if((auth->flags&QOP_AUTH) || (auth->flags&QOP_AUTH_INT)) michael@16: { michael@16: /* if qop generate nonce-count and cnonce */ michael@16: cnonce.s = int2str(core_hash(&auth->nonce, 0, 0),&cnonce.len); michael@16: michael@16: /* do authentication */ michael@16: uac_calc_HA1( crd, auth, &cnonce, ha1); michael@16: uac_calc_HA2( &req->first_line.u.request.method, uri, michael@16: auth, 0/*hentity*/, ha2 ); michael@16: michael@16: uac_calc_response( ha1, ha2, auth, &nc, &cnonce, response); michael@16: auth->nc = &nc; michael@16: auth->cnonce = &cnonce; michael@16: } else { michael@16: /* do authentication */ michael@16: uac_calc_HA1( crd, auth, 0/*cnonce*/, ha1); michael@16: uac_calc_HA2( &req->first_line.u.request.method, uri, michael@16: auth, 0/*hentity*/, ha2 ); michael@16: michael@16: uac_calc_response( ha1, ha2, auth, 0/*nc*/, 0/*cnonce*/, response); michael@16: } michael@16: } michael@16: michael@16: michael@16: static inline int apply_urihdr_changes( struct sip_msg *req, michael@16: str *uri, str *hdr) michael@16: { michael@16: struct lump* anchor; michael@16: michael@16: /* add the uri - move it to branch directly FIXME (bogdan)*/ michael@16: if (req->new_uri.s) michael@16: { michael@16: pkg_free(req->new_uri.s); michael@16: req->new_uri.len=0; michael@16: } michael@16: req->parsed_uri_ok=0; michael@16: req->new_uri.s = (char*)pkg_malloc(uri->len+1); michael@16: if (req->new_uri.s==0) michael@16: { michael@16: LM_ERR("no more pkg\n"); michael@16: goto error; michael@16: } michael@16: memcpy( req->new_uri.s, uri->s, uri->len); michael@16: req->new_uri.s[uri->len]=0; michael@16: req->new_uri.len=uri->len; michael@16: michael@16: /* add the header */ michael@16: if (parse_headers(req, HDR_EOH_F, 0) == -1) michael@16: { michael@16: LM_ERR("failed to parse message\n"); michael@16: goto error; michael@16: } michael@16: michael@16: anchor = anchor_lump(req, req->unparsed - req->buf, 0, 0); michael@16: if (anchor==0) michael@16: { michael@16: LM_ERR("failed to get anchor\n"); michael@16: goto error; michael@16: } michael@16: michael@16: if (insert_new_lump_before(anchor, hdr->s, hdr->len, 0) == 0) michael@16: { michael@16: LM_ERR("faield to insert lump\n"); michael@16: goto error; michael@16: } michael@16: michael@16: return 0; michael@16: error: michael@16: pkg_free( hdr->s ); michael@16: return -1; michael@16: } michael@16: michael@16: michael@16: michael@16: int uac_auth( struct sip_msg *msg) michael@16: { michael@16: static struct authenticate_body auth; michael@16: struct uac_credential *crd; michael@16: int code, branch; michael@16: struct sip_msg *rpl; michael@16: struct cell *t; michael@16: struct hdr_field *hdr; michael@16: HASHHEX response; michael@16: str *new_hdr; michael@16: michael@16: /* get transaction */ michael@16: t = uac_tmb.t_gett(); michael@16: if (t==T_UNDEFINED || t==T_NULL_CELL) michael@16: { michael@16: LM_CRIT("no current transaction found\n"); michael@16: goto error; michael@16: } michael@16: michael@16: /* get the selected branch */ michael@16: branch = uac_tmb.t_get_picked(); michael@16: if (branch<0) { michael@16: LM_CRIT("no picked branch (%d)\n",branch); michael@16: goto error; michael@16: } michael@16: michael@16: rpl = t->uac[branch].reply; michael@16: code = t->uac[branch].last_received; michael@16: LM_DBG("picked reply is %p, code %d\n",rpl,code); michael@16: michael@16: if (rpl==0) michael@16: { michael@16: LM_CRIT("empty reply on picked branch\n"); michael@16: goto error; michael@16: } michael@16: if (rpl==FAKED_REPLY) michael@16: { michael@16: LM_ERR("cannot process a FAKED reply\n"); michael@16: goto error; michael@16: } michael@16: michael@16: hdr = get_autenticate_hdr( rpl, code); michael@16: if (hdr==0) michael@16: { michael@16: LM_ERR("failed to extract authenticate hdr\n"); michael@16: goto error; michael@16: } michael@16: michael@16: LM_DBG("header found; body=<%.*s>\n", michael@16: hdr->body.len, hdr->body.s); michael@16: michael@16: if (parse_authenticate_body( &hdr->body, &auth)<0) michael@16: { michael@16: LM_ERR("failed to parse auth hdr body\n"); michael@16: goto error; michael@16: } michael@16: michael@16: /* can we authenticate this realm? */ michael@16: crd = 0; michael@16: /* first look into AVP, if set */ michael@16: if ( auth_realm_spec.type==PVT_AVP ) michael@16: crd = get_avp_credential( msg, &auth.realm ); michael@16: /* if not found, look into predefined credentials */ michael@16: if (crd==0) michael@16: crd = lookup_realm( &auth.realm ); michael@16: /* found? */ michael@16: if (crd==0) michael@16: { michael@16: LM_DBG("no credential for realm \"%.*s\"\n", michael@16: auth.realm.len, auth.realm.s); michael@16: goto error; michael@16: } michael@16: michael@16: /* do authentication */ michael@16: do_uac_auth( msg, &t->uac[branch].uri, crd, &auth, response); michael@16: michael@16: /* build the authorization header */ michael@16: new_hdr = build_authorization_hdr( code, &t->uac[branch].uri, michael@16: crd, &auth, response); michael@16: if (new_hdr==0) michael@16: { michael@16: LM_ERR("failed to build authorization hdr\n"); michael@16: goto error; michael@16: } michael@16: michael@16: /* so far, so good -> add the header and set the proper RURI */ michael@16: if ( apply_urihdr_changes( msg, &t->uac[branch].uri, new_hdr)<0 ) michael@16: { michael@16: LM_ERR("failed to apply changes\n"); michael@16: goto error; michael@16: } michael@16: michael@16: /* increas the Cseq nr */ michael@16: michael@16: michael@16: return 0; michael@16: error: michael@16: return -1; michael@16: } michael@16: michael@16: michael@16: