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: acefc287ac964fcefcd620f508b96fc7ea5a2e4e $
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: acefc287ac964fcefcd620f508b96fc7ea5a2e4e $")
29 
30 #define LOG_PREFIX mctx->inst->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/table.h>
39 #include <freeradius-devel/unlang/function.h>
40 #include <freeradius-devel/unlang/xlat_func.h>
41 
42 #include <sys/stat.h>
43 
44 #include "rlm_sql.h"
45 
46 extern module_rlm_t rlm_sql;
47 
48 static const conf_parser_t module_config[] = {
49  { FR_CONF_OFFSET_TYPE_FLAGS("driver", FR_TYPE_VOID, 0, rlm_sql_t, driver_submodule), .dflt = "null",
51  { FR_CONF_OFFSET("server", rlm_sql_config_t, sql_server), .dflt = "" }, /* Must be zero length so drivers can determine if it was set */
52  { FR_CONF_OFFSET("port", rlm_sql_config_t, sql_port), .dflt = "0" },
53  { FR_CONF_OFFSET("login", rlm_sql_config_t, sql_login), .dflt = "" },
54  { FR_CONF_OFFSET_FLAGS("password", CONF_FLAG_SECRET, rlm_sql_config_t, sql_password), .dflt = "" },
55  { FR_CONF_OFFSET("radius_db", rlm_sql_config_t, sql_db), .dflt = "radius" },
56  { FR_CONF_OFFSET("read_groups", rlm_sql_config_t, read_groups), .dflt = "yes" },
57  { FR_CONF_OFFSET("group_attribute", rlm_sql_config_t, group_attribute) },
58  { FR_CONF_OFFSET("cache_groups", rlm_sql_config_t, cache_groups) },
59  { FR_CONF_OFFSET("read_profiles", rlm_sql_config_t, read_profiles), .dflt = "yes" },
60  { FR_CONF_OFFSET("open_query", rlm_sql_config_t, connect_query) },
61 
62  { FR_CONF_OFFSET("safe_characters", rlm_sql_config_t, allowed_chars), .dflt = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
63 
64  /*
65  * This only works for a few drivers.
66  */
67  { FR_CONF_OFFSET("query_timeout", rlm_sql_config_t, query_timeout) },
68 
70 };
71 
72 static fr_dict_t const *dict_freeradius;
73 
76  { .out = &dict_freeradius, .proto = "freeradius" },
77  { NULL }
78 };
79 
84 
87  { .out = &attr_fall_through, .name = "Fall-Through", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
88  { .out = &attr_sql_user_name, .name = "SQL-User-Name", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
89  { .out = &attr_user_profile, .name = "User-Profile", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
90  { .out = &attr_expr_bool_enum, .name = "Expr-Bool-Enum", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
91  { NULL }
92 };
93 
94 typedef struct {
95  fr_value_box_t user; //!< Expansion of the sql_user_name
96  tmpl_t *check_query; //!< Tmpl to expand to form authorize_check_query
97  tmpl_t *reply_query; //!< Tmpl to expand to form authorize_reply_query
98  tmpl_t *membership_query; //!< Tmpl to expand to form group_membership_query
99  tmpl_t *group_check_query; //!< Tmpl to expand to form authorize_group_check_query
100  tmpl_t *group_reply_query; //!< Tmpl to expand to form authorize_group_reply_query
102 
105  .env = (call_env_parser_t[]) {
109  { FR_CALL_ENV_PARSE_ONLY_OFFSET("group_membership_query", FR_TYPE_STRING, CALL_ENV_FLAG_PARSE_ONLY, sql_autz_call_env_t, membership_query) },
110  { 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) },
111  { 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) },
113  }
114 };
115 
116 static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc,
117  char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule);
118 
119 static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc,
120  char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule);
121 
122 typedef struct {
125 
128  .env = (call_env_parser_t[]) {
130  .pair.escape = {
133  .mode = TMPL_ESCAPE_PRE_CONCAT
134  },
135  .pair.literals_safe_for = (fr_value_box_safe_for_t)rad_filename_box_make_safe,
136  },
138  }
139 };
140 
142 
143 /** Status of the authorization process
144  */
145 typedef enum {
146  SQL_AUTZ_CHECK = 0x11, //!< Running user `check` query
147  SQL_AUTZ_REPLY = 0x12, //!< Running user `reply` query
148  SQL_AUTZ_GROUP_MEMB = 0x20, //!< Running group membership query
149  SQL_AUTZ_GROUP_CHECK = 0x21, //!< Running group `check` query
150  SQL_AUTZ_GROUP_REPLY = 0x22, //!< Running group `reply` query
151  SQL_AUTZ_PROFILE_START = 0x40, //!< Starting processing user profiles
152  SQL_AUTZ_PROFILE_CHECK = 0x41, //!< Running profile `check` query
153  SQL_AUTZ_PROFILE_REPLY = 0x42, //!< Running profile `reply` query
155 
156 #define SQL_AUTZ_STAGE_GROUP 0x20
157 #define SQL_AUTZ_STAGE_PROFILE 0x40
158 
159 /** Context for SQL authorization
160  */
161 typedef struct {
162  rlm_sql_t const *inst; //!< Module instance.
163  request_t *request; //!< Request being processed.
164  rlm_rcode_t rcode; //!< Module return code.
165  rlm_sql_handle_t *handle; //!< Database connection handle in use for current authorization.
166  sql_autz_call_env_t *call_env; //!< Call environment data.
167  map_list_t check_tmp; //!< List to store check items before processing.
168  map_list_t reply_tmp; //!< List to store reply items before processing.
169  sql_autz_status_t status; //!< Current status of the authorization.
170  fr_value_box_list_t query; //!< Where expanded query tmpls will be written.
171  bool user_found; //!< Has the user been found anywhere?
172  rlm_sql_grouplist_t *groups; //!< List of groups returned by the group membership query.
173  rlm_sql_grouplist_t *group; //!< Current group being processed.
174  fr_pair_t *sql_group; //!< Pair to update with group being processed.
175  fr_pair_t *profile; //!< Current profile being processed.
177 
178 typedef struct {
179  fr_value_box_t user; //!< Expansion of sql_user_name.
180  fr_value_box_t filename; //!< File name to write SQL logs to.
181  tmpl_t **query; //!< Array of tmpls for list of queries to run.
183 
186  .env = (call_env_parser_t[]) {
191  }
192 };
193 
196  .env = (call_env_parser_t[]) {
201  }
202 };
203 
204 /** Context for tracking redundant SQL query sets
205  */
206 typedef struct {
207  rlm_sql_t const *inst; //!< Module instance.
208  request_t *request; //!< Request being processed.
209  rlm_sql_handle_t *handle; //!< Database connection handle.
210  sql_redundant_call_env_t *call_env; //!< Call environment data.
211  size_t query_no; //!< Current query number.
212  fr_value_box_list_t query; //!< Where expanded query tmpl will be written.
214 
215 typedef struct {
219 
222  .env = (call_env_parser_t[]) {
224  { 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) },
226  }
227 };
228 
229 static int _sql_escape_uxtx_free(void *uctx)
230 {
231  return talloc_free(uctx);
232 }
233 
234 /*
235  * Create a thread local uctx which is used in SQL value box escaping
236  * so that an already reserved connection can be used.
237  */
238 static void *sql_escape_uctx_alloc(request_t *request, void const *uctx)
239 {
240  static _Thread_local rlm_sql_escape_uctx_t *t_ctx;
241 
242  if (unlikely(t_ctx == NULL)) {
244 
245  MEM(ctx = talloc_zero(NULL, rlm_sql_escape_uctx_t));
247  }
248  t_ctx->sql = uctx;
249  t_ctx->handle = request_data_reference(request, (void *)sql_escape_uctx_alloc, 0);
250 
251  return t_ctx;
252 }
253 
254 /*
255  * Fall-Through checking function from rlm_files.c
256  */
257 static sql_fall_through_t fall_through(map_list_t *maps)
258 {
259  bool rcode;
260  map_t *map, *next;
261 
262  for (map = map_list_head(maps);
263  map != NULL;
264  map = next) {
265  next = map_list_next(maps, map);
266 
267  fr_assert(tmpl_is_attr(map->lhs));
268 
269  if (tmpl_attr_tail_da(map->lhs) == attr_fall_through) {
270  (void) map_list_remove(maps, map);
271 
272  if (tmpl_is_data(map->rhs)) {
274 
275  rcode = tmpl_value(map->rhs)->vb_bool;
276  } else {
277  rcode = false;
278  }
279 
280  talloc_free(map);
281  return rcode;
282  }
283  }
284 
285  return FALL_THROUGH_DEFAULT;
286 }
287 
288 /*
289  * Yucky prototype.
290  */
291 static size_t sql_escape_func(request_t *, char *out, size_t outlen, char const *in, void *arg);
292 
293 /** Escape a tainted VB used as an xlat argument
294  *
295  */
296 static int CC_HINT(nonnull(2,3)) sql_xlat_escape(request_t *request, fr_value_box_t *vb, void *uctx)
297 {
298  fr_sbuff_t sbuff;
299  fr_sbuff_uctx_talloc_t sbuff_ctx;
300 
301  size_t len;
302  rlm_sql_handle_t *handle;
303  rlm_sql_escape_uctx_t *ctx = uctx;
305  fr_value_box_entry_t entry;
306 
307  /*
308  * If it's already safe, don't do anything.
309  */
310  if (fr_value_box_is_safe_for(vb, inst->driver)) return 0;
311 
312  handle = ctx->handle ? ctx->handle : fr_pool_connection_get(inst->pool, request);
313  if (!handle) {
314  error:
316  return -1;
317  }
318 
319  /*
320  * Escaping functions work on strings - ensure the box is a string
321  */
322  if ((vb->type != FR_TYPE_STRING) && (fr_value_box_cast_in_place(vb, vb, FR_TYPE_STRING, NULL) < 0)) goto error;
323 
324  /*
325  * Maximum escaped length is 3 * original - if every character needs escaping
326  */
327  if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, vb->vb_length * 3, vb->vb_length * 3)) {
328  fr_strerror_printf_push("Failed to allocate buffer for escaped sql argument");
329  return -1;
330  }
331 
332  len = inst->sql_escape_func(request, fr_sbuff_buff(&sbuff), vb->vb_length * 3 + 1, vb->vb_strvalue, handle);
333 
334  /*
335  * fr_value_box_strdup_shallow resets the dlist entries - take a copy
336  */
337  entry = vb->entry;
338  fr_sbuff_trim_talloc(&sbuff, len);
340  fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), vb->tainted);
341 
342  /*
343  * Different databases have slightly different ideas as
344  * to what is safe. So we track the database type in the
345  * safe value. This means that we don't
346  * cross-contaminate "safe" values across databases.
347  */
348  fr_value_box_mark_safe_for(vb, inst->driver);
349  vb->entry = entry;
350 
351  if (!ctx->handle) fr_pool_connection_release(inst->pool, request, handle);
352  return 0;
353 }
354 
355 static int sql_box_escape(fr_value_box_t *vb, void *uctx)
356 {
357  return sql_xlat_escape(NULL, vb, uctx);
358 }
359 
360 /** Execute an arbitrary SQL query
361  *
362  * For SELECTs, the values of the first column will be returned.
363  * For INSERTS, UPDATEs and DELETEs, the number of rows affected will
364  * be returned instead.
365  *
366 @verbatim
367 %sql(<sql statement>)
368 @endverbatim
369  *
370  * @ingroup xlat_functions
371  */
372 static xlat_action_t sql_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
373  xlat_ctx_t const *xctx,
374  request_t *request, fr_value_box_list_t *in)
375 {
376  sql_xlat_call_env_t *call_env = talloc_get_type_abort(xctx->env_data, sql_xlat_call_env_t);
377  rlm_sql_handle_t *handle = NULL;
378  rlm_sql_row_t row;
379  rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->inst->data, rlm_sql_t);
380  sql_rcode_t rcode;
382  char const *p;
383  fr_value_box_t *arg = fr_value_box_list_head(in);
384  fr_value_box_t *vb = NULL;
385  bool fetched = false;
386 
387  handle = fr_pool_connection_get(inst->pool, request); /* connection pool should produce error */
388  if (!handle) return XLAT_ACTION_FAIL;
389 
390  if (call_env->filename.type == FR_TYPE_STRING && call_env->filename.vb_length > 0) {
391  rlm_sql_query_log(inst, call_env->filename.vb_strvalue, arg->vb_strvalue);
392  }
393 
394  p = arg->vb_strvalue;
395 
396  /*
397  * Trim whitespace for the prefix check
398  */
400 
401  /*
402  * If the query starts with any of the following prefixes,
403  * then return the number of rows affected
404  */
405  if ((strncasecmp(p, "insert", 6) == 0) ||
406  (strncasecmp(p, "update", 6) == 0) ||
407  (strncasecmp(p, "delete", 6) == 0)) {
408  int numaffected;
409 
410  rcode = rlm_sql_query(inst, request, &handle, arg->vb_strvalue);
411  if (rcode != RLM_SQL_OK) {
412  query_error:
413  RERROR("SQL query failed: %s", fr_table_str_by_value(sql_rcode_description_table, rcode, "<INVALID>"));
414 
415  ret = XLAT_ACTION_FAIL;
416  goto finish;
417  }
418 
419  numaffected = (inst->driver->sql_affected_rows)(handle, &inst->config);
420  if (numaffected < 1) {
421  RDEBUG2("SQL query affected no rows");
422  (inst->driver->sql_finish_query)(handle, &inst->config);
423 
424  goto finish;
425  }
426 
427  MEM(vb = fr_value_box_alloc_null(ctx));
428  fr_value_box_uint32(vb, NULL, (uint32_t)numaffected, false);
429  fr_dcursor_append(out, vb);
430 
431  (inst->driver->sql_finish_query)(handle, &inst->config);
432 
433  goto finish;
434  } /* else it's a SELECT statement */
435 
436  rcode = rlm_sql_select_query(inst, request, &handle, arg->vb_strvalue);
437  if (rcode != RLM_SQL_OK) goto query_error;
438 
439  do {
440  rcode = rlm_sql_fetch_row(&row, inst, request, &handle);
441  switch (rcode) {
442  case RLM_SQL_OK:
443  if (row[0]) break;
444 
445  RDEBUG2("NULL value in first column of result");
446  ret = XLAT_ACTION_FAIL;
447 
448  goto finish_query;
449 
451  if (!fetched) {
452  RDEBUG2("SQL query returned no results");
453  ret = XLAT_ACTION_FAIL;
454  }
455 
456  goto finish_query;
457 
458  default:
459  (inst->driver->sql_finish_select_query)(handle, &inst->config);
460  goto query_error;
461  }
462 
463  fetched = true;
464 
465  MEM(vb = fr_value_box_alloc_null(ctx));
466  fr_value_box_strdup(vb, vb, NULL, row[0], false);
467  fr_dcursor_append(out, vb);
468 
469  } while (1);
470 
471 finish_query:
472  (inst->driver->sql_finish_select_query)(handle, &inst->config);
473 
474 finish:
475  fr_pool_connection_release(inst->pool, request, handle);
476 
477  return ret;
478 }
479 
480 /** Converts a string value into a #fr_pair_t
481  *
482  * @param[in,out] ctx to allocate #fr_pair_t (s).
483  * @param[out] out where to write the resulting #fr_pair_t.
484  * @param[in] request The current request.
485  * @param[in] map to process.
486  * @param[in] uctx The value to parse.
487  * @return
488  * - 0 on success.
489  * - -1 on failure.
490  */
491 static int _sql_map_proc_get_value(TALLOC_CTX *ctx, fr_pair_list_t *out,
492  request_t *request, map_t const *map, void *uctx)
493 {
494  fr_pair_t *vp;
495  char const *value = uctx;
496 
498  if (!vp) return -1;
499 
500  /*
501  * Buffer not always talloced, sometimes it's
502  * just a pointer to a field in a result struct.
503  */
504  if (fr_pair_value_from_str(vp, value, strlen(value), NULL, true) < 0) {
505  RPEDEBUG("Failed parsing value \"%pV\" for attribute %s",
506  fr_box_strvalue_buffer(value), vp->da->name);
507  return -1;
508  }
509 
510  return 0;
511 }
512 
513 /*
514  * Verify the result of the map.
515  */
516 static int sql_map_verify(CONF_SECTION *cs, UNUSED void *mod_inst, UNUSED void *proc_inst,
517  tmpl_t const *src, UNUSED map_list_t const *maps)
518 {
519  if (!src) {
520  cf_log_err(cs, "Missing SQL query");
521 
522  return -1;
523  }
524 
525  return 0;
526 }
527 
528 /** Executes a SELECT query and maps the result to server attributes
529  *
530  * @param p_result Result of map expansion:
531  * - #RLM_MODULE_NOOP no rows were returned or columns matched.
532  * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
533  * - #RLM_MODULE_FAIL if a fault occurred.
534  * @param mod_inst #rlm_sql_t instance.
535  * @param proc_inst Instance data for this specific mod_proc call (unused).
536  * @param request The current request.
537  * @param query string to execute.
538  * @param maps Head of the map list.
539  * @return UNLANG_ACTION_CALCULATE_RESULT
540  */
541 static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void *mod_inst, UNUSED void *proc_inst, request_t *request,
542  fr_value_box_list_t *query, map_list_t const *maps)
543 {
544  rlm_sql_t *inst = talloc_get_type_abort(mod_inst, rlm_sql_t);
545  rlm_sql_handle_t *handle = NULL;
546 
547  int i, j;
548 
550  sql_rcode_t ret;
551 
552  map_t const *map;
553 
554  rlm_sql_row_t row;
555 
556  int rows = 0;
557  int field_cnt;
558  char const **fields = NULL, *map_rhs;
559  char map_rhs_buff[128];
560 
561  char const *query_str = NULL;
562  fr_value_box_t *query_head = fr_value_box_list_head(query);
563 
564 #define MAX_SQL_FIELD_INDEX (64)
565 
566  int field_index[MAX_SQL_FIELD_INDEX];
567  bool found_field = false; /* Did we find any matching fields in the result set ? */
568 
569  fr_assert(inst->driver->sql_fields); /* Should have been caught during validation... */
570 
571  if (!query_head) {
572  REDEBUG("Query cannot be (null)");
574  }
575 
577  query_head, query, FR_TYPE_STRING,
579  SIZE_MAX) < 0) {
580  RPEDEBUG("Failed concatenating input string");
582  }
583  query_str = query_head->vb_strvalue;
584 
585  for (i = 0; i < MAX_SQL_FIELD_INDEX; i++) field_index[i] = -1;
586 
587  handle = fr_pool_connection_get(inst->pool, request); /* connection pool should produce error */
588  if (!handle) {
590  }
591 
592  ret = rlm_sql_select_query(inst, request, &handle, query_str);
593  if (ret != RLM_SQL_OK) {
594  RERROR("SQL query failed: %s", fr_table_str_by_value(sql_rcode_description_table, ret, "<INVALID>"));
595  rcode = RLM_MODULE_FAIL;
596  goto finish;
597  }
598 
599  /*
600  * Not every driver provides an sql_num_rows function
601  */
602  if (inst->driver->sql_num_rows) {
603  ret = inst->driver->sql_num_rows(handle, &inst->config);
604  if (ret == 0) {
605  RDEBUG2("Server returned an empty result");
606  rcode = RLM_MODULE_NOOP;
607  (inst->driver->sql_finish_select_query)(handle, &inst->config);
608  goto finish;
609  }
610 
611  if (ret < 0) {
612  RERROR("Failed retrieving row count");
613  error:
614  rcode = RLM_MODULE_FAIL;
615  (inst->driver->sql_finish_select_query)(handle, &inst->config);
616  goto finish;
617  }
618  }
619 
620  /*
621  * Map proc only registered if driver provides an sql_fields function
622  */
623  ret = (inst->driver->sql_fields)(&fields, handle, &inst->config);
624  if (ret != RLM_SQL_OK) {
625  RERROR("Failed retrieving field names: %s", fr_table_str_by_value(sql_rcode_description_table, ret, "<INVALID>"));
626  goto error;
627  }
628  fr_assert(fields);
629  field_cnt = talloc_array_length(fields);
630 
631  if (RDEBUG_ENABLED3) for (j = 0; j < field_cnt; j++) RDEBUG3("Got field: %s", fields[j]);
632 
633  /*
634  * Iterate over the maps, it's O(N2)ish but probably
635  * faster than building a radix tree each time the
636  * map set is evaluated (map->rhs can be dynamic).
637  */
638  for (map = map_list_head(maps), i = 0;
639  map && (i < MAX_SQL_FIELD_INDEX);
640  map = map_list_next(maps, map), i++) {
641  /*
642  * Expand the RHS to get the name of the SQL field
643  */
644  if (tmpl_expand(&map_rhs, map_rhs_buff, sizeof(map_rhs_buff),
645  request, map->rhs, NULL, NULL) < 0) {
646  RPERROR("Failed getting field name");
647  goto error;
648  }
649 
650  for (j = 0; j < field_cnt; j++) {
651  if (strcmp(fields[j], map_rhs) != 0) continue;
652  field_index[i] = j;
653  found_field = true;
654  }
655  }
656 
657  /*
658  * Couldn't resolve any map RHS values to fields
659  * in the result set.
660  */
661  if (!found_field) {
662  RDEBUG2("No fields matching map found in query result");
663  rcode = RLM_MODULE_NOOP;
664  (inst->driver->sql_finish_select_query)(handle, &inst->config);
665  goto finish;
666  }
667 
668  /*
669  * We've resolved all the maps to result indexes, now convert
670  * the values at those indexes into fr_pair_ts.
671  *
672  * Note: Not all SQL client libraries provide a row count,
673  * so we have to do the count here.
674  */
675  while (((ret = rlm_sql_fetch_row(&row, inst, request, &handle)) == RLM_SQL_OK)) {
676  rows++;
677  for (map = map_list_head(maps), j = 0;
678  map && (j < MAX_SQL_FIELD_INDEX);
679  map = map_list_next(maps, map), j++) {
680  if (field_index[j] < 0) continue; /* We didn't find the map RHS in the field set */
681  if (map_to_request(request, map, _sql_map_proc_get_value, row[field_index[j]]) < 0) goto error;
682  }
683  }
684 
685  if (ret == RLM_SQL_ERROR) goto error;
686 
687  if (rows == 0) {
688  RDEBUG2("SQL query returned no results");
689  rcode = RLM_MODULE_NOOP;
690  }
691 
692  (inst->driver->sql_finish_select_query)(handle, &inst->config);
693 
694 finish:
695  talloc_free(fields);
696  fr_pool_connection_release(inst->pool, request, handle);
697 
698  RETURN_MODULE_RCODE(rcode);
699 }
700 
701 
702 /** xlat escape function for drivers which do not provide their own
703  *
704  */
705 static size_t sql_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, void *arg)
706 {
707  rlm_sql_handle_t *handle = arg;
709  size_t len = 0;
710 
711  while (in[0]) {
712  size_t utf8_len;
713 
714  /*
715  * Allow all multi-byte UTF8 characters.
716  */
717  utf8_len = fr_utf8_char((uint8_t const *) in, -1);
718  if (utf8_len > 1) {
719  if (outlen <= utf8_len) break;
720 
721  memcpy(out, in, utf8_len);
722  in += utf8_len;
723  out += utf8_len;
724 
725  outlen -= utf8_len;
726  len += utf8_len;
727  continue;
728  }
729 
730  /*
731  * Because we register our own escape function
732  * we're now responsible for escaping all special
733  * chars in an xlat expansion or attribute value.
734  */
735  switch (in[0]) {
736  case '\n':
737  if (outlen <= 2) break;
738  out[0] = '\\';
739  out[1] = 'n';
740 
741  in++;
742  out += 2;
743  outlen -= 2;
744  len += 2;
745  break;
746 
747  case '\r':
748  if (outlen <= 2) break;
749  out[0] = '\\';
750  out[1] = 'r';
751 
752  in++;
753  out += 2;
754  outlen -= 2;
755  len += 2;
756  break;
757 
758  case '\t':
759  if (outlen <= 2) break;
760  out[0] = '\\';
761  out[1] = 't';
762 
763  in++;
764  out += 2;
765  outlen -= 2;
766  len += 2;
767  break;
768  }
769 
770  /*
771  * Non-printable characters get replaced with their
772  * mime-encoded equivalents.
773  */
774  if ((in[0] < 32) ||
775  strchr(inst->config.allowed_chars, *in) == NULL) {
776  /*
777  * Only 3 or less bytes available.
778  */
779  if (outlen <= 3) {
780  break;
781  }
782 
783  snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
784  in++;
785  out += 3;
786  outlen -= 3;
787  len += 3;
788  continue;
789  }
790 
791  /*
792  * Only one byte left.
793  */
794  if (outlen <= 1) {
795  break;
796  }
797 
798  /*
799  * Allowed character.
800  */
801  *out = *in;
802  out++;
803  in++;
804  outlen--;
805  len++;
806  }
807  *out = '\0';
808  return len;
809 }
810 
811 /*
812  * Set the SQL user name.
813  *
814  * We don't call the escape function here. The resulting string
815  * will be escaped later in the queries xlat so we don't need to
816  * escape it twice. (it will make things wrong if we have an
817  * escape candidate character in the username)
818  */
819 static void sql_set_user(rlm_sql_t const *inst, request_t *request, fr_value_box_t *user)
820 {
821  fr_pair_t *vp = NULL;
822 
823  fr_assert(request->packet != NULL);
824 
825  MEM(pair_update_request(&vp, inst->sql_user) >= 0);
826  if(!user || (user->type != FR_TYPE_STRING)) {
828  return;
829  }
830 
831  /*
832  * Replace any existing SQL-User-Name with new value
833  */
834  fr_pair_value_bstrdup_buffer(vp, user->vb_strvalue, user->tainted);
835  RDEBUG2("SQL-User-Name set to '%pV'", &vp->data);
836 }
837 
838 /*
839  * Do a set/unset user, so it's a bit clearer what's going on.
840  */
841 #define sql_unset_user(_i, _r) fr_pair_delete_by_da(&_r->request_pairs, _i->sql_user)
842 
843 
845  char *name;
847 };
848 
849 static int sql_get_grouplist(rlm_sql_t const *inst, rlm_sql_handle_t **handle, request_t *request,
850  char const *query, rlm_sql_grouplist_t **phead)
851 {
852  int num_groups = 0;
853  rlm_sql_row_t row;
854  rlm_sql_grouplist_t *entry;
855  int ret;
856 
857  /* NOTE: sql_set_user should have been run before calling this function */
858 
859  entry = *phead = NULL;
860 
861  if (!query || !*query) return 0;
862 
863  ret = rlm_sql_select_query(inst, request, handle, query);
864  if (ret != RLM_SQL_OK) return -1;
865 
866  while (rlm_sql_fetch_row(&row, inst, request, handle) == RLM_SQL_OK) {
867  if (!row[0]){
868  RDEBUG2("row[0] returned NULL");
869  (inst->driver->sql_finish_select_query)(*handle, &inst->config);
870  talloc_free(entry);
871  return -1;
872  }
873 
874  if (!*phead || !entry) { /* clang scan couldn't tell that when *phead != NULL then entry != NULL */
875  *phead = talloc_zero(*handle, rlm_sql_grouplist_t);
876  entry = *phead;
877  } else {
878  entry->next = talloc_zero(*phead, rlm_sql_grouplist_t);
879  entry = entry->next;
880  }
881  entry->next = NULL;
882  entry->name = talloc_typed_strdup(entry, row[0]);
883 
884  num_groups++;
885  }
886 
887  (inst->driver->sql_finish_select_query)(*handle, &inst->config);
888 
889  return num_groups;
890 }
891 
892 /** Check if a given group is in the SQL group for this user.
893  *
894  */
895 static bool CC_HINT(nonnull) sql_check_group(rlm_sql_t const *inst, request_t *request, char const *query, char const *name)
896 {
897  bool rcode = false;
898  rlm_sql_handle_t *handle;
899  rlm_sql_grouplist_t *entry, *head = NULL;
900 
901  /*
902  * Get a socket for this lookup
903  */
904  handle = fr_pool_connection_get(inst->pool, request);
905  if (!handle) {
906  REDEBUG("Failed getting connection handle");
907  return false;
908  }
909 
910  /*
911  * Get the list of groups this user is a member of
912  */
913  if (sql_get_grouplist(inst, &handle, request, query, &head) < 0) {
914  talloc_free(head);
915  REDEBUG("Error getting group membership");
916  fr_pool_connection_release(inst->pool, request, handle);
917  return false;
918  }
919 
920  for (entry = head; entry != NULL; entry = entry->next) {
921  if (strcmp(entry->name, name) == 0) {
922  rcode = true;
923  break;
924  }
925  }
926 
927  /* Free the grouplist */
928  talloc_free(head);
929  fr_pool_connection_release(inst->pool, request, handle);
930 
931  return rcode;
932 }
933 
934 typedef struct {
935  fr_value_box_list_t query;
937 
938 static xlat_action_t sql_group_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
939  request_t *request, fr_value_box_list_t *in)
940 {
941  sql_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(xctx->rctx, sql_group_xlat_ctx_t);
942  rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->inst->data, rlm_sql_t);
943  fr_value_box_t *arg = fr_value_box_list_head(in);
944  char const *p = arg->vb_strvalue;
945  fr_value_box_t *query, *vb;
946 
947  query = fr_value_box_list_head(&xlat_ctx->query);
948  if (!query) return XLAT_ACTION_FAIL;
949 
951 
953  vb->vb_bool = sql_check_group(inst, request, query->vb_strvalue, p);
954  fr_dcursor_append(out, vb);
955 
956  return XLAT_ACTION_DONE;
957 }
958 
959 
960 /** Check if the user is a member of a particular group
961  *
962 @verbatim
963 %sql.group(<name>)
964 @endverbatim
965  *
966  * @ingroup xlat_functions
967  */
968 static xlat_action_t sql_group_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx,
969  request_t *request, UNUSED fr_value_box_list_t *in)
970 {
971  sql_group_xlat_call_env_t *call_env = talloc_get_type_abort(xctx->env_data, sql_group_xlat_call_env_t);
972  sql_group_xlat_ctx_t *xlat_ctx;
973  rlm_sql_t const *inst = talloc_get_type_abort(xctx->mctx->inst->data, rlm_sql_t);
974 
975  if (!call_env->membership_query) {
976  RWARN("Cannot check group membership - group_membership_query not set");
977  return XLAT_ACTION_FAIL;
978  }
979 
980  /*
981  * Set the user attr here
982  */
983  sql_set_user(inst, request, &call_env->user);
984 
985  MEM(xlat_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), sql_group_xlat_ctx_t));
986  fr_value_box_list_init(&xlat_ctx->query);
987 
988  if (unlang_xlat_yield(request, sql_group_xlat_resume, NULL, 0, xlat_ctx) != XLAT_ACTION_YIELD) return XLAT_ACTION_FAIL;
989  if (unlang_tmpl_push(xlat_ctx, &xlat_ctx->query, request, call_env->membership_query, NULL) < 0) return XLAT_ACTION_FAIL;
991 }
992 
993 /** Process a "check" map
994  *
995  * Any entries using an assignment operator will be moved to the reply map
996  * for later merging into the request.
997  *
998  * @param request Current request.
999  * @param check_map to process.
1000  * @param reply_map where any assignment entries will be moved.
1001  * @return
1002  * - 0 if all the check entries pass.
1003  * - -1 if the checks fail.
1004  */
1005 static int check_map_process(request_t *request, map_list_t *check_map, map_list_t *reply_map)
1006 {
1007  map_t *map, *next;
1008 
1009  for (map = map_list_head(check_map);
1010  map != NULL;
1011  map = next) {
1012  next = map_list_next(check_map, map);
1013 
1014  if (fr_assignment_op[map->op]) {
1015  (void) map_list_remove(check_map, map);
1016  map_list_insert_tail(reply_map, map);
1017  continue;
1018  }
1019 
1020  if (!fr_comparison_op[map->op]) {
1021  REDEBUG("Invalid operator '%s'", fr_tokens[map->op]);
1022  goto fail;
1023  }
1024 
1025  if (fr_type_is_structural(tmpl_attr_tail_da(map->lhs)->type) &&
1026  (map->op != T_OP_CMP_TRUE) && (map->op != T_OP_CMP_FALSE)) {
1027  REDEBUG("Invalid comparison for structural type");
1028  goto fail;
1029  }
1030 
1031  RDEBUG2(" &%s %s %s", map->lhs->name, fr_tokens[map->op], map->rhs->name);
1032  if (radius_legacy_map_cmp(request, map) != 1) {
1033  fail:
1034  map_list_talloc_free(check_map);
1035  map_list_talloc_free(reply_map);
1036  RDEBUG2("failed match: skipping this entry");
1037  return -1;
1038  }
1039  }
1040  return 0;
1041 }
1042 
1043 static int sql_autz_ctx_free(sql_autz_ctx_t *to_free)
1044 {
1045  (void) request_data_get(to_free->request, (void *)sql_escape_uctx_alloc, 0);
1046  if (to_free->handle) fr_pool_connection_release(to_free->inst->pool, to_free->request, to_free->handle);
1047  map_list_talloc_free(&to_free->check_tmp);
1048  map_list_talloc_free(&to_free->reply_tmp);
1049  sql_unset_user(to_free->inst, to_free->request);
1050 
1051  return 0;
1052 }
1053 
1054 /** Resume function called after authorization group / profile expansion of check / reply query tmpl
1055  *
1056  * Groups and profiles are treated almost identically except:
1057  * - groups are read from an SQL query
1058  * - profiles are read from &control.User-Profile
1059  * - if `cache_groups` is set, groups populate &control.SQL-Group
1060  *
1061  * Profiles are handled after groups, and will not happend if the last group resulted in `Fall-Through = no`
1062  *
1063  * Before each query is run, &request.SQL-Group is populated with the value of the group being evaluated.
1064  *
1065  * @param p_result Result of current authorization.
1066  * @param priority Unused.
1067  * @param request Current request.
1068  * @param uctx Current authorization context.
1069  * @return one of the RLM_MODULE_* values.
1070  */
1071 static unlang_action_t mod_autz_group_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
1072 {
1073  sql_autz_ctx_t *autz_ctx = talloc_get_type_abort(uctx, sql_autz_ctx_t);
1074  sql_autz_call_env_t *call_env = autz_ctx->call_env;
1075  rlm_sql_t const *inst = autz_ctx->inst;
1076  fr_value_box_t *query = fr_value_box_list_pop_head(&autz_ctx->query);
1077  int rows;
1078  sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT;
1079  fr_pair_t *vp;
1080 
1081  switch(autz_ctx->status) {
1082  case SQL_AUTZ_GROUP_MEMB:
1083  rows = sql_get_grouplist(inst, &autz_ctx->handle, request, query->vb_strvalue, &autz_ctx->groups);
1084  talloc_free(query);
1085 
1086  if (rows < 0) {
1087  talloc_free(autz_ctx->groups);
1088  REDEBUG("Error retrieving group list");
1090  }
1091 
1092  if (rows == 0) {
1093  RDEBUG2("User not found in any groups");
1094  break;
1095  }
1096  fr_assert(autz_ctx->groups);
1097 
1098  RDEBUG2("User found in the group table");
1099  autz_ctx->user_found = true;
1100  autz_ctx->group = autz_ctx->groups;
1101  MEM(pair_update_request(&autz_ctx->sql_group, inst->group_da) >= 0);
1102 
1103  next_group:
1104  fr_pair_value_strdup(autz_ctx->sql_group, autz_ctx->group->name, true);
1105  autz_ctx->status = SQL_AUTZ_GROUP_CHECK;
1106  FALL_THROUGH;
1107 
1109  next_profile:
1110  if (autz_ctx->status & SQL_AUTZ_STAGE_PROFILE) {
1111  fr_pair_value_strdup(autz_ctx->sql_group, autz_ctx->profile->vp_strvalue, true);
1112  autz_ctx->status = SQL_AUTZ_PROFILE_CHECK;
1113  }
1114  RDEBUG3("Processing %s %pV",
1115  autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "group" : "profile", &autz_ctx->sql_group->data);
1116  if (inst->config.cache_groups && autz_ctx->status & SQL_AUTZ_STAGE_GROUP) {
1117  MEM(pair_append_control(&vp, inst->group_da) >= 0);
1118  fr_pair_value_strdup(vp, autz_ctx->group->name, true);
1119  }
1120 
1121  if (call_env->group_check_query) {
1123  if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request,
1124  call_env->group_check_query, NULL) < 0) RETURN_MODULE_FAIL;
1126  }
1127 
1128  goto group_reply_push;
1129 
1130  case SQL_AUTZ_GROUP_CHECK:
1132  rows = sql_get_map_list(autz_ctx, inst, request, &autz_ctx->handle, &autz_ctx->check_tmp,
1133  query->vb_strvalue, request_attr_request);
1134  talloc_free(query);
1135 
1136  if (rows < 0) {
1137  REDEBUG("Error retrieving check pairs for %s %pV",
1138  autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "group" : "profile",
1139  &autz_ctx->sql_group->data);
1141  }
1142 
1143  /*
1144  * If we got check rows we need to process them before we decide to
1145  * process the reply rows
1146  */
1147  if (rows > 0) {
1148  if (check_map_process(request, &autz_ctx->check_tmp, &autz_ctx->reply_tmp) < 0) {
1149  map_list_talloc_free(&autz_ctx->check_tmp);
1150  goto next_group_find;
1151  }
1152  RDEBUG2("%s \"%pV\": Conditional check items matched",
1153  autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "Group" : "Profile", &autz_ctx->sql_group->data);
1154  } else {
1155  RDEBUG2("%s \"%pV\": Conditional check items matched (empty)",
1156  autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "Group" : "Profile", &autz_ctx->sql_group->data);
1157  }
1158 
1159  if (autz_ctx->rcode == RLM_MODULE_NOOP) autz_ctx->rcode = RLM_MODULE_OK;
1160 
1161  map_list_talloc_free(&autz_ctx->check_tmp);
1162 
1163  if (call_env->group_reply_query) {
1164  group_reply_push:
1166  if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request,
1167  call_env->group_reply_query, NULL) < 0) RETURN_MODULE_FAIL;
1170  }
1171 
1172  if (map_list_num_elements(&autz_ctx->reply_tmp)) goto group_attr_cache;
1173 
1174  goto next_group_find;
1175 
1176  case SQL_AUTZ_GROUP_REPLY:
1178  rows = sql_get_map_list(autz_ctx, inst, request, &autz_ctx->handle, &autz_ctx->reply_tmp,
1179  query->vb_strvalue, request_attr_reply);
1180  talloc_free(query);
1181 
1182  if (rows < 0) {
1183  REDEBUG("Error retrieving reply pairs for %s %pV",
1184  autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "group" : "profile", &autz_ctx->sql_group->data);
1186  }
1187 
1188  if (rows == 0) {
1189  do_fall_through = FALL_THROUGH_DEFAULT;
1190  goto group_attr_cache;
1191  }
1192 
1193  fr_assert(!map_list_empty(&autz_ctx->reply_tmp)); /* coverity, among others */
1194  do_fall_through = fall_through(&autz_ctx->reply_tmp);
1195 
1196  group_attr_cache:
1197  if (inst->config.cache_groups && autz_ctx->status & SQL_AUTZ_STAGE_GROUP) {
1198  MEM(pair_append_control(&vp, inst->group_da) >= 0);
1199  fr_pair_value_strdup(vp, autz_ctx->group->name, true);
1200  }
1201 
1202  if (map_list_num_elements(&autz_ctx->reply_tmp) == 0) goto next_group_find;
1203  RDEBUG2("%s \"%pV\": Merging control and reply items",
1204  autz_ctx->status & SQL_AUTZ_STAGE_GROUP ? "Group" : "Profile", &autz_ctx->sql_group->data);
1205  autz_ctx->rcode = RLM_MODULE_UPDATED;
1206 
1207  RINDENT();
1208  if (radius_legacy_map_list_apply(request, &autz_ctx->reply_tmp, NULL) < 0) {
1209  RPEDEBUG("Failed applying reply item");
1210  REXDENT();
1212  }
1213  REXDENT();
1214  map_list_talloc_free(&autz_ctx->reply_tmp);
1215 
1216  next_group_find:
1217  if (do_fall_through != FALL_THROUGH_YES) break;
1218  if (autz_ctx->status & SQL_AUTZ_STAGE_PROFILE) {
1219  autz_ctx->profile = fr_pair_find_by_da(&request->control_pairs, autz_ctx->profile, attr_user_profile);
1220  if (autz_ctx->profile) goto next_profile;
1221  break;
1222  }
1223  autz_ctx->group = autz_ctx->group->next;
1224  if (autz_ctx->group) goto next_group;
1225 
1226  break;
1227 
1228  default:
1229  fr_assert(0);
1230  }
1231 
1232  /*
1233  * If group processing has completed, check to see if profile processing should be done
1234  */
1235  if ((autz_ctx->status & SQL_AUTZ_STAGE_GROUP) &&
1236  ((do_fall_through == FALL_THROUGH_YES) ||
1237  (inst->config.read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT)))) {
1238  RDEBUG3("... falling-through to profile processing");
1239 
1240  autz_ctx->profile = fr_pair_find_by_da(&request->control_pairs, NULL, attr_user_profile);
1241  if (autz_ctx->profile) {
1242  MEM(pair_update_request(&autz_ctx->sql_group, inst->group_da) >= 0);
1243  autz_ctx->status = SQL_AUTZ_PROFILE_START;
1244  goto next_profile;
1245  }
1246  }
1247 
1248  if (!autz_ctx->user_found) RETURN_MODULE_NOTFOUND;
1249 
1250  RETURN_MODULE_RCODE(autz_ctx->rcode);
1251 }
1252 
1253 /** Resume function called after authorization check / reply tmpl expansion
1254  *
1255  * @param p_result Result of current authorization.
1256  * @param priority Unused.
1257  * @param request Current request.
1258  * @param uctx Current authorization context.
1259  * @return one of the RLM_MODULE_* values.
1260  */
1261 static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, int *priority, request_t *request, void *uctx)
1262 {
1263  sql_autz_ctx_t *autz_ctx = talloc_get_type_abort(uctx, sql_autz_ctx_t);
1264  sql_autz_call_env_t *call_env = autz_ctx->call_env;
1265  rlm_sql_t const *inst = autz_ctx->inst;
1266  fr_value_box_t *query = fr_value_box_list_pop_head(&autz_ctx->query);
1267  int rows;
1268  sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT;
1269 
1270  switch(autz_ctx->status) {
1271  case SQL_AUTZ_CHECK:
1272  rows = sql_get_map_list(autz_ctx, inst, request, &autz_ctx->handle, &autz_ctx->check_tmp,
1273  query->vb_strvalue, request_attr_request);
1274  talloc_free(query);
1275 
1276  if (rows < 0) {
1277  REDEBUG("Failed getting check attributes");
1279  }
1280 
1281  if (rows == 0) goto skip_reply; /* Don't need to handle map entries we don't have */
1282 
1283  /*
1284  * Only do this if *some* check pairs were returned
1285  */
1286  RDEBUG2("User found in radcheck table");
1287  autz_ctx->user_found = true;
1288 
1289  if (check_map_process(request, &autz_ctx->check_tmp, &autz_ctx->reply_tmp) < 0) goto skip_reply;
1290  RDEBUG2("Conditional check items matched");
1291 
1292  autz_ctx->rcode = RLM_MODULE_OK;
1293  map_list_talloc_free(&autz_ctx->check_tmp);
1294 
1295  if (!call_env->reply_query) goto skip_reply;
1296 
1298  if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request, call_env->reply_query, NULL) < 0) RETURN_MODULE_FAIL;
1299  autz_ctx->status = SQL_AUTZ_REPLY;
1301 
1302  case SQL_AUTZ_REPLY:
1303  rows = sql_get_map_list(autz_ctx, inst, request, &autz_ctx->handle, &autz_ctx->reply_tmp,
1304  query->vb_strvalue, request_attr_reply);
1305  talloc_free(query);
1306 
1307  if (rows < 0) {
1308  REDEBUG("SQL query error getting reply attributes");
1310  }
1311 
1312  if (rows == 0) goto skip_reply;
1313 
1314  do_fall_through = fall_through(&autz_ctx->reply_tmp);
1315 
1316  RDEBUG2("User found in radreply table");
1317  autz_ctx->user_found = true;
1318 
1319  skip_reply:
1320  if (map_list_num_elements(&autz_ctx->reply_tmp)) {
1321  RDEBUG2("Merging control and reply items");
1322  RINDENT();
1323  if (radius_legacy_map_list_apply(request, &autz_ctx->reply_tmp, NULL) < 0) {
1324  RPEDEBUG("Failed applying item");
1325  REXDENT();
1327  }
1328  REXDENT();
1329 
1330  autz_ctx->rcode = RLM_MODULE_UPDATED;
1331  map_list_talloc_free(&autz_ctx->reply_tmp);
1332  }
1333 
1334  if ((do_fall_through == FALL_THROUGH_YES) ||
1335  (inst->config.read_groups && (do_fall_through == FALL_THROUGH_DEFAULT))) {
1336  RDEBUG3("... falling-through to group processing");
1337 
1338  if (!call_env->membership_query) {
1339  RWARN("Cannot check groups when group_membership_query is not set");
1340  break;
1341  }
1342 
1343  if (!call_env->group_check_query && !call_env->group_reply_query) {
1344  RWARN("Cannot process groups when neither authorize_group_check_query nor authorize_group_check_query are set");
1345  break;
1346  }
1347 
1349  if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request,
1350  call_env->membership_query, NULL) < 0) RETURN_MODULE_FAIL;
1351  autz_ctx->status = SQL_AUTZ_GROUP_MEMB;
1353  }
1354 
1355  if ((do_fall_through == FALL_THROUGH_YES) ||
1356  (inst->config.read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT))) {
1357  RDEBUG3("... falling-through to profile processing");
1358 
1359  if (!call_env->group_check_query && !call_env->group_reply_query) {
1360  RWARN("Cannot process profiles when neither authorize_group_check_query nor authorize_group_check_query are set");
1361  break;
1362  }
1363 
1364  autz_ctx->profile = fr_pair_find_by_da(&request->control_pairs, NULL, attr_user_profile);
1365  if (!autz_ctx->profile) break;
1366 
1367  MEM(pair_update_request(&autz_ctx->sql_group, inst->group_da) >= 0);
1368  autz_ctx->status = SQL_AUTZ_PROFILE_START;
1369  return mod_autz_group_resume(p_result, priority, request, autz_ctx);
1370  }
1371  break;
1372 
1373  default:
1374  fr_assert(0);
1375  }
1376 
1377  if (!autz_ctx->user_found) RETURN_MODULE_NOTFOUND;
1378  RETURN_MODULE_RCODE(autz_ctx->rcode);
1379 }
1380 
1381 /** Start of module authorize method
1382  *
1383  * Pushes the tmpl relating to the first required query for evaluation
1384  */
1385 static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1386 {
1388  sql_autz_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, sql_autz_call_env_t);
1389  sql_autz_ctx_t *autz_ctx;
1390 
1391  fr_assert(request->packet != NULL);
1392  fr_assert(request->reply != NULL);
1393 
1394  if (!call_env->check_query && !call_env->reply_query && !(inst->config.read_groups && call_env->membership_query)) {
1395  RWDEBUG("No authorization checks configured, returning noop");
1397  }
1398 
1399  /*
1400  * Set and check the user attr here
1401  */
1402  sql_set_user(inst, request, &call_env->user);
1403 
1404  MEM(autz_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), sql_autz_ctx_t));
1405  *autz_ctx = (sql_autz_ctx_t) {
1406  .inst = inst,
1407  .call_env = call_env,
1408  .request = request,
1409  .rcode = RLM_MODULE_NOOP
1410  };
1411  map_list_init(&autz_ctx->check_tmp);
1412  map_list_init(&autz_ctx->reply_tmp);
1413  talloc_set_destructor(autz_ctx, sql_autz_ctx_free);
1414 
1415  /*
1416  * Reserve a socket
1417  *
1418  * This is freed by the talloc destructor for autz_ctx
1419  */
1420  autz_ctx->handle = fr_pool_connection_get(inst->pool, request);
1421  if (!autz_ctx->handle) RETURN_MODULE_FAIL;
1422 
1423  request_data_add(request, (void *)sql_escape_uctx_alloc, 0, autz_ctx->handle, false, false, false);
1424 
1425  if (unlang_function_push(request, NULL, mod_authorize_resume, NULL, 0,
1426  UNLANG_SUB_FRAME, autz_ctx) < 0) {
1427  error:
1428  talloc_free(autz_ctx);
1430  }
1431 
1432  fr_value_box_list_init(&autz_ctx->query);
1433 
1434  /*
1435  * Query the check table to find any conditions associated with this user/realm/whatever...
1436  */
1437  if (call_env->check_query) {
1438  if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request, call_env->check_query, NULL) < 0) goto error;
1439  autz_ctx->status = SQL_AUTZ_CHECK;
1441  }
1442 
1443  if (call_env->reply_query) {
1444  if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request, call_env->reply_query, NULL) < 0) goto error;
1445  autz_ctx->status = SQL_AUTZ_REPLY;
1447  }
1448 
1449  /*
1450  * Neither check nor reply queries were set, so we must be doing group stuff
1451  */
1452  if (unlang_tmpl_push(autz_ctx, &autz_ctx->query, request, call_env->membership_query, NULL) < 0) goto error;
1453  autz_ctx->status = SQL_AUTZ_GROUP_MEMB;
1455 }
1456 
1457 /** Tidy up when freeing an SQL redundant context
1458  *
1459  * Release the connection handle and unset the SQL-User attribute.
1460  */
1462 {
1463  (void) request_data_get(to_free->request, (void *)sql_escape_uctx_alloc, 0);
1464  if (to_free->handle) fr_pool_connection_release(to_free->inst->pool, to_free->request, to_free->handle);
1465  sql_unset_user(to_free->inst, to_free->request);
1466 
1467  return 0;
1468 }
1469 
1470 /** Resume function called after expansion of next query in a redundant list of queries
1471  *
1472  * @param p_result Result of current module call.
1473  * @param priority Unused.
1474  * @param request Current request.
1475  * @param uctx Current redundant sql context.
1476  * @return one of the RLM_MODULE_* values.
1477  */
1478 static unlang_action_t mod_sql_redundant_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
1479 {
1480  sql_redundant_ctx_t *redundant_ctx = talloc_get_type_abort(uctx, sql_redundant_ctx_t);
1481  sql_redundant_call_env_t *call_env = redundant_ctx->call_env;
1482  rlm_sql_t const *inst = redundant_ctx->inst;
1483  fr_value_box_t *query;
1484  int sql_ret;
1485  int numaffected = 0;
1486  tmpl_t *next_query;
1487 
1488  query = fr_value_box_list_pop_head(&redundant_ctx->query);
1489  if (!query) RETURN_MODULE_FAIL;
1490 
1491  if ((call_env->filename.type == FR_TYPE_STRING) && (call_env->filename.vb_length > 0)) {
1492  rlm_sql_query_log(inst, call_env->filename.vb_strvalue, query->vb_strvalue);
1493  }
1494 
1495  sql_ret = rlm_sql_query(inst, request, &redundant_ctx->handle, query->vb_strvalue);
1496  talloc_free(query);
1497 
1498  RDEBUG2("SQL query returned: %s", fr_table_str_by_value(sql_rcode_description_table, sql_ret, "<INVALID>"));
1499 
1500  switch (sql_ret) {
1501  /*
1502  * Query was a success! Now we just need to check if it did anything.
1503  */
1504  case RLM_SQL_OK:
1505  break;
1506 
1507  /*
1508  * A general, unrecoverable server fault.
1509  */
1510  case RLM_SQL_ERROR:
1511  /*
1512  * If we get RLM_SQL_RECONNECT it means all connections in the pool
1513  * were exhausted, and we couldn't create a new connection,
1514  * so we do not need to call fr_pool_connection_release.
1515  */
1516  case RLM_SQL_RECONNECT:
1518 
1519  /*
1520  * Query was invalid, this is a terminal error.
1521  */
1522  case RLM_SQL_QUERY_INVALID:
1524 
1525  /*
1526  * Driver found an error (like a unique key constraint violation)
1527  * that hinted it might be a good idea to try an alternative query.
1528  */
1529  case RLM_SQL_ALT_QUERY:
1530  goto next;
1531  }
1532  fr_assert(redundant_ctx->handle);
1533 
1534  /*
1535  * We need to have updated something for the query to have been
1536  * counted as successful.
1537  */
1538  numaffected = (inst->driver->sql_affected_rows)(redundant_ctx->handle, &inst->config);
1539  (inst->driver->sql_finish_query)(redundant_ctx->handle, &inst->config);
1540  RDEBUG2("%i record(s) updated", numaffected);
1541 
1542  if (numaffected > 0) RETURN_MODULE_OK; /* A query succeeded, were done! */
1543 next:
1544  /*
1545  * Look to see if there are any more queries to expand
1546  */
1547  redundant_ctx->query_no++;
1548  if (redundant_ctx->query_no >= talloc_array_length(call_env->query)) RETURN_MODULE_NOOP;
1549  next_query = *(tmpl_t **)((uint8_t *)call_env->query + sizeof(void *) * redundant_ctx->query_no);
1551  if (unlang_tmpl_push(redundant_ctx, &redundant_ctx->query, request, next_query, NULL) < 0) RETURN_MODULE_FAIL;
1552 
1553  RDEBUG2("Trying next query...");
1554 
1556 }
1557 
1558 /** Generic module call for failing between a bunch of queries.
1559  *
1560  * Used for `accounting` and `send` module calls
1561  *
1562  */
1563 static unlang_action_t CC_HINT(nonnull) mod_sql_redundant(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1564 {
1566  sql_redundant_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, sql_redundant_call_env_t);
1567  sql_redundant_ctx_t *redundant_ctx;
1568 
1569  /*
1570  * No query to expand - do nothing.
1571  */
1572  if (!call_env->query) {
1573  RWARN("No query configured");
1575  }
1576 
1577  MEM(redundant_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), sql_redundant_ctx_t));
1578  *redundant_ctx = (sql_redundant_ctx_t) {
1579  .inst = inst,
1580  .request = request,
1581  .call_env = call_env,
1582  .query_no = 0
1583  };
1584  talloc_set_destructor(redundant_ctx, sql_redundant_ctx_free);
1585 
1586  redundant_ctx->handle = fr_pool_connection_get(inst->pool, request);
1587  if (!redundant_ctx->handle) RETURN_MODULE_FAIL;
1588 
1589  request_data_add(request, (void *)sql_escape_uctx_alloc, 0, redundant_ctx->handle, false, false, false);
1590 
1591  sql_set_user(inst, request, &call_env->user);
1592 
1593  if (unlang_function_push(request, NULL, mod_sql_redundant_resume, NULL, 0,
1594  UNLANG_SUB_FRAME, redundant_ctx) < 0) RETURN_MODULE_FAIL;
1595 
1596  fr_value_box_list_init(&redundant_ctx->query);
1597  if (unlang_tmpl_push(redundant_ctx, &redundant_ctx->query, request, *call_env->query, NULL) < 0) RETURN_MODULE_FAIL;
1598 
1600 }
1601 
1602 static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
1603  CONF_ITEM *ci, char const *section_name1, char const *section_name2,
1604  UNUSED void const *data, UNUSED call_env_parser_t const *rule)
1605 {
1606  CONF_SECTION const *subcs = NULL, *subsubcs = NULL;
1607  CONF_PAIR const *to_parse = NULL;
1608  tmpl_t *parsed_tmpl;
1609  call_env_parsed_t *parsed_env;
1610  tmpl_rules_t our_rules;
1611  char *section2, *p;
1612 
1613  /*
1614  * The call env subsection which calls this has CF_IDENT_ANY as its name
1615  * which results in finding the first child section of the module config.
1616  * We actually want the whole module config - so go to the parent.
1617  */
1618  ci = cf_parent(ci);
1619 
1620  /*
1621  * Find the instance of "logfile" to parse
1622  *
1623  * If the module call is from `accounting Start` then first is
1624  * <module> { accounting { start { logfile } } }
1625  * then
1626  * <module> { accounting { logfile } }
1627  * falling back to
1628  * <module> { logfile }
1629  */
1630  subcs = cf_section_find(cf_item_to_section(ci), section_name1, CF_IDENT_ANY);
1631  if (subcs) {
1632  if (section_name2) {
1633  section2 = talloc_strdup(NULL, section_name2);
1634  p = section2;
1635  while (*p != '\0') {
1636  *(p) = tolower((uint8_t)*p);
1637  p++;
1638  }
1639  subsubcs = cf_section_find(subcs, section2, CF_IDENT_ANY);
1640  talloc_free(section2);
1641  if (subsubcs) to_parse = cf_pair_find(subsubcs, "logfile");
1642  }
1643  if (!to_parse) to_parse = cf_pair_find(subcs, "logfile");
1644  }
1645 
1646  if (!to_parse) to_parse = cf_pair_find(cf_item_to_section(ci), "logfile");
1647 
1648  if (!to_parse) return 0;
1649 
1650  /*
1651  * Use filename safety escape functions
1652  */
1653  our_rules = *t_rules;
1656  our_rules.escape.mode = TMPL_ESCAPE_PRE_CONCAT;
1657  our_rules.literals_safe_for = our_rules.escape.safe_for;
1658 
1659  MEM(parsed_env = call_env_parsed_add(ctx, out,
1661 
1662  if (tmpl_afrom_substr(parsed_env, &parsed_tmpl,
1663  &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
1664  cf_pair_value_quote(to_parse), NULL, &our_rules) < 0) {
1665  error:
1666  call_env_parsed_free(out, parsed_env);
1667  return -1;
1668  }
1669  if (tmpl_needs_resolving(parsed_tmpl) &&
1670  (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = our_rules.attr.dict_def }) < 0)) goto error;
1671 
1672  call_env_parsed_set_tmpl(parsed_env, parsed_tmpl);
1673 
1674  return 0;
1675 }
1676 
1677 static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
1678  CONF_ITEM *ci, UNUSED char const *section_name1, char const *section_name2,
1679  void const *data, UNUSED call_env_parser_t const *rule)
1680 {
1682  CONF_SECTION const *subcs = NULL;
1683  CONF_PAIR const *to_parse = NULL;
1684  tmpl_t *parsed_tmpl;
1685  call_env_parsed_t *parsed_env;
1686  tmpl_rules_t our_rules;
1687  char *section2, *p;
1688  ssize_t count, slen, multi_index = 0;
1689 
1690  if (!section_name2) return -1;
1691 
1692  /*
1693  * Find the instance(s) of "query" to parse
1694  *
1695  * If the module call is from `accounting Start` then it should be
1696  * <module> { accounting { start { query } } }
1697  */
1698  section2 = talloc_strdup(NULL, section_name2);
1699  p = section2;
1700  while (*p != '\0') {
1701  *(p) = tolower((uint8_t)*p);
1702  p++;
1703  }
1704  subcs = cf_section_find(cf_item_to_section(ci), section2, CF_IDENT_ANY);
1705  talloc_free(section2);
1706  if (!subcs) return 0;
1707 
1708  /*
1709  * Use module specific escape functions
1710  */
1711  our_rules = *t_rules;
1712  our_rules.escape.func = inst->box_escape_func;
1713  our_rules.escape.safe_for = (fr_value_box_safe_for_t)inst->driver;
1714  our_rules.escape.mode = TMPL_ESCAPE_PRE_CONCAT;
1715  our_rules.literals_safe_for = our_rules.escape.safe_for;
1716 
1717  count = cf_pair_count(subcs, "query");
1718 
1719  while ((to_parse = cf_pair_find_next(subcs, to_parse, "query"))) {
1720  MEM(parsed_env = call_env_parsed_add(ctx, out,
1722 
1723  slen = tmpl_afrom_substr(parsed_env, &parsed_tmpl,
1724  &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
1725  cf_pair_value_quote(to_parse), NULL, &our_rules);
1726  if (slen <= 0) {
1727  cf_canonicalize_error(to_parse, slen, "Failed parsing query", cf_pair_value(to_parse));
1728  error:
1729  call_env_parsed_free(out, parsed_env);
1730  return -1;
1731  }
1732  if (tmpl_needs_resolving(parsed_tmpl) &&
1733  (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = our_rules.attr.dict_def }) < 0)) {
1734  cf_log_perr(to_parse, "Failed resolving query");
1735  goto error;
1736  }
1737 
1738  call_env_parsed_set_multi_index(parsed_env, count, multi_index++);
1739  call_env_parsed_set_data(parsed_env, parsed_tmpl);
1740  }
1741 
1742  return 0;
1743 }
1744 
1745 static int mod_detach(module_detach_ctx_t const *mctx)
1746 {
1747  rlm_sql_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sql_t);
1748 
1749  if (inst->pool) fr_pool_free(inst->pool);
1750 
1751  /*
1752  * We need to explicitly free all children, so if the driver
1753  * parented any memory off the instance, their destructors
1754  * run before we unload the bytecode for them.
1755  *
1756  * If we don't do this, we get a SEGV deep inside the talloc code
1757  * when it tries to call a destructor that no longer exists.
1758  */
1759  talloc_free_children(inst);
1760 
1761  return 0;
1762 }
1763 
1764 static int mod_bootstrap(module_inst_ctx_t const *mctx)
1765 {
1766  rlm_sql_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sql_t);
1767  CONF_SECTION *conf = mctx->inst->conf;
1768  xlat_t *xlat;
1769  xlat_arg_parser_t *sql_xlat_arg;
1770  rlm_sql_escape_uctx_t *uctx;
1771 
1772  inst->name = mctx->inst->name; /* Need this for functions in sql.c */
1773  inst->driver = (rlm_sql_driver_t const *)inst->driver_submodule->module; /* Public symbol exported by the submodule */
1774 
1775  /*
1776  * Register the group comparison attribute
1777  */
1778  if (cf_pair_find(conf, "group_membership_query")) {
1779  char const *group_attribute;
1780  fr_dict_attr_flags_t flags = {};
1781  char buffer[256];
1782 
1783  if (inst->config.group_attribute) {
1784  group_attribute = inst->config.group_attribute;
1785  } else if (cf_section_name2(conf)) {
1786  snprintf(buffer, sizeof(buffer), "%s-SQL-Group", mctx->inst->name);
1787  group_attribute = buffer;
1788  } else {
1789  group_attribute = "SQL-Group";
1790  }
1791 
1793  FR_TYPE_STRING, &flags) < 0) {
1794  cf_log_perr(conf, "Failed defining group attribute");
1795  return -1;
1796  }
1797 
1798  inst->group_da = fr_dict_attr_search_by_qualified_oid(NULL, dict_freeradius, group_attribute,
1799  false, false);
1800  if (!inst->group_da) {
1801  cf_log_perr(conf, "Failed resolving group attribute");
1802  return -1;
1803  }
1804 
1805  /*
1806  * Define the new %sql.group(name) xlat. The
1807  * register function automatically adds the
1808  * module instance name as a prefix.
1809  */
1810  xlat = xlat_func_register_module(inst, mctx, "group", sql_group_xlat, FR_TYPE_BOOL);
1811  if (!xlat) {
1812  cf_log_perr(conf, "Failed registering %s expansion", group_attribute);
1813  return -1;
1814  }
1816 
1817  /*
1818  * The xlat escape function needs access to inst - so
1819  * argument parser details need to be defined here
1820  */
1821  sql_xlat_arg = talloc_zero_array(inst, xlat_arg_parser_t, 2);
1822  sql_xlat_arg[0].type = FR_TYPE_STRING;
1823  sql_xlat_arg[0].required = true;
1824  sql_xlat_arg[0].concat = true;
1825  sql_xlat_arg[0].func = NULL; /* No real escaping done - we do strcmp() on it */
1826  sql_xlat_arg[0].uctx = NULL;
1827  sql_xlat_arg[1] = (xlat_arg_parser_t)XLAT_ARG_PARSER_TERMINATOR;
1828 
1829  xlat_func_mono_set(xlat, sql_xlat_arg);
1830  }
1831 
1832  /*
1833  * Register the SQL xlat function
1834  */
1835  xlat = xlat_func_register_module(inst, mctx, NULL, sql_xlat, FR_TYPE_VOID); /* Returns an integer sometimes */
1836  if (!xlat) {
1837  cf_log_perr(conf, "Failed registering %s expansion", mctx->inst->name);
1838  return -1;
1839  }
1841 
1842  /*
1843  * The xlat escape function needs access to inst - so
1844  * argument parser details need to be defined here
1845  */
1846  sql_xlat_arg = talloc_zero_array(inst, xlat_arg_parser_t, 2);
1847  uctx = talloc_zero(sql_xlat_arg, rlm_sql_escape_uctx_t);
1848  *uctx = (rlm_sql_escape_uctx_t){ .sql = inst, .handle = NULL };
1849  sql_xlat_arg[0].type = FR_TYPE_STRING;
1850  sql_xlat_arg[0].required = true;
1851  sql_xlat_arg[0].concat = true;
1852  sql_xlat_arg[0].func = sql_xlat_escape;
1853  sql_xlat_arg[0].safe_for = (fr_value_box_safe_for_t)inst->driver;
1854  sql_xlat_arg[0].uctx = uctx;
1855  sql_xlat_arg[1] = (xlat_arg_parser_t)XLAT_ARG_PARSER_TERMINATOR;
1856 
1857  xlat_func_mono_set(xlat, sql_xlat_arg);
1858 
1859  /*
1860  * Register the SQL map processor function
1861  */
1862  if (inst->driver->sql_fields) map_proc_register(inst, mctx->inst->name, mod_map_proc, sql_map_verify, 0, (fr_value_box_safe_for_t)inst->driver);
1863 
1864  return 0;
1865 }
1866 
1867 static int mod_instantiate(module_inst_ctx_t const *mctx)
1868 {
1869  rlm_sql_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sql_t);
1870  CONF_SECTION *conf = mctx->inst->conf;
1871 
1872  /*
1873  * We need authorize_group_check_query or authorize_group_reply_query
1874  * if group_membership_query is set.
1875  *
1876  * Or we need group_membership_query if authorize_group_check_query or
1877  * authorize_group_reply_query is set.
1878  */
1879  if (!cf_pair_find(conf, "group_membership_query")) {
1880  if (cf_pair_find(conf, "authorize_group_check_query")) {
1881  WARN("Ignoring authorize_group_check_query as group_membership_query is not configured");
1882  }
1883 
1884  if (cf_pair_find(conf, "authorize_group_reply_query")) {
1885  WARN("Ignoring authorize_group_reply_query as group_membership_query is not configured");
1886  }
1887 
1888  if (!inst->config.read_groups) {
1889  WARN("Ignoring read_groups as group_membership_query is not configured");
1890  inst->config.read_groups = false;
1891  }
1892  } /* allow the group check / reply queries to be NULL */
1893 
1894  /*
1895  * Cache the SQL-User-Name fr_dict_attr_t, so we can be slightly
1896  * more efficient about creating SQL-User-Name attributes.
1897  */
1898  inst->sql_user = attr_sql_user_name;
1899 
1900  /*
1901  * Export these methods, too. This avoids RTDL_GLOBAL.
1902  */
1903  inst->query = rlm_sql_query;
1904  inst->select = rlm_sql_select_query;
1905  inst->fetch_row = rlm_sql_fetch_row;
1906 
1907  /*
1908  * Either use the module specific escape function
1909  * or our default one.
1910  */
1911  inst->sql_escape_func = inst->driver->sql_escape_func ?
1912  inst->driver->sql_escape_func :
1914  inst->box_escape_func = sql_box_escape;
1915 
1916  inst->ef = module_rlm_exfile_init(inst, conf, 256, fr_time_delta_from_sec(30), true, NULL, NULL);
1917  if (!inst->ef) {
1918  cf_log_err(conf, "Failed creating log file context");
1919  return -1;
1920  }
1921 
1922  /*
1923  * Initialise the connection pool for this instance
1924  */
1925  INFO("Attempting to connect to database \"%s\"", inst->config.sql_db);
1926 
1927  inst->pool = module_rlm_connection_pool_init(conf, inst, sql_mod_conn_create, NULL, NULL, NULL, NULL);
1928  if (!inst->pool) return -1;
1929 
1930  return 0;
1931 }
1932 
1933 /* globally exported name */
1935  .common = {
1936  .magic = MODULE_MAGIC_INIT,
1937  .name = "sql",
1938  .flags = MODULE_TYPE_THREAD_SAFE,
1939  .inst_size = sizeof(rlm_sql_t),
1940  .config = module_config,
1941  .bootstrap = mod_bootstrap,
1943  .detach = mod_detach
1944  },
1945  .method_names = (module_method_name_t[]){
1946  /*
1947  * Hack to support old configurations
1948  */
1949  { .name1 = "authorize", .name2 = CF_IDENT_ANY, .method = mod_authorize,
1950  .method_env = &authorize_method_env },
1951 
1952  { .name1 = "recv", .name2 = CF_IDENT_ANY, .method = mod_authorize,
1953  .method_env = &authorize_method_env },
1954  { .name1 = "accounting", .name2 = CF_IDENT_ANY, .method = mod_sql_redundant,
1955  .method_env = &accounting_method_env },
1956  { .name1 = "send", .name2 = CF_IDENT_ANY, .method = mod_sql_redundant,
1957  .method_env = &send_method_env },
1959  }
1960 };
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
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:444
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define unlikely(_x)
Definition: build.h:378
#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:697
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:610
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:682
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:667
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:639
#define CALL_ENV_TERMINATOR
Definition: call_env.h:212
#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:216
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _ident2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
Definition: call_env.h:388
@ CALL_ENV_FLAG_CONCAT
If the tmpl produced multiple boxes they should be concatenated.
Definition: call_env.h:74
@ CALL_ENV_FLAG_SUBSECTION
This is a subsection.
Definition: call_env.h:85
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition: call_env.h:83
@ CALL_ENV_FLAG_MULTI
Multiple instances of the conf pairs are allowed.
Definition: call_env.h:76
#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:316
#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:365
Per method call config.
Definition: call_env.h:171
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:256
@ CONF_FLAG_SECRET
Only print value if debug level >= 3.
Definition: cf_parse.h:410
#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:563
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:89
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:970
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:1437
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:1356
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1126
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1511
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition: cf_util.c:649
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition: cf_util.c:1555
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:1370
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
#define cf_parent(_cf)
Definition: cf_util.h:98
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition: cf_util.h:340
#define cf_log_perr(_cf, _fmt,...)
Definition: cf_util.h:272
#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:405
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:2678
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:1997
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition: dict_util.c:4179
static fr_slen_t in
Definition: dict.h:645
int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t const *flags))
Add an attribute to the dictionary.
Definition: dict_util.c:1245
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Values of the encryption flags.
Definition: merged_model.c:139
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
Test enumeration values.
Definition: dict_test.h:92
char const *_CONST name
Instance name.
Definition: dl_module.h:163
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
dl_module_inst_t const * inst
Definition: dl_module.h:87
#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_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:968
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:372
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition: interpret.c:1384
#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:1783
int rad_filename_box_make_safe(fr_value_box_t *vb, UNUSED void *uxtc)
Definition: util.c:165
talloc_free(reap)
int map_proc_register(void *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:111
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ 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
void * env_data
Per call environment data.
Definition: module_ctx.h:44
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
Specifies a module method identifier.
Definition: module_method.c:36
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:92
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:913
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:248
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
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:688
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition: pair.c:2631
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:462
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:2586
int radius_legacy_map_cmp(request_t *request, map_t const *map)
Definition: pairmove.c:791
int radius_legacy_map_list_apply(request_t *request, map_list_t const *list, fr_edit_list_t *el)
Definition: pairmove.c:772
void fr_pool_connection_release(fr_pool_t *pool, request_t *request, void *conn)
Release a connection.
Definition: pool.c:1405
void fr_pool_free(fr_pool_t *pool)
Delete a connection pool.
Definition: pool.c:1327
void * fr_pool_connection_get(fr_pool_t *pool, request_t *request)
Reserve a connection in the connection pool.
Definition: pool.c:1390
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
static const conf_parser_t config[]
Definition: base.c:188
#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 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 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_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:41
fr_dict_attr_t const * request_attr_reply
Definition: request.c:42
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
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1312
fr_value_box_t user
Definition: rlm_sql.c:216
rlm_sql_t const * inst
Module instance.
Definition: rlm_sql.c:207
static void * sql_escape_uctx_alloc(request_t *request, void const *uctx)
Definition: rlm_sql.c:238
static int mod_detach(module_detach_ctx_t const *mctx)
Definition: rlm_sql.c:1745
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:1478
static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule)
sql_redundant_call_env_t * call_env
Call environment data.
Definition: rlm_sql.c:210
rlm_sql_grouplist_t * groups
List of groups returned by the group membership query.
Definition: rlm_sql.c:172
tmpl_t * check_query
Tmpl to expand to form authorize_check_query.
Definition: rlm_sql.c:96
static unlang_action_t mod_autz_group_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
Resume function called after authorization group / profile expansion of check / reply query tmpl.
Definition: rlm_sql.c:1071
static sql_fall_through_t fall_through(map_list_t *maps)
Definition: rlm_sql.c:257
rlm_sql_grouplist_t * group
Current group being processed.
Definition: rlm_sql.c:173
fr_value_box_t filename
File name to write SQL logs to.
Definition: rlm_sql.c:180
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:491
request_t * request
Request being processed.
Definition: rlm_sql.c:208
size_t query_no
Current query number.
Definition: rlm_sql.c:211
static const call_env_method_t authorize_method_env
Definition: rlm_sql.c:103
tmpl_t * reply_query
Tmpl to expand to form authorize_reply_query.
Definition: rlm_sql.c:97
static int _sql_escape_uxtx_free(void *uctx)
Definition: rlm_sql.c:229
static fr_dict_attr_t const * attr_sql_user_name
Definition: rlm_sql.c:81
static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule)
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:1005
#define sql_unset_user(_i, _r)
Definition: rlm_sql.c:841
rlm_sql_handle_t * handle
Database connection handle.
Definition: rlm_sql.c:209
static fr_dict_attr_t const * attr_fall_through
Definition: rlm_sql.c:80
static fr_dict_t const * dict_freeradius
Definition: rlm_sql.c:72
static void sql_set_user(rlm_sql_t const *inst, request_t *request, fr_value_box_t *user)
Definition: rlm_sql.c:819
fr_value_box_t filename
Definition: rlm_sql.c:123
static fr_dict_attr_t const * attr_expr_bool_enum
Definition: rlm_sql.c:83
fr_value_box_t user
Expansion of the sql_user_name.
Definition: rlm_sql.c:95
fr_pair_t * sql_group
Pair to update with group being processed.
Definition: rlm_sql.c:174
#define SQL_AUTZ_STAGE_PROFILE
Definition: rlm_sql.c:157
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:1563
map_list_t check_tmp
List to store check items before processing.
Definition: rlm_sql.c:167
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition: rlm_sql.c:1764
rlm_sql_handle_t * handle
Database connection handle in use for current authorization.
Definition: rlm_sql.c:165
static xlat_action_t sql_group_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Definition: rlm_sql.c:938
sql_autz_call_env_t * call_env
Call environment data.
Definition: rlm_sql.c:166
module_rlm_t rlm_sql
Definition: rlm_sql.c:1934
static int sql_map_verify(CONF_SECTION *cs, UNUSED void *mod_inst, UNUSED void *proc_inst, tmpl_t const *src, UNUSED map_list_t const *maps)
Definition: rlm_sql.c:516
static const call_env_method_t xlat_method_env
Definition: rlm_sql.c:126
static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void *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:541
sql_autz_status_t
Status of the authorization process.
Definition: rlm_sql.c:145
@ SQL_AUTZ_PROFILE_REPLY
Running profile reply query.
Definition: rlm_sql.c:153
@ SQL_AUTZ_CHECK
Running user check query.
Definition: rlm_sql.c:146
@ SQL_AUTZ_REPLY
Running user reply query.
Definition: rlm_sql.c:147
@ SQL_AUTZ_GROUP_MEMB
Running group membership query.
Definition: rlm_sql.c:148
@ SQL_AUTZ_PROFILE_START
Starting processing user profiles.
Definition: rlm_sql.c:151
@ SQL_AUTZ_GROUP_CHECK
Running group check query.
Definition: rlm_sql.c:149
@ SQL_AUTZ_PROFILE_CHECK
Running profile check query.
Definition: rlm_sql.c:152
@ SQL_AUTZ_GROUP_REPLY
Running group reply query.
Definition: rlm_sql.c:150
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:296
rlm_sql_t const * inst
Module instance.
Definition: rlm_sql.c:162
#define SQL_AUTZ_STAGE_GROUP
Definition: rlm_sql.c:156
tmpl_t * group_reply_query
Tmpl to expand to form authorize_group_reply_query.
Definition: rlm_sql.c:100
tmpl_t * group_check_query
Tmpl to expand to form authorize_group_check_query.
Definition: rlm_sql.c:99
static int sql_autz_ctx_free(sql_autz_ctx_t *to_free)
Definition: rlm_sql.c:1043
static int sql_get_grouplist(rlm_sql_t const *inst, rlm_sql_handle_t **handle, request_t *request, char const *query, rlm_sql_grouplist_t **phead)
Definition: rlm_sql.c:849
fr_value_box_list_t query
Where expanded query tmpl will be written.
Definition: rlm_sql.c:212
map_list_t reply_tmp
List to store reply items before processing.
Definition: rlm_sql.c:168
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:1385
sql_autz_status_t status
Current status of the authorization.
Definition: rlm_sql.c:169
request_t * request
Request being processed.
Definition: rlm_sql.c:163
fr_pair_t * profile
Current profile being processed.
Definition: rlm_sql.c:175
#define MAX_SQL_FIELD_INDEX
fr_dict_attr_autoload_t rlm_sql_dict_attr[]
Definition: rlm_sql.c:86
fr_value_box_list_t query
Definition: rlm_sql.c:935
static bool sql_check_group(rlm_sql_t const *inst, request_t *request, char const *query, char const *name)
Check if a given group is in the SQL group for this user.
Definition: rlm_sql.c:895
rlm_rcode_t rcode
Module return code.
Definition: rlm_sql.c:164
tmpl_t ** query
Array of tmpls for list of queries to run.
Definition: rlm_sql.c:181
rlm_sql_grouplist_t * next
Definition: rlm_sql.c:846
static const call_env_method_t accounting_method_env
Definition: rlm_sql.c:184
static int sql_redundant_ctx_free(sql_redundant_ctx_t *to_free)
Tidy up when freeing an SQL redundant context.
Definition: rlm_sql.c:1461
static const call_env_method_t group_xlat_method_env
Definition: rlm_sql.c:220
tmpl_t * membership_query
Tmpl to expand to form group_membership_query.
Definition: rlm_sql.c:98
static int sql_box_escape(fr_value_box_t *vb, void *uctx)
Definition: rlm_sql.c:355
static const conf_parser_t module_config[]
Definition: rlm_sql.c:48
fr_value_box_list_t query
Where expanded query tmpls will be written.
Definition: rlm_sql.c:170
bool user_found
Has the user been found anywhere?
Definition: rlm_sql.c:171
static fr_dict_attr_t const * attr_user_profile
Definition: rlm_sql.c:82
static const call_env_method_t send_method_env
Definition: rlm_sql.c:194
fr_dict_autoload_t rlm_sql_dict[]
Definition: rlm_sql.c:75
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_sql.c:1867
fr_value_box_t user
Expansion of sql_user_name.
Definition: rlm_sql.c:179
static size_t sql_escape_func(request_t *, char *out, size_t outlen, char const *in, void *arg)
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:1261
Context for SQL authorization.
Definition: rlm_sql.c:161
Context for tracking redundant SQL query sets.
Definition: rlm_sql.c:206
Prototypes and functions for the SQL module.
rlm_sql_t const * inst
The rlm_sql instance this connection belongs to.
Definition: rlm_sql.h:103
void rlm_sql_query_log(rlm_sql_t const *inst, char const *filename, char const *query)
Definition: sql.c:613
sql_rcode_t rlm_sql_fetch_row(rlm_sql_row_t *out, rlm_sql_t const *inst, request_t *request, rlm_sql_handle_t **handle)
Call the driver's sql_fetch_row function.
Definition: sql.c:299
int sql_get_map_list(TALLOC_CTX *ctx, rlm_sql_t const *inst, request_t *request, rlm_sql_handle_t **handle, map_list_t *out, char const *query, fr_dict_attr_t const *list)
Definition: sql.c:559
rlm_sql_handle_t * handle
Definition: rlm_sql.h:144
sql_rcode_t
Action to take at end of an SQL query.
Definition: rlm_sql.h:42
@ RLM_SQL_QUERY_INVALID
Query syntax error.
Definition: rlm_sql.h:43
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
Definition: rlm_sql.h:47
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
Definition: rlm_sql.h:46
@ RLM_SQL_ERROR
General connection/server error.
Definition: rlm_sql.h:44
@ RLM_SQL_OK
Success.
Definition: rlm_sql.h:45
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition: rlm_sql.h:48
struct sql_inst rlm_sql_t
Definition: rlm_sql.h:98
sql_rcode_t rlm_sql_select_query(rlm_sql_t const *inst, request_t *request, rlm_sql_handle_t **handle, char const *query))
Call the driver's sql_select_query method, reconnecting if necessary.
Definition: sql.c:493
sql_fall_through_t
Definition: rlm_sql.h:51
@ FALL_THROUGH_DEFAULT
Definition: rlm_sql.h:54
@ FALL_THROUGH_YES
Definition: rlm_sql.h:53
char ** rlm_sql_row_t
Definition: rlm_sql.h:57
void * sql_mod_conn_create(TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout)
Definition: sql.c:65
rlm_sql_t const * sql
Definition: rlm_sql.h:143
sql_rcode_t rlm_sql_query(rlm_sql_t const *inst, request_t *request, rlm_sql_handle_t **handle, char const *query))
Call the driver's sql_query method, reconnecting if necessary.
Definition: sql.c:392
fr_table_num_sorted_t const sql_rcode_description_table[]
Definition: sql.c:45
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:399
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_buff(_sbuff_or_marker)
Talloc sbuff extension structure.
Definition: sbuff.h:114
@ MODULE_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: module.h:49
#define MODULE_NAME_TERMINATOR
Definition: module.h:135
#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:932
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:796
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:934
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:1044
#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:175
RETURN_MODULE_FAIL
if(!subtype_vp) goto 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:179
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:253
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition: talloc.c:333
#define talloc_get_type_abort_const
Definition: talloc.h:270
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition: time.h:588
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_PRE_CONCAT
Pre-concatenation escaping is useful for DSLs where elements of the expansion are static,...
Definition: tmpl_escape.h:61
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
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
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
void * uctx
Argument to pass to escape callback.
Definition: xlat.h:157
bool required
Argument must be present, and non-empty.
Definition: xlat.h:146
xlat_escape_func_t func
Function to handle tainted values.
Definition: xlat.h:154
static fr_slen_t head
Definition: xlat.h:408
bool concat
Concat boxes together.
Definition: xlat.h:147
fr_value_box_safe_for_t safe_for
Escaped value to set for boxes processed by this escape function.
Definition: xlat.h:155
#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
#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
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:3521
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition: value.c:3630
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:3876
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:3985
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:5725
@ FR_VALUE_BOX_LIST_FREE
Definition: value.h:214
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition: value.h:608
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition: value.h:1035
#define fr_box_strvalue_buffer(_val)
Definition: value.h:282
static fr_slen_t data
Definition: value.h:1259
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition: value.h:1042
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:619
static size_t char ** out
Definition: value.h:984
void * rctx
Resume context.
Definition: xlat_ctx.h:47
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition: xlat_ctx.h:45
void * env_data
Expanded call env data.
Definition: xlat_ctx.h:46
An xlat calling ctx.
Definition: xlat_ctx.h:42
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:405
int xlat_func_mono_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the argument of an xlat.
Definition: xlat_func.c:392
xlat_t * xlat_func_register_module(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function for a module.
Definition: xlat_func.c:274