#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <freeradius-devel/rad_assert.h>
#include <ctype.h>
typedef struct rlm_preprocess_t {
char *huntgroup_file;
char *hints_file;
PAIR_LIST *huntgroups;
PAIR_LIST *hints;
int with_ascend_hack;
int ascend_channels_per_line;
int with_ntdomain_hack;
int with_specialix_jetstream_hack;
int with_cisco_vsa_hack;
int with_alvarion_vsa_hack;
} rlm_preprocess_t;
static const CONF_PARSER module_config[] = {
{ "huntgroups", PW_TYPE_FILENAME,
offsetof(rlm_preprocess_t,huntgroup_file), NULL,
"${raddbdir}/huntgroups" },
{ "hints", PW_TYPE_FILENAME,
offsetof(rlm_preprocess_t,hints_file), NULL,
"${raddbdir}/hints" },
{ "with_ascend_hack", PW_TYPE_BOOLEAN,
offsetof(rlm_preprocess_t,with_ascend_hack), NULL, "no" },
{ "ascend_channels_per_line", PW_TYPE_INTEGER,
offsetof(rlm_preprocess_t,ascend_channels_per_line), NULL, "23" },
{ "with_ntdomain_hack", PW_TYPE_BOOLEAN,
offsetof(rlm_preprocess_t,with_ntdomain_hack), NULL, "no" },
{ "with_specialix_jetstream_hack", PW_TYPE_BOOLEAN,
offsetof(rlm_preprocess_t,with_specialix_jetstream_hack), NULL,
"no" },
{ "with_cisco_vsa_hack", PW_TYPE_BOOLEAN,
offsetof(rlm_preprocess_t,with_cisco_vsa_hack), NULL, "no" },
{ "with_alvarion_vsa_hack", PW_TYPE_BOOLEAN,
offsetof(rlm_preprocess_t,with_alvarion_vsa_hack), NULL, "no" },
{ NULL, -1, 0, NULL, NULL }
};
static int fallthrough(VALUE_PAIR *vp)
{
VALUE_PAIR *tmp;
tmp = pairfind(vp, PW_FALL_THROUGH);
return tmp ? tmp->lvalue : 0;
}
static void ascend_nasport_hack(VALUE_PAIR *nas_port, int channels_per_line)
{
int service;
int line;
int channel;
if (!nas_port) {
return;
}
if (nas_port->vp_integer > 9999) {
service = nas_port->vp_integer/10000;
line = (nas_port->vp_integer - (10000 * service)) / 100;
channel = nas_port->vp_integer-((10000 * service)+(100 * line));
nas_port->vp_integer =
(channel - 1) + (line - 1) * channels_per_line;
}
}
static void cisco_vsa_hack(VALUE_PAIR *vp)
{
int vendorcode;
char *ptr;
char newattr[MAX_STRING_LEN];
for ( ; vp != NULL; vp = vp->next) {
vendorcode = VENDOR(vp->attribute);
if (!((vendorcode == 9) || (vendorcode == 6618))) continue;
if (vp->type != PW_TYPE_STRING) continue;
ptr = strchr(vp->vp_strvalue, '=');
if (!ptr) continue;
if ((vp->attribute & 0xffff) == 1) {
const char *p;
DICT_ATTR *dattr;
p = vp->vp_strvalue;
gettoken(&p, newattr, sizeof(newattr));
if ((dattr = dict_attrbyname(newattr)) != NULL) {
VALUE_PAIR *newvp;
newvp = pairmake(newattr, ptr + 1, T_OP_EQ);
if (newvp) {
pairadd(&vp, newvp);
}
}
} else {
strlcpy(newattr, ptr + 1, sizeof(newattr));
strlcpy((char *)vp->vp_strvalue, newattr,
sizeof(vp->vp_strvalue));
vp->length = strlen((char *)vp->vp_strvalue);
}
}
}
static void alvarion_vsa_hack(VALUE_PAIR *vp)
{
int vendorcode;
int number = 1;
for ( ; vp != NULL; vp = vp->next) {
DICT_ATTR *da;
vendorcode = VENDOR(vp->attribute);
if (vendorcode != 12394) continue;
if (vp->type != PW_TYPE_STRING) continue;
da = dict_attrbyvalue(number | (12394 << 16));
if (!da) continue;
vp->attribute = da->attr;
vp->name = da->name;
number++;
}
}
static void rad_mangle(rlm_preprocess_t *data, REQUEST *request)
{
VALUE_PAIR *namepair;
VALUE_PAIR *request_pairs;
VALUE_PAIR *tmp;
request_pairs = request->packet->vps;
namepair = pairfind(request_pairs, PW_USER_NAME);
if ((namepair == NULL) ||
(namepair->length <= 0)) {
return;
}
if (data->with_ntdomain_hack) {
char *ptr;
char newname[MAX_STRING_LEN];
if ((ptr = strchr(namepair->vp_strvalue, '\\')) != NULL) {
strlcpy(newname, ptr + 1, sizeof(newname));
strcpy(namepair->vp_strvalue, newname);
namepair->length = strlen(newname);
}
}
if (data->with_specialix_jetstream_hack) {
char *ptr;
if ((strlen((char *)namepair->vp_strvalue) > 10) &&
(namepair->vp_strvalue[10] == '/')) {
for (ptr = (char *)namepair->vp_strvalue + 11; *ptr; ptr++)
*(ptr - 1) = *ptr;
*(ptr - 1) = 0;
namepair->length = strlen((char *)namepair->vp_strvalue);
}
}
if (pairfind(request_pairs, PW_FRAMED_PROTOCOL) != NULL &&
pairfind(request_pairs, PW_SERVICE_TYPE) == NULL) {
tmp = radius_paircreate(request, &request->packet->vps,
PW_SERVICE_TYPE, PW_TYPE_INTEGER);
tmp->vp_integer = PW_FRAMED_USER;
}
}
static int hunt_paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check)
{
VALUE_PAIR *check_item = check;
VALUE_PAIR *tmp;
int result = -1;
if (check == NULL) return 0;
while (result != 0 && check_item != NULL) {
tmp = check_item->next;
check_item->next = NULL;
result = paircompare(req, request, check_item, NULL);
check_item->next = tmp;
check_item = check_item->next;
}
return result;
}
static int hints_setup(PAIR_LIST *hints, REQUEST *request)
{
char *name;
VALUE_PAIR *add;
VALUE_PAIR *tmp;
PAIR_LIST *i;
VALUE_PAIR *request_pairs;
int updated = 0, ft;
request_pairs = request->packet->vps;
if (hints == NULL || request_pairs == NULL)
return RLM_MODULE_NOOP;
if ((tmp = pairfind(request_pairs, PW_USER_NAME)) == NULL)
name = NULL;
else
name = (char *)tmp->vp_strvalue;
if (name == NULL || name[0] == 0)
return RLM_MODULE_NOOP;
for (i = hints; i; i = i->next) {
if (((strcmp(i->name, "DEFAULT") == 0) ||
(strcmp(i->name, name) == 0)) &&
(paircompare(request, request_pairs, i->check, NULL) == 0)) {
RDEBUG2(" hints: Matched %s at %d",
i->name, i->lineno);
add = paircopy(i->reply);
ft = fallthrough(add);
pairdelete(&add, PW_STRIP_USER_NAME);
pairdelete(&add, PW_FALL_THROUGH);
pairxlatmove(request, &request->packet->vps, &add);
pairfree(&add);
updated = 1;
if (!ft) break;
}
}
if (updated == 0) return RLM_MODULE_NOOP;
return RLM_MODULE_UPDATED;
}
static int huntgroup_access(REQUEST *request, PAIR_LIST *huntgroups)
{
PAIR_LIST *i;
int r = RLM_MODULE_OK;
VALUE_PAIR *request_pairs = request->packet->vps;
if (huntgroups == NULL)
return RLM_MODULE_OK;
for(i = huntgroups; i; i = i->next) {
if (paircompare(request, request_pairs, i->check, NULL) != 0)
continue;
r = RLM_MODULE_REJECT;
if (hunt_paircmp(request, request_pairs, i->reply) == 0) {
VALUE_PAIR *vp;
vp = pairfind(request_pairs, PW_HUNTGROUP_NAME);
if (!vp) {
vp = radius_paircreate(request,
&request->packet->vps,
PW_HUNTGROUP_NAME,
PW_TYPE_STRING);
strlcpy(vp->vp_strvalue, i->name,
sizeof(vp->vp_strvalue));
vp->length = strlen(vp->vp_strvalue);
}
r = RLM_MODULE_OK;
}
break;
}
return r;
}
static int add_nas_attr(REQUEST *request)
{
VALUE_PAIR *nas;
switch (request->packet->src_ipaddr.af) {
case AF_INET:
nas = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS);
if (!nas) {
nas = radius_paircreate(request, &request->packet->vps,
PW_NAS_IP_ADDRESS,
PW_TYPE_IPADDR);
nas->vp_ipaddr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
}
break;
case AF_INET6:
nas = pairfind(request->packet->vps, PW_NAS_IPV6_ADDRESS);
if (!nas) {
nas = radius_paircreate(request, &request->packet->vps,
PW_NAS_IPV6_ADDRESS,
PW_TYPE_IPV6ADDR);
memcpy(nas->vp_strvalue,
&request->packet->src_ipaddr.ipaddr,
sizeof(request->packet->src_ipaddr.ipaddr));
}
break;
default:
radlog(L_ERR, "Unknown address family for packet");
return -1;
}
return 0;
}
static int preprocess_instantiate(CONF_SECTION *conf, void **instance)
{
int rcode;
rlm_preprocess_t *data;
data = (rlm_preprocess_t *) rad_malloc(sizeof(*data));
memset(data, 0, sizeof(*data));
if (cf_section_parse(conf, data, module_config) < 0) {
free(data);
return -1;
}
data->huntgroups = NULL;
data->hints = NULL;
rcode = pairlist_read(data->huntgroup_file, &(data->huntgroups), 0);
if (rcode < 0) {
radlog(L_ERR|L_CONS, "rlm_preprocess: Error reading %s",
data->huntgroup_file);
return -1;
}
rcode = pairlist_read(data->hints_file, &(data->hints), 0);
if (rcode < 0) {
radlog(L_ERR|L_CONS, "rlm_preprocess: Error reading %s",
data->hints_file);
return -1;
}
*instance = data;
return 0;
}
static int preprocess_authorize(void *instance, REQUEST *request)
{
int r;
rlm_preprocess_t *data = (rlm_preprocess_t *) instance;
rad_mangle(data, request);
if (data->with_ascend_hack) {
ascend_nasport_hack(pairfind(request->packet->vps,
PW_NAS_PORT),
data->ascend_channels_per_line);
}
if (data->with_cisco_vsa_hack) {
cisco_vsa_hack(request->packet->vps);
}
if (data->with_alvarion_vsa_hack) {
alvarion_vsa_hack(request->packet->vps);
}
if (add_nas_attr(request) < 0) {
return RLM_MODULE_FAIL;
}
hints_setup(data->hints, request);
if (pairfind(request->packet->vps, PW_CHAP_PASSWORD) &&
pairfind(request->packet->vps, PW_CHAP_CHALLENGE) == NULL) {
VALUE_PAIR *vp;
vp = radius_paircreate(request, &request->packet->vps,
PW_CHAP_CHALLENGE, PW_TYPE_OCTETS);
vp->length = AUTH_VECTOR_LEN;
memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN);
}
if ((r = huntgroup_access(request,
data->huntgroups)) != RLM_MODULE_OK) {
char buf[1024];
radlog_request(L_AUTH, 0, request, "No huntgroup access: [%s] (%s)",
request->username ? request->username->vp_strvalue : "<NO User-Name>",
auth_name(buf, sizeof(buf), request, 1));
return r;
}
return RLM_MODULE_OK;
}
static int preprocess_preaccounting(void *instance, REQUEST *request)
{
int r;
rlm_preprocess_t *data = (rlm_preprocess_t *) instance;
rad_mangle(data, request);
if (data->with_cisco_vsa_hack) {
cisco_vsa_hack(request->packet->vps);
}
if (data->with_alvarion_vsa_hack) {
alvarion_vsa_hack(request->packet->vps);
}
if (add_nas_attr(request) < 0) {
return RLM_MODULE_FAIL;
}
hints_setup(data->hints, request);
if ((r = huntgroup_access(request,
data->huntgroups)) != RLM_MODULE_OK) {
char buf[1024];
radlog_request(L_INFO, 0, request, "No huntgroup access: [%s] (%s)",
request->username ? request->username->vp_strvalue : "<NO User-Name>",
auth_name(buf, sizeof(buf), request, 1));
return r;
}
return r;
}
static int preprocess_detach(void *instance)
{
rlm_preprocess_t *data = (rlm_preprocess_t *) instance;
pairlist_free(&(data->huntgroups));
pairlist_free(&(data->hints));
free(data);
return 0;
}
module_t rlm_preprocess = {
RLM_MODULE_INIT,
"preprocess",
RLM_TYPE_CHECK_CONFIG_SAFE,
preprocess_instantiate,
preprocess_detach,
{
NULL,
preprocess_authorize,
preprocess_preaccounting,
NULL,
NULL,
NULL,
NULL,
NULL
},
};