/*
* modules.c Radius module support.
*
- * Author: Alan DeKok <aland@ox.org>
- *
* Version: $Id$
*
+ * This program 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.
+ *
+ * This program 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 St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2003 The FreeRADIUS server project
+ * Copyright 2000 Alan DeKok <aland@ox.org>
+ * Copyright 2000 Alan Curry <pacman@world.std.com>
*/
static const char rcsid[] = "$Id$";
-#include "autoconf.h"
-#include "libradius.h"
+#include <freeradius-devel/autoconf.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-#include "radiusd.h"
-#include "modules.h"
-#include "conffile.h"
-#include "ltdl.h"
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modpriv.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/modcall.h>
+#include <freeradius-devel/conffile.h>
+#include "ltdl.h"
+#include <freeradius-devel/rad_assert.h>
-/*
- * Keep track of which modules we've loaded.
- */
-typedef struct module_list_t {
- struct module_list_t *next;
- char name[MAX_STRING_LEN];
- module_t *module;
- lt_dlhandle handle;
-} module_list_t;
+typedef struct indexed_modcallable {
+ struct indexed_modcallable *next;
+ int idx;
+ modcallable *modulelist;
+} indexed_modcallable;
/*
- * Internal list of all of the modules we have loaded.
+ * For each component, keep an ordered list of ones to call.
*/
-static module_list_t *module_list = NULL;
+static indexed_modcallable *components[RLM_COMPONENT_COUNT];
-/*
- * Per-instance data structure, to correlate the modules
- * with the instance names (may NOT be the module names!),
- * and the per-instance data structures.
- */
-typedef struct module_instance_t {
- struct module_instance_t *next;
- char name[MAX_STRING_LEN];
- module_list_t *entry;
- void *insthandle;
-} module_instance_t;
+static rbtree_t *module_tree = NULL;
-/*
- * Internal list of each module instance.
- */
-static module_instance_t *module_instance_list = NULL;
+typedef struct section_type_value_t {
+ const char *section;
+ const char *typename;
+ int attr;
+} section_type_value_t;
-/*
- * For each authorize/authenticate/etc, we have an ordered
- * list of instances to call. This data structure keeps track
- * of that order.
- */
-typedef struct config_module_t {
- struct config_module_t *next;
- int index;
- module_instance_t *instance;
-#if HAVE_PTHREAD_H
- pthread_mutex_t *mutex;
-#endif
-} config_module_t;
/*
- * For each component, keep an ordered list of ones to call.
+ * Ordered by component
*/
-static config_module_t *components[RLM_COMPONENT_COUNT];
+static const section_type_value_t section_type_value[RLM_COMPONENT_COUNT] = {
+ { "authenticate", "Auth-Type", PW_AUTH_TYPE },
+ { "authorize", "Autz-Type", PW_AUTZ_TYPE },
+ { "preacct", "Pre-Acct-Type", PW_PRE_ACCT_TYPE },
+ { "accounting", "Acct-Type", PW_ACCT_TYPE },
+ { "session", "Session-Type", PW_SESSION_TYPE },
+ { "pre-proxy", "Pre-Proxy-Type", PW_PRE_PROXY_TYPE },
+ { "post-proxy", "Post-Proxy-Type", PW_POST_PROXY_TYPE },
+ { "post-auth", "Post-Auth-Type", PW_POST_AUTH_TYPE },
+};
/*
- * The component names.
- *
- * Hmm... we probably should be getting these from the configuration
- * file, too.
+ * Delete ASAP.
*/
-static const char *component_names[RLM_COMPONENT_COUNT] =
-{
- "authenticate",
- "authorize",
- "preacct",
- "accounting",
- "session"
+static const section_type_value_t old_section_type_value[] = {
+ { "authenticate", "authtype", PW_AUTH_TYPE },
+ { "authorize", "autztype", PW_AUTZ_TYPE },
+ { "preacct", "Pre-Acct-Type", PW_PRE_ACCT_TYPE },/* unused */
+ { "accounting", "acctype", PW_ACCT_TYPE },
+ { "session", "sesstype", PW_SESSION_TYPE },
+ { "pre-proxy", "Pre-Proxy-Type", PW_PRE_PROXY_TYPE }, /* unused */
+ { "post-proxy", "Post-Proxy-Type", PW_POST_PROXY_TYPE }, /* unused */
+ { "post-auth", "post-authtype", PW_POST_AUTH_TYPE }
};
-static void config_list_free(config_module_t **cf)
+
+static void indexed_modcallable_free(indexed_modcallable **cf)
{
- config_module_t *c, *next;
+ indexed_modcallable *c, *next;
c = *cf;
while (c) {
-#if HAVE_PTHREAD_H
- if (c->mutex) {
- /*
- * The mutex MIGHT be locked...
- * we'll check for that later, I guess.
- */
- pthread_mutex_destroy(c->mutex);
- free(c->mutex);
- }
-#endif
next = c->next;
+ modcallable_free(&c->modulelist);
free(c);
c = next;
}
*cf = NULL;
}
-static void instance_list_free(module_instance_t **i)
+/*
+ * Free a module instance.
+ */
+static void module_instance_free(void *data)
{
- module_instance_t *c, *next;
+ module_instance_t *this = data;
- c = *i;
- while (c) {
- next = c->next;
- if(c->entry->module->detach)
- (c->entry->module->detach)(c->insthandle);
- free(c);
- c = next;
+ if (this->entry->module->detach)
+ (this->entry->module->detach)(this->insthandle);
+#ifdef HAVE_PTHREAD_H
+ if (this->mutex) {
+ /*
+ * FIXME
+ * The mutex MIGHT be locked...
+ * we'll check for that later, I guess.
+ */
+ pthread_mutex_destroy(this->mutex);
+ free(this->mutex);
}
- *i = NULL;
+#endif
+ free(this);
}
-static void module_list_free(void)
-{
- module_list_t *ml, *next;
- int i;
- /*
- * Delete the internal component pointers.
- */
- for (i = 0; i < RLM_COMPONENT_COUNT; i++) {
- config_list_free(&components[i]);
- }
+/*
+ * Compare two module entries
+ */
+static int module_entry_cmp(const void *one, const void *two)
+{
+ const module_entry_t *a = one;
+ const module_entry_t *b = two;
- instance_list_free(&module_instance_list);
+ return strcmp(a->name, b->name);
+}
- ml = module_list;
- while (ml) {
- next = ml->next;
- if (ml->module->destroy)
- (ml->module->destroy)();
- lt_dlclose(ml->handle); /* ignore any errors */
- free(ml);
- ml = next;
- }
+/*
+ * Free a module entry.
+ */
+static void module_entry_free(void *data)
+{
+ module_entry_t *this = data;
- module_list = NULL;
+ lt_dlclose(this->handle); /* ignore any errors */
+ free(this);
}
+
/*
- * New Auth-Type's start at a large number, and go up from there.
- *
- * We could do something more intelligent, but this should work almost
- * all of the time.
- *
- * FIXME: move this to dict.c as dict_valadd() and dict_valdel()
- * also clear value in module_list free (necessary?)
+ * Remove the module lists.
*/
-static int new_authtype_value(const char *name)
+int detach_modules(void)
{
- static int max_value = 32767;
- DICT_VALUE *old_value, *new_value;
-
+ int i;
+
/*
- * Check to see if it's already defined.
- * If so, return the old value.
+ * Delete the internal component pointers.
*/
- old_value = dict_valbyname(name);
- if (old_value) return old_value->value;
-
- /* Look for the predefined Auth-Type value */
- old_value = dict_valbyattr(PW_AUTHTYPE, 0);
- if (!old_value) return 0; /* something WIERD is happening */
-
- /* allocate a new value */
- new_value = (DICT_VALUE *) rad_malloc(sizeof(DICT_VALUE));
-
- /* copy the old to the new */
- memcpy(new_value, old_value, sizeof(DICT_VALUE));
- old_value->next = new_value;
-
- /* set it up */
- strNcpy(new_value->name, name, sizeof(new_value->name));
- new_value->value = max_value++;
-
- return new_value->value;
+ for (i = 0; i < RLM_COMPONENT_COUNT; i++) {
+ indexed_modcallable_free(&components[i]);
+ }
+
+ return 0;
}
/*
* Find a module on disk or in memory, and link to it.
*/
-static module_list_t *linkto_module(const char *module_name,
- const char *cffilename, int cflineno)
+static module_entry_t *linkto_module(const char *module_name,
+ const char *cffilename, int cflineno)
{
- module_list_t **last, *node;
- lt_dlhandle *handle;
+ module_entry_t myentry;
+ module_entry_t *node;
+ lt_dlhandle handle;
+ char module_struct[256];
+ char *p;
+ const void *module;
- /*
- * Look through the global module library list for the
- * named module.
- */
- last = &module_list;
- for (node = module_list; node != NULL; node = node->next) {
- /*
- * Found the named module. Return it.
- */
- if (strcmp(node->name, module_name) == 0)
- return node;
-
- /*
- * Keep a pointer to the last entry to update...
- */
- last = &node->next;
- }
+ strNcpy(myentry.name, module_name, sizeof(myentry.name));
+ node = rbtree_finddata(module_tree, &myentry);
+ if (node) return node;
/*
* Keep the handle around so we can dlclose() it.
return NULL;
}
- /* make room for the module type */
- node = (module_list_t *) rad_malloc(sizeof(module_list_t));
-
- /* fill in the module structure */
- node->next = NULL;
- node->handle = handle;
- strNcpy(node->name, module_name, sizeof(node->name));
-
/*
* Link to the module's rlm_FOO{} module structure.
+ *
+ * The module_name variable has the version number
+ * embedded in it, and we don't want that here.
+ */
+ strcpy(module_struct, module_name);
+ p = strrchr(module_struct, '-');
+ if (p) *p = '\0';
+
+ DEBUG3(" (Loaded %s, checking if it's valid)", module_name);
+
+ /*
+ * libltld MAY core here, if the handle it gives us contains
+ * garbage data.
*/
- node->module = (module_t *) lt_dlsym(node->handle, module_name);
- if (!node->module) {
+ module = lt_dlsym(handle, module_struct);
+ if (!module) {
radlog(L_ERR|L_CONS, "%s[%d] Failed linking to "
- "%s structure in %s: %s\n",
- cffilename, cflineno,
- module_name, cffilename, lt_dlerror());
- lt_dlclose(node->handle); /* ignore any errors */
- free(node);
+ "%s structure in %s: %s\n",
+ cffilename, cflineno,
+ module_name, cffilename, lt_dlerror());
+ lt_dlclose(handle);
return NULL;
}
-
- /* call the modules initialization */
- if (node->module->init && (node->module->init)() < 0) {
- radlog(L_ERR|L_CONS, "%s[%d] Module initialization failed.\n",
- cffilename, cflineno);
- lt_dlclose(node->handle); /* ignore any errors */
- free(node);
+ /*
+ * Before doing anything else, check if it's sane.
+ */
+ if ((*(const uint32_t *) module) != RLM_MODULE_MAGIC_NUMBER) {
+ lt_dlclose(handle);
+ radlog(L_ERR|L_CONS, "%s[%d] Invalid version in module '%s'",
+ cffilename, cflineno, module_name);
return NULL;
+
}
+ /* make room for the module type */
+ node = rad_malloc(sizeof(*node));
+ memset(node, 0, sizeof(*node));
+ strNcpy(node->name, module_name, sizeof(node->name));
+ node->module = module;
+ node->handle = handle;
+
DEBUG("Module: Loaded %s ", node->module->name);
- *last = node;
+ /*
+ * Add the module as "rlm_foo-version" to the configuration
+ * section.
+ */
+ if (!rbtree_insert(module_tree, node)) {
+ radlog(L_ERR, "Failed to cache module %s", module_name);
+ lt_dlclose(handle);
+ free(node);
+ return NULL;
+ }
return node;
}
/*
* Find a module instance.
*/
-static module_instance_t *find_module_instance(const char *instname)
+module_instance_t *find_module_instance(CONF_SECTION *modules,
+ const char *instname)
{
- CONF_SECTION *cs, *inst_cs;
+ CONF_SECTION *cs;
const char *name1, *name2;
- module_instance_t *node, **last;
+ module_instance_t *node;
char module_name[256];
- /*
- * Look through the global module instance list for the
- * named module.
- */
- last = &module_instance_list;
- for (node = module_instance_list; node != NULL; node = node->next) {
- /*
- * Found the named instance. Return it.
- */
- if (strcmp(node->name, instname) == 0)
- return node;
-
- /*
- * Keep a pointer to the last entry to update...
- */
- last = &node->next;
- }
-
- /*
- * Instance doesn't exist yet. Try to find the
- * corresponding configuration section and create it.
- */
-
- /*
- * Look for the 'modules' configuration section.
- */
- cs = cf_section_find("modules");
- if (!cs) {
- radlog(L_ERR|L_CONS, "ERROR: Cannot find a 'modules' section in the configuration file.\n");
- return NULL;
- }
+ if (!modules) return NULL;
/*
* Module instances are declared in the modules{} block
* name2 from the config section, or name1 if there was
* no name2.
*/
-
- for(inst_cs=cf_subsection_find_next(cs, NULL, NULL)
- ; inst_cs ;
- inst_cs=cf_subsection_find_next(cs, inst_cs, NULL)) {
- name1 = cf_section_name1(inst_cs);
- name2 = cf_section_name2(inst_cs);
- if ( (name2 && !strcmp(name2, instname)) ||
- (!name2 && !strcmp(name1, instname)) )
- break;
- }
- if (!inst_cs) {
+ cs = cf_section_sub_find_name2(modules, NULL, instname);
+ if (cs == NULL) {
radlog(L_ERR|L_CONS, "ERROR: Cannot find a configuration entry for module \"%s\".\n", instname);
return NULL;
}
/*
+ * If there's already a module instance, return it.
+ */
+ node = cf_data_find(cs, "instance");
+ if (node) return node;
+
+ name1 = cf_section_name1(cs);
+ name2 = cf_section_name2(cs);
+
+ /*
* Found the configuration entry.
*/
node = rad_malloc(sizeof(*node));
- node->next = NULL;
+ memset(node, 0, sizeof(*node));
+
node->insthandle = NULL;
-
+
/*
- * Link to the module by name: rlm_FOO
+ * Names in the "modules" section aren't prefixed
+ * with "rlm_", so we add it here.
*/
snprintf(module_name, sizeof(module_name), "rlm_%s", name1);
+
node->entry = linkto_module(module_name,
- "radiusd.conf", cf_section_lineno(inst_cs));
+ mainconfig.radiusd_conf,
+ cf_section_lineno(cs));
if (!node->entry) {
free(node);
/* linkto_module logs any errors */
return NULL;
}
-
+
/*
* Call the module's instantiation routine.
*/
if ((node->entry->module->instantiate) &&
- ((node->entry->module->instantiate)(inst_cs,
- &node->insthandle) < 0)) {
+ ((node->entry->module->instantiate)(cs, &node->insthandle) < 0)) {
radlog(L_ERR|L_CONS,
- "radiusd.conf[%d]: %s: Module instantiation failed.\n",
- cf_section_lineno(inst_cs), instname);
+ "%s[%d]: %s: Module instantiation failed.\n",
+ mainconfig.radiusd_conf, cf_section_lineno(cs),
+ instname);
free(node);
return NULL;
}
-
+
/*
* We're done. Fill in the rest of the data structure,
* and link it to the module instance list.
*/
strNcpy(node->name, instname, sizeof(node->name));
- *last = node;
-
- DEBUG("Module: Instantiated %s (%s) ", name1, node->name);
-
- return node;
-}
-
-/*
- * Add one entry at the end of the config_module_t list.
- */
-static void add_to_list(int comp, module_instance_t *instance, int index)
-{
- config_module_t *node;
- config_module_t **last;
- config_module_t **head;
-
- head = &components[comp];
- last = head;
-
- for (node = *head; node != NULL; node = node->next) {
- last = &node->next;
- }
-
- node = (config_module_t *) rad_malloc(sizeof(config_module_t));
- node->next = NULL;
- node->instance = instance;
- node->index = index;
-
-#if HAVE_PTHREAD_H
+#ifdef HAVE_PTHREAD_H
/*
* If we're threaded, check if the module is thread-safe.
*
* If it isn't, we create a mutex.
*/
- if ((instance->entry->module->type & RLM_TYPE_THREAD_UNSAFE) != 0) {
+ if ((node->entry->module->type & RLM_TYPE_THREAD_UNSAFE) != 0) {
node->mutex = (pthread_mutex_t *) rad_malloc(sizeof(pthread_mutex_t));
/*
* Initialize the mutex.
node->mutex = NULL;
}
-#endif
+#endif
+ cf_data_add(cs, "instance", node, module_instance_free);
- *last = node;
+ DEBUG("Module: Instantiated %s (%s) ", name1, node->name);
+
+ return node;
}
-static config_module_t *lookup_by_index(config_module_t *head, int index)
+static indexed_modcallable *lookup_by_index(indexed_modcallable *head, int idx)
{
- config_module_t *p;
+ indexed_modcallable *p;
for (p = head; p != NULL; p = p->next) {
- if( p->index == index)
+ if( p->idx == idx)
return p;
}
return NULL;
}
-static void load_module_section(CONF_SECTION *cs, int comp, const char *filename)
+/*
+ * Create a new sublist.
+ */
+static indexed_modcallable *new_sublist(int comp, int idx)
+{
+ indexed_modcallable **head = &components[comp];
+ indexed_modcallable *node = *head;
+ indexed_modcallable **last = head;
+
+ while (node) {
+ /* It is an error to try to create a sublist that already
+ * exists. It would almost certainly be caused by accidental
+ * duplication in the config file.
+ *
+ * index 0 is the exception, because it is used when we want
+ * to collect _all_ listed modules under a single index by
+ * default, which is currently the case in all components
+ * except authenticate. */
+ if (node->idx == idx) {
+ if (idx == 0)
+ return node;
+ else
+ return NULL;
+ }
+ last = &node->next;
+ node = node->next;
+ }
+
+ node = rad_malloc(sizeof *node);
+ node->next = NULL;
+ node->modulelist = NULL;
+ node->idx = idx;
+ *last = node;
+ return node;
+}
+
+static int indexed_modcall(int comp, int idx, REQUEST *request)
+{
+ indexed_modcallable *this;
+
+ this = lookup_by_index(components[comp], idx);
+ if (!this) {
+ if (idx != 0) DEBUG2(" ERROR: Unknown value specified for %s. Cannot perform requested action.",
+ section_type_value[comp].typename);
+ /* Return a default value appropriate for the component */
+ switch(comp) {
+ case RLM_COMPONENT_AUTZ: return RLM_MODULE_NOTFOUND;
+ case RLM_COMPONENT_AUTH: return RLM_MODULE_REJECT;
+ case RLM_COMPONENT_PREACCT: return RLM_MODULE_NOOP;
+ case RLM_COMPONENT_ACCT: return RLM_MODULE_NOOP;
+ case RLM_COMPONENT_SESS: return RLM_MODULE_FAIL;
+ case RLM_COMPONENT_PRE_PROXY: return RLM_MODULE_NOOP;
+ case RLM_COMPONENT_POST_PROXY: return RLM_MODULE_NOOP;
+ case RLM_COMPONENT_POST_AUTH: return RLM_MODULE_NOOP;
+ default: return RLM_MODULE_FAIL;
+ }
+ }
+
+ DEBUG2(" Processing the %s section of %s",
+ section_type_value[comp].section, mainconfig.radiusd_conf);
+ return modcall(comp, this->modulelist, request);
+}
+
+/*
+ * Load a sub-module list, as found inside an Auth-Type foo {}
+ * block
+ */
+static int load_subcomponent_section(CONF_SECTION *cs, int comp,
+ const char *filename)
{
- module_instance_t *this;
- CONF_ITEM *modref;
- int modreflineno;
- const char *modrefname;
+ indexed_modcallable *subcomp;
+ modcallable *ml;
+ DICT_VALUE *dval;
+ const char *name2 = cf_section_name2(cs);
- for(modref=cf_item_find_next(cs, NULL)
- ; modref ;
- modref=cf_item_find_next(cs, modref)) {
+ rad_assert(comp >= RLM_COMPONENT_AUTH);
+ rad_assert(comp <= RLM_COMPONENT_COUNT);
- if(cf_item_is_section(modref)) {
+ /*
+ * Sanity check.
+ */
+ if (!name2) {
+ radlog(L_ERR|L_CONS,
+ "%s[%d]: No name specified for %s block",
+ filename, cf_section_lineno(cs),
+ section_type_value[comp].typename);
+ return 1;
+ }
+
+ /*
+ * Compile the group.
+ */
+ ml = compile_modgroup(comp, cs, filename);
+ if (!ml) {
+ return 0;
+ }
+
+ /*
+ * We must assign a numeric index to this subcomponent.
+ * It is generated and placed in the dictionary by
+ * setup_modules(), when it loads the sections. If it
+ * isn't found, it's a serious error.
+ */
+ dval = dict_valbyname(section_type_value[comp].attr, name2);
+ if (!dval) {
+ radlog(L_ERR|L_CONS,
+ "%s[%d] %s %s Not previously configured",
+ filename, cf_section_lineno(cs),
+ section_type_value[comp].typename, name2);
+ modcallable_free(&ml);
+ return 0;
+ }
+
+ subcomp = new_sublist(comp, dval->value);
+ if (!subcomp) {
+ radlog(L_ERR|L_CONS,
+ "%s[%d] %s %s already configured - skipping",
+ filename, cf_section_lineno(cs),
+ section_type_value[comp].typename, name2);
+ modcallable_free(&ml);
+ return 1;
+ }
+
+ subcomp->modulelist = ml;
+ return 1; /* OK */
+}
+
+static int load_component_section(CONF_SECTION *cs, int comp,
+ const char *filename)
+{
+ modcallable *this;
+ CONF_ITEM *modref;
+ int idx;
+ indexed_modcallable *subcomp;
+ const char *modname;
+ const char *visiblename;
+
+ /*
+ * Loop over the entries in the named section.
+ */
+ for (modref = cf_item_find_next(cs, NULL);
+ modref != NULL;
+ modref = cf_item_find_next(cs, modref)) {
+ /*
+ * Look for Auth-Type foo {}, which are special
+ * cases of named sections, and allowable ONLY
+ * at the top-level.
+ *
+ * i.e. They're not allowed in a "group" or "redundant"
+ * subsection.
+ */
+ if (cf_item_is_section(modref)) {
+ const char *sec_name;
CONF_SECTION *scs;
scs = cf_itemtosection(modref);
- modreflineno = cf_section_lineno(scs);
- modrefname = cf_section_name1(scs);
+
+ sec_name = cf_section_name1(scs);
+
+ if (strcmp(sec_name,
+ section_type_value[comp].typename) == 0) {
+ if (!load_subcomponent_section(scs, comp,
+ filename)) {
+ return -1; /* FIXME: memleak? */
+ }
+ continue;
+ }
+
+ /*
+ * Allow old names, too.
+ */
+ if (strcmp(sec_name,
+ old_section_type_value[comp].typename) == 0) {
+ if (!load_subcomponent_section(scs, comp,
+ filename)) {
+ return -1; /* FIXME: memleak? */
+ }
+ continue;
+ }
} else {
CONF_PAIR *cp;
cp = cf_itemtopair(modref);
- modreflineno = cf_pair_lineno(cp);
- modrefname = cf_pair_attr(cp);
}
/*
- * Find an instance for this module.
- * This means link to one if it already exists,
- * or instantiate one, or load the library and
- * instantiate/link.
+ * Try to compile one entry.
*/
- this = find_module_instance(modrefname);
- if (this == NULL) {
- /* find_module_instance logs any errors */
- exit(1);
+ this = compile_modsingle(comp, modref, filename, &modname);
+ if (!this) {
+ radlog(L_ERR|L_CONS,
+ "%s[%d] Failed to parse %s section.\n",
+ filename, cf_section_lineno(cs),
+ cf_section_name1(cs));
+ return -1;
}
- switch (comp) {
- case RLM_COMPONENT_AUTH:
- if (!this->entry->module->authenticate) {
- radlog(L_ERR|L_CONS,
- "%s[%d] Module %s does not contain "
- "an 'authenticate' entry\n",
- filename, modreflineno,
- this->entry->module->name);
- exit(1);
- }
- add_to_list(RLM_COMPONENT_AUTH, this, new_authtype_value(this->name));
- break;
- case RLM_COMPONENT_AUTZ:
- if (!this->entry->module->authorize) {
- radlog(L_ERR|L_CONS,
- "%s[%d] Module %s does not contain "
- "an 'authorize' entry\n",
- filename, modreflineno,
- this->entry->module->name);
- exit(1);
- }
- add_to_list(RLM_COMPONENT_AUTZ, this, 0);
- break;
- case RLM_COMPONENT_PREACCT:
- if (!this->entry->module->preaccounting) {
- radlog(L_ERR|L_CONS,
- "%s[%d] Module %s does not contain "
- "a 'preacct' entry\n",
- filename, modreflineno,
- this->entry->module->name);
- exit(1);
- }
- add_to_list(RLM_COMPONENT_PREACCT, this, 0);
- break;
- case RLM_COMPONENT_ACCT:
- if (!this->entry->module->accounting) {
- radlog(L_ERR|L_CONS,
- "%s[%d] Module %s does not contain "
- "an 'accounting' entry\n",
- filename, modreflineno,
- this->entry->module->name);
- exit(1);
- }
- add_to_list(RLM_COMPONENT_ACCT, this, 0);
- break;
- case RLM_COMPONENT_SESS:
- if (!this->entry->module->checksimul) {
- radlog(L_ERR|L_CONS,
- "%s[%d] Module %s does not contain "
- "a 'checksimul' entry\n",
- filename, modreflineno,
- this->entry->module->name);
- exit(1);
+ if (comp == RLM_COMPONENT_AUTH) {
+ DICT_VALUE *dval;
+
+ dval = dict_valbyname(PW_AUTH_TYPE, modname);
+ if (!dval) {
+ /*
+ * It's a section, but nothing we
+ * recognize. Die!
+ */
+ radlog(L_ERR|L_CONS, "%s[%d] Unknown Auth-Type \"%s\" in %s section.",
+ filename, cf_section_lineno(cs),
+ modname, section_type_value[comp].section);
+ return -1;
}
- add_to_list(RLM_COMPONENT_SESS, this, 0);
- break;
- default:
- radlog(L_ERR|L_CONS, "%s[%d] Unknown component %d.\n",
- filename, modreflineno, comp);
- exit(1);
+ idx = dval->value;
+ } else {
+ /* See the comment in new_sublist() for explanation
+ * of the special index 0 */
+ idx = 0;
}
- }
+
+ subcomp = new_sublist(comp, idx);
+ if (subcomp == NULL) {
+ radlog(L_INFO|L_CONS,
+ "%s %s %s already configured - skipping",
+ filename, section_type_value[comp].typename,
+ modname);
+ modcallable_free(&this);
+ continue;
+ }
+
+ /* If subcomp->modulelist is NULL, add_to_modcallable will
+ * create it */
+ visiblename = cf_section_name2(cs);
+ if (visiblename == NULL)
+ visiblename = cf_section_name1(cs);
+ add_to_modcallable(&subcomp->modulelist, this,
+ comp, visiblename);
+ }
+
+ return 0;
}
+
/*
* Parse the module config sections, and load
* and call each module's init() function.
* Libtool makes your life a LOT easier, especially with libltdl.
* see: http://www.gnu.org/software/libtool/
*/
-int setup_modules(void)
+int setup_modules(int reload)
{
int comp;
- CONF_SECTION *cs;
- const char *filename="radiusd.conf";
+ CONF_SECTION *cs, *modules;
+ int do_component[RLM_COMPONENT_COUNT];
+ rad_listen_t *listener;
/*
- * No current list of modules: Go initialize libltdl.
+ * If necessary, initialize libltdl.
*/
- if (!module_list) {
- if (lt_dlinit() != 0) {
- radlog(L_ERR|L_CONS, "Failed to initialize libraries: %s\n",
- lt_dlerror());
- exit(1); /* FIXME */
-
- }
-
+ if (!reload) {
/*
* Set the default list of preloaded symbols.
* This is used to initialize libltdl's list of
- * preloaded modules.
+ * preloaded modules.
*
* i.e. Static modules.
*/
LTDL_SET_PRELOADED_SYMBOLS();
+ if (lt_dlinit() != 0) {
+ radlog(L_ERR|L_CONS, "Failed to initialize libraries: %s\n",
+ lt_dlerror());
+ return -1;
+ }
+
/*
* Set the search path to ONLY our library directory.
* This prevents the modules from being found from
* any location on the disk.
*/
lt_dlsetsearchpath(radlib_dir);
-
+
DEBUG2("Module: Library search path is %s",
- lt_dlgetsearchpath());
+ lt_dlgetsearchpath());
/*
* Initialize the components.
components[comp] = NULL;
}
+ /*
+ * Set up the internal module struct.
+ */
+ module_tree = rbtree_create(module_entry_cmp,
+ module_entry_free, 0);
+ if (!module_tree) {
+ radlog(L_ERR|L_CONS, "Failed to initialize modules\n");
+ return -1;
+ }
+
+
} else {
- module_list_free();
+ detach_modules();
}
/*
- * Loop over all of the known components, finding their
- * configuration section, and loading it.
+ * Figure out which sections to load.
*/
- for (comp = 0; comp < RLM_COMPONENT_COUNT; ++comp) {
- cs = cf_section_find(component_names[comp]);
- if (!cs) continue;
-
- load_module_section(cs, comp, filename);
- }
+ memset(do_component, 0, sizeof(do_component));
+ for (listener = mainconfig.listen;
+ listener != NULL;
+ listener = listener->next) {
+ switch (listener->type) {
+ case RAD_LISTEN_AUTH:
+ do_component[RLM_COMPONENT_AUTZ] = 1;
+ do_component[RLM_COMPONENT_AUTH] = 1;
+ do_component[RLM_COMPONENT_POST_AUTH] = 1;
+ do_component[RLM_COMPONENT_SESS] = 1;
+ break;
- return 0;
-}
+ case RAD_LISTEN_DETAIL: /* just like acct */
+ case RAD_LISTEN_ACCT:
+ do_component[RLM_COMPONENT_PREACCT] = 1;
+ do_component[RLM_COMPONENT_ACCT] = 1;
+ break;
+ case RAD_LISTEN_PROXY:
+ do_component[RLM_COMPONENT_PRE_PROXY] = 1;
+ do_component[RLM_COMPONENT_POST_PROXY] = 1;
+ break;
-/*
- * Update the Stripped-User-Name attribute.
- */
-static void update_username(REQUEST *request, char *newname)
-{
- VALUE_PAIR *vp;
+ default:
+ rad_assert(0 == 1);
+ break;
+ }
+ }
- /*
- * If there isn't a Stripped-User-Name attribute,
- * go add one, and make it the definitive user name.
- */
- 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);
+ for (comp = RLM_COMPONENT_AUTH; comp < RLM_COMPONENT_COUNT; comp++) {
+ /*
+ * Have the debugging messages all in one place.
+ */
+ if (!do_component[comp]) {
+ DEBUG2("modules: Not loading %s{} section",
+ section_type_value[comp].section);
}
- DEBUG2(" authorize: Creating Stripped-User-Name of %s", newname);
- strcpy((char *)vp->strvalue, newname);
- vp->length = strlen((char *)vp->strvalue);
- pairadd(&request->packet->vps, vp);
- request->username = vp;
- return;
}
/*
- * There is one, update it in place.
+ * Create any DICT_VALUE's for the types. See
+ * 'doc/configurable_failover' for examples of 'authtype'
+ * used to create new Auth-Type values. In order to
+ * let the user create new names, we've got to look for
+ * those names, and create DICT_VALUE's for them.
*/
- vp = request->username;
- DEBUG2(" authorize: Updating Stripped-User-Name from %s to %s",
- vp->strvalue, newname);
- strcpy((char *)vp->strvalue, newname);
- vp->length = strlen((char *)vp->strvalue);
-}
+ for (comp = RLM_COMPONENT_AUTH; comp < RLM_COMPONENT_COUNT; comp++) {
+ int value;
+ const char *name2;
+ DICT_ATTR *dattr;
+ DICT_VALUE *dval;
+ CONF_SECTION *sub, *next;
+ CONF_PAIR *cp;
-#if HAVE_PTHREAD_H
-/*
- * Lock the mutex for the module
- */
-static void safe_lock(config_module_t *instance)
-{
- if (instance->mutex) pthread_mutex_lock(instance->mutex);
-}
+ /*
+ * Not needed, don't load it.
+ */
+ if (!do_component[comp]) {
+ continue;
+ }
+ cs = cf_section_find(section_type_value[comp].section);
-/*
- * Unlock the mutex for the module
- */
-static void safe_unlock(config_module_t *instance)
-{
- if (instance->mutex) pthread_mutex_unlock(instance->mutex);
-}
-#else
-/*
- * No threads: these functions become NULL's.
- */
-#define safe_lock(foo)
-#define safe_unlock(foo)
-#endif
+ if (!cs) continue;
-/*
- * Call all authorization modules until one returns
- * somethings else than RLM_MODULE_OK
- */
-int module_authorize(REQUEST *request)
-{
- config_module_t *this;
- int rcode = RLM_MODULE_OK;
-
- this = components[RLM_COMPONENT_AUTZ];
- rcode = RLM_MODULE_OK;
-
- while (this && rcode == RLM_MODULE_OK) {
- DEBUG2(" authorize: %s", this->instance->entry->module->name);
- safe_lock(this);
- rcode = (this->instance->entry->module->authorize)(
- this->instance->insthandle, request);
- safe_unlock(this);
- this = this->next;
+ sub = NULL;
+ do {
+ /*
+ * See if there's a sub-section by that
+ * name.
+ */
+ next = cf_subsection_find_next(cs, sub,
+ section_type_value[comp].typename);
+
+ /*
+ * Allow some old names, too.
+ */
+ if (!next && (comp <= 4)) {
+ next = cf_subsection_find_next(cs, sub,
+ old_section_type_value[comp].typename);
+ }
+ sub = next;
+
+ /*
+ * If so, look for it to define a new
+ * value.
+ */
+ name2 = cf_section_name2(sub);
+ if (!name2) continue;
+
+
+ /*
+ * If the value already exists, don't
+ * create it again.
+ */
+ dval = dict_valbyname(section_type_value[comp].attr,
+ name2);
+ if (dval) continue;
+
+ /*
+ * Find the attribute for the value.
+ */
+ dattr = dict_attrbyvalue(section_type_value[comp].attr);
+ if (!dattr) {
+ radlog(L_ERR, "%s[%d]: No such attribute %s",
+ mainconfig.radiusd_conf,
+ cf_section_lineno(sub),
+ section_type_value[comp].typename);
+ continue;
+ }
+
+ /*
+ * Create a new unique value with a
+ * meaningless number. You can't look at
+ * it from outside of this code, so it
+ * doesn't matter. The only requirement
+ * is that it's unique.
+ */
+ do {
+ value = lrad_rand() & 0x00ffffff;
+ } while (dict_valbyattr(dattr->attr, value));
+
+ if (dict_addvalue(name2, dattr->name, value) < 0) {
+ radlog(L_ERR, "%s", librad_errstr);
+ return -1;
+ }
+ } while (sub != NULL);
+
+ /*
+ * Loop over the non-sub-sections, too.
+ */
+ cp = NULL;
+ do {
+ /*
+ * See if there's a conf-pair by that
+ * name.
+ */
+ cp = cf_pair_find_next(cs, cp, NULL);
+ if (!cp) break;
+
+
+ /*
+ * If the value already exists, don't
+ * create it again.
+ */
+ name2 = cf_pair_attr(cp);
+ dval = dict_valbyname(section_type_value[comp].attr,
+ name2);
+ if (dval) continue;
+
+ /*
+ * Find the attribute for the value.
+ */
+ dattr = dict_attrbyvalue(section_type_value[comp].attr);
+ if (!dattr) {
+ radlog(L_ERR, "%s[%d]: No such attribute %s",
+ mainconfig.radiusd_conf,
+ cf_section_lineno(sub),
+ section_type_value[comp].typename);
+ continue;
+ }
+
+ /*
+ * Finally, create the new attribute.
+ */
+ do {
+ value = lrad_rand() & 0x00ffffff;
+ } while (dict_valbyattr(dattr->attr, value));
+ if (dict_addvalue(name2, dattr->name, value) < 0) {
+ radlog(L_ERR, "%s", librad_errstr);
+ return -1;
+ }
+ } while (cp != NULL);
+ } /* over the sections which can have redundent sub-sections */
+
+ /*
+ * Remember where the modules were stored.
+ */
+ modules = cf_section_find("modules");
+ if (!modules) {
+ radlog(L_ERR, "Cannot find a \"modules\" section in the configuration file!");
+ return -1;
}
/*
- * Before authenticating the user, update the
- * Stripped-User-Name attribute with any additions.
- *
- * No name: nothing to add.
+ * Look for the 'instantiate' section, which tells us
+ * the instantiation order of the modules, and also allows
+ * us to load modules with no authorize/authenticate/etc.
+ * sections.
*/
- if (request->username != NULL) {
- char newname[256];
- VALUE_PAIR *vp;
+ cs = cf_section_find("instantiate");
+ if (cs != NULL) {
+ CONF_ITEM *ci;
+ CONF_PAIR *cp;
+ module_instance_t *module;
+ const char *name;
/*
- * Try to add a prefix
+ * Loop over the items in the 'instantiate' section.
*/
- for (vp = request->config_items; vp != NULL; vp = vp->next) {
- switch (vp->attribute) {
- default:
- break;
-
- case PW_ADD_PREFIX:
- if ((size_t)(vp->length + request->username->length) > sizeof(vp->strvalue)) {
- DEBUG2("\"%s\"+\"%s\" too long",
- vp->strvalue,
- request->username->strvalue);
- continue;
- }
- strcpy(newname, (char *)vp->strvalue);
- strcat(newname, (char *)request->username->strvalue);
- update_username(request, newname);
- break;
-
- case PW_ADD_SUFFIX:
- if ((size_t)(vp->length + request->username->length) > sizeof(vp->strvalue)) {
- DEBUG2("\"%s\"+\"%s\" too long",
- request->username->strvalue,
- vp->strvalue);
- continue;
- }
- strcpy(newname,
- (char *)request->username->strvalue);
- strcat(newname, (char *)vp->strvalue);
- update_username(request, newname);
- break;
+ for (ci=cf_item_find_next(cs, NULL);
+ ci != NULL;
+ ci=cf_item_find_next(cs, ci)) {
+
+ /*
+ * Skip sections. They'll be handled
+ * later, if they're referenced at all...
+ */
+ if (cf_item_is_section(ci)) {
+ continue;
+ }
+
+ cp = cf_itemtopair(ci);
+ name = cf_pair_attr(cp);
+ module = find_module_instance(modules, name);
+ if (!module) {
+ return -1;
}
- } /* over all configuration items */
+ } /* loop over items in the subsection */
+ } /* if there's an 'instantiate' section. */
- pairdelete(&request->config_items, PW_ADD_PREFIX);
- pairdelete(&request->config_items, PW_ADD_SUFFIX);
+ /*
+ * Loop over all of the known components, finding their
+ * configuration section, and loading it.
+ */
+ for (comp = 0; comp < RLM_COMPONENT_COUNT; ++comp) {
+ cs = cf_section_find(section_type_value[comp].section);
+ if (cs == NULL)
+ continue;
+
+ if (!do_component[comp]) {
+ continue;
+ }
+
+ if (load_component_section(cs, comp, mainconfig.radiusd_conf) < 0) {
+ return -1;
+ }
}
- return rcode;
+ return 0;
}
/*
- * Authenticate a user/password with various methods.
+ * Call all authorization modules until one returns
+ * somethings else than RLM_MODULE_OK
*/
-int module_authenticate(int auth_type, REQUEST *request)
+int module_authorize(int autz_type, REQUEST *request)
{
- config_module_t *this;
- int rcode = RLM_MODULE_FAIL;
-
- this = lookup_by_index(components[RLM_COMPONENT_AUTH], auth_type);
-
/*
- * Only check the FIRST component. If we want multiple
- * Auth-Types, then this function should be called multiple
- * times.
+ * Older versions of the server would pass proxy requests
+ * through the 'authorize' sections twice; once when the
+ * packet was received from the NAS, and again after the
+ * reply was received from the home server. Now that we
+ * have a 'post_proxy' section, the replies from the home
+ * server should be sent through that, instead of through
+ * the 'authorize' section again.
*/
- DEBUG2(" authenticate: %s",
- this->instance->entry->module->name);
- safe_lock(this);
- rcode = (this->instance->entry->module->authenticate)(
- this->instance->insthandle, request);
- safe_unlock(this);
-
- return rcode;
+ if (request->proxy != NULL) {
+ DEBUG2(" authorize: Skipping authorize in post-proxy stage");
+ return RLM_MODULE_NOOP;
+ }
+
+ return indexed_modcall(RLM_COMPONENT_AUTZ, autz_type, request);
}
+/*
+ * Authenticate a user/password with various methods.
+ */
+int module_authenticate(int auth_type, REQUEST *request)
+{
+ return indexed_modcall(RLM_COMPONENT_AUTH, auth_type, request);
+}
/*
* Do pre-accounting for ALL configured sessions
*/
int module_preacct(REQUEST *request)
{
- config_module_t *this;
- int rcode;
-
- this = components[RLM_COMPONENT_PREACCT];
- rcode = RLM_MODULE_OK;
-
- while (this && (rcode == RLM_MODULE_OK)) {
- DEBUG2(" preacct: %s", this->instance->entry->module->name);
- safe_lock(this);
- rcode = (this->instance->entry->module->preaccounting)
- (this->instance->insthandle, request);
- safe_unlock(this);
- this = this->next;
- }
-
- return rcode;
+ return indexed_modcall(RLM_COMPONENT_PREACCT, 0, request);
}
/*
* Do accounting for ALL configured sessions
*/
-int module_accounting(REQUEST *request)
+int module_accounting(int acct_type, REQUEST *request)
{
- config_module_t *this;
- int rcode;
-
- this = components[RLM_COMPONENT_ACCT];
- rcode = RLM_MODULE_OK;
-
- while (this && (rcode == RLM_MODULE_OK)) {
- DEBUG2(" accounting: %s", this->instance->entry->module->name);
- safe_lock(this);
- rcode = (this->instance->entry->module->accounting)
- (this->instance->insthandle, request);
- safe_unlock(this);
- this = this->next;
- }
-
- return rcode;
+ return indexed_modcall(RLM_COMPONENT_ACCT, acct_type, request);
}
/*
*
* Returns: 0 == OK, 1 == double logins, 2 == multilink attempt
*/
-int module_checksimul(REQUEST *request, int maxsimul)
+int module_checksimul(int sess_type, REQUEST *request, int maxsimul)
{
- config_module_t *this;
- int rcode;
+ int rcode;
if(!components[RLM_COMPONENT_SESS])
return 0;
request->simul_max = maxsimul;
request->simul_mpp = 1;
- this = components[RLM_COMPONENT_SESS];
- rcode = RLM_MODULE_FAIL;
-
- while (this && (rcode == RLM_MODULE_FAIL)) {
- DEBUG2(" checksimul: %s", this->instance->entry->module->name);
- safe_lock(this);
- rcode = (this->instance->entry->module->checksimul)
- (this->instance->insthandle, request);
- safe_unlock(this);
- this = this->next;
- }
+ rcode = indexed_modcall(RLM_COMPONENT_SESS, sess_type, request);
- if(rcode != RLM_MODULE_OK) {
+ if (rcode != RLM_MODULE_OK) {
/* FIXME: Good spot for a *rate-limited* warning to the log */
return 0;
}
return (request->simul_count < maxsimul) ? 0 : request->simul_mpp;
}
+
+/*
+ * Do pre-proxying for ALL configured sessions
+ */
+int module_pre_proxy(int type, REQUEST *request)
+{
+ return indexed_modcall(RLM_COMPONENT_PRE_PROXY, type, request);
+}
+
+/*
+ * Do post-proxying for ALL configured sessions
+ */
+int module_post_proxy(int type, REQUEST *request)
+{
+ return indexed_modcall(RLM_COMPONENT_POST_PROXY, type, request);
+}
+
+/*
+ * Do post-authentication for ALL configured sessions
+ */
+int module_post_auth(int postauth_type, REQUEST *request)
+{
+ return indexed_modcall(RLM_COMPONENT_POST_AUTH, postauth_type, request);
+}
+