/src/freeradius-server/src/lib/server/module_rlm.c
Line | Count | Source |
1 | | /* |
2 | | * This program is free software; you can redistribute it and/or modify |
3 | | * it under the terms of the GNU General Public License as published by |
4 | | * the Free Software Foundation; either version 2 of the License, or |
5 | | * (at your option) any later version. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** |
18 | | * $Id: e448fdac7ab12ff57480adcf5f0d83b44c68eb63 $ |
19 | | * |
20 | | * @file src/lib/server/module_rlm.c |
21 | | * @brief Defines functions for rlm module (re-)initialisation. |
22 | | * |
23 | | * @copyright 2003,2006,2016 The FreeRADIUS server project |
24 | | * @copyright 2016,2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org) |
25 | | * @copyright 2000 Alan DeKok (aland@freeradius.org) |
26 | | * @copyright 2000 Alan Curry (pacman@world.std.com) |
27 | | */ |
28 | | |
29 | | RCSID("$Id: e448fdac7ab12ff57480adcf5f0d83b44c68eb63 $") |
30 | | |
31 | | #include <freeradius-devel/server/cf_file.h> |
32 | | |
33 | | #include <freeradius-devel/server/global_lib.h> |
34 | | #include <freeradius-devel/server/modpriv.h> |
35 | | #include <freeradius-devel/server/module_rlm.h> |
36 | | #include <freeradius-devel/server/pair.h> |
37 | | |
38 | | #include <freeradius-devel/util/atexit.h> |
39 | | |
40 | | #include <freeradius-devel/unlang/compile.h> |
41 | | |
42 | | #include <freeradius-devel/unlang/xlat_func.h> |
43 | | #include <freeradius-devel/unlang/xlat_redundant.h> |
44 | | |
45 | | |
46 | | /** Lookup virtual module by name |
47 | | */ |
48 | | static fr_rb_tree_t *module_rlm_virtual_name_tree; |
49 | | |
50 | | typedef struct { |
51 | | fr_rb_node_t name_node; //!< Entry in the name tree. |
52 | | char const *name; //!< module name |
53 | | CONF_SECTION *cs; //!< CONF_SECTION where it is defined |
54 | | bool all_same; |
55 | | } module_rlm_virtual_t; |
56 | | |
57 | | /** Compare virtual modules by name |
58 | | */ |
59 | | static int8_t module_rlm_virtual_name_cmp(void const *one, void const *two) |
60 | 0 | { |
61 | 0 | module_rlm_virtual_t const *a = one; |
62 | 0 | module_rlm_virtual_t const *b = two; |
63 | 0 | int ret; |
64 | |
|
65 | 0 | ret = strcmp(a->name, b->name); |
66 | 0 | return CMP(ret, 0); |
67 | 0 | } |
68 | | |
69 | | /** Global module list for all backend modules |
70 | | * |
71 | | */ |
72 | | static module_list_t *rlm_modules_static; |
73 | | |
74 | | /** Runtime instantiated list |
75 | | * |
76 | | */ |
77 | | static module_list_t *rlm_modules_dynamic; |
78 | | |
79 | | /** Print information on all loaded modules |
80 | | * |
81 | | */ |
82 | | void module_rlm_list_debug(void) |
83 | 0 | { |
84 | 0 | module_list_debug(rlm_modules_static); |
85 | 0 | } |
86 | | |
87 | | /** Initialise a module specific exfile handle |
88 | | * |
89 | | * @see exfile_init |
90 | | * |
91 | | * @param[in] ctx to bind the lifetime of the exfile handle to. |
92 | | * @param[in] module section. |
93 | | * @param[in] max_entries Max file descriptors to cache, and manage locks for. |
94 | | * @param[in] max_idle Maximum time a file descriptor can be idle before it's closed. |
95 | | * @param[in] locking Whether or not to lock the files. |
96 | | * @param[in] triggers Should triggers be enabled. |
97 | | * @param[in] trigger_prefix if NULL will be set automatically from the module CONF_SECTION. |
98 | | * @param[in] trigger_args to make available in any triggers executed by the connection pool. |
99 | | * @return |
100 | | * - New connection pool. |
101 | | * - NULL on error. |
102 | | */ |
103 | | exfile_t *module_rlm_exfile_init(TALLOC_CTX *ctx, |
104 | | CONF_SECTION *module, |
105 | | uint32_t max_entries, |
106 | | fr_time_delta_t max_idle, |
107 | | bool locking, |
108 | | bool triggers, |
109 | | char const *trigger_prefix, |
110 | | fr_pair_list_t *trigger_args) |
111 | 0 | { |
112 | 0 | char trigger_prefix_buff[128]; |
113 | 0 | bool prefix_set = trigger_prefix ? true : false; |
114 | 0 | exfile_t *handle; |
115 | |
|
116 | 0 | if (!trigger_prefix) { |
117 | 0 | snprintf(trigger_prefix_buff, sizeof(trigger_prefix_buff), "modules.%s.file", cf_section_name1(module)); |
118 | 0 | trigger_prefix = trigger_prefix_buff; |
119 | 0 | } |
120 | |
|
121 | 0 | handle = exfile_init(ctx, max_entries, max_idle, locking); |
122 | 0 | if (!handle) return NULL; |
123 | | |
124 | 0 | if (triggers) exfile_enable_triggers(handle, prefix_set ? module : cf_section_find(module, "file", NULL), |
125 | 0 | trigger_prefix, trigger_args); |
126 | |
|
127 | 0 | return handle; |
128 | 0 | } |
129 | | |
130 | | /** Resolve polymorphic item's from a module's #CONF_SECTION to a subsection in another module |
131 | | * |
132 | | * This allows certain module sections to reference module sections in other instances |
133 | | * of the same module and share #CONF_DATA associated with them. |
134 | | * |
135 | | * @verbatim |
136 | | example { |
137 | | data { |
138 | | ... |
139 | | } |
140 | | } |
141 | | |
142 | | example inst { |
143 | | data = example |
144 | | } |
145 | | * @endverbatim |
146 | | * |
147 | | * @param[out] out where to write the pointer to a module's config section. May be NULL on success, |
148 | | * indicating the config item was not found within the module #CONF_SECTION |
149 | | * or the chain of module references was followed and the module at the end of the chain |
150 | | * did not a subsection. |
151 | | * @param[in] module #CONF_SECTION. |
152 | | * @param[in] name of the polymorphic sub-section. |
153 | | * @return |
154 | | * - 0 on success with referenced section. |
155 | | * - 1 on success with local section. |
156 | | * - -1 on failure. |
157 | | */ |
158 | | int module_rlm_sibling_section_find(CONF_SECTION **out, CONF_SECTION *module, char const *name) |
159 | 0 | { |
160 | 0 | CONF_PAIR *cp; |
161 | 0 | CONF_SECTION *cs; |
162 | 0 | CONF_DATA const *cd; |
163 | | |
164 | |
|
165 | 0 | module_instance_t *mi; |
166 | 0 | char const *inst_name; |
167 | |
|
168 | 0 | #define FIND_SIBLING_CF_KEY "find_sibling" |
169 | |
|
170 | 0 | *out = NULL; |
171 | | |
172 | | /* |
173 | | * Is a real section (not referencing sibling module). |
174 | | */ |
175 | 0 | cs = cf_section_find(module, name, NULL); |
176 | 0 | if (cs) { |
177 | 0 | *out = cs; |
178 | 0 | return 0; |
179 | 0 | } |
180 | | |
181 | | /* |
182 | | * Item omitted completely from module config. |
183 | | */ |
184 | 0 | cp = cf_pair_find(module, name); |
185 | 0 | if (!cp) return 0; |
186 | | |
187 | 0 | if (cf_data_find(module, CONF_SECTION, FIND_SIBLING_CF_KEY)) { |
188 | 0 | cf_log_err(cp, "Module reference loop found"); |
189 | 0 | return -1; |
190 | 0 | } |
191 | 0 | cd = cf_data_add(module, module, FIND_SIBLING_CF_KEY, false); |
192 | | |
193 | | /* |
194 | | * Item found, resolve it to a module instance. |
195 | | * This triggers module loading, so we don't have |
196 | | * instantiation order issues. |
197 | | */ |
198 | 0 | inst_name = cf_pair_value(cp); |
199 | 0 | mi = module_instance_by_name(rlm_modules_static, NULL, inst_name); |
200 | 0 | if (!mi) { |
201 | 0 | cf_log_err(cp, "Unknown module instance \"%s\"", inst_name); |
202 | 0 | error: |
203 | 0 | cf_data_remove_by_data(module, cd); |
204 | 0 | return -1; |
205 | 0 | } |
206 | | |
207 | 0 | if ((mi->state != MODULE_INSTANCE_INSTANTIATED) && |
208 | 0 | (module_instantiate(module_instance_by_name(rlm_modules_static, NULL, inst_name)) < 0)) { |
209 | 0 | goto error; |
210 | 0 | } |
211 | | |
212 | | /* |
213 | | * Remove the config data we added for loop |
214 | | * detection. |
215 | | */ |
216 | 0 | cf_data_remove_by_data(module, cd); |
217 | | |
218 | | /* |
219 | | * Check the module instances are of the same type. |
220 | | */ |
221 | 0 | if (strcmp(cf_section_name1(mi->conf), cf_section_name1(module)) != 0) { |
222 | 0 | cf_log_err(cp, "Referenced module is a rlm_%s instance, must be a rlm_%s instance", |
223 | 0 | cf_section_name1(mi->conf), cf_section_name1(module)); |
224 | 0 | return -1; |
225 | 0 | } |
226 | | |
227 | 0 | *out = cf_section_find(mi->conf, name, NULL); |
228 | |
|
229 | 0 | return 1; |
230 | 0 | } |
231 | | |
232 | | xlat_t *module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, |
233 | | char const *name, xlat_func_t func, fr_type_t return_type) |
234 | | { |
235 | | module_instance_t *mi = mctx->mi; |
236 | | module_rlm_instance_t *mri = talloc_get_type_abort(mi->uctx, module_rlm_instance_t); |
237 | | module_rlm_xlat_t *mrx; |
238 | | xlat_t *x; |
239 | | char inst_name[256]; |
240 | | |
241 | | fr_assert_msg(name != mctx->mi->name, "`name` must not be the same as the module " |
242 | | "instance name. Pass a NULL `name` arg if this is required"); |
243 | | |
244 | | if (!name) { |
245 | | name = mctx->mi->name; |
246 | | } else { |
247 | | if ((size_t)snprintf(inst_name, sizeof(inst_name), "%s.%s", mctx->mi->name, name) >= sizeof(inst_name)) { |
248 | | ERROR("%s: Instance name too long", __FUNCTION__); |
249 | | return NULL; |
250 | | } |
251 | | name = inst_name; |
252 | | } |
253 | | |
254 | | x = xlat_func_register(ctx, name, func, return_type); |
255 | | if (unlikely(x == NULL)) return NULL; |
256 | | |
257 | | xlat_mctx_set(x, mctx); |
258 | | |
259 | | MEM(mrx = talloc(mi, module_rlm_xlat_t)); |
260 | | mrx->xlat = x; |
261 | | mrx->mi = mi; |
262 | | |
263 | | fr_dlist_insert_tail(&mri->xlats, mrx); |
264 | | |
265 | | return x; |
266 | | } |
267 | | |
268 | | /** Initialise a module specific connection pool |
269 | | * |
270 | | * @see fr_pool_init |
271 | | * |
272 | | * @param[in] module section. |
273 | | * @param[in] opaque data pointer to pass to callbacks. |
274 | | * @param[in] c Callback to create new connections. |
275 | | * @param[in] a Callback to check the status of connections. |
276 | | * @param[in] log_prefix override, if NULL will be set automatically from the module CONF_SECTION. |
277 | | * @param[in] trigger_prefix if NULL will be set automatically from the module CONF_SECTION. |
278 | | * @param[in] trigger_args to make available in any triggers executed by the connection pool. |
279 | | * @return |
280 | | * - New connection pool. |
281 | | * - NULL on error. |
282 | | */ |
283 | | fr_pool_t *module_rlm_connection_pool_init(CONF_SECTION *module, |
284 | | void *opaque, |
285 | | fr_pool_connection_create_t c, |
286 | | fr_pool_connection_alive_t a, |
287 | | char const *log_prefix, |
288 | | char const *trigger_prefix, |
289 | | fr_pair_list_t *trigger_args) |
290 | 0 | { |
291 | 0 | CONF_SECTION *cs, *mycs; |
292 | 0 | char log_prefix_buff[128]; |
293 | 0 | char trigger_prefix_buff[128]; |
294 | |
|
295 | 0 | fr_pool_t *pool; |
296 | 0 | char const *mod_name1, *mod_name2; |
297 | |
|
298 | 0 | int ret; |
299 | |
|
300 | 0 | #define parent_name(_x) cf_section_name(cf_item_to_section(cf_parent(_x))) |
301 | |
|
302 | 0 | mod_name1 = cf_section_name1(module); |
303 | 0 | mod_name2 = cf_section_name2(module); |
304 | 0 | if (!mod_name2) mod_name2 = mod_name1; |
305 | |
|
306 | 0 | if (!trigger_prefix) { |
307 | 0 | snprintf(trigger_prefix_buff, sizeof(trigger_prefix_buff), "modules.%s.pool", mod_name1); |
308 | 0 | trigger_prefix = trigger_prefix_buff; |
309 | 0 | } |
310 | |
|
311 | 0 | if (!log_prefix) { |
312 | 0 | snprintf(log_prefix_buff, sizeof(log_prefix_buff), "rlm_%s (%s)", mod_name1, mod_name2); |
313 | 0 | log_prefix = log_prefix_buff; |
314 | 0 | } |
315 | | |
316 | | /* |
317 | | * Get sibling's pool config section |
318 | | */ |
319 | 0 | ret = module_rlm_sibling_section_find(&cs, module, "pool"); |
320 | 0 | switch (ret) { |
321 | 0 | case -1: |
322 | 0 | return NULL; |
323 | | |
324 | 0 | case 1: |
325 | 0 | DEBUG4("%s: Using pool section from \"%s\"", log_prefix, parent_name(cs)); |
326 | 0 | break; |
327 | | |
328 | 0 | case 0: |
329 | 0 | DEBUG4("%s: Using local pool section", log_prefix); |
330 | 0 | break; |
331 | 0 | } |
332 | | |
333 | | /* |
334 | | * Get our pool config section |
335 | | */ |
336 | 0 | mycs = cf_section_find(module, "pool", NULL); |
337 | 0 | if (!mycs) { |
338 | 0 | DEBUG4("%s: Adding pool section to module configuration \"%s\" to store pool references", log_prefix, |
339 | 0 | mod_name2); |
340 | |
|
341 | 0 | mycs = cf_section_alloc(module, module, "pool", NULL); |
342 | 0 | if (!mycs) return NULL; |
343 | 0 | } |
344 | | |
345 | | /* |
346 | | * Sibling didn't have a pool config section |
347 | | * Use our own local pool. |
348 | | */ |
349 | 0 | if (!cs) { |
350 | 0 | DEBUG4("%s: \"%s.pool\" section not found, using \"%s.pool\"", log_prefix, |
351 | 0 | mod_name1, parent_name(mycs)); |
352 | 0 | cs = mycs; |
353 | 0 | } |
354 | | |
355 | | /* |
356 | | * If fr_pool_init has already been called |
357 | | * for this config section, reuse the previous instance. |
358 | | * |
359 | | * This allows modules to pass in the config sections |
360 | | * they would like to use the connection pool from. |
361 | | */ |
362 | 0 | pool = cf_data_value(cf_data_find(cs, fr_pool_t, NULL)); |
363 | 0 | if (!pool) { |
364 | 0 | DEBUG4("%s: No pool reference found for config item \"%s.pool\"", log_prefix, parent_name(cs)); |
365 | 0 | pool = fr_pool_init(cs, cs, opaque, c, a, log_prefix); |
366 | 0 | if (!pool) return NULL; |
367 | | |
368 | 0 | fr_pool_enable_triggers(pool, trigger_prefix, trigger_args); |
369 | |
|
370 | 0 | if (fr_pool_start(pool) < 0) { |
371 | 0 | ERROR("%s: Starting initial connections failed", log_prefix); |
372 | 0 | return NULL; |
373 | 0 | } |
374 | | |
375 | 0 | DEBUG4("%s: Adding pool reference %p to config item \"%s.pool\"", log_prefix, pool, parent_name(cs)); |
376 | 0 | cf_data_add(cs, pool, NULL, false); |
377 | 0 | return pool; |
378 | 0 | } |
379 | 0 | fr_pool_ref(pool); |
380 | |
|
381 | 0 | DEBUG4("%s: Found pool reference %p in config item \"%s.pool\"", log_prefix, pool, parent_name(cs)); |
382 | | |
383 | | /* |
384 | | * We're reusing pool data add it to our local config |
385 | | * section. This allows other modules to transitively |
386 | | * reuse a pool through this module. |
387 | | */ |
388 | 0 | if (mycs != cs) { |
389 | 0 | DEBUG4("%s: Copying pool reference %p from config item \"%s.pool\" to config item \"%s.pool\"", |
390 | 0 | log_prefix, pool, parent_name(cs), parent_name(mycs)); |
391 | 0 | cf_data_add(mycs, pool, NULL, false); |
392 | 0 | } |
393 | |
|
394 | 0 | return pool; |
395 | 0 | } |
396 | | |
397 | | /** Set the next section type if it's not already set |
398 | | * |
399 | | * @param[in] request The current request. |
400 | | * @param[in] type_da to use. Usually attr_auth_type. |
401 | | * @param[in] enumv Enumeration value of the specified type_da. |
402 | | */ |
403 | | bool module_rlm_section_type_set(request_t *request, fr_dict_attr_t const *type_da, fr_dict_enum_value_t const *enumv) |
404 | 0 | { |
405 | 0 | fr_pair_t *vp; |
406 | |
|
407 | 0 | switch (pair_update_control(&vp, type_da)) { |
408 | 0 | case 0: |
409 | 0 | if (unlikely(fr_value_box_copy(vp, &vp->data, enumv->value) < 0)) { |
410 | 0 | fr_strerror_printf("Failed to set control.%pP to %s", vp, enumv->name); |
411 | 0 | return false; |
412 | 0 | } |
413 | 0 | vp->data.enumv = vp->da; /* So we get the correct string alias */ |
414 | 0 | RDEBUG2("Setting control.%pP", vp); |
415 | 0 | return true; |
416 | | |
417 | 0 | case 1: |
418 | 0 | RDEBUG2("control.%s already set. Not setting to %s", vp->da->name, enumv->name); |
419 | 0 | return false; |
420 | | |
421 | 0 | default: |
422 | 0 | return false; |
423 | 0 | } |
424 | 0 | } |
425 | | |
426 | | /** Iterate over an array of named module methods, looking for matches |
427 | | * |
428 | | * @param[in] mmg A structure containing a terminated array of |
429 | | * module method bindings. pre-sorted using #section_name_cmp |
430 | | * with name2 sublists populated. |
431 | | * @param[in] section name1 of the method being called can be one of the following: |
432 | | * - An itenfier. |
433 | | * - CF_IDENT_ANY if the method is a wildcard. |
434 | | * name2 of the method being called can be one of the following: |
435 | | * - An itenfier. |
436 | | * - NULL to match section names with only a name1. |
437 | | * - CF_IDENT_ANY if the method is a wildcard. |
438 | | * @return |
439 | | * - The module_method_name_t on success. |
440 | | * - NULL on not found. |
441 | | */ |
442 | | static CC_HINT(nonnull) |
443 | | module_method_binding_t const *module_binding_find(module_method_group_t const *mmg, section_name_t const *section) |
444 | 0 | { |
445 | 0 | module_method_group_t const *mmg_p = mmg; |
446 | 0 | module_method_binding_t const *p; |
447 | |
|
448 | 0 | while (mmg_p) { |
449 | | /* |
450 | | * This could potentially be improved by using a binary search |
451 | | * but given the small number of items, reduced branches and |
452 | | * sequential access just scanning the list, it's probably not |
453 | | * worth it. |
454 | | */ |
455 | 0 | for (p = mmg_p->bindings; p->section; p++) { |
456 | 0 | switch (section_name_match(p->section, section)) { |
457 | 0 | case 1: /* match */ |
458 | 0 | return p; |
459 | | |
460 | 0 | case -1: /* name1 didn't match, skip to the end of the sub-list */ |
461 | 0 | p = fr_dlist_tail(&p->same_name1); |
462 | 0 | break; |
463 | | |
464 | 0 | case 0: /* name1 did match - see if we can find a matching name2 */ |
465 | 0 | { |
466 | 0 | fr_dlist_head_t const *same_name1 = &p->same_name1; |
467 | |
|
468 | 0 | while ((p = fr_dlist_next(same_name1, p))) { |
469 | 0 | if (section_name2_match(p->section, section)) return p; |
470 | 0 | } |
471 | 0 | p = fr_dlist_tail(same_name1); |
472 | 0 | } |
473 | 0 | break; |
474 | 0 | } |
475 | | #ifdef __clang_analyzer__ |
476 | | /* Will never be NULL, worse case, p doesn't change*/ |
477 | | if (!p) break; |
478 | | #endif |
479 | 0 | } |
480 | | |
481 | | /* |
482 | | * Failed to match, search the next deepest group in the chain. |
483 | | */ |
484 | 0 | mmg_p = mmg_p->next; |
485 | 0 | } |
486 | | |
487 | 0 | return NULL; |
488 | 0 | } |
489 | | |
490 | | /** Dump the available bindings for the module into the strerror stack |
491 | | * |
492 | | * @note Methods from _all_ linked module method groups will be pushed onto the error stack. |
493 | | * |
494 | | * @param[in] mmg module method group to evaluate. |
495 | | */ |
496 | | static void module_rlm_methods_to_strerror(module_method_group_t const *mmg) |
497 | 0 | { |
498 | 0 | module_method_group_t const *mmg_p = mmg; |
499 | 0 | module_method_binding_t const *mmb_p; |
500 | 0 | bool first = true; |
501 | |
|
502 | 0 | while (mmg_p) { |
503 | 0 | mmb_p = mmg_p->bindings; |
504 | |
|
505 | 0 | if (!mmb_p || !mmb_p[0].section) goto next; |
506 | | |
507 | 0 | if (first) { |
508 | 0 | fr_strerror_const_push("Available methods are:"); |
509 | 0 | first = false; |
510 | 0 | } |
511 | |
|
512 | 0 | for (; mmb_p->section; mmb_p++) { |
513 | 0 | char const *name1 = section_name_str(mmb_p->section->name1); |
514 | 0 | char const *name2 = section_name_str(mmb_p->section->name2); |
515 | |
|
516 | 0 | fr_strerror_printf_push(" %s%s%s", |
517 | 0 | name1, name2 ? "." : "", name2 ? name2 : ""); |
518 | 0 | } |
519 | 0 | next: |
520 | 0 | mmg_p = mmg_p->next; |
521 | 0 | } |
522 | | |
523 | 0 | if (first) { |
524 | 0 | fr_strerror_const_push("No methods available"); |
525 | 0 | } |
526 | 0 | } |
527 | | |
528 | | /** Find an existing module instance and verify it implements the specified method |
529 | | * |
530 | | * Extracts the method from the module name where the format is @verbatim <module>[.<method1>[.<method2>]] @endverbatim |
531 | | * and ensures the module implements the specified method. |
532 | | * |
533 | | * @param[in] ctx to allocate the dynamic module key tmpl from. |
534 | | * @param[out] mmc_out the result from resolving the module method, |
535 | | * plus the key tmpl for dynamic modules. |
536 | | * This is not allocated from the ctx to save the runtime |
537 | | * dereference. |
538 | | * @param[in] vs Virtual server to search for alternative module names in. |
539 | | * @param[in] section Section name containing the module call. |
540 | | * @param[in] name The module method call i.e. module[<key>][.<method>] |
541 | | * @param[in] t_rules for resolving the dynamic module key. |
542 | | * @return |
543 | | * - The module instance on success. |
544 | | * - NULL on not found |
545 | | * |
546 | | * If the module exists but the method doesn't exist, then `method` is set to NULL. |
547 | | */ |
548 | | fr_slen_t module_rlm_by_name_and_method(TALLOC_CTX *ctx, module_method_call_t *mmc_out, |
549 | | virtual_server_t const *vs, section_name_t const *section, fr_sbuff_t *name, |
550 | | tmpl_rules_t const *t_rules) |
551 | 0 | { |
552 | 0 | fr_sbuff_term_t const *dyn_tt = &FR_SBUFF_TERMS( |
553 | 0 | L(""), |
554 | 0 | L("\t"), |
555 | 0 | L("\n"), |
556 | 0 | L(" "), |
557 | 0 | L("[") |
558 | 0 | ); |
559 | |
|
560 | 0 | fr_sbuff_term_t const *elem_tt = &FR_SBUFF_TERMS( |
561 | 0 | L(""), |
562 | 0 | L("\t"), |
563 | 0 | L("\n"), |
564 | 0 | L(" "), |
565 | 0 | L(".") |
566 | 0 | ); |
567 | |
|
568 | 0 | fr_sbuff_t *elem1; |
569 | 0 | module_method_call_t *mmc; |
570 | 0 | module_method_call_t mmc_tmp; |
571 | 0 | module_method_binding_t const *mmb; |
572 | |
|
573 | 0 | fr_sbuff_marker_t meth_start; |
574 | 0 | bool softfail; |
575 | |
|
576 | 0 | fr_slen_t slen; |
577 | 0 | fr_sbuff_t our_name = FR_SBUFF(name); |
578 | |
|
579 | 0 | mmc = mmc_out ? mmc_out : &mmc_tmp; |
580 | 0 | if (mmc_out) *mmc_out = (module_method_call_t) {}; |
581 | |
|
582 | 0 | softfail = fr_sbuff_next_if_char(&our_name, '-'); |
583 | | |
584 | | /* |
585 | | * Advance until the start of the dynamic selector |
586 | | * (if it exists). |
587 | | */ |
588 | 0 | if (fr_sbuff_adv_until(&our_name, SIZE_MAX, dyn_tt, '\0') == 0) { |
589 | 0 | fr_strerror_printf("Invalid module method name"); |
590 | 0 | return fr_sbuff_error(&our_name); |
591 | 0 | } |
592 | | |
593 | 0 | FR_SBUFF_TALLOC_THREAD_LOCAL(&elem1, MODULE_INSTANCE_LEN_MAX, (MODULE_INSTANCE_LEN_MAX + 1) * 10); |
594 | | |
595 | | /* |
596 | | * If the method string contains a '[' |
597 | | * |
598 | | * Search for a dynamic module method, e.g. `elem1[<key>]`. |
599 | | */ |
600 | 0 | if (fr_sbuff_is_char(&our_name, '[')) { |
601 | 0 | fr_sbuff_marker_t end, s_end; |
602 | 0 | fr_sbuff_marker(&end, &our_name); |
603 | |
|
604 | 0 | slen = tmpl_afrom_substr(ctx, &mmc->key, &our_name, T_BARE_WORD, NULL, t_rules); |
605 | 0 | if (slen < 0) { |
606 | 0 | fr_strerror_const_push("Invalid dynamic module selector expression"); |
607 | 0 | return slen; |
608 | 0 | } |
609 | | |
610 | 0 | if (!fr_sbuff_is_char(&our_name, ']')) { |
611 | 0 | fr_strerror_const_push("Missing terminating ']' for dynamic module selector"); |
612 | 0 | error: |
613 | 0 | return fr_sbuff_error(&our_name); |
614 | 0 | } |
615 | 0 | fr_sbuff_marker(&s_end, &our_name); |
616 | |
|
617 | 0 | fr_sbuff_set_to_start(&our_name); |
618 | 0 | slen = fr_sbuff_out_bstrncpy(elem1, &our_name, fr_sbuff_ahead(&end)); |
619 | 0 | if (slen < 0) { |
620 | 0 | fr_strerror_const("Module method string too long"); |
621 | 0 | goto error; |
622 | 0 | } |
623 | 0 | mmc->mi = module_instance_by_name(rlm_modules_dynamic, NULL, elem1->start); |
624 | 0 | if (!mmc->mi) { |
625 | 0 | fr_strerror_printf("No such dynamic module '%s'", elem1->start); |
626 | 0 | goto error; |
627 | 0 | } |
628 | 0 | mmc->rlm = module_rlm_from_module(mmc->mi->exported); |
629 | |
|
630 | 0 | fr_sbuff_set(&our_name, &s_end); |
631 | 0 | fr_sbuff_advance(&our_name, 1); /* Skip the ']' */ |
632 | | /* |
633 | | * With elem1.elem2.elem3 |
634 | | * |
635 | | * Search for a static module matching one of the following: |
636 | | * |
637 | | * - elem1.elem2.elem3 |
638 | | * - elem1.elem2 |
639 | | * - elem1 |
640 | | */ |
641 | 0 | } else { |
642 | 0 | char *p; |
643 | |
|
644 | 0 | fr_sbuff_set_to_start(&our_name); |
645 | |
|
646 | 0 | slen = fr_sbuff_out_bstrncpy_until(elem1, &our_name, SIZE_MAX, dyn_tt, NULL); |
647 | 0 | if (slen == 0) { |
648 | 0 | fr_strerror_const("Invalid module name"); |
649 | 0 | goto error; |
650 | 0 | } |
651 | 0 | if (slen < 0) { |
652 | 0 | fr_strerror_const("Module method string too long"); |
653 | 0 | goto error; |
654 | 0 | } |
655 | | |
656 | | /* |
657 | | * Now we have a mutable buffer, we can start chopping |
658 | | * it up to find the module. |
659 | | */ |
660 | 0 | for (;;) { |
661 | 0 | mmc->mi = (module_instance_t *)module_rlm_static_by_name(NULL, elem1->start); |
662 | 0 | if (mmc->mi) { |
663 | 0 | mmc->rlm = module_rlm_from_module(mmc->mi->exported); |
664 | 0 | break; /* Done */ |
665 | 0 | } |
666 | | |
667 | 0 | p = strrchr(elem1->start, '.'); |
668 | 0 | if (!p) break; /* No more '.' */ |
669 | 0 | *p = '\0'; /* Chop off the last '.' */ |
670 | 0 | } |
671 | |
|
672 | 0 | if (!mmc->mi) { |
673 | 0 | if (softfail) return fr_sbuff_set(name, &our_name); |
674 | | |
675 | 0 | fr_strerror_printf("No such module '%pV'", fr_box_strvalue_len(our_name.start, slen)); |
676 | 0 | return -1; |
677 | 0 | } |
678 | | |
679 | 0 | fr_sbuff_set_to_start(&our_name); |
680 | 0 | fr_sbuff_advance(&our_name, strlen(elem1->start)); /* Advance past the module name */ |
681 | 0 | if (fr_sbuff_is_char(&our_name, '.')) { |
682 | 0 | fr_sbuff_advance(&our_name, 1); /* Static module method, search directly */ |
683 | 0 | } else { |
684 | 0 | fr_sbuff_marker(&meth_start, &our_name); /* for the errors... */ |
685 | 0 | goto by_section; /* Get the method dynamically from the section*/ |
686 | 0 | } |
687 | 0 | } |
688 | | |
689 | | /* |
690 | | * For both cases, the buffer should be pointing |
691 | | * at the start of the method string. |
692 | | */ |
693 | 0 | fr_sbuff_marker(&meth_start, &our_name); |
694 | | |
695 | | /* |
696 | | * If a module method was provided, search for it in the named |
697 | | * methods provided by the module. |
698 | | * |
699 | | * The method name should be either: |
700 | | * |
701 | | * - name1 |
702 | | * - name1.name2 |
703 | | */ |
704 | 0 | { |
705 | 0 | section_name_t method; |
706 | 0 | fr_sbuff_t *elem2; |
707 | |
|
708 | 0 | fr_sbuff_set_to_start(elem1); /* May have used this already for module lookups */ |
709 | |
|
710 | 0 | slen = fr_sbuff_out_bstrncpy_until(elem1, &our_name, SIZE_MAX, elem_tt, NULL); |
711 | 0 | if (slen < 0) { |
712 | 0 | fr_strerror_const("Module method string too long"); |
713 | 0 | return fr_sbuff_error(&our_name); |
714 | 0 | } |
715 | 0 | if (slen == 0) goto by_section; /* This works for both dynamic and static modules */ |
716 | | |
717 | 0 | FR_SBUFF_TALLOC_THREAD_LOCAL(&elem2, MODULE_INSTANCE_LEN_MAX, MODULE_INSTANCE_LEN_MAX); |
718 | |
|
719 | 0 | if (fr_sbuff_is_char(&our_name, '.')) { |
720 | 0 | fr_sbuff_advance(&our_name, 1); |
721 | 0 | if (fr_sbuff_out_bstrncpy_until(elem2, &our_name, SIZE_MAX, |
722 | 0 | elem_tt, NULL) == MODULE_INSTANCE_LEN_MAX) { |
723 | 0 | fr_strerror_const("Module method string too long"); |
724 | 0 | goto error; |
725 | 0 | } |
726 | 0 | } |
727 | | |
728 | 0 | method = (section_name_t) { |
729 | 0 | .name1 = elem1->start, |
730 | 0 | .name2 = fr_sbuff_used(elem2) ? elem2->start : NULL |
731 | 0 | }; |
732 | |
|
733 | 0 | mmb = module_binding_find(&mmc->rlm->method_group, &method); |
734 | 0 | if (!mmb) { |
735 | 0 | fr_strerror_printf("Module \"%s\" does not have method %s%s%s", |
736 | 0 | mmc->mi->name, |
737 | 0 | method.name1, |
738 | 0 | method.name2 ? "." : "", |
739 | 0 | method.name2 ? method.name2 : "" |
740 | 0 | ); |
741 | |
|
742 | 0 | module_rlm_methods_to_strerror(&mmc->rlm->method_group); |
743 | 0 | return fr_sbuff_error(&meth_start); |
744 | 0 | } |
745 | 0 | mmc->mmb = *mmb; /* For locality of reference and fewer derefs */ |
746 | 0 | if (mmc_out) section_name_dup(ctx, &mmc->asked, &method); |
747 | |
|
748 | 0 | return fr_sbuff_set(name, &our_name); |
749 | 0 | } |
750 | | |
751 | 0 | by_section: |
752 | | /* |
753 | | * First look for the section name in the module's |
754 | | * bindings. If that fails, look for the alt |
755 | | * section names from the virtual server section. |
756 | | * |
757 | | * If that fails, we're done. |
758 | | */ |
759 | 0 | mmb = module_binding_find(&mmc->rlm->method_group, section); |
760 | 0 | if (!mmb) { |
761 | 0 | section_name_t const **alt_p = virtual_server_section_methods(vs, section); |
762 | 0 | if (alt_p) { |
763 | 0 | for (; *alt_p; alt_p++) { |
764 | 0 | mmb = module_binding_find(&mmc->rlm->method_group, *alt_p); |
765 | 0 | if (mmb) { |
766 | 0 | if (mmc_out) section_name_dup(ctx, &mmc->asked, *alt_p); |
767 | 0 | break; |
768 | 0 | } |
769 | 0 | } |
770 | 0 | } |
771 | 0 | } else { |
772 | 0 | if (mmc_out) section_name_dup(ctx, &mmc->asked, section); |
773 | 0 | } |
774 | 0 | if (!mmb) { |
775 | 0 | fr_strerror_printf("Module \"%s\" has no method for section %s %s { ... }, i.e. %s%s%s", |
776 | 0 | mmc->mi->name, |
777 | 0 | section->name1, |
778 | 0 | section->name2 ? section->name2 : "", |
779 | 0 | section->name1, |
780 | 0 | section->name2 ? "." : "", |
781 | 0 | section->name2 ? section->name2 : "" |
782 | 0 | ); |
783 | 0 | module_rlm_methods_to_strerror(&mmc->rlm->method_group); |
784 | |
|
785 | 0 | return fr_sbuff_error(&meth_start); |
786 | 0 | } |
787 | 0 | mmc->mmb = *mmb; /* For locality of reference and fewer derefs */ |
788 | |
|
789 | 0 | return fr_sbuff_set(name, &our_name); |
790 | 0 | } |
791 | | |
792 | | CONF_SECTION *module_rlm_virtual_by_name(char const *asked_name) |
793 | 0 | { |
794 | 0 | module_rlm_virtual_t *inst; |
795 | |
|
796 | 0 | inst = fr_rb_find(module_rlm_virtual_name_tree, |
797 | 0 | &(module_rlm_virtual_t){ |
798 | 0 | .name = asked_name, |
799 | 0 | }); |
800 | 0 | if (!inst) return NULL; |
801 | | |
802 | 0 | return inst->cs; |
803 | 0 | } |
804 | | |
805 | | module_instance_t *module_rlm_dynamic_by_name(module_instance_t const *parent, char const *asked_name) |
806 | 0 | { |
807 | 0 | return module_instance_by_name(rlm_modules_dynamic, parent, asked_name); |
808 | 0 | } |
809 | | |
810 | | module_instance_t *module_rlm_static_by_name(module_instance_t const *parent, char const *asked_name) |
811 | 0 | { |
812 | 0 | return module_instance_by_name(rlm_modules_static, parent, asked_name); |
813 | 0 | } |
814 | | |
815 | | /** Create a virtual module. |
816 | | * |
817 | | * @param[in] cs that defines the virtual module. |
818 | | * @return |
819 | | * - 0 on success. |
820 | | * - -1 on failure. |
821 | | */ |
822 | | static int module_rlm_bootstrap_virtual(CONF_SECTION *cs) |
823 | | { |
824 | | char const *name; |
825 | | bool all_same; |
826 | | CONF_ITEM *sub_ci = NULL; |
827 | | CONF_PAIR *cp; |
828 | | module_instance_t *mi; |
829 | | module_rlm_virtual_t *inst; |
830 | | |
831 | | name = cf_section_name1(cs); |
832 | | |
833 | | /* |
834 | | * Groups, etc. must have a name. |
835 | | */ |
836 | | if ((strcmp(name, "group") == 0) || |
837 | | (strcmp(name, "redundant") == 0) || |
838 | | (strcmp(name, "redundant-load-balance") == 0) || |
839 | | (strcmp(name, "load-balance") == 0)) { |
840 | | name = cf_section_name2(cs); |
841 | | if (!name) { |
842 | | cf_log_err(cs, "Keyword module must have a second name"); |
843 | | return -1; |
844 | | } |
845 | | |
846 | | /* |
847 | | * name2 was already checked in modules_rlm_bootstrap() |
848 | | */ |
849 | | fr_assert(!unlang_compile_is_keyword(name)); |
850 | | } else { |
851 | | cf_log_err(cs, "Module names cannot be unlang keywords '%s'", name); |
852 | | return -1; |
853 | | } |
854 | | |
855 | | /* |
856 | | * Ensure that the module doesn't exist. |
857 | | */ |
858 | | mi = module_instance_by_name(rlm_modules_static, NULL, name); |
859 | | if (mi) { |
860 | | ERROR("Duplicate module \"%s\" in file %s[%d] and file %s[%d]", |
861 | | name, |
862 | | cf_filename(cs), |
863 | | cf_lineno(cs), |
864 | | cf_filename(mi->conf), |
865 | | cf_lineno(mi->conf)); |
866 | | return -1; |
867 | | } |
868 | | |
869 | | /* |
870 | | * Don't bother registering redundant xlats for a simple "group". |
871 | | */ |
872 | | all_same = (strcmp(cf_section_name1(cs), "group") != 0); |
873 | | |
874 | | { |
875 | | module_t const *last = NULL; |
876 | | |
877 | | /* |
878 | | * Ensure that the modules we reference here exist. |
879 | | */ |
880 | | while ((sub_ci = cf_item_next(cs, sub_ci))) { |
881 | | if (cf_item_is_pair(sub_ci)) { |
882 | | cp = cf_item_to_pair(sub_ci); |
883 | | if (cf_pair_value(cp)) { |
884 | | cf_log_err(sub_ci, "Cannot set return codes in a %s block", cf_section_name1(cs)); |
885 | | return -1; |
886 | | } |
887 | | |
888 | | mi = module_rlm_static_by_name(NULL, cf_pair_attr(cp)); |
889 | | if (!mi) { |
890 | | cf_log_perr(sub_ci, "Failed resolving module reference '%s' in %s block", |
891 | | cf_pair_attr(cp), cf_section_name1(cs)); |
892 | | return -1; |
893 | | } |
894 | | |
895 | | if (all_same) { |
896 | | if (!last) { |
897 | | last = mi->exported; |
898 | | } else if (last != mi->exported) { |
899 | | last = NULL; |
900 | | all_same = false; |
901 | | } |
902 | | } |
903 | | } else { |
904 | | all_same = false; |
905 | | } |
906 | | |
907 | | /* |
908 | | * Don't check subsections for now. That check |
909 | | * happens later in the unlang compiler. |
910 | | */ |
911 | | } /* loop over things in a virtual module section */ |
912 | | } |
913 | | |
914 | | inst = talloc_zero(cs, module_rlm_virtual_t); |
915 | | if (!inst) return -1; |
916 | | |
917 | | inst->cs = cs; |
918 | | MEM(inst->name = talloc_strdup(inst, name)); |
919 | | inst->all_same = all_same; |
920 | | |
921 | | if (!fr_cond_assert(fr_rb_insert(module_rlm_virtual_name_tree, inst))) { |
922 | | talloc_free(inst); |
923 | | return -1; |
924 | | } |
925 | | |
926 | | return 0; |
927 | | } |
928 | | |
929 | | /** Generic conf_parser_t func for loading drivers |
930 | | * |
931 | | */ |
932 | | int module_rlm_submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, |
933 | | CONF_ITEM *ci, conf_parser_t const *rule) |
934 | 0 | { |
935 | 0 | conf_parser_t our_rule = *rule; |
936 | |
|
937 | 0 | our_rule.uctx = &rlm_modules_static; |
938 | |
|
939 | 0 | return module_submodule_parse(ctx, out, parent, ci, &our_rule); |
940 | 0 | } |
941 | | |
942 | | /** Frees thread-specific data for all registered backend modules |
943 | | * |
944 | | */ |
945 | | void modules_rlm_thread_detach(void) |
946 | 0 | { |
947 | 0 | modules_thread_detach(rlm_modules_static); |
948 | 0 | } |
949 | | |
950 | | /** Allocates thread-specific data for all registered backend modules |
951 | | * |
952 | | * @param[in] ctx To allocate any thread-specific data in. |
953 | | * @param[in] el to register events. |
954 | | * @return |
955 | | * - 0 if all modules were instantiated successfully. |
956 | | * - -1 if a module failed instantiation. |
957 | | */ |
958 | | int modules_rlm_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el) |
959 | 0 | { |
960 | 0 | return modules_thread_instantiate(ctx, rlm_modules_static, el); |
961 | 0 | } |
962 | | |
963 | | /** Runs the coord_attach method of all registered backend modules |
964 | | * |
965 | | * @param[in] el to register events. |
966 | | * @return |
967 | | * - 0 if all calls succeeded. |
968 | | * - -1 if a call failed. |
969 | | */ |
970 | | int modules_rlm_coord_attach(fr_event_list_t *el) |
971 | 0 | { |
972 | 0 | return modules_coord_attach(rlm_modules_static, el); |
973 | 0 | } |
974 | | |
975 | | /** Performs the instantiation phase for all backend modules |
976 | | * |
977 | | * @return |
978 | | * - 0 if all modules were instantiated successfully. |
979 | | * - -1 if a module failed instantiation. |
980 | | */ |
981 | | int modules_rlm_instantiate(void) |
982 | 0 | { |
983 | 0 | return modules_instantiate(rlm_modules_static); |
984 | 0 | } |
985 | | |
986 | | /** Compare the section names of two module_method_binding_t structures |
987 | | */ |
988 | | static int8_t binding_name_cmp(void const *one, void const *two) |
989 | 0 | { |
990 | 0 | module_method_binding_t const *a = one; |
991 | 0 | module_method_binding_t const *b = two; |
992 | |
|
993 | 0 | return section_name_cmp(a->section, b->section); |
994 | 0 | } |
995 | | |
996 | | static int module_method_group_validate(module_method_group_t *group) |
997 | 0 | { |
998 | 0 | module_method_binding_t *p, *srt_p; |
999 | 0 | fr_dlist_head_t bindings; |
1000 | 0 | bool in_order = true; |
1001 | | |
1002 | | /* |
1003 | | * Not all modules export module method bindings |
1004 | | */ |
1005 | 0 | if (!group || !group->bindings || group->validated) return 0; |
1006 | | |
1007 | 0 | fr_dlist_init(&bindings, module_method_binding_t, entry); |
1008 | |
|
1009 | 0 | for (p = group->bindings; p->section; p++) { |
1010 | 0 | if (!fr_cond_assert_msg(p->section->name1, |
1011 | 0 | "First section identifier can't be NULL")) return -1; |
1012 | | |
1013 | | /* |
1014 | | * All the bindings go in a list so we can sort them |
1015 | | * and produce the list in the correct order. |
1016 | | */ |
1017 | 0 | fr_dlist_insert_tail(&bindings, p); |
1018 | 0 | } |
1019 | | |
1020 | 0 | fr_dlist_sort(&bindings, binding_name_cmp); |
1021 | | |
1022 | | /* |
1023 | | * Iterate over the sorted list of bindings, |
1024 | | * and the original list, to ensure they're |
1025 | | * in the correct order. |
1026 | | */ |
1027 | 0 | for (srt_p = fr_dlist_head(&bindings), p = group->bindings; |
1028 | 0 | srt_p; |
1029 | 0 | srt_p = fr_dlist_next(&bindings, srt_p), p++) { |
1030 | 0 | if (p != srt_p) { |
1031 | 0 | in_order = false; |
1032 | 0 | break; |
1033 | 0 | } |
1034 | 0 | } |
1035 | | |
1036 | | /* |
1037 | | * Rebuild the binding list in the correct order. |
1038 | | */ |
1039 | 0 | if (!in_order) { |
1040 | 0 | module_method_binding_t *ordered; |
1041 | |
|
1042 | 0 | MEM(ordered = talloc_array(NULL, module_method_binding_t, fr_dlist_num_elements(&bindings))); |
1043 | 0 | for (srt_p = fr_dlist_head(&bindings), p = ordered; |
1044 | 0 | srt_p; |
1045 | 0 | srt_p = fr_dlist_next(&bindings, srt_p), p++) { |
1046 | 0 | *p = *srt_p; |
1047 | 0 | } |
1048 | 0 | memcpy(group->bindings, ordered, fr_dlist_num_elements(&bindings) * sizeof(*ordered)); |
1049 | 0 | talloc_free(ordered); |
1050 | 0 | } |
1051 | | |
1052 | | /* |
1053 | | * Build the "skip" list of name1 entries |
1054 | | */ |
1055 | 0 | { |
1056 | 0 | module_method_binding_t *last_binding = NULL; |
1057 | |
|
1058 | 0 | for (p = group->bindings; p->section; p++) { |
1059 | 0 | if (!last_binding || |
1060 | 0 | ( |
1061 | 0 | (last_binding->section->name1 != p->section->name1) && |
1062 | 0 | ( |
1063 | 0 | (last_binding->section->name1 == CF_IDENT_ANY) || |
1064 | 0 | (p->section->name1 == CF_IDENT_ANY) || |
1065 | 0 | (strcmp(last_binding->section->name1, p->section->name1) != 0) |
1066 | 0 | ) |
1067 | 0 | ) |
1068 | 0 | ) { |
1069 | 0 | fr_dlist_init(&p->same_name1, module_method_binding_t, entry); |
1070 | 0 | last_binding = p; |
1071 | 0 | } |
1072 | 0 | fr_dlist_insert_tail(&last_binding->same_name1, p); |
1073 | 0 | } |
1074 | 0 | } |
1075 | 0 | group->validated = true; |
1076 | |
|
1077 | 0 | return module_method_group_validate(group->next); |
1078 | 0 | } |
1079 | | |
1080 | | static int module_method_validate(module_instance_t *mi) |
1081 | 0 | { |
1082 | 0 | module_rlm_t *mrlm = module_rlm_from_module(mi->exported); |
1083 | |
|
1084 | 0 | return module_method_group_validate(&mrlm->method_group); |
1085 | 0 | } |
1086 | | |
1087 | | /** Allocate a rlm module instance |
1088 | | * |
1089 | | * These have extra space allocated to hold the dlist of associated xlats. |
1090 | | * |
1091 | | * @param[in] ml Module list to allocate from. |
1092 | | * @param[in] parent Parent module instance. |
1093 | | * @param[in] type Type of module instance. |
1094 | | * @param[in] mod_name Name of the module. |
1095 | | * @param[in] inst_name Name of the instance. |
1096 | | * @param[in] init_state Initial state of the module instance. |
1097 | | * @return |
1098 | | * - The allocated module instance on success. |
1099 | | * - NULL on failure. |
1100 | | */ |
1101 | | static inline CC_HINT(always_inline) |
1102 | | module_instance_t *module_rlm_instance_alloc(module_list_t *ml, |
1103 | | module_instance_t const *parent, |
1104 | | dl_module_type_t type, char const *mod_name, char const *inst_name, |
1105 | | module_instance_state_t init_state) |
1106 | | { |
1107 | | module_instance_t *mi; |
1108 | | module_rlm_instance_t *mri; |
1109 | | |
1110 | | mi = module_instance_alloc(ml, parent, type, mod_name, inst_name, init_state); |
1111 | | if (unlikely(mi == NULL)) return NULL; |
1112 | | |
1113 | | MEM(mri = talloc(mi, module_rlm_instance_t)); |
1114 | | module_instance_uctx_set(mi, mri); |
1115 | | |
1116 | | fr_dlist_talloc_init(&mri->xlats, module_rlm_xlat_t, entry); |
1117 | | |
1118 | | return mi; |
1119 | | } |
1120 | | |
1121 | | static int module_conf_parse(module_list_t *ml, CONF_SECTION *mod_conf) |
1122 | 0 | { |
1123 | 0 | char const *name; |
1124 | 0 | char const *inst_name; |
1125 | 0 | module_instance_t *mi = NULL; |
1126 | 0 | CONF_SECTION *actions; |
1127 | | |
1128 | | /* |
1129 | | * name2 can't be a keyword |
1130 | | */ |
1131 | 0 | name = cf_section_name2(mod_conf); |
1132 | 0 | if (name && unlang_compile_is_keyword(name)) { |
1133 | 0 | invalid_name: |
1134 | 0 | cf_log_err(mod_conf, "Module names cannot be unlang keywords '%s'", name); |
1135 | 0 | return -1; |
1136 | 0 | } |
1137 | | |
1138 | 0 | name = cf_section_name1(mod_conf); |
1139 | | |
1140 | | /* |
1141 | | * For now, ignore name1 which is a keyword. |
1142 | | */ |
1143 | 0 | if (unlang_compile_is_keyword(name)) { |
1144 | 0 | if (!cf_section_name2(mod_conf)) { |
1145 | 0 | cf_log_err(mod_conf, "Missing second name at '%s'", name); |
1146 | 0 | return -1; |
1147 | 0 | } |
1148 | 0 | if (module_rlm_bootstrap_virtual(mod_conf) < 0) return -1; |
1149 | 0 | return 0; |
1150 | 0 | } |
1151 | | |
1152 | | /* |
1153 | | * Skip inline templates, and disallow "template { ... }" |
1154 | | */ |
1155 | 0 | if (strcmp(name, "template") == 0) { |
1156 | 0 | if (!cf_section_name2(mod_conf)) goto invalid_name; |
1157 | 0 | return 0; |
1158 | 0 | } |
1159 | | |
1160 | 0 | if (module_instance_name_from_conf(&inst_name, mod_conf) < 0) goto invalid_name; |
1161 | | |
1162 | 0 | mi = module_rlm_instance_alloc(ml, NULL, DL_MODULE_TYPE_MODULE, name, inst_name, 0); |
1163 | 0 | if (unlikely(mi == NULL)) { |
1164 | 0 | cf_log_perr(mod_conf, "Failed loading module"); |
1165 | 0 | return -1; |
1166 | 0 | } |
1167 | | |
1168 | | /* |
1169 | | * First time we've loaded the dl module, so we need to |
1170 | | * check the module methods to make sure they're ordered |
1171 | | * correctly, and to add the "skip list" style name2 |
1172 | | * entries. |
1173 | | */ |
1174 | 0 | if ((mi->module->refs == 1) && (module_method_validate(mi) < 0)) { |
1175 | 0 | talloc_free(mi); |
1176 | 0 | return -1; |
1177 | 0 | } |
1178 | | |
1179 | 0 | if (module_instance_conf_parse(mi, mod_conf) < 0) { |
1180 | 0 | cf_log_perr(mod_conf, "Failed parsing module config"); |
1181 | 0 | talloc_free(mi); |
1182 | 0 | return -1; |
1183 | 0 | } |
1184 | | |
1185 | | /* |
1186 | | * Compile the default "actions" subsection, which includes retries. |
1187 | | */ |
1188 | 0 | actions = cf_section_find(mod_conf, "actions", NULL); |
1189 | 0 | if (actions && !unlang_compile_actions(&mi->actions, actions, (mi->exported->flags & MODULE_TYPE_RETRY))) { |
1190 | 0 | talloc_free(mi); |
1191 | 0 | return -1; |
1192 | 0 | } |
1193 | | |
1194 | 0 | return 0; |
1195 | 0 | } |
1196 | | |
1197 | | /** Bootstrap modules and virtual modules |
1198 | | * |
1199 | | * Parse the module config sections, and load and call each module's init() function. |
1200 | | * |
1201 | | * @param[in] root of the server configuration. |
1202 | | * @return |
1203 | | * - 0 if all modules were bootstrapped successfully. |
1204 | | * - -1 if a module/virtual module failed to bootstrap. |
1205 | | */ |
1206 | | int modules_rlm_bootstrap(CONF_SECTION *root) |
1207 | 0 | { |
1208 | 0 | CONF_SECTION *cs, *modules, *static_cs, *dynamic_cs; |
1209 | 0 | module_rlm_virtual_t *vm; |
1210 | 0 | fr_rb_iter_inorder_t iter; |
1211 | | |
1212 | | /* |
1213 | | * Ensure any libraries the modules depend on are instantiated |
1214 | | */ |
1215 | 0 | global_lib_instantiate(); |
1216 | | |
1217 | | /* |
1218 | | * Remember where the modules were stored. |
1219 | | */ |
1220 | 0 | modules = cf_section_find(root, "modules", NULL); |
1221 | 0 | if (!modules) { |
1222 | 0 | WARN("Cannot find a \"modules\" section in the configuration file!"); |
1223 | 0 | return 0; |
1224 | 0 | } |
1225 | | |
1226 | 0 | static_cs = cf_section_find(modules, "static", NULL); |
1227 | 0 | if (!static_cs) { |
1228 | 0 | static_cs = cf_section_alloc(modules, NULL, "static", NULL); |
1229 | 0 | cf_section_foreach(modules, mod_cs) { |
1230 | 0 | CONF_ITEM *prev; |
1231 | 0 | char const *name1 = cf_section_name1(mod_cs); |
1232 | | |
1233 | | /* |
1234 | | * Skip over the dynamic section |
1235 | | */ |
1236 | 0 | if ((strcmp(name1, "dynamic") == 0) && !cf_section_name2(mod_cs)) continue; |
1237 | | |
1238 | | /* |
1239 | | * Ignore this section if it is commented out with a magic name. |
1240 | | */ |
1241 | 0 | if (*name1 == '-') continue; |
1242 | | |
1243 | | /* |
1244 | | * Move all modules which are not in |
1245 | | * the dynamic section into the static |
1246 | | * section for backwards compatibility. |
1247 | | */ |
1248 | 0 | prev = cf_item_remove(modules, mod_cs); |
1249 | 0 | cf_item_add(static_cs, mod_cs); |
1250 | | |
1251 | | /* |
1252 | | * Find the previous item that's a section |
1253 | | */ |
1254 | 0 | while (prev && !cf_item_is_section(prev)) prev = cf_item_prev(modules, prev); |
1255 | | |
1256 | | /* |
1257 | | * Resume iterating from that item |
1258 | | */ |
1259 | 0 | mod_cs = cf_item_to_section(prev); |
1260 | 0 | } |
1261 | 0 | cf_item_add(modules, static_cs); |
1262 | 0 | } |
1263 | 0 | DEBUG2("#### Bootstrapping static modules ####"); |
1264 | 0 | cf_log_debug(modules, " modules {"); |
1265 | 0 | cf_log_debug(modules, " static {"); |
1266 | 0 | cf_section_foreach(static_cs, mod_conf) { |
1267 | 0 | if (module_conf_parse(rlm_modules_static, mod_conf) < 0) return -1; |
1268 | 0 | } |
1269 | 0 | cf_log_debug(modules, " } # static"); |
1270 | | |
1271 | | /* |
1272 | | * Now we have a module tree, run bootstrap on all the modules. |
1273 | | * This will bootstrap modules and then submodules. |
1274 | | */ |
1275 | 0 | if (unlikely(modules_bootstrap(rlm_modules_static) < 0)) return -1; |
1276 | | |
1277 | 0 | if (fr_command_register_hook(NULL, NULL, static_cs, module_cmd_list_table) < 0) { |
1278 | 0 | PERROR("Failed registering radmin commands for modules"); |
1279 | 0 | return -1; |
1280 | 0 | } |
1281 | | |
1282 | | /* |
1283 | | * Build the configuration and parse dynamic modules |
1284 | | */ |
1285 | 0 | dynamic_cs = cf_section_find(modules, "dynamic", NULL); |
1286 | 0 | if (dynamic_cs) { |
1287 | 0 | DEBUG2("#### Bootstrapping dynamic modules ####"); |
1288 | | /* |
1289 | | * Parse and then instantiate any dynamic modules configure |
1290 | | */ |
1291 | 0 | cf_log_debug(modules, " dynamic {"); |
1292 | 0 | cf_section_foreach(dynamic_cs, mod_conf) { |
1293 | 0 | if (unlikely(module_conf_parse(rlm_modules_dynamic, mod_conf) < 0)) return -1; |
1294 | 0 | } |
1295 | 0 | cf_log_debug(modules, " } # dynamic"); |
1296 | 0 | if (unlikely(modules_bootstrap(rlm_modules_dynamic) < 0)) return -1; |
1297 | 0 | cf_log_debug(modules, " } # modules"); |
1298 | 0 | } |
1299 | | |
1300 | | /* |
1301 | | * Check for duplicate policies. They're treated as |
1302 | | * modules, so we might as well check them here. |
1303 | | */ |
1304 | 0 | cs = cf_section_find(root, "policy", NULL); |
1305 | 0 | if (cs) { |
1306 | 0 | cf_section_foreach(cs, policy_cs) { |
1307 | 0 | CONF_SECTION *problemcs; |
1308 | 0 | char const *name1 = cf_section_name1(policy_cs); |
1309 | |
|
1310 | 0 | if (unlang_compile_is_keyword(name1)) { |
1311 | 0 | cf_log_err(policy_cs, "Policy name '%s' cannot be an unlang keyword", name1); |
1312 | 0 | return -1; |
1313 | 0 | } |
1314 | | |
1315 | 0 | if (cf_section_name2(policy_cs)) { |
1316 | 0 | cf_log_err(policy_cs, "Policies cannot have two names"); |
1317 | 0 | return -1; |
1318 | 0 | } |
1319 | | |
1320 | 0 | problemcs = cf_section_find_next(cs, policy_cs, name1, CF_IDENT_ANY); |
1321 | 0 | if (!problemcs) continue; |
1322 | | |
1323 | 0 | cf_log_err(problemcs, "Duplicate policy '%s' is forbidden.", |
1324 | 0 | cf_section_name1(policy_cs)); |
1325 | 0 | return -1; |
1326 | 0 | } |
1327 | 0 | } |
1328 | | |
1329 | | /* |
1330 | | * Now that all of the xlat things have been registered, |
1331 | | * register our redundant xlats. But only when all of |
1332 | | * the items in such a section are the same. |
1333 | | */ |
1334 | 0 | for (vm = fr_rb_iter_init_inorder(module_rlm_virtual_name_tree, &iter); |
1335 | 0 | vm; |
1336 | 0 | vm = fr_rb_iter_next_inorder(module_rlm_virtual_name_tree, &iter)) { |
1337 | 0 | if (!vm->all_same) continue; |
1338 | | |
1339 | 0 | if (xlat_register_redundant(vm->cs) < 0) return -1; |
1340 | 0 | } |
1341 | | |
1342 | 0 | return 0; |
1343 | 0 | } |
1344 | | |
1345 | | /** Cleanup all global structures |
1346 | | * |
1347 | | * Automatically called on exit. |
1348 | | */ |
1349 | | int modules_rlm_free(void) |
1350 | 0 | { |
1351 | 0 | if (talloc_free(rlm_modules_static) < 0) return -1; |
1352 | 0 | rlm_modules_static = NULL; |
1353 | |
|
1354 | 0 | if (talloc_free(rlm_modules_dynamic) < 0) return -1; |
1355 | 0 | rlm_modules_dynamic = NULL; |
1356 | |
|
1357 | 0 | if (talloc_free(module_rlm_virtual_name_tree) < 0) return -1; |
1358 | 0 | module_rlm_virtual_name_tree = NULL; |
1359 | |
|
1360 | 0 | return 0; |
1361 | 0 | } |
1362 | | |
1363 | | static int _modules_rlm_free_atexit(UNUSED void *uctx) |
1364 | 0 | { |
1365 | 0 | return modules_rlm_free(); |
1366 | 0 | } |
1367 | | |
1368 | | /** Initialise the module list structure |
1369 | | * |
1370 | | */ |
1371 | | int modules_rlm_init(void) |
1372 | | { |
1373 | | MEM(rlm_modules_static = module_list_alloc(NULL, &module_list_type_global, "rlm", true)); |
1374 | | MEM(rlm_modules_dynamic = module_list_alloc(NULL, &module_list_type_thread_local, "rlm", true)); |
1375 | | module_list_mask_set(rlm_modules_dynamic, MODULE_INSTANCE_INSTANTIATED); /* Ensure we never instantiate dynamic modules */ |
1376 | | |
1377 | | MEM(module_rlm_virtual_name_tree = fr_rb_inline_alloc(NULL, module_rlm_virtual_t, name_node, |
1378 | | module_rlm_virtual_name_cmp, NULL)); |
1379 | | fr_atexit_global(_modules_rlm_free_atexit, NULL); |
1380 | | |
1381 | | return 0; |
1382 | | } |