123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- /*
- * $Id$
- *
- * Digest Authentication - Database support
- *
- * Copyright (C) 2001-2003 FhG Fokus
- *
- * This file is part of Kamailio, a free SIP server.
- *
- * Kamailio is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * Kamailio is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * history:
- * ---------
- * 2003-02-28 scratchpad compatibility abandoned
- * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri)
- * 2004-06-06 updated to the new DB api, added auth_db_{init,bind,close,ver}
- * (andrei)
- * 2005-05-31 general definition of AVPs in credentials now accepted - ID AVP,
- * STRING AVP, AVP aliases (bogdan)
- * 2006-03-01 pseudo variables support for domain name (bogdan)
- */
- #include <string.h>
- #include "../../ut.h"
- #include "../../str.h"
- #include "../../lib/srdb1/db.h"
- #include "../../lib/srdb1/db_ut.h"
- #include "../../dprint.h"
- #include "../../parser/digest/digest.h"
- #include "../../parser/hf.h"
- #include "../../parser/parser_f.h"
- #include "../../parser/parse_from.h"
- #include "../../parser/parse_to.h"
- #include "../../parser/parse_uri.h"
- #include "../../usr_avp.h"
- #include "../../mod_fix.h"
- #include "../../mem/mem.h"
- #include "api.h"
- #include "authdb_mod.h"
- int fetch_credentials(sip_msg_t *msg, str *user, str* domain, str *table)
- {
- pv_elem_t *cred;
- db_key_t keys[2];
- db_val_t vals[2];
- db_key_t *col;
- db1_res_t *res = NULL;
- int n, nc;
- col = pkg_malloc(sizeof(*col) * (credentials_n + 1));
- if (col == NULL) {
- LM_ERR("no more pkg memory\n");
- return -1;
- }
- keys[0] = &user_column;
- keys[1] = &domain_column;
- for (n = 0, cred=credentials; cred ; n++, cred=cred->next) {
- col[n] = &cred->text;
- }
- VAL_TYPE(vals) = VAL_TYPE(vals + 1) = DB1_STR;
- VAL_NULL(vals) = VAL_NULL(vals + 1) = 0;
- n = 1;
- VAL_STR(vals) = *user;
- if (domain && domain->len) {
- VAL_STR(vals + 1) = *domain;
- n = 2;
- }
- nc = credentials_n;
- if (auth_dbf.use_table(auth_db_handle, table) < 0) {
- LM_ERR("failed to use_table\n");
- pkg_free(col);
- return -1;
- }
- if (auth_dbf.query(auth_db_handle, keys, 0, vals, col, n, nc, 0, &res) < 0) {
- LM_ERR("failed to query database\n");
- pkg_free(col);
- if(res)
- auth_dbf.free_result(auth_db_handle, res);
- return -1;
- }
- pkg_free(col);
- if (RES_ROW_N(res) == 0) {
- if(res)
- auth_dbf.free_result(auth_db_handle, res);
- LM_DBG("no result for user \'%.*s%s%.*s\' in [%.*s]\n",
- user->len, user->s, (n==2)?"@":"",
- (n==2)?domain->len:0, (n==2)?domain->s:"",
- table->len, table->s);
- return -2;
- }
- for (cred=credentials, n=0; cred; cred=cred->next, n++) {
- if (db_val2pv_spec(msg, &RES_ROWS(res)[0].values[n], cred->spec) != 0) {
- if(res)
- auth_dbf.free_result(auth_db_handle, res);
- LM_ERR("Failed to convert value for column %.*s\n",
- RES_NAMES(res)[n]->len, RES_NAMES(res)[n]->s);
- return -3;
- }
- }
- if(res)
- auth_dbf.free_result(auth_db_handle, res);
- return 0;
- }
- static inline int get_ha1(struct username* _username, str* _domain,
- const str* _table, char* _ha1, db1_res_t** res)
- {
- pv_elem_t *cred;
- db_key_t keys[2];
- db_val_t vals[2];
- db_key_t *col;
- str result;
- int n, nc;
- col = pkg_malloc(sizeof(*col) * (credentials_n + 1));
- if (col == NULL) {
- LM_ERR("no more pkg memory\n");
- return -1;
- }
- keys[0] = &user_column;
- keys[1] = &domain_column;
- /* should we calculate the HA1, and is it calculated with domain? */
- col[0] = (_username->domain.len && !calc_ha1) ?
- (&pass_column_2) : (&pass_column);
- for (n = 0, cred=credentials; cred ; n++, cred=cred->next) {
- col[1 + n] = &cred->text;
- }
- VAL_TYPE(vals) = VAL_TYPE(vals + 1) = DB1_STR;
- VAL_NULL(vals) = VAL_NULL(vals + 1) = 0;
- VAL_STR(vals).s = _username->user.s;
- VAL_STR(vals).len = _username->user.len;
- if (_username->domain.len) {
- VAL_STR(vals + 1) = _username->domain;
- } else {
- VAL_STR(vals + 1) = *_domain;
- }
- n = (use_domain ? 2 : 1);
- nc = 1 + credentials_n;
- if (auth_dbf.use_table(auth_db_handle, _table) < 0) {
- LM_ERR("failed to use_table\n");
- pkg_free(col);
- return -1;
- }
- if (auth_dbf.query(auth_db_handle, keys, 0, vals, col, n, nc, 0, res) < 0) {
- LM_ERR("failed to query database\n");
- pkg_free(col);
- return -1;
- }
- pkg_free(col);
- if (RES_ROW_N(*res) == 0) {
- LM_DBG("no result for user \'%.*s@%.*s\'\n",
- _username->user.len, ZSW(_username->user.s),
- (use_domain ? (_domain->len) : 0), ZSW(_domain->s));
- return 1;
- }
- result.s = (char*)ROW_VALUES(RES_ROWS(*res))[0].val.string_val;
- result.len = strlen(result.s);
- if (calc_ha1) {
- /* Only plaintext passwords are stored in database,
- * we have to calculate HA1 */
- auth_api.calc_HA1(HA_MD5, &_username->whole, _domain, &result,
- 0, 0, _ha1);
- LM_DBG("HA1 string calculated: %s\n", _ha1);
- } else {
- memcpy(_ha1, result.s, result.len);
- _ha1[result.len] = '\0';
- }
- return 0;
- }
- /*
- * Generate AVPs from the database result
- */
- static int generate_avps(struct sip_msg* msg, db1_res_t* db_res)
- {
- pv_elem_t *cred;
- int i;
- for (cred=credentials, i=1; cred; cred=cred->next, i++) {
- if (db_val2pv_spec(msg, &RES_ROWS(db_res)[0].values[i], cred->spec) != 0) {
- LM_ERR("Failed to convert value for column %.*s\n",
- RES_NAMES(db_res)[i]->len, RES_NAMES(db_res)[i]->s);
- return -1;
- }
- }
- return 0;
- }
- /*
- * Authorize digest credentials and set the pointer to used hdr
- */
- static int digest_authenticate_hdr(sip_msg_t* msg, str *realm,
- str *table, hdr_types_t hftype, str *method, hdr_field_t **ahdr)
- {
- char ha1[256];
- int res;
- struct hdr_field* h;
- auth_body_t* cred;
- db1_res_t* result = NULL;
- int ret;
- cred = 0;
- ret = AUTH_ERROR;
- ret = auth_api.pre_auth(msg, realm, hftype, &h, NULL);
- switch(ret) {
- case NONCE_REUSED:
- LM_DBG("nonce reused");
- ret = AUTH_NONCE_REUSED;
- goto end;
- case STALE_NONCE:
- LM_DBG("stale nonce\n");
- ret = AUTH_STALE_NONCE;
- goto end;
- case NO_CREDENTIALS:
- LM_DBG("no credentials\n");
- ret = AUTH_NO_CREDENTIALS;
- goto end;
- case ERROR:
- case BAD_CREDENTIALS:
- LM_DBG("error or bad credentials\n");
- ret = AUTH_ERROR;
- goto end;
- case CREATE_CHALLENGE:
- LM_ERR("CREATE_CHALLENGE is not a valid state\n");
- ret = AUTH_ERROR;
- goto end;
- case DO_RESYNCHRONIZATION:
- LM_ERR("DO_RESYNCHRONIZATION is not a valid state\n");
- ret = AUTH_ERROR;
- goto end;
- case NOT_AUTHENTICATED:
- LM_DBG("not authenticated\n");
- ret = AUTH_ERROR;
- goto end;
- case DO_AUTHENTICATION:
- break;
- case AUTHENTICATED:
- ret = AUTH_OK;
- goto end;
- }
- cred = (auth_body_t*)h->parsed;
- if(ahdr!=NULL) *ahdr = h;
- res = get_ha1(&cred->digest.username, realm, table, ha1, &result);
- if (res < 0) {
- /* Error while accessing the database */
- ret = AUTH_ERROR;
- goto end;
- }
- if (res > 0) {
- /* Username not found in the database */
- ret = AUTH_USER_UNKNOWN;
- goto end;
- }
- /* Recalculate response, it must be same to authorize successfully */
- ret = auth_api.check_response(&(cred->digest), method, ha1);
- if(ret==AUTHENTICATED) {
- ret = AUTH_OK;
- switch(auth_api.post_auth(msg, h)) {
- case AUTHENTICATED:
- generate_avps(msg, result);
- break;
- default:
- ret = AUTH_ERROR;
- break;
- }
- } else {
- if(ret==NOT_AUTHENTICATED)
- ret = AUTH_INVALID_PASSWORD;
- else
- ret = AUTH_ERROR;
- }
- end:
- if(result)
- auth_dbf.free_result(auth_db_handle, result);
- return ret;
- }
- /*
- * Authorize digest credentials
- */
- static int digest_authenticate(sip_msg_t* msg, str *realm,
- str *table, hdr_types_t hftype, str *method)
- {
- return digest_authenticate_hdr(msg, realm, table, hftype, method, NULL);
- }
- /*
- * Authenticate using Proxy-Authorize header field
- */
- int proxy_authenticate(struct sip_msg* _m, char* _realm, char* _table)
- {
- str srealm;
- str stable;
- if(_table==NULL) {
- LM_ERR("invalid table parameter\n");
- return AUTH_ERROR;
- }
- stable.s = _table;
- stable.len = strlen(stable.s);
- if (get_str_fparam(&srealm, _m, (fparam_t*)_realm) < 0) {
- LM_ERR("failed to get realm value\n");
- return AUTH_ERROR;
- }
- if (srealm.len==0)
- {
- LM_ERR("invalid realm parameter - empty value\n");
- return AUTH_ERROR;
- }
- LM_DBG("realm value [%.*s]\n", srealm.len, srealm.s);
- return digest_authenticate(_m, &srealm, &stable, HDR_PROXYAUTH_T,
- &_m->first_line.u.request.method);
- }
- /*
- * Authenticate using WWW-Authorize header field
- */
- int www_authenticate(struct sip_msg* _m, char* _realm, char* _table)
- {
- str srealm;
- str stable;
- if(_table==NULL) {
- LM_ERR("invalid table parameter\n");
- return AUTH_ERROR;
- }
- stable.s = _table;
- stable.len = strlen(stable.s);
- if (get_str_fparam(&srealm, _m, (fparam_t*)_realm) < 0) {
- LM_ERR("failed to get realm value\n");
- return AUTH_ERROR;
- }
- if (srealm.len==0)
- {
- LM_ERR("invalid realm parameter - empty value\n");
- return AUTH_ERROR;
- }
- LM_DBG("realm value [%.*s]\n", srealm.len, srealm.s);
- return digest_authenticate(_m, &srealm, &stable, HDR_AUTHORIZATION_T,
- &_m->first_line.u.request.method);
- }
- int www_authenticate2(struct sip_msg* _m, char* _realm, char* _table, char *_method)
- {
- str srealm;
- str stable;
- str smethod;
- if(_table==NULL) {
- LM_ERR("invalid table parameter\n");
- return AUTH_ERROR;
- }
- stable.s = _table;
- stable.len = strlen(stable.s);
- if (get_str_fparam(&srealm, _m, (fparam_t*)_realm) < 0) {
- LM_ERR("failed to get realm value\n");
- return AUTH_ERROR;
- }
- if (srealm.len==0)
- {
- LM_ERR("invalid realm parameter - empty value\n");
- return AUTH_ERROR;
- }
- LM_DBG("realm value [%.*s]\n", srealm.len, srealm.s);
- if (get_str_fparam(&smethod, _m, (fparam_t*)_method) < 0) {
- LM_ERR("failed to get method value\n");
- return AUTH_ERROR;
- }
- if (smethod.len==0)
- {
- LM_ERR("invalid method parameter - empty value\n");
- return AUTH_ERROR;
- }
- LM_DBG("method value [%.*s]\n", smethod.len, smethod.s);
- return digest_authenticate(_m, &srealm, &stable, HDR_AUTHORIZATION_T,
- &smethod);
- }
- /*
- * Authenticate using WWW/Proxy-Authorize header field
- */
- int auth_check(struct sip_msg* _m, char* _realm, char* _table, char *_flags)
- {
- str srealm;
- str stable;
- int iflags;
- int ret;
- hdr_field_t *hdr;
- sip_uri_t *uri = NULL;
- sip_uri_t *turi = NULL;
- sip_uri_t *furi = NULL;
- if ((_m->REQ_METHOD == METHOD_ACK) || (_m->REQ_METHOD == METHOD_CANCEL)) {
- return AUTH_OK;
- }
- if(_m==NULL || _realm==NULL || _table==NULL || _flags==NULL) {
- LM_ERR("invalid parameters\n");
- return AUTH_ERROR;
- }
- if (get_str_fparam(&srealm, _m, (fparam_t*)_realm) < 0) {
- LM_ERR("failed to get realm value\n");
- return AUTH_ERROR;
- }
- if (srealm.len==0) {
- LM_ERR("invalid realm parameter - empty value\n");
- return AUTH_ERROR;
- }
- if (get_str_fparam(&stable, _m, (fparam_t*)_table) < 0) {
- LM_ERR("failed to get realm value\n");
- return AUTH_ERROR;
- }
- if (stable.len==0) {
- LM_ERR("invalid table parameter - empty value\n");
- return AUTH_ERROR;
- }
- if(fixup_get_ivalue(_m, (gparam_p)_flags, &iflags)!=0)
- {
- LM_ERR("invalid flags parameter\n");
- return -1;
- }
- LM_DBG("realm [%.*s] table [%.*s] flags [%d]\n", srealm.len, srealm.s,
- stable.len, stable.s, iflags);
- hdr = NULL;
- if(_m->REQ_METHOD==METHOD_REGISTER)
- ret = digest_authenticate_hdr(_m, &srealm, &stable, HDR_AUTHORIZATION_T,
- &_m->first_line.u.request.method, &hdr);
- else
- ret = digest_authenticate_hdr(_m, &srealm, &stable, HDR_PROXYAUTH_T,
- &_m->first_line.u.request.method, &hdr);
- if(ret==AUTH_OK && hdr!=NULL && (iflags&AUTH_CHECK_ID_F)) {
- srealm = ((auth_body_t*)(hdr->parsed))->digest.username.user;
-
- if((furi=parse_from_uri(_m))==NULL)
- return AUTH_ERROR;
-
- if(_m->REQ_METHOD==METHOD_REGISTER || _m->REQ_METHOD==METHOD_PUBLISH) {
- if((turi=parse_to_uri(_m))==NULL)
- return AUTH_ERROR;
- uri = turi;
- } else {
- uri = furi;
- }
- if(!((iflags&AUTH_CHECK_SKIPFWD_F)
- && (_m->REQ_METHOD==METHOD_INVITE || _m->REQ_METHOD==METHOD_BYE
- || _m->REQ_METHOD==METHOD_PRACK || _m->REQ_METHOD==METHOD_UPDATE
- || _m->REQ_METHOD==METHOD_MESSAGE))) {
- if(srealm.len!=uri->user.len
- || strncmp(srealm.s, uri->user.s, srealm.len)!=0)
- return AUTH_USER_MISMATCH;
- }
- if(_m->REQ_METHOD==METHOD_REGISTER || _m->REQ_METHOD==METHOD_PUBLISH) {
- /* check from==to */
- if(furi->user.len!=turi->user.len
- || strncmp(furi->user.s, turi->user.s, furi->user.len)!=0)
- return AUTH_USER_MISMATCH;
- if(use_domain!=0 && (furi->host.len!=turi->host.len
- || strncmp(furi->host.s, turi->host.s, furi->host.len)!=0))
- return AUTH_USER_MISMATCH;
- /* check r-uri==from for publish */
- if(_m->REQ_METHOD==METHOD_PUBLISH) {
- if(parse_sip_msg_uri(_m)<0)
- return AUTH_ERROR;
- uri = &_m->parsed_uri;
- if(furi->user.len!=uri->user.len
- || strncmp(furi->user.s, uri->user.s, furi->user.len)!=0)
- return AUTH_USER_MISMATCH;
- if(use_domain!=0 && (furi->host.len!=uri->host.len
- || strncmp(furi->host.s, uri->host.s, furi->host.len)!=0))
- return AUTH_USER_MISMATCH;
- }
- }
- return AUTH_OK;
- }
- return ret;
- }
- /**
- * @brief bind functions to AUTH_DB API structure
- */
- int bind_auth_db(auth_db_api_t *api)
- {
- if (!api) {
- ERR("Invalid parameter value\n");
- return -1;
- }
- api->digest_authenticate = digest_authenticate;
- return 0;
- }
|