rlm_attr_rewrite.c [plain text]
#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#ifdef HAVE_REGEX_H
# include <regex.h>
#endif
#define RLM_REGEX_INPACKET 0
#define RLM_REGEX_INCONFIG 1
#define RLM_REGEX_INREPLY 2
#define RLM_REGEX_INPROXY 3
#define RLM_REGEX_INPROXYREPLY 4
typedef struct rlm_attr_rewrite_t {
char *attribute;
int attr_num;
char *search;
int search_len;
char *searchin_str;
char searchin;
char *replace;
int replace_len;
int append;
int nocase;
int new_attr;
int num_matches;
const char *name;
} rlm_attr_rewrite_t;
static const CONF_PARSER module_config[] = {
{ "attribute", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,attribute), NULL, NULL },
{ "searchfor", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,search), NULL, NULL },
{ "searchin", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,searchin_str), NULL, "packet" },
{ "replacewith", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,replace), NULL, NULL },
{ "append", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,append),NULL, "no" },
{ "ignore_case", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,nocase), NULL, "yes" },
{ "new_attribute", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,new_attr), NULL, "no" },
{ "max_matches", PW_TYPE_INTEGER, offsetof(rlm_attr_rewrite_t,num_matches), NULL, "10" },
{ NULL, -1, 0, NULL, NULL }
};
static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance)
{
rlm_attr_rewrite_t *data;
DICT_ATTR *dattr;
data = rad_malloc(sizeof(*data));
if (!data) {
return -1;
}
memset(data, 0, sizeof(*data));
if (cf_section_parse(conf, data, module_config) < 0) {
free(data);
return -1;
}
if (data->attribute == NULL) {
radlog(L_ERR, "rlm_attr_rewrite: 'attribute' must be set.");
return -1;
}
if (data->search == NULL || data->replace == NULL) {
radlog(L_ERR, "rlm_attr_rewrite: search/replace strings must be set.");
return -1;
}
data->search_len = strlen(data->search);
data->replace_len = strlen(data->replace);
if (data->replace_len == 0 && data->new_attr){
radlog(L_ERR, "rlm_attr_rewrite: replace string must not be zero length in order to create new attribute.");
return -1;
}
if (data->num_matches < 1 || data->num_matches > MAX_STRING_LEN) {
radlog(L_ERR, "rlm_attr_rewrite: Illegal range for match number.");
return -1;
}
if (data->searchin_str == NULL) {
radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
data->searchin = RLM_REGEX_INPACKET;
}
else{
if (strcmp(data->searchin_str, "packet") == 0)
data->searchin = RLM_REGEX_INPACKET;
else if (strcmp(data->searchin_str, "config") == 0)
data->searchin = RLM_REGEX_INCONFIG;
else if (strcmp(data->searchin_str, "control") == 0)
data->searchin = RLM_REGEX_INCONFIG;
else if (strcmp(data->searchin_str, "reply") == 0)
data->searchin = RLM_REGEX_INREPLY;
else if (strcmp(data->searchin_str, "proxy") == 0)
data->searchin = RLM_REGEX_INPROXY;
else if (strcmp(data->searchin_str, "proxy_reply") == 0)
data->searchin = RLM_REGEX_INPROXYREPLY;
else {
radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
data->searchin = RLM_REGEX_INPACKET;
}
}
dattr = dict_attrbyname(data->attribute);
if (dattr == NULL) {
radlog(L_ERR, "rlm_attr_rewrite: No such attribute %s",
data->attribute);
return -1;
}
data->attr_num = dattr->attr;
data->name = cf_section_name2(conf);
*instance = data;
return 0;
}
static int do_attr_rewrite(void *instance, REQUEST *request)
{
rlm_attr_rewrite_t *data = (rlm_attr_rewrite_t *) instance;
int ret = RLM_MODULE_NOOP;
VALUE_PAIR *attr_vp = NULL;
VALUE_PAIR *tmp = NULL;
regex_t preg;
regmatch_t pmatch[9];
int cflags = 0;
int err = 0;
char done_xlat = 0;
unsigned int len = 0;
char err_msg[MAX_STRING_LEN];
unsigned int i = 0;
unsigned int j = 0;
unsigned int counter = 0;
char new_str[MAX_STRING_LEN];
char *ptr, *ptr2;
char search_STR[MAX_STRING_LEN];
char replace_STR[MAX_STRING_LEN];
if ((attr_vp = pairfind(request->config_items, PW_REWRITE_RULE)) != NULL){
if (data->name == NULL || strcmp(data->name,attr_vp->vp_strvalue))
return RLM_MODULE_NOOP;
}
if (data->new_attr){
if (!radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL)) {
DEBUG2("%s: xlat on replace string failed.", data->name);
return ret;
}
attr_vp = pairmake(data->attribute,replace_STR,0);
if (attr_vp == NULL){
DEBUG2("%s: Could not add new attribute %s with value '%s'", data->name,
data->attribute,replace_STR);
return ret;
}
switch(data->searchin){
case RLM_REGEX_INPACKET:
pairadd(&request->packet->vps,attr_vp);
break;
case RLM_REGEX_INCONFIG:
pairadd(&request->config_items,attr_vp);
break;
case RLM_REGEX_INREPLY:
pairadd(&request->reply->vps,attr_vp);
break;
case RLM_REGEX_INPROXY:
if (!request->proxy) {
pairbasicfree(attr_vp);
return RLM_MODULE_NOOP;
}
pairadd(&request->proxy->vps, attr_vp);
break;
case RLM_REGEX_INPROXYREPLY:
if (!request->proxy_reply) {
pairbasicfree(attr_vp);
return RLM_MODULE_NOOP;
}
pairadd(&request->proxy_reply->vps, attr_vp);
break;
default:
radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name);
data->searchin = RLM_REGEX_INPACKET;
pairadd(&request->packet->vps,attr_vp);
break;
}
DEBUG2("%s: Added attribute %s with value '%s'", data->name,data->attribute,replace_STR);
ret = RLM_MODULE_OK;
} else {
int replace_len = 0;
switch (data->searchin) {
case RLM_REGEX_INPACKET:
if (data->attr_num == PW_USER_NAME)
attr_vp = request->username;
else if (data->attr_num == PW_USER_PASSWORD)
attr_vp = request->password;
else
tmp = request->packet->vps;
break;
case RLM_REGEX_INCONFIG:
tmp = request->config_items;
break;
case RLM_REGEX_INREPLY:
tmp = request->reply->vps;
break;
case RLM_REGEX_INPROXYREPLY:
if (!request->proxy_reply)
return RLM_MODULE_NOOP;
tmp = request->proxy_reply->vps;
break;
case RLM_REGEX_INPROXY:
if (!request->proxy)
return RLM_MODULE_NOOP;
tmp = request->proxy->vps;
break;
default:
radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name);
data->searchin = RLM_REGEX_INPACKET;
attr_vp = pairfind(request->packet->vps, data->attr_num);
break;
}
do_again:
if (tmp != NULL)
attr_vp = pairfind(tmp, data->attr_num);
if (attr_vp == NULL) {
DEBUG2("%s: Could not find value pair for attribute %s", data->name,data->attribute);
return ret;
}
if (attr_vp->vp_strvalue == NULL || attr_vp->length == 0){
DEBUG2("%s: Attribute %s string value NULL or of zero length", data->name,data->attribute);
return ret;
}
cflags |= REG_EXTENDED;
if (data->nocase)
cflags |= REG_ICASE;
if (!radius_xlat(search_STR, sizeof(search_STR), data->search, request, NULL) && data->search_len != 0) {
DEBUG2("%s: xlat on search string failed.", data->name);
return ret;
}
if ((err = regcomp(&preg,search_STR,cflags))) {
regerror(err, &preg, err_msg, MAX_STRING_LEN);
DEBUG2("%s: regcomp() returned error: %s", data->name,err_msg);
return ret;
}
if ((attr_vp->type == PW_TYPE_IPADDR) &&
(attr_vp->vp_strvalue[0] == '\0')) {
inet_ntop(AF_INET, &(attr_vp->vp_ipaddr),
attr_vp->vp_strvalue,
sizeof(attr_vp->vp_strvalue));
}
ptr = new_str;
ptr2 = attr_vp->vp_strvalue;
counter = 0;
for ( i = 0 ;i < (unsigned)data->num_matches; i++) {
err = regexec(&preg, ptr2, REQUEST_MAX_REGEX, pmatch, 0);
if (err == REG_NOMATCH) {
if (i == 0) {
DEBUG2("%s: Does not match: %s = %s", data->name,
data->attribute, attr_vp->vp_strvalue);
regfree(&preg);
goto to_do_again;
} else
break;
}
if (err != 0) {
regfree(&preg);
radlog(L_ERR, "%s: match failure for attribute %s with value '%s'", data->name,
data->attribute, attr_vp->vp_strvalue);
return ret;
}
if (pmatch[0].rm_so == -1)
break;
len = pmatch[0].rm_so;
if (data->append) {
len = len + (pmatch[0].rm_eo - pmatch[0].rm_so);
}
counter += len;
if (counter >= MAX_STRING_LEN) {
regfree(&preg);
DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
data->attribute, attr_vp->vp_strvalue);
return ret;
}
memcpy(ptr, ptr2,len);
ptr += len;
*ptr = '\0';
ptr2 += pmatch[0].rm_eo;
if (i == 0){
for(j = 0; j <= REQUEST_MAX_REGEX; j++){
char *p;
char buffer[sizeof(attr_vp->vp_strvalue)];
if (pmatch[j].rm_so == -1){
p = request_data_get(request,request,REQUEST_DATA_REGEX | j);
if (p){
free(p);
continue;
}
break;
}
memcpy(buffer,
attr_vp->vp_strvalue + pmatch[j].rm_so,
pmatch[j].rm_eo - pmatch[j].rm_so);
buffer[pmatch[j].rm_eo - pmatch[j].rm_so] = '\0';
p = strdup(buffer);
request_data_add(request,request,REQUEST_DATA_REGEX | j,p,free);
}
}
if (!done_xlat){
if (data->replace_len != 0 &&
radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL) == 0) {
DEBUG2("%s: xlat on replace string failed.", data->name);
return ret;
}
replace_len = (data->replace_len != 0) ? strlen(replace_STR) : 0;
done_xlat = 1;
}
counter += replace_len;
if (counter >= MAX_STRING_LEN) {
regfree(&preg);
DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
data->attribute, attr_vp->vp_strvalue);
return ret;
}
if (replace_len){
memcpy(ptr, replace_STR, replace_len);
ptr += replace_len;
*ptr = '\0';
}
}
regfree(&preg);
len = strlen(ptr2) + 1;
counter += len;
if (counter >= MAX_STRING_LEN){
DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
data->attribute, attr_vp->vp_strvalue);
return ret;
}
memcpy(ptr, ptr2, len);
ptr[len] = '\0';
DEBUG2("%s: Changed value for attribute %s from '%s' to '%s'", data->name,
data->attribute, attr_vp->vp_strvalue, new_str);
if (pairparsevalue(attr_vp, new_str) == NULL) {
DEBUG2("%s: Could not write value '%s' into attribute %s: %s", data->name, new_str, data->attribute, fr_strerror());
return ret;
}
to_do_again:
ret = RLM_MODULE_OK;
if (tmp != NULL){
tmp = attr_vp->next;
if (tmp != NULL)
goto do_again;
}
}
return ret;
}
static int attr_rewrite_accounting(void *instance, REQUEST *request)
{
return do_attr_rewrite(instance, request);
}
static int attr_rewrite_authorize(void *instance, REQUEST *request)
{
return do_attr_rewrite(instance, request);
}
static int attr_rewrite_authenticate(void *instance, REQUEST *request)
{
return do_attr_rewrite(instance, request);
}
static int attr_rewrite_preacct(void *instance, REQUEST *request)
{
return do_attr_rewrite(instance, request);
}
static int attr_rewrite_checksimul(void *instance, REQUEST *request)
{
return do_attr_rewrite(instance, request);
}
static int attr_rewrite_preproxy(void *instance, REQUEST *request)
{
return do_attr_rewrite(instance, request);
}
static int attr_rewrite_postproxy(void *instance, REQUEST *request)
{
return do_attr_rewrite(instance, request);
}
static int attr_rewrite_postauth(void *instance, REQUEST *request)
{
return do_attr_rewrite(instance, request);
}
static int attr_rewrite_detach(void *instance)
{
free(instance);
return 0;
}
module_t rlm_attr_rewrite = {
RLM_MODULE_INIT,
"attr_rewrite",
RLM_TYPE_THREAD_UNSAFE,
attr_rewrite_instantiate,
attr_rewrite_detach,
{
attr_rewrite_authenticate,
attr_rewrite_authorize,
attr_rewrite_preacct,
attr_rewrite_accounting,
attr_rewrite_checksimul,
attr_rewrite_preproxy,
attr_rewrite_postproxy,
attr_rewrite_postauth
},
};