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