/*
- * rlm_realm.c
+ * rlm_realm.c
*
* Version: $Id$
*
*
* 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2000 The FreeRADIUS server project
+ * Copyright 2000,2006 The FreeRADIUS server project
* FIXME add copyrights
*/
-#include "autoconf.h"
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
-#include "libradius.h"
-#include "radiusd.h"
-#include "modules.h"
-
-static const char rcsid[] = "$Id$";
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
#define REALM_FORMAT_PREFIX 0
#define REALM_FORMAT_SUFFIX 1
int format;
char *formatstring;
char *delim;
+ int ignore_default;
+ int ignore_null;
} realm_config_t;
static CONF_PARSER module_config[] = {
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 } /* end the list */
};
/*
* Internal function to cut down on duplicated code.
*
- * Returns NULL on don't proxy, realm otherwise.
+ * Returns -1 on failure, 0 on no failure. returnrealm
+ * is NULL on don't proxy, realm otherwise.
*/
-static REALM *check_for_realm(void *instance, REQUEST *request)
+static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm)
{
- int is_local;
char namebuf[MAX_STRING_LEN];
char *username;
char *realmname = NULL;
struct realm_config_t *inst = instance;
+ /* initiate returnrealm */
+ *returnrealm = NULL;
+
/*
* If the request has a proxy entry, then it's a proxy
* reply, and we're walking through the module list again.
* Also, if there's no User-Name attribute, we can't
* proxy it, either.
*/
+#ifdef WITH_PROXY
if ((request->proxy != NULL) ||
(request->username == NULL)) {
- DEBUG2(" rlm_realm: Proxy reply, or no user name. Ignoring.");
- return NULL;
+ RDEBUG2("Proxy reply, or no User-Name. Ignoring.");
+ return RLM_MODULE_OK;
}
+#endif
/*
* Check for 'Realm' attribute. If it exists, then we've proxied
* it already ( via another rlm_realm instance ) and should return.
*/
- if ( (vp = pairfind(request->packet->vps, PW_REALM)) != NULL ) {
- DEBUG2(" rlm_realm: Request already proxied. Ignoring.");
- return NULL;
+ if (pairfind(request->packet->vps, PW_REALM, 0) != NULL ) {
+ RDEBUG2("Request already proxied. Ignoring.");
+ return RLM_MODULE_OK;
}
/*
* We will be modifing this later, so we want our own copy
* of it.
*/
- strNcpy(namebuf, (char *)request->username->strvalue, sizeof(namebuf));
+ strlcpy(namebuf, (char *)request->username->vp_strvalue, sizeof(namebuf));
username = namebuf;
switch(inst->format)
{
case REALM_FORMAT_SUFFIX:
-
+
/* DEBUG2(" rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */
- realmname = strrchr(username, inst->delim[0]);
+ realmname = strrchr(username, inst->delim[0]);
if (realmname) {
*realmname = '\0';
realmname++;
}
break;
-
+
case REALM_FORMAT_PREFIX:
-
+
/* DEBUG2(" rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */
-
+
ptr = strchr(username, inst->delim[0]);
if (ptr) {
*ptr = '\0';
username = ptr;
}
break;
-
+
default:
realmname = NULL;
break;
}
- DEBUG2(" rlm_realm: Looking up realm %s for User-Name = \"%s\"",
- (realmname == NULL) ? "NULL" : realmname,
- request->username->strvalue);
+ /*
+ * Print out excruciatingly descriptive debugging messages
+ * for the people who find it too difficult to think about
+ * what's going on.
+ */
+ 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);
+ }
/*
- * Allow NULL realms.
+ * Allow DEFAULT realms unless told not to.
*/
- realm = realm_find(realmname, (request->packet->code == PW_ACCOUNTING_REQUEST));
+ realm = realm_find(realmname);
if (!realm) {
- DEBUG2(" rlm_realm: No such realm %s",
+ RDEBUG2("No such realm \"%s\"",
(realmname == NULL) ? "NULL" : realmname);
- return NULL;
+ return RLM_MODULE_NOOP;
}
- DEBUG2(" rlm_realm: Found realm %s", realm->realm);
-
+ 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 we've been told to strip the realm off, then do so.
*/
*
*/
if (request->username->attribute != PW_STRIPPED_USER_NAME) {
- vp = paircreate(PW_STRIPPED_USER_NAME, PW_TYPE_STRING);
- if (!vp) {
- radlog(L_ERR|L_CONS, "no memory");
- exit(1);
- }
- pairadd(&request->packet->vps, vp);
- DEBUG2(" rlm_realm: Adding Stripped-User-Name = \"%s\"", username);
+ vp = radius_paircreate(request, &request->packet->vps,
+ PW_STRIPPED_USER_NAME, 0,
+ PW_TYPE_STRING);
+ RDEBUG2("Adding Stripped-User-Name = \"%s\"", username);
} else {
vp = request->username;
- DEBUG2(" rlm_realm: Setting Stripped-User-Name = \"%s\"", username);
+ RDEBUG2("Setting Stripped-User-Name = \"%s\"", username);
}
- strcpy(vp->strvalue, username);
- vp->length = strlen((char *)vp->strvalue);
+ strcpy(vp->vp_strvalue, username);
+ vp->length = strlen((char *)vp->vp_strvalue);
request->username = vp;
}
- DEBUG2(" rlm_realm: Proxying request from user %s to realm %s",
- username, realm->realm);
+ /*
+ * Add the realm name to the request.
+ * If the realm is a regex, the use the realm as entered
+ * by the user. Otherwise, use the configured realm name,
+ * as realm name comparison is case insensitive. We want
+ * to use the configured name, rather than what the user
+ * entered.
+ */
+ if (realm->name[0] != '~') realmname = realm->name;
+ pairadd(&request->packet->vps, pairmake("Realm", realmname,
+ T_OP_EQ));
+ RDEBUG2("Adding Realm = \"%s\"", realmname);
/*
* Figure out what to do with the request.
*/
- is_local = FALSE;
switch (request->packet->code) {
default:
- DEBUG2("rlm_realm: Unknown packet code %d\n",
+ RDEBUG2("Unknown packet code %d\n",
request->packet->code);
- return NULL; /* don't do anything */
-
+ return RLM_MODULE_OK; /* don't do anything */
+
/*
* Perhaps accounting proxying was turned off.
*/
case PW_ACCOUNTING_REQUEST:
- if (realm->acct_ipaddr == htonl(INADDR_NONE)) {
- DEBUG2("rlm_realm: Accounting realm is LOCAL.");
- is_local = TRUE;
- }
-
- if (realm->acct_port == 0) {
- DEBUG2("rlm_realm: acct_port is not set. proxy cancelled");
- return NULL;
+ if (!realm->acct_pool) {
+ RDEBUG2("Accounting realm is LOCAL.");
+ return RLM_MODULE_OK;
}
break;
* Perhaps authentication proxying was turned off.
*/
case PW_AUTHENTICATION_REQUEST:
- if (realm->ipaddr == htonl(INADDR_NONE)) {
- DEBUG2("rlm_realm: Authentication realm is LOCAL.");
- is_local = TRUE;
- }
-
- if (realm->auth_port == 0) {
- DEBUG2("rlm_realm: auth_port is not set. proxy cancelled");
- return NULL;
+ if (!realm->auth_pool) {
+ RDEBUG2("Authentication realm is LOCAL.");
+ return RLM_MODULE_OK;
}
break;
}
+#ifdef WITH_PROXY
+ RDEBUG2("Proxying request from user %s to realm %s",
+ username, realm->name);
+
/*
- * Add the realm name to the request.
+ * Skip additional checks if it's not an accounting
+ * request.
*/
- pairadd(&request->packet->vps, pairmake("Realm", realm->realm,
- T_OP_EQ));
- DEBUG2(" rlm_realm: Adding Realm = \"%s\"", realm->realm);
+ if (request->packet->code != PW_ACCOUNTING_REQUEST) {
+ *returnrealm = realm;
+ return RLM_MODULE_UPDATED;
+ }
/*
- * Local realm, don't proxy it.
+ * FIXME: Each server should have a unique server key,
+ * and put it in the accounting packet. Every server
+ * should know about the keys, and NOT proxy requests to
+ * a server with key X if the packet already contains key
+ * X.
*/
- if (is_local) {
- return NULL;
+
+ /*
+ * If this request has arrived from another freeradius server
+ * that has already proxied the request, we don't need to do
+ * it again.
+ */
+ vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0);
+ 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;
+
+ /*
+ * Loop over the home accounting servers for this
+ * realm. If one of them has the same IP as the
+ * FreeRADIUS-Proxied-To attribute, then the
+ * packet has already been sent there. Don't
+ * send it there again.
+ */
+ 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;
+ }
+ }
+
+ /*
+ * See detail_recv() in src/main/listen.c for the
+ * additional checks.
+ */
+ } 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;
+
+ /*
+ * Loop over the home accounting servers for this
+ * realm. If one of them has the same IP as the
+ * FreeRADIUS-Proxied-To attribute, then the
+ * packet has already been sent there. Don't
+ * send it there again.
+ */
+ 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;
+ }
+ }
+
}
+#endif
- return realm;
+ /*
+ * We got this far, which means we have a realm, set returnrealm
+ */
+ *returnrealm = realm;
+
+ return RLM_MODULE_UPDATED;
}
/*
* Tell the server to proxy this request to another
* realm.
*/
- vp = pairmake("Proxy-To-Realm", realm->realm, T_OP_EQ);
+ vp = pairmake("Proxy-To-Realm", realm->name, T_OP_EQ);
if (!vp) {
radlog(L_ERR|L_CONS, "no memory");
exit(1);
}
-
+
/*
* Add it, even if it's already present.
*/
struct realm_config_t *inst;
/* setup a storage area for instance data */
- inst = rad_malloc(sizeof(struct realm_config_t));
+ 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);
free(inst);
return -1;
}
- free(inst->formatstring);
if(strlen(inst->delim) != 1) {
radlog(L_ERR, "Bad value \"%s\" for realm delimiter value", inst->delim);
free(inst);
-
+
/*
* Examine a request for a username with an realm, and if it
*/
static int realm_authorize(void *instance, REQUEST *request)
{
+ int rcode;
REALM *realm;
/*
* If not, return without adding a Proxy-To-Realm
* attribute.
*/
- realm = check_for_realm(instance, request);
- if (!realm) {
- return RLM_MODULE_NOOP;
- }
+ rcode = check_for_realm(instance, request, &realm);
+ if (rcode != RLM_MODULE_UPDATED) return rcode;
+ if (!realm) return RLM_MODULE_NOOP;
/*
* Maybe add a Proxy-To-Realm attribute to the request.
*/
- DEBUG2("rlm_realm: Preparing to proxy authentication request to realm %s\n",
- realm->realm);
+ RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
+ realm->name);
add_proxy_to_realm(&request->config_items, realm);
return RLM_MODULE_UPDATED; /* try the next module */
*/
static int realm_preacct(void *instance, REQUEST *request)
{
- const char *name = (char *)request->username->strvalue;
+ int rcode;
+ const char *name = (char *)request->username->vp_strvalue;
REALM *realm;
if (!name)
return RLM_MODULE_OK;
-
+
/*
* Check if we've got to proxy the request.
* If not, return without adding a Proxy-To-Realm
* attribute.
*/
- realm = check_for_realm(instance, request);
- if (!realm) {
- return RLM_MODULE_NOOP;
- }
-
+ rcode = check_for_realm(instance, request, &realm);
+ if (rcode != RLM_MODULE_UPDATED) return rcode;
+ if (!realm) return RLM_MODULE_NOOP;
/*
* Maybe add a Proxy-To-Realm attribute to the request.
*/
- DEBUG2("rlm_realm: Preparing to proxy accounting request to realm %s\n",
- realm->realm);
+ RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
+ realm->name);
add_proxy_to_realm(&request->config_items, realm);
- return RLM_MODULE_OK; /* try the next module */
+ return RLM_MODULE_UPDATED; /* try the next module */
}
static int realm_detach(void *instance)
{
- struct realm_config_t *inst = instance;
- free(inst->delim);
free(instance);
return 0;
}
/* globally exported name */
module_t rlm_realm = {
- "realm",
- 0, /* type: reserved */
- NULL, /* initialization */
- realm_instantiate, /* instantiation */
- {
- NULL, /* authentication */
- realm_authorize, /* authorization */
- realm_preacct, /* preaccounting */
- NULL, /* accounting */
- NULL /* checksimul */
- },
- realm_detach, /* detach */
- NULL, /* destroy */
+ RLM_MODULE_INIT,
+ "realm",
+ RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE, /* type */
+ realm_instantiate, /* instantiation */
+ realm_detach, /* detach */
+ {
+ NULL, /* authentication */
+ realm_authorize, /* authorization */
+ realm_preacct, /* preaccounting */
+ NULL, /* accounting */
+ NULL, /* checksimul */
+ NULL, /* pre-proxy */
+ NULL, /* post-proxy */
+ NULL /* post-auth */
+ },
};