#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#define REALM_FORMAT_PREFIX 0
#define REALM_FORMAT_SUFFIX 1
typedef struct realm_config_t {
int format;
char *formatstring;
char *delim;
int ignore_default;
int ignore_null;
} realm_config_t;
static CONF_PARSER module_config[] = {
{ "format", PW_TYPE_STRING_PTR,
offsetof(realm_config_t,formatstring), NULL, "suffix" },
{ "delimiter", PW_TYPE_STRING_PTR,
offsetof(realm_config_t,delim), NULL, "@" },
{ "ignore_default", PW_TYPE_BOOLEAN,
offsetof(realm_config_t,ignore_default), NULL, "no" },
{ "ignore_null", PW_TYPE_BOOLEAN,
offsetof(realm_config_t,ignore_null), NULL, "no" },
{ NULL, -1, 0, NULL, NULL }
};
static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm)
{
char namebuf[MAX_STRING_LEN];
char *username;
char *realmname = NULL;
char *ptr;
VALUE_PAIR *vp;
REALM *realm;
struct realm_config_t *inst = instance;
*returnrealm = NULL;
if ((request->proxy != NULL) ||
(request->username == NULL)) {
RDEBUG2("Proxy reply, or no User-Name. Ignoring.");
return RLM_MODULE_OK;
}
if (pairfind(request->packet->vps, PW_REALM) != NULL ) {
RDEBUG2("Request already proxied. Ignoring.");
return RLM_MODULE_OK;
}
strlcpy(namebuf, (char *)request->username->vp_strvalue, sizeof(namebuf));
username = namebuf;
switch(inst->format)
{
case REALM_FORMAT_SUFFIX:
realmname = strrchr(username, inst->delim[0]);
if (realmname) {
*realmname = '\0';
realmname++;
}
break;
case REALM_FORMAT_PREFIX:
ptr = strchr(username, inst->delim[0]);
if (ptr) {
*ptr = '\0';
ptr++;
realmname = username;
username = ptr;
}
break;
default:
realmname = NULL;
break;
}
if (realmname) {
RDEBUG2("Looking up realm \"%s\" for User-Name = \"%s\"",
realmname, request->username->vp_strvalue);
} else {
if( inst->ignore_null ) {
RDEBUG2("No '%c' in User-Name = \"%s\", skipping NULL due to config.",
inst->delim[0], request->username->vp_strvalue);
return RLM_MODULE_NOOP;
}
RDEBUG2("No '%c' in User-Name = \"%s\", looking up realm NULL",
inst->delim[0], request->username->vp_strvalue);
}
realm = realm_find(realmname);
if (!realm) {
RDEBUG2("No such realm \"%s\"",
(realmname == NULL) ? "NULL" : realmname);
return RLM_MODULE_NOOP;
}
if( inst->ignore_default &&
(strcmp(realm->name, "DEFAULT")) == 0) {
RDEBUG2("Found DEFAULT, but skipping due to config.");
return RLM_MODULE_NOOP;
}
RDEBUG2("Found realm \"%s\"", realm->name);
if (realm->striprealm) {
if (request->username->attribute != PW_STRIPPED_USER_NAME) {
vp = radius_paircreate(request, &request->packet->vps,
PW_STRIPPED_USER_NAME,
PW_TYPE_STRING);
RDEBUG2("Adding Stripped-User-Name = \"%s\"", username);
} else {
vp = request->username;
RDEBUG2("Setting Stripped-User-Name = \"%s\"", username);
}
strcpy(vp->vp_strvalue, username);
vp->length = strlen((char *)vp->vp_strvalue);
request->username = vp;
}
if (realm->name[0] != '~') realmname = realm->name;
pairadd(&request->packet->vps, pairmake("Realm", realmname,
T_OP_EQ));
RDEBUG2("Adding Realm = \"%s\"", realmname);
switch (request->packet->code) {
default:
RDEBUG2("Unknown packet code %d\n",
request->packet->code);
return RLM_MODULE_OK;
case PW_ACCOUNTING_REQUEST:
if (!realm->acct_pool) {
RDEBUG2("Accounting realm is LOCAL.");
return RLM_MODULE_OK;
}
break;
case PW_AUTHENTICATION_REQUEST:
if (!realm->auth_pool) {
RDEBUG2("Authentication realm is LOCAL.");
return RLM_MODULE_OK;
}
break;
}
RDEBUG2("Proxying request from user %s to realm %s",
username, realm->name);
if (request->packet->code != PW_ACCOUNTING_REQUEST) {
*returnrealm = realm;
return RLM_MODULE_UPDATED;
}
vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO);
if (vp && (request->packet->src_ipaddr.af == AF_INET)) {
int i;
fr_ipaddr_t my_ipaddr;
my_ipaddr.af = AF_INET;
my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
&my_ipaddr) == 0) {
RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To");
return RLM_MODULE_OK;
}
}
} else if ((request->listener->type == RAD_LISTEN_DETAIL) &&
((request->packet->src_ipaddr.af == AF_INET6) ||
(request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE)))) {
int i;
for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
if ((fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
&request->packet->src_ipaddr) == 0) &&
(realm->acct_pool->servers[i]->port == request->packet->src_port)) {
RDEBUG2("Suppressing proxy because packet was already sent to a server in that realm");
return RLM_MODULE_OK;
}
}
}
*returnrealm = realm;
return RLM_MODULE_UPDATED;
}
static void add_proxy_to_realm(VALUE_PAIR **vps, REALM *realm)
{
VALUE_PAIR *vp;
vp = pairmake("Proxy-To-Realm", realm->name, T_OP_EQ);
if (!vp) {
radlog(L_ERR|L_CONS, "no memory");
exit(1);
}
pairadd(vps, vp);
}
static int realm_instantiate(CONF_SECTION *conf, void **instance)
{
struct realm_config_t *inst;
inst = rad_malloc(sizeof(*inst));
if (!inst) {
return -1;
}
memset(inst, 0, sizeof(*inst));
if(cf_section_parse(conf, inst, module_config) < 0) {
free(inst);
return -1;
}
if(strcasecmp(inst->formatstring, "suffix") == 0) {
inst->format = REALM_FORMAT_SUFFIX;
} else if(strcasecmp(inst->formatstring, "prefix") == 0) {
inst->format = REALM_FORMAT_PREFIX;
} else {
radlog(L_ERR, "Bad value \"%s\" for realm format value", inst->formatstring);
free(inst);
return -1;
}
if(strlen(inst->delim) != 1) {
radlog(L_ERR, "Bad value \"%s\" for realm delimiter value", inst->delim);
free(inst);
return -1;
}
*instance = inst;
return 0;
}
static int realm_authorize(void *instance, REQUEST *request)
{
int rcode;
REALM *realm;
rcode = check_for_realm(instance, request, &realm);
if (rcode != RLM_MODULE_UPDATED) return rcode;
if (!realm) return RLM_MODULE_NOOP;
RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
realm->name);
add_proxy_to_realm(&request->config_items, realm);
return RLM_MODULE_UPDATED;
}
static int realm_preacct(void *instance, REQUEST *request)
{
int rcode;
const char *name = (char *)request->username->vp_strvalue;
REALM *realm;
if (!name)
return RLM_MODULE_OK;
rcode = check_for_realm(instance, request, &realm);
if (rcode != RLM_MODULE_UPDATED) return rcode;
if (!realm) return RLM_MODULE_NOOP;
RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
realm->name);
add_proxy_to_realm(&request->config_items, realm);
return RLM_MODULE_UPDATED;
}
static int realm_detach(void *instance)
{
free(instance);
return 0;
}
module_t rlm_realm = {
RLM_MODULE_INIT,
"realm",
RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,
realm_instantiate,
realm_detach,
{
NULL,
realm_authorize,
realm_preacct,
NULL,
NULL,
NULL,
NULL,
NULL
},
};