The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_sql.c
Go to the documentation of this file.
1/*
2 * This program is 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 (at
5 * 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: fc8245d7d5b99f5839f4548e82aa941b94cd5fcd $
19 * @file rlm_sql.c
20 * @brief Implements SQL 'users' file, and SQL accounting.
21 *
22 * @copyright 2012-2014 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 * @copyright 2000,2006 The FreeRADIUS server project
24 * @copyright 2000 Mike Machado (mike@innercite.com)
25 * @copyright 2000 Alan DeKok (aland@freeradius.org)
26 */
27
28RCSID("$Id: fc8245d7d5b99f5839f4548e82aa941b94cd5fcd $")
29
30#define LOG_PREFIX mctx->mi->name
31
32#include <freeradius-devel/server/base.h>
33#include <freeradius-devel/server/exfile.h>
34#include <freeradius-devel/server/map_proc.h>
35#include <freeradius-devel/server/module_rlm.h>
36#include <freeradius-devel/server/pairmove.h>
37#include <freeradius-devel/server/rcode.h>
38#include <freeradius-devel/server/trigger.h>
39#include <freeradius-devel/util/debug.h>
40#include <freeradius-devel/util/dict.h>
41#include <freeradius-devel/util/skip.h>
42#include <freeradius-devel/util/table.h>
43#include <freeradius-devel/unlang/action.h>
44#include <freeradius-devel/unlang/function.h>
45#include <freeradius-devel/unlang/xlat_func.h>
46#include <freeradius-devel/unlang/module.h>
47#include <freeradius-devel/unlang/map.h>
48
49#include <sys/stat.h>
50
51#include "rlm_sql.h"
52
53#define SQL_SAFE_FOR (fr_value_box_safe_for_t)inst->driver
54
56
57static int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
58
63
64static const conf_parser_t module_config[] = {
65 { FR_CONF_OFFSET_TYPE_FLAGS("driver", FR_TYPE_VOID, 0, rlm_sql_t, driver_submodule), .dflt = "null",
67 { FR_CONF_OFFSET("server", rlm_sql_config_t, sql_server), .dflt = "" }, /* Must be zero length so drivers can determine if it was set */
68 { FR_CONF_OFFSET("port", rlm_sql_config_t, sql_port), .dflt = "0" },
69 { FR_CONF_OFFSET("login", rlm_sql_config_t, sql_login), .dflt = "" },
70 { FR_CONF_OFFSET_FLAGS("password", CONF_FLAG_SECRET, rlm_sql_config_t, sql_password), .dflt = "" },
71 { FR_CONF_OFFSET("radius_db", rlm_sql_config_t, sql_db), .dflt = "radius" },
72 { FR_CONF_OFFSET("read_groups", rlm_sql_config_t, read_groups), .dflt = "yes" },
73 { FR_CONF_OFFSET("group_attribute", rlm_sql_config_t, group_attribute) },
74 { FR_CONF_OFFSET("cache_groups", rlm_sql_config_t, cache_groups) },
75 { FR_CONF_OFFSET("read_profiles", rlm_sql_config_t, read_profiles), .dflt = "yes" },
76 { FR_CONF_OFFSET("open_query", rlm_sql_config_t, connect_query) },
77 { FR_CONF_OFFSET("query_number_attribute", rlm_sql_config_t, query_number_attribute) },
78
79 { FR_CONF_OFFSET("safe_characters", rlm_sql_config_t, allowed_chars), .dflt = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
80
81 /*
82 * This only works for a few drivers.
83 */
84 { FR_CONF_OFFSET("query_timeout", rlm_sql_config_t, query_timeout), .dflt = "5" },
85
86 /*
87 * The pool section is used for trunk config
88 */
89 { FR_CONF_OFFSET_SUBSECTION("pool", 0, rlm_sql_config_t, trunk_conf, trunk_config) },
90
92
94};
95
97
100 { .out = &dict_freeradius, .proto = "freeradius" },
102};
103
108
111 { .out = &attr_fall_through, .name = "Fall-Through", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
112 { .out = &attr_sql_user_name, .name = "SQL-User-Name", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
113 { .out = &attr_user_profile, .name = "User-Profile", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
114 { .out = &attr_expr_bool_enum, .name = "Expr-Bool-Enum", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
116};
117
118typedef struct {
119 fr_value_box_t user; //!< Expansion of the sql_user_name
120 tmpl_t *check_query; //!< Tmpl to expand to form authorize_check_query
121 tmpl_t *reply_query; //!< Tmpl to expand to form authorize_reply_query
122 tmpl_t *membership_query; //!< Tmpl to expand to form group_membership_query
123 tmpl_t *group_check_query; //!< Tmpl to expand to form authorize_group_check_query
124 tmpl_t *group_reply_query; //!< Tmpl to expand to form authorize_group_reply_query
126
127static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc,
128 call_env_ctx_t const *cec, call_env_parser_t const *rule);
129
130static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc,
131 call_env_ctx_t const *cec, call_env_parser_t const *rule);
132
136
139 .env = (call_env_parser_t[]) {
141 .pair.escape = {
142 .box_escape = {
145 .always_escape = false,
146 },
148 },
149 .pair.literals_safe_for = (fr_value_box_safe_for_t)rad_filename_box_make_safe,
150 },
152 }
153};
154
156
157/** Status of the authorization process
158 */
159typedef enum {
160 SQL_AUTZ_CHECK = 0x12, //!< Running user `check` query
161 SQL_AUTZ_CHECK_RESUME = 0x13, //!< Completed user `check` query
162 SQL_AUTZ_REPLY = 0x14, //!< Running user `reply` query
163 SQL_AUTZ_REPLY_RESUME = 0x15, //!< Completed user `reply` query
164 SQL_AUTZ_GROUP_MEMB = 0x20, //!< Running group membership query
165 SQL_AUTZ_GROUP_MEMB_RESUME = 0x21, //!< Completed group membership query
166 SQL_AUTZ_GROUP_CHECK = 0x22, //!< Running group `check` query
167 SQL_AUTZ_GROUP_CHECK_RESUME = 0x23, //!< Completed group `check` query
168 SQL_AUTZ_GROUP_REPLY = 0x24, //!< Running group `reply` query
169 SQL_AUTZ_GROUP_REPLY_RESUME = 0x25, //!< Completed group `reply` query
170 SQL_AUTZ_PROFILE_START = 0x40, //!< Starting processing user profiles
171 SQL_AUTZ_PROFILE_CHECK = 0x42, //!< Running profile `check` query
172 SQL_AUTZ_PROFILE_CHECK_RESUME = 0x43, //!< Completed profile `check` query
173 SQL_AUTZ_PROFILE_REPLY = 0x44, //!< Running profile `reply` query
174 SQL_AUTZ_PROFILE_REPLY_RESUME = 0x45, //!< Completed profile `reply` query
176
177#define SQL_AUTZ_STAGE_GROUP 0x20
178#define SQL_AUTZ_STAGE_PROFILE 0x40
179
180/** Context for group membership query evaluation
181 */
182typedef struct {
183 rlm_sql_t const *inst; //!< Module instance.
184 fr_value_box_t *query; //!< Query string used for evaluating group membership.
185 fr_sql_query_t *query_ctx; //!< Query context.
186 rlm_sql_grouplist_t *groups; //!< List of groups retrieved.
187 int num_groups; //!< How many groups have been retrieved.
189
190/** Context for SQL authorization
191 */
192typedef struct {
193 rlm_sql_t const *inst; //!< Module instance.
194 request_t *request; //!< Request being processed.
195 rlm_rcode_t rcode; //!< Module return code.
196 trunk_t *trunk; //!< Trunk connection for current authorization.
197 sql_autz_call_env_t *call_env; //!< Call environment data.
198 map_list_t check_tmp; //!< List to store check items before processing.
199 map_list_t reply_tmp; //!< List to store reply items before processing.
200 sql_autz_status_t status; //!< Current status of the authorization.
201 fr_value_box_list_t query; //!< Where expanded query tmpls will be written.
202 bool user_found; //!< Has the user been found anywhere?
203 rlm_sql_grouplist_t *group; //!< Current group being processed.
204 fr_pair_t *sql_group; //!< Pair to update with group being processed.
205 fr_pair_t *profile; //!< Current profile being processed.
206 fr_sql_map_ctx_t *map_ctx; //!< Context used for retrieving attribute value pairs as a map list.
207 sql_group_ctx_t *group_ctx; //!< Context used for retrieving user group membership.
209
210typedef struct {
211 fr_value_box_t user; //!< Expansion of sql_user_name.
212 fr_value_box_t filename; //!< File name to write SQL logs to.
213 tmpl_t **query; //!< Array of tmpls for list of queries to run.
215
225
235
236/** Context for tracking redundant SQL query sets
237 */
238typedef struct {
239 rlm_sql_t const *inst; //!< Module instance.
240 request_t *request; //!< Request being processed.
241 trunk_t *trunk; //!< Trunk connection for queries.
242 sql_redundant_call_env_t *call_env; //!< Call environment data.
243 size_t query_no; //!< Current query number.
244 fr_value_box_list_t query; //!< Where expanded query tmpl will be written.
245 fr_value_box_t *query_vb; //!< Current query string.
246 fr_sql_query_t *query_ctx; //!< Query context for current query.
248
253
262
263int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
264{
265 rlm_sql_t *inst = talloc_get_type_abort(parent, rlm_sql_t);
267 int ret;
268
269 if (unlikely(ret = module_rlm_submodule_parse(ctx, out, parent, ci, rule) < 0)) return ret;
270
271 mi = talloc_get_type_abort(*((void **)out), module_instance_t);
272 inst->driver = (rlm_sql_driver_t const *)mi->exported; /* Public symbol exported by the submodule */
273
274 return 0;
275}
276
277static int _sql_escape_uxtx_free(void *uctx)
278{
279 return talloc_free(uctx);
280}
281
282/*
283 * Create a thread local uctx which is used in SQL value box escaping
284 * so that an already reserved connection can be used.
285 */
286static void *sql_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
287{
288 static _Thread_local rlm_sql_escape_uctx_t *t_ctx;
289
290 if (unlikely(t_ctx == NULL)) {
292
293 MEM(ctx = talloc_zero(NULL, rlm_sql_escape_uctx_t));
295 }
296 t_ctx->sql = uctx;
297
298 return t_ctx;
299}
300
301/*
302 * Fall-Through checking function from rlm_files.c
303 */
304static sql_fall_through_t fall_through(map_list_t *maps)
305{
306 bool rcode;
307 map_t *map, *next;
308
309 for (map = map_list_head(maps);
310 map != NULL;
311 map = next) {
312 next = map_list_next(maps, map);
313
315
317 (void) map_list_remove(maps, map);
318
319 if (tmpl_is_data(map->rhs)) {
321
322 rcode = tmpl_value(map->rhs)->vb_bool;
323 } else {
324 rcode = false;
325 }
326
327 talloc_free(map);
328 return rcode;
329 }
330 }
331
333}
334
335/*
336 * Yucky prototype.
337 */
338static ssize_t sql_escape_func(request_t *, char *out, size_t outlen, char const *in, void *arg);
339
340/** Escape a tainted VB used as an xlat argument
341 *
342 */
343static int CC_HINT(nonnull(2,3)) sql_xlat_escape(request_t *request, fr_value_box_t *vb, void *uctx)
344{
345 fr_sbuff_t sbuff;
346 fr_sbuff_uctx_talloc_t sbuff_ctx;
347
348 ssize_t len;
349 void *arg = NULL;
350 rlm_sql_escape_uctx_t *ctx = uctx;
352 rlm_sql_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data, rlm_sql_thread_t);
353
354 /*
355 * If it's already safe, don't do anything.
356 */
357 if (fr_value_box_is_safe_for(vb, inst->driver)) return 0;
358
359 /*
360 * Don't print "::" for enum names. Instead we convert
361 * the box to a string which contains the enum name, and
362 * then see if we need to escape it.
363 */
364 if (vb->enumv && vb->enumv->flags.has_value) {
365 char const *name;
366
367 name = fr_dict_enum_name_by_value(vb->enumv, vb);
368 if (name) {
369 int rcode;
370
371 /*
372 * Store list pointers to restore later - fr_value_box_cast clears them
373 */
374 fr_value_box_entry_t entry = vb->entry;
375
376 rcode = fr_value_box_strdup(vb, vb, NULL, name, false);
377 vb->entry = entry;
378
379 if (rcode < 0) return rcode;
380
381 goto check_escape_arg;
382 }
383 }
384
385 /*
386 * No need to escape types with inherently safe data
387 */
388 switch (vb->type) {
389 case FR_TYPE_NUMERIC:
390 case FR_TYPE_IP:
391 case FR_TYPE_ETHERNET:
392 fr_value_box_mark_safe_for(vb, inst->driver);
393 return 0;
394
395 default:
396 break;
397 }
398
399check_escape_arg:
400 if (inst->sql_escape_arg) {
401 arg = inst->sql_escape_arg;
402 } else if (thread->sql_escape_arg) {
403 arg = thread->sql_escape_arg;
404 }
405 if (!arg) {
406 error:
408 return -1;
409 }
410
411 /*
412 * Escaping functions work on strings - ensure the box is a string
413 */
414 if ((vb->type != FR_TYPE_STRING) && (fr_value_box_cast_in_place(vb, vb, FR_TYPE_STRING, NULL) < 0)) goto error;
415
416 /*
417 * Maximum escaped length is 3 * original - if every character needs escaping
418 */
419 if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, vb->vb_length * 3, vb->vb_length * 3)) {
420 fr_strerror_printf_push("Failed to allocate buffer for escaped sql argument");
421 return -1;
422 }
423
424 len = inst->sql_escape_func(request, fr_sbuff_buff(&sbuff), vb->vb_length * 3 + 1, vb->vb_strvalue, arg);
425 if (len < 0) goto error;
426
427 fr_sbuff_trim_talloc(&sbuff, len);
429
430 /*
431 * Different databases have slightly different ideas as
432 * to what is safe. So we track the database type in the
433 * safe value. This means that we don't
434 * cross-contaminate "safe" values across databases.
435 */
436 fr_value_box_mark_safe_for(vb, inst->driver);
437
438 return 0;
439}
440
441static int sql_box_escape(fr_value_box_t *vb, void *uctx)
442{
443 return sql_xlat_escape(NULL, vb, uctx);
444}
445
446/** Escape a value to make it SQL safe.
447 *
448@verbatim
449%sql.escape(<value>)
450@endverbatim
451 *
452 * @ingroup xlat_functions
453 */
454static xlat_action_t sql_escape_xlat(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
455 request_t *request, fr_value_box_list_t *in)
456{
457 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
458 fr_value_box_t *vb;
459 rlm_sql_escape_uctx_t *escape_uctx = NULL;
460
461 while ((vb = fr_value_box_list_pop_head(in))) {
462 if (fr_value_box_is_safe_for(vb, inst->driver)) goto append;
463 if (!escape_uctx) escape_uctx = sql_escape_uctx_alloc(request, inst);
464 sql_box_escape(vb, escape_uctx);
465 append:
467 }
468 return XLAT_ACTION_DONE;
469}
470
471static xlat_action_t sql_xlat_query_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
472 request_t *request, UNUSED fr_value_box_list_t *in)
473{
474 fr_sql_query_t *query_ctx = talloc_get_type_abort(xctx->rctx, fr_sql_query_t);
475 rlm_sql_t const *inst = query_ctx->inst;
476 fr_value_box_t *vb;
478 int numaffected;
479
480 fr_assert(query_ctx->type == SQL_QUERY_OTHER);
481
482 switch (query_ctx->rcode) {
484 case RLM_SQL_ERROR:
487 query_ctx->rcode, "<INVALID>"));
488 rlm_sql_print_error(inst, request, query_ctx, false);
489 ret = XLAT_ACTION_FAIL;
490 goto finish;
491
492 default:
493 break;
494 }
495
496 numaffected = (inst->driver->sql_affected_rows)(query_ctx, &inst->config);
497 if (numaffected < 1) {
498 RDEBUG2("SQL query affected no rows");
499 numaffected = 0;
500 }
501
502 MEM(vb = fr_value_box_alloc_null(ctx));
503 fr_value_box_uint32(vb, NULL, (uint32_t)numaffected, false);
505
506finish:
507 talloc_free(query_ctx);
508
509 return ret;
510}
511
512static xlat_action_t sql_xlat_select_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
513 request_t *request, UNUSED fr_value_box_list_t *in)
514{
515 fr_sql_query_t *query_ctx = talloc_get_type_abort(xctx->rctx, fr_sql_query_t);
516 rlm_sql_t const *inst = query_ctx->inst;
517 fr_value_box_t *vb;
519 unlang_result_t p_result;
520 rlm_sql_row_t row;
521 bool fetched = false;
522
523 fr_assert(query_ctx->type == SQL_QUERY_SELECT);
524
525 if (query_ctx->rcode != RLM_SQL_OK) {
526 query_error:
528 query_ctx->rcode, "<INVALID>"));
529 rlm_sql_print_error(inst, request, query_ctx, false);
530 ret = XLAT_ACTION_FAIL;
531 goto finish;
532 }
533
534 do {
535 inst->fetch_row(&p_result, request, query_ctx);
536 row = query_ctx->row;
537 switch (query_ctx->rcode) {
538 case RLM_SQL_OK:
539 if (row[0]) break;
540
541 RDEBUG2("NULL value in first column of result");
542 goto finish;
543
545 if (!fetched) RDEBUG2("SQL query returned no results");
546 goto finish;
547
548 default:
549 goto query_error;
550 }
551
552 fetched = true;
553
554 MEM(vb = fr_value_box_alloc_null(ctx));
555 fr_value_box_strdup(vb, vb, NULL, row[0], false);
557
558 } while (1);
559
560finish:
561 talloc_free(query_ctx);
562
563 return ret;
564}
565
566/** Execute an arbitrary SQL query
567 *
568 * For SELECTs, the values of the first column will be returned.
569 * For INSERTS, UPDATEs and DELETEs, the number of rows affected will
570 * be returned instead.
571 *
572@verbatim
573%sql(<sql statement>)
574@endverbatim
575 *
576 * @ingroup xlat_functions
577 */
578static xlat_action_t sql_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
579 xlat_ctx_t const *xctx,
580 request_t *request, fr_value_box_list_t *in)
581{
582 sql_xlat_call_env_t *call_env = talloc_get_type_abort(xctx->env_data, sql_xlat_call_env_t);
583 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
584 rlm_sql_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_sql_thread_t);
585 char const *p;
586 fr_value_box_t *arg = fr_value_box_list_head(in);
587 fr_sql_query_t *query_ctx = NULL;
589
590 if (call_env->filename.type == FR_TYPE_STRING && call_env->filename.vb_length > 0) {
591 rlm_sql_query_log(inst, call_env->filename.vb_strvalue, arg->vb_strvalue);
592 }
593
594 p = arg->vb_strvalue;
595
596 /*
597 * Trim whitespace for the prefix check
598 */
600
601 /*
602 * If the query starts with any of the following prefixes,
603 * then return the number of rows affected
604 */
605 if ((strncasecmp(p, "insert", 6) == 0) ||
606 (strncasecmp(p, "update", 6) == 0) ||
607 (strncasecmp(p, "delete", 6) == 0)) {
608 MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request,
609 thread->trunk, arg->vb_strvalue, SQL_QUERY_OTHER));
610
611 unlang_xlat_yield(request, sql_xlat_query_resume, NULL, 0, query_ctx);
612
613 /* Modify current frame's rcode directly */
614 query_ret = inst->query(unlang_interpret_result(request), request, query_ctx);
616
617 return sql_xlat_query_resume(ctx, out, &(xlat_ctx_t){.rctx = query_ctx, .inst = inst}, request, in);
618 } /* else it's a SELECT statement */
619
620 MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request,
621 thread->trunk, arg->vb_strvalue, SQL_QUERY_SELECT));
622
623 unlang_xlat_yield(request, sql_xlat_select_resume, NULL, 0, query_ctx);
624
625 if (unlang_function_push_with_result(/* discard, sql_xlat_select_resume just uses query_ctx->rcode */ NULL,
626 request,
627 inst->select,
628 NULL,
629 NULL, 0,
632}
633
634/** Execute an arbitrary SQL query, expecting results to be returned
635 *
636@verbatim
637%sql.fetch(<sql statement>)
638@endverbatim
639 *
640 * @ingroup xlat_functions
641 */
643 request_t *request, fr_value_box_list_t *in)
644{
645 sql_xlat_call_env_t *call_env = talloc_get_type_abort(xctx->env_data, sql_xlat_call_env_t);
646 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
647 rlm_sql_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_sql_thread_t);
648 fr_value_box_t *arg = fr_value_box_list_head(in);
649 fr_sql_query_t *query_ctx = NULL;
650
651 if (call_env->filename.type == FR_TYPE_STRING && call_env->filename.vb_length > 0) {
652 rlm_sql_query_log(inst, call_env->filename.vb_strvalue, arg->vb_strvalue);
653 }
654
655 MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request,
656 thread->trunk, arg->vb_strvalue, SQL_QUERY_SELECT));
657
658 unlang_xlat_yield(request, sql_xlat_select_resume, NULL, 0, query_ctx);
659 if (unlang_function_push_with_result(/* discard, sql_xlat_select_resume just uses query_ctx->rcode */NULL,
660 request,
661 inst->select,
662 NULL,
663 NULL, 0,
666}
667
668/** Execute an arbitrary SQL query, returning the number of rows affected
669 *
670@verbatim
671%sql.modify(<sql statement>)
672@endverbatim
673 *
674 * @ingroup xlat_functions
675 */
676static xlat_action_t sql_modify_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
677 request_t *request, fr_value_box_list_t *in)
678{
679 sql_xlat_call_env_t *call_env = talloc_get_type_abort(xctx->env_data, sql_xlat_call_env_t);
680 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
681 rlm_sql_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_sql_thread_t);
682 fr_value_box_t *arg = fr_value_box_list_head(in);
683 fr_sql_query_t *query_ctx = NULL;
684
685 if (call_env->filename.type == FR_TYPE_STRING && call_env->filename.vb_length > 0) {
686 rlm_sql_query_log(inst, call_env->filename.vb_strvalue, arg->vb_strvalue);
687 }
688
689 MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request,
690 thread->trunk, arg->vb_strvalue, SQL_QUERY_OTHER));
691
692 unlang_xlat_yield(request, sql_xlat_query_resume, NULL, 0, query_ctx);
693 /* Write out the result directly to this frame's rcode */
694 if (inst->query(unlang_interpret_result(request), request, query_ctx) == UNLANG_ACTION_PUSHED_CHILD) return XLAT_ACTION_PUSH_UNLANG;
695
696 return sql_xlat_query_resume(ctx, out, &(xlat_ctx_t){.rctx = query_ctx, .inst = inst}, request, in);
697}
698
699/** Converts a string value into a #fr_pair_t
700 *
701 * @param[in,out] ctx to allocate #fr_pair_t (s).
702 * @param[out] out where to write the resulting #fr_pair_t.
703 * @param[in] request The current request.
704 * @param[in] map to process.
705 * @param[in] uctx The value to parse.
706 * @return
707 * - 0 on success.
708 * - -1 on failure.
709 */
710static int _sql_map_proc_get_value(TALLOC_CTX *ctx, fr_pair_list_t *out,
711 request_t *request, map_t const *map, void *uctx)
712{
713 fr_pair_t *vp;
714 char const *value = uctx;
715
717 if (!vp) return -1;
718
719 /*
720 * Buffer not always talloced, sometimes it's
721 * just a pointer to a field in a result struct.
722 */
723 if (fr_pair_value_from_str(vp, value, strlen(value), NULL, true) < 0) {
724 RPEDEBUG("Failed parsing value \"%pV\" for attribute %s",
727 return -1;
728 }
730
731 return 0;
732}
733
734/*
735 * Verify the result of the map.
736 */
737static int sql_map_verify(CONF_SECTION *cs, UNUSED void const *mod_inst, UNUSED void *proc_inst,
738 tmpl_t const *src, UNUSED map_list_t const *maps)
739{
740 if (!src) {
741 cf_log_err(cs, "Missing SQL query");
742
743 return -1;
744 }
745
746 return 0;
747}
748
749#define MAX_SQL_FIELD_INDEX (64)
750
751/** Process the results of an SQL map query
752 *
753 * @param p_result Result of map expansion:
754 * - #RLM_MODULE_NOOP no rows were returned or columns matched.
755 * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
756 * - #RLM_MODULE_FAIL if a fault occurred.
757 * @param mpctx Map context, containing the module instance.
758 * @param request The current request.
759 * @param query string to execute.
760 * @param maps Head of the map list.
761 * @return UNLANG_ACTION_CALCULATE_RESULT
762 */
763static unlang_action_t mod_map_resume(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
764 UNUSED fr_value_box_list_t *query, map_list_t const *maps)
765{
766 fr_sql_query_t *query_ctx = talloc_get_type_abort(mpctx->rctx, fr_sql_query_t);
767 rlm_sql_t const *inst = mpctx->moi;
768 map_t const *map;
770 sql_rcode_t ret;
771 char const **fields = NULL, *map_rhs;
772 rlm_sql_row_t row;
773 int i, j, field_cnt, rows = 0;
774 int field_index[MAX_SQL_FIELD_INDEX];
775 char map_rhs_buff[128];
776 bool found_field = false; /* Did we find any matching fields in the result set ? */
777
778 if (query_ctx->rcode != RLM_SQL_OK) {
779 RERROR("SQL query failed: %s", fr_table_str_by_value(sql_rcode_description_table, query_ctx->rcode, "<INVALID>"));
780 rcode = RLM_MODULE_FAIL;
781 goto finish;
782 }
783
784 /*
785 * Not every driver provides an sql_num_rows function
786 */
787 if (inst->driver->sql_num_rows) {
788 ret = inst->driver->sql_num_rows(query_ctx, &inst->config);
789 if (ret == 0) {
790 RDEBUG2("Server returned an empty result");
791 rcode = RLM_MODULE_NOTFOUND;
792 goto finish;
793 }
794
795 if (ret < 0) {
796 RERROR("Failed retrieving row count");
797 error:
798 rcode = RLM_MODULE_FAIL;
799 goto finish;
800 }
801 }
802
803 for (i = 0; i < MAX_SQL_FIELD_INDEX; i++) field_index[i] = -1;
804
805 /*
806 * Map proc only registered if driver provides an sql_fields function
807 */
808 ret = (inst->driver->sql_fields)(&fields, query_ctx, &inst->config);
809 if (ret != RLM_SQL_OK) {
810 RERROR("Failed retrieving field names: %s", fr_table_str_by_value(sql_rcode_description_table, ret, "<INVALID>"));
811 goto error;
812 }
813 fr_assert(fields);
814 field_cnt = talloc_array_length(fields);
815
816 if (RDEBUG_ENABLED3) for (j = 0; j < field_cnt; j++) RDEBUG3("Got field: %s", fields[j]);
817
818 /*
819 * Iterate over the maps, it's O(N2)ish but probably
820 * faster than building a radix tree each time the
821 * map set is evaluated (map->rhs can be dynamic).
822 */
823 for (map = map_list_head(maps), i = 0;
824 map && (i < MAX_SQL_FIELD_INDEX);
825 map = map_list_next(maps, map), i++) {
826 /*
827 * Expand the RHS to get the name of the SQL field
828 */
829 if (tmpl_expand(&map_rhs, map_rhs_buff, sizeof(map_rhs_buff),
830 request, map->rhs) < 0) {
831 RPERROR("Failed getting field name");
832 goto error;
833 }
834
835 for (j = 0; j < field_cnt; j++) {
836 if (strcasecmp(fields[j], map_rhs) != 0) continue;
837 field_index[i] = j;
838 found_field = true;
839 }
840 }
841
842 /*
843 * Couldn't resolve any map RHS values to fields
844 * in the result set.
845 */
846 if (!found_field) {
847 RDEBUG2("No fields matching map found in query result");
848 rcode = RLM_MODULE_NOOP;
849 goto finish;
850 }
851
852 /*
853 * We've resolved all the maps to result indexes, now convert
854 * the values at those indexes into fr_pair_ts.
855 *
856 * Note: Not all SQL client libraries provide a row count,
857 * so we have to do the count here.
858 */
859 while ((inst->fetch_row(p_result, request, query_ctx) == UNLANG_ACTION_CALCULATE_RESULT) &&
860 (query_ctx->rcode == RLM_SQL_OK)) {
861 row = query_ctx->row;
862 rows++;
863 for (map = map_list_head(maps), j = 0;
864 map && (j < MAX_SQL_FIELD_INDEX);
865 map = map_list_next(maps, map), j++) {
866 if (field_index[j] < 0) continue; /* We didn't find the map RHS in the field set */
867 if (!row[field_index[j]]) {
868 RDEBUG2("Database returned NULL for %s", fields[j]);
869 continue;
870 }
871 if (map_to_request(request, map, _sql_map_proc_get_value, row[field_index[j]]) < 0) goto error;
872 }
873 }
874
875 if (query_ctx->rcode == RLM_SQL_ERROR) goto error;
876
877 if (rows == 0) {
878 RDEBUG2("SQL query returned no results");
879 rcode = RLM_MODULE_NOTFOUND;
880 }
881
882finish:
883 talloc_free(fields);
884 talloc_free(query_ctx);
885
886 RETURN_UNLANG_RCODE(rcode);
887}
888
889/** Executes a SELECT query and maps the result to server attributes
890 *
891 * @param p_result Result of map expansion:
892 * - #RLM_MODULE_NOOP no rows were returned or columns matched.
893 * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
894 * - #RLM_MODULE_FAIL if a fault occurred.
895 * @param mpctx Map context, containing the module instance.
896 * @param request The current request.
897 * @param query string to execute.
898 * @param maps Head of the map list.
899 * @return UNLANG_ACTION_CALCULATE_RESULT
900 */
901static unlang_action_t mod_map_proc(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
902 fr_value_box_list_t *query, UNUSED map_list_t const *maps)
903{
905 rlm_sql_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data, rlm_sql_thread_t);
906 fr_value_box_t *query_head = fr_value_box_list_head(query);
907 fr_sql_query_t *query_ctx = NULL;
908 fr_value_box_t *vb = NULL;
909 rlm_sql_escape_uctx_t *escape_uctx = NULL;
910
911 fr_assert(inst->driver->sql_fields); /* Should have been caught during validation... */
912
913 if (!query_head) {
914 REDEBUG("Query cannot be (null)");
916 }
917
918 while ((vb = fr_value_box_list_next(query, vb))) {
919 if (fr_value_box_is_safe_for(vb, inst->driver)) continue;
920 if (!escape_uctx) escape_uctx = sql_escape_uctx_alloc(request, inst);
921 sql_box_escape(vb, escape_uctx);
922 }
923
925 query_head, query, FR_TYPE_STRING,
927 SIZE_MAX) < 0) {
928 RPEDEBUG("Failed concatenating input string");
930 }
931
932 query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request,
933 thread->trunk, query_head->vb_strvalue, SQL_QUERY_SELECT);
934
935 if (unlang_map_yield(request, mod_map_resume, NULL, 0, query_ctx) != UNLANG_ACTION_YIELD) RETURN_UNLANG_FAIL;
936 return unlang_function_push_with_result(/* discard, mod_map_resume just uses query_ctx->rcode */ NULL,
937 request,
938 inst->select,
939 NULL,
940 NULL,
942 query_ctx);
943}
944
945/** xlat escape function for drivers which do not provide their own
946 *
947 */
948static ssize_t sql_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, void *arg)
949{
951 size_t len = 0;
952
953 while (in[0]) {
954 size_t utf8_len;
955
956 /*
957 * Allow all multi-byte UTF8 characters.
958 */
959 utf8_len = fr_utf8_char((uint8_t const *) in, -1);
960 if (utf8_len > 1) {
961 if (outlen <= utf8_len) break;
962
963 memcpy(out, in, utf8_len);
964 in += utf8_len;
965 out += utf8_len;
966
967 outlen -= utf8_len;
968 len += utf8_len;
969 continue;
970 }
971
972 /*
973 * Because we register our own escape function
974 * we're now responsible for escaping all special
975 * chars in an xlat expansion or attribute value.
976 */
977 switch (in[0]) {
978 case '\n':
979 if (outlen <= 2) break;
980 out[0] = '\\';
981 out[1] = 'n';
982
983 in++;
984 out += 2;
985 outlen -= 2;
986 len += 2;
987 break;
988
989 case '\r':
990 if (outlen <= 2) break;
991 out[0] = '\\';
992 out[1] = 'r';
993
994 in++;
995 out += 2;
996 outlen -= 2;
997 len += 2;
998 break;
999
1000 case '\t':
1001 if (outlen <= 2) break;
1002 out[0] = '\\';
1003 out[1] = 't';
1004
1005 in++;
1006 out += 2;
1007 outlen -= 2;
1008 len += 2;
1009 break;
1010 }
1011
1012 /*
1013 * Non-printable characters get replaced with their
1014 * mime-encoded equivalents.
1015 */
1016 if ((in[0] < 32) ||
1017 strchr(inst->config.allowed_chars, *in) == NULL) {
1018 /*
1019 * Only 3 or less bytes available.
1020 */
1021 if (outlen <= 3) {
1022 break;
1023 }
1024
1025 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
1026 in++;
1027 out += 3;
1028 outlen -= 3;
1029 len += 3;
1030 continue;
1031 }
1032
1033 /*
1034 * Only one byte left.
1035 */
1036 if (outlen <= 1) {
1037 break;
1038 }
1039
1040 /*
1041 * Allowed character.
1042 */
1043 *out = *in;
1044 out++;
1045 in++;
1046 outlen--;
1047 len++;
1048 }
1049 *out = '\0';
1050 return len;
1051}
1052
1053/*
1054 * Set the SQL user name.
1055 *
1056 * We don't call the escape function here. The resulting string
1057 * will be escaped later in the queries xlat so we don't need to
1058 * escape it twice. (it will make things wrong if we have an
1059 * escape candidate character in the username)
1060 */
1061static void sql_set_user(rlm_sql_t const *inst, request_t *request, fr_value_box_t *user)
1062{
1063 fr_pair_t *vp = NULL;
1064
1065 fr_assert(request->packet != NULL);
1066
1067 MEM(pair_update_request(&vp, inst->sql_user) >= 0);
1068 if(!user || (user->type != FR_TYPE_STRING)) {
1070 return;
1071 }
1072
1073 /*
1074 * Replace any existing SQL-User-Name with new value
1075 */
1076 fr_pair_value_bstrdup_buffer(vp, user->vb_strvalue, user->tainted);
1077 RDEBUG2("SQL-User-Name set to '%pV'", &vp->data);
1078}
1079
1080/*
1081 * Do a set/unset user, so it's a bit clearer what's going on.
1082 */
1083#define sql_unset_user(_i, _r) fr_pair_delete_by_da(&_r->request_pairs, _i->sql_user)
1084
1085
1090
1092{
1093 sql_group_ctx_t *group_ctx = talloc_get_type_abort(uctx, sql_group_ctx_t);
1094 fr_sql_query_t *query_ctx = group_ctx->query_ctx;
1095 rlm_sql_t const *inst = group_ctx->inst;
1096 rlm_sql_row_t row;
1097 rlm_sql_grouplist_t *entry = group_ctx->groups;
1098
1099 if (query_ctx->rcode != RLM_SQL_OK) {
1100 error:
1101 rlm_sql_print_error(inst, request, query_ctx, false);
1102 talloc_free(query_ctx);
1104 }
1105
1106 while ((inst->fetch_row(p_result, request, query_ctx) == UNLANG_ACTION_CALCULATE_RESULT) &&
1107 (query_ctx->rcode == RLM_SQL_OK)) {
1108 row = query_ctx->row;
1109 if (!row[0]){
1110 RDEBUG2("row[0] returned NULL");
1111 goto error;
1112 }
1113
1114 if (!entry) {
1115 group_ctx->groups = talloc_zero(group_ctx, rlm_sql_grouplist_t);
1116 entry = group_ctx->groups;
1117 } else {
1118 entry->next = talloc_zero(group_ctx, rlm_sql_grouplist_t);
1119 entry = entry->next;
1120 }
1121 entry->next = NULL;
1122 entry->name = talloc_typed_strdup(entry, row[0]);
1123
1124 group_ctx->num_groups++;
1125 }
1126
1127 talloc_free(query_ctx);
1129}
1130
1132{
1133 rlm_sql_t const *inst = group_ctx->inst;
1134
1135 /* NOTE: sql_set_user should have been run before calling this function */
1136
1137 if (!group_ctx->query || (group_ctx->query->vb_length == 0)) return UNLANG_ACTION_CALCULATE_RESULT;
1138
1139 MEM(group_ctx->query_ctx = fr_sql_query_alloc(group_ctx, inst, request, trunk,
1140 group_ctx->query->vb_strvalue, SQL_QUERY_SELECT));
1141
1142 if (unlang_function_push_with_result(/* sql_get_grouplist_resume translates the query_ctx->rocde into a module rcode */p_result,
1143 request,
1144 NULL,
1146 NULL,
1148 group_ctx) < 0) return UNLANG_ACTION_FAIL;
1149
1150 return unlang_function_push_with_result(/* discard, sql_get_grouplist_resume translates rcodes */NULL,
1151 request,
1152 inst->select,
1153 NULL,
1154 NULL, 0,
1155 UNLANG_SUB_FRAME, group_ctx->query_ctx);
1156}
1157
1158typedef struct {
1159 fr_value_box_list_t query;
1162
1163/** Compare list of groups returned from SQL query to xlat argument.
1164 *
1165 * Called after the SQL query has completed and group list has been built.
1166 */
1168 UNUSED request_t *request, fr_value_box_list_t *in)
1169{
1170 sql_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(xctx->rctx, sql_group_xlat_ctx_t);
1171 sql_group_ctx_t *group_ctx = talloc_get_type_abort(xlat_ctx->group_ctx, sql_group_ctx_t);
1172 fr_value_box_t *arg = fr_value_box_list_head(in);
1173 char const *name = arg->vb_strvalue;
1174 fr_value_box_t *vb;
1175 rlm_sql_grouplist_t *entry;
1176
1178
1180 for (entry = group_ctx->groups; entry != NULL; entry = entry->next) {
1181 if (strcmp(entry->name, name) == 0) {
1182 vb->vb_bool = true;
1183 break;
1184 }
1185 }
1187
1189
1190 return XLAT_ACTION_DONE;
1191}
1192
1193/** Run SQL query for group membership to return list of groups
1194 *
1195 * Called after group membership query tmpl is expanded
1196 */
1198 request_t *request, UNUSED fr_value_box_list_t *in)
1199{
1200 sql_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(xctx->rctx, sql_group_xlat_ctx_t);
1201 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
1202 rlm_sql_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_sql_thread_t);
1203 fr_value_box_t *query;
1204
1205 query = fr_value_box_list_head(&xlat_ctx->query);
1206 if (!query) return XLAT_ACTION_FAIL;
1207
1208 MEM(xlat_ctx->group_ctx = talloc(xlat_ctx, sql_group_ctx_t));
1209
1210 *xlat_ctx->group_ctx = (sql_group_ctx_t) {
1211 .inst = inst,
1212 .query = query,
1213 };
1214
1216
1217 if (sql_get_grouplist(NULL, xlat_ctx->group_ctx, thread->trunk, request) != UNLANG_ACTION_PUSHED_CHILD) {
1218 return XLAT_ACTION_FAIL;
1219 }
1220
1222}
1223
1224
1225/** Check if the user is a member of a particular group
1226 *
1227@verbatim
1228%sql.group(<name>)
1229@endverbatim
1230 *
1231 * @ingroup xlat_functions
1232 */
1234 request_t *request, UNUSED fr_value_box_list_t *in)
1235{
1236 sql_group_xlat_call_env_t *call_env = talloc_get_type_abort(xctx->env_data, sql_group_xlat_call_env_t);
1238 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
1239
1240 if (!call_env->membership_query) {
1241 RWARN("Cannot check group membership - group_membership_query not set");
1242 return XLAT_ACTION_FAIL;
1243 }
1244
1245 /*
1246 * Set the user attr here
1247 */
1248 sql_set_user(inst, request, &call_env->user);
1249
1251 fr_value_box_list_init(&xlat_ctx->query);
1252
1254 if (unlang_tmpl_push(xlat_ctx, NULL, &xlat_ctx->query, request, call_env->membership_query, NULL, UNLANG_SUB_FRAME) < 0) return XLAT_ACTION_FAIL;
1256}
1257
1258/** Process a "check" map
1259 *
1260 * Any entries using an assignment operator will be moved to the reply map
1261 * for later merging into the request.
1262 *
1263 * @param request Current request.
1264 * @param check_map to process.
1265 * @param reply_map where any assignment entries will be moved.
1266 * @return
1267 * - 0 if all the check entries pass.
1268 * - -1 if the checks fail.
1269 */
1270static int check_map_process(request_t *request, map_list_t *check_map, map_list_t *reply_map)
1271{
1272 map_t *map, *next;
1273
1274 for (map = map_list_head(check_map);
1275 map != NULL;
1276 map = next) {
1277 next = map_list_next(check_map, map);
1278
1279 if (fr_assignment_op[map->op]) {
1280 (void) map_list_remove(check_map, map);
1281 map_list_insert_tail(reply_map, map);
1282 continue;
1283 }
1284
1285 if (!fr_comparison_op[map->op]) {
1286 REDEBUG("Invalid operator '%s'", fr_tokens[map->op]);
1287 goto fail;
1288 }
1289
1290 if (fr_type_is_structural(tmpl_attr_tail_da(map->lhs)->type) &&
1291 (map->op != T_OP_CMP_TRUE) && (map->op != T_OP_CMP_FALSE)) {
1292 REDEBUG("Invalid comparison for structural type");
1293 goto fail;
1294 }
1295
1296 RDEBUG2(" &%s %s %s", map->lhs->name, fr_tokens[map->op], map->rhs->name);
1297 if (radius_legacy_map_cmp(request, map) != 1) {
1298 fail:
1299 map_list_talloc_free(check_map);
1300 map_list_talloc_free(reply_map);
1301 RDEBUG2("failed match: skipping this entry");
1302 return -1;
1303 }
1304 }
1305 return 0;
1306}
1307
1309{
1310 if (!to_free->inst->sql_escape_arg) (void) request_data_get(to_free->request, (void *)sql_escape_uctx_alloc, 0);
1311 map_list_talloc_free(&to_free->check_tmp);
1312 map_list_talloc_free(&to_free->reply_tmp);
1313 sql_unset_user(to_free->inst, to_free->request);
1314
1315 return 0;
1316}
1317
1318/** Resume function called after authorization group / profile expansion of check / reply query tmpl
1319 *
1320 * Groups and profiles are treated almost identically except:
1321 * - groups are read from an SQL query
1322 * - profiles are read from &control.User-Profile
1323 * - if `cache_groups` is set, groups populate &control.SQL-Group
1324 *
1325 * Profiles are handled after groups, and will not happend if the last group resulted in `Fall-Through = no`
1326 *
1327 * Before each query is run, &request.SQL-Group is populated with the value of the group being evaluated.
1328 *
1329 * @param p_result Result of current authorization.
1330 * @param mctx Current request.
1331 * @param request Current authorization context.
1332 * @return one of the RLM_MODULE_* values.
1333 */
1335{
1336 sql_autz_ctx_t *autz_ctx = talloc_get_type_abort(mctx->rctx, sql_autz_ctx_t);
1337 sql_autz_call_env_t *call_env = autz_ctx->call_env;
1338 sql_group_ctx_t *group_ctx = autz_ctx->group_ctx;
1339 fr_sql_map_ctx_t *map_ctx = autz_ctx->map_ctx;
1340 rlm_sql_t const *inst = autz_ctx->inst;
1341 fr_value_box_t *query = fr_value_box_list_pop_head(&autz_ctx->query);
1342 sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT;
1343 fr_pair_t *vp;
1344
1345 switch (p_result->rcode) {
1348
1349 default:
1350 break;
1351 }
1352
1353 switch(autz_ctx->status) {
1355 if (!query) RETURN_UNLANG_FAIL;
1357 MEM(autz_ctx->group_ctx = talloc(autz_ctx, sql_group_ctx_t));
1358 *autz_ctx->group_ctx = (sql_group_ctx_t) {
1359 .inst = inst,
1360 .query = query,
1361 };
1362
1363 if (sql_get_grouplist(p_result, autz_ctx->group_ctx, autz_ctx->trunk, request) == UNLANG_ACTION_PUSHED_CHILD) {
1366 }
1367
1368 group_ctx = autz_ctx->group_ctx;
1369
1371
1373 talloc_free(group_ctx->query);
1374
1375 if (group_ctx->num_groups == 0) {
1376 RDEBUG2("User not found in any groups");
1377 break;
1378 }
1379 fr_assert(group_ctx->groups);
1380
1381 RDEBUG2("User found in the group table");
1382 autz_ctx->user_found = true;
1383 autz_ctx->group = group_ctx->groups;
1384 MEM(pair_update_request(&autz_ctx->sql_group, inst->group_da) >= 0);
1385
1386 next_group:
1387 fr_pair_value_strdup(autz_ctx->sql_group, autz_ctx->group->name, true);
1388 autz_ctx->status = SQL_AUTZ_GROUP_CHECK;
1390
1392 next_profile:
1393 if (autz_ctx->status & SQL_AUTZ_STAGE_PROFILE) {
1394 fr_pair_value_strdup(autz_ctx->sql_group, autz_ctx->profile->vp_strvalue, true);
1395 autz_ctx->status = SQL_AUTZ_PROFILE_CHECK;
1396 }
1397 RDEBUG3("Processing %s %pV",
1398 autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "group" : "profile", &autz_ctx->sql_group->data);
1399 if (inst->config.cache_groups && autz_ctx->status & SQL_AUTZ_STAGE_GROUP) {
1400 MEM(pair_append_control(&vp, inst->group_da) >= 0);
1401 fr_pair_value_strdup(vp, autz_ctx->group->name, true);
1402 }
1403
1404 if (call_env->group_check_query) {
1406 if (unlang_tmpl_push(autz_ctx, NULL, &autz_ctx->query, request,
1409 }
1410
1411 if (call_env->group_reply_query) goto group_reply_push;
1412
1413 break;
1414
1417 if (!query) RETURN_UNLANG_FAIL;
1418 *autz_ctx->map_ctx = (fr_sql_map_ctx_t) {
1419 .ctx = autz_ctx,
1420 .inst = inst,
1421 .out = &autz_ctx->check_tmp,
1422 .list = request_attr_request,
1423 .query = query,
1424 };
1425
1427 if (sql_get_map_list(p_result, request, map_ctx, autz_ctx->trunk) == UNLANG_ACTION_PUSHED_CHILD) {
1430 }
1431
1433
1436 talloc_free(map_ctx->query);
1437
1438 /*
1439 * If we got check rows we need to process them before we decide to
1440 * process the reply rows
1441 */
1442 if (map_ctx->rows > 0) {
1443 if (check_map_process(request, &autz_ctx->check_tmp, &autz_ctx->reply_tmp) < 0) {
1444 map_list_talloc_free(&autz_ctx->check_tmp);
1445 goto next_group_find;
1446 }
1447 RDEBUG2("%s \"%pV\": Conditional check items matched",
1448 autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "Group" : "Profile", &autz_ctx->sql_group->data);
1449 } else {
1450 RDEBUG2("%s \"%pV\": Conditional check items matched (empty)",
1451 autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "Group" : "Profile", &autz_ctx->sql_group->data);
1452 }
1453
1454 if (autz_ctx->rcode == RLM_MODULE_NOOP) autz_ctx->rcode = RLM_MODULE_OK;
1455
1456 map_list_talloc_free(&autz_ctx->check_tmp);
1457
1458 if (call_env->group_reply_query) {
1459 group_reply_push:
1461 if (unlang_tmpl_push(autz_ctx, NULL, &autz_ctx->query, request,
1465 }
1466
1467 if (map_list_num_elements(&autz_ctx->reply_tmp)) goto group_attr_cache;
1468
1469 goto next_group_find;
1470
1473 if (!query) RETURN_UNLANG_FAIL;
1474 *autz_ctx->map_ctx = (fr_sql_map_ctx_t) {
1475 .ctx = autz_ctx,
1476 .inst = inst,
1477 .out = &autz_ctx->reply_tmp,
1478 .list = request_attr_reply,
1479 .query = query,
1480 .expand_rhs = true,
1481 };
1482
1484 if (sql_get_map_list(p_result, request, map_ctx, autz_ctx->trunk) == UNLANG_ACTION_PUSHED_CHILD) {
1487 }
1488
1490
1493 talloc_free(map_ctx->query);
1494
1495 if (map_ctx->rows == 0) {
1496 do_fall_through = FALL_THROUGH_DEFAULT;
1497 goto group_attr_cache;
1498 }
1499
1500 fr_assert(!map_list_empty(&autz_ctx->reply_tmp)); /* coverity, among others */
1501 do_fall_through = fall_through(&autz_ctx->reply_tmp);
1502
1503 group_attr_cache:
1504 if (inst->config.cache_groups && autz_ctx->status & SQL_AUTZ_STAGE_GROUP) {
1505 MEM(pair_append_control(&vp, inst->group_da) >= 0);
1506 fr_pair_value_strdup(vp, autz_ctx->group->name, true);
1507 }
1508
1509 if (map_list_num_elements(&autz_ctx->reply_tmp) == 0) goto next_group_find;
1510 RDEBUG2("%s \"%pV\": Merging control and reply items",
1511 autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "Group" : "Profile", &autz_ctx->sql_group->data);
1512 autz_ctx->rcode = RLM_MODULE_UPDATED;
1513
1514 RINDENT();
1515 if (radius_legacy_map_list_apply(request, &autz_ctx->reply_tmp, NULL) < 0) {
1516 RPEDEBUG("Failed applying reply item");
1517 REXDENT();
1519 }
1520 REXDENT();
1521 map_list_talloc_free(&autz_ctx->reply_tmp);
1522
1523 next_group_find:
1524 if (do_fall_through != FALL_THROUGH_YES) break;
1525 if (autz_ctx->status & SQL_AUTZ_STAGE_PROFILE) {
1526 autz_ctx->profile = fr_pair_find_by_da(&request->control_pairs, autz_ctx->profile, attr_user_profile);
1527 if (autz_ctx->profile) goto next_profile;
1528 break;
1529 }
1530 autz_ctx->group = autz_ctx->group->next;
1531 if (autz_ctx->group) goto next_group;
1532
1533 break;
1534
1535 default:
1536 fr_assert(0);
1537 }
1538
1539 /*
1540 * If group processing has completed, check to see if profile processing should be done
1541 */
1542 if ((autz_ctx->status & SQL_AUTZ_STAGE_GROUP) &&
1543 ((do_fall_through == FALL_THROUGH_YES) ||
1544 (inst->config.read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT)))) {
1545 RDEBUG3("... falling-through to profile processing");
1546
1547 autz_ctx->profile = fr_pair_find_by_da(&request->control_pairs, NULL, attr_user_profile);
1548 if (autz_ctx->profile) {
1549 MEM(pair_update_request(&autz_ctx->sql_group, inst->group_da) >= 0);
1550 autz_ctx->status = SQL_AUTZ_PROFILE_START;
1551 goto next_profile;
1552 }
1553 }
1554
1555 if (!autz_ctx->user_found) RETURN_UNLANG_NOTFOUND;
1556
1557 RETURN_UNLANG_RCODE(autz_ctx->rcode);
1558}
1559
1560/** Resume function called after authorization check / reply tmpl expansion
1561 *
1562 * @param p_result Result of current authorization.
1563 * @param mctx Module call ctx.
1564 * @param request Current request.
1565 * @return one of the RLM_MODULE_* values.
1566 */
1568{
1569 sql_autz_ctx_t *autz_ctx = talloc_get_type_abort(mctx->rctx, sql_autz_ctx_t);
1570 sql_autz_call_env_t *call_env = autz_ctx->call_env;
1571 rlm_sql_t const *inst = autz_ctx->inst;
1572 fr_value_box_t *query = fr_value_box_list_pop_head(&autz_ctx->query);
1573 sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT;
1574 fr_sql_map_ctx_t *map_ctx = autz_ctx->map_ctx;
1575
1576 /*
1577 * If a previous async call returned one of the "failure" results just return.
1578 */
1579 switch (p_result->rcode) {
1582
1583 default:
1584 break;
1585 }
1586
1587 switch(autz_ctx->status) {
1588 case SQL_AUTZ_CHECK:
1589 if (!query) RETURN_UNLANG_FAIL;
1590 *autz_ctx->map_ctx = (fr_sql_map_ctx_t) {
1591 .ctx = autz_ctx,
1592 .inst = inst,
1593 .out = &autz_ctx->check_tmp,
1594 .list = request_attr_request,
1595 .query = query,
1596 };
1597
1599 if (sql_get_map_list(p_result, request, map_ctx, autz_ctx->trunk) == UNLANG_ACTION_PUSHED_CHILD){
1600 autz_ctx->status = SQL_AUTZ_CHECK_RESUME;
1602 }
1603
1605
1607 talloc_free(map_ctx->query);
1608
1609 if (map_ctx->rows == 0) goto skip_reply; /* Don't need to handle map entries we don't have */
1610
1611 /*
1612 * Only do this if *some* check pairs were returned
1613 */
1614 RDEBUG2("User found in radcheck table");
1615 autz_ctx->user_found = true;
1616
1617 if (check_map_process(request, &autz_ctx->check_tmp, &autz_ctx->reply_tmp) < 0) goto skip_reply;
1618 RDEBUG2("Conditional check items matched");
1619
1620 autz_ctx->rcode = RLM_MODULE_OK;
1621 map_list_talloc_free(&autz_ctx->check_tmp);
1622
1623 if (!call_env->reply_query) goto skip_reply;
1624
1626 if (unlang_tmpl_push(autz_ctx, NULL, &autz_ctx->query, request, call_env->reply_query, NULL, UNLANG_SUB_FRAME) < 0) RETURN_UNLANG_FAIL;
1627 autz_ctx->status = SQL_AUTZ_REPLY;
1629
1630 case SQL_AUTZ_REPLY:
1631 if (!query) RETURN_UNLANG_FAIL;
1632 *autz_ctx->map_ctx = (fr_sql_map_ctx_t) {
1633 .ctx = autz_ctx,
1634 .inst = inst,
1635 .out = &autz_ctx->reply_tmp,
1636 .list = request_attr_reply,
1637 .query = query,
1638 .expand_rhs = true,
1639 };
1640
1642 if (sql_get_map_list(p_result, request, map_ctx, autz_ctx->trunk) == UNLANG_ACTION_PUSHED_CHILD){
1643 autz_ctx->status = SQL_AUTZ_REPLY_RESUME;
1645 }
1646
1648
1650 talloc_free(map_ctx->query);
1651
1652 if (map_ctx->rows == 0) goto skip_reply;
1653
1654 do_fall_through = fall_through(&autz_ctx->reply_tmp);
1655
1656 RDEBUG2("User found in radreply table");
1657 autz_ctx->user_found = true;
1658
1659 skip_reply:
1660 if (map_list_num_elements(&autz_ctx->reply_tmp)) {
1661 RDEBUG2("Merging control and reply items");
1662 RINDENT();
1663 if (radius_legacy_map_list_apply(request, &autz_ctx->reply_tmp, NULL) < 0) {
1664 RPEDEBUG("Failed applying item");
1665 REXDENT();
1667 }
1668 REXDENT();
1669
1670 autz_ctx->rcode = RLM_MODULE_UPDATED;
1671 map_list_talloc_free(&autz_ctx->reply_tmp);
1672 }
1673
1674 if ((do_fall_through == FALL_THROUGH_YES) ||
1675 (inst->config.read_groups && (do_fall_through == FALL_THROUGH_DEFAULT))) {
1676 RDEBUG3("... falling-through to group processing");
1677
1678 if (!call_env->membership_query) {
1679 RWARN("Cannot check groups when group_membership_query is not set");
1680 break;
1681 }
1682
1683 if (!call_env->group_check_query && !call_env->group_reply_query) {
1684 RWARN("Cannot process groups when neither authorize_group_check_query nor authorize_group_check_query are set");
1685 break;
1686 }
1687
1689 if (unlang_tmpl_push(autz_ctx, NULL, &autz_ctx->query, request,
1691 autz_ctx->status = SQL_AUTZ_GROUP_MEMB;
1693 }
1694
1695 if ((do_fall_through == FALL_THROUGH_YES) ||
1696 (inst->config.read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT))) {
1697 RDEBUG3("... falling-through to profile processing");
1698
1699 if (!call_env->group_check_query && !call_env->group_reply_query) {
1700 RWARN("Cannot process profiles when neither authorize_group_check_query nor authorize_group_check_query are set");
1701 break;
1702 }
1703
1704 autz_ctx->profile = fr_pair_find_by_da(&request->control_pairs, NULL, attr_user_profile);
1705 if (!autz_ctx->profile) break;
1706
1707 MEM(pair_update_request(&autz_ctx->sql_group, inst->group_da) >= 0);
1708 autz_ctx->status = SQL_AUTZ_PROFILE_START;
1709 return mod_autz_group_resume(p_result, mctx, request);
1710 }
1711 break;
1712
1713 default:
1714 fr_assert_msg(0, "Invalid status %d in mod_authorize_resume", autz_ctx->status);
1715 }
1716
1717 if (!autz_ctx->user_found) RETURN_UNLANG_NOTFOUND;
1718 RETURN_UNLANG_RCODE(autz_ctx->rcode);
1719}
1720
1721/** Start of module authorize method
1722 *
1723 * Pushes the tmpl relating to the first required query for evaluation
1724 */
1725static unlang_action_t CC_HINT(nonnull) mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1726{
1728 rlm_sql_thread_t *thread = talloc_get_type_abort(mctx->thread, rlm_sql_thread_t);
1729 sql_autz_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, sql_autz_call_env_t);
1730 sql_autz_ctx_t *autz_ctx;
1731
1732 fr_assert(request->packet != NULL);
1733 fr_assert(request->reply != NULL);
1734
1735 if (!call_env->check_query && !call_env->reply_query && !(inst->config.read_groups && call_env->membership_query)) {
1736 RWDEBUG("No authorization checks configured, returning noop");
1738 }
1739
1740 /*
1741 * Set and check the user attr here
1742 */
1743 sql_set_user(inst, request, &call_env->user);
1744
1745 MEM(autz_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), sql_autz_ctx_t));
1746 *autz_ctx = (sql_autz_ctx_t) {
1747 .inst = inst,
1748 .call_env = call_env,
1749 .request = request,
1750 .trunk = thread->trunk,
1751 .rcode = RLM_MODULE_NOOP
1752 };
1753 map_list_init(&autz_ctx->check_tmp);
1754 map_list_init(&autz_ctx->reply_tmp);
1755 MEM(autz_ctx->map_ctx = talloc_zero(autz_ctx, fr_sql_map_ctx_t));
1756 talloc_set_destructor(autz_ctx, sql_autz_ctx_free);
1757
1758 if (unlang_module_yield(request,
1760 NULL, 0, autz_ctx) == UNLANG_ACTION_FAIL) {
1761 error:
1762 talloc_free(autz_ctx);
1764 }
1765
1766 fr_value_box_list_init(&autz_ctx->query);
1767
1768 /*
1769 * Query the check table to find any conditions associated with this user/realm/whatever...
1770 */
1771 if (call_env->check_query) {
1772 if (unlang_tmpl_push(autz_ctx, NULL, &autz_ctx->query, request, call_env->check_query, NULL, UNLANG_SUB_FRAME) < 0) goto error;
1773 autz_ctx->status = SQL_AUTZ_CHECK;
1775 }
1776
1777 if (call_env->reply_query) {
1778 if (unlang_tmpl_push(autz_ctx, NULL, &autz_ctx->query, request, call_env->reply_query, NULL, UNLANG_SUB_FRAME) < 0) goto error;
1779 autz_ctx->status = SQL_AUTZ_REPLY;
1781 }
1782
1783 /*
1784 * Neither check nor reply queries were set, so we must be doing group stuff
1785 */
1786 if (unlang_tmpl_push(autz_ctx, NULL, &autz_ctx->query, request, call_env->membership_query, NULL, UNLANG_SUB_FRAME) < 0) goto error;
1787 autz_ctx->status = SQL_AUTZ_GROUP_MEMB;
1789}
1790
1791/** Tidy up when freeing an SQL redundant context
1792 *
1793 * Release the connection handle and unset the SQL-User attribute.
1794 */
1796{
1797 if (!to_free->inst->sql_escape_arg) (void) request_data_get(to_free->request, (void *)sql_escape_uctx_alloc, 0);
1798 sql_unset_user(to_free->inst, to_free->request);
1799
1800 return 0;
1801}
1802
1803static unlang_action_t mod_sql_redundant_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request);
1804
1805/** Resume function called after executing an SQL query in a redundant list of queries.
1806 *
1807 * @param p_result Result of current module call.
1808 * @param mctx Current module ctx.
1809 * @param request Current request.
1810 * @return one of the RLM_MODULE_* values.
1811 */
1813{
1814 sql_redundant_ctx_t *redundant_ctx = talloc_get_type_abort(mctx->rctx, sql_redundant_ctx_t);
1815 sql_redundant_call_env_t *call_env = redundant_ctx->call_env;
1816 rlm_sql_t const *inst = redundant_ctx->inst;
1817 fr_sql_query_t *query_ctx = redundant_ctx->query_ctx;
1818 int numaffected = 0;
1819
1820 RDEBUG2("SQL query returned: %s", fr_table_str_by_value(sql_rcode_description_table, query_ctx->rcode, "<INVALID>"));
1821
1822 switch (query_ctx->rcode) {
1823 /*
1824 * Query was a success! Now we just need to check if it did anything.
1825 */
1826 case RLM_SQL_OK:
1827 break;
1828
1829 /*
1830 * A general, unrecoverable server fault.
1831 */
1832 case RLM_SQL_ERROR:
1833 /*
1834 * If we get RLM_SQL_RECONNECT it means all connections in the pool
1835 * were exhausted, and we couldn't create a new connection,
1836 * so we do not need to call fr_pool_connection_release.
1837 */
1838 case RLM_SQL_RECONNECT:
1839 rlm_sql_print_error(inst, request, query_ctx, false);
1841
1842 /*
1843 * Query was invalid, this is a terminal error.
1844 */
1846 rlm_sql_print_error(inst, request, query_ctx, false);
1848
1849 /*
1850 * Driver found an error (like a unique key constraint violation)
1851 * that hinted it might be a good idea to try an alternative query.
1852 */
1853 case RLM_SQL_ALT_QUERY:
1854 goto next;
1855
1857 break;
1858 }
1859
1860 /*
1861 * We need to have updated something for the query to have been
1862 * counted as successful.
1863 */
1864 numaffected = (inst->driver->sql_affected_rows)(query_ctx, &inst->config);
1865 TALLOC_FREE(query_ctx);
1866 RDEBUG2("%i record(s) updated", numaffected);
1867
1868 if (numaffected > 0) {
1869 if (inst->query_number_da) {
1870 fr_pair_t *vp;
1871 if (unlikely(pair_update_control(&vp, inst->query_number_da) < 0)) RETURN_UNLANG_FAIL;
1872 vp->vp_uint32 = redundant_ctx->query_no + 1;
1873 RDEBUG2("control.%pP", vp);
1874 }
1875 RETURN_UNLANG_OK; /* A query succeeded, were done! */
1876 }
1877next:
1878 /*
1879 * Look to see if there are any more queries to expand
1880 */
1881 talloc_free(query_ctx);
1882 redundant_ctx->query_no++;
1883 if (redundant_ctx->query_no >= talloc_array_length(call_env->query)) RETURN_UNLANG_NOOP;
1885 if (unlang_tmpl_push(redundant_ctx, NULL, &redundant_ctx->query, request, call_env->query[redundant_ctx->query_no], NULL, UNLANG_SUB_FRAME) < 0) RETURN_UNLANG_FAIL;
1886
1887 RDEBUG2("Trying next query...");
1888
1890}
1891
1892
1893/** Resume function called after expansion of next query in a redundant list of queries
1894 *
1895 * @param p_result Result of current module call.
1896 * @param mctx Current module ctx.
1897 * @param request Current request.
1898 * @return one of the RLM_MODULE_* values.
1899 */
1901{
1902 sql_redundant_ctx_t *redundant_ctx = talloc_get_type_abort(mctx->rctx, sql_redundant_ctx_t);
1903 sql_redundant_call_env_t *call_env = redundant_ctx->call_env;
1904 rlm_sql_t const *inst = redundant_ctx->inst;
1905
1906 redundant_ctx->query_vb = fr_value_box_list_pop_head(&redundant_ctx->query);
1907 if (!redundant_ctx->query_vb) RETURN_UNLANG_FAIL;
1908
1909 if ((call_env->filename.type == FR_TYPE_STRING) && (call_env->filename.vb_length > 0)) {
1910 rlm_sql_query_log(inst, call_env->filename.vb_strvalue, redundant_ctx->query_vb->vb_strvalue);
1911 }
1912
1913 MEM(redundant_ctx->query_ctx = fr_sql_query_alloc(redundant_ctx, inst, request, redundant_ctx->trunk,
1914 redundant_ctx->query_vb->vb_strvalue, SQL_QUERY_OTHER));
1915
1916 unlang_module_yield(request, mod_sql_redundant_query_resume, NULL, 0, redundant_ctx);
1917 return unlang_function_push_with_result(/* discard, mod_sql_redundant_query_resume uses query_ctx->rcode*/ NULL,
1918 request,
1919 inst->query,
1920 NULL,
1921 NULL,
1923 redundant_ctx->query_ctx);
1924}
1925
1926/** Generic module call for failing between a bunch of queries.
1927 *
1928 * Used for `accounting` and `send` module calls
1929 *
1930 */
1931static unlang_action_t CC_HINT(nonnull) mod_sql_redundant(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1932{
1934 rlm_sql_thread_t *thread = talloc_get_type_abort(mctx->thread, rlm_sql_thread_t);
1935 sql_redundant_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, sql_redundant_call_env_t);
1936 sql_redundant_ctx_t *redundant_ctx;
1937
1938 /*
1939 * No query to expand - do nothing.
1940 */
1941 if (!call_env->query) {
1942 RWARN("No query configured");
1944 }
1945
1946 MEM(redundant_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), sql_redundant_ctx_t));
1947 *redundant_ctx = (sql_redundant_ctx_t) {
1948 .inst = inst,
1949 .request = request,
1950 .trunk = thread->trunk,
1951 .call_env = call_env,
1952 .query_no = 0
1953 };
1954 talloc_set_destructor(redundant_ctx, sql_redundant_ctx_free);
1955
1956 sql_set_user(inst, request, &call_env->user);
1957
1958 fr_value_box_list_init(&redundant_ctx->query);
1959
1960 return unlang_module_yield_to_tmpl(request, &redundant_ctx->query, request, *call_env->query,
1961 NULL, mod_sql_redundant_resume, NULL, 0, redundant_ctx);
1962}
1963
1964static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
1965 CONF_ITEM *ci,
1966 call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1967{
1968 CONF_SECTION const *subcs = NULL, *subsubcs = NULL;
1969 CONF_PAIR const *to_parse = NULL;
1970 tmpl_t *parsed_tmpl;
1971 call_env_parsed_t *parsed_env;
1972 tmpl_rules_t our_rules;
1973 char *section2, *p;
1974
1976
1977 /*
1978 * The call env subsection which calls this has CF_IDENT_ANY as its name
1979 * which results in finding the first child section of the module config.
1980 * We actually want the whole module config - so go to the parent.
1981 */
1982 ci = cf_parent(ci);
1983
1984 /*
1985 * Find the instance of "logfile" to parse
1986 *
1987 * If the module call is from `accounting Start` then first is
1988 * <module> { accounting { start { logfile } } }
1989 * then
1990 * <module> { accounting { logfile } }
1991 * falling back to
1992 * <module> { logfile }
1993 */
1995 if (subcs) {
1996 if (cec->asked->name2) {
1997 section2 = talloc_strdup(NULL, cec->asked->name2);
1998 p = section2;
1999 while (*p != '\0') {
2000 *(p) = tolower((uint8_t)*p);
2001 p++;
2002 }
2003 subsubcs = cf_section_find(subcs, section2, CF_IDENT_ANY);
2004 talloc_free(section2);
2005 if (subsubcs) to_parse = cf_pair_find(subsubcs, "logfile");
2006 }
2007 if (!to_parse) to_parse = cf_pair_find(subcs, "logfile");
2008 }
2009
2010 if (!to_parse) to_parse = cf_pair_find(cf_item_to_section(ci), "logfile");
2011
2012 if (!to_parse) return 0;
2013
2014 /*
2015 * Use filename safety escape functions
2016 */
2017 our_rules = *t_rules;
2021 .always_escape = false,
2022 };
2024 our_rules.literals_safe_for = our_rules.escape.box_escape.safe_for;
2025
2026 MEM(parsed_env = call_env_parsed_add(ctx, out,
2028
2029 if (tmpl_afrom_substr(parsed_env, &parsed_tmpl,
2030 &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
2032 &our_rules) < 0) {
2033 error:
2034 call_env_parsed_free(out, parsed_env);
2035 return -1;
2036 }
2037 if (tmpl_needs_resolving(parsed_tmpl) &&
2038 (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = our_rules.attr.dict_def }) < 0)) goto error;
2039
2040 call_env_parsed_set_tmpl(parsed_env, parsed_tmpl);
2041
2042 return 0;
2043}
2044
2045static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
2046 CONF_ITEM *ci,
2047 call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
2048{
2050 CONF_SECTION const *subcs = NULL;
2051 CONF_PAIR const *to_parse = NULL;
2052 tmpl_t *parsed_tmpl;
2053 call_env_parsed_t *parsed_env;
2054 tmpl_rules_t our_rules;
2055 char *section2, *p;
2056 ssize_t count, slen, multi_index = 0;
2057
2059
2060 /*
2061 * Find the instance(s) of "query" to parse
2062 *
2063 * If the module call is from `accounting Start` then it should be
2064 * <module> { accounting { start { query } } }
2065 */
2066 section2 = talloc_strdup(NULL, section_name_str(cec->asked->name2));
2067 p = section2;
2068 while (*p != '\0') {
2069 *(p) = tolower((uint8_t)*p);
2070 p++;
2071 }
2072 subcs = cf_section_find(cf_item_to_section(ci), section2, CF_IDENT_ANY);
2073 if (!subcs) {
2074 no_query:
2075 cf_log_warn(ci, "No query found for \"%s.%s\", this query will be disabled",
2076 section_name_str(cec->asked->name1), section2);
2077 talloc_free(section2);
2078 return 0;
2079 }
2080 count = cf_pair_count(subcs, "query");
2081 if (count == 0) goto no_query;
2082
2083 talloc_free(section2);
2084
2085 /*
2086 * Use module specific escape functions
2087 */
2088 our_rules = *t_rules;
2089 our_rules.escape = (tmpl_escape_t) {
2092 .safe_for = SQL_SAFE_FOR,
2093 .always_escape = false,
2094 },
2095 .uctx = { .func = { .uctx = inst, .alloc = sql_escape_uctx_alloc }, .type = TMPL_ESCAPE_UCTX_ALLOC_FUNC },
2096 .mode = TMPL_ESCAPE_PRE_CONCAT,
2097 };
2098 our_rules.literals_safe_for = our_rules.escape.box_escape.safe_for;
2099
2100 while ((to_parse = cf_pair_find_next(subcs, to_parse, "query"))) {
2101 MEM(parsed_env = call_env_parsed_add(ctx, out,
2105 }));
2106
2107 slen = tmpl_afrom_substr(parsed_env, &parsed_tmpl,
2108 &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
2110 &our_rules);
2111 if (slen <= 0) {
2112 cf_canonicalize_error(to_parse, slen, "Failed parsing query", cf_pair_value(to_parse));
2113 error:
2114 call_env_parsed_free(out, parsed_env);
2115 return -1;
2116 }
2117 if (tmpl_needs_resolving(parsed_tmpl) &&
2118 (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = our_rules.attr.dict_def }) < 0)) {
2119 cf_log_perr(to_parse, "Failed resolving query");
2120 goto error;
2121 }
2122
2123 call_env_parsed_set_multi_index(parsed_env, count, multi_index++);
2124 call_env_parsed_set_data(parsed_env, parsed_tmpl);
2125 }
2126
2127 return 0;
2128}
2129
2130static int mod_detach(module_detach_ctx_t const *mctx)
2131{
2132 rlm_sql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_t);
2133
2134 /*
2135 * We need to explicitly free all children, so if the driver
2136 * parented any memory off the instance, their destructors
2137 * run before we unload the bytecode for them.
2138 *
2139 * If we don't do this, we get a SEGV deep inside the talloc code
2140 * when it tries to call a destructor that no longer exists.
2141 */
2142 talloc_free_children(inst);
2143
2144 return 0;
2145}
2146
2147static int mod_instantiate(module_inst_ctx_t const *mctx)
2148{
2149 rlm_sql_boot_t const *boot = talloc_get_type_abort(mctx->mi->boot, rlm_sql_boot_t);
2150 rlm_sql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_t);
2151 CONF_SECTION *conf = mctx->mi->conf;
2152
2153 /*
2154 * We can't modify the inst field in bootstrap, and there's no
2155 * point in making rlm_sql_boot_t available everywhere.
2156 */
2157 inst->group_da = boot->group_da;
2158 inst->query_number_da = boot->query_number_da;
2159
2160 inst->name = mctx->mi->name; /* Need this for functions in sql.c */
2161 inst->mi = mctx->mi; /* For looking up thread instance data */
2162
2163 /*
2164 * We need authorize_group_check_query or authorize_group_reply_query
2165 * if group_membership_query is set.
2166 *
2167 * Or we need group_membership_query if authorize_group_check_query or
2168 * authorize_group_reply_query is set.
2169 */
2170 if (!cf_pair_find(conf, "group_membership_query")) {
2171 if (cf_pair_find(conf, "authorize_group_check_query")) {
2172 WARN("Ignoring authorize_group_check_query as group_membership_query is not configured");
2173 }
2174
2175 if (cf_pair_find(conf, "authorize_group_reply_query")) {
2176 WARN("Ignoring authorize_group_reply_query as group_membership_query is not configured");
2177 }
2178
2179 if (!inst->config.read_groups) {
2180 WARN("Ignoring read_groups as group_membership_query is not configured");
2181 inst->config.read_groups = false;
2182 }
2183 } /* allow the group check / reply queries to be NULL */
2184
2185 /*
2186 * Cache the SQL-User-Name fr_dict_attr_t, so we can be slightly
2187 * more efficient about creating SQL-User-Name attributes.
2188 */
2189 inst->sql_user = attr_sql_user_name;
2190
2191 /*
2192 * Export these methods, too. This avoids RTDL_GLOBAL.
2193 */
2194 inst->query = rlm_sql_trunk_query;
2195 inst->select = rlm_sql_trunk_query;
2196 inst->fetch_row = rlm_sql_fetch_row;
2197 inst->query_alloc = fr_sql_query_alloc;
2198
2199 /*
2200 * Either use the module specific escape function
2201 * or our default one.
2202 */
2203 if (inst->driver->sql_escape_func) {
2204 inst->sql_escape_func = inst->driver->sql_escape_func;
2205 } else {
2206 inst->sql_escape_func = sql_escape_func;
2207 inst->sql_escape_arg = inst;
2208 }
2209 inst->box_escape = (fr_value_box_escape_t) {
2210 .func = sql_box_escape,
2211 .safe_for = SQL_SAFE_FOR,
2212 .always_escape = false,
2213 };
2214
2215 inst->ef = module_rlm_exfile_init(inst, conf, 256, fr_time_delta_from_sec(30), true, false, NULL, NULL);
2216 if (!inst->ef) {
2217 cf_log_err(conf, "Failed creating log file context");
2218 return -1;
2219 }
2220
2221 /*
2222 * Most SQL trunks can only have one running request per connection.
2223 */
2224 if (!(inst->driver->flags & RLM_SQL_MULTI_QUERY_CONN)) {
2225 inst->config.trunk_conf.target_req_per_conn = 1;
2226 inst->config.trunk_conf.max_req_per_conn = 1;
2227 }
2228 if (!inst->driver->trunk_io_funcs.connection_notify) {
2229 inst->config.trunk_conf.always_writable = true;
2230 }
2231
2232 /*
2233 * Instantiate the driver module
2234 */
2235 if (unlikely(module_instantiate(inst->driver_submodule) < 0)) {
2236 cf_log_err(conf, "Failed instantiating driver module");
2237 return -1;
2238 }
2239
2240 if (!inst->config.trunk_conf.conn_triggers) return 0;
2241
2242 MEM(inst->trigger_args = fr_pair_list_alloc(inst));
2243 return module_trigger_args_build(inst->trigger_args, inst->trigger_args, cf_section_find(conf, "pool", NULL),
2245 .module = inst->mi->module->name,
2246 .name = inst->name,
2247 .server = inst->config.sql_server,
2248 .port = inst->config.sql_port
2249 });
2250}
2251
2252static int mod_bootstrap(module_inst_ctx_t const *mctx)
2253{
2254 rlm_sql_boot_t *boot = talloc_get_type_abort(mctx->mi->boot, rlm_sql_boot_t);
2255 rlm_sql_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_t);
2256 CONF_SECTION *conf = mctx->mi->conf;
2257 xlat_t *xlat;
2258 xlat_arg_parser_t *sql_xlat_arg;
2260
2261 /*
2262 * Register the group comparison attribute
2263 */
2264 if (cf_pair_find(conf, "group_membership_query")) {
2265 char const *group_attribute;
2266 char buffer[256];
2267
2268 if (inst->config.group_attribute) {
2269 group_attribute = inst->config.group_attribute;
2270 } else if (cf_section_name2(conf)) {
2271 snprintf(buffer, sizeof(buffer), "%s-SQL-Group", mctx->mi->name);
2272 group_attribute = buffer;
2273 } else {
2274 group_attribute = "SQL-Group";
2275 }
2276
2277 boot->group_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), group_attribute);
2278 if (!boot->group_da) {
2280 cf_log_perr(conf, "Failed defining group attribute");
2281 return -1;
2282 }
2283
2284 boot->group_da = fr_dict_attr_search_by_qualified_oid(NULL, dict_freeradius, group_attribute,
2285 false, false);
2286 if (!boot->group_da) {
2287 cf_log_perr(conf, "Failed resolving group attribute");
2288 return -1;
2289 }
2290 }
2291
2292 /*
2293 * Define the new %sql.group(name) xlat. The
2294 * register function automatically adds the
2295 * module instance name as a prefix.
2296 */
2297 xlat = module_rlm_xlat_register(boot, mctx, "group", sql_group_xlat, FR_TYPE_BOOL);
2298 if (!xlat) {
2299 cf_log_perr(conf, "Failed registering %s expansion", group_attribute);
2300 return -1;
2301 }
2303
2304 /*
2305 * The xlat escape function needs access to inst - so
2306 * argument parser details need to be defined here
2307 */
2308 sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2);
2309 sql_xlat_arg[0] = (xlat_arg_parser_t){
2311 .required = true,
2312 .concat = true
2313 };
2315
2316 xlat_func_args_set(xlat, sql_xlat_arg);
2317 }
2318
2319 /*
2320 * If we need to record which query from a redundant set succeeds, find / create the attribute to use.
2321 */
2322 if (inst->config.query_number_attribute) {
2324 inst->config.query_number_attribute);
2325 if (!boot->query_number_da) {
2327 inst->config.query_number_attribute, FR_TYPE_UINT32, NULL) < 0) {
2328 ERROR("Failed defining query number attribute \"%s\"", inst->config.query_number_attribute);
2329 return -1;
2330 }
2331
2333 inst->config.query_number_attribute,
2334 false, false);
2335 if (!boot->query_number_da) {
2336 ERROR("Failed resolving query number attribute \"%s\"", inst->config.query_number_attribute);
2337 return -1;
2338 }
2339 } else {
2340 if (boot->query_number_da->type != FR_TYPE_UINT32) {
2341 ERROR("Query number attribute \"%s\" is type \"%s\", needs to be uint32",
2342 inst->config.query_number_attribute, fr_type_to_str(boot->query_number_da->type));
2343 return -1;
2344 }
2345 }
2346 }
2347
2348 /*
2349 * Register the SQL xlat function
2350 */
2351 xlat = module_rlm_xlat_register(boot, mctx, NULL, sql_xlat, FR_TYPE_VOID); /* Returns an integer sometimes */
2352 if (!xlat) {
2353 cf_log_perr(conf, "Failed registering %s expansion", mctx->mi->name);
2354 return -1;
2355 }
2357
2358 /*
2359 * The xlat escape function needs access to inst - so
2360 * argument parser details need to be defined here.
2361 * Parented off the module instance "boot" so it can be shared
2362 * between three xlats.
2363 */
2364 MEM(sql_xlat_arg = talloc_zero_array(boot, xlat_arg_parser_t, 2));
2365 MEM(uctx = talloc_zero(sql_xlat_arg, rlm_sql_escape_uctx_t));
2366 *uctx = (rlm_sql_escape_uctx_t){ .sql = inst };
2367 sql_xlat_arg[0] = (xlat_arg_parser_t){
2369 .required = true,
2370 .concat = true,
2371 .func = sql_xlat_escape,
2372 .safe_for = SQL_SAFE_FOR,
2373 .uctx = uctx
2374 };
2376
2377 xlat_func_args_set(xlat, sql_xlat_arg);
2378
2379 /*
2380 * Register instances of the SQL xlat with pre-determined output types
2381 */
2382 if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "fetch", sql_fetch_xlat, FR_TYPE_VOID)))) return -1;
2384 xlat_func_args_set(xlat, sql_xlat_arg);
2385
2386 if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "modify", sql_modify_xlat, FR_TYPE_UINT32)))) return -1;
2388 xlat_func_args_set(xlat, sql_xlat_arg);
2389
2390 if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "escape", sql_escape_xlat, FR_TYPE_STRING)))) return -1;
2391 sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2);
2392 sql_xlat_arg[0] = (xlat_arg_parser_t){
2394 .variadic = true,
2395 .concat = true,
2396 };
2398 xlat_func_args_set(xlat, sql_xlat_arg);
2401
2402 if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "safe", xlat_transparent, FR_TYPE_STRING)))) return -1;
2403 sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2);
2404 sql_xlat_arg[0] = (xlat_arg_parser_t){
2406 .variadic = true,
2407 .concat = true
2408 };
2410 xlat_func_args_set(xlat, sql_xlat_arg);
2413
2414 /*
2415 * Register the SQL map processor function
2416 */
2417 if (inst->driver->sql_fields) map_proc_register(mctx->mi->boot, inst, mctx->mi->name, mod_map_proc, sql_map_verify, 0, SQL_SAFE_FOR);
2418
2419 return 0;
2420}
2421
2422/** Initialise thread specific data structure
2423 *
2424 */
2426{
2427 rlm_sql_thread_t *thread = talloc_get_type_abort(mctx->thread, rlm_sql_thread_t);
2428 rlm_sql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_t);
2429
2430 if (inst->driver->sql_escape_arg_alloc) {
2431 thread->sql_escape_arg = inst->driver->sql_escape_arg_alloc(thread, mctx->el, inst);
2432 if (!thread->sql_escape_arg) return -1;
2433 }
2434
2435 thread->inst = inst;
2436
2437 thread->trunk = trunk_alloc(thread, mctx->el, &inst->driver->trunk_io_funcs,
2438 &inst->config.trunk_conf, inst->name, thread, false, inst->trigger_args);
2439 if (!thread->trunk) return -1;
2440
2441 return 0;
2442}
2443
2445{
2446 rlm_sql_thread_t *thread = talloc_get_type_abort(mctx->thread, rlm_sql_thread_t);
2447 rlm_sql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_t);
2448
2449 if (inst->driver->sql_escape_arg_free) inst->driver->sql_escape_arg_free(thread->sql_escape_arg);
2450
2451 return 0;
2452}
2453
2454/** Custom parser for sql call env queries
2455 *
2456 * Needed as the escape function needs to reference the correct SQL driver
2457 */
2458static int sql_call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
2459 call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
2460{
2462 tmpl_t *parsed_tmpl;
2463 CONF_PAIR const *to_parse = cf_item_to_pair(ci);
2464 tmpl_rules_t our_rules = *t_rules;
2465
2466 /*
2467 * Set the sql module instance data as the uctx for escaping
2468 * and use the same "safe_for" as the sql module.
2469 */
2472 .safe_for = SQL_SAFE_FOR,
2473 .always_escape = false,
2474 };
2475 our_rules.escape.uctx.func.uctx = inst;
2476 our_rules.literals_safe_for = SQL_SAFE_FOR;
2477
2478 if (tmpl_afrom_substr(ctx, &parsed_tmpl,
2479 &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
2481 &our_rules) < 0) return -1;
2482 *(void **)out = parsed_tmpl;
2483 return 0;
2484}
2485
2486#define QUERY_ESCAPE .pair.escape = { \
2487 .mode = TMPL_ESCAPE_PRE_CONCAT, \
2488 .uctx = { .func = { .alloc = sql_escape_uctx_alloc }, .type = TMPL_ESCAPE_UCTX_ALLOC_FUNC }, \
2489}, .pair.func = sql_call_env_parse
2490
2503
2504/* globally exported name */
2506 .common = {
2507 .magic = MODULE_MAGIC_INIT,
2508 .name = "sql",
2509 .boot_size = sizeof(rlm_sql_boot_t),
2510 .boot_type = "rlm_sql_boot_t",
2511 .inst_size = sizeof(rlm_sql_t),
2512 .config = module_config,
2513 .bootstrap = mod_bootstrap,
2514 .instantiate = mod_instantiate,
2515 .detach = mod_detach,
2516 .thread_inst_size = sizeof(rlm_sql_thread_t),
2519 },
2520 .method_group = {
2521 .bindings = (module_method_binding_t[]){
2522 /*
2523 * Hack to support old configurations
2524 */
2525 { .section = SECTION_NAME("accounting", CF_IDENT_ANY), .method = mod_sql_redundant, .method_env = &accounting_method_env },
2526 { .section = SECTION_NAME("authorize", CF_IDENT_ANY), .method = mod_authorize, .method_env = &authorize_method_env },
2527
2528 { .section = SECTION_NAME("recv", CF_IDENT_ANY), .method = mod_authorize, .method_env = &authorize_method_env },
2529 { .section = SECTION_NAME("send", CF_IDENT_ANY), .method = mod_sql_redundant, .method_env = &send_method_env },
2531 }
2532 }
2533};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition action.h:41
static int const char char buffer[256]
Definition acutest.h:578
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:221
#define RCSID(id)
Definition build.h:485
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:324
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
void call_env_parsed_free(call_env_parsed_head_t *parsed, call_env_parsed_t *ptr)
Remove a call_env_parsed_t from the list of parsed call envs.
Definition call_env.c:775
call_env_parsed_t * call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs.
Definition call_env.c:688
void call_env_parsed_set_multi_index(call_env_parsed_t *parsed, size_t count, size_t index)
Assign a count and index to a call_env_parsed_t.
Definition call_env.c:760
void call_env_parsed_set_data(call_env_parsed_t *parsed, void const *data)
Assign data to a call_env_parsed_t.
Definition call_env.c:745
void call_env_parsed_set_tmpl(call_env_parsed_t *parsed, tmpl_t const *tmpl)
Assign a tmpl to a call_env_parsed_t.
Definition call_env.c:717
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
call_env_ctx_type_t type
Type of callenv ctx.
Definition call_env.h:227
@ CALL_ENV_CTX_TYPE_MODULE
The callenv is registered to a module method.
Definition call_env.h:222
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition call_env.h:240
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:247
section_name_t const * asked
The actual name1/name2 that resolved to a module_method_binding_t.
Definition call_env.h:232
@ CALL_ENV_FLAG_CONCAT
If the tmpl produced multiple boxes they should be concatenated.
Definition call_env.h:76
@ CALL_ENV_FLAG_SUBSECTION
This is a subsection.
Definition call_env.h:87
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition call_env.h:85
@ CALL_ENV_FLAG_MULTI
Multiple instances of the conf pairs are allowed.
Definition call_env.h:78
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition call_env.h:80
module_instance_t const * mi
Module instance that the callenv is registered to.
Definition call_env.h:229
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
Definition call_env.h:412
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition call_env.h:340
#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field)
Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified.
Definition call_env.h:389
Per method call config.
Definition call_env.h:180
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:660
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:614
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:283
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:271
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition cf_parse.h:312
@ CONF_FLAG_SECRET
Only print value if debug level >= 3.
Definition cf_parse.h:436
@ CONF_FLAG_HIDDEN
Used by scripts to omit items from the generated documentation.
Definition cf_parse.h:458
#define FR_CONF_OFFSET_TYPE_FLAGS(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:241
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:597
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_PAIR * cf_pair_find_next(CONF_SECTION const *cs, CONF_PAIR const *prev, char const *attr)
Find a pair with a name matching attr, after specified pair.
Definition cf_util.c:1440
unsigned int cf_pair_count(CONF_SECTION const *cs, char const *attr)
Count the number of times an attribute occurs in a parent section.
Definition cf_util.c:1507
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1184
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1027
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition cf_util.c:1426
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1625
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1581
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:286
#define cf_parent(_cf)
Definition cf_util.h:101
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:364
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:293
#define cf_log_warn(_cf, _fmt,...)
Definition cf_util.h:287
#define CF_IDENT_ANY
Definition cf_util.h:78
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:408
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:202
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
fr_dict_attr_t const * fr_dict_attr_search_by_qualified_oid(fr_dict_attr_err_t *err, fr_dict_t const *dict_def, char const *attr, bool internal, bool foreign))
Locate a qualified fr_dict_attr_t by its name and a dictionary qualifier.
Definition dict_util.c:3195
int fr_dict_attr_add_name_only(fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name, fr_type_t type, fr_dict_attr_flags_t const *flags))
Add an attribute to the dictionary.
Definition dict_util.c:1834
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4742
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3377
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2514
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:289
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:302
char const * fr_dict_enum_name_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
Lookup the name of an enum value in a fr_dict_attr_t.
Definition dict_util.c:3537
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:308
static fr_slen_t in
Definition dict.h:866
Specifies an attribute which must be present for the module to function.
Definition dict.h:288
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:301
Test enumeration values.
Definition dict_test.h:92
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define unlang_function_push_with_result(_result_p, _request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack that produces a result.
Definition function.h:144
static xlat_action_t sql_escape_xlat(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Escape a value to make it SQL safe.
Definition rlm_sql.c:454
static xlat_action_t sql_group_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Check if the user is a member of a particular group.
Definition rlm_sql.c:1233
static xlat_action_t sql_modify_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Execute an arbitrary SQL query, returning the number of rows affected.
Definition rlm_sql.c:676
static xlat_action_t sql_fetch_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Execute an arbitrary SQL query, expecting results to be returned.
Definition rlm_sql.c:642
static xlat_action_t sql_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Execute an arbitrary SQL query.
Definition rlm_sql.c:578
unlang_result_t * unlang_interpret_result(request_t *request)
Get the last instruction result OR the last frame that was popped.
Definition interpret.c:1558
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1658
#define UNLANG_SUB_FRAME
Definition interpret.h:37
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:134
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:443
#define RWDEBUG(fmt,...)
Definition log.h:361
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:335
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RWARN(fmt,...)
Definition log.h:297
#define RERROR(fmt,...)
Definition log.h:298
#define RPERROR(fmt,...)
Definition log.h:302
#define RPEDEBUG(fmt,...)
Definition log.h:376
#define RINDENT()
Indent R* messages by one level.
Definition log.h:430
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition map.c:1872
int rad_filename_box_make_safe(fr_value_box_t *vb, UNUSED void *uxtc)
Definition util.c:161
unlang_action_t unlang_map_yield(request_t *request, map_proc_func_t resume, unlang_map_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition map.c:110
talloc_free(reap)
static TALLOC_CTX * map_ctx
Definition map_builtin.c:32
int map_proc_register(TALLOC_CTX *ctx, void const *mod_inst, char const *name, map_proc_func_t evaluate, map_proc_instantiate_t instantiate, size_t inst_size, fr_value_box_safe_for_t literals_safe_for)
Register a map processor.
Definition map_proc.c:125
void * rctx
Resume ctx that a module previously set.
Definition map_proc.h:53
void const * moi
Map module instance.
Definition map_proc.h:54
Temporary structure to hold arguments for map calls.
Definition map_proc.h:52
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_BOOL
A truth value.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:36
int strcasecmp(char *s1, char *s2)
Definition missing.c:66
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * thread
Thread specific instance data.
Definition module_ctx.h:43
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition module_ctx.h:68
module_instance_t * mi
Module instance to detach.
Definition module_ctx.h:57
void * thread
Thread instance data.
Definition module_ctx.h:67
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:64
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for detach calls.
Definition module_ctx.h:56
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition module_ctx.h:63
exfile_t * module_rlm_exfile_init(TALLOC_CTX *ctx, CONF_SECTION *module, uint32_t max_entries, fr_time_delta_t max_idle, bool locking, bool triggers, char const *trigger_prefix, fr_pair_list_t *trigger_args)
Initialise a module specific exfile handle.
Definition module_rlm.c:103
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition module_rlm.c:247
int module_rlm_submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Generic conf_parser_t func for loading drivers.
Definition module_rlm.c:943
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition pair.c:2648
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, UNUSED bool tainted)
Convert string value to native attribute value.
Definition pair.c:2602
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:708
fr_pair_list_t * fr_pair_list_alloc(TALLOC_CTX *ctx)
Allocate a new pair list on the heap.
Definition pair.c:119
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1353
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:287
int radius_legacy_map_cmp(request_t *request, map_t const *map)
Definition pairmove.c:792
int radius_legacy_map_list_apply(request_t *request, map_list_t const *list, fr_edit_list_t *el)
Definition pairmove.c:773
size_t fr_utf8_char(uint8_t const *str, ssize_t inlen)
Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8.
Definition print.c:39
#define fr_assert(_expr)
Definition rad_assert.h:38
#define pair_update_request(_attr, _da)
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define WARN(fmt,...)
Definition radclient.h:47
static void thread_detach(UNUSED void *uctx)
Explicitly cleanup module/xlat resources.
Definition radiusd.c:150
static int thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el, UNUSED void *uctx)
Create module and xlat per-thread instances.
Definition radiusd.c:133
static rs_t * conf
Definition radsniff.c:53
#define RLM_MODULE_USER_SECTION_REJECT
Rcodes that translate to a user configurable section failing overall.
Definition rcode.h:75
#define RETURN_UNLANG_INVALID
Definition rcode.h:62
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:57
#define RETURN_UNLANG_NOTFOUND
Definition rcode.h:64
#define RETURN_UNLANG_FAIL
Definition rcode.h:59
#define RETURN_UNLANG_OK
Definition rcode.h:60
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:45
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:44
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:49
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:51
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:50
#define RETURN_UNLANG_NOOP
Definition rcode.h:65
fr_dict_attr_t const * request_attr_request
Definition request.c:43
fr_dict_attr_t const * request_attr_reply
Definition request.c:44
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
static char const * name
fr_value_box_t user
Definition rlm_sql.c:250
rlm_sql_t const * inst
Module instance.
Definition rlm_sql.c:239
static int sql_map_verify(CONF_SECTION *cs, UNUSED void const *mod_inst, UNUSED void *proc_inst, tmpl_t const *src, UNUSED map_list_t const *maps)
Definition rlm_sql.c:737
static int mod_detach(module_detach_ctx_t const *mctx)
Definition rlm_sql.c:2130
static unlang_action_t mod_autz_group_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Resume function called after authorization group / profile expansion of check / reply query tmpl.
Definition rlm_sql.c:1334
fr_sql_query_t * query_ctx
Query context.
Definition rlm_sql.c:185
sql_group_ctx_t * group_ctx
Definition rlm_sql.c:1160
rlm_sql_t const * inst
Module instance.
Definition rlm_sql.c:183
sql_redundant_call_env_t * call_env
Call environment data.
Definition rlm_sql.c:242
#define QUERY_ESCAPE
Definition rlm_sql.c:2486
tmpl_t * check_query
Tmpl to expand to form authorize_check_query.
Definition rlm_sql.c:120
static sql_fall_through_t fall_through(map_list_t *maps)
Definition rlm_sql.c:304
rlm_sql_grouplist_t * group
Current group being processed.
Definition rlm_sql.c:203
fr_value_box_t filename
File name to write SQL logs to.
Definition rlm_sql.c:212
static int _sql_map_proc_get_value(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
Converts a string value into a fr_pair_t.
Definition rlm_sql.c:710
request_t * request
Request being processed.
Definition rlm_sql.c:240
size_t query_no
Current query number.
Definition rlm_sql.c:243
static const call_env_method_t authorize_method_env
Definition rlm_sql.c:2491
tmpl_t * reply_query
Tmpl to expand to form authorize_reply_query.
Definition rlm_sql.c:121
static int _sql_escape_uxtx_free(void *uctx)
Definition rlm_sql.c:277
static fr_dict_attr_t const * attr_sql_user_name
Definition rlm_sql.c:105
fr_dict_attr_t const * query_number_da
Definition rlm_sql.c:61
static unlang_action_t sql_get_grouplist_resume(unlang_result_t *p_result, request_t *request, void *uctx)
Definition rlm_sql.c:1091
#define SQL_SAFE_FOR
Definition rlm_sql.c:53
static unlang_action_t mod_authorize_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Resume function called after authorization check / reply tmpl expansion.
Definition rlm_sql.c:1567
static int check_map_process(request_t *request, map_list_t *check_map, map_list_t *reply_map)
Process a "check" map.
Definition rlm_sql.c:1270
#define sql_unset_user(_i, _r)
Definition rlm_sql.c:1083
static fr_dict_attr_t const * attr_fall_through
Definition rlm_sql.c:104
static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc, call_env_ctx_t const *cec, call_env_parser_t const *rule)
static fr_dict_t const * dict_freeradius
Definition rlm_sql.c:96
static xlat_action_t sql_group_xlat_resume(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Run SQL query for group membership to return list of groups.
Definition rlm_sql.c:1197
fr_sql_map_ctx_t * map_ctx
Context used for retrieving attribute value pairs as a map list.
Definition rlm_sql.c:206
static xlat_action_t sql_xlat_select_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_sql.c:512
static void sql_set_user(rlm_sql_t const *inst, request_t *request, fr_value_box_t *user)
Definition rlm_sql.c:1061
rlm_sql_grouplist_t * groups
List of groups retrieved.
Definition rlm_sql.c:186
fr_value_box_t filename
Definition rlm_sql.c:134
static xlat_action_t sql_group_xlat_query_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Compare list of groups returned from SQL query to xlat argument.
Definition rlm_sql.c:1167
static fr_dict_attr_t const * attr_expr_bool_enum
Definition rlm_sql.c:107
fr_value_box_t user
Expansion of the sql_user_name.
Definition rlm_sql.c:119
fr_pair_t * sql_group
Pair to update with group being processed.
Definition rlm_sql.c:204
trunk_t * trunk
Trunk connection for queries.
Definition rlm_sql.c:241
#define SQL_AUTZ_STAGE_PROFILE
Definition rlm_sql.c:178
static xlat_action_t sql_xlat_query_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_sql.c:471
map_list_t check_tmp
List to store check items before processing.
Definition rlm_sql.c:198
static int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Definition rlm_sql.c:263
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_sql.c:2252
fr_value_box_t * query
Query string used for evaluating group membership.
Definition rlm_sql.c:184
sql_autz_call_env_t * call_env
Call environment data.
Definition rlm_sql.c:197
module_rlm_t rlm_sql
Definition rlm_sql.c:2505
static unlang_action_t mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Start of module authorize method.
Definition rlm_sql.c:1725
static unlang_action_t sql_get_grouplist(unlang_result_t *p_result, sql_group_ctx_t *group_ctx, trunk_t *trunk, request_t *request)
Definition rlm_sql.c:1131
static const call_env_method_t xlat_method_env
Definition rlm_sql.c:137
sql_autz_status_t
Status of the authorization process.
Definition rlm_sql.c:159
@ SQL_AUTZ_PROFILE_REPLY
Running profile reply query.
Definition rlm_sql.c:173
@ SQL_AUTZ_CHECK
Running user check query.
Definition rlm_sql.c:160
@ SQL_AUTZ_GROUP_MEMB_RESUME
Completed group membership query.
Definition rlm_sql.c:165
@ SQL_AUTZ_REPLY
Running user reply query.
Definition rlm_sql.c:162
@ SQL_AUTZ_GROUP_MEMB
Running group membership query.
Definition rlm_sql.c:164
@ SQL_AUTZ_PROFILE_REPLY_RESUME
Completed profile reply query.
Definition rlm_sql.c:174
@ SQL_AUTZ_PROFILE_CHECK_RESUME
Completed profile check query.
Definition rlm_sql.c:172
@ SQL_AUTZ_CHECK_RESUME
Completed user check query.
Definition rlm_sql.c:161
@ SQL_AUTZ_PROFILE_START
Starting processing user profiles.
Definition rlm_sql.c:170
@ SQL_AUTZ_GROUP_REPLY_RESUME
Completed group reply query.
Definition rlm_sql.c:169
@ SQL_AUTZ_REPLY_RESUME
Completed user reply query.
Definition rlm_sql.c:163
@ SQL_AUTZ_GROUP_CHECK
Running group check query.
Definition rlm_sql.c:166
@ SQL_AUTZ_PROFILE_CHECK
Running profile check query.
Definition rlm_sql.c:171
@ SQL_AUTZ_GROUP_REPLY
Running group reply query.
Definition rlm_sql.c:168
@ SQL_AUTZ_GROUP_CHECK_RESUME
Completed group check query.
Definition rlm_sql.c:167
static int sql_xlat_escape(request_t *request, fr_value_box_t *vb, void *uctx)
Escape a tainted VB used as an xlat argument.
Definition rlm_sql.c:343
rlm_sql_t const * inst
Module instance.
Definition rlm_sql.c:193
#define SQL_AUTZ_STAGE_GROUP
Definition rlm_sql.c:177
tmpl_t * group_reply_query
Tmpl to expand to form authorize_group_reply_query.
Definition rlm_sql.c:124
tmpl_t * group_check_query
Tmpl to expand to form authorize_group_check_query.
Definition rlm_sql.c:123
int num_groups
How many groups have been retrieved.
Definition rlm_sql.c:187
static int sql_autz_ctx_free(sql_autz_ctx_t *to_free)
Definition rlm_sql.c:1308
trunk_t * trunk
Trunk connection for current authorization.
Definition rlm_sql.c:196
fr_value_box_t * query_vb
Current query string.
Definition rlm_sql.c:245
fr_value_box_list_t query
Where expanded query tmpl will be written.
Definition rlm_sql.c:244
map_list_t reply_tmp
List to store reply items before processing.
Definition rlm_sql.c:199
sql_autz_status_t status
Current status of the authorization.
Definition rlm_sql.c:200
request_t * request
Request being processed.
Definition rlm_sql.c:194
fr_pair_t * profile
Current profile being processed.
Definition rlm_sql.c:205
#define MAX_SQL_FIELD_INDEX
Definition rlm_sql.c:749
fr_dict_attr_autoload_t rlm_sql_dict_attr[]
Definition rlm_sql.c:110
fr_value_box_list_t query
Definition rlm_sql.c:1159
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Initialise thread specific data structure.
Definition rlm_sql.c:2425
static void * sql_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
Definition rlm_sql.c:286
static unlang_action_t mod_sql_redundant(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Generic module call for failing between a bunch of queries.
Definition rlm_sql.c:1931
fr_sql_query_t * query_ctx
Query context for current query.
Definition rlm_sql.c:246
static unlang_action_t mod_sql_redundant_query_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Resume function called after executing an SQL query in a redundant list of queries.
Definition rlm_sql.c:1812
rlm_rcode_t rcode
Module return code.
Definition rlm_sql.c:195
tmpl_t ** query
Array of tmpls for list of queries to run.
Definition rlm_sql.c:213
rlm_sql_grouplist_t * next
Definition rlm_sql.c:1088
static const call_env_method_t accounting_method_env
Definition rlm_sql.c:216
static int sql_redundant_ctx_free(sql_redundant_ctx_t *to_free)
Tidy up when freeing an SQL redundant context.
Definition rlm_sql.c:1795
static const call_env_method_t group_xlat_method_env
Definition rlm_sql.c:254
sql_group_ctx_t * group_ctx
Context used for retrieving user group membership.
Definition rlm_sql.c:207
tmpl_t * membership_query
Tmpl to expand to form group_membership_query.
Definition rlm_sql.c:122
static int sql_box_escape(fr_value_box_t *vb, void *uctx)
Definition rlm_sql.c:441
static const conf_parser_t module_config[]
Definition rlm_sql.c:64
fr_value_box_list_t query
Where expanded query tmpls will be written.
Definition rlm_sql.c:201
bool user_found
Has the user been found anywhere?
Definition rlm_sql.c:202
static fr_dict_attr_t const * attr_user_profile
Definition rlm_sql.c:106
static const call_env_method_t send_method_env
Definition rlm_sql.c:226
fr_dict_autoload_t rlm_sql_dict[]
Definition rlm_sql.c:99
fr_dict_attr_t const * group_da
Definition rlm_sql.c:60
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Definition rlm_sql.c:2444
static unlang_action_t mod_map_resume(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request, UNUSED fr_value_box_list_t *query, map_list_t const *maps)
Process the results of an SQL map query.
Definition rlm_sql.c:763
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_sql.c:2147
fr_value_box_t user
Expansion of sql_user_name.
Definition rlm_sql.c:211
static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc, call_env_ctx_t const *cec, call_env_parser_t const *rule)
static unlang_action_t mod_map_proc(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request, fr_value_box_list_t *query, UNUSED map_list_t const *maps)
Executes a SELECT query and maps the result to server attributes.
Definition rlm_sql.c:901
static ssize_t sql_escape_func(request_t *, char *out, size_t outlen, char const *in, void *arg)
static int sql_call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
Custom parser for sql call env queries.
Definition rlm_sql.c:2458
static unlang_action_t mod_sql_redundant_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Resume function called after expansion of next query in a redundant list of queries.
Definition rlm_sql.c:1900
Context for SQL authorization.
Definition rlm_sql.c:192
Context for group membership query evaluation.
Definition rlm_sql.c:182
Context for tracking redundant SQL query sets.
Definition rlm_sql.c:238
Prototypes and functions for the SQL module.
fr_sql_query_t * fr_sql_query_alloc(TALLOC_CTX *ctx, rlm_sql_t const *inst, request_t *request, trunk_t *trunk, char const *query_str, fr_sql_query_type_t type)
Allocate an sql query structure.
Definition sql.c:183
trunk_t * trunk
Trunk connection for this thread.
Definition rlm_sql.h:114
rlm_sql_t const * inst
Module instance data.
Definition rlm_sql.h:115
#define RLM_SQL_MULTI_QUERY_CONN
Can support multiple queries on a single connection.
Definition rlm_sql.h:173
fr_sql_query_type_t type
Type of query.
Definition rlm_sql.h:143
unlang_action_t sql_get_map_list(unlang_result_t *p_result, request_t *request, fr_sql_map_ctx_t *map_ctx, trunk_t *trunk)
Submit the query to get any user / group check or reply pairs.
Definition sql.c:356
unlang_action_t rlm_sql_fetch_row(unlang_result_t *p_result, request_t *request, void *uctx)
Call the driver's sql_fetch_row function.
Definition sql.c:79
void rlm_sql_query_log(rlm_sql_t const *inst, char const *filename, char const *query)
Definition sql.c:386
rlm_sql_t const * inst
Module instance for this query.
Definition rlm_sql.h:137
sql_rcode_t
Action to take at end of an SQL query.
Definition rlm_sql.h:44
@ RLM_SQL_QUERY_INVALID
Query syntax error.
Definition rlm_sql.h:45
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
Definition rlm_sql.h:49
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
Definition rlm_sql.h:48
@ RLM_SQL_ERROR
General connection/server error.
Definition rlm_sql.h:46
@ RLM_SQL_OK
Success.
Definition rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition rlm_sql.h:50
@ SQL_QUERY_SELECT
Definition rlm_sql.h:120
@ SQL_QUERY_OTHER
Definition rlm_sql.h:121
TALLOC_CTX * ctx
To allocate map entries in.
Definition rlm_sql.h:153
void * sql_escape_arg
Thread specific argument to be passed to escape function.
Definition rlm_sql.h:116
sql_fall_through_t
Definition rlm_sql.h:53
@ FALL_THROUGH_DEFAULT
Definition rlm_sql.h:56
@ FALL_THROUGH_YES
Definition rlm_sql.h:55
char ** rlm_sql_row_t
Definition rlm_sql.h:59
void rlm_sql_print_error(rlm_sql_t const *inst, request_t *request, fr_sql_query_t *query_ctx, bool force_debug)
Retrieve any errors from the SQL driver.
Definition sql.c:122
rlm_sql_row_t row
Row data from the last query.
Definition rlm_sql.h:146
sql_rcode_t rcode
Result code.
Definition rlm_sql.h:145
rlm_sql_t const * sql
Definition rlm_sql.h:197
fr_table_num_sorted_t const sql_rcode_description_table[]
Definition sql.c:45
unlang_action_t rlm_sql_trunk_query(unlang_result_t *p_result, request_t *request, void *uctx)
Submit an SQL query using a trunk connection.
Definition sql.c:242
Context used when fetching attribute value pairs as a map list.
Definition rlm_sql.h:152
int fr_sbuff_trim_talloc(fr_sbuff_t *sbuff, size_t len)
Trim a talloced sbuff to the minimum length required to represent the contained string.
Definition sbuff.c:424
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_buff(_sbuff_or_marker)
Talloc sbuff extension structure.
Definition sbuff.h:140
static char const * section_name_str(char const *name)
Return a printable string for the section name.
Definition section.h:98
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
char const * name2
Second section name. Usually a packet type like 'access-request', 'access-accept',...
Definition section.h:46
char const * name1
First section name. Usually a verb like 'recv', 'send', etc...
Definition section.h:45
char const * name
Instance name e.g. user_database.
Definition module.h:355
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:349
void * data
Module's instance data.
Definition module.h:291
void * boot
Data allocated during the boostrap phase.
Definition module.h:294
void * data
Thread specific instance data.
Definition module.h:372
static module_thread_instance_t * module_thread(module_instance_t const *mi)
Retrieve module/thread specific instance for a module.
Definition module.h:501
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
size_t boot_size
Size of the module's bootstrap data.
Definition module.h:209
module_t * exported
Public module structure.
Definition module.h:296
Module instance data.
Definition module.h:285
Named methods exported by a module.
Definition module.h:174
#define pair_append_control(_attr, _da)
Allocate and append a fr_pair_t to the control list.
Definition pair.h:57
#define pair_delete_request(_pair_or_da)
Delete a fr_pair_t in the request list.
Definition pair.h:172
#define pair_update_control(_attr, _da)
Return or allocate a fr_pair_t in the control list.
Definition pair.h:140
tmpl_escape_t escape
How escaping should be handled during evaluation.
Definition tmpl.h:351
int tmpl_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules))
Attempt to resolve functions and attributes in xlats and attribute references.
#define tmpl_value(_tmpl)
Definition tmpl.h:939
fr_value_box_safe_for_t literals_safe_for
safe_for value assigned to literal values in xlats, execs, and data.
Definition tmpl.h:349
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
#define tmpl_is_data(vpt)
Definition tmpl.h:206
#define tmpl_expand(_out, _buff, _buff_len, _request, _vpt)
Expand a tmpl to a C type, using existing storage to hold variably sized types.
Definition tmpl.h:1054
#define tmpl_value_type(_tmpl)
Definition tmpl.h:941
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:337
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:803
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:223
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:366
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:334
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition skip.h:37
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
return count
Definition module.c:155
int module_instantiate(module_instance_t *instance)
Manually complete module setup by calling its instantiate function.
Definition module.c:1189
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:431
unlang_action_t unlang_module_yield_to_tmpl(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *vpt, unlang_tmpl_args_t *args, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Push a pre-compiled tmpl and resumption state onto the stack for evaluation.
Definition module.c:216
eap_aka_sim_process_conf_t * inst
fr_pair_value_bstrdup_buffer(vp, eap_session->identity, true)
fr_pair_t * vp
eap_type_t type
The preferred EAP-Type of this instance of the EAP-SIM/AKA/AKA' state machine.
Value pair map.
Definition map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
void * sql_escape_arg
Instance specific argument to be passed to escape function.
Definition rlm_sql.h:239
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:467
#define talloc_get_type_abort_const
Definition talloc.h:244
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
int unlang_tmpl_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args, bool top_frame)
Push a tmpl onto the stack for evaluation.
Definition tmpl.c:276
@ TMPL_ESCAPE_UCTX_ALLOC_FUNC
A new uctx of the specified size and type is allocated and pre-populated by memcpying uctx....
Definition tmpl_escape.h:35
fr_value_box_escape_t box_escape
How to escape when returned from evaluation.
Definition tmpl_escape.h:81
@ TMPL_ESCAPE_PRE_CONCAT
Pre-concatenation escaping is useful for DSLs where elements of the expansion are static,...
Definition tmpl_escape.h:61
struct tmpl_escape_t::@75 uctx
tmpl_escape_mode_t mode
Whether to apply escape function after concatenation, i.e.
Definition tmpl_escape.h:83
Escaping rules for tmpls.
Definition tmpl_escape.h:80
const bool fr_assignment_op[T_TOKEN_LAST]
Definition token.c:169
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:79
const bool fr_comparison_op[T_TOKEN_LAST]
Definition token.c:199
@ T_OP_CMP_TRUE
Definition token.h:104
@ T_OP_CMP_FALSE
Definition token.h:105
int module_trigger_args_build(TALLOC_CTX *ctx, fr_pair_list_t *list, CONF_SECTION *cs, module_trigger_args_t *args)
Build trigger args pair list for modules.
Definition trigger.c:499
Common values used by modules when building trigger args.
Definition trigger.h:42
trunk_t * trunk_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, trunk_io_funcs_t const *funcs, trunk_conf_t const *conf, char const *log_prefix, void const *uctx, bool delay_start, fr_pair_list_t *trigger_args)
Allocate a new collection of connections.
Definition trunk.c:4963
conf_parser_t const trunk_config[]
Config parser definitions to populate a trunk_conf_t.
Definition trunk.c:341
Main trunk management handle.
Definition trunk.c:215
static int expand_rhs(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
Definition edit.c:1176
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:544
fr_type_t type
Type to cast argument to.
Definition xlat.h:155
xlat_action_t xlat_transparent(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_YIELD
An xlat function pushed a resume frame onto the stack.
Definition xlat.h:42
@ XLAT_ACTION_PUSH_UNLANG
An xlat function pushed an unlang frame onto the unlang stack.
Definition xlat.h:39
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumend by an xlat function.
Definition xlat.h:145
static fr_slen_t parent
Definition pair.h:841
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_type_is_structural(_x)
Definition types.h:393
#define FR_TYPE_IP
Definition types.h:309
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:455
#define FR_TYPE_NUMERIC
Definition types.h:307
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
Definition value.c:612
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:3976
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:4085
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition value.c:4368
void fr_value_box_strdup_shallow_replace(fr_value_box_t *vb, char const *src, ssize_t len)
Free the existing buffer (if talloced) associated with the valuebox, and replace it with a new one.
Definition value.c:4493
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
Definition value.c:6274
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:239
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:643
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1074
#define fr_box_strvalue_buffer(_val)
Definition value.h:311
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition value.h:1081
fr_value_box_safe_for_t safe_for
Definition value.h:677
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:162
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:654
fr_value_box_escape_func_t func
Definition value.h:676
static size_t char ** out
Definition value.h:1023
static TALLOC_CTX * xlat_ctx
void * rctx
Resume context.
Definition xlat_ctx.h:54
void * env_data
Expanded call env data.
Definition xlat_ctx.h:53
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition xlat_ctx.h:52
An xlat calling ctx.
Definition xlat_ctx.h:49
void xlat_func_flags_set(xlat_t *x, xlat_func_flags_t flags)
Specify flags that alter the xlat's behaviour.
Definition xlat_func.c:399
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:363
void xlat_func_call_env_set(xlat_t *x, call_env_method_t const *env_method)
Register call environment of an xlat.
Definition xlat_func.c:389
#define xlat_func_safe_for_set(_xlat, _escaped)
Set the escaped values for output boxes.
Definition xlat_func.h:82
@ XLAT_FUNC_FLAG_PURE
Definition xlat_func.h:38