The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
call_env.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at 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: dd133e4e4fc0ee397918678950e22a89519121d6 $
19  *
20  * @file unlang/call_env.c
21  * @brief Call environment parsing functions
22  *
23  * @copyright 2023 Network RADIUS SAS (legal@networkradius.com)
24  */
25 
26 RCSID("$Id: dd133e4e4fc0ee397918678950e22a89519121d6 $")
27 
28 #include <freeradius-devel/server/log.h>
29 #include <freeradius-devel/server/cf_util.h>
30 #include <freeradius-devel/server/tmpl.h>
31 #include <freeradius-devel/server/request.h>
32 #include <freeradius-devel/unlang/tmpl.h>
33 #include <freeradius-devel/unlang/function.h>
34 #include <freeradius-devel/unlang/interpret.h>
35 #include <freeradius-devel/unlang/call_env.h>
36 #include <freeradius-devel/util/token.h>
37 
38 #include <talloc.h>
39 #include "call_env.h"
40 
42  call_env_parsed_entry_t entry; //!< Entry in list of parsed call_env_parsers.
43 
44  union {
45  tmpl_t const *tmpl; //!< Tmpl produced from parsing conf pair.
46  fr_value_box_t const *vb; //!< Value box produced from parsing conf pair.
47  void const *ptr; //!< Data produced from parsing conf pair.
48  } data;
49 
50  size_t count; //!< Number of CONF_PAIRs found, matching the #call_env_parser_t.
51  size_t multi_index; //!< Array index for this instance.
52  call_env_parser_t const *rule; //!< Used to produce this.
53 };
54 FR_DLIST_FUNCS(call_env_parsed, call_env_parsed_t, entry)
55 
56 /** Parse the result of call_env tmpl expansion
57  */
58 static inline CC_HINT(always_inline)
59 call_env_result_t call_env_result(TALLOC_CTX *ctx, request_t *request, void *out,
60  void **tmpl_out, call_env_parsed_t const *env,
61  fr_value_box_list_t *tmpl_expanded)
62 {
63  fr_value_box_t *vb;
64 
65  if (tmpl_out) *tmpl_out = UNCONST(tmpl_t *, env->data.tmpl);
66  if (call_env_parse_only(env->rule->flags)) return CALL_ENV_SUCCESS;
67 
68  vb = fr_value_box_list_head(tmpl_expanded);
69  if (!vb) {
70  if (!call_env_nullable(env->rule->flags)) {
71  RPEDEBUG("Failed to evaluate required module option %s = %s", env->rule->name, env->data.tmpl->name);
72  return CALL_ENV_MISSING;
73  }
74  return CALL_ENV_SUCCESS;
75  }
76 
77  /*
78  * Concatenate multiple boxes if needed
79  */
80  if (call_env_concat(env->rule->flags) &&
81  fr_value_box_list_concat_in_place(vb, vb, tmpl_expanded, env->rule->pair.cast_type,
82  FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0 ) {
83  RPEDEBUG("Failed concatenating values for %s", env->rule->name);
84  return CALL_ENV_INVALID;
85  }
86 
87  if (call_env_single(env->rule->flags) && (fr_value_box_list_num_elements(tmpl_expanded) > 1)) {
88  RPEDEBUG("%d values found for %s. Only one is allowed",
89  fr_value_box_list_num_elements(tmpl_expanded), env->rule->name);
90  return CALL_ENV_INVALID;
91  }
92 
93  while ((vb = fr_value_box_list_pop_head(tmpl_expanded))) {
94  switch (env->rule->pair.type) {
97  break;
98 
100  if (!fr_value_box_list_initialised((fr_value_box_list_t *)out)) fr_value_box_list_init((fr_value_box_list_t *)out);
101  fr_value_box_list_insert_tail((fr_value_box_list_t *)out, vb);
102  break;
103 
104  default:
105  fr_assert(0);
106  break;
107  }
108  }
109 
110  return CALL_ENV_SUCCESS;
111 }
112 
113 /** Context to keep track of expansion of call environments
114  *
115  */
116 typedef struct {
117  call_env_result_t *result; //!< Where to write the return code of callenv expansion.
118  call_env_t const *call_env; //!< Call env being expanded.
119  call_env_parsed_t const *last_expanded; //!< The last expanded tmpl.
120  fr_value_box_list_t tmpl_expanded; //!< List to write value boxes to as tmpls are expanded.
121  void **data; //!< Final destination structure for value boxes.
123 
124 static unlang_action_t call_env_expand_repeat(rlm_rcode_t *p_result, int *priority, request_t *request, void *uctx);
125 
126 /** Start the expansion of a call environment tmpl.
127  *
128  */
129 static unlang_action_t call_env_expand_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
130 {
131  call_env_rctx_t *call_env_rctx = talloc_get_type_abort(uctx, call_env_rctx_t);
132  TALLOC_CTX *ctx;
133  call_env_parsed_t const *env = NULL;
134  void **out;
135 
136  while ((call_env_rctx->last_expanded = call_env_parsed_next(&call_env_rctx->call_env->parsed, call_env_rctx->last_expanded))) {
137  env = call_env_rctx->last_expanded;
138  fr_assert(env != NULL);
139 
140  /*
141  * Subsections are expanded during parsing to produce a list of
142  * call_env_parsed_t. They are not expanded at runtime.
143  */
144  fr_assert_msg(call_env_is_subsection(env->rule->flags) == false, "Subsections cannot be expanded at runtime");
145 
146  /*
147  * If there's an offset to copy the output to, do that.
148  * We may also need to expand the tmpl_t and write out the result
149  * to the pair offset.
150  */
151  if (env->rule->pair.parsed.offset >= 0) {
152  /*
153  * If we only need the tmpl or data, just set the pointer and move the next.
154  */
155  out = (void **)((uint8_t *)*call_env_rctx->data + env->rule->pair.parsed.offset);
156 
157  /*
158  * For multi pair options, the pointers need to go into a new array.
159  * When processing the first expansion, allocate the array, and for
160  * all expansions adjust the `out` pointer to write to.
161  */
162  if (call_env_multi(env->rule->flags)) {
163  void **array;
164  if (env->multi_index == 0) {
165  /*
166  * Coverity thinks talloc_zero_array being called with the type `void *`
167  * is a size mismatch. This works round the false positive.
168  */
169  MEM(array = _talloc_zero_array((*call_env_rctx->data), sizeof(uint8_t *),
170  env->count, "void *"));
171  *out = array;
172  }
173  array = (void **)(*out);
174  out = (void **)((uint8_t *)array + sizeof(void *) * env->multi_index);
175  }
176 
177  switch (env->rule->pair.parsed.type) {
179  *out = UNCONST(tmpl_t *, env->data.tmpl);
180  break;
181 
183  *out = UNCONST(fr_value_box_t *, env->data.vb);
184  continue; /* Can't evaluate these */
185 
187  *out = UNCONST(void *, env->data.ptr);
188  continue; /* Can't evaluate these */
189  }
190  }
191 
192  /*
193  * If this is not parse_only, we need to expand the tmpl.
194  */
195  if ((env->rule->pair.parsed.type == CALL_ENV_PARSE_TYPE_TMPL) && !call_env_parse_only(env->rule->flags)) break;
196  }
197 
198  if (!call_env_rctx->last_expanded) { /* No more! */
199  if (call_env_rctx->result) *call_env_rctx->result = CALL_ENV_SUCCESS;
201  }
202 
203  ctx = *call_env_rctx->data;
204 
205  fr_assert(env != NULL);
206 
207  /*
208  * Multi pair options should allocate boxes in the context of the array
209  */
210  if (call_env_multi(env->rule->flags)) {
211  out = (void **)((uint8_t *)(*call_env_rctx->data) + env->rule->pair.offset);
212 
213  /*
214  * For multi pair options, allocate the array before expanding the first entry.
215  */
216  if (env->multi_index == 0) {
217  void *array;
218  MEM(array = _talloc_zero_array((*call_env_rctx->data), env->rule->pair.size,
219  env->count, env->rule->pair.type_name));
220  *out = array;
221  }
222  ctx = *out;
223  }
224 
225  if (unlang_tmpl_push(ctx, &call_env_rctx->tmpl_expanded, request, call_env_rctx->last_expanded->data.tmpl,
226  NULL) < 0) return UNLANG_ACTION_FAIL;
227 
229 }
230 
231 /** Extract expanded call environment tmpl and store in env_data
232  *
233  * If there are more tmpls to expand, push the next expansion.
234  */
236  request_t *request, void *uctx)
237 {
238  void *out = NULL, *tmpl_out = NULL;
239  call_env_rctx_t *call_env_rctx = talloc_get_type_abort(uctx, call_env_rctx_t);
240  call_env_parsed_t const *env;
241  call_env_result_t result;
242 
243  env = call_env_rctx->last_expanded;
244  if (!env) return UNLANG_ACTION_CALCULATE_RESULT;
245 
246  if (call_env_parse_only(env->rule->flags)) goto parse_only;
247  /*
248  * Find the location of the output
249  */
250  out = ((uint8_t*)(*call_env_rctx->data)) + env->rule->pair.offset;
251 
252  /*
253  * If this is a multi pair option, the output is an array.
254  * Find the correct offset in the array
255  */
256  if (call_env_multi(env->rule->flags)) {
257  void *array = *(void **)out;
258  out = ((uint8_t *)array) + env->rule->pair.size * env->multi_index;
259  }
260 
261 parse_only:
262  if (env->rule->pair.parsed.offset >= 0) tmpl_out = ((uint8_t *)*call_env_rctx->data) + env->rule->pair.parsed.offset;
263 
264  result = call_env_result(*call_env_rctx->data, request, out, tmpl_out, env, &call_env_rctx->tmpl_expanded);
265  if (result != CALL_ENV_SUCCESS) {
266  if (call_env_rctx->result) *call_env_rctx->result = result;
267  return UNLANG_ACTION_FAIL;
268  }
269 
270  if (!call_env_parsed_next(&call_env_rctx->call_env->parsed, env)) {
271  if (call_env_rctx->result) *call_env_rctx->result = CALL_ENV_SUCCESS;
273  }
274 
276  call_env_rctx);
277 }
278 
279 /** Initialise the expansion of a call environment
280  *
281  * @param[in] ctx in which to allocate destination structure for resulting value boxes.
282  * @param[in] request Current request.
283  * @param[out] env_result Where to write the result of the callenv expansion. May be NULL
284  * @param[in,out] env_data Where the destination structure should be created.
285  * @param[in] call_env Call environment being expanded.
286  */
287 unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, call_env_result_t *env_result, void **env_data,
288  call_env_t const *call_env)
289 {
290  call_env_rctx_t *call_env_rctx;
291 
292  MEM(call_env_rctx = talloc_zero(ctx, call_env_rctx_t));
293  MEM(*env_data = talloc_zero_array(ctx, uint8_t, call_env->method->inst_size));
294  talloc_set_name_const(*env_data, call_env->method->inst_type);
295  call_env_rctx->result = env_result;
296  if (env_result) *env_result = CALL_ENV_INVALID; /* Make sure we ran to completion*/
297  call_env_rctx->data = env_data;
298  call_env_rctx->call_env = call_env;
299  fr_value_box_list_init(&call_env_rctx->tmpl_expanded);
300 
302  call_env_rctx);
303 }
304 
305 /** Allocates a new call env parsed struct
306  *
307  */
308 static inline CC_HINT(always_inline)
310 {
311  call_env_parsed_t *call_env_parsed;
312 
313  MEM(call_env_parsed = talloc_zero(ctx, call_env_parsed_t));
314  call_env_parsed->rule = rule;
315  call_env_parsed->count = 1;
316  call_env_parsed->multi_index = 0;
317 
318  return call_env_parsed;
319 }
320 
321 static inline CC_HINT(always_inline)
322 int call_env_parsed_valid(call_env_parsed_t const *parsed, CONF_ITEM const *ci, call_env_parser_t const *rule)
323 {
324  tmpl_t const *tmpl;
325 
326  if (rule->pair.parsed.type != CALL_ENV_PARSE_TYPE_TMPL) return 0;
327 
328  tmpl = parsed->data.tmpl;
329  switch (tmpl->type) {
330  case TMPL_TYPE_DATA:
331  case TMPL_TYPE_EXEC:
332  case TMPL_TYPE_XLAT:
333  if (call_env_attribute(rule->flags)) {
334  cf_log_perr(ci, "'%s' expands to %s - attribute reference required", tmpl->name,
335  tmpl_type_to_str(tmpl->type));
336  return -1;
337  }
338  FALL_THROUGH;
339 
340  case TMPL_TYPE_ATTR:
341  break;
342 
343  default:
344  cf_log_err(ci, "'%s' expands to invalid tmpl type %s", tmpl->name,
345  tmpl_type_to_str(tmpl->type));
346  return -1;
347  }
348 
349  return 0;
350 }
351 
352 /** Standard function we use for parsing call env pairs
353  *
354  * @note This is called where no custom pair parsing function is provided, but may be called by custom functions to avoid
355  * duplicating the standard parsing code.
356  *
357  * @param[in] ctx to allocate any data in.
358  * @param[out] out Where to write the result of parsing.
359  * @param[in] t_rules we're parsing attributes with. Contains the default dictionary and nested 'caller' tmpl_rules_t.
360  * @param[in] ci The #CONF_SECTION or #CONF_PAIR to parse.
361  * @param[in] data module / xlat instance data of the module / xlat allocating this call_env
362  * @param[in] rule Parse rules - How the #CONF_PAIR or #CONF_SECTION should be converted.
363  * @return
364  * - 0 on success.
365  * - -1 on failure.
366  */
367 int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
368  UNUSED char const *section_name1, UNUSED char const *section_name2,
369  UNUSED void const *data, UNUSED call_env_parser_t const *rule)
370 {
371  CONF_PAIR const *to_parse = cf_item_to_pair(ci);
372  tmpl_t *parsed_tmpl;
373 
374  if (tmpl_afrom_substr(ctx, &parsed_tmpl,
375  &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_strlen(cf_pair_value(to_parse))),
376  cf_pair_value_quote(to_parse), NULL, t_rules) < 0) {
377  return -1;
378  }
379  *(void **)out = parsed_tmpl;
380 
381  /*
382  * All attributes and functions should be resolved at this point
383  */
384  return tmpl_resolve(parsed_tmpl, NULL);
385 }
386 
387 /** Parse per call env
388  *
389  * Used for config options which must be parsed in the context in which
390  * the module is being called.
391  *
392  * @param[in] ctx To allocate parsed environment in.
393  * @param[out] parsed Where to write parsed environment.
394  * @param[in] name Module name for error messages.
395  * @param[in] t_rules controlling how the call env is parsed.
396  * @param[in] cs Module config.
397  * @param[in] section_name1 Name 1 from calling section for module calls
398  * @param[in] section_name2 Name 2 from calling section for module calls
399  * @param[in] data module / xlat instance data of the module / xlat allocating this call_env
400  * @param[in] rule to parse.
401  * @return
402  * - 0 on success;
403  * - <0 on failure;
404  */
405 static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char const *name, tmpl_rules_t const *t_rules,
406  CONF_SECTION const *cs, char const *section_name1, char const *section_name2,
407  void const *data, call_env_parser_t const *rule) {
408  CONF_PAIR const *cp, *next;
409  call_env_parsed_t *call_env_parsed = NULL;
410  ssize_t count, multi_index;
411 
412  while (rule->name) {
413  if (call_env_is_subsection(rule->flags)) {
414  CONF_SECTION const *subcs;
415  subcs = cf_section_find(cs, rule->name, rule->section.ident2);
416  if (!subcs && !call_env_parse_missing(rule->flags)) {
417  if (!call_env_required(rule->flags)) goto next;
418  cf_log_err(cs, "Module %s missing required section \"%s\"", name, rule->name);
419  return -1;
420  }
421 
422  /*
423  * Hand off to custom parsing function if there is one...
424  */
425  if (rule->section.func) {
426  /*
427  * Record our position so we can process any new entries
428  * after the callback returns.
429  */
430  call_env_parsed_t *last = call_env_parsed_tail(parsed);
431 
432  if (rule->section.func(ctx, parsed, t_rules, cf_section_to_item(subcs), section_name1,
433  section_name2, data, rule) < 0) {
434  cf_log_perr(cs, "Failed parsing configuration section %s", rule->name);
435  talloc_free(call_env_parsed);
436  return -1;
437  }
438 
439  /*
440  * We _could_ fix up count and multi_index on behalf of
441  * the callback, but there's no guarantee that all call_env_parsed_t
442  * are related to each other, so we don't.
443  */
444  call_env_parsed = last;
445  while ((call_env_parsed = call_env_parsed_next(parsed, call_env_parsed))) {
446  if (call_env_parsed_valid(call_env_parsed, cf_section_to_item(subcs), rule) < 0) {
447  cf_log_err(cf_section_to_item(subcs), "Invalid data produced by %s", rule->name);
448  return -1;
449  }
450  }
451  goto next;
452  }
453 
454  if (call_env_parse(ctx, parsed, name, t_rules, subcs, section_name1, section_name2,
455  data, rule->section.subcs) < 0) return -1;
456  goto next;
457  }
458 
459  cp = cf_pair_find(cs, rule->name);
460 
461  if (!cp && !rule->pair.dflt) {
462  if (!call_env_required(rule->flags)) goto next;
463 
464  cf_log_err(cs, "Missing required config item '%s'", rule->name);
465  return -1;
466  }
467 
468  /*
469  * Check for additional conf pairs and error
470  * if there is one and multi is not allowed.
471  */
472  if (!call_env_multi(rule->flags) && ((next = cf_pair_find_next(cs, cp, rule->name)))) {
473  cf_log_err(cf_pair_to_item(next), "Invalid duplicate configuration item '%s'", rule->name);
474  return -1;
475  }
476 
477  count = cf_pair_count(cs, rule->name);
478  if (count == 0) count = 1;
479 
480  for (multi_index = 0; multi_index < count; multi_index++) {
481  CONF_PAIR *tmp_cp = NULL;
482  CONF_PAIR const *to_parse;
483  tmpl_rules_t our_rules = {};
484  fr_type_t type = rule->pair.cast_type;
485  call_env_parse_pair_t func = rule->pair.func ? rule->pair.func : call_env_parse_pair;
486 
487  if (t_rules) {
488  our_rules.parent = t_rules->parent;
489  our_rules.attr.dict_def = t_rules->attr.dict_def;
490  our_rules.escape = rule->pair.escape; /* Escape rules will now get embedded in the tmpl_t and used at evaluation */
491  }
492 
493  our_rules.attr.list_def = request_attr_request;
494  our_rules.cast = ((type == FR_TYPE_VOID) ? FR_TYPE_NULL : type);
495  our_rules.literals_safe_for = rule->pair.literals_safe_for;
496 
497  call_env_parsed = call_env_parsed_alloc(ctx, rule);
498  call_env_parsed->count = count;
499  call_env_parsed->multi_index = multi_index;
500 
501  /*
502  * With the conf_parser code we can add default pairs
503  * if they don't exist, but as the same CONF_SECTIONs
504  * are evaluated multiple times for each module call
505  * we can't do that here.
506  */
507  if (cp) {
508  if (call_env_force_quote(rule->flags)) {
509  to_parse = tmp_cp = cf_pair_alloc(NULL,
511  cf_pair_attr_quote(cp),
512  call_env_force_quote(rule->flags) ? rule->pair.dflt_quote : cf_pair_value_quote(cp));
513  } else {
514  to_parse = cp;
515  }
516  } else {
517  to_parse = tmp_cp = cf_pair_alloc(NULL,
518  rule->name, rule->pair.dflt, T_OP_EQ,
519  T_BARE_WORD, rule->pair.dflt_quote);
520  }
521 
522  /*
523  * The parsing function can either produce a tmpl_t as tmpl_afrom_substr
524  * would, or produce a custom structure, which will be copied into the
525  * result structure.
526  */
527  if (unlikely(func(ctx, &call_env_parsed->data, &our_rules, cf_pair_to_item(to_parse), section_name1, section_name2, data, rule) < 0)) {
528  error:
529  cf_log_perr(to_parse, "Failed to parse configuration item '%s = %s'", rule->name, cf_pair_value(to_parse));
530  talloc_free(call_env_parsed);
531  talloc_free(tmp_cp);
532  return -1;
533  }
534  if (!call_env_parsed->data.ptr) {
535  talloc_free(call_env_parsed);
536  goto next_pair;
537  }
538 
539  /*
540  * Ensure only valid data is produced.
541  */
542  if (call_env_parsed_valid(call_env_parsed, cf_pair_to_item(to_parse), rule) < 0) goto error;
543 
544  call_env_parsed_insert_tail(parsed, call_env_parsed);
545  next_pair:
546  talloc_free(tmp_cp);
547  cp = cf_pair_find_next(cs, cp, rule->name);
548  }
549  next:
550  rule++;
551  }
552 
553  return 0;
554 }
555 
556 /** Perform a quick assessment of how many parsed call env will be produced.
557  *
558  * @param[in,out] names_len Where to write the sum of bytes required to represent
559  * the strings which will be parsed as tmpls. This is required
560  * to pre-allocate space for the tmpl name buffers.
561  * @param[in] cs Conf section to search for pairs.
562  * @param[in] call_env to parse.
563  * @return Number of parsed_call_env expected to be required.
564  */
565 static size_t call_env_count(size_t *names_len, CONF_SECTION const *cs, call_env_parser_t const *call_env)
566 {
567  size_t pair_count, tmpl_count = 0;
568  CONF_PAIR const *cp;
569 
570  *names_len = 0;
571 
572  while (call_env->name) {
573  if (call_env_is_subsection(call_env->flags)) {
574  CONF_SECTION const *subcs;
575  subcs = cf_section_find(cs, call_env->name, call_env->section.ident2);
576  if (!subcs) goto next;
577 
578  /*
579  * May only be a callback...
580  */
581  if (call_env->section.subcs) tmpl_count += call_env_count(names_len, subcs, call_env->section.subcs);
582  goto next;
583  }
584  pair_count = 0;
585  cp = NULL;
586  while ((cp = cf_pair_find_next(cs, cp, call_env->name))) {
587  pair_count++;
588  *names_len += talloc_array_length(cf_pair_value(cp));
589  }
590  if (!pair_count && call_env->pair.dflt) {
591  pair_count = 1;
592  *names_len += strlen(call_env->pair.dflt);
593  }
594  tmpl_count += pair_count;
595  next:
596  call_env++;
597  }
598 
599  return tmpl_count;
600 }
601 
602 /** Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs
603  *
604  * @note tmpl_t and void * should be allocated in the context of the call_env_parsed_t
605  *
606  * @param[in] ctx to allocate the new call_env_parsed_t in.
607  * @param[out] head to add the new call_env_parsed_t to.
608  * @param[in] rule to base call_env_parsed_t around. MUST NOT BE THE RULE PASSED TO THE CALLBACK.
609  * The rule passed to the callback describes how to parse a subsection, but the
610  * subsection callback is adding rules describing how to parse its children.
611  * @return The new call_env_parsed_t.
612  */
613 call_env_parsed_t *call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
614 {
615  call_env_parsed_t *call_env_parsed;
616  call_env_parser_t *our_rules;
617 
618  fr_assert_msg(call_env_is_subsection(rule->flags) == false, "Rules added by subsection callbacks cannot be subsections themselves");
619 
620  MEM(call_env_parsed = call_env_parsed_alloc(ctx, rule));
621 
622  /*
623  * Copy the rule the callback provided, there's no guarantee
624  * it's not stack allocated, or in some way ephemeral.
625  */
626  MEM(our_rules = talloc(call_env_parsed, call_env_parser_t));
627  memcpy(our_rules, rule, sizeof(*our_rules));
628  call_env_parsed->rule = our_rules;
629  call_env_parsed_insert_tail(head, call_env_parsed);
630 
631  return call_env_parsed;
632 }
633 
634 /** Assign a tmpl to a call_env_parsed_t
635  *
636  * @note Intended to be used by subsection callbacks to add a tmpl to be
637  * evaluated during the call.
638  *
639  * @param[in] parsed to assign the tmpl to.
640  * @param[in] tmpl to assign.
641  */
643 {
644  fr_assert_msg(parsed->rule->pair.parsed.type == CALL_ENV_PARSE_TYPE_TMPL, "Rule must indicate parsed output is a tmpl_t");
645  parsed->data.tmpl = tmpl;
646 }
647 
648 /** Assign a value box to a call_env_parsed_t
649  *
650  * @note Intended to be used by subsection callbacks to set a static boxed
651  * value to be written out to the result structure.
652  *
653  * @param[in] parsed to assign the tmpl to.
654  * @param[in] vb to assign.
655  */
657 {
658  fr_assert_msg(parsed->rule->pair.parsed.type == CALL_ENV_PARSE_TYPE_VALUE_BOX, "Rule must indicate parsed output is a value box");
659  parsed->data.vb = vb;
660 }
661 
662 /** Assign data to a call_env_parsed_t
663  *
664  * @note Intended to be used by subsection callbacks to set arbitrary data
665  * to be written out to the result structure.
666  *
667  * @param[in] parsed to assign the tmpl to.
668  * @param[in] data to assign.
669  */
671 {
672  fr_assert_msg(parsed->rule->pair.parsed.type == CALL_ENV_PARSE_TYPE_VOID, "Rule must indicate parsed output is a void *");
673  parsed->data.ptr = data;
674 }
675 
676 /** Assign a count and index to a call_env_parsed_t
677  *
678  * @note Intended to be used by subsection callbacks to indicate related
679  * call_env_parsed_t.
680  *
681  * @param[in] parsed to modify metadata of.
682  * @param[in] count to assign.
683  * @param[in] index to assign.
684  */
685 void call_env_parsed_set_multi_index(call_env_parsed_t *parsed, size_t count, size_t index)
686 {
687  fr_assert_msg(call_env_multi(parsed->rule->flags), "Rule must indicate parsed output is a multi pair");
688  parsed->multi_index = index;
689  parsed->count = count;
690 }
691 
692 /** Remove a call_env_parsed_t from the list of parsed call envs
693  *
694  * @note Intended to be used by subsection callbacks to remove a call_env_parsed_t
695  * from the list of parsed call envs (typically on error).
696  *
697  * @param[in] parsed to remove parsed data from.
698  * @param[in] ptr to remove.
699  */
700 void call_env_parsed_free(call_env_parsed_head_t *parsed, call_env_parsed_t *ptr)
701 {
702  call_env_parsed_remove(parsed, ptr);
703  talloc_free(ptr);
704 }
705 
706 /** Given a call_env_method, parse all call_env_pair_t in the context of a specific call to an xlat or module method
707  *
708  * @param[in] ctx to allocate the call_env_t in.
709  * @param[in] name Module name for error messages.
710  * @param[in] call_env_method containing the call_env_pair_t to evaluate against the specified CONF_SECTION.
711  * @param[in] t_rules that control how call_env_pair_t are parsed.
712  * @param[in] cs to parse in the context of the call.
713  * @param[in] data module / xlat instance data of the module / xlat allocating this call_env
714  * @return
715  * - A new call_env_t on success.
716  * - NULL on failure.
717  */
718 call_env_t *call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t const *call_env_method,
719  tmpl_rules_t const *t_rules, CONF_SECTION *cs, char const *section_name1,
720  char const *section_name2, void const *data)
721 {
722  unsigned int count;
723  size_t names_len;
724  call_env_t *call_env;
725 
726  /*
727  * Only used if caller doesn't use a more specific assert
728  */
729  fr_assert_msg(call_env_method->inst_size, "inst_size 0 for %s, method_env (%p)", name, call_env_method);
730 
731  /*
732  * Firstly assess how many parsed env there will be and create a talloc pool to hold them.
733  * The pool size is a rough estimate based on each tmpl also allocating at least two children,
734  * for which we allow twice the length of the value to be parsed.
735  */
736  count = call_env_count(&names_len, cs, call_env_method->env);
737 
738  /*
739  * Pre-allocated headers:
740  * 1 header for the call_env_pair_parsed_t, 1 header for the tmpl_t, 1 header for the name,
741  * one header for the value.
742  *
743  * Pre-allocated memory:
744  * ((sizeof(call_env_pair_parsed_t) + sizeof(tmpl_t)) * count) + (names of tmpls * 2)... Not sure what
745  * the * 2 is for, maybe for slop?
746  */
747  MEM(call_env = talloc_pooled_object(ctx, call_env_t, count * 4, (sizeof(call_env_parser_t) + sizeof(tmpl_t)) * count + names_len * 2));
748  call_env->method = call_env_method;
749  call_env_parsed_init(&call_env->parsed);
750  if (call_env_parse(call_env, &call_env->parsed, name, t_rules, cs, section_name1, section_name2,
751  data, call_env_method->env) < 0) {
752  talloc_free(call_env);
753  return NULL;
754  }
755 
756  return call_env;
757 }
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition: action.h:39
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition: action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition: action.h:37
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#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
static unlang_action_t call_env_expand_repeat(rlm_rcode_t *p_result, int *priority, request_t *request, void *uctx)
call_env_parser_t const * rule
Used to produce this.
Definition: call_env.c:52
size_t count
Number of CONF_PAIRs found, matching the call_env_parser_t.
Definition: call_env.c:50
static unlang_action_t call_env_expand_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Start the expansion of a call environment tmpl.
Definition: call_env.c:129
call_env_parsed_t const * last_expanded
The last expanded tmpl.
Definition: call_env.c:119
static call_env_result_t call_env_result(TALLOC_CTX *ctx, request_t *request, void *out, void **tmpl_out, call_env_parsed_t const *env, fr_value_box_list_t *tmpl_expanded)
Parse the result of call_env tmpl expansion.
Definition: call_env.c:59
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:700
call_env_t * call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t const *call_env_method, tmpl_rules_t const *t_rules, CONF_SECTION *cs, char const *section_name1, char const *section_name2, void const *data)
Given a call_env_method, parse all call_env_pair_t in the context of a specific call to an xlat or mo...
Definition: call_env.c:718
static call_env_parsed_t * call_env_parsed_alloc(TALLOC_CTX *ctx, call_env_parser_t const *rule)
Allocates a new call env parsed struct.
Definition: call_env.c:309
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:613
static int call_env_parsed_valid(call_env_parsed_t const *parsed, CONF_ITEM const *ci, call_env_parser_t const *rule)
Definition: call_env.c:322
unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, call_env_result_t *env_result, void **env_data, call_env_t const *call_env)
Initialise the expansion of a call environment.
Definition: call_env.c:287
static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char const *name, tmpl_rules_t const *t_rules, CONF_SECTION const *cs, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule)
Parse per call env.
Definition: call_env.c:405
call_env_t const * call_env
Call env being expanded.
Definition: call_env.c:118
static size_t call_env_count(size_t *names_len, CONF_SECTION const *cs, call_env_parser_t const *call_env)
Perform a quick assessment of how many parsed call env will be produced.
Definition: call_env.c:565
size_t multi_index
Array index for this instance.
Definition: call_env.c:51
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:685
call_env_parsed_entry_t entry
Entry in list of parsed call_env_parsers.
Definition: call_env.c:42
call_env_result_t * result
Where to write the return code of callenv expansion.
Definition: call_env.c:117
void ** data
Final destination structure for value boxes.
Definition: call_env.c:121
fr_value_box_list_t tmpl_expanded
List to write value boxes to as tmpls are expanded.
Definition: call_env.c:120
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:670
int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2, UNUSED void const *data, UNUSED call_env_parser_t const *rule)
Standard function we use for parsing call env pairs.
Definition: call_env.c:367
void call_env_parsed_set_value(call_env_parsed_t *parsed, fr_value_box_t const *vb)
Assign a value box to a call_env_parsed_t.
Definition: call_env.c:656
union call_env_parsed_s::@94 data
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:642
Context to keep track of expansion of call environments.
Definition: call_env.c:116
Structures and functions for handling call environments.
#define call_env_parse_missing(_flags)
Definition: call_env.h:127
call_env_flags_t flags
Flags controlling parser behaviour.
Definition: call_env.h:173
#define call_env_is_subsection(_flags)
Definition: call_env.h:125
#define call_env_required(_flags)
Definition: call_env.h:109
char const * name
Of conf pair to pass to tmpl_tokenizer.
Definition: call_env.h:172
#define call_env_nullable(_flags)
Definition: call_env.h:117
int(* call_env_parse_pair_t)(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule)
Callback for performing custom parsing of a CONF_PAIR.
Definition: call_env.h:144
#define call_env_multi(_flags)
Definition: call_env.h:115
#define call_env_single(_flags)
Definition: call_env.h:113
#define call_env_concat(_flags)
Definition: call_env.h:111
@ CALL_ENV_RESULT_TYPE_VALUE_BOX_LIST
Output of the evaluation phase is a list of value boxes.
Definition: call_env.h:67
@ CALL_ENV_RESULT_TYPE_VALUE_BOX
Output of the evaluation phase is a single value box.
Definition: call_env.h:66
size_t inst_size
Size of per call env.
Definition: call_env.h:221
#define call_env_attribute(_flags)
Definition: call_env.h:123
call_env_result_t
Definition: call_env.h:49
@ CALL_ENV_INVALID
Definition: call_env.h:52
@ CALL_ENV_MISSING
Definition: call_env.h:51
@ CALL_ENV_SUCCESS
Definition: call_env.h:50
call_env_parser_t const * env
Parsing rules for call method env.
Definition: call_env.h:223
char const * inst_type
Type of per call env.
Definition: call_env.h:222
#define call_env_parse_only(_flags)
Definition: call_env.h:121
#define call_env_force_quote(_flags)
Definition: call_env.h:119
call_env_parsed_head_t parsed
The per call parsed call environment.
Definition: call_env.h:229
@ CALL_ENV_PARSE_TYPE_VALUE_BOX
Output of the parsing phase is a single value box (static data).
Definition: call_env.h:59
@ CALL_ENV_PARSE_TYPE_TMPL
Output of the parsing phase is a tmpl_t.
Definition: call_env.h:58
@ CALL_ENV_PARSE_TYPE_VOID
Output of the parsing phase is undefined (a custom structure).
Definition: call_env.h:60
call_env_method_t const * method
The method this call env is for.
Definition: call_env.h:230
Per method call config.
Definition: call_env.h:171
Structure containing both a talloc pool, a list of parsed call_env_pairs.
Definition: call_env.h:228
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_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: cf_util.c:629
CONF_ITEM * cf_section_to_item(CONF_SECTION const *cs)
Cast a CONF_SECTION to a CONF_ITEM.
Definition: cf_util.c:703
fr_token_t cf_pair_attr_quote(CONF_PAIR const *pair)
Return the value (lhs) quoting of a pair.
Definition: cf_util.c:1540
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition: cf_util.c:1495
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_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1511
fr_token_t cf_pair_operator(CONF_PAIR const *pair)
Return the operator of a pair.
Definition: cf_util.c:1525
CONF_ITEM * cf_pair_to_item(CONF_PAIR const *cp)
Cast a CONF_PAIR to a CONF_ITEM.
Definition: cf_util.c:687
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
CONF_PAIR * cf_pair_alloc(CONF_SECTION *parent, char const *attr, char const *value, fr_token_t op, fr_token_t lhs_quote, fr_token_t rhs_quote)
Allocate a CONF_PAIR.
Definition: cf_util.c:1220
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
#define cf_log_perr(_cf, _fmt,...)
Definition: cf_util.h:272
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition: debug.h:208
#define FR_DLIST_FUNCS(_name, _element_type, _element_entry)
Define type specific wrapper functions for dlists.
Definition: dlist.h:1152
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition: function.h:111
#define UNLANG_SUB_FRAME
Definition: interpret.h:36
#define RPEDEBUG(fmt,...)
Definition: log.h:376
talloc_free(reap)
fr_type_t
Definition: merged_model.c:80
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
Definition: merged_model.c:81
@ FR_TYPE_VOID
User data.
Definition: merged_model.c:127
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
static size_t array[MY_ARRAY_SIZE]
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
fr_dict_attr_t const * request_attr_request
Definition: request.c:41
static char const * name
#define FR_SBUFF_IN(_start, _len_or_end)
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.
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
tmpl_rules_t const * parent
for parent / child relationships
Definition: tmpl.h:342
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition: tmpl.h:146
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition: tmpl.h:150
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition: tmpl.h:154
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition: tmpl.h:142
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.
fr_type_t cast
Whether there was an explicit cast.
Definition: tmpl.h:349
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition: tmpl.h:344
static char const * tmpl_type_to_str(tmpl_type_t type)
Return a static string containing the type name.
Definition: tmpl.h:629
Optional arguments passed to vp_tmpl functions.
Definition: tmpl.h:341
return count
Definition: module.c:175
if(!subtype_vp) goto fail
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_aka_sim_id_type_t type
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition: tmpl.h:307
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition: tmpl.h:285
Functions which we wish were included in the standard talloc distribution.
#define talloc_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
Definition: talloc.h:168
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition: talloc.h:277
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
@ T_BARE_WORD
Definition: token.h:120
@ T_OP_EQ
Definition: token.h:83
static fr_slen_t head
Definition: xlat.h:408
void fr_value_box_copy_shallow(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *src)
Perform a shallow copy of a value_box.
Definition: value.c:3783
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
static fr_slen_t data
Definition: value.h:1259
static size_t char ** out
Definition: value.h:984