/*
* $Id: authorize.c 1754 2007-03-06 12:53:27Z miconda $
*
* Digest Authentication - Database support
*
* Copyright (C) 2001-2003 FhG Fokus
*
* This file is part of openser, a free SIP server.
*
* openser 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
*
* openser 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "../../db/db.h"
#include "../../dprint.h"
#include "../../parser/digest/digest.h"
#include "../../parser/hf.h"
#include "../../parser/parser_f.h"
#include "../../usr_avp.h"
#include "../../items.h"
#include "../../mem/mem.h"
#include "aaa_avps.h"
#include "authdb_mod.h"
#include "rfc2617.h"
static str auth_500_err = str_init("Server Internal Error");
static inline int get_ha1(struct username* _username, str* _domain,
char* _table, char* _ha1, db_res_t** res)
{
struct aaa_avp *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) {
LOG(L_ERR, "get_ha1(): Error while allocating memory\n");
return -1;
}
keys[0] = user_column.s;
keys[1] = domain_column.s;
col[0] = (_username->domain.len && !calc_ha1) ?
(pass_column_2.s) : (pass_column.s);
for (n = 0, cred=credentials; cred ; n++, cred=cred->next) {
col[1 + n] = cred->attr_name.s;
}
VAL_TYPE(vals) = VAL_TYPE(vals + 1) = DB_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) {
LOG(L_ERR, "get_ha1(): Error in use_table\n");
pkg_free(col);
return -1;
}
if (auth_dbf.query(auth_db_handle, keys, 0, vals, col, n, nc, 0, res) < 0) {
LOG(L_ERR, "get_ha1(): Error while querying database\n");
pkg_free(col);
return -1;
}
pkg_free(col);
if (RES_ROW_N(*res) == 0) {
DBG("get_ha1(): 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 */
calc_HA1(HA_MD5, &_username->whole, _domain, &result, 0, 0, _ha1);
DBG("HA1 string calculated: %s\n", _ha1);
} else {
memcpy(_ha1, result.s, result.len);
_ha1[result.len] = '\0';
}
return 0;
}
/*
* Calculate the response and compare with the given response string
* Authorization is successful if this two strings are same
*/
static inline int check_response(dig_cred_t* _cred, str* _method, char* _ha1)
{
HASHHEX resp, hent;
/*
* First, we have to verify that the response received has
* the same length as responses created by us
*/
if (_cred->response.len != 32) {
DBG("check_response(): Receive response len != 32\n");
return 1;
}
/*
* Now, calculate our response from parameters received
* from the user agent
*/
calc_response(_ha1, &(_cred->nonce),
&(_cred->nc), &(_cred->cnonce),
&(_cred->qop.qop_str), _cred->qop.qop_parsed == QOP_AUTHINT,
_method, &(_cred->uri), hent, resp);
DBG("check_response(): Our result = \'%s\'\n", resp);
/*
* And simply compare the strings, the user is
* authorized if they match
*/
if (!memcmp(resp, _cred->response.s, 32)) {
DBG("check_response(): Authorization is OK\n");
return 0;
} else {
DBG("check_response(): Authorization failed\n");
return 2;
}
}
/*
* Generate AVPs from the database result
*/
static int generate_avps(db_res_t* result)
{
struct aaa_avp *cred;
int_str ivalue;
int i;
for (cred=credentials, i=1; cred; cred=cred->next, i++) {
switch (result->col.types[i]) {
case DB_STR:
ivalue.s = VAL_STR(&(result->rows[0].values[i]));
if (VAL_NULL(&(result->rows[0].values[i])) ||
ivalue.s.s == NULL || ivalue.s.len==0)
continue;
if (add_avp(cred->avp_type|AVP_VAL_STR,cred->avp_name,ivalue)!=0){
LOG(L_ERR,"ERROR:auth_db:generate_avps: failed to add AVP\n");
return -1;
}
DBG("generate_avps: set string AVP \"%s\"/%d = \"%.*s\"\n",
(cred->avp_type&AVP_NAME_STR)?cred->avp_name.s.s:"",
(cred->avp_type&AVP_NAME_STR)?0:cred->avp_name.n,
ivalue.s.len, ZSW(ivalue.s.s));
break;
case DB_STRING:
ivalue.s.s = (char*)VAL_STRING(&(result->rows[0].values[i]));
if (VAL_NULL(&(result->rows[0].values[i])) ||
ivalue.s.s == NULL || (ivalue.s.len=strlen(ivalue.s.s))==0 )
continue;
if (add_avp(cred->avp_type|AVP_VAL_STR,cred->avp_name,ivalue)!=0){
LOG(L_ERR,"ERROR:auth_db:generate_avps: failed to add AVP\n");
return -1;
}
DBG("generate_avps: set string AVP \"%s\"/%d = \"%.*s\"\n",
(cred->avp_type&AVP_NAME_STR)?cred->avp_name.s.s:"",
(cred->avp_type&AVP_NAME_STR)?0:cred->avp_name.n,
ivalue.s.len, ZSW(ivalue.s.s));
break;
case DB_INT:
if (VAL_NULL(&(result->rows[0].values[i])))
continue;
ivalue.n = (int)VAL_INT(&(result->rows[0].values[i]));
if (add_avp(cred->avp_type, cred->avp_name, ivalue)!=0) {
LOG(L_ERR,"ERROR:auth_db:generate_avps: failed to add AVP\n");
return -1;
}
DBG("generate_avps: set int AVP \"%s\"/%d = %d\n",
(cred->avp_type&AVP_NAME_STR)?cred->avp_name.s.s:"",
(cred->avp_type&AVP_NAME_STR)?0:cred->avp_name.n,
ivalue.n);
break;
default:
LOG(L_ERR, "ERROR:auth_db:generate_avps: subscriber table "
"column `%s' has unsuported type. Only string/str or int "
"columns are supported by load_credentials.\n",
result->col.names[i]);
break;
}
}
return 0;
}
/*
* Authorize digest credentials
*/
static inline int authorize(struct sip_msg* _m, xl_elem_t* _realm,
char* _table, hdr_types_t _hftype)
{
char ha1[256];
int res;
struct hdr_field* h;
auth_body_t* cred;
auth_result_t ret;
str domain;
db_res_t* result = NULL;
if (_realm) {
if (xl_printf_s(_m, _realm, &domain)!=0) {
LOG(L_ERR, "ERROR:auth_db:authorize: xl_printf_s failed\n");
return -1;
}
} else {
domain.len = 0;
domain.s = 0;
}
ret = auth_api.pre_auth(_m, &domain, _hftype, &h);
switch(ret) {
case ERROR: return 0;
case NOT_AUTHORIZED: return -1;
case DO_AUTHORIZATION: break;
case AUTHORIZED: return 1;
}
cred = (auth_body_t*)h->parsed;
res = get_ha1(&cred->digest.username, &domain, _table, ha1, &result);
if (res < 0) {
/* Error while accessing the database */
if (slb.reply(_m, 500, &auth_500_err) == -1) {
LOG(L_ERR, "authorize(): Error while sending 500 reply\n");
}
return 0;
}
if (res > 0) {
/* Username not found in the database */
auth_dbf.free_result(auth_db_handle, result);
return -1;
}
/* Recalculate response, it must be same to authorize successfully */
if (!check_response(&(cred->digest),&_m->first_line.u.request.method,ha1)) {
ret = auth_api.post_auth(_m, h);
switch(ret) {
case ERROR:
auth_dbf.free_result(auth_db_handle, result);
return 1;
case NOT_AUTHORIZED:
auth_dbf.free_result(auth_db_handle, result);
return -1;
case AUTHORIZED:
generate_avps(result);
auth_dbf.free_result(auth_db_handle, result);
return 1;
default:
auth_dbf.free_result(auth_db_handle, result);
return -1;
}
}
auth_dbf.free_result(auth_db_handle, result);
return -1;
}
/*
* Authorize using Proxy-Authorize header field
*/
int proxy_authorize(struct sip_msg* _m, char* _realm, char* _table)
{
return authorize(_m, (xl_elem_t*)_realm, _table, HDR_PROXYAUTH_T);
}
/*
* Authorize using WWW-Authorize header field
*/
int www_authorize(struct sip_msg* _m, char* _realm, char* _table)
{
return authorize(_m, (xl_elem_t*)_realm, _table, HDR_AUTHORIZATION_T);
}
syntax highlighted by Code2HTML, v. 0.9.1