2 * modules.c Radius module support.
4 * Author: Alan DeKok <aland@ox.org>
10 static const char rcsid[] = "$Id$";
24 #define RLM_AUTHORIZE 1
25 #define RLM_AUTHENTICATE 2
26 #define RLM_ACCOUNTING 4
29 * Keep track of which modules we've loaded.
31 typedef struct module_list_t {
32 char filename[MAX_STRING_LEN];
33 int default_auth_type;
36 struct module_list_t *next;
39 static module_list_t *module_list = NULL;
41 typedef struct module_instance_t {
43 char name[MAX_STRING_LEN];
45 struct module_instance_t *next;
48 static module_instance_t *module_instance_list = NULL;
50 typedef struct config_module_t {
51 module_instance_t *instance;
52 struct config_module_t *next;
55 typedef struct indexed_config_module_t {
57 config_module_t *modulelist;
58 struct indexed_config_module_t *next;
59 } indexed_config_module_t;
61 static config_module_t *authorize = NULL;
62 static indexed_config_module_t *authenticate = NULL;
63 static config_module_t *preacct = NULL;
64 static config_module_t *accounting = NULL;
66 static void config_list_free(config_module_t **cf)
68 config_module_t *c, *next;
79 static void indexed_config_list_free(indexed_config_module_t **cf)
81 indexed_config_module_t *c, *next;
86 config_list_free(&c->modulelist);
93 static void instance_list_free(module_instance_t **i)
95 module_instance_t *c, *next;
100 if(c->entry->module->detach)
101 (c->entry->module->detach)(c->insthandle);
108 static void module_list_free(void)
110 module_list_t *ml, *next;
112 indexed_config_list_free(&authenticate);
113 config_list_free(&authorize);
114 config_list_free(&preacct);
115 config_list_free(&accounting);
117 instance_list_free(&module_instance_list);
122 if (ml->module->destroy)
123 (ml->module->destroy)();
124 lt_dlclose(ml->handle); /* ignore any errors */
133 * New Auth-Type's start at a large number, and go up from there.
135 * We could do something more intelligent, but this should work almost
138 * FIXME: move this to dict.c as dict_valadd() and dict_valdel()
139 * also clear value in module_list free (necessary?)
141 static int new_authtype_value(const char *name)
143 static int max_value = 32767;
144 DICT_VALUE *old_value, *new_value;
147 * Check to see if it's already defined.
148 * If so, return the old value.
150 old_value = dict_valbyname(name);
151 if (old_value) return old_value->value;
153 /* Look for the predefined Auth-Type value */
154 old_value = dict_valbyattr(PW_AUTHTYPE, 0);
155 if (!old_value) return 0; /* something WIERD is happening */
157 /* allocate a new value */
158 new_value = (DICT_VALUE *) malloc(sizeof(DICT_VALUE));
160 fprintf(stderr, "Out of memory\n");
164 /* copy the old to the new */
165 memcpy(new_value, old_value, sizeof(DICT_VALUE));
166 old_value->next = new_value;
169 strNcpy(new_value->name, name, sizeof(new_value->name));
170 new_value->value = max_value++;
172 return new_value->value;
175 static module_list_t *find_module(module_list_t *head, const char *filename,
176 const char *module_name,
177 const char *cffilename, int cflineno)
179 module_list_t **last, *node;
185 if (strcmp(head->filename, filename) == 0)
198 * Keep the handle around so we can dlclose() it.
199 * Also ensure that any further dependencies are exported,
200 * so that PAM can work.
202 * i.e. rlm_pam.so links to libpam.so, which in turn dlopen()'s
203 * pam_foo.so. Without RTLD_GLOBAL, the functions in libpam.so
204 * won't get exported to pam_foo.so.
206 handle = lt_dlopenext(filename);
207 if (handle == NULL) {
208 fprintf(stderr, "%s[%d] Failed to link to module '%s':"
209 " %s\n", cffilename, cflineno, filename, lt_dlerror());
213 /* make room for the module type */
214 new = (module_list_t *) malloc(sizeof(module_list_t));
216 fprintf(stderr, "%s[%d] Failed to allocate memory.\n",
217 cffilename, cflineno);
218 lt_dlclose(handle); /* ignore any errors */
222 /* fill in the module structure */
224 new->handle = handle;
225 strNcpy(new->filename, filename, sizeof(new->filename));
227 new->module = (module_t *) lt_dlsym(new->handle, module_name);
228 error = lt_dlerror();
230 fprintf(stderr, "%s[%d] Failed linking to "
231 "%s structure in %s: %s\n",
232 cffilename, cflineno,
233 module_name, cffilename, error);
234 lt_dlclose(new->handle); /* ignore any errors */
239 /* If there's an authentication method, add a new Auth-Type */
240 if (new->module->authenticate)
241 new->default_auth_type =
242 new_authtype_value(new->module->name);
244 /* call the modules initialization */
245 if (new->module->init &&
246 (new->module->init)() < 0) {
248 "%s[%d] Module initialization failed.\n",
249 cffilename, cflineno);
250 lt_dlclose(new->handle); /* ignore any errors */
255 DEBUG("Module: Loaded %s ", new->module->name);
262 static module_instance_t *find_module_instance(module_instance_t *head,
263 const char *instname)
265 CONF_SECTION *cs, *inst_cs;
266 const char *name1, *name2;
267 module_instance_t *new;
269 char module_name[256];
272 if (strcmp(head->name, instname) == 0)
277 /* Instance doesn't exist yet. Try to find the corresponding config
278 * section and create it. */
280 new = malloc(sizeof *new);
282 fprintf(stderr, "Out of memory\n");
286 cs = cf_section_find("modules");
291 /* Module instances are declared in the modules{} block and referenced
292 * later by their name, which is the name2 from the config section,
293 * or name1 if there was no name2. */
295 for(inst_cs=cf_subsection_find_next(cs, NULL, NULL)
297 inst_cs=cf_subsection_find_next(cs, inst_cs, NULL)) {
298 name1 = cf_section_name1(inst_cs);
299 name2 = cf_section_name2(inst_cs);
300 if ( (name2 && !strcmp(name2, instname)) ||
301 (!name2 && !strcmp(name1, instname)) )
307 snprintf(library, sizeof library, "rlm_%s.so", name1);
308 snprintf(module_name, sizeof module_name, "rlm_%s", name1);
309 new->entry = find_module(module_list, library, module_name,
310 "radiusd.conf", cf_section_lineno(inst_cs));
316 if(!new->entry->module->instantiate) {
318 } else if((new->entry->module->instantiate)(inst_cs,
319 &new->insthandle) < 0) {
321 "radiusd.conf[%d]: %s: Module instantiation failed.\n",
322 cf_section_lineno(inst_cs), instname);
327 strNcpy(new->name, instname, sizeof(new->name));
328 new->next = module_instance_list;
329 module_instance_list = new;
331 DEBUG("Module: Instantiated %s (%s) ", name1, new->name);
337 * Add one entry at the end of the config_module_t list.
339 static void add_to_list(config_module_t **head, module_instance_t *instance)
341 config_module_t *node = *head;
342 config_module_t **last = head;
349 node = (config_module_t *) malloc(sizeof(config_module_t));
351 fprintf(stderr, "Out of memory\n");
356 node->instance = instance;
360 static indexed_config_module_t *new_sublist(indexed_config_module_t **head,
363 indexed_config_module_t *node = *head;
364 indexed_config_module_t **last = head;
367 if (node->idx == idx) {
368 /* It is an error to try to create a sublist that
376 node = malloc(sizeof *node);
378 fprintf(stderr, "Out of memory\n");
383 node->modulelist = NULL;
389 static config_module_t *lookup_indexed_config(indexed_config_module_t *head,
392 indexed_config_module_t *p;
393 for (p=head; p; p=p->next) {
395 return p->modulelist;
400 /* Why is this both here and in radiusd.c:server_config? --Pac. */
401 static CONF_PARSER module_config[] = {
402 { "libdir", PW_TYPE_STRING_PTR, &radlib_dir, LIBDIR },
404 { NULL, -1, NULL, NULL }
407 static void load_authtype_subsection(CONF_SECTION *cs, const char *filename)
409 module_instance_t *this;
412 const char *modrefname;
414 indexed_config_module_t *auth_type_config;
416 auth_type = new_authtype_value(cf_section_name2(cs));
417 auth_type_config = new_sublist(&authenticate, auth_type);
418 if (!auth_type_config) {
420 "%s[%d] authtype %s already configured - skipping",
421 filename, cf_section_lineno(cs), cf_section_name2(cs));
425 for(modref=cf_item_find_next(cs, NULL)
427 modref=cf_item_find_next(cs, modref)) {
429 if(cf_item_is_section(modref)) {
431 scs = cf_itemtosection(modref);
432 modreflineno = cf_section_lineno(scs);
433 modrefname = cf_section_name1(scs);
436 cp = cf_itemtopair(modref);
437 modreflineno = cf_pair_lineno(cp);
438 modrefname = cf_pair_attr(cp);
441 this = find_module_instance(module_instance_list, modrefname);
446 if (!this->entry->module->authenticate) {
448 "%s[%d] Module %s does not contain "
449 "an 'authenticate' entry\n",
450 filename, modreflineno,
451 this->entry->module->name);
454 add_to_list(&auth_type_config->modulelist, this);
459 static void load_indexed_module_section(CONF_SECTION *cs, int comp, const char *filename)
461 module_instance_t *this;
464 const char *modrefname;
465 indexed_config_module_t *auth_type_config;
467 /* This function does not yet need or want to handle anything but
469 assert(comp==RLM_COMPONENT_AUTH);
471 for(modref=cf_item_find_next(cs, NULL)
473 modref=cf_item_find_next(cs, modref)) {
475 if(cf_item_is_section(modref)) {
477 scs = cf_itemtosection(modref);
478 if (!strcmp(cf_section_name1(scs), "authtype")) {
479 load_authtype_subsection(scs, filename);
482 modreflineno = cf_section_lineno(scs);
483 modrefname = cf_section_name1(scs);
486 cp = cf_itemtopair(modref);
487 modreflineno = cf_pair_lineno(cp);
488 modrefname = cf_pair_attr(cp);
491 this = find_module_instance(module_instance_list, modrefname);
496 if (!this->entry->module->authenticate) {
498 "%s[%d] Module %s does not contain "
499 "an 'authenticate' entry\n",
500 filename, modreflineno,
501 this->entry->module->name);
504 auth_type_config = new_sublist(&authenticate,
505 this->entry->default_auth_type);
506 if (!auth_type_config) {
508 "%s[%d] authtype %s already configured - skipping",
509 filename, modreflineno, this->entry->module->name);
512 add_to_list(&auth_type_config->modulelist, this);
517 static void load_module_section(CONF_SECTION *cs, int comp, const char *filename)
519 module_instance_t *this;
522 const char *modrefname;
524 /* Authentication is special - it is not an ordered list but an
525 * associative array keyed on auth-type */
526 if (comp==RLM_COMPONENT_AUTH) {
527 load_indexed_module_section(cs, comp, filename);
531 for(modref=cf_item_find_next(cs, NULL)
533 modref=cf_item_find_next(cs, modref)) {
535 if(cf_item_is_section(modref)) {
537 scs = cf_itemtosection(modref);
538 modreflineno = cf_section_lineno(scs);
539 modrefname = cf_section_name1(scs);
542 cp = cf_itemtopair(modref);
543 modreflineno = cf_pair_lineno(cp);
544 modrefname = cf_pair_attr(cp);
547 this = find_module_instance(module_instance_list, modrefname);
553 case RLM_COMPONENT_AUTZ:
554 if (!this->entry->module->authorize) {
556 "%s[%d] Module %s does not contain "
557 "an 'authorize' entry\n",
558 filename, modreflineno,
559 this->entry->module->name);
562 add_to_list(&authorize, this);
564 case RLM_COMPONENT_PREACCT:
565 if (!this->entry->module->preaccounting) {
567 "%s[%d] Module %s does not contain "
568 "a 'preacct' entry\n",
569 filename, modreflineno,
570 this->entry->module->name);
573 add_to_list(&preacct, this);
575 case RLM_COMPONENT_ACCT:
576 if (!this->entry->module->accounting) {
578 "%s[%d] Module %s does not contain "
579 "an 'accounting' entry\n",
580 filename, modreflineno,
581 this->entry->module->name);
584 add_to_list(&accounting, this);
587 radlog(L_ERR|L_CONS, "%s[%d] Unknown component %d.\n",
588 filename, modreflineno, comp);
595 * Parse the module config sections, and load
596 * and call each module's init() function.
598 * Libtool makes your life a LOT easier, especially with libltdl.
599 * see: http://www.gnu.org/software/libtool/
601 int setup_modules(void)
606 const char *filename="radiusd.conf";
609 * And parse the modules configuration values.
611 cs = cf_section_find(NULL);
613 cf_section_parse(cs, module_config);
617 * No current list of modules: Go initialize libltdl.
620 if (lt_dlinit() != 0) {
621 fprintf(stderr, "Failed to initialize libraries: %s\n",
627 * Set the default list of preloaded symbols.
628 * This is used to initialize libltdl's list of
631 * i.e. Static modules.
633 LTDL_SET_PRELOADED_SYMBOLS();
636 * Set the search path to ONLY our library directory.
637 * This prevents the modules from being found from
638 * any location on the disk.
640 lt_dlsetsearchpath(radlib_dir);
642 DEBUG2("modules: Library search path is %s",
643 lt_dlgetsearchpath());
649 for (comp=0; comp<RLM_COMPONENT_COUNT; ++comp) {
651 case RLM_COMPONENT_AUTH: control="authenticate"; break;
652 case RLM_COMPONENT_AUTZ: control="authorize"; break;
653 case RLM_COMPONENT_PREACCT: control="preacct"; break;
654 case RLM_COMPONENT_ACCT: control="accounting"; break;
655 default: control="unknown";
658 cs = cf_section_find(control);
662 load_module_section(cs, comp, filename);
670 * Update the Stripped-User-Name attribute.
672 static void update_username(REQUEST *request, char *newname)
677 * If there isn't a Stripped-User-Name attribute,
678 * go add one, and make it the definitive user name.
680 if (request->username->attribute != PW_STRIPPED_USER_NAME) {
681 vp = paircreate(PW_STRIPPED_USER_NAME, PW_TYPE_STRING);
683 radlog(L_ERR|L_CONS, "no memory");
686 DEBUG2(" authorize: Creating Stripped-User-Name of %s", newname);
687 strcpy((char *)vp->strvalue, newname);
688 vp->length = strlen((char *)vp->strvalue);
689 pairadd(&request->packet->vps, vp);
690 request->username = vp;
695 * There is one, update it in place.
697 vp = request->username;
698 DEBUG2(" authorize: Updating Stripped-User-Name from %s to %s",
699 vp->strvalue, newname);
700 strcpy((char *)vp->strvalue, newname);
701 vp->length = strlen((char *)vp->strvalue);
705 * Call all authorization modules until one returns
706 * somethings else than RLM_MODULE_OK
708 int module_authorize(REQUEST *request)
710 config_module_t *this;
711 int rcode = RLM_MODULE_OK;
714 rcode = RLM_MODULE_OK;
716 while (this && rcode == RLM_MODULE_OK) {
717 DEBUG2(" authorize: %s", this->instance->entry->module->name);
718 rcode = (this->instance->entry->module->authorize)(
719 this->instance->insthandle, request);
724 * Before authenticating the user, update the
725 * Stripped-User-Name attribute with any additions.
727 * No name: nothing to add.
729 if (request->username != NULL) {
734 * Try to add a prefix
736 for (vp = request->config_items; vp != NULL; vp = vp->next) {
737 switch (vp->attribute) {
742 if ((size_t)(vp->length + request->username->length) > sizeof(vp->strvalue)) {
743 DEBUG2("\"%s\"+\"%s\" too long",
745 request->username->strvalue);
748 strcpy(newname, (char *)vp->strvalue);
749 strcat(newname, (char *)request->username->strvalue);
750 update_username(request, newname);
754 if ((size_t)(vp->length + request->username->length) > sizeof(vp->strvalue)) {
755 DEBUG2("\"%s\"+\"%s\" too long",
756 request->username->strvalue,
761 (char *)request->username->strvalue);
762 strcat(newname, (char *)vp->strvalue);
763 update_username(request, newname);
766 } /* over all configuration items */
768 pairdelete(&request->config_items, PW_ADD_PREFIX);
769 pairdelete(&request->config_items, PW_ADD_SUFFIX);
776 * Authenticate a user/password with various methods.
778 int module_authenticate(int auth_type, REQUEST *request)
780 config_module_t *this;
781 int rcode = RLM_MODULE_FAIL;
784 * We MUST have a password, of SOME type!
786 if (request->password == NULL) {
787 return RLM_MODULE_FAIL;
790 this = lookup_indexed_config(authenticate, auth_type);
792 while (this && rcode == RLM_MODULE_FAIL) {
793 DEBUG2(" authenticate: %s",
794 this->instance->entry->module->name);
795 rcode = (this->instance->entry->module->authenticate)(
796 this->instance->insthandle, request);
804 * Do pre-accounting for ALL configured sessions
806 int module_preacct(REQUEST *request)
808 config_module_t *this;
812 rcode = RLM_MODULE_OK;
814 while (this && (rcode == RLM_MODULE_OK)) {
815 DEBUG2(" preacct: %s", this->instance->entry->module->name);
816 rcode = (this->instance->entry->module->preaccounting)
817 (this->instance->insthandle, request);
825 * Do accounting for ALL configured sessions
827 int module_accounting(REQUEST *request)
829 config_module_t *this;
833 rcode = RLM_MODULE_OK;
835 while (this && (rcode == RLM_MODULE_OK)) {
836 DEBUG2(" accounting: %s", this->instance->entry->module->name);
837 rcode = (this->instance->entry->module->accounting)
838 (this->instance->insthandle, request);
846 * Module malloc() call, which does stuff if the malloc fails.
848 * This call ALWAYS succeeds!
850 void *rlm_malloc(size_t size)
852 void *ptr = malloc(size);
855 radlog(L_ERR|L_CONS, "no memory");