All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
map.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: 33d6ad0bddff18c91ba33aeed0d47395a46bf03b $
19  *
20  * @brief map / template functions
21  * @file main/map.c
22  *
23  * @ingroup AVP
24  *
25  * @copyright 2013 The FreeRADIUS server project
26  * @copyright 2013 Alan DeKok <aland@freeradius.org>
27  */
28 
29 RCSID("$Id: 33d6ad0bddff18c91ba33aeed0d47395a46bf03b $")
30 
31 #include <freeradius-devel/radiusd.h>
32 #include <freeradius-devel/rad_assert.h>
33 
34 #include <ctype.h>
35 
36 #ifdef DEBUG_MAP
37 static void map_dump(REQUEST *request, vp_map_t const *map)
38 {
39  RDEBUG(">>> MAP TYPES LHS: %s, RHS: %s",
40  fr_int2str(tmpl_names, map->lhs->type, "???"),
41  fr_int2str(tmpl_names, map->rhs->type, "???"));
42 
43  if (map->rhs) {
44  RDEBUG(">>> MAP NAMES %s %s", map->lhs->name, map->rhs->name);
45  }
46 }
47 #endif
48 
49 
50 /** re-parse a map where the lhs is an unknown attribute.
51  *
52  *
53  * @param map to process.
54  * @param rhs_type quotation type around rhs.
55  * @param rhs string to re-parse.
56  */
57 bool map_cast_from_hex(vp_map_t *map, FR_TOKEN rhs_type, char const *rhs)
58 {
59  size_t len;
60  ssize_t rlen;
61  uint8_t *ptr;
62  char const *p;
63  pair_lists_t list;
64 
65  fr_dict_attr_t const *da;
66  VALUE_PAIR *vp = NULL;
67  vp_cursor_t cursor;
68  vp_tmpl_t *vpt;
69 
70  rad_assert(map != NULL);
71 
72  rad_assert(map->lhs != NULL);
74 
75  rad_assert(map->rhs == NULL);
76  rad_assert(rhs != NULL);
77 
78  VERIFY_MAP(map);
79 
80  /*
81  * If the attribute is still unknown, go parse the RHS.
82  */
83  da = fr_dict_attr_by_num(NULL, map->lhs->tmpl_da->vendor, map->lhs->tmpl_da->attr);
84  if (!da || da->flags.is_unknown) return false;
85 
86  /*
87  * If the RHS is something OTHER than an octet
88  * string, go parse it as that.
89  */
90  if (rhs_type != T_BARE_WORD) return false;
91  if ((rhs[0] != '0') || (tolower((int)rhs[1]) != 'x')) return false;
92  if (!rhs[2]) return false;
93 
94  len = strlen(rhs + 2);
95 
96  ptr = talloc_array(map, uint8_t, len >> 1);
97  if (!ptr) return false;
98 
99  len = fr_hex2bin(ptr, len >> 1, rhs + 2, len);
100 
101  /*
102  * If we can't parse it, or if it's malformed,
103  * it's still unknown.
104  */
105  fr_cursor_init(&cursor, &vp);
106  rlen = fr_radius_decode_pair_value(map, &cursor, da, ptr, len, len, NULL);
107  talloc_free(ptr);
108 
109  if (rlen < 0) return false;
110 
111  if ((size_t) rlen < len) {
112  free_vp:
113  fr_pair_list_free(&vp);
114  return false;
115  }
116 
117  /*
118  * Was still parsed as an unknown attribute.
119  */
120  if (vp->da->flags.is_unknown) goto free_vp;
121 
122  /*
123  * Set the RHS to the PARSED name, not the crap octet
124  * string which was input.
125  */
126  map->rhs = tmpl_alloc(map, TMPL_TYPE_DATA, NULL, 0, T_INVALID);
127  if (!map->rhs) goto free_vp;
128 
129  map->rhs->tmpl_data_type = da->type;
130  map->rhs->tmpl_data_length = vp->vp_length;
131  if (vp->da->flags.is_pointer) {
132  if (vp->da->type == PW_TYPE_STRING) {
133  map->rhs->tmpl_data_value.ptr = talloc_bstrndup(map->rhs, vp->data.ptr, vp->vp_length);
135  } else {
136  map->rhs->tmpl_data_value.ptr = talloc_memdup(map->rhs, vp->data.ptr, vp->vp_length);
137  map->rhs->quote = T_BARE_WORD;
138  }
139  } else {
140  memcpy(&map->rhs->tmpl_data_value, &vp->data, sizeof(map->rhs->tmpl_data_value));
141  map->rhs->quote = T_BARE_WORD;
142  }
143  map->rhs->name = fr_pair_value_asprint(map->rhs, vp, fr_token_quote[map->rhs->quote]);
144  map->rhs->len = talloc_array_length(map->rhs->name) - 1;
145 
146  /*
147  * Set the LHS to the REAL attribute name.
148  */
149  vpt = tmpl_alloc(map, TMPL_TYPE_ATTR, map->lhs->tmpl_da->name, -1, T_BARE_WORD);
150  memcpy(&vpt->data.attribute, &map->lhs->data.attribute, sizeof(vpt->data.attribute));
151  vpt->tmpl_da = da;
152 
153  /*
154  * Be sure to keep the "&control:" or "control:" prefix.
155  * If it's there, we re-generate it from whatever was in
156  * the original name, including the '&'.
157  */
158  p = map->lhs->name;
159  if (*p == '&') p++;
160  len = radius_list_name(&list, p, PAIR_LIST_UNKNOWN);
161 
162  if (list != PAIR_LIST_UNKNOWN) {
163  rad_const_free(vpt->name);
164 
165  vpt->name = talloc_asprintf(vpt, "%.*s:%s",
166  (int) len, map->lhs->name,
167  map->lhs->tmpl_da->name);
168  vpt->len = strlen(vpt->name);
169  }
170 
171  talloc_free(map->lhs);
172  map->lhs = vpt;
173 
174  fr_pair_list_free(&vp);
175 
176  VERIFY_MAP(map);
177 
178  return true;
179 }
180 
181 /** Convert CONFIG_PAIR (which may contain refs) to vp_map_t.
182  *
183  * Treats the left operand as an attribute reference
184  * @verbatim<request>.<list>.<attribute>@endverbatim
185  *
186  * Treatment of left operand depends on quotation, barewords are treated as
187  * attribute references, double quoted values are treated as expandable strings,
188  * single quoted values are treated as literal strings.
189  *
190  * Return must be freed with talloc_free
191  *
192  * @param[in] ctx for talloc.
193  * @param[in] out Where to write the pointer to the new #vp_map_t.
194  * @param[in] cp to convert to map.
195  * @param[in] dst_request_def The default request to insert unqualified
196  * attributes into.
197  * @param[in] dst_list_def The default list to insert unqualified attributes
198  * into.
199  * @param[in] src_request_def The default request to resolve attribute
200  * references in.
201  * @param[in] src_list_def The default list to resolve unqualified attributes
202  * in.
203  * @return
204  * - #vp_map_t if successful.
205  * - NULL on error.
206  */
207 int map_afrom_cp(TALLOC_CTX *ctx, vp_map_t **out, CONF_PAIR *cp,
208  request_refs_t dst_request_def, pair_lists_t dst_list_def,
209  request_refs_t src_request_def, pair_lists_t src_list_def)
210 {
211  vp_map_t *map;
212  char const *attr, *value;
213  ssize_t slen;
214  FR_TOKEN type;
215 
216  *out = NULL;
217 
218  if (!cp) return -1;
219 
220  map = talloc_zero(ctx, vp_map_t);
221  map->op = cf_pair_operator(cp);
222  map->ci = cf_pair_to_item(cp);
223 
224  attr = cf_pair_attr(cp);
225  value = cf_pair_value(cp);
226  if (!value) {
227  cf_log_err_cp(cp, "Missing attribute value");
228  goto error;
229  }
230 
231  /*
232  * LHS may be an expansion (that expands to an attribute reference)
233  * or an attribute reference. Quoting determines which it is.
234  */
235  type = cf_pair_attr_type(cp);
236  switch (type) {
239  slen = tmpl_afrom_str(ctx, &map->lhs, attr, talloc_array_length(attr) - 1,
240  type, dst_request_def, dst_list_def, true);
241  if (slen <= 0) {
242  char *spaces, *text;
243 
244  marker:
245  fr_canonicalize_error(ctx, &spaces, &text, slen, attr);
246  cf_log_err_cp(cp, "%s", text);
247  cf_log_err_cp(cp, "%s^ %s", spaces, fr_strerror());
248 
249  talloc_free(spaces);
250  talloc_free(text);
251  goto error;
252  }
253  break;
254 
255  default:
256  slen = tmpl_afrom_attr_str(ctx, &map->lhs, attr, dst_request_def, dst_list_def, true, true);
257  if (slen <= 0) {
258  cf_log_err_cp(cp, "Failed parsing attribute reference");
259 
260  goto marker;
261  }
262 
263  if (tmpl_define_unknown_attr(map->lhs) < 0) {
264  cf_log_err_cp(cp, "Failed creating attribute %s: %s",
265  map->lhs->name, fr_strerror());
266  goto error;
267  }
268 
269  break;
270  }
271 
272  /*
273  * RHS might be an attribute reference.
274  */
275  type = cf_pair_value_type(cp);
276 
277  if ((type == T_BARE_WORD) && (value[0] == '0') && (tolower((int)value[1]) == 'x') &&
278  map_cast_from_hex(map, type, value)) {
279  /* do nothing */
280  } else {
281  slen = tmpl_afrom_str(map, &map->rhs, value, strlen(value), type, src_request_def, src_list_def, true);
282  if (slen < 0) goto marker;
283  if (tmpl_define_unknown_attr(map->rhs) < 0) {
284  cf_log_err_cp(cp, "Failed creating attribute %s: %s", map->rhs->name, fr_strerror());
285  goto error;
286  }
287  }
288  if (!map->rhs) {
289  cf_log_err_cp(cp, "%s", fr_strerror());
290  goto error;
291  }
292 
293  /*
294  * We cannot assign a count to an attribute. That must
295  * be done in an xlat.
296  */
297  if ((map->rhs->type == TMPL_TYPE_ATTR) &&
298  (map->rhs->tmpl_num == NUM_COUNT)) {
299  cf_log_err_cp(cp, "Cannot assign from a count");
300  goto error;
301  }
302 
303  VERIFY_MAP(map);
304 
305  *out = map;
306 
307  return 0;
308 
309 error:
310  talloc_free(map);
311  return -1;
312 }
313 
314 /** Convert an 'update' config section into an attribute map.
315  *
316  * Uses 'name2' of section to set default request and lists.
317  *
318  * @param[in] cs the update section
319  * @param[out] out Where to store the head of the map.
320  * @param[in] dst_list_def The default destination list, usually dictated by
321  * the section the module is being called in.
322  * @param[in] src_list_def The default source list, usually dictated by the
323  * section the module is being called in.
324  * @param[in] validate map using this callback (may be NULL).
325  * @param[in] ctx to pass to callback.
326  * @param[in] max number of mappings to process.
327  * @return
328  * - 0 on success.
329  * - -1 on failure.
330  */
332  pair_lists_t dst_list_def, pair_lists_t src_list_def,
333  map_validate_t validate, void *ctx,
334  unsigned int max)
335 {
336  char const *cs_list, *p;
337 
338  request_refs_t request_def = REQUEST_CURRENT;
339 
340  CONF_ITEM *ci;
341  CONF_PAIR *cp;
342 
343  unsigned int total = 0;
344  vp_map_t **tail, *map;
345  TALLOC_CTX *parent;
346 
347  *out = NULL;
348  tail = out;
349 
350  /*
351  * The first map has cs as the parent.
352  * The rest have the previous map as the parent.
353  */
354  parent = cs;
355 
356  ci = cf_section_to_item(cs);
357 
358  /*
359  * Check the destination list for "update" sections.
360  */
361  cs_list = p = cf_section_name2(cs);
362  if (cs_list && (strcmp(cf_section_name1(cs), "update") == 0)) {
363  p += radius_request_name(&request_def, p, REQUEST_CURRENT);
364  if (request_def == REQUEST_UNKNOWN) {
365  cf_log_err(ci, "Default request specified in mapping section is invalid");
366  return -1;
367  }
368 
369  dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN);
370  if (dst_list_def == PAIR_LIST_UNKNOWN) {
371  cf_log_err(ci, "Default list \"%s\" specified "
372  "in mapping section is invalid", p);
373  return -1;
374  }
375  }
376 
377  for (ci = cf_item_find_next(cs, NULL);
378  ci != NULL;
379  ci = cf_item_find_next(cs, ci)) {
380  if (total++ == max) {
381  cf_log_err(ci, "Map size exceeded");
382  error:
383  TALLOC_FREE(*out);
384  return -1;
385  }
386 
387  if (!cf_item_is_pair(ci)) {
388  cf_log_err(ci, "Entry is not in \"attribute = value\" format");
389  goto error;
390  }
391 
392  cp = cf_item_to_pair(ci);
393  if (map_afrom_cp(parent, &map, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def) < 0) {
394  goto error;
395  }
396 
397  VERIFY_MAP(map);
398 
399  /*
400  * Check the types in the map are valid
401  */
402  if (validate && (validate(map, ctx) < 0)) goto error;
403 
404  parent = *tail = map;
405  tail = &(map->next);
406  }
407 
408  return 0;
409 
410 }
411 
412 /** Convert strings to #vp_map_t
413  *
414  * Treatment of operands depends on quotation, barewords are treated
415  * as attribute references, double quoted values are treated as
416  * expandable strings, single quoted values are treated as literal
417  * strings.
418  *
419  * Return must be freed with talloc_free
420  *
421  * @param[in] ctx for talloc
422  * @param[out] out Where to store the head of the map.
423  * @param[in] lhs of the operation
424  * @param[in] lhs_type type of the LHS string
425  * @param[in] op the operation to perform
426  * @param[in] rhs of the operation
427  * @param[in] rhs_type type of the RHS string
428  * @param[in] dst_request_def The default request to insert unqualified attributes into.
429  * @param[in] dst_list_def The default list to insert unqualified attributes into.
430  * @param[in] src_request_def The default request to resolve attribute references in.
431  * @param[in] src_list_def The default list to resolve unqualified attributes in.
432  * @return #vp_map_t if successful or NULL on error.
433  */
434 int map_afrom_fields(TALLOC_CTX *ctx, vp_map_t **out, char const *lhs, FR_TOKEN lhs_type,
435  FR_TOKEN op, char const *rhs, FR_TOKEN rhs_type,
436  request_refs_t dst_request_def,
437  pair_lists_t dst_list_def,
438  request_refs_t src_request_def,
439  pair_lists_t src_list_def)
440 {
441  ssize_t slen;
442  vp_map_t *map;
443 
444  map = talloc_zero(ctx, vp_map_t);
445 
446  slen = tmpl_afrom_str(map, &map->lhs, lhs, strlen(lhs), lhs_type, dst_request_def, dst_list_def, true);
447  if (slen < 0) {
448  error:
449  talloc_free(map);
450  return -1;
451  }
452 
453  map->op = op;
454 
455  if ((map->lhs->type == TMPL_TYPE_ATTR) &&
456  map->lhs->tmpl_da->flags.is_unknown &&
457  map_cast_from_hex(map, rhs_type, rhs)) {
458  return 0;
459  }
460 
461  slen = tmpl_afrom_str(map, &map->rhs, rhs, strlen(rhs), rhs_type, src_request_def, src_list_def, true);
462  if (slen < 0) goto error;
463 
464  VERIFY_MAP(map);
465 
466  *out = map;
467 
468  return 0;
469 }
470 
471 /** Convert a value pair string to valuepair map
472  *
473  * Takes a valuepair string with list and request qualifiers and converts it into a
474  * #vp_map_t.
475  *
476  * @param ctx where to allocate the map.
477  * @param out Where to write the new map (must be freed with talloc_free()).
478  * @param vp_str string to parse.
479  * @param dst_request_def to use if attribute isn't qualified.
480  * @param dst_list_def to use if attribute isn't qualified.
481  * @param src_request_def to use if attribute isn't qualified.
482  * @param src_list_def to use if attribute isn't qualified.
483  * @return
484  * - 0 on success.
485  * - < 0 on error.
486  */
487 int map_afrom_attr_str(TALLOC_CTX *ctx, vp_map_t **out, char const *vp_str,
488  request_refs_t dst_request_def, pair_lists_t dst_list_def,
489  request_refs_t src_request_def, pair_lists_t src_list_def)
490 {
491  char const *p = vp_str;
492  FR_TOKEN quote;
493 
494  VALUE_PAIR_RAW raw;
495  vp_map_t *map = NULL;
496 
497  quote = gettoken(&p, raw.l_opand, sizeof(raw.l_opand), false);
498  switch (quote) {
499  case T_BARE_WORD:
500  break;
501 
502  case T_INVALID:
503  error:
504  return -1;
505 
506  default:
507  fr_strerror_printf("Left operand must be an attribute");
508  return -1;
509  }
510 
511  raw.op = getop(&p);
512  if (raw.op == T_INVALID) goto error;
513 
514  raw.quote = gettoken(&p, raw.r_opand, sizeof(raw.r_opand), false);
515  if (raw.quote == T_INVALID) goto error;
516  if (!fr_str_tok[raw.quote]) {
517  fr_strerror_printf("Right operand must be an attribute or string");
518  return -1;
519  }
520 
521  if (map_afrom_fields(ctx, &map, raw.l_opand, T_BARE_WORD, raw.op, raw.r_opand, raw.quote,
522  dst_request_def, dst_list_def, src_request_def, src_list_def) < 0) {
523  return -1;
524  }
525 
526  rad_assert(map != NULL);
527  *out = map;
528 
529  VERIFY_MAP(map);
530 
531  return 0;
532 }
533 
534 /** Compare map where LHS is #TMPL_TYPE_ATTR
535  *
536  * Compares maps by lhs->tmpl_da, lhs->tmpl_tag, lhs->tmpl_num
537  *
538  * @note both map->lhs must be #TMPL_TYPE_ATTR.
539  *
540  * @param a first map.
541  * @param b second map.
542  */
543 int8_t map_cmp_by_lhs_attr(void const *a, void const *b)
544 {
545  vp_tmpl_t const *my_a = ((vp_map_t const *)a)->lhs;
546  vp_tmpl_t const *my_b = ((vp_map_t const *)b)->lhs;
547 
548  VERIFY_TMPL(my_a);
549  VERIFY_TMPL(my_b);
550 
551  uint8_t cmp;
552 
553  rad_assert(my_a->type == TMPL_TYPE_ATTR);
554  rad_assert(my_b->type == TMPL_TYPE_ATTR);
555 
556  cmp = fr_pointer_cmp(my_a->tmpl_da, my_b->tmpl_da);
557  if (cmp != 0) return cmp;
558 
559  if (my_a->tmpl_tag < my_b->tmpl_tag) return -1;
560 
561  if (my_a->tmpl_tag > my_b->tmpl_tag) return 1;
562 
563  if (my_a->tmpl_num < my_b->tmpl_num) return -1;
564 
565  if (my_a->tmpl_num > my_b->tmpl_num) return 1;
566 
567  return 0;
568 }
569 
570 static void map_sort_split(vp_map_t *source, vp_map_t **front, vp_map_t **back)
571 {
572  vp_map_t *fast;
573  vp_map_t *slow;
574 
575  /*
576  * Stopping condition - no more elements left to split
577  */
578  if (!source || !source->next) {
579  *front = source;
580  *back = NULL;
581 
582  return;
583  }
584 
585  /*
586  * Fast advances twice as fast as slow, so when it gets to the end,
587  * slow will point to the middle of the linked list.
588  */
589  slow = source;
590  fast = source->next;
591 
592  while (fast) {
593  fast = fast->next;
594  if (fast) {
595  slow = slow->next;
596  fast = fast->next;
597  }
598  }
599 
600  *front = source;
601  *back = slow->next;
602  slow->next = NULL;
603 }
604 
606 {
607  vp_map_t *result = NULL;
608 
609  if (!a) return b;
610  if (!b) return a;
611 
612  /*
613  * Compare things in the maps
614  */
615  if (cmp(a, b) <= 0) {
616  result = a;
617  result->next = map_sort_merge(a->next, b, cmp);
618  } else {
619  result = b;
620  result->next = map_sort_merge(a, b->next, cmp);
621  }
622 
623  return result;
624 }
625 
626 /** Sort a linked list of #vp_map_t using merge sort
627  *
628  * @param[in,out] maps List of #vp_map_t to sort.
629  * @param[in] cmp to sort with
630  */
632 {
633  vp_map_t *head = *maps;
634  vp_map_t *a;
635  vp_map_t *b;
636 
637  /*
638  * If there's 0-1 elements it must already be sorted.
639  */
640  if (!head || !head->next) {
641  return;
642  }
643 
644  map_sort_split(head, &a, &b); /* Split into sublists */
645  map_sort(&a, cmp); /* Traverse left */
646  map_sort(&b, cmp); /* Traverse right */
647 
648  /*
649  * merge the two sorted lists together
650  */
651  *maps = map_sort_merge(a, b, cmp);
652 }
653 
654 /** Process map which has exec as a src
655  *
656  * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections, and so
657  * has been broken out into it's own function.
658  *
659  * @param[in,out] ctx to allocate new #VALUE_PAIR (s) in.
660  * @param[out] out Where to write the #VALUE_PAIR (s).
661  * @param[in] request structure (used only for talloc).
662  * @param[in] map the map. The LHS (dst) must be #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
663  * The RHS (src) must be #TMPL_TYPE_EXEC.
664  * @return
665  * - 0 on success.
666  * - -1 on failure.
667  */
668 static int map_exec_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map)
669 {
670  int result;
671  char *expanded = NULL;
672  char answer[1024];
673  VALUE_PAIR **input_pairs = NULL;
674  VALUE_PAIR *output_pairs = NULL;
675 
676  *out = NULL;
677 
678  VERIFY_MAP(map);
679 
680  rad_assert(map->rhs->type == TMPL_TYPE_EXEC);
681  rad_assert((map->lhs->type == TMPL_TYPE_ATTR) || (map->lhs->type == TMPL_TYPE_LIST));
682 
683  /*
684  * We always put the request pairs into the environment
685  */
686  input_pairs = radius_list(request, PAIR_LIST_REQUEST);
687 
688  /*
689  * Automagically switch output type depending on our destination
690  * If dst is a list, then we create attributes from the output of the program
691  * if dst is an attribute, then we create an attribute of that type and then
692  * call fr_pair_value_from_str on the output of the script.
693  */
694  result = radius_exec_program(ctx, answer, sizeof(answer),
695  (map->lhs->type == TMPL_TYPE_LIST) ? &output_pairs : NULL,
696  request, map->rhs->name, input_pairs ? *input_pairs : NULL,
697  true, true, EXEC_TIMEOUT);
698  talloc_free(expanded);
699  if (result != 0) {
700  talloc_free(output_pairs);
701  return -1;
702  }
703 
704  switch (map->lhs->type) {
705  case TMPL_TYPE_LIST:
706  if (!output_pairs) {
707  REDEBUG("No valid attributes received from program");
708  return -2;
709  }
710  *out = output_pairs;
711  return 0;
712 
713  case TMPL_TYPE_ATTR:
714  {
715  VALUE_PAIR *vp;
716 
717  vp = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
718  if (!vp) return -1;
719  vp->op = map->op;
720  vp->tag = map->lhs->tmpl_tag;
721  if (fr_pair_value_from_str(vp, answer, -1) < 0) {
722  fr_pair_list_free(&vp);
723  return -2;
724  }
725  *out = vp;
726 
727  return 0;
728  }
729 
730  default:
731  rad_assert(0);
732  }
733 
734  return -1;
735 }
736 
737 /** Convert a map to a #VALUE_PAIR
738  *
739  * @param[in,out] ctx to allocate #VALUE_PAIR (s) in.
740  * @param[out] out Where to write the #VALUE_PAIR (s), which may be NULL if not found
741  * @param[in] request The current request.
742  * @param[in] map the map. The LHS (dst) has to be #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
743  * @param[in] uctx unused.
744  * @return
745  * - 0 on success.
746  * - -1 on failure.
747  */
748 int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, UNUSED void *uctx)
749 {
750  int rcode = 0;
751  VALUE_PAIR *vp = NULL, *new, *found = NULL;
752  REQUEST *context = request;
753  vp_cursor_t cursor;
754  ssize_t slen;
755  char *str;
756 
757  *out = NULL;
758 
759  VERIFY_MAP(map);
760  rad_assert(map->lhs != NULL);
761  rad_assert(map->rhs != NULL);
762 
763  rad_assert((map->lhs->type == TMPL_TYPE_LIST) || (map->lhs->type == TMPL_TYPE_ATTR));
764 
765  /*
766  * Special case for !*, we don't need to parse RHS as this is a unary operator.
767  */
768  if (map->op == T_OP_CMP_FALSE) return 0;
769 
770  /*
771  * List to list found, this is a special case because we don't need
772  * to allocate any attributes, just finding the current list, and change
773  * the op.
774  */
775  if ((map->lhs->type == TMPL_TYPE_LIST) && (map->rhs->type == TMPL_TYPE_LIST)) {
776  VALUE_PAIR **from = NULL;
777 
778  if (radius_request(&context, map->rhs->tmpl_request) == 0) {
779  from = radius_list(context, map->rhs->tmpl_list);
780  }
781  if (!from) return 0;
782 
783  found = fr_pair_list_copy(ctx, *from);
784 
785  /*
786  * List to list copy is empty if the src list has no attributes.
787  */
788  if (!found) return 0;
789 
790  for (vp = fr_cursor_init(&cursor, &found);
791  vp;
792  vp = fr_cursor_next(&cursor)) {
793  vp->op = T_OP_ADD;
794  }
795 
796  *out = found;
797 
798  return 0;
799  }
800 
801  /*
802  * And parse the RHS
803  */
804  switch (map->rhs->type) {
806  rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
807  rad_assert(map->lhs->tmpl_da); /* We need to know which attribute to create */
808  rad_assert(map->rhs->tmpl_xlat != NULL);
809 
810  new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
811  if (!new) return -1;
812 
813  str = NULL;
814  slen = radius_axlat_struct(&str, request, map->rhs->tmpl_xlat, NULL, NULL);
815  if (slen < 0) {
816  rcode = slen;
817  goto error;
818  }
819 
820  /*
821  * We do the debug printing because radius_axlat_struct
822  * doesn't have access to the original string. It's been
823  * mangled during the parsing to xlat_exp_t
824  */
825  RDEBUG2("EXPAND %s", map->rhs->name);
826  RDEBUG2(" --> %s", str);
827 
828  rcode = fr_pair_value_from_str(new, str, -1);
829  talloc_free(str);
830  if (rcode < 0) {
831  fr_pair_list_free(&new);
832  goto error;
833  }
834  new->op = map->op;
835  new->tag = map->lhs->tmpl_tag;
836  *out = new;
837  break;
838 
839  case TMPL_TYPE_XLAT:
840  rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
841  rad_assert(map->lhs->tmpl_da); /* We need to know which attribute to create */
842 
843  new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
844  if (!new) return -1;
845 
846  str = NULL;
847  slen = radius_axlat(&str, request, map->rhs->name, NULL, NULL);
848  if (slen < 0) {
849  rcode = slen;
850  goto error;
851  }
852 
853  rcode = fr_pair_value_from_str(new, str, -1);
854  talloc_free(str);
855  if (rcode < 0) {
856  fr_pair_list_free(&new);
857  goto error;
858  }
859  new->op = map->op;
860  new->tag = map->lhs->tmpl_tag;
861  *out = new;
862  break;
863 
864  case TMPL_TYPE_UNPARSED:
865  rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
866  rad_assert(map->lhs->tmpl_da); /* We need to know which attribute to create */
867 
868  new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
869  if (!new) return -1;
870 
871  if (fr_pair_value_from_str(new, map->rhs->name, -1) < 0) {
872  rcode = 0;
873  goto error;
874  }
875  new->op = map->op;
876  new->tag = map->lhs->tmpl_tag;
877  *out = new;
878  break;
879 
880  case TMPL_TYPE_ATTR:
881  {
882  vp_cursor_t from;
883 
884  rad_assert(((map->lhs->type == TMPL_TYPE_ATTR) && map->lhs->tmpl_da) ||
885  ((map->lhs->type == TMPL_TYPE_LIST) && !map->lhs->tmpl_da));
886 
887  /*
888  * @todo should log error, and return -1 for v3.1 (causes update to fail)
889  */
890  if (tmpl_copy_vps(ctx, &found, request, map->rhs) < 0) return 0;
891 
892  vp = fr_cursor_init(&from, &found);
893 
894  /*
895  * Src/Dst attributes don't match, convert src attributes
896  * to match dst.
897  */
898  if ((map->lhs->type == TMPL_TYPE_ATTR) &&
899  (map->rhs->tmpl_da->type != map->lhs->tmpl_da->type)) {
900  vp_cursor_t to;
901 
902  (void) fr_cursor_init(&to, out);
903  for (; vp; vp = fr_cursor_next(&from)) {
904  new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
905  if (!new) return -1;
906 
907  if (value_data_cast(new, &new->data, new->da->type, new->da,
908  vp->da->type, vp->da, &vp->data) < 0) {
909  REDEBUG("Attribute conversion failed: %s", fr_strerror());
910  fr_pair_list_free(&found);
911  fr_pair_list_free(&new);
912  return -1;
913  }
914  vp = fr_cursor_remove(&from);
915  talloc_free(vp);
916 
917  if (new->da->type == PW_TYPE_STRING) {
918  rad_assert(new->vp_strvalue != NULL);
919  }
920 
921  new->op = map->op;
922  new->tag = map->lhs->tmpl_tag;
923  fr_cursor_insert(&to, new);
924  }
925  return 0;
926  }
927 
928  /*
929  * Otherwise we just need to fixup the attribute types
930  * and operators
931  */
932  for (; vp; vp = fr_cursor_next(&from)) {
933  vp->da = map->lhs->tmpl_da;
934  vp->op = map->op;
935  vp->tag = map->lhs->tmpl_tag;
936  }
937  *out = found;
938  }
939  break;
940 
941  case TMPL_TYPE_DATA:
942  rad_assert(map->lhs->tmpl_da);
943  rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
944 
945  new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
946  if (!new) return -1;
947 
948  if (map->lhs->tmpl_da->type == map->rhs->tmpl_data_type) {
949  if (value_data_copy(new, &new->data, new->da->type, &map->rhs->tmpl_data_value) < 0) {
950  rcode = -1;
951  goto error;
952  }
953  } else {
954  if (value_data_cast(new, &new->data, new->da->type, new->da, map->rhs->tmpl_data_type,
955  NULL, &map->rhs->tmpl_data_value) < 0) {
956  REDEBUG("Implicit cast failed: %s", fr_strerror());
957  rcode = -1;
958  goto error;
959  }
960  }
961  new->op = map->op;
962  new->tag = map->lhs->tmpl_tag;
963  *out = new;
964 
965  VERIFY_MAP(map);
966  break;
967 
968  /*
969  * This essentially does the same as rlm_exec xlat, except it's non-configurable.
970  * It's only really here as a convenience for people who expect the contents of
971  * backticks to be executed in a shell.
972  *
973  * exec string is xlat expanded and arguments are shell escaped.
974  */
975  case TMPL_TYPE_EXEC:
976  return map_exec_to_vp(ctx, out, request, map);
977 
978  default:
979  rad_assert(0); /* Should have been caught at parse time */
980 
981  error:
982  fr_pair_list_free(&vp);
983  return rcode;
984  }
985 
986  return 0;
987 }
988 
989 #define DEBUG_OVERWRITE(_old, _new) \
990 do {\
991  if (RDEBUG_ENABLED3) {\
992  char *old = fr_pair_value_asprint(request, _old, '"');\
993  char *new = fr_pair_value_asprint(request, _new, '"');\
994  RINDENT();\
995  RDEBUG3("--> overwriting '%s' with '%s'", old, new);\
996  REXDENT();\
997  talloc_free(old);\
998  talloc_free(new);\
999  }\
1000 } while (0)
1001 
1002 /** Convert #vp_map_t to #VALUE_PAIR (s) and add them to a #REQUEST.
1003  *
1004  * Takes a single #vp_map_t, resolves request and list identifiers
1005  * to pointers in the current request, then attempts to retrieve module
1006  * specific value(s) using callback, and adds the resulting values to the
1007  * correct request/list.
1008  *
1009  * @param request The current request.
1010  * @param map specifying destination attribute and location and src identifier.
1011  * @param func to retrieve module specific values and convert them to
1012  * #VALUE_PAIR.
1013  * @param ctx to be passed to func.
1014  * @return
1015  * - -1 if the operation failed.
1016  * - -2 in the source attribute wasn't valid.
1017  * - 0 on success.
1018  */
1019 int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t func, void *ctx)
1020 {
1021  int rcode = 0;
1022  int num;
1023  VALUE_PAIR **list, *vp, *dst, *head = NULL;
1024  bool found = false;
1025  REQUEST *context;
1026  TALLOC_CTX *parent;
1027  vp_cursor_t dst_list, src_list;
1028 
1029  vp_map_t exp_map;
1030  vp_tmpl_t exp_lhs;
1031 
1032  VERIFY_MAP(map);
1033  rad_assert(map->lhs != NULL);
1034  rad_assert(map->rhs != NULL);
1035 
1036  /*
1037  * Preprocessing of the LHS of the map.
1038  */
1039  switch (map->lhs->type) {
1040  /*
1041  * Already in the correct form.
1042  */
1043  case TMPL_TYPE_LIST:
1044  case TMPL_TYPE_ATTR:
1045  break;
1046 
1047  /*
1048  * Everything else gets expanded, then re-parsed as an
1049  * attribute reference.
1050  */
1051  case TMPL_TYPE_XLAT:
1052  case TMPL_TYPE_XLAT_STRUCT:
1053  case TMPL_TYPE_EXEC:
1054  {
1055  char *attr;
1056  ssize_t slen;
1057 
1058  slen = tmpl_aexpand(request, &attr, request, map->lhs, NULL, NULL);
1059  if (slen <= 0) {
1060  REDEBUG("Left side \"%.*s\" of map failed expansion", (int)map->lhs->len, map->lhs->name);
1061  rad_assert(!attr);
1062  return -1;
1063  }
1064 
1065  slen = tmpl_from_attr_str(&exp_lhs, attr, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) ;
1066  if (slen <= 0) {
1067  REDEBUG("Left side \"%.*s\" expansion not an attribute reference: %s",
1068  (int)map->lhs->len, map->lhs->name, fr_strerror());
1069  talloc_free(attr);
1070  return -1;
1071  }
1072  rad_assert((exp_lhs.type == TMPL_TYPE_ATTR) || (exp_lhs.type == TMPL_TYPE_LIST));
1073 
1074  memcpy(&exp_map, map, sizeof(exp_map));
1075  exp_map.lhs = &exp_lhs;
1076  map = &exp_map;
1077  }
1078  break;
1079 
1080  default:
1081  rad_assert(0);
1082  break;
1083  }
1084 
1085 
1086  /*
1087  * Sanity check inputs. We can have a list or attribute
1088  * as a destination.
1089  */
1090  if ((map->lhs->type != TMPL_TYPE_LIST) &&
1091  (map->lhs->type != TMPL_TYPE_ATTR)) {
1092  REDEBUG("Left side \"%.*s\" of map should be an attr or list but is an %s",
1093  (int)map->lhs->len, map->lhs->name,
1094  fr_int2str(tmpl_names, map->lhs->type, "<INVALID>"));
1095  return -2;
1096  }
1097 
1098  context = request;
1099  if (radius_request(&context, map->lhs->tmpl_request) < 0) {
1100  REDEBUG("Mapping \"%.*s\" -> \"%.*s\" invalid in this context",
1101  (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
1102  return -2;
1103  }
1104 
1105  /*
1106  * If there's no CoA packet and we're updating it,
1107  * auto-allocate it.
1108  */
1109  if (((map->lhs->tmpl_list == PAIR_LIST_COA) ||
1110  (map->lhs->tmpl_list == PAIR_LIST_DM)) && !request->coa) {
1111  if (!request_alloc_coa(context)) {
1112  REDEBUG("Failed to create a CoA/Disconnect Request message");
1113  return -2;
1114  }
1115  context->coa->proxy->code = (map->lhs->tmpl_list == PAIR_LIST_COA) ?
1118  }
1119 
1120  list = radius_list(context, map->lhs->tmpl_list);
1121  if (!list) {
1122  REDEBUG("Mapping \"%.*s\" -> \"%.*s\" invalid in this context",
1123  (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
1124 
1125  return -2;
1126  }
1127 
1128  parent = radius_list_ctx(context, map->lhs->tmpl_list);
1129  rad_assert(parent);
1130 
1131  /*
1132  * The callback should either return -1 to signify operations error,
1133  * -2 when it can't find the attribute or list being referenced, or
1134  * 0 to signify success. It may return "success", but still have no
1135  * VPs to work with.
1136  */
1137  if (map->rhs->type != TMPL_TYPE_NULL) {
1138  rcode = func(parent, &head, request, map, ctx);
1139  if (rcode < 0) {
1140  rad_assert(!head);
1141  return rcode;
1142  }
1143  if (!head) {
1144  RDEBUG2("%.*s skipped: No values available", (int)map->lhs->len, map->lhs->name);
1145  return rcode;
1146  }
1147  } else {
1148  if (rad_debug_lvl) map_debug_log(request, map, NULL);
1149  }
1150 
1151  /*
1152  * Print the VPs
1153  */
1154  for (vp = fr_cursor_init(&src_list, &head);
1155  vp;
1156  vp = fr_cursor_next(&src_list)) {
1157  VERIFY_VP(vp);
1158 
1159  if (rad_debug_lvl) map_debug_log(request, map, vp);
1160  }
1161 
1162  /*
1163  * The destination is a list (which is a completely different set of operations)
1164  */
1165  if (map->lhs->type == TMPL_TYPE_LIST) {
1166  switch (map->op) {
1167  case T_OP_CMP_FALSE:
1168  /* We don't need the src VPs (should just be 'ANY') */
1169  rad_assert(!head);
1170 
1171  /* Clear the entire dst list */
1172  fr_pair_list_free(list);
1173 
1174  if (map->lhs->tmpl_list == PAIR_LIST_REQUEST) {
1175  context->username = NULL;
1176  context->password = NULL;
1177  }
1178  return 0;
1179 
1180  case T_OP_SET:
1181  if (map->rhs->type == TMPL_TYPE_LIST) {
1182  fr_pair_list_free(list);
1183  *list = head;
1184  head = NULL;
1185  } else {
1186  case T_OP_EQ:
1187  rad_assert(map->rhs->type == TMPL_TYPE_EXEC);
1188  case T_OP_ADD:
1189  fr_pair_list_move(parent, list, &head);
1190  fr_pair_list_free(&head);
1191  }
1192  goto finish;
1193 
1194  default:
1195  fr_pair_list_free(&head);
1196  return -1;
1197  }
1198  }
1199 
1200  /*
1201  * Find the destination attribute. We leave with either
1202  * the dst_list and vp pointing to the attribute or the VP
1203  * being NULL (no attribute at that index).
1204  */
1205  num = map->lhs->tmpl_num;
1206  (void) fr_cursor_init(&dst_list, list);
1207  if (num != NUM_ANY) {
1208  while ((dst = fr_cursor_next_by_da(&dst_list, map->lhs->tmpl_da, map->lhs->tmpl_tag))) {
1209  if (num-- == 0) break;
1210  }
1211  } else {
1212  dst = fr_cursor_next_by_da(&dst_list, map->lhs->tmpl_da, map->lhs->tmpl_tag);
1213  }
1214  rad_assert(!dst || (map->lhs->tmpl_da == dst->da));
1215 
1216  /*
1217  * The destination is an attribute
1218  */
1219  switch (map->op) {
1220  default:
1221  break;
1222  /*
1223  * !* - Remove all attributes which match dst in the specified list.
1224  * This doesn't use attributes returned by the func(), and immediately frees them.
1225  */
1226  case T_OP_CMP_FALSE:
1227  /* We don't need the src VPs (should just be 'ANY') */
1228  rad_assert(!head);
1229  if (!dst) return 0;
1230 
1231  /*
1232  * Wildcard: delete all of the matching ones, based on tag.
1233  */
1234  if (map->lhs->tmpl_num == NUM_ANY) {
1235  fr_pair_delete_by_num(list, map->lhs->tmpl_da->vendor, map->lhs->tmpl_da->attr,
1236  map->lhs->tmpl_tag);
1237  dst = NULL;
1238  /*
1239  * We've found the Nth one. Delete it, and only it.
1240  */
1241  } else {
1242  dst = fr_cursor_remove(&dst_list);
1243  fr_pair_list_free(&dst);
1244  }
1245 
1246  /*
1247  * Check that the User-Name and User-Password
1248  * caches point to the correct attribute.
1249  */
1250  goto finish;
1251 
1252  /*
1253  * -= - Delete attributes in the dst list which match any of the
1254  * src_list attributes.
1255  *
1256  * This operation has two modes:
1257  * - If map->lhs->tmpl_num > 0, we check each of the src_list attributes against
1258  * the dst attribute, to see if any of their values match.
1259  * - If map->lhs->tmpl_num == NUM_ANY, we compare all instances of the dst attribute
1260  * against each of the src_list attributes.
1261  */
1262  case T_OP_SUB:
1263  /* We didn't find any attributes earlier */
1264  if (!dst) {
1265  fr_pair_list_free(&head);
1266  return 0;
1267  }
1268 
1269  /*
1270  * Instance specific[n] delete
1271  */
1272  if (map->lhs->tmpl_num != NUM_ANY) {
1273  for (vp = fr_cursor_first(&src_list);
1274  vp;
1275  vp = fr_cursor_next(&src_list)) {
1276  head->op = T_OP_CMP_EQ;
1277  rcode = radius_compare_vps(request, vp, dst);
1278  if (rcode == 0) {
1279  dst = fr_cursor_remove(&dst_list);
1280  fr_pair_list_free(&dst);
1281  found = true;
1282  }
1283  }
1284  fr_pair_list_free(&head);
1285  if (!found) return 0;
1286  goto finish;
1287  }
1288 
1289  /*
1290  * All instances[*] delete
1291  */
1292  for (dst = fr_cursor_current(&dst_list);
1293  dst;
1294  dst = fr_cursor_next_by_da(&dst_list, map->lhs->tmpl_da, map->lhs->tmpl_tag)) {
1295  for (vp = fr_cursor_first(&src_list);
1296  vp;
1297  vp = fr_cursor_next(&src_list)) {
1298  head->op = T_OP_CMP_EQ;
1299  rcode = radius_compare_vps(request, vp, dst);
1300  if (rcode == 0) {
1301  dst = fr_cursor_remove(&dst_list);
1302  fr_pair_list_free(&dst);
1303  found = true;
1304  }
1305  }
1306  }
1307  fr_pair_list_free(&head);
1308  if (!found) return 0;
1309  goto finish;
1310  }
1311 
1312  /*
1313  * Another fixup pass to set tags on attributes were about to insert
1314  */
1315  if (map->lhs->tmpl_tag != TAG_ANY) {
1316  for (vp = fr_cursor_init(&src_list, &head);
1317  vp;
1318  vp = fr_cursor_next(&src_list)) {
1319  vp->tag = map->lhs->tmpl_tag;
1320  }
1321  }
1322 
1323  switch (map->op) {
1324  /*
1325  * = - Set only if not already set
1326  */
1327  case T_OP_EQ:
1328  if (dst) {
1329  RDEBUG3("Refusing to overwrite (use :=)");
1330  fr_pair_list_free(&head);
1331  return 0;
1332  }
1333 
1334  /* Insert first instance (if multiple) */
1335  fr_cursor_first(&src_list);
1336  fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1337  /* Free any we didn't insert */
1338  fr_pair_list_free(&head);
1339  break;
1340 
1341  /*
1342  * := - Overwrite existing attribute with last src_list attribute
1343  */
1344  case T_OP_SET:
1345  /* Wind to last instance */
1346  fr_cursor_last(&src_list);
1347  if (dst) {
1348  DEBUG_OVERWRITE(dst, fr_cursor_current(&src_list));
1349  dst = fr_cursor_replace(&dst_list, fr_cursor_remove(&src_list));
1350  fr_pair_list_free(&dst);
1351  } else {
1352  fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1353  }
1354  /* Free any we didn't insert */
1355  fr_pair_list_free(&head);
1356  break;
1357 
1358  /*
1359  * += - Add all src_list attributes to the destination
1360  */
1361  case T_OP_ADD:
1362  /* Insert all the instances! (if multiple) */
1363  fr_pair_add(list, head);
1364  head = NULL;
1365  break;
1366 
1367  /*
1368  * Filtering operators
1369  */
1370  default:
1371  /*
1372  * If the dst doesn't exist, the filters will add
1373  * it with the given value.
1374  */
1375  if (!dst) {
1376  RDEBUG3("No existing attribute to filter, adding instead");
1377  fr_cursor_merge(&dst_list, head);
1378  head = NULL;
1379  goto finish;
1380  }
1381 
1382  /*
1383  * The LHS exists. We need to limit it's value based on
1384  * the operator, and the value of the RHS.
1385  */
1386  found = false;
1387  for (vp = fr_cursor_first(&src_list);
1388  vp;
1389  vp = fr_cursor_next(&src_list)) {
1390  vp->op = map->op;
1391  rcode = radius_compare_vps(request, vp, dst);
1392  vp->op = T_OP_SET;
1393 
1394  switch (map->op) {
1395  case T_OP_CMP_EQ:
1396  if (rcode == 0) continue;
1397  replace:
1398  dst = fr_cursor_remove(&dst_list);
1399  DEBUG_OVERWRITE(dst, fr_cursor_current(&src_list));
1400  fr_pair_list_free(&dst);
1401  fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1402  found = true;
1403  continue;
1404 
1405  case T_OP_LE:
1406  if (rcode <= 0) continue;
1407  goto replace;
1408 
1409  case T_OP_GE:
1410  if (rcode >= 0) continue;
1411  goto replace;
1412 
1413  default:
1414  fr_pair_list_free(&head);
1415  return -1;
1416  }
1417  }
1418  fr_pair_list_free(&head);
1419  if (!found) return 0;
1420 
1421  break;
1422  }
1423 
1424 finish:
1425  rad_assert(!head);
1426 
1427  /*
1428  * Update the cached username && password. This is code
1429  * we execute on EVERY update (sigh) so that SOME modules
1430  * MIGHT NOT have to do the search themselves.
1431  *
1432  * TBH, we should probably make each module just do the
1433  * search themselves.
1434  */
1435  if (map->lhs->tmpl_list == PAIR_LIST_REQUEST) {
1436  context->username = NULL;
1437  context->password = NULL;
1438 
1439  for (vp = fr_cursor_init(&src_list, list);
1440  vp;
1441  vp = fr_cursor_next(&src_list)) {
1442 
1443  if (vp->da->vendor != 0) continue;
1444  if (vp->da->flags.has_tag) continue;
1445 
1446  if (!context->username && (vp->da->attr == PW_USER_NAME)) {
1447  context->username = vp;
1448  continue;
1449  }
1450 
1451  if (vp->da->attr == PW_STRIPPED_USER_NAME) {
1452  context->username = vp;
1453  continue;
1454  }
1455 
1456  if (vp->da->attr == PW_USER_PASSWORD) {
1457  context->password = vp;
1458  continue;
1459  }
1460  }
1461  }
1462  return 0;
1463 }
1464 
1465 /** Check whether the destination of a map is currently valid
1466  *
1467  * @param request The current request.
1468  * @param map to check.
1469  * @return
1470  * - true if the map resolves to a request and list.
1471  * - false.
1472  */
1473 bool map_dst_valid(REQUEST *request, vp_map_t const *map)
1474 {
1475  REQUEST *context = request;
1476 
1477  VERIFY_MAP(map);
1478 
1479  if (radius_request(&context, map->lhs->tmpl_request) < 0) return false;
1480  if (!radius_list(context, map->lhs->tmpl_list)) return false;
1481 
1482  return true;
1483 }
1484 
1485 /** Print a map to a string
1486  *
1487  * @param[out] out Buffer to write string to.
1488  * @param[in] outlen Size of the output buffer.
1489  * @param[in] map to print.
1490  * @return
1491  * - The number of bytes written to the out buffer.
1492  * - A number >= outlen if truncation has occurred.
1493  */
1494 size_t map_snprint(char *out, size_t outlen, vp_map_t const *map)
1495 {
1496  size_t len;
1497  fr_dict_attr_t const *da = NULL;
1498  char *p = out;
1499  char *end = out + outlen;
1500 
1501  VERIFY_MAP(map);
1502 
1503  if (map->lhs->type == TMPL_TYPE_ATTR) da = map->lhs->tmpl_da;
1504 
1505  len = tmpl_snprint(out, (end - p) - 1, map->lhs, da); /* -1 for proceeding ' ' */
1506  RETURN_IF_TRUNCATED(p, len, (end - p) - 1);
1507 
1508  *(p++) = ' ';
1509  len = strlcpy(p, fr_token_name(map->op), (end - p) - 1); /* -1 for proceeding ' ' */
1510  RETURN_IF_TRUNCATED(p, len, (end - p) - 1);
1511  *(p++) = ' ';
1512 
1513  /*
1514  * The RHS doesn't matter for many operators
1515  */
1516  if ((map->op == T_OP_CMP_TRUE) || (map->op == T_OP_CMP_FALSE)) {
1517  len = strlcpy(p, "ANY", (end - p));
1518  RETURN_IF_TRUNCATED(p, len, (end - p) - 1);
1519  return p - out;
1520  }
1521 
1522  rad_assert(map->rhs != NULL);
1523 
1524  if ((map->lhs->type == TMPL_TYPE_ATTR) &&
1525  (map->lhs->tmpl_da->type == PW_TYPE_STRING) &&
1526  (map->rhs->type == TMPL_TYPE_UNPARSED)) {
1527  *(p++) = '\'';
1528  len = tmpl_snprint(p, (end - p) - 1, map->rhs, da); /* -1 for proceeding '\'' */
1529  RETURN_IF_TRUNCATED(p, len, (end - p) - 1);
1530  *(p++) = '\'';
1531  } else {
1532  len = tmpl_snprint(p, end - p, map->rhs, da);
1533  RETURN_IF_TRUNCATED(p, len, (end - p) - 1);
1534  }
1535 
1536  *p = '\0';
1537  return p - out;
1538 }
1539 
1540 /*
1541  * Debug print a map / VP
1542  */
1543 void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp)
1544 {
1545  char *value;
1546  char buffer[1024];
1547 
1548  VERIFY_MAP(map);
1549  rad_assert(map->lhs != NULL);
1550  rad_assert(map->rhs != NULL);
1551 
1552  rad_assert(vp || (map->rhs->type == TMPL_TYPE_NULL));
1553 
1554  switch (map->rhs->type) {
1555  /*
1556  * Just print the value being assigned
1557  */
1558  default:
1559  case TMPL_TYPE_UNPARSED:
1560  fr_pair_value_snprint(buffer, sizeof(buffer), vp, fr_token_quote[map->rhs->quote]);
1561  value = buffer;
1562  break;
1563 
1564  case TMPL_TYPE_XLAT:
1565  case TMPL_TYPE_XLAT_STRUCT:
1566  fr_pair_value_snprint(buffer, sizeof(buffer), vp, fr_token_quote[map->rhs->quote]);
1567  value = buffer;
1568  break;
1569 
1570  case TMPL_TYPE_DATA:
1571  fr_pair_value_snprint(buffer, sizeof(buffer), vp, fr_token_quote[map->rhs->quote]);
1572  value = buffer;
1573  break;
1574 
1575  /*
1576  * For the lists, we can't use the original name, and have to
1577  * rebuild it using tmpl_snprint, for each attribute we're
1578  * copying.
1579  */
1580  case TMPL_TYPE_LIST:
1581  {
1582  char attr[256];
1583  char quote = '\0';
1584  vp_tmpl_t vpt;
1585  /*
1586  * Fudge a temporary tmpl that describes the attribute we're copying
1587  * this is a combination of the original list tmpl, and values from
1588  * the VALUE_PAIR. This way, we get tag info included.
1589  */
1590  memcpy(&vpt, map->rhs, sizeof(vpt));
1591  vpt.tmpl_da = vp->da;
1592  vpt.tmpl_tag = vp->tag;
1593  vpt.type = TMPL_TYPE_ATTR;
1594 
1595  /*
1596  * Not appropriate to use map->rhs->quote here, as that's the quoting
1597  * around the list ref. The attribute value has no quoting, so we choose
1598  * the quoting based on the data type, and whether it's printable.
1599  */
1600  if (vp->da->type == PW_TYPE_STRING) quote = is_printable(vp->vp_strvalue,
1601  vp->vp_length) ? '\'' : '"';
1602  fr_pair_value_snprint(buffer, sizeof(buffer), vp, quote);
1603  tmpl_snprint(attr, sizeof(attr), &vpt, vp->da);
1604  value = talloc_typed_asprintf(request, "%s -> %s", attr, buffer);
1605  }
1606  break;
1607 
1608  case TMPL_TYPE_ATTR:
1609  {
1610  char quote = '\0';
1611 
1612  /*
1613  * Not appropriate to use map->rhs->quote here, as that's the quoting
1614  * around the attr ref. The attribute value has no quoting, so we choose
1615  * the quoting based on the data type, and whether it's printable.
1616  */
1617  if (vp->da->type == PW_TYPE_STRING) quote = is_printable(vp->vp_strvalue,
1618  vp->vp_length) ? '\'' : '"';
1619  fr_pair_value_snprint(buffer, sizeof(buffer), vp, quote);
1620  value = talloc_typed_asprintf(request, "%.*s -> %s", (int)map->rhs->len, map->rhs->name, buffer);
1621  }
1622  break;
1623 
1624  case TMPL_TYPE_NULL:
1625  strcpy(buffer, "ANY");
1626  value = buffer;
1627  break;
1628  }
1629 
1630  switch (map->lhs->type) {
1631  case TMPL_TYPE_LIST:
1632  RDEBUG("%.*s:%s %s %s", (int)map->lhs->len, map->lhs->name, vp ? vp->da->name : "",
1633  fr_int2str(fr_tokens_table, vp ? vp->op : map->op, "<INVALID>"), value);
1634  break;
1635 
1636  case TMPL_TYPE_ATTR:
1637  RDEBUG("%.*s %s %s", (int)map->lhs->len, map->lhs->name,
1638  fr_int2str(fr_tokens_table, vp ? vp->op : map->op, "<INVALID>"), value);
1639  break;
1640 
1641  default:
1642  break;
1643  }
1644 
1645  if (value != buffer) talloc_free(value);
1646 }
void fr_pair_list_free(VALUE_PAIR **)
Free memory used by a valuepair list.
Definition: pair.c:544
int8_t(* fr_cmp_t)(void const *a, void const *b)
Definition: pair.h:227
ssize_t ssize_t ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, xlat_escape_t escape, void *escape_ctx) CC_HINT(nonnull(1
int(* radius_map_getvalue_t)(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx)
Definition: map.h:68
int radius_exec_program(TALLOC_CTX *ctx, char *out, size_t outlen, VALUE_PAIR **output_pairs, REQUEST *request, char const *cmd, VALUE_PAIR *input_pairs, bool exec_wait, bool shell_escape, int timeout) CC_HINT(nonnull(5
FR_TOKEN getop(char const **ptr)
Definition: token.c:413
const char fr_token_quote[]
Convert tokens back to a quoting character.
Definition: token.c:96
VALUE_PAIR * fr_cursor_first(vp_cursor_t *cursor)
Rewind cursor to the start of the list.
Definition: cursor.c:105
FR_TOKEN cf_pair_value_type(CONF_PAIR const *pair)
Return the value (rhs) type.
Definition: conffile.c:3541
ssize_t tmpl_afrom_str(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name, size_t inlen, FR_TOKEN type, request_refs_t request_def, pair_lists_t list_def, bool do_escape)
Convert an arbitrary string into a vp_tmpl_t.
Definition: tmpl.c:1022
ssize_t ssize_t ssize_t ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, xlat_escape_t escape, void *ctx) CC_HINT(nonnull(1
static int map_exec_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map)
Process map which has exec as a src.
Definition: map.c:668
VALUE_PAIR * fr_cursor_last(vp_cursor_t *cursor)
Wind cursor to the last pair in the list.
Definition: cursor.c:126
#define NUM_ANY
Definition: pair.h:201
Dictionary attribute.
Definition: dict.h:77
int8_t fr_pointer_cmp(void const *a, void const *b)
Compares two pointers.
Definition: misc.c:958
char const * name
Raw string used to create the template.
Definition: tmpl.h:190
char const * fr_token_name(int)
Definition: token.c:520
int map_afrom_cp(TALLOC_CTX *ctx, vp_map_t **out, CONF_PAIR *cp, request_refs_t dst_request_def, pair_lists_t dst_list_def, request_refs_t src_request_def, pair_lists_t src_list_def)
Convert CONFIG_PAIR (which may contain refs) to vp_map_t.
Definition: map.c:207
static struct cmp * cmp
Definition: pair.c:45
vp_tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:47
CONF_ITEM * cf_pair_to_item(CONF_PAIR const *cp)
Cast a CONF_PAIR to a CONF_ITEM.
Definition: conffile.c:211
TALLOC_CTX * radius_list_ctx(REQUEST *request, pair_lists_t list_name)
Return the correct TALLOC_CTX to alloc VALUE_PAIR in, for a list.
Definition: tmpl.c:331
VALUE_PAIR ** radius_list(REQUEST *request, pair_lists_t list)
Resolve attribute pair_lists_t value to an attribute list.
Definition: tmpl.c:195
Dictionary attribute.
Definition: tmpl.h:133
vp_tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:48
Pre-parsed XLAT expansion.
Definition: tmpl.h:139
static vp_map_t * map_sort_merge(vp_map_t *a, vp_map_t *b, fr_cmp_t cmp)
Definition: map.c:605
int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert vp_map_t to VALUE_PAIR (s) and add them to a REQUEST.
Definition: map.c:1019
VALUE_PAIR * username
Cached username VALUE_PAIR from request RADIUS_PACKET.
Definition: radiusd.h:222
Attributes to send in a forked CoA-Request.
Definition: tmpl.h:97
#define VERIFY_VP(_x)
Definition: pair.h:44
#define UNUSED
Definition: libradius.h:134
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name, request_refs_t request_def, pair_lists_t list_def, bool allow_unknown, bool allow_undefined)
Parse a string into a TMPL_TYPE_ATTR_* or TMPL_TYPE_LIST type vp_tmpl_t.
Definition: tmpl.c:956
int8_t map_cmp_by_lhs_attr(void const *a, void const *b)
Compare map where LHS is TMPL_TYPE_ATTR.
Definition: map.c:543
Unparsed literal string.
Definition: tmpl.h:131
int8_t tag
Tag value used to group valuepairs.
Definition: pair.h:121
void size_t fr_pair_value_snprint(char *out, size_t outlen, VALUE_PAIR const *vp, char quote)
Print the value of an attribute to a string.
Definition: pair.c:2107
Unknown request.
Definition: tmpl.h:109
const FR_NAME_NUMBER fr_tokens_table[]
Definition: token.c:30
bool map_cast_from_hex(vp_map_t *map, FR_TOKEN rhs_type, char const *rhs)
re-parse a map where the lhs is an unknown attribute.
Definition: map.c:57
unsigned int is_pointer
data is a pointer
Definition: dict.h:51
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
void void void cf_log_err_cp(CONF_PAIR const *cp, char const *fmt,...) CC_HINT(format(printf
VALUE_PAIR * password
Cached password VALUE_PAIR from request RADIUS_PACKET.
Definition: radiusd.h:223
REQUEST * request_alloc_coa(REQUEST *request)
Definition: request.c:208
int map_afrom_fields(TALLOC_CTX *ctx, vp_map_t **out, char const *lhs, FR_TOKEN lhs_type, FR_TOKEN op, char const *rhs, FR_TOKEN rhs_type, request_refs_t dst_request_def, pair_lists_t dst_list_def, request_refs_t src_request_def, pair_lists_t src_list_def)
Convert strings to vp_map_t.
Definition: map.c:434
Definition: token.h:46
static expr_map_t map[]
Definition: rlm_expr.c:169
static void map_sort_split(vp_map_t *source, vp_map_t **front, vp_map_t **back)
Definition: map.c:570
FR_TOKEN op
Operator.
Definition: pair.h:162
size_t map_snprint(char *out, size_t outlen, vp_map_t const *map)
Print a map to a string.
Definition: map.c:1494
int map_afrom_attr_str(TALLOC_CTX *ctx, vp_map_t **out, char const *vp_str, request_refs_t dst_request_def, pair_lists_t dst_list_def, request_refs_t src_request_def, pair_lists_t src_list_def)
Convert a value pair string to valuepair map.
Definition: map.c:487
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
fr_dict_attr_flags_t flags
Flags.
Definition: dict.h:88
char const * cf_pair_value(CONF_PAIR const *pair)
Definition: conffile.c:3506
struct vp_map * next
The next valuepair map.
Definition: map.h:55
bool map_dst_valid(REQUEST *request, vp_map_t const *map)
Check whether the destination of a map is currently valid.
Definition: map.c:1473
char * fr_pair_value_asprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
Print one attribute value to a string.
Definition: pair.c:2123
RADIUS_PACKET * proxy
Outgoing request to proxy server.
Definition: radiusd.h:237
Attributes to send in a forked Disconnect-Request.
Definition: tmpl.h:100
pair_lists
Definition: tmpl.h:80
Definition: token.h:50
#define rad_assert(expr)
Definition: rad_assert.h:38
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
Definition: token.c:451
int tmpl_define_unknown_attr(vp_tmpl_t *vpt)
Add an unknown fr_dict_attr_t specified by a vp_tmpl_t to the main dictionary.
Definition: tmpl.c:1360
ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent, uint8_t const *data, size_t const attr_len, size_t const packet_len, void *decoder_ctx)
Create any kind of VP from the attribute contents.
VALUE_PAIR * fr_pair_list_copy(TALLOC_CTX *ctx, VALUE_PAIR *from)
Copy a pairlist.
Definition: pair.c:1394
char r_opand[1024]
Right hand side of the pair.
Definition: pair.h:158
unsigned int is_unknown
Attribute number or vendor is unknown.
Definition: dict.h:42
bool is_printable(void const *value, size_t len)
Check whether the string is made up of printable UTF8 chars.
Definition: misc.c:329
int value_data_cast(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE dst_type, fr_dict_attr_t const *dst_enumv, PW_TYPE src_type, fr_dict_attr_t const *src_enumv, value_data_t const *src)
Convert one type of value_data_t to another.
Definition: value.c:1073
int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, UNUSED void *uctx)
Convert a map to a VALUE_PAIR.
Definition: map.c:748
char const * cf_pair_attr(CONF_PAIR const *pair)
Definition: conffile.c:3497
Value in native format.
Definition: tmpl.h:138
void fr_cursor_merge(vp_cursor_t *cursor, VALUE_PAIR *vp)
Merges multiple VALUE_PAIR into the cursor.
Definition: cursor.c:394
const bool fr_str_tok[]
Definition: token.c:195
ssize_t tmpl_from_attr_str(vp_tmpl_t *vpt, char const *name, request_refs_t request_def, pair_lists_t list_def, bool allow_unknown, bool allow_undefined)
Parse a string into a TMPL_TYPE_ATTR_* or TMPL_TYPE_LIST type vp_tmpl_t.
Definition: tmpl.c:877
static char spaces[]
Definition: proto.c:28
void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp)
Insert a single VALUE_PAIR at the end of the list.
Definition: cursor.c:321
void fr_pair_add(VALUE_PAIR **head, VALUE_PAIR *vp)
Add a VP to the end of the list.
Definition: pair.c:659
size_t fr_hex2bin(uint8_t *bin, size_t outlen, char const *hex, size_t inlen)
Convert hex strings to binary data.
Definition: misc.c:220
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *item)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: conffile.c:181
unsigned int attr
Attribute number.
Definition: dict.h:79
REQUEST * coa
CoA request originated by this request.
Definition: radiusd.h:307
Definition: token.h:43
Attributes in incoming or internally proxied request.
Definition: tmpl.h:82
int radius_request(REQUEST **request, request_refs_t name)
Resolve a request_refs_t to a REQUEST.
Definition: tmpl.c:451
unsigned int code
Packet code (type).
Definition: libradius.h:155
A VALUE_PAIR in string format.
Definition: pair.h:156
Attribute list.
Definition: tmpl.h:135
CONF_ITEM * cf_section_to_item(CONF_SECTION const *cs)
Cast a CONF_SECTION to a CONF_ITEM.
Definition: conffile.c:224
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
Definition: token.h:44
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
void rad_const_free(void const *ptr)
Definition: util.c:424
ssize_t tmpl_aexpand(TALLOC_CTX *ctx, char **out, REQUEST *request, vp_tmpl_t const *vpt, xlat_escape_t escape, void *escape_ctx)
Expand a template to a string, allocing a new buffer to hold the string.
Definition: tmpl.c:1653
VALUE_PAIR * fr_cursor_current(vp_cursor_t *cursor)
Return the VALUE_PAIR the cursor current points to.
Definition: cursor.c:304
char * talloc_typed_asprintf(void const *t, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: missing.c:611
bool cf_item_is_pair(CONF_ITEM const *item)
Definition: conffile.c:3928
Configuration AVP similar to a VALUE_PAIR.
Definition: conffile.c:82
The current request.
Definition: tmpl.h:113
Definition: token.h:45
FR_TOKEN op
Operator to use when moving or inserting valuepair into a list.
Definition: pair.h:118
#define VERIFY_TMPL(_x)
Definition: tmpl.h:266
int fr_pair_value_from_str(VALUE_PAIR *vp, char const *value, size_t len)
Convert string value to native attribute value.
Definition: pair.c:1840
tmpl_type_t type
What type of value tmpl refers to.
Definition: tmpl.h:188
RFC3575/RFC5176 - CoA-Request.
Definition: radius.h:108
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
size_t len
Length of the raw string used to create the template.
Definition: tmpl.h:191
#define RETURN_IF_TRUNCATED(_p, _ret, _max)
Boilerplate for checking truncation.
Definition: libradius.h:216
int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_tmpl_t const *vpt)
Copy pairs matching a vp_tmpl_t in the current REQUEST.
Definition: tmpl.c:2181
int value_data_copy(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE type, const value_data_t *src)
Copy value data verbatim duplicating any buffers.
Definition: value.c:1479
void fr_pair_delete_by_num(VALUE_PAIR **head, unsigned int vendor, unsigned int attr, int8_t tag)
Delete matching pairs.
Definition: pair.c:797
char const * cf_section_name1(CONF_SECTION const *cs)
Definition: conffile.c:3592
Definition: token.h:48
Callout to an external script or program.
Definition: tmpl.h:137
#define RDEBUG2(fmt,...)
Definition: log.h:244
int(* map_validate_t)(vp_map_t *map, void *ctx)
Definition: map.h:67
void fr_pair_list_move(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
Move pairs from source list to destination list respecting operator.
Definition: pair.c:1508
char name[1]
Attribute name.
Definition: dict.h:89
char * talloc_bstrndup(void const *t, char const *in, size_t inlen)
Binary safe strndup function.
Definition: missing.c:632
VALUE_PAIR * fr_cursor_replace(vp_cursor_t *cursor, VALUE_PAIR *new)
Replace the current pair.
Definition: cursor.c:491
void cf_log_err(CONF_ITEM const *ci, char const *fmt,...) CC_HINT(format(printf
#define TAG_ANY
Definition: pair.h:191
FR_TOKEN op
The operator that controls insertion of the dst attribute.
Definition: map.h:50
log_lvl_t rad_debug_lvl
Global debugging level.
Definition: log.c:49
FR_TOKEN gettoken(char const **ptr, char *buf, int buflen, bool unescape)
Definition: token.c:405
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
size_t radius_list_name(pair_lists_t *out, char const *name, pair_lists_t default_list)
Resolve attribute name to a pair_lists_t value.
Definition: tmpl.c:120
Unknown list.
Definition: tmpl.h:81
size_t tmpl_snprint(char *buffer, size_t bufsize, vp_tmpl_t const *vpt, fr_dict_attr_t const *values)
Print a vp_tmpl_t to a string.
Definition: tmpl.c:1822
int map_afrom_cs(vp_map_t **out, CONF_SECTION *cs, pair_lists_t dst_list_def, pair_lists_t src_list_def, map_validate_t validate, void *ctx, unsigned int max)
Convert an 'update' config section into an attribute map.
Definition: map.c:331
void void void fr_canonicalize_error(TALLOC_CTX *ctx, char **spaces, char **text, ssize_t slen, char const *msg)
Canonicalize error strings, removing tabs, and generate spaces for error marker.
Definition: log.c:359
enum pair_lists pair_lists_t
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
#define REDEBUG(fmt,...)
Definition: log.h:254
unsigned int has_tag
Tagged attribute.
Definition: dict.h:46
CONF_ITEM * cf_item_find_next(CONF_SECTION const *section, CONF_ITEM const *item)
Return the next item after a CONF_ITEM.
Definition: conffile.c:3850
#define DEBUG_OVERWRITE(_old, _new)
Definition: map.c:989
#define EXEC_TIMEOUT
Definition: radiusd.h:329
VALUE_PAIR * fr_cursor_remove(vp_cursor_t *cursor)
Remove the current pair.
Definition: cursor.c:433
enum fr_token FR_TOKEN
size_t radius_request_name(request_refs_t *out, char const *name, request_refs_t unknown)
Resolve attribute name to a request_refs_t value.
Definition: tmpl.c:413
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
VALUE_PAIR * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute.
Definition: pair.c:58
FR_TOKEN cf_pair_attr_type(CONF_PAIR const *pair)
Return the value (lhs) type.
Definition: conffile.c:3526
Definition: pair.c:37
char const * fr_int2str(FR_NAME_NUMBER const *table, int number, char const *def)
Definition: token.c:506
XLAT expansion.
Definition: tmpl.h:132
#define NUM_COUNT
Definition: pair.h:203
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
char l_opand[256]
Left hand side of the pair.
Definition: pair.h:157
fr_dict_attr_t const * fr_dict_attr_by_num(fr_dict_t *dict, unsigned int vendor, unsigned int attr)
Lookup a fr_dict_attr_t by its vendor and attribute numbers.
Definition: dict.c:3519
String of printable characters.
Definition: radius.h:33
FR_TOKEN quote
What type of quoting was around the raw string.
Definition: tmpl.h:192
FR_TOKEN quote
Type of quoting around the r_opand.
Definition: pair.h:160
PW_TYPE type
Value type.
Definition: dict.h:80
#define RCSID(id)
Definition: build.h:135
VALUE_PAIR * fr_cursor_next_by_da(vp_cursor_t *cursor, fr_dict_attr_t const *da, int8_t tag) CC_HINT(nonnull)
Iterate over attributes of a given DA in the pairlist.
Definition: cursor.c:237
FR_TOKEN cf_pair_operator(CONF_PAIR const *pair)
Definition: conffile.c:3511
int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
Value pair map.
Definition: map.h:46
union vp_tmpl_t::@11 data
#define RDEBUG(fmt,...)
Definition: log.h:243
A source or sink of value data.
Definition: tmpl.h:187
enum requests request_refs_t
#define VERIFY_MAP(_x)
Definition: map.h:59
const FR_NAME_NUMBER tmpl_names[]
Map tmpl_type_t values to descriptive strings.
Definition: tmpl.c:36
vp_tmpl_t * tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, char const *name, ssize_t len, FR_TOKEN quote)
Create a new heap allocated vp_tmpl_t.
Definition: tmpl.c:526
Has no value.
Definition: tmpl.h:141
CONF_ITEM * ci
Config item that the map was created from.
Definition: map.h:52
char const * cf_section_name2(CONF_SECTION const *cs)
Definition: conffile.c:3601
void map_sort(vp_map_t **maps, fr_cmp_t cmp)
Sort a linked list of vp_map_t using merge sort.
Definition: map.c:631
void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp)
Definition: map.c:1543
RFC3575/RFC5176 - Disconnect-Request.
Definition: radius.h:105
#define RDEBUG3(fmt,...)
Definition: log.h:245
value_data_t data
Definition: pair.h:133