You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/misc_radius/functions.c

781 lines
20 KiB

/*
* functions.c Implementation of exported functions
*
* Copyright (C) 2004 FhG Fokus
* Copyright (C) 2008 Juha Heinanen <jh@tutpro.com>
*
* 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
*
*/
#include "../../mod_fix.h"
#include "../../ut.h"
#include "../../dprint.h"
#include "../../usr_avp.h"
#include "../../lib/kcore/radius.h"
#include "../../parser/parse_uri.h"
#include "misc_radius.h"
#include "extra.h"
/* Array for extra attribute values */
static str val_arr[MAX_EXTRA];
/* Extract one reply item value to AVP flags, name and value */
static inline int extract_avp(VALUE_PAIR* vp, unsigned short *flags,
int_str *name, int_str *value)
{
static str names, values;
unsigned int r;
char *p;
char *end;
/* empty? */
if (vp->lvalue==0)
goto error;
p = vp->strvalue;
end = vp->strvalue + vp->lvalue;
/* get name */
if (*p!='#') {
/* name AVP */
*flags |= AVP_NAME_STR;
names.s = p;
} else {
names.s = ++p;
}
names.len = 0;
while( p<end && *p!=':' && *p!='#')
p++;
if (names.s==p || p==end) {
LM_ERR("empty AVP name\n");
goto error;
}
names.len = p - names.s;
/* get value */
if (*p!='#') {
/* string value */
*flags |= AVP_VAL_STR;
}
values.s = ++p;
values.len = end-values.s;
if (values.len==0) {
LM_ERR("empty AVP value\n");
goto error;
}
if ( !((*flags)&AVP_NAME_STR) ) {
/* convert name to id*/
if (str2int(&names,&r)!=0 ) {
LM_ERR("invalid AVP ID '%.*s'\n", names.len,names.s);
goto error;
}
name->n = (int)r;
} else {
name->s = names;
}
if ( !((*flags)&AVP_VAL_STR) ) {
/* convert value to integer */
if (str2int(&values,&r)!=0 ) {
LM_ERR("invalid AVP numrical value '%.*s'\n", values.len,values.s);
goto error;
}
value->n = (int)r;
} else {
value->s = values;
}
return 0;
error:
return -1;
}
static void generate_avps_rad(VALUE_PAIR* received)
{
int_str name, val;
unsigned short flags;
VALUE_PAIR *vp;
vp = received;
for( ; vp ; vp=vp->next) {
flags = AVP_NAME_STR;
switch(vp->type)
{
case PW_TYPE_STRING:
flags |= AVP_VAL_STR;
name.s.len = strlen(vp->name);
val.s.len = strlen(vp->strvalue);
name.s.s = vp->name;
val.s.s = vp->strvalue;
if (add_avp( flags, name, val ) < 0) {
LM_ERR("unable to create a new AVP\n");
} else {
LM_DBG("AVP '%.*s'/%d='%.*s'/%d has been added\n",
(flags&AVP_NAME_STR)?name.s.len:4,
(flags&AVP_NAME_STR)?name.s.s:"null",
(flags&AVP_NAME_STR)?0:name.n,
(flags&AVP_VAL_STR)?val.s.len:4,
(flags&AVP_VAL_STR)?val.s.s:"null",
(flags&AVP_VAL_STR)?0:val.n );
}
continue;
case PW_TYPE_INTEGER:
name.s.len = strlen(vp->name);
name.s.s = vp->name;
val.n = vp->lvalue;
if (add_avp( flags, name, val ) < 0) {
LM_ERR("unable to create a new AVP\n");
} else {
LM_DBG("AVP '%.*s'/%d='%.*s'/%d has been added\n",
(flags&AVP_NAME_STR)?name.s.len:4,
(flags&AVP_NAME_STR)?name.s.s:"null",
(flags&AVP_NAME_STR)?0:name.n,
(flags&AVP_VAL_STR)?val.s.len:4,
(flags&AVP_VAL_STR)?val.s.s:"null",
(flags&AVP_VAL_STR)?0:val.n );
}
continue;
default:
LM_ERR("skip attribute type %d (non-string)", vp->type);
continue;
}
return;
}
}
/* Generate AVPs from Radius reply items */
static void generate_avps(struct attr *attrs, VALUE_PAIR* received)
{
int_str name, val;
unsigned short flags;
VALUE_PAIR *vp;
vp = received;
for( ; (vp=rc_avpair_get(vp,attrs[SA_SIP_AVP].v,0)) ; vp=vp->next) {
flags = 0;
if (extract_avp( vp, &flags, &name, &val)!=0 )
continue;
if (add_avp( flags, name, val) < 0) {
LM_ERR("unable to create a new AVP\n");
} else {
LM_DBG("AVP '%.*s'/%d='%.*s'/%d has been added\n",
(flags&AVP_NAME_STR)?name.s.len:4,
(flags&AVP_NAME_STR)?name.s.s:"null",
(flags&AVP_NAME_STR)?0:name.n,
(flags&AVP_VAL_STR)?val.s.len:4,
(flags&AVP_VAL_STR)?val.s.s:"null",
(flags&AVP_VAL_STR)?0:val.n );
}
}
return;
}
/* Macro to add extra attribute */
#define ADD_EXTRA_AVPAIR(_attrs, _attr, _val, _len) \
do { \
if ((_len) != 0) { \
if ((_len) == -1) { \
if (_attrs[_attr].t != PW_TYPE_INTEGER) { \
if (_attrs[_attr].t != PW_TYPE_IPADDR) { \
LM_ERR("attribute %d is not of type integer or ipaddr\n", \
_attrs[_attr].v); \
goto error; \
} \
} \
} \
if (!rc_avpair_add( rh, &send, _attrs[_attr].v, _val, _len, 0)) { \
LM_ERR("failed to add %s, %d\n", _attrs[_attr].n, _attr); \
goto error; \
} \
} \
}while(0)
/*
* Loads from Radius caller's AVPs based on pvar argument.
* Returns 1 if Radius request succeeded and -1 otherwise.
*/
int radius_load_caller_avps(struct sip_msg* _m, char* _caller, char* _s2)
{
str user;
VALUE_PAIR *send, *received;
uint32_t service;
static char msg[4096];
int extra_cnt, offset, i, res;
if ((_caller == NULL) ||
(fixup_get_svalue(_m, (gparam_p)_caller, &user) != 0)) {
LM_ERR("invalid caller parameter");
return -1;
}
send = received = 0;
if (!rc_avpair_add(rh, &send, caller_attrs[SA_USER_NAME].v,
user.s, user.len, 0)) {
LM_ERR("in adding SA_USER_NAME\n");
return -1;
}
service = caller_vals[RV_SIP_CALLER_AVPS].v;
if (!rc_avpair_add(rh, &send, caller_attrs[SA_SERVICE_TYPE].v,
&service, -1, 0)) {
LM_ERR("error adding SA_SERVICE_TYPE <%u>\n", service);
goto error;
}
/* Add extra attributes */
extra_cnt = extra2strar(caller_extra, _m, val_arr);
if (extra_cnt == -1) {
LM_ERR("in getting values of caller extra attributes\n");
goto error;
}
offset = SA_STATIC_MAX;
for (i = 0; i < extra_cnt; i++) {
if (val_arr[i].len == -1) {
/* Add integer attribute */
ADD_EXTRA_AVPAIR(caller_attrs, offset+i,
&(val_arr[i].s), val_arr[i].len );
} else {
/* Add string attribute */
ADD_EXTRA_AVPAIR(caller_attrs, offset+i,
val_arr[i].s, val_arr[i].len );
}
}
res = rc_auth(rh, 0, send, &received, msg);
if (res == OK_RC) {
LM_DBG("Radius Load Caller Success\n");
rc_avpair_free(send);
if (common_response) {
generate_avps_rad(received);
} else {
generate_avps(caller_attrs, received);
}
rc_avpair_free(received);
return 1;
} else if(res == BADRESP_RC){
LM_ERR("Authz radius - Load Caller - BAD RESPONSE \n");
} else if(res == ERROR_RC){
LM_ERR("Authz radius - Load Caller - ERROR \n");
} else if(res == TIMEOUT_RC){
LM_ERR("Authz radius - Load Caller - TIMEOUT \n");
} else if(res == REJECT_RC){
LM_ERR("Authz radius - Load Caller - REJECTED \n");
} else{
LM_ERR("Authz radius - Load Caller - Unkown Response \n");
}
rc_avpair_free(send);
if (common_response){
generate_avps_rad(received);
}
rc_avpair_free(received);
return -1;
error:
rc_avpair_free(send);
return -1;
}
/*
* Loads from Radius callee's AVPs based on pvar argument.
* Returns 1 if Radius request succeeded and -1 otherwise.
*/
int radius_load_callee_avps(struct sip_msg* _m, char* _callee, char* _s2)
{
str user;
VALUE_PAIR *send, *received;
uint32_t service;
static char msg[4096];
int extra_cnt, offset, i, res;
send = received = 0;
if ((_callee == NULL) ||
(fixup_get_svalue(_m, (gparam_p)_callee, &user) != 0)) {
LM_ERR("invalid callee parameter");
return -1;
}
if (!rc_avpair_add(rh, &send, callee_attrs[SA_USER_NAME].v,
user.s, user.len, 0)) {
LM_ERR("in adding SA_USER_NAME\n");
return -1;
}
service = callee_vals[EV_SIP_CALLEE_AVPS].v;
if (!rc_avpair_add(rh, &send, callee_attrs[SA_SERVICE_TYPE].v,
&service, -1, 0)) {
LM_ERR("in adding SA_SERVICE_TYPE <%u>\n", service);
goto error;
}
/* Add extra attributes */
extra_cnt = extra2strar(callee_extra, _m, val_arr);
if (extra_cnt == -1) {
LM_ERR("in getting values of callee extra attributes\n");
goto error;
}
offset = SA_STATIC_MAX;
for (i = 0; i < extra_cnt; i++) {
if (val_arr[i].len == -1) {
/* Add integer attribute */
ADD_EXTRA_AVPAIR(callee_attrs, offset+i,
&(val_arr[i].s), val_arr[i].len );
} else {
/* Add string attribute */
ADD_EXTRA_AVPAIR(callee_attrs, offset+i,
val_arr[i].s, val_arr[i].len );
}
}
res = rc_auth(rh, 0, send, &received, msg);
if (res == OK_RC) {
LM_DBG("Radius Load Callee Success\n");
rc_avpair_free(send);
if (common_response) {
generate_avps_rad(received);
} else {
generate_avps(callee_attrs, received);
}
rc_avpair_free(received);
return 1;
} else if(res == BADRESP_RC){
LM_ERR("Authz radius - Load Callee - BAD RESPONSE \n");
} else if(res == ERROR_RC){
LM_ERR("Authz radius - Load Callee - ERROR \n");
} else if(res == TIMEOUT_RC){
LM_ERR("Authz radius - Load Callee - TIMEOUT \n");
} else if(res == REJECT_RC){
LM_ERR("Authz radius - Load Callee - REJECTED \n");
} else {
LM_ERR("Authz radius - Load Callee - Unkown response \n");
}
rc_avpair_free(send);
if (common_response){
generate_avps_rad(received);
}
rc_avpair_free(received);
return -1;
error:
rc_avpair_free(send);
return -1;
}
/*
* Check from Radius if a user belongs to a group. User-Name is given in
* first string argment that may contain pseudo variables. SIP-Group is
* given in second string variable that may not contain pseudo variables.
* Service-Type is Group-Check.
*/
int radius_is_user_in(struct sip_msg* _m, char* _user, char* _group)
{
str user, *group;
VALUE_PAIR *send, *received;
uint32_t service;
static char msg[4096];
int extra_cnt, offset, i, res;
send = received = 0;
if ((_user == NULL) ||
(fixup_get_svalue(_m, (gparam_p)_user, &user) != 0)) {
LM_ERR("invalid user parameter");
return -1;
}
if (!rc_avpair_add(rh, &send, group_attrs[SA_USER_NAME].v,
user.s, user.len, 0)) {
LM_ERR("in adding SA_USER_NAME\n");
return -1;
}
group = (str*)_group;
if ((group == NULL) || (group->len == 0)) {
LM_ERR("invalid group parameter");
goto error;
}
if (!rc_avpair_add(rh, &send, group_attrs[SA_SIP_GROUP].v,
group->s, group->len, 0)) {
LM_ERR("in adding SA_SIP_GROUP\n");
goto error;
}
service = group_vals[GV_GROUP_CHECK].v;
if (!rc_avpair_add(rh, &send, group_attrs[SA_SERVICE_TYPE].v,
&service, -1, 0)) {
LM_ERR("in adding SA_SERVICE_TYPE <%u>\n", service);
goto error;
}
/* Add extra attributes */
extra_cnt = extra2strar(group_extra, _m, val_arr);
if (extra_cnt == -1) {
LM_ERR("in getting values of group extra attributes\n");
goto error;
}
offset = SA_STATIC_MAX;
for (i = 0; i < extra_cnt; i++) {
if (val_arr[i].len == -1) {
/* Add integer attribute */
ADD_EXTRA_AVPAIR(group_attrs, offset+i,
&(val_arr[i].s), val_arr[i].len );
} else {
/* Add string attribute */
ADD_EXTRA_AVPAIR(group_attrs, offset+i,
val_arr[i].s, val_arr[i].len );
}
}
res = rc_auth(rh, 0, send, &received, msg);
if (res == OK_RC) {
LM_DBG("Authz radius - success\n");
rc_avpair_free(send);
generate_avps(group_attrs, received);
rc_avpair_free(received);
return 1;
} else if(res == BADRESP_RC){
LM_ERR("Authz radius - BAD RESPONSE \n");
} else if(res == ERROR_RC){
LM_ERR("Authz radius - ERROR \n");
} else if(res == TIMEOUT_RC){
LM_ERR("Authz radius - TIMEOUT \n");
} else if(res == REJECT_RC){
LM_ERR("Authz radius - REJECTED \n");
} else{
LM_ERR("Authz radius - Unkown Response \n");
}
rc_avpair_free(send);
rc_avpair_free(received);
return -1;
error:
rc_avpair_free(send);
return -1;
}
/*
* Check from Radius if URI, whose user and host parts are given as
* arguments, exists. If so, loads AVPs based on reply items returned
* from Radius. If use_sip_uri_host module parameter has non-zero value,
* user is send in SA_USER_NAME attribute and host in SA_SIP_URI_HOST
* attribute. If is has zero value, user@host is send in SA_USER_NAME
* attribute.
*/
int radius_does_uri_user_host_exist(struct sip_msg* _m, str user, str host)
{
char* at, *user_host;
VALUE_PAIR *send, *received;
uint32_t service;
static char msg[4096];
int extra_cnt, offset, i, res;
send = received = 0;
user_host = 0;
if (!use_sip_uri_host) {
/* Send user@host in SA_USER_NAME attr */
user_host = (char*)pkg_malloc(user.len + host.len + 2);
if (!user_host) {
LM_ERR("no more pkg memory\n");
return -1;
}
at = user_host;
memcpy(at, user.s, user.len);
at += user.len;
*at = '@';
at++;
memcpy(at , host.s, host.len);
at += host.len;
*at = '\0';
if (!rc_avpair_add(rh, &send, uri_attrs[SA_USER_NAME].v, user_host,
-1, 0)) {
LM_ERR("in adding SA_USER_NAME\n");
pkg_free(user_host);
return -1;
}
} else {
/* Send user in SA_USER_NAME attribute and host in SA_SIP_URI_HOST
attribute */
if (!rc_avpair_add(rh, &send, uri_attrs[SA_USER_NAME].v,
user.s, user.len, 0)) {
LM_ERR("adding User-Name failed\n");
return -1;
}
if (!rc_avpair_add(rh, &send, uri_attrs[SA_SIP_URI_HOST].v,
host.s, host.len, 0)) {
LM_ERR("adding SIP-URI-Host failed\n");
goto error;
}
}
service = uri_vals[UV_CALL_CHECK].v;
if (!rc_avpair_add(rh, &send, uri_attrs[SA_SERVICE_TYPE].v,
&service, -1, 0)) {
LM_ERR("in adding SA_SERVICE_TYPE <%u>\n", service);
goto error;
}
/* Add extra attributes */
extra_cnt = extra2strar(uri_extra, _m, val_arr);
if (extra_cnt == -1) {
LM_ERR("in getting values of group extra attributes\n");
goto error;
}
offset = SA_STATIC_MAX;
for (i = 0; i < extra_cnt; i++) {
if (val_arr[i].len == -1) {
/* Add integer attribute */
ADD_EXTRA_AVPAIR(uri_attrs, offset+i,
&(val_arr[i].s), val_arr[i].len );
} else {
/* Add string attribute */
ADD_EXTRA_AVPAIR(uri_attrs, offset+i,
val_arr[i].s, val_arr[i].len );
}
}
if ((res = rc_auth(rh, 0, send, &received, msg)) == OK_RC) {
LM_DBG("success\n");
if (user_host) pkg_free(user_host);
rc_avpair_free(send);
generate_avps(uri_attrs, received);
rc_avpair_free(received);
return 1;
} else {
if (user_host) pkg_free(user_host);
rc_avpair_free(send);
rc_avpair_free(received);
#ifdef REJECT_RC
if (res == REJECT_RC) {
LM_DBG("rejected\n");
return -1;
} else {
LM_ERR("failure\n");
return -2;
}
#else
LM_DBG("failure\n");
return -1;
#endif
}
error:
rc_avpair_free(send);
if (user_host) pkg_free(user_host);
return -1;
}
/*
* Check from Radius if Request URI belongs to a local user.
* If so, loads AVPs based on reply items returned from Radius.
*/
int radius_does_uri_exist_0(struct sip_msg* _m, char* _s1, char* _s2)
{
if (parse_sip_msg_uri(_m) < 0) {
LM_ERR("parsing Request-URI failed\n");
return -1;
}
return radius_does_uri_user_host_exist(_m, _m->parsed_uri.user,
_m->parsed_uri.host);
}
/*
* Check from Radius if URI given in pvar argument belongs to a local user.
* If so, loads AVPs based on reply items returned from Radius.
*/
int radius_does_uri_exist_1(struct sip_msg* _m, char* _sp, char* _s2)
{
pv_spec_t *sp;
pv_value_t pv_val;
struct sip_uri parsed_uri;
sp = (pv_spec_t *)_sp;
if (sp && (pv_get_spec_value(_m, sp, &pv_val) == 0)) {
if (pv_val.flags & PV_VAL_STR) {
if (pv_val.rs.len == 0 || pv_val.rs.s == NULL) {
LM_ERR("pvar argument is empty\n");
return -1;
}
} else {
LM_ERR("pvar value is not string\n");
return -1;
}
} else {
LM_ERR("cannot get pvar value\n");
return -1;
}
if (parse_uri(pv_val.rs.s, pv_val.rs.len, &parsed_uri) < 0) {
LM_ERR("parsing of URI in pvar failed\n");
return -1;
}
return radius_does_uri_user_host_exist(_m, parsed_uri.user,
parsed_uri.host);
}
/*
* Check from Radius if URI user given as argument belongs to a local user.
* If so, loads AVPs based on reply items returned from Radius.
*/
int radius_does_uri_user_exist(struct sip_msg* _m, str user)
{
static char msg[4096];
VALUE_PAIR *send, *received;
uint32_t service;
int res, extra_cnt, offset, i;
send = received = 0;
if (!rc_avpair_add(rh, &send, uri_attrs[SA_USER_NAME].v,
user.s, user.len, 0)) {
LM_ERR("in adding SA_USER_NAME\n");
return -1;
}
service = uri_vals[UV_CALL_CHECK].v;
if (!rc_avpair_add(rh, &send, uri_attrs[SA_SERVICE_TYPE].v,
&service, -1, 0)) {
LM_ERR("in adding SA_SERVICE_TYPE <%u>\n", service);
goto error;
}
/* Add extra attributes */
extra_cnt = extra2strar(uri_extra, _m, val_arr);
if (extra_cnt == -1) {
LM_ERR("in getting values of group extra attributes\n");
goto error;
}
offset = SA_STATIC_MAX;
for (i = 0; i < extra_cnt; i++) {
if (val_arr[i].len == -1) {
/* Add integer attribute */
ADD_EXTRA_AVPAIR(uri_attrs, offset+i,
&(val_arr[i].s), val_arr[i].len );
} else {
/* Add string attribute */
ADD_EXTRA_AVPAIR(uri_attrs, offset+i,
val_arr[i].s, val_arr[i].len );
}
}
res = rc_auth(rh, 0, send, &received, msg);
if (res == OK_RC) {
LM_DBG("Radius Success\n");
rc_avpair_free(send);
generate_avps(uri_attrs, received);
rc_avpair_free(received);
return 1;
} else if(res == BADRESP_RC){
LM_ERR("Authz radius - BAD RESPONSE \n");
} else if(res == ERROR_RC){
LM_ERR("Authz radius - ERROR \n");
} else if(res == TIMEOUT_RC){
LM_ERR("Authz radius - TIMEOUT \n");
} else if(res == REJECT_RC){
LM_ERR("Authz radius - REJECTED \n");
} else {
LM_ERR("Authz radius - Unkown response \n");
}
rc_avpair_free(send);
rc_avpair_free(received);
return -1;
error:
rc_avpair_free(send);
return -1;
}
/*
* Check from Radius if Request URI user belongs to a local user.
* If so, loads AVPs based on reply items returned from Radius.
*/
int radius_does_uri_user_exist_0(struct sip_msg* _m, char* _s1, char* _s2)
{
if (parse_sip_msg_uri(_m) < 0) {
LM_ERR("parsing Request-URI failed\n");
return -1;
}
return radius_does_uri_user_exist(_m, _m->parsed_uri.user);
}
/*
* Check from Radius if URI user given in pvar argument belongs
* to a local user. If so, loads AVPs based on reply items returned
* from Radius.
*/
int radius_does_uri_user_exist_1(struct sip_msg* _m, char* _sp, char* _s2)
{
pv_spec_t *sp;
pv_value_t pv_val;
sp = (pv_spec_t *)_sp;
if (sp && (pv_get_spec_value(_m, sp, &pv_val) == 0)) {
if (pv_val.flags & PV_VAL_STR) {
if (pv_val.rs.len == 0 || pv_val.rs.s == NULL) {
LM_ERR("pvar argument is empty\n");
return -1;
}
} else {
LM_ERR("pvar value is not string\n");
return -1;
}
} else {
LM_ERR("cannot get pvar value\n");
return -1;
}
return radius_does_uri_user_exist(_m, pv_val.rs);
}