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