2 * modules.c Radius module support.
4 * Author: Alan DeKok <aland@ox.org>
10 static const char rcsid[] = "$Id$";
23 #define RLM_AUTHORIZE 1
24 #define RLM_AUTHENTICATE 2
25 #define RLM_ACCOUNTING 4
28 * Keep track of which modules we've loaded.
30 typedef struct module_list_t {
31 char filename[MAX_STRING_LEN];
35 struct module_list_t *next;
38 static module_list_t *module_list = NULL;
40 typedef struct module_instance_t {
42 char name[MAX_STRING_LEN];
44 struct module_instance_t *next;
47 static module_instance_t *module_instance_list = NULL;
49 typedef struct config_module_t {
50 module_instance_t *instance;
51 struct config_module_t *next;
54 static config_module_t *authorize = NULL;
55 static config_module_t *authenticate = NULL;
56 static config_module_t *preacct = NULL;
57 static config_module_t *accounting = NULL;
59 static void config_list_free(config_module_t **cf)
61 config_module_t *c, *next;
72 static void instance_list_free(module_instance_t **i)
74 module_instance_t *c, *next;
79 if(c->entry->module->detach)
80 (c->entry->module->detach)(c->insthandle);
87 static void module_list_free(void)
89 module_list_t *ml, *next;
91 config_list_free(&authenticate);
92 config_list_free(&authorize);
93 config_list_free(&preacct);
94 config_list_free(&accounting);
96 instance_list_free(&module_instance_list);
101 if (ml->module->destroy)
102 (ml->module->destroy)();
103 lt_dlclose(ml->handle); /* ignore any errors */
112 * New Auth-Type's start at a large number, and go up from there.
114 * We could do something more intelligent, but this should work almost
117 * FIXME: move this to dict.c as dict_valadd() and dict_valdel()
118 * also clear value in module_list free (nessecary?)
120 static int new_authtype_value(const char *name)
122 static int max_value = 32767;
123 DICT_VALUE *old_value, *new_value;
126 * Check to see if it's already defined.
127 * If so, return the old value.
129 old_value = dict_valbyname(name);
130 if (old_value) return old_value->value;
132 /* Look for the predefined Auth-Type value */
133 old_value = dict_valbyattr(PW_AUTHTYPE, 0);
134 if (!old_value) return 0; /* something WIERD is happening */
136 /* allocate a new value */
137 new_value = (DICT_VALUE *) malloc(sizeof(DICT_VALUE));
139 fprintf(stderr, "Out of memory\n");
143 /* copy the old to the new */
144 memcpy(new_value, old_value, sizeof(DICT_VALUE));
145 old_value->next = new_value;
148 strNcpy(new_value->name, name, sizeof(new_value->name));
149 new_value->value = max_value++;
151 return new_value->value;
154 static module_list_t *find_module(module_list_t *head, const char *filename,
155 const char *module_name,
156 const char *cffilename, int cflineno)
158 module_list_t **last, *node;
164 if (strcmp(head->filename, filename) == 0)
177 * Keep the handle around so we can dlclose() it.
178 * Also ensure that any further dependencies are exported,
179 * so that PAM can work.
181 * i.e. rlm_pam.so links to libpam.so, which in turn dlopen()'s
182 * pam_foo.so. Without RTLD_GLOBAL, the functions in libpam.so
183 * won't get exported to pam_foo.so.
185 handle = lt_dlopenext(filename);
186 if (handle == NULL) {
187 fprintf(stderr, "%s[%d] Failed to link to module %s:"
188 " %s\n", cffilename, cflineno, filename, lt_dlerror());
192 /* make room for the module type */
193 new = (module_list_t *) malloc(sizeof(module_list_t));
195 fprintf(stderr, "%s[%d] Failed to allocate memory.\n",
196 cffilename, cflineno);
197 lt_dlclose(handle); /* ignore any errors */
201 /* fill in the module structure */
203 new->handle = handle;
204 strNcpy(new->filename, filename, sizeof(new->filename));
206 new->module = (module_t *) lt_dlsym(new->handle, module_name);
207 error = lt_dlerror();
209 fprintf(stderr, "%s[%d] Failed linking to "
210 "%s structure in %s: %s\n",
211 cffilename, cflineno,
212 module_name, cffilename, error);
213 lt_dlclose(new->handle); /* ignore any errors */
218 /* If there's an authentication method, add a new Auth-Type */
219 if (new->module->authenticate)
221 new_authtype_value(new->module->name);
223 /* call the modules initialization */
224 if (new->module->init &&
225 (new->module->init)() < 0) {
227 "%s[%d] Module initialization failed.\n",
228 cffilename, cflineno);
229 lt_dlclose(new->handle); /* ignore any errors */
234 DEBUG("Module: Loaded %s ", new->module->name);
241 static module_instance_t *find_module_instance(module_instance_t *head,
242 const char *instname)
244 CONF_SECTION *cs, *inst_cs;
245 module_instance_t *new;
247 char module_name[256];
250 if (strcmp(head->name, instname) == 0)
255 /* Instance doesn't exist yet. Try to find the corresponding config
256 * section and create it. */
258 new = malloc(sizeof *new);
260 fprintf(stderr, "Out of memory\n");
264 cs = cf_section_find("modules");
269 /* Module instances are declared in the modules{} block and referenced
270 * later by their name, which is the name2 from the config section,
271 * or name1 if there was no name2. */
273 for(inst_cs=cf_subsection_find_next(cs, NULL, NULL)
275 inst_cs=cf_subsection_find_next(cs, inst_cs, NULL)) {
276 if ( (inst_cs->name2 && !strcmp(inst_cs->name2, instname)) ||
277 (!inst_cs->name2 && !strcmp(inst_cs->name1, instname)) )
283 snprintf(library, sizeof library, "rlm_%s.so", inst_cs->name1);
284 snprintf(module_name, sizeof module_name, "rlm_%s", inst_cs->name1);
285 new->entry = find_module(module_list, library, module_name,
286 "radiusd.conf", inst_cs->lineno);
292 if(!new->entry->module->instantiate) {
294 } else if((new->entry->module->instantiate)(inst_cs,
295 &new->insthandle) < 0) {
297 "radiusd.conf[%d]: %s: Module instantiation failed.\n",
298 inst_cs->lineno, instname);
303 strNcpy(new->name, instname, sizeof(new->name));
304 new->next = module_instance_list;
305 module_instance_list = new;
307 DEBUG("Module: Instantiated %s (%s) ", inst_cs->name1, new->name);
313 * Add one entry at the end of the config_module_t list.
315 static void add_to_list(config_module_t **head, module_instance_t *instance)
317 config_module_t *node = *head;
318 config_module_t **last = head;
325 node = (config_module_t *) malloc(sizeof(config_module_t));
327 fprintf(stderr, "Out of memory\n");
332 node->instance = instance;
337 /* Why is this both here and in radiusd.c:server_config? --Pac. */
338 static CONF_PARSER module_config[] = {
339 { "libdir", PW_TYPE_STRING_PTR, &radlib_dir, LIBDIR },
341 { NULL, -1, NULL, NULL }
345 * Parse the module config sections, and load
346 * and call each module's init() function.
348 * Libtool makes your life a LOT easier, especially with libltdl.
349 * see: http://www.gnu.org/software/libtool/
351 int setup_modules(void)
353 module_instance_t *this;
358 const char *filename="radiusd.conf";
360 this = NULL; /* Shut up stupid gcc */
363 * And parse the modules configuration values.
365 cs = cf_section_find(NULL);
367 cf_section_parse(cs, module_config);
371 * No current list of modules: Go initialize libltdl.
374 if (lt_dlinit() != 0) {
375 fprintf(stderr, "Failed to initialize libraries: %s\n",
381 * Set the default list of preloaded symbols.
382 * This is used to initialize libltdl's list of
385 * i.e. Static modules.
387 LTDL_SET_PRELOADED_SYMBOLS();
390 * Set the search path to ONLY our library directory.
391 * This prevents the modules fromn being found from
392 * any location on the disk.
394 lt_dlsetsearchpath(radlib_dir);
397 * Add 'raddb' directory, so that testing the server
398 * in the source directory can work.
400 lt_dladdsearchdir(radius_dir);
402 DEBUG2("modules: Library search path is %s",
403 lt_dlgetsearchpath());
409 for (comp=0; comp<RLM_COMPONENT_COUNT; ++comp) {
411 case RLM_COMPONENT_AUTZ: control="authorize"; break;
412 case RLM_COMPONENT_AUTH: control="authenticate"; break;
413 case RLM_COMPONENT_PREACCT: control="preacct"; break;
414 case RLM_COMPONENT_ACCT: control="accounting"; break;
415 default: control="unknown";
418 cs = cf_section_find(control);
422 /* These need to be subsections instead of attr/value pairs if we're
423 * going to do failover config here. I will wait until I've figured
424 * out the shorthand empty-subsection syntax first though.
426 for(modref=cf_pair_find_next(cs, NULL, NULL)
428 modref=cf_pair_find_next(cs, modref, NULL)) {
431 * Yes, we're missing two indents here.
432 * It's yucky but otherwise it doesn't fit. That
433 * ofcourse means that this function should
436 this = find_module_instance(module_instance_list, cf_pair_attr(modref));
441 if (strcmp(control, "authorize") == 0) {
442 if (!this->entry->module->authorize) {
443 fprintf(stderr, "%s[%d] Module %s does not contain "
444 "an 'authorize' entry\n",
445 filename, modref->lineno,
446 this->entry->module->name);
450 add_to_list(&authorize, this);
451 } else if (strcmp(control, "authenticate") == 0) {
452 if (!this->entry->module->authenticate) {
453 fprintf(stderr, "%s[%d] Module %s does not contain "
454 "an 'authenticate' entry\n",
455 filename, modref->lineno,
456 this->entry->module->name);
460 add_to_list(&authenticate, this);
461 } else if (strcmp(control, "preacct") == 0) {
462 if (!this->entry->module->preaccounting) {
463 fprintf(stderr, "%s[%d] Module %s does not contain "
464 "a 'preacct' entry\n",
465 filename, modref->lineno,
466 this->entry->module->name);
469 add_to_list(&preacct, this);
470 } else if (strcmp(control, "accounting") == 0) {
471 if (!this->entry->module->accounting) {
472 fprintf(stderr, "%s[%d] Module %s does not contain "
473 "an 'accounting' entry\n",
474 filename, modref->lineno,
475 this->entry->module->name);
479 add_to_list(&accounting, this);
481 fprintf(stderr, "%s[%d] Unknown control \"%s\".\n",
482 filename, modref->lineno, control);
494 * Update the Stripped-User-Name attribute.
496 static void update_username(REQUEST *request, char *newname)
501 * If there isn't a Stripped-User-Name attribute,
502 * go add one, and make it the definitive user name.
504 if (request->username->attribute != PW_STRIPPED_USER_NAME) {
505 vp = paircreate(PW_STRIPPED_USER_NAME, PW_TYPE_STRING);
507 log(L_ERR|L_CONS, "no memory");
510 DEBUG2(" authorize: Creating Stripped-User-Name of %s", newname);
511 strcpy(vp->strvalue, newname);
512 vp->length = strlen((char *)vp->strvalue);
513 pairadd(&request->packet->vps, vp);
514 request->username = vp;
519 * There is one, update it in place.
521 vp = request->username;
522 DEBUG2(" authorize: Updating Stripped-User-Name from %s to %s",
523 vp->strvalue, newname);
524 strcpy(vp->strvalue, newname);
525 vp->length = strlen((char *)vp->strvalue);
529 * Call all authorization modules until one returns
530 * somethings else than RLM_MODULE_OK
532 int module_authorize(REQUEST *request,
533 VALUE_PAIR **check_items, VALUE_PAIR **reply_items)
535 config_module_t *this;
536 int rcode = RLM_MODULE_OK;
539 rcode = RLM_MODULE_OK;
541 while (this && rcode == RLM_MODULE_OK) {
542 DEBUG2(" authorize: %s", this->instance->entry->module->name);
543 rcode = (this->instance->entry->module->authorize)(
544 this->instance->insthandle, request, check_items,
550 * Before authenticating the user, update the
551 * Stripped-User-Name attribute with any additions.
553 * No name: nothing to add.
555 if (request->username != NULL) {
560 * Try to add a prefix
562 for (vp = request->config_items; vp != NULL; vp = vp->next) {
563 switch (vp->attribute) {
568 if ((size_t)(vp->length + request->username->length) > sizeof(vp->strvalue)) {
569 DEBUG2("\"%s\"+\"%s\" too long",
571 request->username->strvalue);
574 strcpy(newname, vp->strvalue);
575 strcat(newname, (char *)request->username->strvalue);
576 update_username(request, newname);
580 if ((size_t)(vp->length + request->username->length) > sizeof(vp->strvalue)) {
581 DEBUG2("\"%s\"+\"%s\" too long",
582 request->username->strvalue,
586 strcpy(newname, request->username->strvalue);
587 strcat(newname, (char *)vp->strvalue);
588 update_username(request, newname);
591 } /* over all configuration items */
593 pairdelete(&request->config_items, PW_ADD_PREFIX);
594 pairdelete(&request->config_items, PW_ADD_SUFFIX);
601 * Authenticate a user/password with various methods.
603 int module_authenticate(int auth_type, REQUEST *request)
605 config_module_t *this;
608 * We MUST have a password, of SOME type!
610 if (request->password == NULL) {
611 return RLM_MODULE_FAIL;
615 while (this && this->instance->entry->auth_type != auth_type)
618 if (!this || !this->instance->entry->module->authenticate) {
620 * No such auth_type, or module auth_type not defined
622 return RLM_MODULE_FAIL;
625 DEBUG2(" authenticate: %s", this->instance->entry->module->name);
626 return (this->instance->entry->module->authenticate)(
627 this->instance->insthandle, request);
632 * Do pre-accounting for ALL configured sessions
634 int module_preacct(REQUEST *request)
636 config_module_t *this;
640 rcode = RLM_MODULE_OK;
642 while (this && (rcode == RLM_MODULE_OK)) {
643 DEBUG2(" preacct: %s", this->instance->entry->module->name);
644 rcode = (this->instance->entry->module->preaccounting)
645 (this->instance->insthandle, request);
653 * Do accounting for ALL configured sessions
655 int module_accounting(REQUEST *request)
657 config_module_t *this;
661 rcode = RLM_MODULE_OK;
663 while (this && (rcode == RLM_MODULE_OK)) {
664 DEBUG2(" accounting: %s", this->instance->entry->module->name);
665 rcode = (this->instance->entry->module->accounting)
666 (this->instance->insthandle, request);
674 * Module malloc() call, which does stuff if the malloc fails.
676 * This call ALWAYS succeeds!
678 void *rlm_malloc(size_t size)
680 void *ptr = malloc(size);
683 log(L_ERR|L_CONS, "no memory");