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: 7af78654e40e5b40632e33eba7229b47291a4261 $
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: 7af78654e40e5b40632e33eba7229b47291a4261 $")
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/util/debug.h>
38#include <freeradius-devel/util/dict.h>
39#include <freeradius-devel/util/table.h>
40#include <freeradius-devel/unlang/function.h>
41#include <freeradius-devel/unlang/xlat_func.h>
42
43#include <sys/stat.h>
44
45#include "rlm_sql.h"
46
47#define SQL_SAFE_FOR (fr_value_box_safe_for_t)inst->driver
48
50
51static int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
52
53typedef struct {
56
57static const conf_parser_t module_config[] = {
58 { FR_CONF_OFFSET_TYPE_FLAGS("driver", FR_TYPE_VOID, 0, rlm_sql_t, driver_submodule), .dflt = "null",
60 { FR_CONF_OFFSET("server", rlm_sql_config_t, sql_server), .dflt = "" }, /* Must be zero length so drivers can determine if it was set */
61 { FR_CONF_OFFSET("port", rlm_sql_config_t, sql_port), .dflt = "0" },
62 { FR_CONF_OFFSET("login", rlm_sql_config_t, sql_login), .dflt = "" },
63 { FR_CONF_OFFSET_FLAGS("password", CONF_FLAG_SECRET, rlm_sql_config_t, sql_password), .dflt = "" },
64 { FR_CONF_OFFSET("radius_db", rlm_sql_config_t, sql_db), .dflt = "radius" },
65 { FR_CONF_OFFSET("read_groups", rlm_sql_config_t, read_groups), .dflt = "yes" },
66 { FR_CONF_OFFSET("group_attribute", rlm_sql_config_t, group_attribute) },
67 { FR_CONF_OFFSET("cache_groups", rlm_sql_config_t, cache_groups) },
68 { FR_CONF_OFFSET("read_profiles", rlm_sql_config_t, read_profiles), .dflt = "yes" },
69 { FR_CONF_OFFSET("open_query", rlm_sql_config_t, connect_query) },
70
71 { FR_CONF_OFFSET("safe_characters", rlm_sql_config_t, allowed_chars), .dflt = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
72
73 /*
74 * This only works for a few drivers.
75 */
76 { FR_CONF_OFFSET("query_timeout", rlm_sql_config_t, query_timeout), .dflt = "5" },
77
78 /*
79 * The pool section is used for trunk config
80 */
81 { FR_CONF_OFFSET_SUBSECTION("pool", 0, rlm_sql_config_t, trunk_conf, trunk_config) },
82
84};
85
87
90 { .out = &dict_freeradius, .proto = "freeradius" },
91 { NULL }
92};
93
98
101 { .out = &attr_fall_through, .name = "Fall-Through", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
102 { .out = &attr_sql_user_name, .name = "SQL-User-Name", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
103 { .out = &attr_user_profile, .name = "User-Profile", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
104 { .out = &attr_expr_bool_enum, .name = "Expr-Bool-Enum", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
105 { NULL }
106};
107
108typedef struct {
109 fr_value_box_t user; //!< Expansion of the sql_user_name
110 tmpl_t *check_query; //!< Tmpl to expand to form authorize_check_query
111 tmpl_t *reply_query; //!< Tmpl to expand to form authorize_reply_query
112 tmpl_t *membership_query; //!< Tmpl to expand to form group_membership_query
113 tmpl_t *group_check_query; //!< Tmpl to expand to form authorize_group_check_query
114 tmpl_t *group_reply_query; //!< Tmpl to expand to form authorize_group_reply_query
116
117static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc,
118 call_env_ctx_t const *cec, call_env_parser_t const *rule);
119
120static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc,
121 call_env_ctx_t const *cec, call_env_parser_t const *rule);
122
126
141
143
144/** Status of the authorization process
145 */
146typedef enum {
147 SQL_AUTZ_CHECK = 0x12, //!< Running user `check` query
148 SQL_AUTZ_CHECK_RESUME = 0x13, //!< Completed user `check` query
149 SQL_AUTZ_REPLY = 0x14, //!< Running user `reply` query
150 SQL_AUTZ_REPLY_RESUME = 0x15, //!< Completed user `reply` query
151 SQL_AUTZ_GROUP_MEMB = 0x20, //!< Running group membership query
152 SQL_AUTZ_GROUP_MEMB_RESUME = 0x21, //!< Completed group membership query
153 SQL_AUTZ_GROUP_CHECK = 0x22, //!< Running group `check` query
154 SQL_AUTZ_GROUP_CHECK_RESUME = 0x23, //!< Completed group `check` query
155 SQL_AUTZ_GROUP_REPLY = 0x24, //!< Running group `reply` query
156 SQL_AUTZ_GROUP_REPLY_RESUME = 0x25, //!< Completed group `reply` query
157 SQL_AUTZ_PROFILE_START = 0x40, //!< Starting processing user profiles
158 SQL_AUTZ_PROFILE_CHECK = 0x42, //!< Running profile `check` query
159 SQL_AUTZ_PROFILE_CHECK_RESUME = 0x43, //!< Completed profile `check` query
160 SQL_AUTZ_PROFILE_REPLY = 0x44, //!< Running profile `reply` query
161 SQL_AUTZ_PROFILE_REPLY_RESUME = 0x45, //!< Completed profile `reply` query
163
164#define SQL_AUTZ_STAGE_GROUP 0x20
165#define SQL_AUTZ_STAGE_PROFILE 0x40
166
167/** Context for group membership query evaluation
168 */
169typedef struct {
170 rlm_sql_t const *inst; //!< Module instance.
171 fr_value_box_t *query; //!< Query string used for evaluating group membership.
172 fr_sql_query_t *query_ctx; //!< Query context.
173 rlm_sql_grouplist_t *groups; //!< List of groups retrieved.
174 int num_groups; //!< How many groups have been retrieved.
176
177/** Context for SQL authorization
178 */
179typedef struct {
180 rlm_sql_t const *inst; //!< Module instance.
181 request_t *request; //!< Request being processed.
182 rlm_rcode_t rcode; //!< Module return code.
183 trunk_t *trunk; //!< Trunk connection for current authorization.
184 sql_autz_call_env_t *call_env; //!< Call environment data.
185 map_list_t check_tmp; //!< List to store check items before processing.
186 map_list_t reply_tmp; //!< List to store reply items before processing.
187 sql_autz_status_t status; //!< Current status of the authorization.
188 fr_value_box_list_t query; //!< Where expanded query tmpls will be written.
189 bool user_found; //!< Has the user been found anywhere?
190 rlm_sql_grouplist_t *group; //!< Current group being processed.
191 fr_pair_t *sql_group; //!< Pair to update with group being processed.
192 fr_pair_t *profile; //!< Current profile being processed.
193 fr_sql_map_ctx_t *map_ctx; //!< Context used for retrieving attribute value pairs as a map list.
194 sql_group_ctx_t *group_ctx; //!< Context used for retrieving user group membership.
196
197/** Context for SQL maps
198 *
199 */
200typedef struct {
202 map_list_t const *maps;
205
206typedef struct {
207 fr_value_box_t user; //!< Expansion of sql_user_name.
208 fr_value_box_t filename; //!< File name to write SQL logs to.
209 tmpl_t **query; //!< Array of tmpls for list of queries to run.
211
221
231
232/** Context for tracking redundant SQL query sets
233 */
234typedef struct {
235 rlm_sql_t const *inst; //!< Module instance.
236 request_t *request; //!< Request being processed.
237 trunk_t *trunk; //!< Trunk connection for queries.
238 sql_redundant_call_env_t *call_env; //!< Call environment data.
239 size_t query_no; //!< Current query number.
240 fr_value_box_list_t query; //!< Where expanded query tmpl will be written.
241 fr_value_box_t *query_vb; //!< Current query string.
242 fr_sql_query_t *query_ctx; //!< Query context for current query.
244
249
258
259int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
260{
261 rlm_sql_t *inst = talloc_get_type_abort(parent, rlm_sql_t);
263 int ret;
264
265 if (unlikely(ret = module_rlm_submodule_parse(ctx, out, parent, ci, rule) < 0)) return ret;
266
267 mi = talloc_get_type_abort(*((void **)out), module_instance_t);
268 inst->driver = (rlm_sql_driver_t const *)mi->exported; /* Public symbol exported by the submodule */
269
270 return 0;
271}
272
273static int _sql_escape_uxtx_free(void *uctx)
274{
275 return talloc_free(uctx);
276}
277
278/*
279 * Create a thread local uctx which is used in SQL value box escaping
280 * so that an already reserved connection can be used.
281 */
282static void *sql_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
283{
284 static _Thread_local rlm_sql_escape_uctx_t *t_ctx;
285
286 if (unlikely(t_ctx == NULL)) {
288
289 MEM(ctx = talloc_zero(NULL, rlm_sql_escape_uctx_t));
291 }
292 t_ctx->sql = uctx;
293
294 return t_ctx;
295}
296
297/*
298 * Fall-Through checking function from rlm_files.c
299 */
300static sql_fall_through_t fall_through(map_list_t *maps)
301{
302 bool rcode;
303 map_t *map, *next;
304
305 for (map = map_list_head(maps);
306 map != NULL;
307 map = next) {
308 next = map_list_next(maps, map);
309
311
313 (void) map_list_remove(maps, map);
314
315 if (tmpl_is_data(map->rhs)) {
317
318 rcode = tmpl_value(map->rhs)->vb_bool;
319 } else {
320 rcode = false;
321 }
322
323 talloc_free(map);
324 return rcode;
325 }
326 }
327
329}
330
331/*
332 * Yucky prototype.
333 */
334static ssize_t sql_escape_func(request_t *, char *out, size_t outlen, char const *in, void *arg);
335
336/** Escape a tainted VB used as an xlat argument
337 *
338 */
339static int CC_HINT(nonnull(2,3)) sql_xlat_escape(request_t *request, fr_value_box_t *vb, void *uctx)
340{
341 fr_sbuff_t sbuff;
342 fr_sbuff_uctx_talloc_t sbuff_ctx;
343
344 ssize_t len;
345 void *arg = NULL;
346 rlm_sql_escape_uctx_t *ctx = uctx;
348 fr_value_box_entry_t entry;
349 rlm_sql_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data, rlm_sql_thread_t);
350
351 /*
352 * If it's already safe, don't do anything.
353 */
354 if (fr_value_box_is_safe_for(vb, inst->driver)) return 0;
355
356 /*
357 * No need to escape types with inherently safe data
358 */
359 switch (vb->type) {
360 case FR_TYPE_NUMERIC:
361 case FR_TYPE_IP:
362 case FR_TYPE_ETHERNET:
363 fr_value_box_mark_safe_for(vb, inst->driver);
364 return 0;
365
366 default:
367 break;
368 }
369
370 if (inst->sql_escape_arg) {
371 arg = inst->sql_escape_arg;
372 } else if (thread->sql_escape_arg) {
373 arg = thread->sql_escape_arg;
374 }
375 if (!arg) {
376 error:
378 return -1;
379 }
380
381 /*
382 * Escaping functions work on strings - ensure the box is a string
383 */
384 if ((vb->type != FR_TYPE_STRING) && (fr_value_box_cast_in_place(vb, vb, FR_TYPE_STRING, NULL) < 0)) goto error;
385
386 /*
387 * Maximum escaped length is 3 * original - if every character needs escaping
388 */
389 if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, vb->vb_length * 3, vb->vb_length * 3)) {
390 fr_strerror_printf_push("Failed to allocate buffer for escaped sql argument");
391 return -1;
392 }
393
394 len = inst->sql_escape_func(request, fr_sbuff_buff(&sbuff), vb->vb_length * 3 + 1, vb->vb_strvalue, arg);
395 if (len < 0) goto error;
396
397 /*
398 * fr_value_box_strdup_shallow resets the dlist entries - take a copy
399 */
400 entry = vb->entry;
401 fr_sbuff_trim_talloc(&sbuff, len);
403 fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), vb->tainted);
404
405 /*
406 * Different databases have slightly different ideas as
407 * to what is safe. So we track the database type in the
408 * safe value. This means that we don't
409 * cross-contaminate "safe" values across databases.
410 */
411 fr_value_box_mark_safe_for(vb, inst->driver);
412 vb->entry = entry;
413
414 return 0;
415}
416
417static int sql_box_escape(fr_value_box_t *vb, void *uctx)
418{
419 return sql_xlat_escape(NULL, vb, uctx);
420}
421
422/** Escape a value to make it SQL safe.
423 *
424@verbatim
425%sql.escape(<value>)
426@endverbatim
427 *
428 * @ingroup xlat_functions
429 */
430static xlat_action_t sql_escape_xlat(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
431 request_t *request, fr_value_box_list_t *in)
432{
433 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
434 fr_value_box_t *vb;
435 rlm_sql_escape_uctx_t *escape_uctx = NULL;
436
437 while ((vb = fr_value_box_list_pop_head(in))) {
438 if (fr_value_box_is_safe_for(vb, inst->driver)) goto append;
439 if (!escape_uctx) escape_uctx = sql_escape_uctx_alloc(request, inst);
440 sql_box_escape(vb, escape_uctx);
441 append:
443 }
444 return XLAT_ACTION_DONE;
445}
446
447static xlat_action_t sql_xlat_query_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
448 request_t *request, UNUSED fr_value_box_list_t *in)
449{
450 fr_sql_query_t *query_ctx = talloc_get_type_abort(xctx->rctx, fr_sql_query_t);
451 rlm_sql_t const *inst = query_ctx->inst;
452 fr_value_box_t *vb;
454 int numaffected;
455
456 fr_assert(query_ctx->type == SQL_QUERY_OTHER);
457
458 switch (query_ctx->rcode) {
460 case RLM_SQL_ERROR:
463 query_ctx->rcode, "<INVALID>"));
464 rlm_sql_print_error(inst, request, query_ctx, false);
465 ret = XLAT_ACTION_FAIL;
466 goto finish;
467
468 default:
469 break;
470 }
471
472 numaffected = (inst->driver->sql_affected_rows)(query_ctx, &inst->config);
473 if (numaffected < 1) {
474 RDEBUG2("SQL query affected no rows");
475 goto finish;
476 }
477
478 MEM(vb = fr_value_box_alloc_null(ctx));
479 fr_value_box_uint32(vb, NULL, (uint32_t)numaffected, false);
481
482finish:
483 talloc_free(query_ctx);
484
485 return ret;
486}
487
488static xlat_action_t sql_xlat_select_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
489 request_t *request, UNUSED fr_value_box_list_t *in)
490{
491 fr_sql_query_t *query_ctx = talloc_get_type_abort(xctx->rctx, fr_sql_query_t);
492 rlm_sql_t const *inst = query_ctx->inst;
493 fr_value_box_t *vb;
495 rlm_rcode_t p_result;
496 rlm_sql_row_t row;
497 bool fetched = false;
498
499 fr_assert(query_ctx->type == SQL_QUERY_SELECT);
500
501 if (query_ctx->rcode != RLM_SQL_OK) {
502 query_error:
504 query_ctx->rcode, "<INVALID>"));
505 rlm_sql_print_error(inst, request, query_ctx, false);
506 ret = XLAT_ACTION_FAIL;
507 goto finish;
508 }
509
510 do {
511 inst->fetch_row(&p_result, NULL, request, query_ctx);
512 row = query_ctx->row;
513 switch (query_ctx->rcode) {
514 case RLM_SQL_OK:
515 if (row[0]) break;
516
517 RDEBUG2("NULL value in first column of result");
518 goto finish;
519
521 if (!fetched) RDEBUG2("SQL query returned no results");
522 goto finish;
523
524 default:
525 goto query_error;
526 }
527
528 fetched = true;
529
530 MEM(vb = fr_value_box_alloc_null(ctx));
531 fr_value_box_strdup(vb, vb, NULL, row[0], false);
533
534 } while (1);
535
536finish:
537 talloc_free(query_ctx);
538
539 return ret;
540}
541
542/** Execute an arbitrary SQL query
543 *
544 * For SELECTs, the values of the first column will be returned.
545 * For INSERTS, UPDATEs and DELETEs, the number of rows affected will
546 * be returned instead.
547 *
548@verbatim
549%sql(<sql statement>)
550@endverbatim
551 *
552 * @ingroup xlat_functions
553 */
554static xlat_action_t sql_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
555 xlat_ctx_t const *xctx,
556 request_t *request, fr_value_box_list_t *in)
557{
558 sql_xlat_call_env_t *call_env = talloc_get_type_abort(xctx->env_data, sql_xlat_call_env_t);
559 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
560 rlm_sql_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_sql_thread_t);
561 char const *p;
562 fr_value_box_t *arg = fr_value_box_list_head(in);
563 fr_sql_query_t *query_ctx = NULL;
564 rlm_rcode_t p_result;
566
567 if (call_env->filename.type == FR_TYPE_STRING && call_env->filename.vb_length > 0) {
568 rlm_sql_query_log(inst, call_env->filename.vb_strvalue, arg->vb_strvalue);
569 }
570
571 p = arg->vb_strvalue;
572
573 /*
574 * Trim whitespace for the prefix check
575 */
577
578 /*
579 * If the query starts with any of the following prefixes,
580 * then return the number of rows affected
581 */
582 if ((strncasecmp(p, "insert", 6) == 0) ||
583 (strncasecmp(p, "update", 6) == 0) ||
584 (strncasecmp(p, "delete", 6) == 0)) {
585 MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request,
586 thread->trunk, arg->vb_strvalue, SQL_QUERY_OTHER));
587
588 unlang_xlat_yield(request, sql_xlat_query_resume, NULL, 0, query_ctx);
589 query_ret = inst->query(&p_result, NULL, request, query_ctx);
591
592 return sql_xlat_query_resume(ctx, out, &(xlat_ctx_t){.rctx = query_ctx, .inst = inst}, request, in);
593 } /* else it's a SELECT statement */
594
595 MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request,
596 thread->trunk, arg->vb_strvalue, SQL_QUERY_SELECT));
597
598 unlang_xlat_yield(request, sql_xlat_select_resume, NULL, 0, query_ctx);
599 if (unlang_function_push(request, inst->select, NULL, NULL, 0, UNLANG_SUB_FRAME, query_ctx) != UNLANG_ACTION_PUSHED_CHILD) return XLAT_ACTION_FAIL;
600
602}
603
604/** Execute an arbitrary SQL query, expecting results to be returned
605 *
606@verbatim
607%sql.fetch(<sql statement>)
608@endverbatim
609 *
610 * @ingroup xlat_functions
611 */
613 request_t *request, fr_value_box_list_t *in)
614{
615 sql_xlat_call_env_t *call_env = talloc_get_type_abort(xctx->env_data, sql_xlat_call_env_t);
616 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
617 rlm_sql_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_sql_thread_t);
618 fr_value_box_t *arg = fr_value_box_list_head(in);
619 fr_sql_query_t *query_ctx = NULL;
620
621 if (call_env->filename.type == FR_TYPE_STRING && call_env->filename.vb_length > 0) {
622 rlm_sql_query_log(inst, call_env->filename.vb_strvalue, arg->vb_strvalue);
623 }
624
625 MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request,
626 thread->trunk, arg->vb_strvalue, SQL_QUERY_SELECT));
627
628 unlang_xlat_yield(request, sql_xlat_select_resume, NULL, 0, query_ctx);
629 if (unlang_function_push(request, inst->select, NULL, NULL, 0, UNLANG_SUB_FRAME, query_ctx) != UNLANG_ACTION_PUSHED_CHILD) return XLAT_ACTION_FAIL;
630
632}
633
634/** Execute an arbitrary SQL query, returning the number of rows affected
635 *
636@verbatim
637%sql.modify(<sql statement>)
638@endverbatim
639 *
640 * @ingroup xlat_functions
641 */
642static xlat_action_t sql_modify_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
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 rlm_rcode_t p_result;
651
652 if (call_env->filename.type == FR_TYPE_STRING && call_env->filename.vb_length > 0) {
653 rlm_sql_query_log(inst, call_env->filename.vb_strvalue, arg->vb_strvalue);
654 }
655
656 MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request,
657 thread->trunk, arg->vb_strvalue, SQL_QUERY_OTHER));
658
659 unlang_xlat_yield(request, sql_xlat_query_resume, NULL, 0, query_ctx);
660 if (inst->query(&p_result, NULL, request, query_ctx) == UNLANG_ACTION_PUSHED_CHILD) return XLAT_ACTION_PUSH_UNLANG;
661
662 return sql_xlat_query_resume(ctx, out, &(xlat_ctx_t){.rctx = query_ctx, .inst = inst}, request, in);
663}
664
665/** Converts a string value into a #fr_pair_t
666 *
667 * @param[in,out] ctx to allocate #fr_pair_t (s).
668 * @param[out] out where to write the resulting #fr_pair_t.
669 * @param[in] request The current request.
670 * @param[in] map to process.
671 * @param[in] uctx The value to parse.
672 * @return
673 * - 0 on success.
674 * - -1 on failure.
675 */
676static int _sql_map_proc_get_value(TALLOC_CTX *ctx, fr_pair_list_t *out,
677 request_t *request, map_t const *map, void *uctx)
678{
679 fr_pair_t *vp;
680 char const *value = uctx;
681
683 if (!vp) return -1;
684
685 /*
686 * Buffer not always talloced, sometimes it's
687 * just a pointer to a field in a result struct.
688 */
689 if (fr_pair_value_from_str(vp, value, strlen(value), NULL, true) < 0) {
690 RPEDEBUG("Failed parsing value \"%pV\" for attribute %s",
692 return -1;
693 }
694
695 return 0;
696}
697
698/*
699 * Verify the result of the map.
700 */
701static int sql_map_verify(CONF_SECTION *cs, UNUSED void const *mod_inst, UNUSED void *proc_inst,
702 tmpl_t const *src, UNUSED map_list_t const *maps)
703{
704 if (!src) {
705 cf_log_err(cs, "Missing SQL query");
706
707 return -1;
708 }
709
710 return 0;
711}
712
713#define MAX_SQL_FIELD_INDEX (64)
714
715/** Process the results of an SQL map query
716 *
717 * @param[out] p_result Result of applying the map.
718 * @param[in] priority Unused.
719 * @param[in] request Current request.
720 * @param[in] uctx Map context.
721 * @return One of UNLANG_ACTION_*
722 */
723static unlang_action_t mod_map_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
724{
725 sql_map_ctx_t *map_ctx = talloc_get_type_abort(uctx, sql_map_ctx_t);
726 fr_sql_query_t *query_ctx = map_ctx->query_ctx;
727 map_list_t const *maps = map_ctx->maps;
728 rlm_sql_t const *inst = map_ctx->inst;
729 map_t const *map;
731 sql_rcode_t ret;
732 char const **fields = NULL, *map_rhs;
733 rlm_sql_row_t row;
734 int i, j, field_cnt, rows = 0;
735 int field_index[MAX_SQL_FIELD_INDEX];
736 char map_rhs_buff[128];
737 bool found_field = false; /* Did we find any matching fields in the result set ? */
738
739 if (query_ctx->rcode != RLM_SQL_OK) {
740 RERROR("SQL query failed: %s", fr_table_str_by_value(sql_rcode_description_table, query_ctx->rcode, "<INVALID>"));
741 rcode = RLM_MODULE_FAIL;
742 goto finish;
743 }
744
745 /*
746 * Not every driver provides an sql_num_rows function
747 */
748 if (inst->driver->sql_num_rows) {
749 ret = inst->driver->sql_num_rows(query_ctx, &inst->config);
750 if (ret == 0) {
751 RDEBUG2("Server returned an empty result");
752 rcode = RLM_MODULE_NOTFOUND;
753 goto finish;
754 }
755
756 if (ret < 0) {
757 RERROR("Failed retrieving row count");
758 error:
759 rcode = RLM_MODULE_FAIL;
760 goto finish;
761 }
762 }
763
764 for (i = 0; i < MAX_SQL_FIELD_INDEX; i++) field_index[i] = -1;
765
766 /*
767 * Map proc only registered if driver provides an sql_fields function
768 */
769 ret = (inst->driver->sql_fields)(&fields, query_ctx, &inst->config);
770 if (ret != RLM_SQL_OK) {
771 RERROR("Failed retrieving field names: %s", fr_table_str_by_value(sql_rcode_description_table, ret, "<INVALID>"));
772 goto error;
773 }
774 fr_assert(fields);
775 field_cnt = talloc_array_length(fields);
776
777 if (RDEBUG_ENABLED3) for (j = 0; j < field_cnt; j++) RDEBUG3("Got field: %s", fields[j]);
778
779 /*
780 * Iterate over the maps, it's O(N2)ish but probably
781 * faster than building a radix tree each time the
782 * map set is evaluated (map->rhs can be dynamic).
783 */
784 for (map = map_list_head(maps), i = 0;
785 map && (i < MAX_SQL_FIELD_INDEX);
786 map = map_list_next(maps, map), i++) {
787 /*
788 * Expand the RHS to get the name of the SQL field
789 */
790 if (tmpl_expand(&map_rhs, map_rhs_buff, sizeof(map_rhs_buff),
791 request, map->rhs, NULL, NULL) < 0) {
792 RPERROR("Failed getting field name");
793 goto error;
794 }
795
796 for (j = 0; j < field_cnt; j++) {
797 if (strcasecmp(fields[j], map_rhs) != 0) continue;
798 field_index[i] = j;
799 found_field = true;
800 }
801 }
802
803 /*
804 * Couldn't resolve any map RHS values to fields
805 * in the result set.
806 */
807 if (!found_field) {
808 RDEBUG2("No fields matching map found in query result");
809 rcode = RLM_MODULE_NOOP;
810 goto finish;
811 }
812
813 /*
814 * We've resolved all the maps to result indexes, now convert
815 * the values at those indexes into fr_pair_ts.
816 *
817 * Note: Not all SQL client libraries provide a row count,
818 * so we have to do the count here.
819 */
820 while ((inst->fetch_row(p_result, NULL, request, query_ctx) == UNLANG_ACTION_CALCULATE_RESULT) &&
821 (query_ctx->rcode == RLM_SQL_OK)) {
822 row = query_ctx->row;
823 rows++;
824 for (map = map_list_head(maps), j = 0;
825 map && (j < MAX_SQL_FIELD_INDEX);
826 map = map_list_next(maps, map), j++) {
827 if (field_index[j] < 0) continue; /* We didn't find the map RHS in the field set */
828 if (!row[field_index[j]]) {
829 RWARN("Database returned NULL for %s", fields[j]);
830 continue;
831 }
832 if (map_to_request(request, map, _sql_map_proc_get_value, row[field_index[j]]) < 0) goto error;
833 }
834 }
835
836 if (query_ctx->rcode == RLM_SQL_ERROR) goto error;
837
838 if (rows == 0) {
839 RDEBUG2("SQL query returned no results");
840 rcode = RLM_MODULE_NOTFOUND;
841 }
842
843finish:
844 talloc_free(fields);
845 talloc_free(map_ctx);
846
847 RETURN_MODULE_RCODE(rcode);
848}
849
850/** Executes a SELECT query and maps the result to server attributes
851 *
852 * @param p_result Result of map expansion:
853 * - #RLM_MODULE_NOOP no rows were returned or columns matched.
854 * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
855 * - #RLM_MODULE_FAIL if a fault occurred.
856 * @param mod_inst #rlm_sql_t instance.
857 * @param proc_inst Instance data for this specific mod_proc call (unused).
858 * @param request The current request.
859 * @param query string to execute.
860 * @param maps Head of the map list.
861 * @return UNLANG_ACTION_CALCULATE_RESULT
862 */
863static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void const *mod_inst, UNUSED void *proc_inst, request_t *request,
864 fr_value_box_list_t *query, map_list_t const *maps)
865{
867 rlm_sql_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data, rlm_sql_thread_t);
868 fr_value_box_t *query_head = fr_value_box_list_head(query);
869 sql_map_ctx_t *map_ctx;
870 fr_value_box_t *vb = NULL;
871 rlm_sql_escape_uctx_t *escape_uctx = NULL;
872
873 fr_assert(inst->driver->sql_fields); /* Should have been caught during validation... */
874
875 if (!query_head) {
876 REDEBUG("Query cannot be (null)");
878 }
879
880 while ((vb = fr_value_box_list_next(query, vb))) {
881 if (fr_value_box_is_safe_for(vb, inst->driver)) continue;
882 if (!escape_uctx) escape_uctx = sql_escape_uctx_alloc(request, inst);
883 sql_box_escape(vb, escape_uctx);
884 }
885
887 query_head, query, FR_TYPE_STRING,
889 SIZE_MAX) < 0) {
890 RPEDEBUG("Failed concatenating input string");
892 }
893
894 MEM(map_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), sql_map_ctx_t));
895 *map_ctx = (sql_map_ctx_t) {
896 .inst = inst,
897 .maps = maps,
898 };
899
900 MEM(map_ctx->query_ctx = fr_sql_query_alloc(map_ctx, inst, request,
901 thread->trunk, query_head->vb_strvalue, SQL_QUERY_SELECT));
902
903 if (unlang_function_push(request, NULL, mod_map_resume, NULL, 0,
905
906 return unlang_function_push(request, inst->select, NULL, NULL, 0, UNLANG_SUB_FRAME, map_ctx->query_ctx);
907}
908
909/** xlat escape function for drivers which do not provide their own
910 *
911 */
912static ssize_t sql_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, void *arg)
913{
915 size_t len = 0;
916
917 while (in[0]) {
918 size_t utf8_len;
919
920 /*
921 * Allow all multi-byte UTF8 characters.
922 */
923 utf8_len = fr_utf8_char((uint8_t const *) in, -1);
924 if (utf8_len > 1) {
925 if (outlen <= utf8_len) break;
926
927 memcpy(out, in, utf8_len);
928 in += utf8_len;
929 out += utf8_len;
930
931 outlen -= utf8_len;
932 len += utf8_len;
933 continue;
934 }
935
936 /*
937 * Because we register our own escape function
938 * we're now responsible for escaping all special
939 * chars in an xlat expansion or attribute value.
940 */
941 switch (in[0]) {
942 case '\n':
943 if (outlen <= 2) break;
944 out[0] = '\\';
945 out[1] = 'n';
946
947 in++;
948 out += 2;
949 outlen -= 2;
950 len += 2;
951 break;
952
953 case '\r':
954 if (outlen <= 2) break;
955 out[0] = '\\';
956 out[1] = 'r';
957
958 in++;
959 out += 2;
960 outlen -= 2;
961 len += 2;
962 break;
963
964 case '\t':
965 if (outlen <= 2) break;
966 out[0] = '\\';
967 out[1] = 't';
968
969 in++;
970 out += 2;
971 outlen -= 2;
972 len += 2;
973 break;
974 }
975
976 /*
977 * Non-printable characters get replaced with their
978 * mime-encoded equivalents.
979 */
980 if ((in[0] < 32) ||
981 strchr(inst->config.allowed_chars, *in) == NULL) {
982 /*
983 * Only 3 or less bytes available.
984 */
985 if (outlen <= 3) {
986 break;
987 }
988
989 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
990 in++;
991 out += 3;
992 outlen -= 3;
993 len += 3;
994 continue;
995 }
996
997 /*
998 * Only one byte left.
999 */
1000 if (outlen <= 1) {
1001 break;
1002 }
1003
1004 /*
1005 * Allowed character.
1006 */
1007 *out = *in;
1008 out++;
1009 in++;
1010 outlen--;
1011 len++;
1012 }
1013 *out = '\0';
1014 return len;
1015}
1016
1017/*
1018 * Set the SQL user name.
1019 *
1020 * We don't call the escape function here. The resulting string
1021 * will be escaped later in the queries xlat so we don't need to
1022 * escape it twice. (it will make things wrong if we have an
1023 * escape candidate character in the username)
1024 */
1025static void sql_set_user(rlm_sql_t const *inst, request_t *request, fr_value_box_t *user)
1026{
1027 fr_pair_t *vp = NULL;
1028
1029 fr_assert(request->packet != NULL);
1030
1031 MEM(pair_update_request(&vp, inst->sql_user) >= 0);
1032 if(!user || (user->type != FR_TYPE_STRING)) {
1034 return;
1035 }
1036
1037 /*
1038 * Replace any existing SQL-User-Name with new value
1039 */
1040 fr_pair_value_bstrdup_buffer(vp, user->vb_strvalue, user->tainted);
1041 RDEBUG2("SQL-User-Name set to '%pV'", &vp->data);
1042}
1043
1044/*
1045 * Do a set/unset user, so it's a bit clearer what's going on.
1046 */
1047#define sql_unset_user(_i, _r) fr_pair_delete_by_da(&_r->request_pairs, _i->sql_user)
1048
1049
1054
1055static unlang_action_t sql_get_grouplist_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
1056{
1057 sql_group_ctx_t *group_ctx = talloc_get_type_abort(uctx, sql_group_ctx_t);
1058 fr_sql_query_t *query_ctx = group_ctx->query_ctx;
1059 rlm_sql_t const *inst = group_ctx->inst;
1060 rlm_sql_row_t row;
1061 rlm_sql_grouplist_t *entry = group_ctx->groups;
1062
1063 if (query_ctx->rcode != RLM_SQL_OK) {
1064 error:
1065 talloc_free(query_ctx);
1067 }
1068
1069 while ((inst->fetch_row(p_result, NULL, request, query_ctx) == UNLANG_ACTION_CALCULATE_RESULT) &&
1070 (query_ctx->rcode == RLM_SQL_OK)) {
1071 row = query_ctx->row;
1072 if (!row[0]){
1073 RDEBUG2("row[0] returned NULL");
1074 goto error;
1075 }
1076
1077 if (!entry) {
1078 group_ctx->groups = talloc_zero(group_ctx, rlm_sql_grouplist_t);
1079 entry = group_ctx->groups;
1080 } else {
1081 entry->next = talloc_zero(group_ctx, rlm_sql_grouplist_t);
1082 entry = entry->next;
1083 }
1084 entry->next = NULL;
1085 entry->name = talloc_typed_strdup(entry, row[0]);
1086
1087 group_ctx->num_groups++;
1088 }
1089
1090 talloc_free(query_ctx);
1092}
1093
1095{
1096 rlm_sql_t const *inst = group_ctx->inst;
1097
1098 /* NOTE: sql_set_user should have been run before calling this function */
1099
1100 if (!group_ctx->query || (group_ctx->query->vb_length == 0)) return UNLANG_ACTION_CALCULATE_RESULT;
1101
1102 MEM(group_ctx->query_ctx = fr_sql_query_alloc(group_ctx, inst, request, trunk,
1103 group_ctx->query->vb_strvalue, SQL_QUERY_SELECT));
1104
1105 if (unlang_function_push(request, NULL, sql_get_grouplist_resume, NULL, 0, UNLANG_SUB_FRAME, group_ctx) < 0) return UNLANG_ACTION_FAIL;
1106
1107 return unlang_function_push(request, inst->select, NULL, NULL, 0, UNLANG_SUB_FRAME, group_ctx->query_ctx);
1108}
1109
1110typedef struct {
1111 fr_value_box_list_t query;
1114
1115/** Compare list of groups returned from SQL query to xlat argument.
1116 *
1117 * Called after the SQL query has completed and group list has been built.
1118 */
1120 UNUSED request_t *request, fr_value_box_list_t *in)
1121{
1122 sql_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(xctx->rctx, sql_group_xlat_ctx_t);
1123 sql_group_ctx_t *group_ctx = talloc_get_type_abort(xlat_ctx->group_ctx, sql_group_ctx_t);
1124 fr_value_box_t *arg = fr_value_box_list_head(in);
1125 char const *name = arg->vb_strvalue;
1126 fr_value_box_t *vb;
1127 rlm_sql_grouplist_t *entry;
1128
1130
1132 for (entry = group_ctx->groups; entry != NULL; entry = entry->next) {
1133 if (strcmp(entry->name, name) == 0) {
1134 vb->vb_bool = true;
1135 break;
1136 }
1137 }
1139
1141
1142 return XLAT_ACTION_DONE;
1143}
1144
1145/** Run SQL query for group membership to return list of groups
1146 *
1147 * Called after group membership query tmpl is expanded
1148 */
1150 request_t *request, UNUSED fr_value_box_list_t *in)
1151{
1152 sql_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(xctx->rctx, sql_group_xlat_ctx_t);
1153 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
1154 rlm_sql_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_sql_thread_t);
1155 fr_value_box_t *query;
1156
1157 query = fr_value_box_list_head(&xlat_ctx->query);
1158 if (!query) return XLAT_ACTION_FAIL;
1159
1160 MEM(xlat_ctx->group_ctx = talloc(xlat_ctx, sql_group_ctx_t));
1161
1162 *xlat_ctx->group_ctx = (sql_group_ctx_t) {
1163 .inst = inst,
1164 .query = query,
1165 };
1166
1168
1169 if (sql_get_grouplist(xlat_ctx->group_ctx, thread->trunk, request) != UNLANG_ACTION_PUSHED_CHILD)
1170 return XLAT_ACTION_FAIL;
1171
1173}
1174
1175
1176/** Check if the user is a member of a particular group
1177 *
1178@verbatim
1179%sql.group(<name>)
1180@endverbatim
1181 *
1182 * @ingroup xlat_functions
1183 */
1185 request_t *request, UNUSED fr_value_box_list_t *in)
1186{
1187 sql_group_xlat_call_env_t *call_env = talloc_get_type_abort(xctx->env_data, sql_group_xlat_call_env_t);
1189 rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
1190
1191 if (!call_env->membership_query) {
1192 RWARN("Cannot check group membership - group_membership_query not set");
1193 return XLAT_ACTION_FAIL;
1194 }
1195
1196 /*
1197 * Set the user attr here
1198 */
1199 sql_set_user(inst, request, &call_env->user);
1200
1202 fr_value_box_list_init(&xlat_ctx->query);
1203
1205 if (unlang_tmpl_push(xlat_ctx, &xlat_ctx->query, request, call_env->membership_query, NULL) < 0) return XLAT_ACTION_FAIL;
1207}
1208
1209/** Process a "check" map
1210 *
1211 * Any entries using an assignment operator will be moved to the reply map
1212 * for later merging into the request.
1213 *
1214 * @param request Current request.
1215 * @param check_map to process.
1216 * @param reply_map where any assignment entries will be moved.
1217 * @return
1218 * - 0 if all the check entries pass.
1219 * - -1 if the checks fail.
1220 */
1221static int check_map_process(request_t *request, map_list_t *check_map, map_list_t *reply_map)
1222{
1223 map_t *map, *next;
1224
1225 for (map = map_list_head(check_map);
1226 map != NULL;
1227 map = next) {
1228 next = map_list_next(check_map, map);
1229
1230 if (fr_assignment_op[map->op]) {
1231 (void) map_list_remove(check_map, map);
1232 map_list_insert_tail(reply_map, map);
1233 continue;
1234 }
1235
1236 if (!fr_comparison_op[map->op]) {
1237 REDEBUG("Invalid operator '%s'", fr_tokens[map->op]);
1238 goto fail;
1239 }
1240
1241 if (fr_type_is_structural(tmpl_attr_tail_da(map->lhs)->type) &&
1242 (map->op != T_OP_CMP_TRUE) && (map->op != T_OP_CMP_FALSE)) {
1243 REDEBUG("Invalid comparison for structural type");
1244 goto fail;
1245 }
1246
1247 RDEBUG2(" &%s %s %s", map->lhs->name, fr_tokens[map->op], map->rhs->name);
1248 if (radius_legacy_map_cmp(request, map) != 1) {
1249 fail:
1250 map_list_talloc_free(check_map);
1251 map_list_talloc_free(reply_map);
1252 RDEBUG2("failed match: skipping this entry");
1253 return -1;
1254 }
1255 }
1256 return 0;
1257}
1258
1260{
1261 if (!to_free->inst->sql_escape_arg) (void) request_data_get(to_free->request, (void *)sql_escape_uctx_alloc, 0);
1262 map_list_talloc_free(&to_free->check_tmp);
1263 map_list_talloc_free(&to_free->reply_tmp);
1264 sql_unset_user(to_free->inst, to_free->request);
1265
1266 return 0;
1267}
1268
1269/** Resume function called after authorization group / profile expansion of check / reply query tmpl
1270 *
1271 * Groups and profiles are treated almost identically except:
1272 * - groups are read from an SQL query
1273 * - profiles are read from &control.User-Profile
1274 * - if `cache_groups` is set, groups populate &control.SQL-Group
1275 *
1276 * Profiles are handled after groups, and will not happend if the last group resulted in `Fall-Through = no`
1277 *
1278 * Before each query is run, &request.SQL-Group is populated with the value of the group being evaluated.
1279 *
1280 * @param p_result Result of current authorization.
1281 * @param priority Unused.
1282 * @param request Current request.
1283 * @param uctx Current authorization context.
1284 * @return one of the RLM_MODULE_* values.
1285 */
1286static unlang_action_t mod_autz_group_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
1287{
1288 sql_autz_ctx_t *autz_ctx = talloc_get_type_abort(uctx, sql_autz_ctx_t);
1289 sql_autz_call_env_t *call_env = autz_ctx->call_env;
1290 sql_group_ctx_t *group_ctx = autz_ctx->group_ctx;
1291 fr_sql_map_ctx_t *map_ctx = autz_ctx->map_ctx;
1292 rlm_sql_t const *inst = autz_ctx->inst;
1293 fr_value_box_t *query = fr_value_box_list_pop_head(&autz_ctx->query);
1294 sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT;
1295 fr_pair_t *vp;
1296
1297 switch (*p_result) {
1300
1301 default:
1302 break;
1303 }
1304
1305 switch(autz_ctx->status) {
1308 MEM(autz_ctx->group_ctx = talloc(autz_ctx, sql_group_ctx_t));
1309 *autz_ctx->group_ctx = (sql_group_ctx_t) {
1310 .inst = inst,
1311 .query = query,
1312 };
1313
1314 if (sql_get_grouplist(autz_ctx->group_ctx, autz_ctx->trunk, request) == UNLANG_ACTION_PUSHED_CHILD) {
1317 }
1318
1319 group_ctx = autz_ctx->group_ctx;
1320
1322
1324 talloc_free(group_ctx->query);
1325
1326 if (group_ctx->num_groups == 0) {
1327 RDEBUG2("User not found in any groups");
1328 break;
1329 }
1330 fr_assert(group_ctx->groups);
1331
1332 RDEBUG2("User found in the group table");
1333 autz_ctx->user_found = true;
1334 autz_ctx->group = group_ctx->groups;
1335 MEM(pair_update_request(&autz_ctx->sql_group, inst->group_da) >= 0);
1336
1337 next_group:
1338 fr_pair_value_strdup(autz_ctx->sql_group, autz_ctx->group->name, true);
1339 autz_ctx->status = SQL_AUTZ_GROUP_CHECK;
1341
1343 next_profile:
1344 if (autz_ctx->status & SQL_AUTZ_STAGE_PROFILE) {
1345 fr_pair_value_strdup(autz_ctx->sql_group, autz_ctx->profile->vp_strvalue, true);
1346 autz_ctx->status = SQL_AUTZ_PROFILE_CHECK;
1347 }
1348 RDEBUG3("Processing %s %pV",
1349 autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "group" : "profile", &autz_ctx->sql_group->data);
1350 if (inst->config.cache_groups && autz_ctx->status & SQL_AUTZ_STAGE_GROUP) {
1351 MEM(pair_append_control(&vp, inst->group_da) >= 0);
1352 fr_pair_value_strdup(vp, autz_ctx->group->name, true);
1353 }
1354
1355 if (call_env->group_check_query) {
1357 if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request,
1358 call_env->group_check_query, NULL) < 0) RETURN_MODULE_FAIL;
1360 }
1361
1362 if (call_env->group_reply_query) goto group_reply_push;
1363
1364 break;
1365
1368 *autz_ctx->map_ctx = (fr_sql_map_ctx_t) {
1369 .ctx = autz_ctx,
1370 .inst = inst,
1371 .out = &autz_ctx->check_tmp,
1372 .list = request_attr_request,
1373 .query = query,
1374 };
1375
1377 if (sql_get_map_list(request, map_ctx, autz_ctx->trunk) == UNLANG_ACTION_PUSHED_CHILD) {
1380 }
1381
1383
1386 talloc_free(map_ctx->query);
1387
1388 /*
1389 * If we got check rows we need to process them before we decide to
1390 * process the reply rows
1391 */
1392 if (map_ctx->rows > 0) {
1393 if (check_map_process(request, &autz_ctx->check_tmp, &autz_ctx->reply_tmp) < 0) {
1394 map_list_talloc_free(&autz_ctx->check_tmp);
1395 goto next_group_find;
1396 }
1397 RDEBUG2("%s \"%pV\": Conditional check items matched",
1398 autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "Group" : "Profile", &autz_ctx->sql_group->data);
1399 } else {
1400 RDEBUG2("%s \"%pV\": Conditional check items matched (empty)",
1401 autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "Group" : "Profile", &autz_ctx->sql_group->data);
1402 }
1403
1404 if (autz_ctx->rcode == RLM_MODULE_NOOP) autz_ctx->rcode = RLM_MODULE_OK;
1405
1406 map_list_talloc_free(&autz_ctx->check_tmp);
1407
1408 if (call_env->group_reply_query) {
1409 group_reply_push:
1411 if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request,
1412 call_env->group_reply_query, NULL) < 0) RETURN_MODULE_FAIL;
1415 }
1416
1417 if (map_list_num_elements(&autz_ctx->reply_tmp)) goto group_attr_cache;
1418
1419 goto next_group_find;
1420
1423 *autz_ctx->map_ctx = (fr_sql_map_ctx_t) {
1424 .ctx = autz_ctx,
1425 .inst = inst,
1426 .out = &autz_ctx->reply_tmp,
1427 .list = request_attr_reply,
1428 .query = query,
1429 };
1430
1432 if (sql_get_map_list(request, map_ctx, autz_ctx->trunk) == UNLANG_ACTION_PUSHED_CHILD) {
1435 }
1436
1438
1441 talloc_free(map_ctx->query);
1442
1443 if (map_ctx->rows == 0) {
1444 do_fall_through = FALL_THROUGH_DEFAULT;
1445 goto group_attr_cache;
1446 }
1447
1448 fr_assert(!map_list_empty(&autz_ctx->reply_tmp)); /* coverity, among others */
1449 do_fall_through = fall_through(&autz_ctx->reply_tmp);
1450
1451 group_attr_cache:
1452 if (inst->config.cache_groups && autz_ctx->status & SQL_AUTZ_STAGE_GROUP) {
1453 MEM(pair_append_control(&vp, inst->group_da) >= 0);
1454 fr_pair_value_strdup(vp, autz_ctx->group->name, true);
1455 }
1456
1457 if (map_list_num_elements(&autz_ctx->reply_tmp) == 0) goto next_group_find;
1458 RDEBUG2("%s \"%pV\": Merging control and reply items",
1459 autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "Group" : "Profile", &autz_ctx->sql_group->data);
1460 autz_ctx->rcode = RLM_MODULE_UPDATED;
1461
1462 RINDENT();
1463 if (radius_legacy_map_list_apply(request, &autz_ctx->reply_tmp, NULL) < 0) {
1464 RPEDEBUG("Failed applying reply item");
1465 REXDENT();
1467 }
1468 REXDENT();
1469 map_list_talloc_free(&autz_ctx->reply_tmp);
1470
1471 next_group_find:
1472 if (do_fall_through != FALL_THROUGH_YES) break;
1473 if (autz_ctx->status & SQL_AUTZ_STAGE_PROFILE) {
1474 autz_ctx->profile = fr_pair_find_by_da(&request->control_pairs, autz_ctx->profile, attr_user_profile);
1475 if (autz_ctx->profile) goto next_profile;
1476 break;
1477 }
1478 autz_ctx->group = autz_ctx->group->next;
1479 if (autz_ctx->group) goto next_group;
1480
1481 break;
1482
1483 default:
1484 fr_assert(0);
1485 }
1486
1487 /*
1488 * If group processing has completed, check to see if profile processing should be done
1489 */
1490 if ((autz_ctx->status & SQL_AUTZ_STAGE_GROUP) &&
1491 ((do_fall_through == FALL_THROUGH_YES) ||
1492 (inst->config.read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT)))) {
1493 RDEBUG3("... falling-through to profile processing");
1494
1495 autz_ctx->profile = fr_pair_find_by_da(&request->control_pairs, NULL, attr_user_profile);
1496 if (autz_ctx->profile) {
1497 MEM(pair_update_request(&autz_ctx->sql_group, inst->group_da) >= 0);
1498 autz_ctx->status = SQL_AUTZ_PROFILE_START;
1499 goto next_profile;
1500 }
1501 }
1502
1503 if (!autz_ctx->user_found) RETURN_MODULE_NOTFOUND;
1504
1505 RETURN_MODULE_RCODE(autz_ctx->rcode);
1506}
1507
1508/** Resume function called after authorization check / reply tmpl expansion
1509 *
1510 * @param p_result Result of current authorization.
1511 * @param priority Unused.
1512 * @param request Current request.
1513 * @param uctx Current authorization context.
1514 * @return one of the RLM_MODULE_* values.
1515 */
1516static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, int *priority, request_t *request, void *uctx)
1517{
1518 sql_autz_ctx_t *autz_ctx = talloc_get_type_abort(uctx, sql_autz_ctx_t);
1519 sql_autz_call_env_t *call_env = autz_ctx->call_env;
1520 rlm_sql_t const *inst = autz_ctx->inst;
1521 fr_value_box_t *query = fr_value_box_list_pop_head(&autz_ctx->query);
1522 sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT;
1523 fr_sql_map_ctx_t *map_ctx = autz_ctx->map_ctx;
1524
1525 /*
1526 * If a previous async call returned one of the "failure" results just return.
1527 */
1528 switch (*p_result) {
1531
1532 default:
1533 break;
1534 }
1535
1536 switch(autz_ctx->status) {
1537 case SQL_AUTZ_CHECK:
1538 *autz_ctx->map_ctx = (fr_sql_map_ctx_t) {
1539 .ctx = autz_ctx,
1540 .inst = inst,
1541 .out = &autz_ctx->check_tmp,
1542 .list = request_attr_request,
1543 .query = query,
1544 };
1545
1547 if (sql_get_map_list(request, map_ctx, autz_ctx->trunk) == UNLANG_ACTION_PUSHED_CHILD){
1548 autz_ctx->status = SQL_AUTZ_CHECK_RESUME;
1550 }
1551
1553
1555 talloc_free(map_ctx->query);
1556
1557 if (map_ctx->rows == 0) goto skip_reply; /* Don't need to handle map entries we don't have */
1558
1559 /*
1560 * Only do this if *some* check pairs were returned
1561 */
1562 RDEBUG2("User found in radcheck table");
1563 autz_ctx->user_found = true;
1564
1565 if (check_map_process(request, &autz_ctx->check_tmp, &autz_ctx->reply_tmp) < 0) goto skip_reply;
1566 RDEBUG2("Conditional check items matched");
1567
1568 autz_ctx->rcode = RLM_MODULE_OK;
1569 map_list_talloc_free(&autz_ctx->check_tmp);
1570
1571 if (!call_env->reply_query) goto skip_reply;
1572
1574 if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request, call_env->reply_query, NULL) < 0) RETURN_MODULE_FAIL;
1575 autz_ctx->status = SQL_AUTZ_REPLY;
1577
1578 case SQL_AUTZ_REPLY:
1579 *autz_ctx->map_ctx = (fr_sql_map_ctx_t) {
1580 .ctx = autz_ctx,
1581 .inst = inst,
1582 .out = &autz_ctx->reply_tmp,
1583 .list = request_attr_reply,
1584 .query = query,
1585 };
1586
1588 if (sql_get_map_list(request, map_ctx, autz_ctx->trunk) == UNLANG_ACTION_PUSHED_CHILD){
1589 autz_ctx->status = SQL_AUTZ_REPLY_RESUME;
1591 }
1592
1594
1596 talloc_free(map_ctx->query);
1597
1598 if (map_ctx->rows == 0) goto skip_reply;
1599
1600 do_fall_through = fall_through(&autz_ctx->reply_tmp);
1601
1602 RDEBUG2("User found in radreply table");
1603 autz_ctx->user_found = true;
1604
1605 skip_reply:
1606 if (map_list_num_elements(&autz_ctx->reply_tmp)) {
1607 RDEBUG2("Merging control and reply items");
1608 RINDENT();
1609 if (radius_legacy_map_list_apply(request, &autz_ctx->reply_tmp, NULL) < 0) {
1610 RPEDEBUG("Failed applying item");
1611 REXDENT();
1613 }
1614 REXDENT();
1615
1616 autz_ctx->rcode = RLM_MODULE_UPDATED;
1617 map_list_talloc_free(&autz_ctx->reply_tmp);
1618 }
1619
1620 if ((do_fall_through == FALL_THROUGH_YES) ||
1621 (inst->config.read_groups && (do_fall_through == FALL_THROUGH_DEFAULT))) {
1622 RDEBUG3("... falling-through to group processing");
1623
1624 if (!call_env->membership_query) {
1625 RWARN("Cannot check groups when group_membership_query is not set");
1626 break;
1627 }
1628
1629 if (!call_env->group_check_query && !call_env->group_reply_query) {
1630 RWARN("Cannot process groups when neither authorize_group_check_query nor authorize_group_check_query are set");
1631 break;
1632 }
1633
1635 if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request,
1636 call_env->membership_query, NULL) < 0) RETURN_MODULE_FAIL;
1637 autz_ctx->status = SQL_AUTZ_GROUP_MEMB;
1639 }
1640
1641 if ((do_fall_through == FALL_THROUGH_YES) ||
1642 (inst->config.read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT))) {
1643 RDEBUG3("... falling-through to profile processing");
1644
1645 if (!call_env->group_check_query && !call_env->group_reply_query) {
1646 RWARN("Cannot process profiles when neither authorize_group_check_query nor authorize_group_check_query are set");
1647 break;
1648 }
1649
1650 autz_ctx->profile = fr_pair_find_by_da(&request->control_pairs, NULL, attr_user_profile);
1651 if (!autz_ctx->profile) break;
1652
1653 MEM(pair_update_request(&autz_ctx->sql_group, inst->group_da) >= 0);
1654 autz_ctx->status = SQL_AUTZ_PROFILE_START;
1655 return mod_autz_group_resume(p_result, priority, request, autz_ctx);
1656 }
1657 break;
1658
1659 default:
1660 fr_assert(0);
1661 }
1662
1663 if (!autz_ctx->user_found) RETURN_MODULE_NOTFOUND;
1664 RETURN_MODULE_RCODE(autz_ctx->rcode);
1665}
1666
1667/** Start of module authorize method
1668 *
1669 * Pushes the tmpl relating to the first required query for evaluation
1670 */
1671static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1672{
1674 rlm_sql_thread_t *thread = talloc_get_type_abort(mctx->thread, rlm_sql_thread_t);
1675 sql_autz_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, sql_autz_call_env_t);
1676 sql_autz_ctx_t *autz_ctx;
1677
1678 fr_assert(request->packet != NULL);
1679 fr_assert(request->reply != NULL);
1680
1681 if (!call_env->check_query && !call_env->reply_query && !(inst->config.read_groups && call_env->membership_query)) {
1682 RWDEBUG("No authorization checks configured, returning noop");
1684 }
1685
1686 /*
1687 * Set and check the user attr here
1688 */
1689 sql_set_user(inst, request, &call_env->user);
1690
1691 MEM(autz_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), sql_autz_ctx_t));
1692 *autz_ctx = (sql_autz_ctx_t) {
1693 .inst = inst,
1694 .call_env = call_env,
1695 .request = request,
1696 .trunk = thread->trunk,
1697 .rcode = RLM_MODULE_NOOP
1698 };
1699 map_list_init(&autz_ctx->check_tmp);
1700 map_list_init(&autz_ctx->reply_tmp);
1701 MEM(autz_ctx->map_ctx = talloc_zero(autz_ctx, fr_sql_map_ctx_t));
1702 talloc_set_destructor(autz_ctx, sql_autz_ctx_free);
1703
1704 if (unlang_function_push(request, NULL,
1706 NULL, 0, UNLANG_SUB_FRAME, autz_ctx) < 0) {
1707 error:
1708 talloc_free(autz_ctx);
1710 }
1711
1712 fr_value_box_list_init(&autz_ctx->query);
1713
1714 /*
1715 * Query the check table to find any conditions associated with this user/realm/whatever...
1716 */
1717 if (call_env->check_query) {
1718 if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request, call_env->check_query, NULL) < 0) goto error;
1719 autz_ctx->status = SQL_AUTZ_CHECK;
1721 }
1722
1723 if (call_env->reply_query) {
1724 if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request, call_env->reply_query, NULL) < 0) goto error;
1725 autz_ctx->status = SQL_AUTZ_REPLY;
1727 }
1728
1729 /*
1730 * Neither check nor reply queries were set, so we must be doing group stuff
1731 */
1732 if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request, call_env->membership_query, NULL) < 0) goto error;
1733 autz_ctx->status = SQL_AUTZ_GROUP_MEMB;
1735}
1736
1737/** Tidy up when freeing an SQL redundant context
1738 *
1739 * Release the connection handle and unset the SQL-User attribute.
1740 */
1742{
1743 if (!to_free->inst->sql_escape_arg) (void) request_data_get(to_free->request, (void *)sql_escape_uctx_alloc, 0);
1744 sql_unset_user(to_free->inst, to_free->request);
1745
1746 return 0;
1747}
1748
1749static unlang_action_t mod_sql_redundant_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx);
1750
1751/** Resume function called after executing an SQL query in a redundant list of queries.
1752 *
1753 * @param p_result Result of current module call.
1754 * @param priority Unused.
1755 * @param request Current request.
1756 * @param uctx Current redundant sql context.
1757 * @return one of the RLM_MODULE_* values.
1758 */
1759static unlang_action_t mod_sql_redundant_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
1760{
1761 sql_redundant_ctx_t *redundant_ctx = talloc_get_type_abort(uctx, sql_redundant_ctx_t);
1762 sql_redundant_call_env_t *call_env = redundant_ctx->call_env;
1763 rlm_sql_t const *inst = redundant_ctx->inst;
1764 fr_sql_query_t *query_ctx = redundant_ctx->query_ctx;
1765 int numaffected = 0;
1766 tmpl_t *next_query;
1767
1768 RDEBUG2("SQL query returned: %s", fr_table_str_by_value(sql_rcode_description_table, query_ctx->rcode, "<INVALID>"));
1769
1770 switch (query_ctx->rcode) {
1771 /*
1772 * Query was a success! Now we just need to check if it did anything.
1773 */
1774 case RLM_SQL_OK:
1775 break;
1776
1777 /*
1778 * A general, unrecoverable server fault.
1779 */
1780 case RLM_SQL_ERROR:
1781 /*
1782 * If we get RLM_SQL_RECONNECT it means all connections in the pool
1783 * were exhausted, and we couldn't create a new connection,
1784 * so we do not need to call fr_pool_connection_release.
1785 */
1786 case RLM_SQL_RECONNECT:
1788
1789 /*
1790 * Query was invalid, this is a terminal error.
1791 */
1794
1795 /*
1796 * Driver found an error (like a unique key constraint violation)
1797 * that hinted it might be a good idea to try an alternative query.
1798 */
1799 case RLM_SQL_ALT_QUERY:
1800 goto next;
1801
1803 break;
1804 }
1805
1806 /*
1807 * We need to have updated something for the query to have been
1808 * counted as successful.
1809 */
1810 numaffected = (inst->driver->sql_affected_rows)(query_ctx, &inst->config);
1811 TALLOC_FREE(query_ctx);
1812 RDEBUG2("%i record(s) updated", numaffected);
1813
1814 if (numaffected > 0) RETURN_MODULE_OK; /* A query succeeded, were done! */
1815next:
1816 /*
1817 * Look to see if there are any more queries to expand
1818 */
1819 talloc_free(query_ctx);
1820 redundant_ctx->query_no++;
1821 if (redundant_ctx->query_no >= talloc_array_length(call_env->query)) RETURN_MODULE_NOOP;
1822 next_query = *(tmpl_t **)((uint8_t *)call_env->query + sizeof(void *) * redundant_ctx->query_no);
1824 if (unlang_tmpl_push(redundant_ctx, &redundant_ctx->query, request, next_query, NULL) < 0) RETURN_MODULE_FAIL;
1825
1826 RDEBUG2("Trying next query...");
1827
1829}
1830
1831
1832/** Resume function called after expansion of next query in a redundant list of queries
1833 *
1834 * @param p_result Result of current module call.
1835 * @param priority Unused.
1836 * @param request Current request.
1837 * @param uctx Current redundant sql context.
1838 * @return one of the RLM_MODULE_* values.
1839 */
1840static unlang_action_t mod_sql_redundant_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
1841{
1842 sql_redundant_ctx_t *redundant_ctx = talloc_get_type_abort(uctx, sql_redundant_ctx_t);
1843 sql_redundant_call_env_t *call_env = redundant_ctx->call_env;
1844 rlm_sql_t const *inst = redundant_ctx->inst;
1845
1846 redundant_ctx->query_vb = fr_value_box_list_pop_head(&redundant_ctx->query);
1847 if (!redundant_ctx->query_vb) RETURN_MODULE_FAIL;
1848
1849 if ((call_env->filename.type == FR_TYPE_STRING) && (call_env->filename.vb_length > 0)) {
1850 rlm_sql_query_log(inst, call_env->filename.vb_strvalue, redundant_ctx->query_vb->vb_strvalue);
1851 }
1852
1853 MEM(redundant_ctx->query_ctx = fr_sql_query_alloc(redundant_ctx, inst, request, redundant_ctx->trunk,
1854 redundant_ctx->query_vb->vb_strvalue, SQL_QUERY_OTHER));
1855
1857
1858 return unlang_function_push(request, inst->query, NULL, NULL, 0, UNLANG_SUB_FRAME, redundant_ctx->query_ctx);
1859}
1860
1861/** Generic module call for failing between a bunch of queries.
1862 *
1863 * Used for `accounting` and `send` module calls
1864 *
1865 */
1866static unlang_action_t CC_HINT(nonnull) mod_sql_redundant(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1867{
1869 rlm_sql_thread_t *thread = talloc_get_type_abort(mctx->thread, rlm_sql_thread_t);
1870 sql_redundant_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, sql_redundant_call_env_t);
1871 sql_redundant_ctx_t *redundant_ctx;
1872
1873 /*
1874 * No query to expand - do nothing.
1875 */
1876 if (!call_env->query) {
1877 RWARN("No query configured");
1879 }
1880
1881 MEM(redundant_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), sql_redundant_ctx_t));
1882 *redundant_ctx = (sql_redundant_ctx_t) {
1883 .inst = inst,
1884 .request = request,
1885 .trunk = thread->trunk,
1886 .call_env = call_env,
1887 .query_no = 0
1888 };
1889 talloc_set_destructor(redundant_ctx, sql_redundant_ctx_free);
1890
1891 sql_set_user(inst, request, &call_env->user);
1892
1893 if (unlang_function_push(request, NULL, mod_sql_redundant_resume, NULL, 0,
1894 UNLANG_SUB_FRAME, redundant_ctx) < 0) RETURN_MODULE_FAIL;
1895
1896 fr_value_box_list_init(&redundant_ctx->query);
1897 if (unlang_tmpl_push(redundant_ctx, &redundant_ctx->query, request, *call_env->query, NULL) < 0) RETURN_MODULE_FAIL;
1898
1900}
1901
1902static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
1903 CONF_ITEM *ci,
1904 call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1905{
1906 CONF_SECTION const *subcs = NULL, *subsubcs = NULL;
1907 CONF_PAIR const *to_parse = NULL;
1908 tmpl_t *parsed_tmpl;
1909 call_env_parsed_t *parsed_env;
1910 tmpl_rules_t our_rules;
1911 char *section2, *p;
1912
1914
1915 /*
1916 * The call env subsection which calls this has CF_IDENT_ANY as its name
1917 * which results in finding the first child section of the module config.
1918 * We actually want the whole module config - so go to the parent.
1919 */
1920 ci = cf_parent(ci);
1921
1922 /*
1923 * Find the instance of "logfile" to parse
1924 *
1925 * If the module call is from `accounting Start` then first is
1926 * <module> { accounting { start { logfile } } }
1927 * then
1928 * <module> { accounting { logfile } }
1929 * falling back to
1930 * <module> { logfile }
1931 */
1933 if (subcs) {
1934 if (cec->asked->name2) {
1935 section2 = talloc_strdup(NULL, cec->asked->name2);
1936 p = section2;
1937 while (*p != '\0') {
1938 *(p) = tolower((uint8_t)*p);
1939 p++;
1940 }
1941 subsubcs = cf_section_find(subcs, section2, CF_IDENT_ANY);
1942 talloc_free(section2);
1943 if (subsubcs) to_parse = cf_pair_find(subsubcs, "logfile");
1944 }
1945 if (!to_parse) to_parse = cf_pair_find(subcs, "logfile");
1946 }
1947
1948 if (!to_parse) to_parse = cf_pair_find(cf_item_to_section(ci), "logfile");
1949
1950 if (!to_parse) return 0;
1951
1952 /*
1953 * Use filename safety escape functions
1954 */
1955 our_rules = *t_rules;
1959 our_rules.literals_safe_for = our_rules.escape.safe_for;
1960
1961 MEM(parsed_env = call_env_parsed_add(ctx, out,
1963
1964 if (tmpl_afrom_substr(parsed_env, &parsed_tmpl,
1965 &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
1967 &our_rules) < 0) {
1968 error:
1969 call_env_parsed_free(out, parsed_env);
1970 return -1;
1971 }
1972 if (tmpl_needs_resolving(parsed_tmpl) &&
1973 (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = our_rules.attr.dict_def }) < 0)) goto error;
1974
1975 call_env_parsed_set_tmpl(parsed_env, parsed_tmpl);
1976
1977 return 0;
1978}
1979
1980static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
1981 CONF_ITEM *ci,
1982 call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1983{
1985 CONF_SECTION const *subcs = NULL;
1986 CONF_PAIR const *to_parse = NULL;
1987 tmpl_t *parsed_tmpl;
1988 call_env_parsed_t *parsed_env;
1989 tmpl_rules_t our_rules;
1990 char *section2, *p;
1991 ssize_t count, slen, multi_index = 0;
1992
1994
1995 /*
1996 * Find the instance(s) of "query" to parse
1997 *
1998 * If the module call is from `accounting Start` then it should be
1999 * <module> { accounting { start { query } } }
2000 */
2001 section2 = talloc_strdup(NULL, section_name_str(cec->asked->name2));
2002 p = section2;
2003 while (*p != '\0') {
2004 *(p) = tolower((uint8_t)*p);
2005 p++;
2006 }
2007 subcs = cf_section_find(cf_item_to_section(ci), section2, CF_IDENT_ANY);
2008 if (!subcs) {
2009 no_query:
2010 cf_log_warn(ci, "No query found for \"%s.%s\", this query will be disabled",
2011 section_name_str(cec->asked->name1), section2);
2012 talloc_free(section2);
2013 return 0;
2014 }
2015 count = cf_pair_count(subcs, "query");
2016 if (count == 0) goto no_query;
2017
2018 talloc_free(section2);
2019
2020 /*
2021 * Use module specific escape functions
2022 */
2023 our_rules = *t_rules;
2024 our_rules.escape = (tmpl_escape_t) {
2026 .uctx = { .func = { .uctx = inst, .alloc = sql_escape_uctx_alloc }, .type = TMPL_ESCAPE_UCTX_ALLOC_FUNC },
2027 .safe_for = SQL_SAFE_FOR,
2028 .mode = TMPL_ESCAPE_PRE_CONCAT,
2029 };
2030 our_rules.literals_safe_for = our_rules.escape.safe_for;
2031
2032 while ((to_parse = cf_pair_find_next(subcs, to_parse, "query"))) {
2033 MEM(parsed_env = call_env_parsed_add(ctx, out,
2037 }));
2038
2039 slen = tmpl_afrom_substr(parsed_env, &parsed_tmpl,
2040 &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
2042 &our_rules);
2043 if (slen <= 0) {
2044 cf_canonicalize_error(to_parse, slen, "Failed parsing query", cf_pair_value(to_parse));
2045 error:
2046 call_env_parsed_free(out, parsed_env);
2047 return -1;
2048 }
2049 if (tmpl_needs_resolving(parsed_tmpl) &&
2050 (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = our_rules.attr.dict_def }) < 0)) {
2051 cf_log_perr(to_parse, "Failed resolving query");
2052 goto error;
2053 }
2054
2055 call_env_parsed_set_multi_index(parsed_env, count, multi_index++);
2056 call_env_parsed_set_data(parsed_env, parsed_tmpl);
2057 }
2058
2059 return 0;
2060}
2061
2062static int mod_detach(module_detach_ctx_t const *mctx)
2063{
2064 rlm_sql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_t);
2065
2066 /*
2067 * We need to explicitly free all children, so if the driver
2068 * parented any memory off the instance, their destructors
2069 * run before we unload the bytecode for them.
2070 *
2071 * If we don't do this, we get a SEGV deep inside the talloc code
2072 * when it tries to call a destructor that no longer exists.
2073 */
2074 talloc_free_children(inst);
2075
2076 return 0;
2077}
2078
2079static int mod_instantiate(module_inst_ctx_t const *mctx)
2080{
2081 rlm_sql_boot_t const *boot = talloc_get_type_abort(mctx->mi->boot, rlm_sql_boot_t);
2082 rlm_sql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_t);
2083 CONF_SECTION *conf = mctx->mi->conf;
2084
2085 /*
2086 * We can't modify the inst field in bootstrap, and there's no
2087 * point in making rlm_sql_boot_t available everywhere.
2088 */
2089 inst->group_da = boot->group_da;
2090
2091 inst->name = mctx->mi->name; /* Need this for functions in sql.c */
2092 inst->mi = mctx->mi; /* For looking up thread instance data */
2093
2094 /*
2095 * We need authorize_group_check_query or authorize_group_reply_query
2096 * if group_membership_query is set.
2097 *
2098 * Or we need group_membership_query if authorize_group_check_query or
2099 * authorize_group_reply_query is set.
2100 */
2101 if (!cf_pair_find(conf, "group_membership_query")) {
2102 if (cf_pair_find(conf, "authorize_group_check_query")) {
2103 WARN("Ignoring authorize_group_check_query as group_membership_query is not configured");
2104 }
2105
2106 if (cf_pair_find(conf, "authorize_group_reply_query")) {
2107 WARN("Ignoring authorize_group_reply_query as group_membership_query is not configured");
2108 }
2109
2110 if (!inst->config.read_groups) {
2111 WARN("Ignoring read_groups as group_membership_query is not configured");
2112 inst->config.read_groups = false;
2113 }
2114 } /* allow the group check / reply queries to be NULL */
2115
2116 /*
2117 * Cache the SQL-User-Name fr_dict_attr_t, so we can be slightly
2118 * more efficient about creating SQL-User-Name attributes.
2119 */
2120 inst->sql_user = attr_sql_user_name;
2121
2122 /*
2123 * Export these methods, too. This avoids RTDL_GLOBAL.
2124 */
2125 inst->query = rlm_sql_trunk_query;
2126 inst->select = rlm_sql_trunk_query;
2127 inst->fetch_row = rlm_sql_fetch_row;
2128 inst->query_alloc = fr_sql_query_alloc;
2129
2130 /*
2131 * Either use the module specific escape function
2132 * or our default one.
2133 */
2134 if (inst->driver->sql_escape_func) {
2135 inst->sql_escape_func = inst->driver->sql_escape_func;
2136 } else {
2137 inst->sql_escape_func = sql_escape_func;
2138 inst->sql_escape_arg = inst;
2139 }
2140 inst->box_escape_func = sql_box_escape;
2141
2142 inst->ef = module_rlm_exfile_init(inst, conf, 256, fr_time_delta_from_sec(30), true, NULL, NULL);
2143 if (!inst->ef) {
2144 cf_log_err(conf, "Failed creating log file context");
2145 return -1;
2146 }
2147
2148 /*
2149 * Most SQL trunks can only have one running request per connection.
2150 */
2151 if (!(inst->driver->flags & RLM_SQL_MULTI_QUERY_CONN)) {
2152 inst->config.trunk_conf.target_req_per_conn = 1;
2153 inst->config.trunk_conf.max_req_per_conn = 1;
2154 }
2155 if (!inst->driver->trunk_io_funcs.connection_notify) {
2156 inst->config.trunk_conf.always_writable = true;
2157 }
2158
2159 /*
2160 * Instantiate the driver module
2161 */
2162 if (unlikely(module_instantiate(inst->driver_submodule) < 0)) {
2163 cf_log_err(conf, "Failed instantiating driver module");
2164 return -1;
2165 }
2166
2167 return 0;
2168}
2169
2170static int mod_bootstrap(module_inst_ctx_t const *mctx)
2171{
2172 rlm_sql_boot_t *boot = talloc_get_type_abort(mctx->mi->boot, rlm_sql_boot_t);
2173 rlm_sql_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_t);
2174 CONF_SECTION *conf = mctx->mi->conf;
2175 xlat_t *xlat;
2176 xlat_arg_parser_t *sql_xlat_arg;
2178
2179 /*
2180 * Register the group comparison attribute
2181 */
2182 if (cf_pair_find(conf, "group_membership_query")) {
2183 char const *group_attribute;
2184 char buffer[256];
2185
2186 if (inst->config.group_attribute) {
2187 group_attribute = inst->config.group_attribute;
2188 } else if (cf_section_name2(conf)) {
2189 snprintf(buffer, sizeof(buffer), "%s-SQL-Group", mctx->mi->name);
2190 group_attribute = buffer;
2191 } else {
2192 group_attribute = "SQL-Group";
2193 }
2194
2195 boot->group_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), group_attribute);
2196 if (!boot->group_da) {
2198 cf_log_perr(conf, "Failed defining group attribute");
2199 return -1;
2200 }
2201
2202 boot->group_da = fr_dict_attr_search_by_qualified_oid(NULL, dict_freeradius, group_attribute,
2203 false, false);
2204 if (!boot->group_da) {
2205 cf_log_perr(conf, "Failed resolving group attribute");
2206 return -1;
2207 }
2208 }
2209
2210 /*
2211 * Define the new %sql.group(name) xlat. The
2212 * register function automatically adds the
2213 * module instance name as a prefix.
2214 */
2215 xlat = module_rlm_xlat_register(boot, mctx, "group", sql_group_xlat, FR_TYPE_BOOL);
2216 if (!xlat) {
2217 cf_log_perr(conf, "Failed registering %s expansion", group_attribute);
2218 return -1;
2219 }
2221
2222 /*
2223 * The xlat escape function needs access to inst - so
2224 * argument parser details need to be defined here
2225 */
2226 sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2);
2227 sql_xlat_arg[0] = (xlat_arg_parser_t){
2229 .required = true,
2230 .concat = true
2231 };
2233
2234 xlat_func_args_set(xlat, sql_xlat_arg);
2235 }
2236
2237 /*
2238 * Register the SQL xlat function
2239 */
2240 xlat = module_rlm_xlat_register(boot, mctx, NULL, sql_xlat, FR_TYPE_VOID); /* Returns an integer sometimes */
2241 if (!xlat) {
2242 cf_log_perr(conf, "Failed registering %s expansion", mctx->mi->name);
2243 return -1;
2244 }
2246
2247 /*
2248 * The xlat escape function needs access to inst - so
2249 * argument parser details need to be defined here.
2250 * Parented off the module instance "boot" so it can be shared
2251 * between three xlats.
2252 */
2253 MEM(sql_xlat_arg = talloc_zero_array(boot, xlat_arg_parser_t, 2));
2254 MEM(uctx = talloc_zero(sql_xlat_arg, rlm_sql_escape_uctx_t));
2255 *uctx = (rlm_sql_escape_uctx_t){ .sql = inst };
2256 sql_xlat_arg[0] = (xlat_arg_parser_t){
2258 .required = true,
2259 .concat = true,
2260 .func = sql_xlat_escape,
2261 .safe_for = SQL_SAFE_FOR,
2262 .uctx = uctx
2263 };
2265
2266 xlat_func_args_set(xlat, sql_xlat_arg);
2267
2268 /*
2269 * Register instances of the SQL xlat with pre-determined output types
2270 */
2271 if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "fetch", sql_fetch_xlat, FR_TYPE_VOID)))) return -1;
2273 xlat_func_args_set(xlat, sql_xlat_arg);
2274
2275 if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "modify", sql_modify_xlat, FR_TYPE_UINT32)))) return -1;
2277 xlat_func_args_set(xlat, sql_xlat_arg);
2278
2279 if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "escape", sql_escape_xlat, FR_TYPE_STRING)))) return -1;
2280 sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2);
2281 sql_xlat_arg[0] = (xlat_arg_parser_t){
2283 .variadic = true,
2284 .concat = true,
2285 };
2287 xlat_func_args_set(xlat, sql_xlat_arg);
2290
2291 if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "safe", xlat_transparent, FR_TYPE_STRING)))) return -1;
2292 sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2);
2293 sql_xlat_arg[0] = (xlat_arg_parser_t){
2295 .variadic = true,
2296 .concat = true
2297 };
2299 xlat_func_args_set(xlat, sql_xlat_arg);
2302
2303 /*
2304 * Register the SQL map processor function
2305 */
2306 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);
2307
2308 return 0;
2309}
2310
2311/** Initialise thread specific data structure
2312 *
2313 */
2315{
2316 rlm_sql_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_sql_thread_t);
2317 rlm_sql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_t);
2318
2319 if (inst->driver->sql_escape_arg_alloc) {
2320 t->sql_escape_arg = inst->driver->sql_escape_arg_alloc(t, mctx->el, inst);
2321 if (!t->sql_escape_arg) return -1;
2322 }
2323
2324 t->inst = inst;
2325
2326 t->trunk = trunk_alloc(t, mctx->el, &inst->driver->trunk_io_funcs,
2327 &inst->config.trunk_conf, inst->name, t, false);
2328 if (!t->trunk) return -1;
2329
2330 return 0;
2331}
2332
2334{
2335 rlm_sql_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_sql_thread_t);
2336 rlm_sql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_t);
2337
2338 if (inst->driver->sql_escape_arg_free) inst->driver->sql_escape_arg_free(t->sql_escape_arg);
2339
2340 return 0;
2341}
2342
2343/** Custom parser for sql call env queries
2344 *
2345 * Needed as the escape function needs to reference the correct SQL driver
2346 */
2347static int call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
2348 call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
2349{
2351 tmpl_t *parsed_tmpl;
2352 CONF_PAIR const *to_parse = cf_item_to_pair(ci);
2353 tmpl_rules_t our_rules = *t_rules;
2354
2355 /*
2356 * Set the sql module instance data as the uctx for escaping
2357 * and use the same "safe_for" as the sql module.
2358 */
2359 our_rules.escape.func = sql_box_escape;
2360 our_rules.escape.uctx.func.uctx = inst;
2361 our_rules.escape.safe_for = SQL_SAFE_FOR;
2362 our_rules.literals_safe_for = SQL_SAFE_FOR;
2363
2364 if (tmpl_afrom_substr(ctx, &parsed_tmpl,
2365 &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
2367 &our_rules) < 0) return -1;
2368 *(void **)out = parsed_tmpl;
2369 return 0;
2370}
2371
2372#define QUERY_ESCAPE .pair.escape = { \
2373 .mode = TMPL_ESCAPE_PRE_CONCAT, \
2374 .uctx = { .func = { .alloc = sql_escape_uctx_alloc }, .type = TMPL_ESCAPE_UCTX_ALLOC_FUNC }, \
2375}, .pair.func = call_env_parse
2376
2389
2390/* globally exported name */
2392 .common = {
2393 .magic = MODULE_MAGIC_INIT,
2394 .name = "sql",
2395 .boot_size = sizeof(rlm_sql_boot_t),
2396 .boot_type = "rlm_sql_boot_t",
2397 .inst_size = sizeof(rlm_sql_t),
2398 .config = module_config,
2399 .bootstrap = mod_bootstrap,
2400 .instantiate = mod_instantiate,
2401 .detach = mod_detach,
2402 .thread_inst_size = sizeof(rlm_sql_thread_t),
2405 },
2406 .method_group = {
2407 .bindings = (module_method_binding_t[]){
2408 /*
2409 * Hack to support old configurations
2410 */
2411 { .section = SECTION_NAME("accounting", CF_IDENT_ANY), .method = mod_sql_redundant, .method_env = &accounting_method_env },
2412 { .section = SECTION_NAME("authorize", CF_IDENT_ANY), .method = mod_authorize, .method_env = &authorize_method_env },
2413
2414 { .section = SECTION_NAME("recv", CF_IDENT_ANY), .method = mod_authorize, .method_env = &authorize_method_env },
2415 { .section = SECTION_NAME("send", CF_IDENT_ANY), .method = mod_sql_redundant, .method_env = &send_method_env },
2417 }
2418 }
2419};
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
static int const char char buffer[256]
Definition acutest.h:576
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:221
#define RCSID(id)
Definition build.h:483
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define unlikely(_x)
Definition build.h:381
#define UNUSED
Definition build.h:315
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:718
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:631
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:703
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:688
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:660
#define CALL_ENV_TERMINATOR
Definition call_env.h:231
call_env_ctx_type_t type
Type of callenv ctx.
Definition call_env.h:222
@ CALL_ENV_CTX_TYPE_MODULE
The callenv is registered to a module method.
Definition call_env.h:217
#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:235
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:242
section_name_t const * asked
The actual name1/name2 that resolved to a module_method_binding_t.
Definition call_env.h:227
@ 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
module_instance_t const * mi
Module instance that the callenv is registered to.
Definition call_env.h:224
#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:407
#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:335
#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:384
Per method call config.
Definition call_env.h:175
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:596
#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:268
#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:256
#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:297
@ CONF_FLAG_SECRET
Only print value if debug level >= 3.
Definition cf_parse.h:422
#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:579
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:1453
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:1520
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1185
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:1028
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:684
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:1439
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1638
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:664
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1594
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_parent(_cf)
Definition cf_util.h:101
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:367
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:296
#define cf_log_warn(_cf, _fmt,...)
Definition cf_util.h:290
#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:406
#define MEM(x)
Definition debug.h:36
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:3081
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:1738
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4585
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:3263
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2400
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:268
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:281
static fr_slen_t in
Definition dict.h:824
Specifies an attribute which must be present for the module to function.
Definition dict.h:267
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:280
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_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
Definition function.h:89
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition function.h:111
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:430
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:1184
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:642
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:612
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:554
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1403
#define UNLANG_SUB_FRAME
Definition interpret.h:36
#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:1781
int rad_filename_box_make_safe(fr_value_box_t *vb, UNUSED void *uxtc)
Definition util.c:167
talloc_free(reap)
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:131
@ 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
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition misc.h:59
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
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
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:257
exfile_t * module_rlm_exfile_init(TALLOC_CTX *ctx, CONF_SECTION *module, uint32_t max_entries, fr_time_delta_t max_idle, bool locking, char const *trigger_prefix, fr_pair_list_t *trigger_args)
Initialise a module specific exfile handle.
Definition module_rlm.c:116
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:950
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:2634
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:693
fr_pair_t * fr_pair_afrom_da_nested(TALLOC_CTX *ctx, fr_pair_list_t *list, fr_dict_attr_t const *da)
Create a pair (and all intermediate parents), and append it to the list.
Definition pair.c:467
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, bool tainted)
Convert string value to native attribute value.
Definition pair.c:2589
int radius_legacy_map_cmp(request_t *request, map_t const *map)
Definition pairmove.c:790
int radius_legacy_map_list_apply(request_t *request, map_list_t const *list, fr_edit_list_t *el)
Definition pairmove.c:771
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:149
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:132
static rs_t * conf
Definition radsniff.c:53
#define RETURN_MODULE_NOOP
Definition rcode.h:62
#define RETURN_MODULE_RCODE(_rcode)
Definition rcode.h:64
#define RETURN_MODULE_INVALID
Definition rcode.h:59
#define RLM_MODULE_USER_SECTION_REJECT
Rcodes that translate to a user configurable section failing overall.
Definition rcode.h:72
#define RETURN_MODULE_OK
Definition rcode.h:57
#define RETURN_MODULE_FAIL
Definition rcode.h:56
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:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:49
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
#define RETURN_MODULE_NOTFOUND
Definition rcode.h:61
fr_dict_attr_t const * request_attr_request
Definition request.c:45
fr_dict_attr_t const * request_attr_reply
Definition request.c:46
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:246
static unlang_action_t mod_autz_group_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Resume function called after authorization group / profile expansion of check / reply query tmpl.
Definition rlm_sql.c:1286
rlm_sql_t const * inst
Module instance.
Definition rlm_sql.c:235
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:701
static int mod_detach(module_detach_ctx_t const *mctx)
Definition rlm_sql.c:2062
fr_sql_query_t * query_ctx
Query context.
Definition rlm_sql.c:172
sql_group_ctx_t * group_ctx
Definition rlm_sql.c:1112
static unlang_action_t mod_sql_redundant_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Resume function called after expansion of next query in a redundant list of queries.
Definition rlm_sql.c:1840
rlm_sql_t const * inst
Definition rlm_sql.c:201
rlm_sql_t const * inst
Module instance.
Definition rlm_sql.c:170
sql_redundant_call_env_t * call_env
Call environment data.
Definition rlm_sql.c:238
#define QUERY_ESCAPE
Definition rlm_sql.c:2372
tmpl_t * check_query
Tmpl to expand to form authorize_check_query.
Definition rlm_sql.c:110
static sql_fall_through_t fall_through(map_list_t *maps)
Definition rlm_sql.c:300
rlm_sql_grouplist_t * group
Current group being processed.
Definition rlm_sql.c:190
fr_value_box_t filename
File name to write SQL logs to.
Definition rlm_sql.c:208
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:676
request_t * request
Request being processed.
Definition rlm_sql.c:236
size_t query_no
Current query number.
Definition rlm_sql.c:239
static const call_env_method_t authorize_method_env
Definition rlm_sql.c:2377
tmpl_t * reply_query
Tmpl to expand to form authorize_reply_query.
Definition rlm_sql.c:111
static unlang_action_t mod_map_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Process the results of an SQL map query.
Definition rlm_sql.c:723
static int _sql_escape_uxtx_free(void *uctx)
Definition rlm_sql.c:273
static fr_dict_attr_t const * attr_sql_user_name
Definition rlm_sql.c:95
#define SQL_SAFE_FOR
Definition rlm_sql.c:47
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:1221
#define sql_unset_user(_i, _r)
Definition rlm_sql.c:1047
static fr_dict_attr_t const * attr_fall_through
Definition rlm_sql.c:94
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:86
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:1149
fr_sql_map_ctx_t * map_ctx
Context used for retrieving attribute value pairs as a map list.
Definition rlm_sql.c:193
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:488
static void sql_set_user(rlm_sql_t const *inst, request_t *request, fr_value_box_t *user)
Definition rlm_sql.c:1025
static int 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:2347
rlm_sql_grouplist_t * groups
List of groups retrieved.
Definition rlm_sql.c:173
fr_value_box_t filename
Definition rlm_sql.c:124
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:1119
static fr_dict_attr_t const * attr_expr_bool_enum
Definition rlm_sql.c:97
fr_value_box_t user
Expansion of the sql_user_name.
Definition rlm_sql.c:109
fr_pair_t * sql_group
Pair to update with group being processed.
Definition rlm_sql.c:191
trunk_t * trunk
Trunk connection for queries.
Definition rlm_sql.c:237
#define SQL_AUTZ_STAGE_PROFILE
Definition rlm_sql.c:165
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:447
static unlang_action_t mod_sql_redundant(rlm_rcode_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:1866
map_list_t check_tmp
List to store check items before processing.
Definition rlm_sql.c:185
static int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Definition rlm_sql.c:259
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_sql.c:2170
fr_value_box_t * query
Query string used for evaluating group membership.
Definition rlm_sql.c:171
fr_sql_query_t * query_ctx
Definition rlm_sql.c:203
sql_autz_call_env_t * call_env
Call environment data.
Definition rlm_sql.c:184
module_rlm_t rlm_sql
Definition rlm_sql.c:2391
static const call_env_method_t xlat_method_env
Definition rlm_sql.c:127
sql_autz_status_t
Status of the authorization process.
Definition rlm_sql.c:146
@ SQL_AUTZ_PROFILE_REPLY
Running profile reply query.
Definition rlm_sql.c:160
@ SQL_AUTZ_CHECK
Running user check query.
Definition rlm_sql.c:147
@ SQL_AUTZ_GROUP_MEMB_RESUME
Completed group membership query.
Definition rlm_sql.c:152
@ SQL_AUTZ_REPLY
Running user reply query.
Definition rlm_sql.c:149
@ SQL_AUTZ_GROUP_MEMB
Running group membership query.
Definition rlm_sql.c:151
@ SQL_AUTZ_PROFILE_REPLY_RESUME
Completed profile reply query.
Definition rlm_sql.c:161
@ SQL_AUTZ_PROFILE_CHECK_RESUME
Completed profile check query.
Definition rlm_sql.c:159
@ SQL_AUTZ_CHECK_RESUME
Completed user check query.
Definition rlm_sql.c:148
@ SQL_AUTZ_PROFILE_START
Starting processing user profiles.
Definition rlm_sql.c:157
@ SQL_AUTZ_GROUP_REPLY_RESUME
Completed group reply query.
Definition rlm_sql.c:156
@ SQL_AUTZ_REPLY_RESUME
Completed user reply query.
Definition rlm_sql.c:150
@ SQL_AUTZ_GROUP_CHECK
Running group check query.
Definition rlm_sql.c:153
@ SQL_AUTZ_PROFILE_CHECK
Running profile check query.
Definition rlm_sql.c:158
@ SQL_AUTZ_GROUP_REPLY
Running group reply query.
Definition rlm_sql.c:155
@ SQL_AUTZ_GROUP_CHECK_RESUME
Completed group check query.
Definition rlm_sql.c:154
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:339
rlm_sql_t const * inst
Module instance.
Definition rlm_sql.c:180
#define SQL_AUTZ_STAGE_GROUP
Definition rlm_sql.c:164
tmpl_t * group_reply_query
Tmpl to expand to form authorize_group_reply_query.
Definition rlm_sql.c:114
tmpl_t * group_check_query
Tmpl to expand to form authorize_group_check_query.
Definition rlm_sql.c:113
int num_groups
How many groups have been retrieved.
Definition rlm_sql.c:174
static int sql_autz_ctx_free(sql_autz_ctx_t *to_free)
Definition rlm_sql.c:1259
static unlang_action_t mod_sql_redundant_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Resume function called after executing an SQL query in a redundant list of queries.
Definition rlm_sql.c:1759
static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void const *mod_inst, UNUSED void *proc_inst, request_t *request, fr_value_box_list_t *query, map_list_t const *maps)
Executes a SELECT query and maps the result to server attributes.
Definition rlm_sql.c:863
trunk_t * trunk
Trunk connection for current authorization.
Definition rlm_sql.c:183
fr_value_box_t * query_vb
Current query string.
Definition rlm_sql.c:241
fr_value_box_list_t query
Where expanded query tmpl will be written.
Definition rlm_sql.c:240
map_list_t reply_tmp
List to store reply items before processing.
Definition rlm_sql.c:186
static unlang_action_t mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Start of module authorize method.
Definition rlm_sql.c:1671
sql_autz_status_t status
Current status of the authorization.
Definition rlm_sql.c:187
request_t * request
Request being processed.
Definition rlm_sql.c:181
fr_pair_t * profile
Current profile being processed.
Definition rlm_sql.c:192
#define MAX_SQL_FIELD_INDEX
Definition rlm_sql.c:713
fr_dict_attr_autoload_t rlm_sql_dict_attr[]
Definition rlm_sql.c:100
fr_value_box_list_t query
Definition rlm_sql.c:1111
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Initialise thread specific data structure.
Definition rlm_sql.c:2314
static void * sql_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
Definition rlm_sql.c:282
fr_sql_query_t * query_ctx
Query context for current query.
Definition rlm_sql.c:242
rlm_rcode_t rcode
Module return code.
Definition rlm_sql.c:182
tmpl_t ** query
Array of tmpls for list of queries to run.
Definition rlm_sql.c:209
rlm_sql_grouplist_t * next
Definition rlm_sql.c:1052
static const call_env_method_t accounting_method_env
Definition rlm_sql.c:212
static int sql_redundant_ctx_free(sql_redundant_ctx_t *to_free)
Tidy up when freeing an SQL redundant context.
Definition rlm_sql.c:1741
static const call_env_method_t group_xlat_method_env
Definition rlm_sql.c:250
sql_group_ctx_t * group_ctx
Context used for retrieving user group membership.
Definition rlm_sql.c:194
tmpl_t * membership_query
Tmpl to expand to form group_membership_query.
Definition rlm_sql.c:112
static int sql_box_escape(fr_value_box_t *vb, void *uctx)
Definition rlm_sql.c:417
static const conf_parser_t module_config[]
Definition rlm_sql.c:57
fr_value_box_list_t query
Where expanded query tmpls will be written.
Definition rlm_sql.c:188
bool user_found
Has the user been found anywhere?
Definition rlm_sql.c:189
static fr_dict_attr_t const * attr_user_profile
Definition rlm_sql.c:96
static const call_env_method_t send_method_env
Definition rlm_sql.c:222
fr_dict_autoload_t rlm_sql_dict[]
Definition rlm_sql.c:89
fr_dict_attr_t const * group_da
Definition rlm_sql.c:54
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Definition rlm_sql.c:2333
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_sql.c:2079
fr_value_box_t user
Expansion of sql_user_name.
Definition rlm_sql.c:207
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 sql_get_grouplist_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Definition rlm_sql.c:1055
static ssize_t sql_escape_func(request_t *, char *out, size_t outlen, char const *in, void *arg)
static unlang_action_t sql_get_grouplist(sql_group_ctx_t *group_ctx, trunk_t *trunk, request_t *request)
Definition rlm_sql.c:1094
map_list_t const * maps
Definition rlm_sql.c:202
static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, int *priority, request_t *request, void *uctx)
Resume function called after authorization check / reply tmpl expansion.
Definition rlm_sql.c:1516
Context for SQL authorization.
Definition rlm_sql.c:179
Context for group membership query evaluation.
Definition rlm_sql.c:169
Context for SQL maps.
Definition rlm_sql.c:200
Context for tracking redundant SQL query sets.
Definition rlm_sql.c:234
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:184
trunk_t * trunk
Trunk connection for this thread.
Definition rlm_sql.h:108
rlm_sql_t const * inst
Module instance data.
Definition rlm_sql.h:109
#define RLM_SQL_MULTI_QUERY_CONN
Can support multiple queries on a single connection.
Definition rlm_sql.h:166
fr_sql_query_type_t type
Type of query.
Definition rlm_sql.h:137
unlang_action_t rlm_sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Call the driver's sql_fetch_row function.
Definition sql.c:80
void rlm_sql_query_log(rlm_sql_t const *inst, char const *filename, char const *query)
Definition sql.c:359
rlm_sql_t const * inst
Module instance for this query.
Definition rlm_sql.h:131
unlang_action_t sql_get_map_list(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:342
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:114
@ SQL_QUERY_OTHER
Definition rlm_sql.h:115
unlang_action_t rlm_sql_trunk_query(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Submit an SQL query using a trunk connection.
Definition sql.c:235
TALLOC_CTX * ctx
To allocate map entries in.
Definition rlm_sql.h:147
void * sql_escape_arg
Thread specific argument to be passed to escape function.
Definition rlm_sql.h:110
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
int rows
How many rows the query returned.
Definition rlm_sql.h:153
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:123
rlm_sql_row_t row
Row data from the last query.
Definition rlm_sql.h:140
sql_rcode_t rcode
Result code.
Definition rlm_sql.h:139
fr_value_box_t * query
Query string used for fetching pairs.
Definition rlm_sql.h:149
rlm_sql_t const * sql
Definition rlm_sql.h:190
fr_table_num_sorted_t const sql_rcode_description_table[]
Definition sql.c:45
Context used when fetching attribute value pairs as a map list.
Definition rlm_sql.h:146
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:419
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_buff(_sbuff_or_marker)
Talloc sbuff extension structure.
Definition sbuff.h:139
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:335
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:329
void * data
Module's instance data.
Definition module.h:271
void * boot
Data allocated during the boostrap phase.
Definition module.h:274
void * data
Thread specific instance data.
Definition module.h:352
static module_thread_instance_t * module_thread(module_instance_t const *mi)
Retrieve module/thread specific instance for a module.
Definition module.h:481
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:151
size_t boot_size
Size of the module's bootstrap data.
Definition module.h:200
module_t * exported
Public module structure.
Definition module.h:276
Module instance data.
Definition module.h:265
Named methods exported by a module.
Definition module.h:173
#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
tmpl_escape_t escape
How escaping should be handled during evaluation.
Definition tmpl.h:358
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:948
fr_value_box_safe_for_t literals_safe_for
safe_for value assigned to literal values in xlats, execs, and data.
Definition tmpl.h:356
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
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:211
#define tmpl_value_type(_tmpl)
Definition tmpl.h:950
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:344
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:812
#define tmpl_expand(_out, _buff, _buff_len, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, using existing storage to hold variably sized types.
Definition tmpl.h:1060
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:228
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:373
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:341
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
return count
Definition module.c:163
int module_instantiate(module_instance_t *instance)
Manually complete module setup by calling its instantiate function.
Definition module.c:1195
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:232
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:285
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:445
#define talloc_get_type_abort_const
Definition talloc.h:282
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
int unlang_tmpl_push(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args)
Push a tmpl onto the stack for evaluation.
Definition tmpl.c:262
fr_value_box_escape_t func
How to escape when returned from evaluation.
Definition tmpl_escape.h:81
@ 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
@ 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::@74 uctx
fr_value_box_safe_for_t safe_for
Value to set on boxes which have been escaped by the fr_value_box_escape_t function.
Definition tmpl_escape.h:83
tmpl_escape_mode_t mode
Whether to apply escape function after concatenation, i.e.
Definition tmpl_escape.h:86
Escaping rules for tmpls.
Definition tmpl_escape.h:80
const bool fr_assignment_op[T_TOKEN_LAST]
Definition token.c:168
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:78
const bool fr_comparison_op[T_TOKEN_LAST]
Definition token.c:198
@ T_OP_CMP_TRUE
Definition token.h:104
@ T_OP_CMP_FALSE
Definition token.h:105
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)
Allocate a new collection of connections.
Definition trunk.c:4945
conf_parser_t const trunk_config[]
Config parser definitions to populate a trunk_conf_t.
Definition trunk.c:315
Main trunk management handle.
Definition trunk.c:198
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:573
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:168
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:147
static fr_slen_t parent
Definition pair.h:851
#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:371
#define FR_TYPE_IP
Definition types.h:288
#define FR_TYPE_NUMERIC
Definition types.h:286
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
Definition value.c:606
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:3572
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:3681
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:3927
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:4036
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:5777
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:221
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:621
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1048
#define fr_box_strvalue_buffer(_val)
Definition value.h:289
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition value.h:1055
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:155
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:632
static size_t char ** out
Definition value.h:997
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:402
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:365
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:392
#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