The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
pair_legacy.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library 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 GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** AVP manipulation and search API
18 *
19 * @file src/lib/util/pair_legacy.c
20 *
21 * @copyright 2000,2006,2015 The FreeRADIUS server project
22 */
23RCSID("$Id: 7f0a9dba7b8b6dcc028101490b80261c711e5c17 $")
24
25#include <sys/wait.h>
26
27#include <freeradius-devel/util/dict.h>
28#include <freeradius-devel/util/pair.h>
29#include <freeradius-devel/util/pair_legacy.h>
30#include <freeradius-devel/util/proto.h>
31#include <freeradius-devel/util/regex.h>
32#include <freeradius-devel/util/syserror.h>
33
34#include <freeradius-devel/protocol/radius/rfc2865.h>
35#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
36
39 L("\t"),
40 L("\n"),
41 L(" "),
42 L("!*"),
43 L("!="),
44 L("!~"),
45 L("&&"), /* Logical operator */
46 L(")"), /* Close condition/sub-condition */
47 L("+="),
48 L("-="),
49 L(":="),
50 L("<"),
51 L("<="),
52 L("=*"),
53 L("=="),
54 L("=~"),
55 L(">"),
56 L(">="),
57 L("||"), /* Logical operator */
58 );
59
61 { L("+="), T_OP_ADD_EQ },
62 { L(":="), T_OP_SET },
63 { L("="), T_OP_EQ },
64};
66
68 { L("!*"), T_OP_CMP_FALSE },
69 { L("!="), T_OP_NE },
70 { L("!~"), T_OP_REG_NE },
71 { L("+="), T_OP_ADD_EQ },
72 { L(":="), T_OP_SET },
73 { L("<"), T_OP_LT },
74 { L("<="), T_OP_LE },
75 { L("="), T_OP_EQ },
76 { L("=*"), T_OP_CMP_TRUE },
77 { L("=="), T_OP_CMP_EQ },
78 { L("=~"), T_OP_REG_EQ },
79 { L(">"), T_OP_GT },
80 { L(">="), T_OP_GE }
81};
83
84/*
85 * Stop parsing bare words at whitespace, comma, or end of list.
86 *
87 * Note that we don't allow escaping of bare words here, as that screws up parsing of raw attributes with
88 * 0x... prefixes.
89 */
90static fr_sbuff_parse_rules_t const bareword_unquoted = {
91 .terminals = &FR_SBUFF_TERMS(
92 L(""),
93 L("\t"),
94 L("\n"),
95 L("\r"),
96 L(" "),
97 L(","),
98 L("}")
99 )
100};
101
102
104{
105 fr_sbuff_t our_in = FR_SBUFF(in);
106 char quote;
107 ssize_t slen;
108 fr_sbuff_parse_rules_t const *rules;
109
110 if (fr_sbuff_next_if_char(&our_in, '"')) {
112 quote = '"';
113 parse:
114 slen = fr_value_box_from_substr(vp, &vp->data, vp->da->type, vp->da, &our_in, rules);
115 } else if (fr_sbuff_next_if_char(&our_in, '\'')) {
117 quote = '\'';
118 goto parse;
119 } else if (!fr_sbuff_next_if_char(&our_in, '`')) {
120 quote = '\0';
121 rules = &bareword_unquoted;
122 goto parse;
123 /*
124 * We _sometimes_ support backticks, depending on the
125 * source of the data. This should ONLY be used on
126 * trusted input, like config files.
127 *
128 * We don't impose arbitrary limits on exec input or
129 * output, as AGAIN this should only be used on trusted
130 * input.
131 *
132 * Only the first line of output from the process is used,
133 * and no escape sequences in the output are processed.
134 */
135 } else {
136 fr_sbuff_t *exec_in;
137 size_t exec_out_buff_len = 0;
138 ssize_t exec_out_len;
139 char *exec_out = NULL;
140 FILE *fp;
141 int ret;
142
143 if (!conf->allow_exec) {
144 fr_strerror_const("Backticks are not supported here");
145 return 0;
146 }
147
148 /*
149 * Should only be used for trusted resources, so no artificial limits
150 */
151 FR_SBUFF_TALLOC_THREAD_LOCAL(&exec_in, 1024, SIZE_MAX);
152 (void)fr_sbuff_out_unescape_until(exec_in, &our_in, SIZE_MAX, &FR_SBUFF_TERMS(L("`")), &fr_value_unescape_backtick);
153 /*
154 * Don't exec if we know we're going to fail
155 */
156 if (!fr_sbuff_is_char(&our_in, '`')) {
157 fr_strerror_const("Unterminated backtick string");
158 return 0;
159 }
160
161 fp = popen(fr_sbuff_start(exec_in), "r");
162 if (!fp) {
163 fr_strerror_printf("Cannot execute command `%pV`: %s",
165 fr_syserror(errno));
166 return 0;
167 }
168
169 errno = 0; /* If we get EOF immediately, we don't want to emit spurious errors */
170 exec_out_len = getline(&exec_out, &exec_out_buff_len, fp);
171 if ((exec_out_len < 0) || (exec_out == NULL)) { /* defensive */
172 fr_strerror_printf("Cannot read output from command `%pV`: %s",
174 fr_syserror(errno));
175 pclose(fp);
176 return 0;
177 }
178
179 /*
180 * Protect against child writing too much data to stdout,
181 * blocking, and never exiting.
182 *
183 * This is likely overly cautious for this particular use
184 * case, but it doesn't hurt.
185 */
186 {
187 char buffer[128];
188
189 while (fread(buffer, 1, sizeof(buffer), fp) > 0) { /* discard */ }
190 }
191
192 errno = 0; /* ensure we don't have stale errno */
193 ret = pclose(fp);
194 if (ret < 0) {
195 fr_strerror_printf("Error waiting for command `%pV` to finish: %s",
197 fr_syserror(errno));
198 pclose_error:
199 free(exec_out);
200 return 0;
201 } else if (ret != 0) {
202 if (WIFEXITED(ret)) {
203 fr_strerror_printf("Command `%pV` exited with status %d",
205 WEXITSTATUS(ret));
206 } else if (WIFSIGNALED(ret)) {
207 fr_strerror_printf("Command `%pV` terminated by signal %d",
209 WTERMSIG(ret));
210 } else {
211 fr_strerror_printf("Command `%pV` terminated abnormally",
213 }
214 goto pclose_error;
215 }
216
217 /*
218 * Trim line endings
219 */
220 if (exec_out_len > 0 && exec_out[exec_out_len - 1] == '\n') exec_out[--exec_out_len] = '\0';
221 if (exec_out_len > 0 && exec_out[exec_out_len - 1] == '\r') exec_out[--exec_out_len] = '\0';
222
223 slen = fr_value_box_from_substr(vp, &vp->data, vp->da->type, vp->da,
224 &FR_SBUFF_IN(exec_out, exec_out_len), &value_parse_rules_single_quoted);
225 free(exec_out);
226 if (unlikely(slen < 0)) {
227 return 0; /* slen is parse position in the exec output*/
228 }
229
230 quote = '`';
231 }
232
233 if (slen < 0) {
234 fr_assert(slen >= -((ssize_t) 1 << 20));
235 return slen - (quote != 0);
236 }
237
238 if (quote && !fr_sbuff_next_if_char(&our_in, quote)) {
239 fr_strerror_const("Unterminated string");
240 return 0;
241 }
242
243 fr_assert(slen <= ((ssize_t) 1 << 20));
244
245 FR_SBUFF_SET_RETURN(in, &our_in);
246}
247
248/** Our version of a DA stack.
249 *
250 * @todo - add in whether or not we added / created the vp? maybe an edit list?
251 * and then we can clean up the unknown DAs, simply by talloc freeing the edit list.
252 */
253typedef struct {
254 int depth;
255 fr_dict_attr_t const *da[FR_DICT_MAX_TLV_STACK]; //!< parent for parsing
256 fr_pair_t *vp[FR_DICT_MAX_TLV_STACK]; //!< which VP we have created or found
258
259/** Parse a #fr_pair_list_t from a substring
260 *
261 * Syntax: ([raw.]|.)<name>[<.name>] op [(cast)] value...
262 *
263 * A "raw" prefix creates a raw attribute, which allows us to encode raw data which might be invalid for
264 * the given data type. Or if a "(cast)" is given, the value is parsed as the specified data type. Note
265 * that casts can only be to a "leaf" data type, and not to a structural type such as "tlv", "group",
266 * "struct", etc. The "(cast)" syntax can only be used for "raw" attributes, and not for attributes
267 * which are known. The "name" can be either a known attribute, or a numerical OID. Either way, the
268 * final attribute which is created is marked as "raw" or "unknown", and is encoded via the "raw" rules,
269 * and not as the known data type.
270 *
271 * If the first name begins with ".", then it is a _relative_ name. The attribute is created in the
272 * context of the most recently created "structural" data type.
273 *
274 * TBD - we have to determine what the heck that means...
275 *
276 * The "name" can be one or more names from the input dictionary. The names must be known, as numerical
277 * OIDs can only be used when the "raw" prefix is used.
278 *
279 * If there are multiple names (e.g. "foo.bar.baz"), then only the last name can be a "leaf" data
280 * type. All of the intermediate names must be "structural" data types.
281 *
282 * Depending on the input arguments, the operator can be a comparison operator (==, <=, etc.). Or, else
283 * it can be an assignment operator (=, +=). The "=" operator is used to assign, and the "+=" operator
284 * is used to append. No other assignment operators are permitted. Note that "+=" cannot be used with
285 * relative names (i.e. where the name begins with ".")
286 *
287 * The "value" can either be a "leaf" data type (e.g. number, IP address, etc.) or for "structural" data
288 * types it can be a sub-list. A sub-list is a set of attribute assignments which are surrounded by
289 * curly brackets "{...}". When a sub-list is specified, the contents must be either children of the
290 * parent attribute (for "tlv", "struct"), or children referenced by a "group", or internal attributes.
291 *
292 * If an intermediate "name" is an ALIAS, then the attributes are created / used as if all intermediate
293 * names were specified. i.e. ALIAS is a short-cut for names (think "soft link), but it does not change
294 * the hierarchy for normal attributes.
295 *
296 *
297 * Examples
298 * --------
299 *
300 * Name = value
301 * Leaf attributes.
302 * The value MUST be parsed as the leaf data type.
303 *
304 * Name = { children }
305 * Structural attributes.
306 * The children MUST be children of the parent.
307 * OR the children can be from the "internal" dictionary.
308 * OR for type 'group', children of the group reference (usually the dictionary root)
309 *
310 * raw.Name = 0xabcdef
311 * Raw attributes.
312 * The value MUST be a hex string.
313 *
314 * raw.Name = { children }
315 *
316 * @param[in] root where we start parsing from
317 * @param[in,out] relative where we left off, or where we should continue from
318 * @param[in] in input sbuff
319 * @return
320 * - <0 on error
321 * - 0 on no input
322 * - >0 on how many bytes of input we read
323 */
325 fr_sbuff_t *in)
326{
327 int i, components;
328 bool raw, was_unknown;
329 bool was_relative = false;
330 bool append;
331 bool keep_going;
332 fr_type_t raw_type;
333 fr_token_t op;
334 fr_slen_t slen;
335 fr_pair_t *vp;
337 fr_sbuff_marker_t lhs_m, op_m, rhs_m;
338 fr_sbuff_t our_in = FR_SBUFF(in);
339 legacy_da_stack_t da_stack = {};
340
341 if (unlikely(!root->ctx)) {
342 fr_strerror_const("Missing input context (fr_pair_parse_t)");
343 return -1;
344 }
345
346 if (unlikely(!root->da)) {
347 fr_strerror_const("Missing namespace attribute");
348 return -1;
349 }
350
351 if (unlikely(!root->list)) {
352 fr_strerror_const("Missing list");
353 return -1;
354 }
355
356 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
357
358 if (fr_sbuff_remaining(&our_in) == 0) return 0;
359
360 /*
361 * Boot strap the relative references from the root.
362 *
363 * The comparison operations are only used for internal tests, and should not be used by
364 * administrators. So we disallow them, unless the destination list is empty. This check
365 * prevents them from being used in administrative policies.
366 */
367 if (!relative->da) {
368 if (root->allow_compare && !fr_pair_list_empty(root->list)) {
369 fr_strerror_const("Attribute comparisons can only be used when the destination list is empty");
370 return -1;
371 }
372
373 *relative = *root;
374 }
375
376#define CLEAN_DA_STACK do { if (was_unknown) { \
377 for (i = 1; i < da_stack.depth; i++) { \
378 fr_dict_attr_unknown_free(&da_stack.da[i]); \
379 } } } while (0)
380
381 /*
382 * Initialize the markers once, before the redo label, so that they are only inserted into the
383 * marker linked list once. After this, use fr_sbuff_set() to update marker positions.
384 */
385 fr_sbuff_marker(&lhs_m, &our_in);
386 fr_sbuff_marker(&op_m, &our_in);
387 fr_sbuff_marker(&rhs_m, &our_in);
388
389redo:
390 raw = false;
391 raw_type = FR_TYPE_NULL;
392 relative->last_char = 0;
393 was_unknown = false;
394 vp = NULL;
395
396 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
397
398 /*
399 * STEP 1: Figure out if we have relative or absolute attributes.
400 *
401 * Absolute attributes start from the root list / parent.
402 * Or, when there is no previous relative setting.
403 *
404 * Relative attributes start from the input list / parent.
405 *
406 * Once we decide where we start parsing from, all subsequent operations are on the "relative"
407 * structure.
408 */
409 if (!fr_sbuff_next_if_char(&our_in, '.')) {
410 *relative = *root;
411
412 append = !was_relative;
413 was_relative = false;
414
415 /*
416 * Be nice to people who expect to use '&' everywhere.
417 */
418 (void) fr_sbuff_next_if_char(&our_in, '&');
419
420 /*
421 * Raw attributes can only be at our root.
422 *
423 * "raw.foo" means that SOME component of the OID is raw. But the starting bits might be known.
424 *
425 * Raw attributes cannot be created in the internal namespace. But an internal group can
426 * contain raw protocol attributes.
427 */
428 if (fr_sbuff_is_str_literal(&our_in, "raw.")) {
429 fr_sbuff_advance(&our_in, 4);
430 goto is_raw;
431 }
432
433 } else if (relative->da->flags.is_root) {
434 fr_strerror_const("The '.Attribute' syntax cannot be used at the root of a dictionary");
435
436 error:
438 return fr_sbuff_error(&our_in);
439
440 } else if (relative->da->type == FR_TYPE_GROUP) {
441 fr_strerror_printf("The '.Attribute' syntax cannot be used with parent %s of data type 'group'",
442 relative->da->name);
443 goto error;
444
445 } else {
446 fr_assert(relative->ctx);
447 fr_assert(relative->list);
448
449 was_relative = true;
450 append = true;
451 }
452
453 /*
454 * If the input root is an unknown attribute, then forbid internal ones, and force everything
455 * else to be raw, too.
456 */
457 if (relative->da->flags.is_unknown) {
458 is_raw:
459 raw = true;
460 }
461
462 /*
463 * Raw internal attributes don't make sense. An internal group can contain raw protocol
464 * attributes, but the group is not raw.
465 */
466 if (raw && relative->da->flags.internal) {
467 fr_strerror_const("Cannot create internal attributes which are 'raw'");
468 goto error;
469 }
470
471 /*
472 * Set the LHS marker to be after any initial '.'
473 */
474 fr_sbuff_set(&lhs_m, &our_in);
475
476 /*
477 * STEP 2: Find and check the operator.
478 *
479 * Skip over the attribute name. We need to get the operator _before_ creating the VPs.
480 */
481 components = 0;
482 do {
483 if (fr_sbuff_adv_past_allowed(&our_in, SIZE_MAX, fr_dict_attr_allowed_chars, NULL) == 0) break;
484 components++;
485 } while (fr_sbuff_next_if_char(&our_in, '.'));
486
487 /*
488 * Couldn't find anything.
489 */
490 if (!components) goto done;
491
492 fr_sbuff_set(&op_m, &our_in);
493 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
494
495 /*
496 * Look for the operator.
497 */
498 if (relative->allow_compare) {
500 if (op == T_INVALID) {
501 fr_strerror_const("Expecting operator");
502 goto error;
503 }
504
505 /*
506 * People can use this, but it doesn't mean anything.
507 */
508 if (op == T_OP_SET) op = T_OP_EQ;
509
510 } else {
511 /*
512 * @todo - handle different operators ala v3?
513 * What is the difference between ":=" and "="? Perhaps nothing?
514 */
516 if (op == T_INVALID) {
517 fr_strerror_const("Expecting operator");
518 goto error;
519 }
520
521 /*
522 * += means "append"
523 * := menas "don't append".
524 */
525 if (op != T_OP_EQ) {
526 if (was_relative) {
527 fr_strerror_printf("The '.Attribute' syntax cannot be used along with the '%s' operator",
528 fr_tokens[op]);
529 goto error;
530 }
531 }
532
533 if (op == T_OP_ADD_EQ) {
534 append = true;
535 }
536
537 if (op == T_OP_SET) {
538 append = false;
539 }
540
541 op = T_OP_EQ;
542 }
543
544 /*
545 * Check the character after the operator. This check is only necessary to produce better error
546 * messages. i.e. We allow "=", but the user enters "==".
547 */
548 {
549 uint8_t c = fr_sbuff_char(&our_in, '\0');
550 static const bool invalid[SBUFF_CHAR_CLASS] = {
551 ['!'] = true, ['#'] = true, ['$'] = true, ['*'] = true,
552 ['+'] = true, ['-'] = true, ['/'] = true, ['<'] = true,
553 ['='] = true, ['>'] = true, ['?'] = true, ['|'] = true,
554 ['~'] = true,
555 };
556
557 if (c && invalid[c]) {
558 fr_strerror_printf("Invalid character '%c' after operator '%s'",
559 (char) c, fr_tokens[op]);
560 goto error;
561 }
562 }
563
564 /*
565 * Skip past whitespace, and set a marker at the RHS value. We do a quick peek at the value, to
566 * set the data type of the RHS. This allows us to parse raw TLVs.
567 */
568 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
569
570 /*
571 * STEP 3: Try to guess the data type for "raw" attributes.
572 *
573 * If the attribute is raw, and the value of the attribute is 0x..., then we always force the raw
574 * type to be octets, even if the attribute is named and known. e.g. raw.Framed-IP-Address =
575 * 0x01.
576 *
577 * OR if the attribute is entirely unknown (and not a raw version of a known one), then we allow a
578 * cast which sets the data type.
579 */
580 if (raw) {
581 if (fr_sbuff_is_str_literal(&our_in, "0x")) {
582 raw_type = FR_TYPE_OCTETS;
583
584 } else if (fr_sbuff_next_if_char(&our_in, '(')) {
585 fr_sbuff_marker_t cast_m;
586
587 fr_sbuff_marker(&cast_m, &our_in);
588
589 fr_sbuff_out_by_longest_prefix(&slen, &raw_type, fr_type_table, &our_in, FR_TYPE_NULL);
590
591 /*
592 * The input has to be a real (non-NULL) leaf. The input shouldn't be cast to a
593 * TLV. Instead, the value should just start with '{'.
594 */
595 if (!fr_type_is_leaf(raw_type)) {
596 fr_sbuff_set(&our_in, &cast_m);
597 fr_strerror_const("Invalid data type in cast");
598 goto error;
599 }
600
601 if (!fr_sbuff_next_if_char(&our_in, ')')) {
602 fr_strerror_const("Missing ')' in cast");
603 goto error;
604 }
605
606 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
607
608 } else if (fr_sbuff_is_char(&our_in, '{')) {
609 /*
610 * Raw attributes default to data type TLV.
611 */
612 raw_type = FR_TYPE_TLV;
613 append = false;
614 }
615 }
616
617 fr_sbuff_set(&rhs_m, &our_in);
618
619 fr_sbuff_set(&our_in, &lhs_m);
620
621 /*
622 * That we know the data type, parse each OID component. We build the DA stack from top to bottom.
623 *
624 * 0 is our relative root. 1..N are the DAs that we find or create.
625 */
626 da_stack = (legacy_da_stack_t) {
627 .da = {
628 [0] = relative->da,
629 },
630 .depth = 1,
631 };
632
633 /*
634 * STEP 4: Re-parse the attributes, building up the da_stack of #fr_dict_attr_t that we will be
635 * using as parents.
636 */
637 for (i = 1; i <= components; i++, da_stack.depth++) {
639 fr_dict_attr_t const *da = NULL;
640 fr_dict_attr_t const *da_unknown = NULL;
641 fr_dict_attr_t const *parent;
642 fr_dict_attr_t const *ref;
643 fr_type_t unknown_type;
644
645 if (da_stack.depth >= FR_DICT_MAX_TLV_STACK) {
646 fr_strerror_printf("Attributes are nested too deeply at \"%.*s\"",
647 (int) fr_sbuff_diff(&op_m, &lhs_m), fr_sbuff_current(&lhs_m));
648 goto error;
649 }
650
651 fr_sbuff_set(&lhs_m, &our_in);
652
653 /*
654 * The fr_pair_t parent might be a group, in which case the fr_dict_attr_t parent will be
655 * different.
656 */
657 parent = da_stack.da[da_stack.depth - 1];
658 if (parent->type == FR_TYPE_GROUP) {
660 fr_assert(parent != NULL);
661 }
662
663 /*
664 * Once we parse a completely unknown attribute, all of the rest of them have to be
665 * unknown, too. We cannot allow unknown TLVs to contain internal attributes, for
666 * example.
667 */
668 if (was_unknown) {
669 goto alloc_unknown;
670 }
671
672 /*
673 * Look up the name (or number). If it's found, life is easy. Otherwise, we jump
674 * through a bunch of hoops to see if we are changing dictionaries, or creating a raw OID
675 * from a number, etc.
676 */
677 slen = fr_dict_oid_component(&err, &da, parent, &our_in, &bareword_terminals);
678 if (err != FR_DICT_ATTR_OK) {
679 /*
680 * We were looking in the internal dictionary. Maybe this attribute is instead
681 * in the protocol dictionary?
682 */
683 if ((i == 1) && (relative->da->dict == relative->internal) && relative->dict) {
684 fr_assert(relative->dict != relative->internal);
685
686 /*
687 * Internal groups can be used to cache protocol data. Internal
688 * structural attributes cannot.
689 *
690 * @todo - this restriction makes sense, but maybe people want to do that
691 * anyways?
692 */
693 if (relative->da->type != FR_TYPE_GROUP) {
694 fr_strerror_printf("Internal attribute '%s' of data type '%s' cannot contain protocol attributes",
695 relative->da->name, fr_type_to_str(relative->da->type));
696 goto error;
697 }
698
699 slen = fr_dict_oid_component(&err, &da, fr_dict_root(relative->dict), &our_in, &bareword_terminals);
700 if (err == FR_DICT_ATTR_OK) {
701 ref = fr_dict_root(relative->dict);
702 goto found;
703 }
704 }
705
706 /*
707 * Try to parse the name from the internal namespace first, as this is the most
708 * likely case. Plus, if we parse the OIDs second, the errors for unknown
709 * attributes mention the protocol dictionary, and not the internal one.
710 *
711 * Raw attributes also cannot be created in the internal dictionary space.
712 */
713 if (!raw && relative->internal) {
714 /*
715 * If the current dictionary isn't internal, then look up the attribute
716 * in the internal dictionary.
717 *
718 * Buf if the current dictionary is internal, AND the internal type is
719 * GROUP, AND we we have a protocol dictionary, then allow an internal
720 * group to contain protocol attributes.
721 */
722 if (parent->dict != relative->internal) {
723 ref = fr_dict_root(relative->internal);
724
725 } else if ((da_stack.da[da_stack.depth - 1]->type == FR_TYPE_GROUP) && (root->da->dict != root->internal)) {
726 ref = fr_dict_root(root->da->dict);
727
728 } else {
729 /*
730 * Otherwise we are already in the internal dictionary, and the
731 * attribute was not found. So don't search for it again in the
732 * internal dictionary. And because we're in the internal
733 * dictionary, we don't allow raw attributes.
734 */
735 goto notfound;
736 }
737
738 slen = fr_dict_oid_component(&err, &da, ref, &our_in, &bareword_terminals);
739 if (err == FR_DICT_ATTR_OK) {
740 goto found;
741 }
742
743 goto notfound;
744 }
745
746 /*
747 * We didn't find anything, that's an error.
748 */
749 if (!raw) {
750 notfound:
751 fr_strerror_printf("Unknown attribute \"%.*s\" for parent \"%s\"",
752 (int) fr_sbuff_diff(&op_m, &our_in), fr_sbuff_current(&our_in),
753 da_stack.da[da_stack.depth - 1]->name);
754 goto error;
755 }
756
757 alloc_unknown:
758 /*
759 * We looked up raw.FOO, and FOO wasn't found. See if we can still parse it.
760 */
761 if (da_stack.da[da_stack.depth - 1]->type == FR_TYPE_GROUP) {
762 fr_strerror_printf("Cannot create 'raw' children in attribute %s of data type 'group'",
763 da_stack.da[da_stack.depth - 1]->name);
764 goto error;
765 }
766
767 /*
768 * Unknown attributes must be 'raw.1234'.
769 */
770 if (!fr_sbuff_is_digit(&our_in)) {
771 goto notfound;
772 }
773
774 /*
775 * Figure out the data type for unknown attributes. Intermediate attributes are
776 * structural. Only the final attribute is forced to "raw_type".
777 */
778 if (i < components) {
779 if (parent->type == FR_TYPE_VSA) {
780 unknown_type = FR_TYPE_VENDOR;
781 } else {
782 unknown_type = FR_TYPE_TLV;
783 }
784
785 } else if (raw_type == FR_TYPE_NULL) {
786 unknown_type = FR_TYPE_OCTETS;
787
788 } else if ((raw_type == FR_TYPE_TLV) && (parent->type == FR_TYPE_VSA)) {
789 /*
790 * We had previously parsed a known VSA, but this component is
791 * perhaps a numerical OID. Set the data type to VENDOR, so that
792 * the hierachy is correct.
793 */
794 unknown_type = FR_TYPE_VENDOR;
795
796 } else {
797 unknown_type = raw_type;
798 }
799
800 da_unknown = fr_dict_attr_unknown_afrom_oid(root->ctx, parent, &our_in, unknown_type);
801 if (!da_unknown) goto error;
802
803 da = da_unknown;
804 was_unknown = true;
805
806 goto next;
807 } /* huge block of "we didn't find a known attribute" */
808
809 /*
810 * We found the component. It MIGHT be an ALIAS which jumps down a few levels. Or, it
811 * might be a group which jumps back to the dictionary root. Or it may suddenly be an
812 * internal attribute.
813 *
814 * For an ALIAS, we need to add intermediate nodes up to the parent.
815 *
816 * For a GROUP, we need to add nodes up to the ref of the group.
817 *
818 * For internal attributes, we need to add nodes up to the root of the internal
819 * dictionary.
820 */
821 if (da->parent != parent) {
822 int j, diff;
823 fr_dict_attr_t const *up;
824
825 ref = parent;
826
827 found:
828 fr_assert(fr_dict_attr_common_parent(ref, da, true) == ref);
829
830 diff = da->depth - ref->depth;
831 fr_assert(diff >= 1);
832
833 diff--;
834
835 if ((da_stack.depth + diff) >= FR_DICT_MAX_TLV_STACK) {
836 fr_strerror_printf("Attributes are nested too deeply at \"%.*s\"",
837 (int) fr_sbuff_diff(&op_m, &lhs_m), fr_sbuff_current(&lhs_m));
838 goto error;
839 }
840
841 /*
842 * Go back up the da_stack, setting the parent.
843 */
844 up = da;
845 for (j = da_stack.depth + diff; j >= da_stack.depth; j--) {
846 da_stack.da[j] = up;
847 up = up->parent;
848 }
849
850 for (j = da_stack.depth; j <= da_stack.depth + diff; j++) {
851 fr_assert(da_stack.da[j] != NULL);
852 }
853
854 /*
855 * Record that we've added more attributes to the da_stack.
856 */
857 da_stack.depth += diff;
858 }
859
860 next:
861 /*
862 * Limit the data types that we can parse. This check is mainly to get better error
863 * messages.
864 */
865 switch (da->type) {
866 case FR_TYPE_GROUP:
867 if (raw && (raw_type != FR_TYPE_OCTETS)) {
868 fr_strerror_printf("Cannot create 'raw' attributes for data type '%s'", fr_type_to_str(da->type));
869 goto error;
870 }
871 break;
872
874 case FR_TYPE_LEAF:
875 break;
876
877 default:
878 fr_strerror_printf("Invalid data type '%s'", fr_type_to_str(da->type));
879 goto error;
880 }
881
882 /*
883 * Everything until the last component must end with a '.', because otherwise there would
884 * be no next component.
885 */
886 if (i < components) {
887 if (!fr_sbuff_next_if_char(&our_in, '.')) {
888 fr_strerror_printf("Missing '.' at \"%.*s\"",
889 (int) fr_sbuff_diff(&op_m, &lhs_m), fr_sbuff_current(&lhs_m));
890 goto error;
891 }
892
893 /*
894 * Leaf attributes cannot appear in the middle of the OID list.
895 */
896 if (fr_type_is_leaf(da->type)) {
898 fr_strerror_printf("Please remove the reference to key field '%s' from the input string",
899 da->name);
900 } else {
901 fr_strerror_printf("Leaf attribute '%s' cannot have children", da->name);
902 }
903
904 goto error;
905 }
906
907 } else if (raw && !da->flags.is_unknown) {
908 /*
909 * Only the last component can be raw. If the attribute we found isn't unknown,
910 * then create an unknown DA from the known one.
911 *
912 * We have parsed the full OID tree, *and* found a known attribute. e.g. raw.Vendor-Specific = ...
913 *
914 * For some reason, we allow: raw.Vendor-Specific = { ... }
915 *
916 * But this is what we really want: raw.Vendor-Specific = 0xabcdef
917 */
918 if ((raw_type != FR_TYPE_OCTETS) && (raw_type != da->type)) {
919 /*
920 * @todo - because it breaks a lot of the encoders.
921 */
922 fr_strerror_printf("Cannot create raw attribute %s which changes data type from %s to %s",
923 da->name, fr_type_to_str(da->type), fr_type_to_str(raw_type));
924 fr_sbuff_set(&our_in, &lhs_m);
925 goto error;
926 }
927
928 da_unknown = fr_dict_attr_unknown_alloc(root->ctx, da, raw_type);
929 if (!da_unknown) goto error;
930
931 da = da_unknown;
932 was_unknown = true;
933 }
934
935 da_stack.da[da_stack.depth] = da;
936 }
937
938 /*
939 * at least [0]=root, [1]=da, [2]=NULL
940 */
941 if (da_stack.depth <= 1) {
942 fr_strerror_const("Internal sanity check failed on depth 1");
943 return fr_sbuff_error(&our_in);
944 }
945
946 if (da_stack.depth <= components) {
947 fr_strerror_const("Internal sanity check failed on depth 2");
948 return fr_sbuff_error(&our_in);
949 }
950
951 /*
952 * STEP 5: Reset the parser to the value, and double-check if it's what we expect.
953 */
954 fr_sbuff_set(&our_in, &rhs_m);
955
956 if (fr_type_is_structural(da_stack.da[da_stack.depth - 1]->type)) {
957 if (!fr_sbuff_is_char(&our_in, '{')) {
958 fr_strerror_printf("Group list for %s MUST start with '{'", da_stack.da[da_stack.depth - 1]->name);
959 goto error;
960 }
961
962 /*
963 * The fr_pair_validate() function doesn't support operators for structural attributes,
964 * so we forbid them here.
965 */
966 if (relative->allow_compare && (op != T_OP_EQ) && (op != T_OP_CMP_EQ)) {
967 fr_strerror_printf("Structural attribute '%s' must use '=' or '==' for comparisons",
968 da_stack.da[da_stack.depth - 1]->name);
969 goto error;
970 }
971
972 /*
973 * If we have "foo = { ... }", then we just create the attribute.
974 */
975 if (components == 1) append = (op != T_OP_EQ);
976 }
977
978#if 0
979 /*
980 * STEP 5.1: Flatten the hierarchy if necessary.
981 */
982 if ((relative->da->flags.allow_flat) && (da_stack.depth > 2)) {
983 da_stack.da[1] = da_stack.da[da_stack.depth - 1];
984
985 da_stack.depth = 2;
986 }
987#endif
988
989 /*
990 * STEP 6: Use the da_stack to either find or add intermediate #fr_pair_t.
991 */
992 my = *relative;
993 for (i = 1; i < da_stack.depth; i++) {
994 fr_dict_attr_t const *da;
995
996 da = da_stack.da[i];
997
998 /*
999 * When we have a full path that contains MEMBERs of a STRUCT, we need to check ordering.
1000 * The children MUST be added in order. If we see a child that is out of order, then
1001 * that means we need to start a new parent STRUCT.
1002 */
1003 if ((da->parent->type == FR_TYPE_STRUCT) && (i > 1)) {
1004 fr_assert(da_stack.da[i - 1] == da->parent);
1005 fr_assert(da_stack.vp[i - 1] != NULL);
1006 fr_assert(my.ctx == da_stack.vp[i - 1]);
1007
1008 /*
1009 * @todo - cache the last previous child that we added? Or maybe the DA of the
1010 * last child?
1011 */
1012 for (vp = fr_pair_list_tail(my.list);
1013 vp != NULL;
1014 vp = fr_pair_list_prev(my.list, vp)) {
1015 if (!vp->da->flags.internal) break;
1016 }
1017
1018 if (vp && (vp->da->attr > da->attr)) {
1019 fr_pair_t *parent = da_stack.vp[i - 2];
1020
1021 if (parent) {
1022 if (fr_pair_append_by_da(parent, &vp, &parent->vp_group, da->parent) < 0) {
1023 goto error;
1024 }
1025 } else {
1026 if (fr_pair_append_by_da(root->ctx, &vp, root->list, da->parent) < 0) {
1027 goto error;
1028 }
1029 }
1030
1031 vp->op = T_OP_EQ;
1033 my.ctx = vp;
1034 my.list = &vp->vp_group;
1035 }
1036 }
1037
1038 /*
1039 * Everything up to the last entry must be structural.
1040 *
1041 * The last entry may be structural, or else it might be a leaf.
1042 */
1043 if (fr_type_is_structural(da->type)) {
1044 if (append) {
1045 vp = fr_pair_find_last_by_da(my.list, NULL, da);
1046 if (vp) goto update_relative;
1047 }
1048
1049 if (fr_pair_append_by_da(my.ctx, &vp, my.list, da) < 0) {
1050 goto error;
1051 }
1052
1053 vp->op = T_OP_EQ;
1055
1056 update_relative:
1057 da_stack.vp[i] = vp;
1058
1059 my.ctx = vp;
1060 my.da = vp->da;
1061 my.list = &vp->vp_group;
1062 continue;
1063 }
1064
1065 /*
1066 * We're finally at the leaf attribute, which must be the last attribute.
1067 */
1068 fr_assert(i == (da_stack.depth - 1));
1069
1070 vp = fr_pair_afrom_da(my.ctx, da);
1071 if (!vp) goto error;
1072
1074 vp->op = op;
1075 da_stack.vp[i] = vp;
1076 }
1077
1078 /*
1079 * Intermediate nodes always use the operator '='. The final one uses the assigned operator.
1080 */
1081 fr_assert(vp != NULL);
1082 fr_assert(vp->op != T_INVALID);
1083
1084 /*
1085 * STEP 7: Parse the value, recursing if necessary.
1086 *
1087 * @todo - do all kinds of cleanups if anything fails. TBH, this really needs the edit lists,
1088 * and that might be a bit much overhead for this code.
1089 */
1090 if (fr_type_is_structural(vp->da->type)) {
1093 .dict = root->dict,
1094 .internal = root->internal,
1095 };
1096
1097 if (!fr_sbuff_next_if_char(&our_in, '{')) {
1098 fr_strerror_printf("Child list for %s MUST start with '{'", vp->da->name);
1099 goto error;
1100 }
1101
1102 fr_assert(my.ctx == vp);
1103 fr_assert(my.da == vp->da);
1104 fr_assert(my.list == &vp->vp_group);
1105 my.allow_compare = root->allow_compare;
1106 my.end_of_list = true;
1107
1108 while (true) {
1109 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
1110
1111 if (fr_sbuff_is_char(&our_in, '}')) {
1112 break;
1113 }
1114
1115 slen = fr_pair_list_afrom_substr(&my, &child, &our_in);
1116 if (!slen) break;
1117
1118 if (slen < 0) goto error;
1119 }
1120
1121 if (!fr_sbuff_next_if_char(&our_in, '}')) {
1122 fr_strerror_const("Failed to end list with '}'");
1123 goto error;
1124 }
1125
1126 /*
1127 * This structure was the last thing we parsed. The next thing starts from here.
1128 */
1129 *relative = my;
1130
1131 } else {
1132 slen = fr_pair_value_from_substr(root, vp, &our_in);
1133 if (slen <= 0) goto error;
1134
1135 fr_pair_append(my.list, vp);
1136 }
1137
1138 PAIR_VERIFY(vp);
1139
1141
1142 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
1143
1144 /*
1145 * STEP 8: See if we're done, or if we need to stop parsing this #fr_pair_t.
1146 *
1147 * Allow a limited set of characters after a value.
1148 *
1149 * It can be "," OR "CRLF" OR ",CRLF". But not anything else.
1150 */
1151 keep_going = false;
1152 if (fr_sbuff_next_if_char(&our_in, ',')) {
1153 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
1154
1155 keep_going = true;
1156 relative->last_char = ',';
1157 }
1158
1159 /*
1160 * We hit the end of the parent list. There's no need to update "relative", we just return, and
1161 * let the caller end the list.
1162 *
1163 * Note that we allow trailing commas: Foo = { Bar = Baz, }
1164 *
1165 * We don't care about any trailing data.
1166 */
1167 if (relative->end_of_list && fr_sbuff_is_char(&our_in, '}')) {
1168 relative->last_char = '\0';
1169 goto done;
1170 }
1171
1172 if (relative->allow_crlf) {
1173 size_t len;
1174
1175 len = fr_sbuff_adv_past_allowed(&our_in, SIZE_MAX, sbuff_char_line_endings, NULL);
1176 if (len) {
1177 keep_going = true;
1178 if (!relative->last_char) relative->last_char = '\n';
1179 }
1180 }
1181
1182 /*
1183 * This is mainly for the detail file reader. We allow zeros as end of "attr op value". But we
1184 * also treat zeros as "don't keep going".
1185 */
1186 if (relative->allow_zeros) {
1187 while (fr_sbuff_next_if_char(&our_in, '\0')) {
1188 /* nothing */
1189 }
1190
1191 goto done;
1192 }
1193
1194 /*
1195 * There's no more input, we're done. Any next attributes will cause the input to be parsed from
1196 * the root again.
1197 */
1198 (void) fr_sbuff_extend(&our_in);
1199 if (!fr_sbuff_remaining(&our_in)) goto done;
1200
1201 /*
1202 * STEP 9: If we need to keep going, then set up the relative references based on what we've
1203 * done, and go back to start over again.
1204 *
1205 * The caller is responsible for checking whether or not we have too much data.
1206 */
1207 if (keep_going) {
1208 /*
1209 * Update the relative list for parsing the next pair.
1210 */
1211 if (fr_type_is_leaf(vp->da->type)) {
1213
1215 if (!parent) {
1216 *relative = *root;
1217
1218 } else {
1219 relative->ctx = parent;
1220 relative->da = parent->da;
1221 relative->list = &parent->vp_group;
1222 }
1223
1224 } else {
1225 relative->ctx = vp;
1226 relative->da = vp->da;
1227 relative->list = &vp->vp_group;
1228 }
1229
1230 goto redo;
1231 }
1232
1233 /*
1234 * STEP 10: Complain if we have unexpected input.
1235 *
1236 * We have more input, BUT we didn't have a comma or CRLF to explicitly finish the last pair we
1237 * read. That's a problem.
1238 */
1239 if (!relative->last_char) {
1240 size_t remaining;
1241
1242 remaining = fr_sbuff_remaining(&our_in);
1243
1244 if (remaining > 20) remaining = 20;
1245
1246 fr_strerror_printf("Unexpected text '%.*s ...' after value",
1247 (int) remaining, fr_sbuff_current(&our_in));
1248 return fr_sbuff_error(&our_in); /* da_stack has already been cleaned */
1249 }
1250
1251done:
1252 /*
1253 * STEP 11: Finally done.
1254 */
1255 FR_SBUFF_SET_RETURN(in, &our_in);
1256}
1257
1258/** Read valuepairs from the fp up to End-Of-File.
1259 *
1260 * @param[in] ctx for talloc
1261 * @param[in] dict to resolve attributes in.
1262 * @param[in,out] out where the parsed fr_pair_ts will be appended.
1263 * @param[in] fp to read valuepairs from.
1264 * @param[out] pfiledone true if file parsing complete;
1265 * @param[in] allow_exec Whether we allow `backtick` expansions.
1266 * @return
1267 * - 0 on success
1268 * - -1 on error
1269 */
1270int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone, bool allow_exec)
1271{
1272 fr_pair_list_t tmp_list;
1273 fr_pair_parse_t root, relative;
1274 bool found = false;
1275 char buf[8192];
1276
1277 /*
1278 * Read all of the attributes on the current line.
1279 *
1280 * If we get nothing but an EOL, it's likely OK.
1281 */
1282 fr_pair_list_init(&tmp_list);
1283
1284 root = (fr_pair_parse_t) {
1285 .ctx = ctx,
1286 .da = fr_dict_root(dict),
1287 .list = &tmp_list,
1288 .dict = dict,
1289 .internal = fr_dict_internal(),
1290 .allow_crlf = true,
1291 .allow_compare = true,
1292 .allow_exec = allow_exec
1293 };
1294 relative = (fr_pair_parse_t) { };
1295
1296 while (fgets(buf, sizeof(buf), fp) != NULL) {
1297 /*
1298 * If we get a '\n' by itself, we assume that's
1299 * the end of that VP list.
1300 */
1301 if ((buf[0] == '\n') || (buf[0] == '\r')) {
1302 if (found) {
1303 *pfiledone = false;
1304 break;
1305 }
1306 continue;
1307 }
1308
1309 /*
1310 * Comments get ignored
1311 */
1312 if (buf[0] == '#') continue;
1313
1314 /*
1315 * Leave "relative" between calls, so that we can do:
1316 *
1317 * foo = {}
1318 * .bar = baz
1319 *
1320 * and get
1321 *
1322 * foo = { bar = baz }
1323 */
1324 if (fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN_STR(buf)) < 0) {
1325 *pfiledone = false;
1326 fr_pair_list_free(&tmp_list);
1327 return -1;
1328 }
1329
1330 found = true;
1331 }
1332
1333#ifdef WITH_VERIFY_PTR
1334 fr_pair_list_verify(__FILE__, __LINE__, ctx, &tmp_list, true);
1335#endif
1336
1337 fr_pair_list_append(out, &tmp_list);
1338
1339 *pfiledone = true;
1340 return 0;
1341}
1342
1343
1344/** Move pairs from source list to destination list respecting operator
1345 *
1346 * @note This function does some additional magic that's probably not needed in most places. Consider using
1347 * radius_legacy_map_cmp() and radius_legacy_map_apply() instead.
1348 *
1349 * @note fr_pair_list_free should be called on the head of the source list to free
1350 * unmoved attributes (if they're no longer needed).
1351 *
1352 * @param[in,out] to destination list.
1353 * @param[in,out] from source list.
1354 * @param[in] op operator for list move.
1355 */
1357{
1358 fr_pair_t *vp, *next, *found;
1359 fr_pair_list_t head_append, head_prepend;
1360
1361 if (!to || fr_pair_list_empty(from)) return;
1362
1363 /*
1364 * We're editing the "to" list while we're adding new
1365 * attributes to it. We don't want the new attributes to
1366 * be edited, so we create an intermediate list to hold
1367 * them during the editing process.
1368 */
1369 fr_pair_list_init(&head_append);
1370
1371 /*
1372 * Any attributes that are requested to be prepended
1373 * are added to a temporary list here
1374 */
1375 fr_pair_list_init(&head_prepend);
1376
1377 /*
1378 * We're looping over the "from" list, moving some
1379 * attributes out, but leaving others in place.
1380 */
1381 for (vp = fr_pair_list_head(from); vp != NULL; vp = next) {
1382 PAIR_VERIFY(vp);
1383 next = fr_pair_list_next(from, vp);
1384
1385 /*
1386 * We never move Fall-Through.
1387 */
1388 if (fr_dict_attr_is_top_level(vp->da) && (vp->da->attr == FR_FALL_THROUGH) &&
1390 continue;
1391 }
1392
1393 /*
1394 * Unlike previous versions, we treat all other
1395 * attributes as normal. i.e. there's no special
1396 * treatment for passwords or Hint.
1397 */
1398
1399 switch (vp->op) {
1400 /*
1401 * Anything else are operators which
1402 * shouldn't occur. We ignore them, and
1403 * leave them in place.
1404 */
1405 default:
1406 continue;
1407
1408 /*
1409 * Add it to the "to" list, but only if
1410 * it doesn't already exist.
1411 */
1412 case T_OP_EQ:
1413 found = fr_pair_find_by_da(to, NULL, vp->da);
1414 if (!found) goto do_add;
1415 continue;
1416
1417 /*
1418 * Add it to the "to" list, and delete any attribute
1419 * of the same vendor/attr which already exists.
1420 */
1421 case T_OP_SET:
1422 found = fr_pair_find_by_da(to, NULL, vp->da);
1423 if (!found) goto do_add;
1424
1425 /*
1426 * Delete *all* matching attributes.
1427 */
1428 fr_pair_delete_by_da(to, found->da);
1429 goto do_add;
1430
1431 /*
1432 * Move it from the old list and add it
1433 * to the new list.
1434 */
1435 case T_OP_ADD_EQ:
1436 do_add:
1437 fr_pair_remove(from, vp);
1438 fr_pair_append(&head_append, vp);
1439 continue;
1440
1441 case T_OP_PREPEND:
1442 fr_pair_remove(from, vp);
1443 fr_pair_prepend(&head_prepend, vp);
1444 continue;
1445 }
1446 } /* loop over the "from" list. */
1447
1448 /*
1449 * If the op parameter was prepend, add the "new list
1450 * attributes first as those whose individual operator
1451 * is prepend should be prepended to the resulting list
1452 */
1453 if (op == T_OP_PREPEND) fr_pair_list_prepend(to, &head_append);
1454
1455 /*
1456 * If there are any items in the prepend list prepend
1457 * it to the "to" list
1458 */
1459 fr_pair_list_prepend(to, &head_prepend);
1460
1461 /*
1462 * If the op parameter was not prepend, take the "new"
1463 * list, and append it to the "to" list.
1464 */
1465 if (op != T_OP_PREPEND) fr_pair_list_append(to, &head_append);
1466
1467 fr_pair_list_free(from);
1468}
static int const char char buffer[256]
Definition acutest.h:576
#define RCSID(id)
Definition build.h:506
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define unlikely(_x)
Definition build.h:402
#define NUM_ELEMENTS(_t)
Definition build.h:358
fr_dict_t const * fr_dict_by_da(fr_dict_attr_t const *da)
Attempt to locate the protocol dictionary containing an attribute.
Definition dict_util.c:2871
fr_dict_attr_t const * fr_dict_attr_common_parent(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
Find a common ancestor that two TLV type attributes share.
Definition dict_util.c:2313
bool const fr_dict_attr_allowed_chars[SBUFF_CHAR_CLASS]
Characters allowed in a single dictionary attribute name.
Definition dict_util.c:63
static fr_slen_t err
Definition dict.h:882
static fr_dict_attr_t * fr_dict_attr_unknown_afrom_oid(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, fr_sbuff_t *in, fr_type_t type)
Definition dict.h:618
fr_dict_attr_t * fr_dict_attr_unknown_alloc(TALLOC_CTX *ctx, fr_dict_attr_t const *da, fr_type_t type))
Allocate an unknown DA.
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2665
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4928
static bool fr_dict_attr_is_top_level(fr_dict_attr_t const *da)
Return true if this attribute is parented directly off the dictionary root.
Definition dict.h:809
#define FR_DICT_MAX_TLV_STACK
Maximum TLV stack size.
Definition dict.h:517
fr_dict_attr_err_t
Errors returned by attribute lookup functions.
Definition dict.h:317
@ FR_DICT_ATTR_OK
No error.
Definition dict.h:318
fr_slen_t fr_dict_oid_component(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_attr_t const *parent, fr_sbuff_t *in, fr_sbuff_term_t const *tt))
Parse an OID component, resolving it to a defined attribute.
Definition dict_util.c:2490
#define fr_dict_attr_is_key_field(_da)
Definition dict.h:170
static fr_slen_t in
Definition dict.h:882
static fr_dict_attr_t const * fr_dict_attr_ref(fr_dict_attr_t const *da)
Return the reference associated with a group type attribute.
Definition dict_ext.h:148
void fr_bio_shutdown & my
Definition fd_errno.h:70
free(array)
size_t fr_sbuff_out_unescape_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
fr_type_t
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
@ FR_TYPE_VSA
Vendor-Specific, for RADIUS attribute 26.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
long int ssize_t
unsigned char uint8_t
ssize_t fr_slen_t
int fr_pair_append_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and append)
Definition pair.c:1471
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:707
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1352
int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list.
Definition pair.c:1696
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition pair.c:956
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:290
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
int fr_pair_prepend(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the start of the list.
Definition pair.c:1321
fr_pair_t * fr_pair_find_last_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the last pair with a matching da.
Definition pair.c:731
static ssize_t fr_pair_value_from_substr(fr_pair_parse_t const *conf, fr_pair_t *vp, fr_sbuff_t *in)
static fr_sbuff_parse_rules_t const bareword_unquoted
Definition pair_legacy.c:90
static fr_table_num_sorted_t const pair_assignment_op_table[]
Definition pair_legacy.c:60
fr_slen_t fr_pair_list_afrom_substr(fr_pair_parse_t const *root, fr_pair_parse_t *relative, fr_sbuff_t *in)
Parse a fr_pair_list_t from a substring.
int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone, bool allow_exec)
Read valuepairs from the fp up to End-Of-File.
void fr_pair_list_move_op(fr_pair_list_t *to, fr_pair_list_t *from, fr_token_t op)
Move pairs from source list to destination list respecting operator.
static size_t pair_comparison_op_table_len
Definition pair_legacy.c:82
fr_pair_t * vp[FR_DICT_MAX_TLV_STACK]
which VP we have created or found
#define CLEAN_DA_STACK
fr_dict_attr_t const * da[FR_DICT_MAX_TLV_STACK]
parent for parsing
static fr_table_num_sorted_t const pair_comparison_op_table[]
Definition pair_legacy.c:67
static fr_sbuff_term_t const bareword_terminals
Definition pair_legacy.c:37
static ssize_t pair_assignment_op_table_len
Definition pair_legacy.c:65
Our version of a DA stack.
TALLOC_CTX * ctx
Definition pair_legacy.h:43
bool allow_crlf
allow CRLF, and treat like comma
Definition pair_legacy.h:51
bool allow_zeros
allow '\0' as end of attribute
Definition pair_legacy.h:52
fr_dict_t const * dict
the protocol dictionary we use
Definition pair_legacy.h:47
char last_char
last character we read - ',', ' ', or 0 for EOF
Definition pair_legacy.h:59
fr_pair_list_t * list
list where output is placed
Definition pair_legacy.h:45
bool end_of_list
do we expect an end of list '}' character?
Definition pair_legacy.h:60
bool allow_compare
allow comparison operators
Definition pair_legacy.h:50
fr_dict_attr_t const * da
root da to start parsing from
Definition pair_legacy.h:44
fr_dict_t const * internal
a cached pointer to the internal dictionary
Definition pair_legacy.h:48
#define fr_assert(_expr)
Definition rad_assert.h:37
static bool done
Definition radclient.c:80
#define WIFEXITED(stat_val)
Definition radiusd.c:66
#define WEXITSTATUS(stat_val)
Definition radiusd.c:63
static rs_t * conf
Definition radsniff.c:52
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static SBUFF_CHAR_CLASS], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition sbuff.c:1818
bool const sbuff_char_line_endings[SBUFF_CHAR_CLASS]
Definition sbuff.c:108
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2129
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define fr_sbuff_is_str_literal(_sbuff, _str)
#define fr_sbuff_set(_dst, _src)
#define SBUFF_CHAR_CLASS
Definition sbuff.h:203
#define fr_sbuff_diff(_a, _b)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_current(_sbuff_or_marker)
#define fr_sbuff_char(_sbuff_or_marker, _eob)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:190
#define fr_sbuff_extend(_sbuff_or_marker)
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define fr_sbuff_is_digit(_sbuff_or_marker)
#define FR_SBUFF_IN_STR(_start)
#define fr_sbuff_error(_sbuff_or_marker)
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_adv_past_blank(_sbuff, _len, _tt)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_remaining(_sbuff_or_marker)
#define fr_sbuff_used(_sbuff_or_marker)
#define FR_SBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
Set of terminal elements.
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:80
enum fr_token fr_token_t
@ T_INVALID
Definition token.h:37
@ T_OP_CMP_TRUE
Definition token.h:102
@ T_OP_EQ
Definition token.h:81
@ T_OP_SET
Definition token.h:82
@ T_OP_NE
Definition token.h:95
@ T_OP_ADD_EQ
Definition token.h:67
@ T_OP_CMP_FALSE
Definition token.h:103
@ T_OP_REG_EQ
Definition token.h:100
@ T_OP_CMP_EQ
Definition token.h:104
@ T_OP_LE
Definition token.h:98
@ T_OP_GE
Definition token.h:96
@ T_OP_GT
Definition token.h:97
@ T_OP_LT
Definition token.h:99
@ T_OP_REG_NE
Definition token.h:101
@ T_OP_PREPEND
Definition token.h:83
#define PAIR_ALLOCED(_x)
Definition pair.h:212
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
#define PAIR_VERIFY(_x)
Definition pair.h:204
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition pair_inline.c:69
fr_pair_t * fr_pair_list_tail(fr_pair_list_t const *list)
Get the tail of a valuepair list.
Definition pair_inline.c:55
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:93
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
void fr_pair_list_prepend(fr_pair_list_t *dst, fr_pair_list_t *src)
Move a list of fr_pair_t from a temporary list to the head of a destination list.
fr_pair_t * fr_pair_list_prev(fr_pair_list_t const *list, fr_pair_t const *item))
Get the previous item in a valuepair list before a specific entry.
Definition pair_inline.c:82
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:42
static fr_slen_t parent
Definition pair.h:858
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
#define FR_TYPE_STRUCTURAL_EXCEPT_GROUP
Definition types.h:315
#define fr_type_is_structural(_x)
Definition types.h:392
#define fr_type_is_leaf(_x)
Definition types.h:393
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
#define FR_TYPE_LEAF
Definition types.h:317
fr_sbuff_parse_rules_t const value_parse_rules_single_quoted
Definition value.c:558
fr_sbuff_unescape_rules_t const fr_value_unescape_backtick
Definition value.c:322
fr_sbuff_parse_rules_t const value_parse_rules_double_quoted
Definition value.c:552
ssize_t fr_value_box_from_substr(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *rules)
Convert string value to a fr_value_box_t type.
Definition value.c:5393
#define fr_box_strvalue_len(_val, _len)
Definition value.h:309
static size_t char ** out
Definition value.h:1030