Coverage Report

Created: 2026-04-12 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/server/tmpl_tokenize.c
Line
Count
Source
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: 5ccf6a018ac925214a5d62941119456b88039d83 $
19
 *
20
 * @brief #fr_pair_t template functions
21
 * @file src/lib/server/tmpl_tokenize.c
22
 *
23
 * @ingroup AVP
24
 *
25
 * @copyright 2014-2020 The FreeRADIUS server project
26
 */
27
RCSID("$Id: 5ccf6a018ac925214a5d62941119456b88039d83 $")
28
29
#define _TMPL_PRIVATE 1
30
31
#include <freeradius-devel/server/tmpl.h>
32
#include <freeradius-devel/server/base.h>
33
#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
34
35
#include <freeradius-devel/util/base16.h>
36
#include <freeradius-devel/util/skip.h>
37
38
/*
39
 *  For xlat_exp_head_alloc(), because xlat_copy() doesn't create an output head.
40
 */
41
#include <freeradius-devel/unlang/xlat_priv.h>
42
43
/** Define a global variable for specifying a default request reference
44
 *
45
 * @param[in] _name what the global variable should be called.
46
 * @param[in] _ref  one of the values of tmpl_request_ref_t
47
 *      - REQUEST_CURRENT
48
 *      - REQUEST_OUTER,
49
 *      - REQUEST_PARENT,
50
 *      - REQUEST_UNKNOWN
51
 */
52
#define TMPL_REQUEST_REF_DEF(_name, _ref) \
53
static tmpl_request_t _name ## _entry = { \
54
  .entry = { \
55
    .entry = { \
56
      .next = &_name.head.entry, \
57
      .prev = &_name.head.entry \
58
    } \
59
  }, \
60
  .request = _ref \
61
}; \
62
FR_DLIST_HEAD(tmpl_request_list) _name = { \
63
  .head = { \
64
    .offset = offsetof(tmpl_request_t, entry), \
65
    .entry = { \
66
      .next = &_name ## _entry.entry.entry, \
67
      .prev = &_name ## _entry.entry.entry, \
68
    }, \
69
    .num_elements = 1, \
70
  } \
71
}
72
73
/** Use the current request as the default
74
 *
75
 * Used as .attr.request_def = \&tmpl_request_def_current;
76
 */
77
TMPL_REQUEST_REF_DEF(tmpl_request_def_current, REQUEST_CURRENT);
78
79
/** Use the outer request as the default
80
 *
81
 * Used as .attr.request_def = \&tmpl_request_def_outer;
82
 */
83
TMPL_REQUEST_REF_DEF(tmpl_request_def_outer, REQUEST_OUTER);
84
85
/** Use the parent request as the default
86
 *
87
 * Used as .attr.request_def = \&tmpl_request_def_parent;
88
 */
89
TMPL_REQUEST_REF_DEF(tmpl_request_def_parent, REQUEST_PARENT);
90
91
/** Default parser rules
92
 *
93
 * Because this is getting to be a ridiculous number of parsing rules
94
 * to pass in via arguments.
95
 *
96
 * Defaults are used if a NULL rules pointer is passed to the parsing function.
97
 */
98
0
#define DEFAULT_RULES tmpl_rules_t default_rules = { .attr = { .list_def = request_attr_request }}
99
100
0
#define CHECK_T_RULES do { \
101
0
  if (!t_rules) { \
102
0
    t_rules = &default_rules; \
103
0
  } \
104
0
  } while (0)
105
106
107
/* clang-format off */
108
/** Map #tmpl_type_t values to descriptive strings
109
 */
110
fr_table_num_ordered_t const tmpl_type_table[] = {
111
  { L("uninitialised"),   TMPL_TYPE_UNINITIALISED   },
112
113
  { L("data"),      TMPL_TYPE_DATA      },
114
115
  { L("attr"),      TMPL_TYPE_ATTR      },
116
117
  { L("exec"),      TMPL_TYPE_EXEC      },
118
  { L("xlat"),      TMPL_TYPE_XLAT      },
119
120
  { L("regex"),     TMPL_TYPE_REGEX     },
121
  { L("regex-uncompiled"),  TMPL_TYPE_REGEX_UNCOMPILED  },
122
  { L("regex-xlat"),    TMPL_TYPE_REGEX_XLAT    },
123
124
  { L("data-unresolved"),   TMPL_TYPE_DATA_UNRESOLVED   },
125
  { L("attr-unresolved"),   TMPL_TYPE_ATTR_UNRESOLVED },
126
  { L("exec-unresolved"),   TMPL_TYPE_EXEC_UNRESOLVED },
127
  { L("xlat-unresolved"),   TMPL_TYPE_XLAT_UNRESOLVED },
128
  { L("regex-unresolved"),  TMPL_TYPE_REGEX_XLAT_UNRESOLVED }
129
};
130
size_t tmpl_type_table_len = NUM_ELEMENTS(tmpl_type_table);
131
132
/** Attr ref types
133
 */
134
static fr_table_num_ordered_t const attr_table[] = {
135
  { L("normal"),    TMPL_ATTR_TYPE_NORMAL   },
136
  { L("unspecified"), TMPL_ATTR_TYPE_UNSPEC },
137
  { L("unknown"),   TMPL_ATTR_TYPE_UNKNOWN    },
138
  { L("unresolved"),  TMPL_ATTR_TYPE_UNRESOLVED }
139
};
140
static size_t attr_table_len = NUM_ELEMENTS(attr_table);
141
142
/** We can print "current", but we shouldn't parse the "current" in a configuration.
143
 */
144
static fr_table_num_sorted_t const tmpl_request_ref_print_table[] = {
145
  { L("current"),   REQUEST_CURRENT     },
146
  { L("outer"),   REQUEST_OUTER     },
147
  { L("parent"),    REQUEST_PARENT      },
148
};
149
static size_t tmpl_request_ref_print_table_len = NUM_ELEMENTS(tmpl_request_ref_print_table);
150
151
/** Map keywords to #tmpl_request_ref_t values
152
 */
153
fr_table_num_sorted_t const tmpl_request_ref_table[] = {
154
  { L("outer"),   REQUEST_OUTER     },
155
  { L("parent"),    REQUEST_PARENT      },
156
};
157
size_t tmpl_request_ref_table_len = NUM_ELEMENTS(tmpl_request_ref_table);
158
159
160
/** Special attribute reference indexes
161
 */
162
static fr_table_num_sorted_t const attr_num_table[] = {
163
  { L("*"),   NUM_ALL       },
164
  { L("#"),   NUM_COUNT     },
165
  { L("u"),   NUM_UNSPEC      },
166
  { L("n"),   NUM_LAST      }
167
};
168
static size_t attr_num_table_len = NUM_ELEMENTS(attr_num_table);
169
/* clang-format on */
170
171
/*
172
 *  Can't use |= or ^= else we get out of range errors
173
 */
174
0
#define UNRESOLVED_SET(_flags) (*(_flags) = (*(_flags) | TMPL_FLAG_UNRESOLVED))
175
0
#define RESOLVED_SET(_flags) (*(_flags) = (*(_flags) & ~TMPL_FLAG_UNRESOLVED))
176
177
/** Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further chars
178
 *
179
 * @param[in] in  the sbuff to check.
180
 * @param[in] p_rules to use terminals from.
181
 * @return
182
 *  - true if substr is terminated correctly.
183
 *  - false if subst is not terminated correctly.
184
 */
185
static inline bool CC_HINT(always_inline) tmpl_substr_terminal_check(fr_sbuff_t *in,
186
                     fr_sbuff_parse_rules_t const *p_rules)
187
0
{
188
0
  fr_sbuff_marker_t m;
189
0
  bool      ret;
190
191
0
  if (!fr_sbuff_extend(in)) return true;   /* we're at the end of the string */
192
0
  if (!p_rules || !p_rules->terminals) return false; /* more stuff to parse but don't have a terminal set */
193
194
0
  fr_sbuff_marker(&m, in);
195
0
  ret = fr_sbuff_is_terminal(in, p_rules->terminals);
196
0
  fr_sbuff_set(in, &m);
197
0
  fr_sbuff_marker_release(&m);
198
0
  return ret;
199
0
}
200
201
void tmpl_attr_ref_debug(FILE *fp, const tmpl_attr_t *ar, int i)
202
0
{
203
0
  char buffer[sizeof(STRINGIFY(INT16_MAX)) + 1];
204
205
0
  snprintf(buffer, sizeof(buffer), "%i", ar->ar_num);
206
207
0
  switch (ar->type) {
208
0
  case TMPL_ATTR_TYPE_NORMAL:
209
0
  case TMPL_ATTR_TYPE_UNSPEC:
210
0
  case TMPL_ATTR_TYPE_UNKNOWN:
211
0
    if (!ar->da) {
212
0
      fprintf(fp, "\t[%u] %s null%s%s%s\n",
213
0
        i,
214
0
        fr_table_str_by_value(attr_table, ar->type, "<INVALID>"),
215
0
        ar->ar_num != NUM_UNSPEC ? "[" : "",
216
0
        ar->ar_num != NUM_UNSPEC ? fr_table_str_by_value(attr_num_table, ar->ar_num, buffer) : "",
217
0
        ar->ar_num != NUM_UNSPEC ? "]" : "");
218
0
      return;
219
0
    }
220
221
0
    fprintf(fp, "\t[%u] %s %s %s%s%s%s (%p) attr %u\n ",
222
0
      i,
223
0
      fr_table_str_by_value(attr_table, ar->type, "<INVALID>"),
224
0
      fr_type_to_str(ar->da->type),
225
0
      ar->da->name,
226
0
      ar->ar_num != NUM_UNSPEC ? "[" : "",
227
0
      ar->ar_num != NUM_UNSPEC ? fr_table_str_by_value(attr_num_table, ar->ar_num, buffer) : "",
228
0
      ar->ar_num != NUM_UNSPEC ? "]" : "",
229
0
      ar->da,
230
0
      ar->da->attr
231
0
    );
232
0
    fprintf(fp, "\t    is_raw     : %s\n", ar_is_raw(ar) ? "yes" : "no");
233
0
    fprintf(fp, "\t    is_unknown : %s\n", ar_is_unknown(ar) ? "yes" : "no");
234
0
    if (ar->ar_parent) fprintf(fp, "\t    parent     : %s (%p)\n", ar->ar_parent->name, ar->ar_parent);
235
0
    break;
236
237
238
0
  case TMPL_ATTR_TYPE_UNRESOLVED:
239
    /*
240
     *  Type reveals unresolved status
241
     *  so we don't need to add it explicitly
242
     */
243
0
    fprintf(fp, "\t[%u] %s %s%s%s%s\n",
244
0
      i,
245
0
      fr_table_str_by_value(attr_table, ar->type, "<INVALID>"),
246
0
      ar->ar_unresolved,
247
0
      ar->ar_num != NUM_UNSPEC ? "[" : "",
248
0
      ar->ar_num != NUM_UNSPEC ? fr_table_str_by_value(attr_num_table, ar->ar_num, buffer) : "",
249
0
      ar->ar_num != NUM_UNSPEC ? "]" : "");
250
0
    if (ar->ar_parent)       fprintf(fp, "\t    parent     : %s\n", ar->ar_parent->name);
251
0
    if (ar->ar_unresolved_namespace) fprintf(fp, "\t    namespace  : %s\n", ar->ar_unresolved_namespace->name);
252
0
    break;
253
254
0
  default:
255
0
    fprintf(fp, "\t[%u] Bad type %s(%u)\n",
256
0
           i, fr_table_str_by_value(attr_table, ar->type, "<INVALID>"), ar->type);
257
0
    break;
258
0
  }
259
0
}
260
261
void tmpl_attr_ref_list_debug(FILE *fp, FR_DLIST_HEAD(tmpl_attr_list) const *ar_head)
262
0
{
263
0
  tmpl_attr_t   *ar = NULL;
264
0
  unsigned int    i = 0;
265
266
0
  fprintf(fp, "attribute references:\n");
267
  /*
268
   *  Print all the attribute references
269
   */
270
0
  while ((ar = tmpl_attr_list_next(ar_head, ar))) {
271
0
    tmpl_attr_ref_debug(fp, ar, i);
272
0
    i++;
273
0
  }
274
0
}
275
276
void tmpl_attr_debug(FILE *fp, tmpl_t const *vpt)
277
0
{
278
0
  tmpl_request_t    *rr = NULL;
279
0
  unsigned int    i = 0;
280
281
0
  switch (vpt->type) {
282
0
  case TMPL_TYPE_ATTR:
283
0
  case TMPL_TYPE_ATTR_UNRESOLVED:
284
0
    break;
285
286
0
  default:
287
0
    fprintf(fp, "%s can't print tmpls of type %s\n", __FUNCTION__,
288
0
      tmpl_type_to_str(vpt->type));
289
0
    return;
290
0
  }
291
292
0
  fprintf(fp, "tmpl_t %s (%.8x) %pV (%p)\n",
293
0
    tmpl_type_to_str(vpt->type),
294
0
    vpt->type,
295
0
    fr_box_strvalue_len(vpt->name, vpt->len), vpt);
296
297
0
  fprintf(fp, "\tcast       : %s\n", fr_type_to_str(tmpl_rules_cast(vpt)));
298
0
  fprintf(fp, "\tquote      : %s\n", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
299
300
0
  fprintf(fp, "request references:");
301
302
  /*
303
   *  Print all the request references
304
   */
305
0
  while ((rr = tmpl_request_list_next(&vpt->data.attribute.rr, rr))) {
306
0
    fprintf(fp, "\t[%u] %s (%u)\n", i,
307
0
           fr_table_str_by_value(tmpl_request_ref_print_table, rr->request, "<INVALID>"), rr->request);
308
0
    i++;
309
0
  }
310
311
0
  fprintf(fp, "list: %s\n", tmpl_list_name(tmpl_list(vpt), "<INVALID>"));
312
0
  tmpl_attr_ref_list_debug(fp, tmpl_attr(vpt));
313
0
}
314
315
void tmpl_debug(FILE *fp, tmpl_t const *vpt)
316
0
{
317
0
  switch (vpt->type) {
318
0
  case TMPL_TYPE_ATTR:
319
0
  case TMPL_TYPE_ATTR_UNRESOLVED:
320
0
    tmpl_attr_debug(fp, vpt);
321
0
    return;
322
323
0
  default:
324
0
    break;
325
0
  }
326
327
0
  fprintf(fp, "tmpl_t %s (%.8x) %s (%p)\n",
328
0
    tmpl_type_to_str(vpt->type),
329
0
    vpt->type,
330
0
    vpt->name, vpt);
331
332
0
  fprintf(fp, "\tcast       : %s\n", fr_type_to_str(tmpl_rules_cast(vpt)));
333
0
  fprintf(fp, "\tquote      : %s\n", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
334
0
  switch (vpt->type) {
335
0
  case TMPL_TYPE_DATA:
336
0
    fprintf(fp, "\ttype       : %s\n", fr_type_to_str(tmpl_value_type(vpt)));
337
0
    fprintf(fp, "\tlen        : %zu\n", tmpl_value_length(vpt));
338
0
    fprintf(fp, "\tvalue      : %pV\n", tmpl_value(vpt));
339
340
0
    if (tmpl_value_enumv(vpt)) fprintf(fp, "\tenumv      : %s (%p)",
341
0
               tmpl_value_enumv(vpt)->name, tmpl_value_enumv(vpt));
342
0
    return;
343
344
0
  case TMPL_TYPE_XLAT:
345
0
  case TMPL_TYPE_EXEC:
346
0
  case TMPL_TYPE_REGEX_XLAT:
347
0
  {
348
0
    char *str;
349
350
0
    xlat_aprint(NULL, &str, tmpl_xlat(vpt), NULL);
351
352
0
    fprintf(fp, "\texpansion  : %s\n", str);
353
354
0
    talloc_free(str);
355
0
  }
356
0
    break;
357
358
0
  case TMPL_TYPE_REGEX:
359
0
  {
360
0
    fprintf(fp, "\tpattern    : %s\n", vpt->name);
361
0
  }
362
0
    break;
363
364
0
  default:
365
0
    if (tmpl_needs_resolving(vpt)) {
366
0
      if (tmpl_is_data_unresolved(vpt)) {
367
0
        fprintf(fp, "\tunescaped  : %s\n", vpt->data.unescaped);
368
0
        fprintf(fp, "\tlen        : %zu\n", talloc_strlen(vpt->data.unescaped));
369
0
      } else {
370
0
        fprintf(fp, "\tunresolved : %s\n", vpt->name);
371
0
        fprintf(fp, "\tlen        : %zu\n", vpt->len);
372
0
      }
373
0
    } else {
374
0
      fprintf(fp, "debug nyi\n");
375
0
    }
376
0
    break;
377
0
  }
378
0
}
379
380
/** @name Parse list and request qualifiers to #fr_pair_list_t and #tmpl_request_ref_t values
381
 *
382
 * These functions also resolve #fr_pair_list_t and #tmpl_request_ref_t values to #request_t
383
 * structs and the head of #fr_pair_t lists in those structs.
384
 *
385
 * For adding new #fr_pair_t to the lists, the #tmpl_list_ctx function can be used
386
 * to obtain the appropriate TALLOC_CTX pointer.
387
 *
388
 * @note These don't really have much to do with #tmpl_t. They're in the same
389
 *  file as they're used almost exclusively by the tmpl_* functions.
390
 * @{
391
 */
392
393
/** Parse one a single list reference
394
 *
395
 * @param[out] da_p attribute representing a list.
396
 * @param[in] in  Sbuff to read request references from.
397
 * @return
398
 *  - > 0 the number of bytes parsed.
399
 *      - 0 no list qualifier found.
400
 */
401
fr_slen_t tmpl_attr_list_from_substr(fr_dict_attr_t const **da_p, fr_sbuff_t *in)
402
0
{
403
0
  fr_dict_attr_t const *da;
404
0
  fr_sbuff_t our_in = FR_SBUFF(in);
405
406
0
  if (((fr_sbuff_adv_past_strcase(&our_in, request_attr_request->name, request_attr_request->name_len)) &&
407
0
       (da = request_attr_request)) ||
408
0
      ((fr_sbuff_adv_past_strcase(&our_in, request_attr_reply->name, request_attr_reply->name_len)) &&
409
0
       (da = request_attr_reply)) ||
410
0
      ((fr_sbuff_adv_past_strcase(&our_in, request_attr_control->name, request_attr_control->name_len)) &&
411
0
       (da = request_attr_control)) ||
412
0
      ((fr_sbuff_adv_past_strcase(&our_in, request_attr_state->name, request_attr_state->name_len)) &&
413
0
       (da = request_attr_state))) {
414
    /* note: no local variables */
415
0
    *da_p = da;
416
0
    FR_SBUFF_SET_RETURN(in, &our_in);
417
0
  }
418
419
0
  return 0;
420
0
}
421
422
 /** Allocate a new request reference and add it to the end of the attribute reference list
423
 *
424
 */
425
static inline CC_HINT(always_inline) CC_HINT(nonnull(2,3))
426
void tmpl_request_ref_list_copy(TALLOC_CTX *ctx,
427
              FR_DLIST_HEAD(tmpl_request_list) *out, FR_DLIST_HEAD(tmpl_request_list) const *in)
428
{
429
  tmpl_request_t  *rr = NULL;
430
  tmpl_request_t  *n_rr = NULL;
431
432
  /*
433
   *  Duplicate the complete default list
434
   */
435
  while ((rr = tmpl_request_list_next(in, rr))) {
436
    MEM(n_rr = talloc(ctx, tmpl_request_t));
437
    *n_rr = (tmpl_request_t){
438
      .request = rr->request
439
    };
440
    tmpl_request_list_insert_tail(out, n_rr);
441
    ctx = n_rr; /* Chain the contexts */
442
  }
443
}
444
445
 /** Allocate a new request reference list and copy request references into it
446
 *
447
 */
448
static inline CC_HINT(always_inline) CC_HINT(nonnull(2,3))
449
void tmpl_request_ref_list_acopy(TALLOC_CTX *ctx,
450
               FR_DLIST_HEAD(tmpl_request_list) **out, FR_DLIST_HEAD(tmpl_request_list) const *in)
451
{
452
  FR_DLIST_HEAD(tmpl_request_list) *rql;
453
454
  MEM(rql = talloc_zero(ctx, FR_DLIST_HEAD(tmpl_request_list)));
455
  tmpl_request_list_talloc_init(rql);
456
457
  tmpl_request_ref_list_copy(rql, rql, in);
458
459
  *out = rql;
460
}
461
462
/** Dump a request list to stderr
463
 *
464
 */
465
void tmpl_request_ref_list_debug(FR_DLIST_HEAD(tmpl_request_list) const *rql)
466
0
{
467
0
  tmpl_request_t *rr = NULL;
468
469
0
  while ((rr = tmpl_request_list_next(rql, rr))) {
470
0
    FR_FAULT_LOG("request - %s (%u)",
471
0
           fr_table_str_by_value(tmpl_request_ref_print_table, rr->request, "<INVALID>"),
472
0
           rr->request);
473
0
  }
474
0
}
475
476
/** Compare a list of request qualifiers
477
 *
478
 * @param[in] a   first list.  If NULL tmpl_request_def_current will be used.
479
 * @param[in] b   second list.  If NULL tmpl_request_def_current will be used.
480
 * @return
481
 *  - >0 a > b
482
 *  - 0 a == b
483
 *  - <0 a < b
484
 */
485
int8_t tmpl_request_ref_list_cmp(FR_DLIST_HEAD(tmpl_request_list) const *a, FR_DLIST_HEAD(tmpl_request_list) const *b)
486
0
{
487
0
  tmpl_request_t *a_rr = NULL, *b_rr = NULL;
488
489
  /*
490
   *  NULL, uninit, empty are all equivalent
491
   *  to tmpl_request_def_current.
492
   *
493
   *  We need all these equivalent checks to
494
   *  deal with uninitialised tmpl rules.
495
   */
496
0
  if (!a || !tmpl_request_list_initialised(a) || tmpl_request_list_empty(a)) a = &tmpl_request_def_current;
497
0
  if (!b || !tmpl_request_list_initialised(b) || tmpl_request_list_empty(b)) b = &tmpl_request_def_current;
498
499
  /*
500
   *  Fast path...
501
   */
502
0
  if (a == b) return 0;
503
504
0
  for (;;) {
505
0
    a_rr = tmpl_request_list_next(a, a_rr);
506
0
    b_rr = tmpl_request_list_next(b, b_rr);
507
508
0
    if (!a_rr || !b_rr) return CMP(tmpl_request_list_num_elements(a), tmpl_request_list_num_elements(b));
509
510
0
    CMP_RETURN(a_rr, b_rr, request);
511
0
  }
512
0
}
513
514
static fr_dict_attr_t const *tmpl_namespace(tmpl_rules_t const *t_rules)
515
0
{
516
0
  if (!t_rules) {
517
0
    return NULL;
518
0
  }
519
520
0
  if (t_rules->attr.namespace) {
521
0
    if (request_attr_is_list(t_rules->attr.namespace)) {
522
0
      return NULL;
523
0
    }
524
525
0
    if (t_rules->attr.namespace->type != FR_TYPE_GROUP) {
526
0
      return t_rules->attr.namespace;
527
0
    }
528
529
0
    if (t_rules->attr.namespace->flags.local) {
530
0
      return t_rules->attr.namespace;
531
0
    }
532
533
0
    if (t_rules->attr.namespace->flags.internal && t_rules->attr.dict_def) {
534
0
      return fr_dict_root(t_rules->attr.dict_def);
535
0
    }
536
537
0
    return fr_dict_attr_ref(t_rules->attr.namespace);
538
0
  }
539
540
0
  if (t_rules->attr.dict_def) {
541
0
    return fr_dict_root(t_rules->attr.dict_def);
542
0
  }
543
544
0
  return NULL;
545
0
}
546
547
/** Parse one or more request references, writing the list to out
548
 *
549
 * @param[in] ctx to allocate request refs in.
550
 * @param[out] err  If !NULL where to write the parsing error.
551
 * @param[in] out The list to write to.
552
 * @param[in] in  Sbuff to read request references from.
553
 * @param[in] t_rules Default list and other rules.
554
 * @param[out] namespace the namespace to use
555
 * @return
556
 *  - >= 0 the number of bytes parsed.
557
 *      - <0 negative offset for where the error occurred
558
 */
559
static fr_slen_t  CC_HINT(nonnull(1,3,4,6)) tmpl_request_ref_list_from_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
560
                    FR_DLIST_HEAD(tmpl_request_list) *out,
561
                    fr_sbuff_t *in,
562
                    tmpl_rules_t const *t_rules,
563
                    fr_dict_attr_t const **namespace)
564
0
{
565
0
  tmpl_request_ref_t  ref;
566
0
  tmpl_request_t    *rr;
567
0
  fr_sbuff_t    our_in = FR_SBUFF(in);
568
0
  tmpl_request_t    *tail = tmpl_request_list_tail(out);
569
0
  fr_sbuff_marker_t m;
570
571
  /*
572
   *  The caller needs to know the default namespace for resolving the attribute.
573
   *
574
   *  But the first round can't have "namespace" set to the root, otherwise things complain.
575
   */
576
0
  *namespace = tmpl_namespace(t_rules);
577
0
  if (*namespace && (*namespace)->flags.is_root) *namespace = NULL;
578
579
  /*
580
   *  We could make the caller do this but as this
581
   *  function is intended to help populate tmpl rules,
582
   *  just be nice...
583
   */
584
0
  if (!tmpl_request_list_initialised(out)) tmpl_request_list_talloc_init(out);
585
586
  /*
587
   *  We're in a name space, OR lists are forbidden, don't allow list qualifiers.
588
   */
589
0
  if (*namespace || (t_rules && (t_rules->attr.list_presence == TMPL_ATTR_LIST_FORBID))) {
590
0
    if (fr_sbuff_is_str_literal(&our_in, "outer.") ||
591
0
        fr_sbuff_is_str_literal(&our_in, "parent.")) {
592
0
      fr_strerror_const("request list qualifiers are not allowed here");
593
0
      if (err) *err = TMPL_ATTR_ERROR_LIST_NOT_ALLOWED;
594
595
0
      fr_sbuff_set(&our_in, in);  /* Marker at the start */
596
0
  error:
597
0
      tmpl_request_list_talloc_free_to_tail(out, tail);
598
0
      FR_SBUFF_ERROR_RETURN(&our_in);
599
0
    }
600
601
0
    return 0;
602
0
  }
603
604
  /*
605
   *  See if there is a known reference.
606
   */
607
0
  fr_sbuff_marker(&m, &our_in);
608
0
  if (fr_sbuff_adv_past_str_literal(&our_in, "outer.")) {
609
0
    ref = REQUEST_OUTER;
610
611
0
  } else if (fr_sbuff_adv_past_str_literal(&our_in, "parent.")) {
612
0
    ref = REQUEST_PARENT;
613
614
0
  } else {
615
    /*
616
     *  No recognized string.  Set the default list if it was specified.
617
     */
618
0
    if (t_rules && t_rules->attr.request_def) tmpl_request_ref_list_copy(ctx, out, t_rules->attr.request_def);
619
620
0
    return 0;
621
0
  }
622
623
  /*
624
   *  Add a new entry to the dlist
625
   */
626
0
  MEM(rr = talloc(ctx, tmpl_request_t));
627
0
  *rr = (tmpl_request_t){
628
0
    .request = ref
629
0
  };
630
0
  tmpl_request_list_insert_tail(out, rr);
631
632
0
  if (ref == REQUEST_OUTER) {
633
    /*
634
     *  No parent?  Guess.
635
     *
636
     *  If there is a parent, we use the outermost one.
637
     */
638
0
    if (!t_rules || !t_rules->parent) {
639
0
      t_rules = NULL;
640
641
0
    } else while (t_rules->parent) {
642
0
      t_rules = t_rules->parent;
643
0
    }
644
645
0
  } else {
646
0
    int depth = 1;
647
648
0
    if (t_rules) t_rules = t_rules->parent;
649
650
0
    while (fr_sbuff_adv_past_str_literal(&our_in, "parent.")) {
651
0
      if (t_rules) t_rules = t_rules->parent;
652
0
      depth++;
653
654
      /*
655
       *  Nesting level too deep
656
       */
657
0
      if (depth > TMPL_MAX_REQUEST_REF_NESTING) {
658
0
        fr_strerror_const("Request ref nesting too deep");
659
0
        if (err) *err = TMPL_ATTR_ERROR_NESTING_TOO_DEEP;
660
0
        goto error; /* Leave marker at the end */
661
0
      }
662
663
0
      MEM(rr = talloc(ctx, tmpl_request_t));
664
0
      *rr = (tmpl_request_t){
665
0
        .request = ref
666
0
      };
667
0
      tmpl_request_list_insert_tail(out, rr);
668
0
    }
669
0
  }
670
671
  /*
672
   *  If we mix and match the references, that's wrong.
673
   */
674
0
  if (fr_sbuff_is_str_literal(&our_in, "outer.") || fr_sbuff_is_str_literal(&our_in, "parent.")) {
675
0
    if (err) *err = TMPL_ATTR_ERROR_INVALID_REQUEST_REF;
676
0
    fr_strerror_const("Invalid list reference - cannot mix 'outer' and 'parent' references");
677
0
    goto error;
678
0
  }
679
680
  /*
681
   *  Now that we have the correct set of tmpl_rules, update the namespace to match.
682
   *
683
   *  This can have "namespace" set to a dict root, because it is not _our_ dict root. It is an
684
   *  outer / parent one.
685
   */
686
0
  *namespace = tmpl_namespace(t_rules);
687
688
0
  FR_SBUFF_SET_RETURN(in, &our_in);
689
0
}
690
691
/** Parse one or more request references, allocing a new list and adding the references to it
692
 *
693
 * This can be used to create request ref lists for rules and for tmpls.
694
 *
695
 * @param[in] ctx to allocate request refs in.
696
 * @param[out] err  If !NULL where to write the parsing error.
697
 * @param[out] out  The new list.
698
 * @param[in] in  Sbuff to read request references from.
699
 * @return
700
 *  - >= 0 the number of bytes parsed.
701
 *      - <0 negative offset for where the error occurred
702
 */
703
fr_slen_t tmpl_request_ref_list_afrom_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
704
               FR_DLIST_HEAD(tmpl_request_list) **out,
705
               fr_sbuff_t *in)
706
0
{
707
0
  fr_slen_t slen;
708
0
  fr_dict_attr_t const *namespace;
709
710
0
  FR_DLIST_HEAD(tmpl_request_list) *rql;
711
712
0
  MEM(rql = talloc_zero(ctx, FR_DLIST_HEAD(tmpl_request_list)));
713
0
  tmpl_request_list_talloc_init(rql);
714
715
0
  slen = tmpl_request_ref_list_from_substr(rql, err, rql, in, NULL, &namespace);
716
0
  if (slen < 0) {
717
0
    talloc_free(rql);
718
0
    return slen;
719
0
  }
720
721
0
  *out = rql;
722
723
0
  return slen;
724
0
}
725
/** @} */
726
727
/** @name Alloc or initialise #tmpl_t
728
 *
729
 * @note Should not usually be called outside of tmpl_* functions, use one of
730
 *  the tmpl_*from_* functions instead.
731
 * @{
732
 */
733
734
/** Initialise fields inside a tmpl depending on its type
735
 *
736
 */
737
static inline CC_HINT(always_inline) void tmpl_type_init(tmpl_t *vpt, tmpl_type_t type)
738
0
{
739
740
0
  switch (type) {
741
#ifndef HAVE_REGEX
742
  case TMPL_TYPE_REGEX:
743
  case TMPL_TYPE_REGEX_UNCOMPILED:
744
  case TMPL_TYPE_REGEX_XLAT:
745
  case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
746
    fr_assert(0);
747
    return;
748
#endif
749
750
0
  case TMPL_TYPE_ATTR:
751
0
  case TMPL_TYPE_ATTR_UNRESOLVED:
752
0
    tmpl_attr_list_talloc_init(tmpl_attr(vpt));
753
0
    tmpl_request_list_talloc_init(&vpt->data.attribute.rr);
754
0
    break;
755
756
0
  default:
757
0
    break;
758
0
  }
759
0
  vpt->type = type;
760
0
 }
761
762
/** Set the name on a pre-initialised tmpl
763
 *
764
 * @param[in] vpt to set the name for.
765
 * @param[in] quote Original quoting around the name.
766
 * @param[in] fmt string.
767
 * @param[in] ... format arguments.
768
 */
769
void tmpl_set_name_printf(tmpl_t *vpt, fr_token_t quote, char const *fmt, ...)
770
0
{
771
0
  va_list   ap;
772
0
  char const  *old = NULL;
773
774
0
  if (vpt->type != TMPL_TYPE_UNINITIALISED) old = vpt->name;
775
776
0
  va_start(ap, fmt);
777
0
  vpt->name = fr_vasprintf(vpt, fmt, ap);
778
0
  vpt->quote = quote;
779
0
  vpt->len = talloc_strlen(vpt->name);
780
0
  va_end(ap);
781
782
0
  talloc_const_free(old); /* Free name last so it can be used in the format string */
783
0
}
784
785
/** Set the name on a pre-initialised tmpl
786
 *
787
 * @param[in] vpt to set the name for.
788
 * @param[in] quote Original quoting around the name.
789
 * @param[in] name  of the #tmpl_t.
790
 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
791
 *      If < 0 strlen will be used to determine the length.
792
 */
793
void tmpl_set_name_shallow(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
794
0
{
795
0
  fr_assert(vpt->type != TMPL_TYPE_UNINITIALISED);
796
797
0
  vpt->name = name;
798
0
  vpt->len = len < 0 ? strlen(name) : (size_t)len;
799
0
  vpt->quote = quote;
800
0
}
801
802
/** Set the name on a pre-initialised tmpl
803
 *
804
 * @param[in] vpt to set the name for.
805
 * @param[in] quote Original quoting around the name.
806
 * @param[in] name  of the #tmpl_t.
807
 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
808
 *      If < 0 strlen will be used to determine the length.
809
 */
810
void tmpl_set_name(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
811
0
{
812
0
  fr_assert(vpt->type != TMPL_TYPE_UNINITIALISED);
813
814
0
  talloc_const_free(vpt->name);
815
816
0
  vpt->name = talloc_bstrndup(vpt, name, len < 0 ? strlen(name) : (size_t)len);
817
0
  vpt->len = talloc_strlen(vpt->name);
818
0
  vpt->quote = quote;
819
0
}
820
821
/** Change the default dictionary in the tmpl's resolution rules
822
 *
823
 * @param[in] vpt to alter.
824
 * @param[in] dict  to set.
825
 */
826
void tmpl_set_dict_def(tmpl_t *vpt, fr_dict_t const *dict)
827
0
{
828
0
  vpt->rules.attr.dict_def = dict;
829
0
}
830
831
/** Set escape parameters for the tmpl output
832
 *
833
 * @param[in] vpt to alter.
834
 * @param[in] escape  to set.
835
 */
836
void tmpl_set_escape(tmpl_t *vpt, tmpl_escape_t const *escape)
837
0
{
838
0
  vpt->rules.escape = *escape;
839
0
}
840
841
/** Change the default dictionary in the tmpl's resolution rules
842
 *
843
 * @param[in] vpt to alter.
844
 * @param[in] xlat  to set.
845
 */
846
void tmpl_set_xlat(tmpl_t *vpt, xlat_exp_head_t *xlat)
847
0
{
848
0
  fr_assert((vpt->type == TMPL_TYPE_XLAT) || (vpt->type == TMPL_TYPE_EXEC));
849
850
0
  tmpl_xlat(vpt) = xlat;
851
0
}
852
853
854
/** Initialise a tmpl using a format string to create the name
855
 *
856
 * @param[in] vpt to initialise.
857
 * @param[in] type  of tmpl to initialise.
858
 * @param[in] quote Original quoting around the name.
859
 * @param[in] fmt string.
860
 * @param[in] ... format arguments.
861
 * @return A pointer to the newly initialised tmpl.
862
 */
863
tmpl_t *tmpl_init_printf(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *fmt, ...)
864
0
{
865
0
  va_list   ap;
866
867
0
  memset(vpt, 0, sizeof(*vpt));
868
0
  tmpl_type_init(vpt, type);
869
870
0
  va_start(ap, fmt);
871
0
  vpt->name = fr_vasprintf(vpt, fmt, ap);
872
0
  vpt->len = talloc_strlen(vpt->name);
873
0
  vpt->quote = quote;
874
0
  va_end(ap);
875
876
0
  return vpt;
877
0
}
878
879
/** Initialise a tmpl without copying the input name string
880
 *
881
 * @note Name is not talloc_strdup'd or memcpy'd so must be available, and must not change
882
 *  for the lifetime of the #tmpl_t.
883
 *
884
 * @param[out] vpt  to initialise.
885
 * @param[in] type  to set in the #tmpl_t.
886
 * @param[in] quote The type of quoting around the template name.
887
 * @param[in] name  of the #tmpl_t.
888
 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
889
 *      If < 0 strlen will be used to determine the length.
890
 * @param[in] t_rules used during parsing.
891
 * @return a pointer to the initialised #tmpl_t. The same value as vpt.
892
 */
893
tmpl_t *tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote,
894
        char const *name, ssize_t len, tmpl_rules_t const *t_rules)
895
0
{
896
0
  memset(vpt, 0, sizeof(*vpt));
897
0
  tmpl_type_init(vpt, type);
898
0
  tmpl_set_name_shallow(vpt, quote, name, len);
899
0
  if (t_rules) vpt->rules = *t_rules;
900
901
0
  return vpt;
902
0
}
903
904
/** Initialise a tmpl using a literal string to create the name
905
 *
906
 * @param[in] vpt to initialise.
907
 * @param[in] type  of tmpl to initialise.
908
 * @param[in] quote Original quoting around the name.
909
 * @param[in] name  to set for the tmpl.
910
 * @param[in] len Name length.  If < 0 strlen will be used
911
 *      to determine the name.
912
 * @param[in] t_rules used during parsing.
913
 * @return A pointer to the newly initialised tmpl.
914
 */
915
tmpl_t *tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote,
916
      char const *name, ssize_t len, tmpl_rules_t const *t_rules)
917
0
{
918
0
  memset(vpt, 0, sizeof(*vpt));
919
0
  tmpl_type_init(vpt, type);
920
0
  tmpl_set_name(vpt, quote, name, len);
921
0
  if (t_rules) vpt->rules = *t_rules;
922
923
0
  return vpt;
924
0
}
925
926
/** Create a new heap allocated #tmpl_t
927
 *
928
 * Must be later initialised with a tmpl_init_* function.
929
 *
930
 * This function is provided to allow tmpls to be pre-allocated for talloc purposes before
931
 * their name is known.
932
 */
933
static inline CC_HINT(always_inline) tmpl_t *tmpl_alloc_null(TALLOC_CTX *ctx)
934
{
935
  tmpl_t *vpt;
936
937
  /*
938
   *  Allocate enough memory to hold at least
939
   *      one attribute reference and one request
940
   *  reference.
941
   */
942
  MEM(vpt = talloc_pooled_object(ctx, tmpl_t, 2, sizeof(tmpl_request_t) + sizeof(tmpl_attr_t)));
943
  vpt->type = TMPL_TYPE_UNINITIALISED;
944
945
  return vpt;
946
}
947
948
/** Create a new heap allocated #tmpl_t
949
 *
950
 * @param[in,out] ctx to allocate in.
951
 * @param[in] type to set in the #tmpl_t.
952
 * @param[in] name of the #tmpl_t (will be copied to a new talloc buffer parented
953
 *  by the #tmpl_t).
954
 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
955
 *  If < 0 strlen will be used to determine the length.
956
 * @param[in] quote The type of quoting around the template name.
957
 * @return the newly allocated #tmpl_t.
958
 */
959
tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
960
0
{
961
0
  tmpl_t *vpt;
962
963
0
  vpt = tmpl_alloc_null(ctx);
964
0
  memset(vpt, 0, sizeof(*vpt));
965
966
0
  tmpl_type_init(vpt, type);
967
0
  if (name) tmpl_set_name(vpt, quote, name, len);
968
969
0
  return vpt;
970
0
}
971
/** @} */
972
973
/** @name Create new #tmpl_t from a string
974
 *
975
 * @{
976
 */
977
978
/** Allocate a new attribute reference and add it to the end of the attribute reference list
979
 *
980
 */
981
static tmpl_attr_t *tmpl_attr_add(tmpl_t *vpt, tmpl_attr_type_t type)
982
{
983
  tmpl_attr_t *ar;
984
  TALLOC_CTX  *ctx;
985
986
  if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) == 0) {
987
    ctx = vpt;
988
  } else {
989
    ctx = tmpl_attr_list_tail(tmpl_attr(vpt));
990
  }
991
992
  MEM(ar = talloc(ctx, tmpl_attr_t));
993
  *ar = (tmpl_attr_t){
994
    .type = type,
995
    .filter = {
996
      .type = TMPL_ATTR_FILTER_TYPE_NONE,
997
      .num = NUM_UNSPEC
998
    }
999
  };
1000
  tmpl_attr_list_insert_tail(tmpl_attr(vpt), ar);
1001
1002
  return ar;
1003
}
1004
1005
/** Create a #tmpl_t from a #fr_value_box_t
1006
 *
1007
 * @param[in,out] ctx to allocate #tmpl_t in.
1008
 * @param[out] out  Where to write pointer to new #tmpl_t.
1009
 * @param[in] data  to convert.
1010
 * @param[in] steal If true, any buffers are moved to the new
1011
 *      ctx instead of being duplicated.
1012
 * @return
1013
 *  - 0 on success.
1014
 *  - -1 on failure.
1015
 */
1016
int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bool steal)
1017
0
{
1018
0
  char    *name;
1019
0
  fr_slen_t slen;
1020
0
  tmpl_t    *vpt;
1021
0
  fr_token_t  quote = (data->type == FR_TYPE_STRING) ? T_SINGLE_QUOTED_STRING : T_BARE_WORD;
1022
1023
0
  MEM(vpt = talloc(ctx, tmpl_t));
1024
0
  slen = fr_value_box_aprint(vpt, &name, data, fr_value_escape_by_quote[quote]);
1025
0
  if (slen < 0) {
1026
0
  error:
1027
0
    talloc_free(vpt);
1028
0
    return -1;
1029
0
  }
1030
1031
0
  tmpl_init_shallow(vpt, TMPL_TYPE_DATA, quote, name, slen, NULL);
1032
1033
0
  if (steal) {
1034
0
    if (fr_value_box_steal(vpt, tmpl_value(vpt), data) < 0) goto error;
1035
0
  } else {
1036
0
    if (unlikely(fr_value_box_copy(vpt, tmpl_value(vpt), data) < 0)) goto error;
1037
0
  }
1038
0
  *out = vpt;
1039
1040
0
  return 0;
1041
0
}
1042
1043
/** Copy a list of attribute and request references from one tmpl to another
1044
 *
1045
 */
1046
int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
1047
{
1048
  tmpl_attr_t *src_ar = NULL, *dst_ar;
1049
1050
  /*
1051
   *  Clear any existing attribute references
1052
   */
1053
  if (tmpl_attr_list_num_elements(tmpl_attr(dst)) > 0) tmpl_attr_list_talloc_reverse_free(tmpl_attr(dst));
1054
1055
  while ((src_ar = tmpl_attr_list_next(tmpl_attr(src), src_ar))) {
1056
    dst_ar = tmpl_attr_add(dst, src_ar->type);
1057
1058
    switch (src_ar->type) {
1059
    case TMPL_ATTR_TYPE_NORMAL:
1060
      dst_ar->ar_da = src_ar->ar_da;
1061
      break;
1062
1063
    case TMPL_ATTR_TYPE_UNSPEC: /* Nothing to copy */
1064
      break;
1065
1066
    case TMPL_ATTR_TYPE_UNKNOWN:
1067
      dst_ar->ar_unknown = fr_dict_attr_unknown_copy(dst_ar, src_ar->ar_unknown);
1068
      break;
1069
1070
    case TMPL_ATTR_TYPE_UNRESOLVED:
1071
      dst_ar->ar_unresolved = talloc_bstrdup(dst_ar, src_ar->ar_unresolved);
1072
      break;
1073
1074
    default:
1075
      if (!fr_cond_assert(0)) return -1;
1076
    }
1077
    dst_ar->ar_num = src_ar->ar_num;
1078
    dst_ar->ar_filter_type = src_ar->ar_filter_type;
1079
    dst_ar->parent = src_ar->parent;
1080
  }
1081
1082
  /*
1083
   *  Clear any existing request references
1084
   *  and copy the ones from the source.
1085
   */
1086
  tmpl_request_list_talloc_reverse_free(&dst->data.attribute.rr);
1087
  tmpl_request_ref_list_copy(dst, &dst->data.attribute.rr, &src->data.attribute.rr);
1088
1089
  /*
1090
   *  Ensure that we copy over any parsing rules, defaults, etc.
1091
   */
1092
  dst->rules = src->rules;
1093
1094
  TMPL_ATTR_VERIFY(dst);
1095
1096
  return 0;
1097
}
1098
1099
/** Replace the current attribute reference
1100
 *
1101
 */
1102
int tmpl_attr_set_da(tmpl_t *vpt, fr_dict_attr_t const *da)
1103
0
{
1104
0
  tmpl_attr_t *ref;
1105
1106
0
  (void)talloc_get_type_abort_const(da, fr_dict_attr_t);
1107
1108
  /*
1109
   *  Clear any existing references
1110
   */
1111
0
  if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) {
1112
0
    tmpl_attr_list_talloc_reverse_free(tmpl_attr(vpt));
1113
0
  }
1114
1115
  /*
1116
   *  Unknown attributes get copied
1117
   */
1118
0
  if (da->flags.is_unknown) {
1119
0
    ref = tmpl_attr_add(vpt, TMPL_ATTR_TYPE_UNKNOWN);
1120
0
    ref->da = ref->ar_unknown = fr_dict_attr_unknown_copy(vpt, da);
1121
0
  } else {
1122
0
    ref = tmpl_attr_add(vpt, TMPL_ATTR_TYPE_NORMAL);
1123
0
    ref->da = da;
1124
0
  }
1125
0
  ref->ar_parent = fr_dict_root(fr_dict_by_da(da)); /* Parent is the root of the dictionary */
1126
1127
0
  TMPL_ATTR_VERIFY(vpt);
1128
1129
0
  return 0;
1130
0
}
1131
1132
/** Replace the leaf attribute only
1133
 *
1134
 */
1135
int tmpl_attr_set_leaf_da(tmpl_t *vpt, fr_dict_attr_t const *da)
1136
0
{
1137
0
  tmpl_attr_t *ref, *parent = NULL;
1138
1139
0
  tmpl_assert_type(tmpl_is_attr(vpt));
1140
0
  (void)talloc_get_type_abort_const(da, fr_dict_attr_t);
1141
1142
  /*
1143
   *  Clear any existing references
1144
   */
1145
0
  if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) {
1146
0
    if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 1) {
1147
0
      ref = tmpl_attr_list_tail(tmpl_attr(vpt));
1148
0
      parent = tmpl_attr_list_prev(tmpl_attr(vpt), ref);
1149
1150
0
      if (!fr_dict_attr_common_parent(parent->ar_da, da, true)) {
1151
0
        fr_strerror_const("New leaf da and old leaf da do not share the same ancestor");
1152
0
        return -1;
1153
0
      }
1154
0
    } else {
1155
0
      ref = tmpl_attr_list_tail(tmpl_attr(vpt));
1156
0
    }
1157
1158
    /*
1159
     *  Free old unknown and unresolved attributes...
1160
     */
1161
0
    talloc_free_children(ref);
1162
1163
    /*
1164
     *
1165
     */
1166
0
    ref->ar_filter_type = TMPL_ATTR_FILTER_TYPE_NONE;
1167
0
    ref->ar_num = NUM_UNSPEC;
1168
1169
0
  } else {
1170
0
    ref = tmpl_attr_add(vpt, da->flags.is_unknown ? TMPL_ATTR_TYPE_UNKNOWN : TMPL_ATTR_TYPE_NORMAL);
1171
0
  }
1172
1173
1174
  /*
1175
   *  Unknown attributes get copied
1176
   */
1177
0
  if (da->flags.is_unknown) {
1178
0
    ref->da = ref->ar_unknown = fr_dict_attr_unknown_copy(vpt, da);
1179
0
  } else {
1180
0
    ref->da = da;
1181
0
  }
1182
1183
  /*
1184
   *  FIXME - Should be calculated from existing ar
1185
   */
1186
0
  ref->ar_parent = fr_dict_root(fr_dict_by_da(da)); /* Parent is the root of the dictionary */
1187
1188
0
  TMPL_ATTR_VERIFY(vpt);
1189
1190
0
  return 0;
1191
0
}
1192
1193
/** Rewrite the leaf's instance number
1194
 *
1195
 *  This function is _only_ called from the compiler, for "update" and "foreach" keywords.  In those cases,
1196
 *  the user historically did "foo-bar", but really meant "foo-bar[*]".  We silently update that for
1197
 *  "update" sections, and complain about it in "foreach" sections.
1198
 *
1199
 *  As the server now supports multiple types of leaf references, we do the rewrite _only_ from "none" (no
1200
 *  filter), OR where it's a numerical index, AND the index hasn't been specified.
1201
 */
1202
void tmpl_attr_rewrite_leaf_num(tmpl_t *vpt, int16_t to)
1203
0
{
1204
0
  tmpl_attr_t *ref = NULL;
1205
1206
0
  tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_attr_unresolved(vpt));
1207
1208
0
  if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) == 0) return;
1209
1210
0
  ref = tmpl_attr_list_tail(tmpl_attr(vpt));
1211
1212
0
  if (ref->ar_filter_type == TMPL_ATTR_FILTER_TYPE_NONE) {
1213
0
    ref->ar_filter_type = TMPL_ATTR_FILTER_TYPE_INDEX;
1214
0
    ref->ar_num = to;
1215
1216
0
  } else if (ref->ar_filter_type != TMPL_ATTR_FILTER_TYPE_INDEX) {
1217
0
    return;
1218
1219
0
  } else if (ref->ar_num == NUM_UNSPEC) {
1220
0
    ref->ar_num = to;
1221
0
  }
1222
1223
0
  TMPL_ATTR_VERIFY(vpt);
1224
0
}
1225
1226
/** Set the request for an attribute ref
1227
 *
1228
 */
1229
void tmpl_attr_set_request_ref(tmpl_t *vpt, FR_DLIST_HEAD(tmpl_request_list) const *request_def)
1230
0
{
1231
0
  fr_assert_msg(tmpl_is_attr(vpt), "Expected tmpl type 'attr', got '%s'",
1232
0
          tmpl_type_to_str(vpt->type));
1233
1234
  /*
1235
   *  Clear any existing request references
1236
   */
1237
0
  tmpl_request_list_talloc_reverse_free(&vpt->data.attribute.rr);
1238
0
  tmpl_request_ref_list_copy(vpt, &vpt->data.attribute.rr, request_def);
1239
1240
0
  TMPL_ATTR_VERIFY(vpt);
1241
0
}
1242
1243
void tmpl_attr_set_list(tmpl_t *vpt, fr_dict_attr_t const *list)
1244
0
{
1245
0
  tmpl_attr_t *ref = tmpl_attr_list_head(tmpl_attr(vpt));
1246
0
  if (tmpl_attr_is_list_attr(ref)) ref->da = list;
1247
1248
0
  TMPL_ATTR_VERIFY(vpt);
1249
0
}
1250
1251
/** Create a new tmpl from a list tmpl and a da
1252
 *
1253
 */
1254
int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_dict_attr_t const *da)
1255
0
{
1256
0
  tmpl_t *vpt;
1257
0
  tmpl_attr_t *ar;
1258
1259
0
  char attr[256];
1260
0
  ssize_t slen;
1261
1262
0
  MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
1263
1264
  /*
1265
   *  Copies request refs and the list ref
1266
   */
1267
0
  tmpl_attr_copy(vpt, list);
1268
0
  tmpl_attr_set_list(vpt, tmpl_list(list));
1269
1270
0
  if (da->flags.is_unknown) {
1271
0
    ar = tmpl_attr_add(vpt, TMPL_ATTR_TYPE_UNKNOWN);
1272
0
    ar->da = ar->ar_unknown = fr_dict_attr_unknown_copy(vpt, da);
1273
0
  } else {
1274
0
    ar = tmpl_attr_add(vpt, TMPL_ATTR_TYPE_NORMAL);
1275
0
    ar->ar_da = da;
1276
0
  }
1277
1278
0
  ar->ar_parent = fr_dict_root(fr_dict_by_da(da));
1279
1280
  /*
1281
   *  We need to rebuild the attribute name, to be the
1282
   *  one we copied from the source list.
1283
   */
1284
0
  slen = tmpl_print(&FR_SBUFF_OUT(attr, sizeof(attr)), vpt,
1285
0
        fr_value_escape_by_quote[list->quote]);
1286
0
  if (slen < 0) {
1287
0
    fr_strerror_printf("Serialized attribute too long.  Must be < "
1288
0
           STRINGIFY(sizeof(attr)) " bytes, got %zu bytes", (size_t)-slen);
1289
0
    talloc_free(vpt);
1290
0
    return -1;
1291
0
  }
1292
1293
0
  vpt->len = (size_t)slen;
1294
0
  vpt->name = talloc_strdup(vpt, attr);
1295
0
  vpt->quote = T_BARE_WORD;
1296
1297
0
  TMPL_ATTR_VERIFY(vpt);
1298
1299
0
  *out = vpt;
1300
1301
0
  return 0;
1302
0
}
1303
/** @} */
1304
1305
/** Insert an attribute reference into a tmpl
1306
 *
1307
 * Not all attribute references can be used to create new attributes,
1308
 * for example those accessing instance > 0 or those that resolve
1309
 * to special indexes.
1310
 *
1311
 * We mark up these references and their parents as resolve only
1312
 * meaning that if any code needs to use a reference chain to build
1313
 * out a pair tree, it bails out early.
1314
 *
1315
 * @param[in] vpt containing the reference list.
1316
 * @param[in] ar  to insert and check.
1317
 */
1318
static inline CC_HINT(always_inline) void tmpl_attr_insert(tmpl_t *vpt, tmpl_attr_t *ar)
1319
0
{
1320
  /*
1321
   *  Insert the reference into the list.
1322
   */
1323
0
  tmpl_attr_list_insert_tail(tmpl_attr(vpt), ar);
1324
1325
0
  switch (ar->ar_num) {
1326
0
  case 0:
1327
0
  case NUM_UNSPEC:
1328
0
    break;
1329
1330
0
  default:
1331
0
    ar->resolve_only = true;
1332
0
    while ((ar = tmpl_attr_list_prev(tmpl_attr(vpt), ar))) ar->resolve_only = true;
1333
0
    break;
1334
0
  }
1335
0
}
1336
1337
/** Parse array subscript and in future other filters
1338
 *
1339
 * @param[out] err  Parse error code.
1340
 * @param[in] ar  to populate filter for.
1341
 * @param[in] name  containing more attribute ref data.
1342
 * @param[in] at_rules  see tmpl_attr_afrom_attr_substr.
1343
 * @return
1344
 *  - >0 if a filter was parsed.
1345
 *  - 0 if no filter was available.
1346
 *  - <0 on filter parse error.
1347
 */
1348
static fr_slen_t tmpl_attr_parse_filter(tmpl_attr_error_t *err, tmpl_attr_t *ar,
1349
          fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1350
0
{
1351
0
  fr_sbuff_t our_name = FR_SBUFF(name);
1352
1353
  /*
1354
   *  Parse array subscript (and eventually complex filters)
1355
   */
1356
0
  if (!fr_sbuff_next_if_char(&our_name, '[')) return 0;
1357
1358
0
  if (at_rules->disallow_filters || tmpl_attr_is_list_attr(ar)) {
1359
0
    fr_strerror_const("Filters not allowed here");
1360
0
    if (err) *err = TMPL_ATTR_ERROR_FILTER_NOT_ALLOWED;
1361
0
    fr_sbuff_set_to_start(&our_name);
1362
0
    FR_SBUFF_ERROR_RETURN(&our_name);
1363
0
  }
1364
1365
0
  ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_INDEX;
1366
0
  fr_sbuff_switch(&our_name, '\0') {
1367
0
  case '#':
1368
0
    ar->ar_num = NUM_COUNT;
1369
0
    fr_sbuff_next(&our_name);
1370
0
    break;
1371
1372
0
  case '*':
1373
0
    ar->ar_num = NUM_ALL;
1374
0
    fr_sbuff_next(&our_name);
1375
0
    break;
1376
1377
0
  case '0':
1378
0
  case '1':
1379
0
  case '2':
1380
0
  case '3':
1381
0
  case '4':
1382
0
  case '5':
1383
0
  case '6':
1384
0
  case '7':
1385
0
  case '8':
1386
0
  case '9':
1387
0
  {
1388
0
    ssize_t rcode;
1389
0
    fr_sbuff_parse_error_t  sberr = FR_SBUFF_PARSE_OK;
1390
0
    fr_sbuff_t tmp = FR_SBUFF(&our_name);
1391
1392
    /*
1393
     *  All digits (not hex).
1394
     */
1395
0
    rcode = fr_sbuff_out(&sberr, &ar->ar_num, &tmp);
1396
0
    if ((rcode < 0) || !fr_sbuff_is_char(&tmp, ']')) goto parse_tmpl;
1397
1398
0
    if ((ar->ar_num > 1000) || (ar->ar_num < 0)) {
1399
0
      fr_strerror_printf("Invalid array index '%hi' (should be between 0-1000)", ar->ar_num);
1400
0
      ar->ar_num = 0;
1401
0
      goto error;
1402
0
    }
1403
1404
0
    fr_sbuff_set(&our_name, &tmp);  /* Advance name _AFTER_ doing checks */
1405
0
    break;
1406
0
  }
1407
1408
0
  case '"':
1409
0
  case '\'':
1410
0
  case '`':
1411
0
  case '/':
1412
0
    fr_strerror_const("Invalid data type for array index");
1413
0
    goto error;
1414
1415
  /* Used as EOB here */
1416
0
  missing_closing:
1417
0
  case '\0':
1418
0
    fr_strerror_const("No closing ']' for array index");
1419
0
  error:
1420
0
    if (err) *err = TMPL_ATTR_ERROR_INVALID_ARRAY_INDEX;
1421
0
    FR_SBUFF_ERROR_RETURN(&our_name);
1422
1423
0
  case '(':   /* (...) expression */
1424
0
  {
1425
0
    fr_sbuff_t tmp = FR_SBUFF(&our_name);
1426
0
    fr_slen_t slen;
1427
0
    tmpl_rules_t t_rules;
1428
0
    fr_sbuff_parse_rules_t p_rules;
1429
0
    fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1430
1431
1432
0
    tmp = FR_SBUFF(&our_name);
1433
0
    t_rules = (tmpl_rules_t) {};
1434
0
    t_rules.attr = *at_rules;
1435
1436
    /*
1437
     *  Unspecified child, we can create a filter starting from the children.
1438
     *
1439
     *  @todo - When parsing the condition, we need to ensure that the condition contains a
1440
     *  reference to the current cursor, and we need to decide what that syntax is.
1441
     */
1442
0
    if (ar->type == TMPL_ATTR_TYPE_UNSPEC) {
1443
0
      if (at_rules->dict_def) t_rules.attr.namespace = fr_dict_root(at_rules->dict_def);
1444
1445
0
    } else {
1446
0
      if (!ar->ar_da || !fr_type_is_structural(ar->ar_da->type)) {
1447
0
        fr_strerror_printf("Invalid filter - cannot use filter on leaf attributes");
1448
0
        ar->ar_num = 0;
1449
0
        goto error;
1450
0
      }
1451
0
      t_rules.attr.namespace = ar->ar_da;
1452
0
    }
1453
1454
0
    p_rules = (fr_sbuff_parse_rules_t) {
1455
0
      .terminals = &filter_terminals,
1456
0
      .escapes = NULL
1457
0
    };
1458
1459
    /*
1460
     *  Check if it's a condition.
1461
     */
1462
0
    slen = xlat_tokenize_condition(ar, &ar->ar_cond, &tmp, &p_rules, &t_rules);
1463
0
    if (slen < 0) goto error;
1464
1465
0
    if (xlat_impure_func(ar->ar_cond)) {
1466
0
      fr_strerror_const("Condition in attribute index cannot depend on functions which call external databases");
1467
0
      goto error;
1468
0
    }
1469
1470
0
    ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_CONDITION;
1471
0
    fr_sbuff_set(&our_name, &tmp);  /* Advance name _AFTER_ doing checks */
1472
0
    break;
1473
0
  }
1474
1475
0
  case '%':   /* ${...} expansion */
1476
0
  {
1477
0
    fr_sbuff_t tmp = FR_SBUFF(&our_name);
1478
0
    fr_slen_t slen;
1479
0
    tmpl_rules_t t_rules;
1480
0
    fr_sbuff_parse_rules_t p_rules;
1481
0
    fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1482
1483
0
    if (!fr_sbuff_is_str(&our_name, "%{", 2)) {
1484
0
      fr_strerror_const("Invalid expression in attribute index");
1485
0
      goto error;
1486
0
    }
1487
1488
0
    tmp = FR_SBUFF(&our_name);
1489
0
    t_rules = (tmpl_rules_t) {};
1490
0
    t_rules.attr = *at_rules;
1491
1492
0
    p_rules = (fr_sbuff_parse_rules_t) {
1493
0
      .terminals = &filter_terminals,
1494
0
      .escapes = NULL
1495
0
    };
1496
1497
    /*
1498
     *  Check if it's an expression.
1499
     */
1500
0
    slen = xlat_tokenize_expression(ar, &ar->ar_expr, &tmp, &p_rules, &t_rules);
1501
0
    if (slen < 0) goto error;
1502
1503
0
    if (xlat_impure_func(ar->ar_expr)) {
1504
0
      fr_strerror_const("Expression in attribute index cannot depend on functions which call external databases");
1505
0
      goto error;
1506
0
    }
1507
1508
0
    ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_EXPR;
1509
1510
0
    fr_sbuff_set(&our_name, &tmp);  /* Advance name _AFTER_ doing checks */
1511
0
    break;
1512
0
  }
1513
1514
0
  case 'n':
1515
    /*
1516
     *  [n] is the last one
1517
     *
1518
     *  [nope] is a reference to "nope".
1519
     */
1520
0
    if (fr_sbuff_is_str(&our_name, "n]", 2)) {
1521
0
      ar->ar_num = NUM_LAST;
1522
0
      fr_sbuff_next(&our_name);
1523
0
      break;
1524
0
    }
1525
0
    FALL_THROUGH;
1526
1527
0
  default:
1528
0
  parse_tmpl:
1529
0
  {
1530
0
    fr_sbuff_t tmp = FR_SBUFF(&our_name);
1531
0
    ssize_t slen;
1532
0
    tmpl_rules_t t_rules;
1533
0
    fr_sbuff_parse_rules_t p_rules;
1534
0
    fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1535
1536
0
    tmp = FR_SBUFF(&our_name);
1537
0
    t_rules = (tmpl_rules_t) {};
1538
0
    t_rules.attr = *at_rules;
1539
1540
    /*
1541
     *  Don't reset namespace, we always want to start searching from the top level of the
1542
     *  dictionaries.
1543
     */
1544
1545
0
    p_rules = (fr_sbuff_parse_rules_t) {
1546
0
      .terminals = &filter_terminals,
1547
0
      .escapes = NULL
1548
0
    };
1549
1550
    /*
1551
     *  @todo - for some reason, the tokenize_condition code allows for internal
1552
     *  vs protocol vs local attributes, whereas the tmpl function only accepts
1553
     *  internal ones.
1554
     */
1555
0
    slen = tmpl_afrom_substr(ar, &ar->ar_tmpl, &tmp, T_BARE_WORD, &p_rules, &t_rules);
1556
0
    if (slen <= 0) goto error;
1557
1558
0
    if (!tmpl_is_attr(ar->ar_tmpl)) {
1559
0
      fr_strerror_printf("Invalid array index '%s'", ar->ar_tmpl->name);
1560
0
      goto error;
1561
0
    }
1562
1563
    /*
1564
     *  Arguably we _could_ say &User-Name["foo"] matches all user-name with value "foo",
1565
     *  but that would confuse the issue for &Integer-Thing[4].
1566
     *
1567
     *  For matching therefore, we really need to have a way to define "self".
1568
     */
1569
0
    if (!fr_type_numeric[tmpl_attr_tail_da(ar->ar_tmpl)->type]) {
1570
0
      fr_strerror_const("Invalid data type for array index (must be numeric)");
1571
0
      goto error;
1572
0
    }
1573
1574
0
    ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_TMPL;
1575
0
    fr_sbuff_set(&our_name, &tmp);  /* Advance name _AFTER_ doing checks */
1576
0
    break;
1577
0
  }
1578
0
  }
1579
1580
  /*
1581
   *  Always advance here, so the error
1582
   *  marker points to the bad char.
1583
   */
1584
0
  if (!fr_sbuff_next_if_char(&our_name, ']')) goto missing_closing;
1585
1586
0
  FR_SBUFF_SET_RETURN(name, &our_name);
1587
0
}
1588
1589
extern fr_dict_attr_t const *tmpl_attr_unspec;
1590
1591
static inline CC_HINT(nonnull(3,4))
1592
fr_slen_t tmpl_attr_ref_from_unspecified_substr(tmpl_attr_t *ar, tmpl_attr_error_t *err,
1593
            tmpl_t *vpt,
1594
            fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1595
0
{
1596
0
  fr_slen_t slen;
1597
1598
0
  *ar = (tmpl_attr_t){
1599
0
    .ar_num = NUM_UNSPEC, /* May be changed by tmpl_attr_parse_filter */
1600
0
    .ar_type = TMPL_ATTR_TYPE_UNSPEC,
1601
0
    .ar_da = tmpl_attr_unspec,
1602
0
  };
1603
1604
0
  slen = tmpl_attr_parse_filter(err, ar, name, at_rules);
1605
0
  if (slen < 0) {
1606
0
    return slen;
1607
1608
  /*
1609
   * No filters and no previous elements is the equivalent of '&'
1610
   * which is not allowed.
1611
   *
1612
   * &[<filter>] is allowed as this lets us perform filtering operations
1613
   * at the root.
1614
   */
1615
0
  } else if ((slen == 0) && (tmpl_attr_num_elements(vpt) == 0)) {
1616
0
    fr_strerror_const("Invalid attribute name");
1617
0
    if (err) *err = TMPL_ATTR_ERROR_INVALID_NAME;
1618
0
    return -1;
1619
0
  }
1620
1621
0
  tmpl_attr_insert(vpt,  ar);
1622
1623
0
  return slen;
1624
0
}
1625
1626
/** Parse an unresolved attribute, i.e. one which can't be found in the current dictionary
1627
 *
1628
 * This function calls itself recursively to process additional OID
1629
 * components once we've failed to resolve one component.
1630
 *
1631
 * @note Do not call directly.
1632
 *
1633
 * @param[in] ctx   to allocate new attribute reference in.
1634
 * @param[out] err    Parse error.
1635
 * @param[in,out] vpt   to append this reference to.
1636
 * @param[in] parent    Last known parent.
1637
 * @param[in] namespace   in which the attribute will be resolved.
1638
 * @param[in] name    to parse.
1639
 * @param[in] at_rules    see tmpl_attr_afrom_attr_substr.
1640
 * @return
1641
 *  - <0 on error.
1642
 *  - 0 on success.
1643
 */
1644
static inline CC_HINT(nonnull(3,6))
1645
fr_slen_t tmpl_attr_ref_afrom_unresolved_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
1646
            tmpl_t *vpt,
1647
            fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace,
1648
            fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1649
0
{
1650
0
  tmpl_attr_t   *ar = NULL, *ar_curr;
1651
0
  fr_sbuff_t    our_name = FR_SBUFF(name);
1652
0
  fr_slen_t   slen;
1653
0
  char      *unresolved;
1654
1655
  /*
1656
   *  Point we free from if something goes wrong.
1657
   */
1658
0
  ar_curr = tmpl_attr_list_tail(tmpl_attr(vpt));
1659
0
  for (;;) {
1660
0
    MEM(ar = talloc(ctx, tmpl_attr_t));
1661
    /*
1662
    * Copy out a string of allowed dictionary chars to form
1663
    * the unresolved attribute name.
1664
    *
1665
    * This will be resolved later (outside of this function).
1666
    */
1667
0
    slen = fr_sbuff_out_abstrncpy_allowed(ar, &unresolved,
1668
0
                  &our_name, FR_DICT_ATTR_MAX_NAME_LEN + 1,
1669
0
                  fr_dict_attr_allowed_chars);
1670
0
    if (slen == 0) {
1671
0
      slen = tmpl_attr_ref_from_unspecified_substr(ar, err, vpt, &our_name, at_rules);
1672
0
      if (slen < 0) {
1673
0
        fr_sbuff_advance(&our_name, +slen);
1674
0
      error:
1675
0
        talloc_free(ar);
1676
0
        tmpl_attr_list_talloc_free_to_tail(tmpl_attr(vpt), ar_curr);
1677
0
        return -1;
1678
0
      }
1679
0
      return fr_sbuff_set(name, &our_name);
1680
0
    } else if (slen > FR_DICT_ATTR_MAX_NAME_LEN) {
1681
0
      fr_strerror_const("Attribute name is too long");
1682
0
      if (err) *err = TMPL_ATTR_ERROR_INVALID_NAME;
1683
0
      goto error;
1684
0
    }
1685
1686
0
    *ar = (tmpl_attr_t){
1687
0
      .ar_num = NUM_UNSPEC,
1688
0
      .ar_type = TMPL_ATTR_TYPE_UNRESOLVED,
1689
0
      .ar_unresolved = unresolved,
1690
0
      .ar_unresolved_namespace = namespace,
1691
0
      .ar_parent = parent
1692
0
    };
1693
1694
0
    if (tmpl_attr_parse_filter(err, ar, &our_name, at_rules) < 0) goto error;
1695
1696
    /*
1697
    * Insert the ar into the list of attribute references
1698
    */
1699
0
    tmpl_attr_insert(vpt, ar);
1700
1701
    /*
1702
    * Once one OID component is created as unresolved all
1703
    * future OID components are also unresolved.
1704
    */
1705
0
    if (!fr_sbuff_next_if_char(&our_name, '.')) break;
1706
0
  }
1707
1708
  /*
1709
   *  Mark the tmpl up as an unresolved attribute reference
1710
   *  the attribute reference will be resolved later.
1711
   */
1712
0
  vpt->type = TMPL_TYPE_ATTR_UNRESOLVED;
1713
1714
0
  return fr_sbuff_set(name, &our_name);
1715
0
}
1716
1717
/*
1718
 *  Add attr_ref when we've parsed an intermediate dictionary name
1719
 *  which is itself a ref.
1720
 */
1721
static void tmpl_attr_ref_fixup(TALLOC_CTX *ctx, tmpl_t *vpt, fr_dict_attr_t const *da, fr_dict_attr_t const *parent)
1722
{
1723
  tmpl_attr_t *ar;
1724
1725
  if (tmpl_attr_tail_da(vpt) == da) return;
1726
1727
  if (da->parent != parent) tmpl_attr_ref_fixup(ctx, vpt, da->parent, parent);
1728
1729
  MEM(ar = talloc(ctx, tmpl_attr_t));
1730
  *ar = (tmpl_attr_t) {
1731
    .ar_num = NUM_UNSPEC,
1732
    .ar_type = TMPL_ATTR_TYPE_NORMAL,
1733
    .ar_da = da,
1734
    .ar_parent = da->parent,
1735
  };
1736
1737
  tmpl_attr_insert(vpt, ar);
1738
}
1739
1740
/** Parse an attribute reference, either an OID or attribute name
1741
 *
1742
 * @note Do not call directly.
1743
 *
1744
 * @param[in] ctx   to allocate new attribute reference in.
1745
 * @param[out] err    Parse error.
1746
 * @param[in,out] vpt   to append this reference to.
1747
 * @param[in] parent    Parent where the attribute will be placed (group, struct, tlv, etc).
1748
 * @param[in] namespace   Where the child attribute will be parsed from (dict root, struct member, TLV child, etc)
1749
 * @param[in] name    to parse.
1750
 * @param[in] p_rules   Formatting rules used to check for trailing garbage.
1751
 * @param[in] at_rules    which places constraints on attribute reference parsing.
1752
 *        Rules interpreted by this function is:
1753
 *        - allow_unknown - If false unknown OID components
1754
 *          result in a parse error.
1755
 *        - allow_unresolved - If false unknown attribute names
1756
 *          result in a parse error.
1757
 *        - allow_foreign - If an attribute resolves in a dictionary
1758
 *          that does not match the parent
1759
 *          (exception being FR_TYPE_GROUP) then that results
1760
 *          in a parse error.
1761
 * @param[in] depth   How deep we are.  Used to check for maximum nesting level.
1762
 * @return
1763
 *  - <0 on error.
1764
 *  - 0 on success.
1765
 */
1766
static int tmpl_attr_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
1767
               tmpl_t *vpt,
1768
               fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace,
1769
               fr_sbuff_t *name,
1770
               fr_sbuff_parse_rules_t const *p_rules, tmpl_attr_rules_t const *at_rules,
1771
               unsigned int depth)
1772
0
{
1773
0
  uint32_t    oid = 0;
1774
0
  tmpl_attr_t   *ar = NULL;
1775
0
  fr_dict_attr_t const  *da;
1776
0
  fr_sbuff_marker_t m_s;
1777
0
  fr_dict_attr_err_t  dict_err;
1778
0
  fr_dict_attr_t const  *our_parent = parent;
1779
1780
0
  fr_sbuff_marker(&m_s, name);
1781
1782
0
  if (depth > FR_DICT_MAX_TLV_STACK) {
1783
0
    fr_strerror_const("Attribute nesting too deep");
1784
0
    if (err) *err = TMPL_ATTR_ERROR_NESTING_TOO_DEEP;
1785
0
  error:
1786
0
    talloc_free(ar);
1787
0
    fr_sbuff_marker_release(&m_s);
1788
0
    FR_SBUFF_ERROR_RETURN(name);
1789
0
  }
1790
1791
  /*
1792
   *  Input too short
1793
   */
1794
0
  if (!fr_sbuff_extend(name)) {
1795
0
    fr_strerror_const("Missing attribute reference");
1796
0
    if (err) *err = TMPL_ATTR_ERROR_INVALID_NAME;
1797
0
    goto error;
1798
0
  }
1799
1800
  /*
1801
   *  Maybe there's no child namespace (struct member, tlv child, etc).  In which case we must
1802
   *  search from the default dictionary root.
1803
   *
1804
   *  This search is probably wrong in some cases.  See the comments below around FR_TYPE_GROUP.
1805
   *
1806
   *  If we change out the dictionaries, we should arguably also change dict_def in the
1807
   *  tmpl_attr_rules_t.  On top of that, the "dict_attr_search" functions take a #fr_dict_t
1808
   *  pointer, and not a pointer to the dict root.  So we can't pass them a namespace.
1809
   */
1810
0
  if (!namespace) {
1811
0
    fr_assert(parent == NULL);
1812
1813
0
    (void)fr_dict_attr_search_by_qualified_name_substr(&dict_err, &da,
1814
0
                   at_rules->dict_def,
1815
0
                   name, p_rules ? p_rules->terminals : NULL,
1816
0
                   true,
1817
0
                   at_rules->allow_foreign);
1818
    /*
1819
     *  The attribute was found either in the dict_def root, OR in the internal root, OR if
1820
     *  !dict_def && allow_foreign, in some other dictionary root.
1821
     *
1822
     *  Otherwise we're still not sure what the attribute is.  It may end up being an
1823
     *  unresolved one.
1824
     */
1825
0
    if (da) {
1826
0
      our_parent = da->parent;
1827
1828
0
      if (!our_parent->flags.is_root) {
1829
0
        tmpl_attr_ref_fixup(ctx, vpt, our_parent, fr_dict_root(da->dict));
1830
0
      }
1831
0
    }
1832
0
  } else {
1833
0
    fr_assert(parent != NULL);
1834
1835
    /*
1836
     *  Otherwise we're resolving the next piece in the context of where-ever we ended up from
1837
     *  parsing the last bit.
1838
     *
1839
     *  The "parent" could be the same as "namespace", if both are at a dictionary root, OR
1840
     *  both are from a struct / tlv attribute.
1841
1842
     *  Or, "parent" could be a grouping attribute (e.g. request), and "namespace" could be
1843
     *  the dictionary root.
1844
     */
1845
0
    (void)fr_dict_attr_by_name_substr(&dict_err,
1846
0
              &da,
1847
0
              namespace,
1848
0
              name,
1849
0
              p_rules ? p_rules->terminals : NULL);
1850
1851
    /*
1852
     *  Allow fallback to internal attributes
1853
     *  if the parent was a group, and we're
1854
     *  allowing internal resolution.
1855
     *
1856
     *  Discard any errors here... It's more
1857
     *  useful to have the original.
1858
     */
1859
0
    if (!da) {
1860
0
      ar = tmpl_attr_list_tail(&vpt->data.attribute.ar);
1861
0
      if (!ar || ((ar->type == TMPL_ATTR_TYPE_NORMAL) && (ar->ar_da->type == FR_TYPE_GROUP))) {
1862
0
        fr_dict_attr_t const *internal_root = fr_dict_root(fr_dict_internal());
1863
1864
0
        (void)fr_dict_attr_by_name_substr(NULL,
1865
0
                  &da, internal_root,
1866
0
                  name,
1867
0
                  p_rules ? p_rules->terminals : NULL);
1868
0
        if (da) {
1869
0
          dict_err = FR_DICT_ATTR_OK;
1870
0
          our_parent = internal_root;
1871
0
        }
1872
0
      }
1873
0
      ar = NULL;
1874
1875
0
    } else {
1876
      /*
1877
       *  If we searched in a local dictionary, but found a real attribute
1878
       *  switch the namespace.
1879
       */
1880
0
      if (!da->flags.local && namespace->flags.local) namespace = our_parent = fr_dict_root(da->dict);
1881
0
    }
1882
0
  }
1883
1884
  /*
1885
   *  Fatal errors related to nesting...
1886
   */
1887
0
  switch (dict_err) {
1888
0
  case FR_DICT_ATTR_NO_CHILDREN:
1889
0
    fr_assert(our_parent != NULL);
1890
0
    if (our_parent->flags.is_unknown) break;
1891
0
    goto error;
1892
1893
0
  case FR_DICT_ATTR_NOT_DESCENDENT:
1894
0
    goto error;
1895
1896
0
  default:
1897
0
    if (!da) break;
1898
1899
    /*
1900
     *  The named component was a known attribute
1901
     *  so record it as a normal attribute
1902
     *  reference.
1903
     */
1904
0
    fr_assert(our_parent != NULL);
1905
1906
    /*
1907
     *  We had an alias in the same namespace,
1908
     *  go add more things in.
1909
     */
1910
0
    if (da->parent != our_parent) {
1911
0
      fr_assert(namespace == our_parent);
1912
0
      tmpl_attr_ref_fixup(ctx, vpt, da->parent, our_parent);
1913
0
    }
1914
1915
0
    goto alloc_ar;
1916
0
  }
1917
1918
  /*
1919
   *  At this point we haven't found a known attribute.  What remains MUST be an OID component, OR an
1920
   *  unresolved attribute.
1921
   *
1922
   *  The default is to parse the OIDs in the current namespace.  If there is none, then we parse
1923
   *  the OIDs and unresolved attributes in the dict_def.  And if that doesn't exist, in the
1924
   *  internal dictionaries.
1925
   *
1926
   *  Note that we do NOT allow unknown attributes in the internal dictionary.  Those attributes are
1927
   *  generally just DEFINEs, and their numbers have no meaning.
1928
   */
1929
0
  if (!namespace) {
1930
0
    if (at_rules->dict_def) {
1931
0
      our_parent = namespace = fr_dict_root(at_rules->dict_def);
1932
0
    } else {
1933
0
      our_parent = namespace = fr_dict_root(fr_dict_internal());
1934
0
    }
1935
0
  }
1936
1937
0
  fr_assert(our_parent != NULL);
1938
0
  fr_assert(namespace != NULL);
1939
1940
  /*
1941
   *  See if the ref begins with an unsigned integer
1942
   *  if it does it's probably an OID component
1943
   *
1944
   *  .<oid>
1945
   */
1946
0
  if (fr_sbuff_out(NULL, &oid, name) > 0) {
1947
0
    if (!at_rules->allow_oid) {
1948
0
      uint8_t c = fr_sbuff_char(name, '\0');
1949
1950
      /*
1951
       *  This extra test is to give the user better errors.  The string "3G" is parsed
1952
       *  as "3", and then an error of "what the heck do you mean by G?"
1953
       *
1954
       *  In contrast, the string "3." is parsed as "3", and then "nope, that's not an attribute reference".
1955
       */
1956
0
      if (c != '.') {
1957
0
        fr_strerror_const("Unexpected text after attribute reference");
1958
0
        if (err) *err = TMPL_ATTR_ERROR_MISSING_TERMINATOR;
1959
0
      } else {
1960
0
        fr_strerror_const("Numerical attribute references are not allowed here");
1961
0
        if (err) *err = TMPL_ATTR_ERROR_INVALID_OID;
1962
1963
0
        fr_sbuff_set(name, &m_s);
1964
0
      }
1965
0
      goto error;
1966
0
    }
1967
1968
0
    our_parent = namespace = fr_dict_unlocal(namespace);
1969
1970
0
    fr_assert(ar == NULL);
1971
1972
0
    fr_strerror_clear();  /* Clear out any existing errors */
1973
1974
0
    if (fr_dict_by_da(namespace) == fr_dict_internal()) goto disallow_unknown;
1975
1976
    /*
1977
     *  The OID component was a known attribute
1978
     *  so record it as a normal attribute
1979
     *  reference.
1980
     */
1981
0
    da = fr_dict_attr_child_by_num(namespace, oid);
1982
0
    if (da) {
1983
0
      fr_assert(da->parent == our_parent);
1984
0
      goto alloc_ar;
1985
0
    }
1986
1987
0
    if (!at_rules->allow_unknown) {
1988
0
    disallow_unknown:
1989
0
      fr_strerror_const("Unknown attributes not allowed here");
1990
0
      if (err) *err = TMPL_ATTR_ERROR_UNKNOWN_NOT_ALLOWED;
1991
0
      fr_sbuff_set(name, &m_s);
1992
0
      goto error;
1993
0
    }
1994
1995
    /*
1996
     *  If it's numeric and not a known attribute
1997
     *      then we create an unknown attribute with
1998
     *  the specified attribute number.
1999
     */
2000
0
    MEM(ar = talloc(ctx, tmpl_attr_t));
2001
2002
    /*
2003
     *  VSAs have VENDORs as children.  All others are just normal things.
2004
     */
2005
0
    switch (namespace->type) {
2006
0
    case FR_TYPE_VSA:
2007
0
      da = fr_dict_attr_unknown_vendor_afrom_num(ar, namespace, oid);
2008
0
      break;
2009
2010
0
    default:
2011
0
      da  = fr_dict_attr_unknown_raw_afrom_num(ar, namespace, oid);
2012
0
      break;
2013
0
    }
2014
2015
0
    if (!da) {
2016
0
      if (err) *err = TMPL_ATTR_ERROR_UNKNOWN_NOT_ALLOWED; /* strerror set by dict function */
2017
0
      goto error;
2018
0
    }
2019
2020
0
    *ar = (tmpl_attr_t){
2021
0
      .ar_num = NUM_UNSPEC,
2022
0
      .ar_type = TMPL_ATTR_TYPE_UNKNOWN,
2023
0
      .ar_unknown = UNCONST(fr_dict_attr_t *, da),
2024
0
      .ar_da = da,
2025
0
      .ar_parent = our_parent,
2026
0
    };
2027
0
    goto do_suffix;
2028
0
  }
2029
2030
  /*
2031
   *  Can't parse it as an attribute, might be a literal string
2032
   *  let the caller decide.
2033
   *
2034
   *  Don't alter the fr_strerror buffer, may contain useful
2035
   *  errors from the dictionary code.
2036
   */
2037
0
  if (!at_rules->allow_unresolved && !(at_rules->allow_wildcard && fr_sbuff_is_char(name, '['))) {
2038
0
    fr_strerror_const_push("Unresolved attributes are not allowed here");
2039
0
    if (err) *err = TMPL_ATTR_ERROR_UNRESOLVED_NOT_ALLOWED;
2040
0
    fr_sbuff_set(name, &m_s);
2041
0
    goto error;
2042
0
  }
2043
2044
0
  fr_sbuff_marker_release(&m_s);
2045
2046
  /*
2047
   *  Once we hit one unresolved attribute we have to treat
2048
   *  the rest of the components are unresolved as well.
2049
   */
2050
0
  return tmpl_attr_ref_afrom_unresolved_substr(ctx, err, vpt, our_parent, namespace, name, at_rules);
2051
2052
0
alloc_ar:
2053
  /*
2054
   *  We have a da, remove any of the errors recorded from failed
2055
   *  searches to find the attribute to avoid misleading messages
2056
   *  if something else fails.
2057
   */
2058
0
  fr_strerror_clear();
2059
2060
0
  MEM(ar = talloc(ctx, tmpl_attr_t));
2061
0
  *ar = (tmpl_attr_t) {
2062
0
    .ar_num = NUM_UNSPEC,
2063
0
    .ar_type = TMPL_ATTR_TYPE_NORMAL,
2064
0
    .ar_da = da,
2065
0
    .ar_parent = da->parent,
2066
0
  };
2067
2068
0
do_suffix:
2069
  /*
2070
   *  Parse the attribute reference filter
2071
   *
2072
   *  Error out immediately if the filter is bad
2073
   *  otherwise determine whether to keep the
2074
   *  attribute reference or omit it based on:
2075
   *
2076
   *  - Whether there was a filter present.
2077
   *  - The type of attribute.
2078
   *  - If this is the leaf attribute reference.
2079
   */
2080
0
  if (tmpl_attr_parse_filter(err, ar, name, at_rules) < 0) goto error;
2081
2082
  /*
2083
   *  Local variables are always unitary.
2084
   *
2085
   *  [0] is allowed, as is [n], [*], and [#].  But [1], etc. aren't allowed.
2086
   */
2087
0
  if (da->flags.local && (ar->ar_num > 0)) {
2088
0
    fr_strerror_printf("Invalid array reference for local variable");
2089
0
    if (err) *err = TMPL_ATTR_ERROR_INVALID_ARRAY_INDEX;
2090
0
    fr_sbuff_set(name, &m_s);
2091
0
    goto error;
2092
0
  }
2093
2094
  /*
2095
   *  At the end of the attribute reference. If there's a
2096
   *  trailing '.' then there's another attribute reference
2097
   *  we need to parse, otherwise we're done.
2098
   */
2099
0
  fr_sbuff_marker_release(&m_s);
2100
0
  fr_sbuff_marker(&m_s, name);
2101
2102
0
  if (fr_sbuff_next_if_char(name, '.')) {
2103
0
    fr_dict_attr_t const *ref;
2104
2105
0
    switch (da->type) {
2106
0
    case FR_TYPE_GROUP:
2107
0
      ref = fr_dict_attr_ref(da);
2108
2109
      /*
2110
       *  If the ref is outside of the internal namespace, then we use it.
2111
       *
2112
       *  If the ref is inside of the internal namespace (e.g. "request"), then we do
2113
       *  something else.
2114
       *
2115
       *  If we were given a root dictionary on input, use that.  We have to follow this
2116
       *  dictionary because this function calls itself recursively, WITHOUT updating
2117
       *  "dict_def" in the attr_rules.  So the dict-def there is whatever got passed
2118
       *  into tmpl_afrom_attr_substr(), BEFORE the "parent.parent.parent..." parsing.
2119
       *  Which means that in many cases, the "dict_def" is completely irrelevant.
2120
       *
2121
       *  If there is no parent on input, then we just use dict_def.
2122
       *
2123
       *  Otherwise we search through all of the dictionaries.
2124
       *
2125
       *  Note that we cannot put random protocol attributes into an internal attribute
2126
       *  of type "group".
2127
       */
2128
0
      if (ref != fr_dict_root(fr_dict_internal())) {
2129
0
        our_parent = namespace = ref;
2130
2131
0
      } else if (parent && parent->flags.is_root) {
2132
0
        our_parent = namespace = parent;
2133
2134
0
      } else if (request_attr_is_list(da)) {
2135
0
        our_parent = namespace = NULL;
2136
2137
0
      } else if (at_rules->dict_def) {
2138
0
        our_parent = namespace = fr_dict_root(at_rules->dict_def);
2139
2140
0
      } else {
2141
0
        our_parent = namespace = NULL;
2142
0
      }
2143
0
      break;
2144
2145
0
    case FR_TYPE_STRUCTURAL_EXCEPT_GROUP:
2146
      /*
2147
       *  Structural types are parented and namespaced from their parent da.
2148
       */
2149
0
      namespace = our_parent = da;
2150
0
      break;
2151
2152
0
    default:
2153
0
      fr_strerror_printf("Attribute %s of data type '%s' cannot have child attributes", da->name, fr_type_to_str(da->type));
2154
0
      fr_sbuff_set(name, &m_s);
2155
0
      goto error;
2156
0
    }
2157
2158
0
    if (ar) tmpl_attr_insert(vpt, ar);
2159
2160
0
    if (tmpl_attr_afrom_attr_substr(ctx, err, vpt, our_parent, namespace, name, p_rules, at_rules, depth + 1) < 0) {
2161
0
      if (ar) {
2162
0
        tmpl_attr_list_talloc_free_tail(&vpt->data.attribute.ar); /* Remove and free ar */
2163
0
        ar = NULL;
2164
0
      }
2165
0
      goto error;
2166
0
    }
2167
2168
  /*
2169
   *  If it's a leaf we always insert the attribute
2170
   *  reference into the list, even if it's a
2171
   *  nesting attribute.
2172
   *
2173
   *  This is useful for nested edit sections
2174
   *  where the tmpl might be the name of a new
2175
   *  subsection.
2176
   */
2177
0
  } else {
2178
0
    tmpl_attr_insert(vpt, ar);
2179
0
  }
2180
2181
  /*
2182
   *  Remove unnecessary casts.
2183
   */
2184
0
  if (tmpl_is_attr(vpt) && tmpl_attr_tail_is_normal(vpt) &&
2185
0
      (tmpl_rules_cast(vpt) == tmpl_attr_tail_da(vpt)->type)) vpt->rules.cast = FR_TYPE_NULL;
2186
2187
0
  TMPL_VERIFY(vpt);
2188
2189
0
  fr_sbuff_marker_release(&m_s);
2190
0
  return 0;
2191
0
}
2192
2193
static int attr_to_raw(tmpl_t *vpt, tmpl_attr_t *ref)
2194
0
{
2195
0
  switch (ref->type) {
2196
0
  case TMPL_ATTR_TYPE_NORMAL:
2197
0
  {
2198
0
    ref->da = ref->ar_unknown = fr_dict_attr_unknown_afrom_da(vpt, ref->da);
2199
0
    if (!ref->da) return -1;
2200
2201
0
    ref->ar_unknown->type = FR_TYPE_OCTETS;
2202
0
    ref->is_raw = 1;
2203
0
    ref->type = TMPL_ATTR_TYPE_UNKNOWN;
2204
0
  }
2205
0
    break;
2206
0
  case TMPL_ATTR_TYPE_UNSPEC: /* noop */
2207
0
    break;
2208
2209
0
  case TMPL_ATTR_TYPE_UNKNOWN:
2210
0
    ref->ar_unknown->type = FR_TYPE_OCTETS;
2211
0
    ref->is_raw = 1;
2212
0
    break;
2213
2214
0
  case TMPL_ATTR_TYPE_UNRESOLVED:
2215
0
    ref->is_raw = true;
2216
0
    break;
2217
0
  }
2218
2219
0
  TMPL_ATTR_VERIFY(vpt);
2220
2221
0
  return 0;
2222
0
}
2223
2224
/** Parse a string into a TMPL_TYPE_ATTR_* type #tmpl_t
2225
 *
2226
 * @param[in,out] ctx   to allocate #tmpl_t in.
2227
 * @param[out] err    May be NULL.  Provides the exact error that the parser hit
2228
 *        when processing the attribute ref.
2229
 * @param[out] out    Where to write pointer to new #tmpl_t.
2230
 * @param[in] name    of attribute including #tmpl_request_ref_t and #fr_pair_list_t qualifiers.
2231
 * @param[in] p_rules   Formatting rules used to check for trailing garbage.
2232
 * @param[in] t_rules   Rules which control parsing:
2233
 *        - dict_def    The default dictionary to use if attributes
2234
 *              are unqualified.
2235
 *        - request_def   The default #request_t to set if no
2236
 *              #tmpl_request_ref_t qualifiers are found in name.
2237
 *        - list_def    The default list to set if no #fr_pair_list_t
2238
 *              qualifiers are found in the name.
2239
 *        - allow_unknown   If true, numerical attributes will be allowed,
2240
 *              even if they're not in the main dictionaries.
2241
 *              If an unknown attribute is found a #TMPL_TYPE_ATTR
2242
 *              #tmpl_t will be produced.
2243
 *              If #tmpl_afrom_attr_substr is being called on
2244
 *              startup, the #tmpl_t may be passed to
2245
 *              #tmpl_attr_unknown_add to
2246
 *              add the unknown attribute to the main dictionary.
2247
 *              If the unknown attribute is not added to
2248
 *              the main dictionary the #tmpl_t cannot be used
2249
 *              to search for a #fr_pair_t in a #request_t.
2250
 *        - allow_unresolved  If true, we don't generate a parse error on
2251
 *              unknown attributes. If an unknown attribute is
2252
 *              found a #TMPL_TYPE_ATTR_UNRESOLVED
2253
 *              #tmpl_t will be produced.
2254
 *        - allow_foreign   If true, allow attribute names to be qualified
2255
 *              with a protocol outside of the passed dict_def.
2256
 *        - disallow_filters
2257
 *
2258
 * @see REMARKER to produce pretty error markers from the return value.
2259
 *
2260
 * @return
2261
 *  - <= 0 on error (offset as negative integer)
2262
 *  - > 0 on success (number of bytes parsed).
2263
 */
2264
ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
2265
             tmpl_t **out, fr_sbuff_t *name,
2266
             fr_sbuff_parse_rules_t const *p_rules,
2267
             tmpl_rules_t const *t_rules)
2268
0
{
2269
0
  int       ret;
2270
0
  tmpl_t        *vpt;
2271
0
  fr_sbuff_t      our_name = FR_SBUFF(name); /* Take a local copy in case we need to back track */
2272
0
  bool        is_raw = false;
2273
0
  tmpl_attr_rules_t const   *at_rules;
2274
0
  tmpl_attr_rules_t   my_attr_rules;
2275
0
  fr_sbuff_marker_t   m_l;
2276
0
  fr_dict_attr_t const    *namespace;
2277
0
  DEFAULT_RULES;
2278
2279
0
  CHECK_T_RULES;
2280
2281
0
  at_rules = &t_rules->attr;
2282
2283
0
  if (err) *err = TMPL_ATTR_ERROR_NONE;
2284
2285
0
  if (!fr_sbuff_extend(&our_name)) {
2286
0
    fr_strerror_const("Empty attribute reference");
2287
0
    if (err) *err = TMPL_ATTR_ERROR_EMPTY;
2288
0
    FR_SBUFF_ERROR_RETURN(&our_name);
2289
0
  }
2290
2291
  /*
2292
   *  '&' prefix is ignored.
2293
   */
2294
0
  if (fr_sbuff_next_if_char(&our_name, '&') && check_config && at_rules->ci) {
2295
0
    cf_log_warn(at_rules->ci, "Using '&' is no longer necessary when referencing attributes.  Please delete it.");
2296
0
  }
2297
2298
  /*
2299
   *  We parsed the tmpl as User-Name, but NOT %{User-Name}.
2300
   */
2301
0
  MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
2302
2303
  /*
2304
   *  The "raw." prefix marks up the leaf attribute
2305
   *  as unknown if it wasn't already which allows
2306
   *  users to stick whatever they want in there as
2307
   *  a value.
2308
   */
2309
0
  if (fr_sbuff_adv_past_strcase_literal(&our_name, "raw.")) {
2310
0
    my_attr_rules = *at_rules;
2311
0
    my_attr_rules.allow_oid = true;
2312
0
    at_rules = &my_attr_rules;
2313
2314
0
    is_raw = true;
2315
0
  }
2316
2317
  /*
2318
   *  Parse one or more request references
2319
   */
2320
0
  ret = tmpl_request_ref_list_from_substr(vpt, err,
2321
0
            &vpt->data.attribute.rr,
2322
0
            &our_name,
2323
0
            t_rules,
2324
0
            &namespace);
2325
0
  if (ret < 0) {
2326
0
  error:
2327
0
    *out = NULL;
2328
0
    talloc_free(vpt);
2329
0
    FR_SBUFF_ERROR_RETURN(&our_name);
2330
0
  }
2331
2332
0
  fr_sbuff_marker(&m_l, &our_name);
2333
2334
  /*
2335
   *  Parse the list and / or attribute reference
2336
   */
2337
0
  ret = tmpl_attr_afrom_attr_substr(vpt, err,
2338
0
            vpt,
2339
0
            namespace, namespace,
2340
0
            &our_name, p_rules, at_rules, 0);
2341
0
  if (ret < 0) goto error;
2342
2343
0
  if (!tmpl_substr_terminal_check(&our_name, p_rules)) {
2344
0
    fr_strerror_const("Unexpected text after attribute reference");
2345
0
    if (err) *err = TMPL_ATTR_ERROR_MISSING_TERMINATOR;
2346
0
    goto error;
2347
0
  }
2348
2349
  /*
2350
   *  Check whether the tmpl has a list qualifier.
2351
   */
2352
0
  switch (at_rules->list_presence) {
2353
0
  case TMPL_ATTR_LIST_ALLOW:
2354
0
    break;
2355
2356
0
  case TMPL_ATTR_LIST_FORBID:
2357
0
    if (tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2358
0
      fr_strerror_const("List qualifiers are not allowed here.");
2359
0
      if (err) *err = TMPL_ATTR_ERROR_LIST_NOT_ALLOWED;
2360
0
      goto error;
2361
0
    }
2362
0
    break;
2363
2364
0
  case TMPL_ATTR_LIST_REQUIRE:
2365
0
    if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2366
0
      fr_strerror_const("List qualifier is required, but no list was found.");
2367
0
      if (err) *err = TMPL_ATTR_ERROR_LIST_MISSING;
2368
0
      goto error;
2369
0
    }
2370
0
    break;
2371
0
  }
2372
2373
0
  tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
2374
0
  vpt->rules = *t_rules;  /* Record the rules */
2375
2376
  /*
2377
   *  Check to see if the user wants the leaf
2378
   *  attribute to be raw.
2379
   *
2380
   *  We can only do the conversion now _if_
2381
   *  the complete hierarchy has been resolved
2382
   *  otherwise we'll need to do the conversion
2383
   *  later.
2384
   */
2385
0
  if (tmpl_is_attr(vpt)) {
2386
0
    tmpl_attr_t *ar = tmpl_attr_list_head(tmpl_attr(vpt));
2387
0
    bool    is_local = ar->ar_da->flags.local;
2388
0
    bool    allow_local = is_local;
2389
2390
    /*
2391
     *  Convert known attributes to raw ones if requested.
2392
     */
2393
0
    if (is_raw) {
2394
      /*
2395
       *  Local variables cannot be raw.
2396
       */
2397
0
      if (is_local) {
2398
0
        fr_strerror_printf("Local attributes cannot be 'raw'");
2399
0
        if (err) *err = TMPL_ATTR_ERROR_UNKNOWN_NOT_ALLOWED;
2400
0
        fr_sbuff_set(&our_name, &m_l);
2401
0
        goto error;
2402
0
      }
2403
0
      ret = attr_to_raw(vpt, tmpl_attr_list_tail(tmpl_attr(vpt)));
2404
0
      if (ret < 0) goto error;
2405
0
    }
2406
2407
    /*
2408
     *  We can transition from local to non-local, but not the other way around.
2409
     */
2410
0
    for (;
2411
0
         ar != NULL;
2412
0
         ar = tmpl_attr_list_next(tmpl_attr(vpt), ar)) {
2413
0
      if (ar->ar_da->flags.local == allow_local) continue;
2414
2415
0
      if (!ar->ar_da->flags.local && allow_local) {
2416
0
        allow_local = false;
2417
0
        continue;
2418
0
      }
2419
2420
0
      if (ar->ar_da->flags.local) {
2421
0
        fr_strerror_printf("Local attributes cannot be used in any list");
2422
0
        if (err) *err = TMPL_ATTR_ERROR_FOREIGN_NOT_ALLOWED;
2423
0
        fr_sbuff_set(&our_name, &m_l);
2424
0
        goto error;
2425
0
      }
2426
0
    }
2427
2428
    /*
2429
     *  Local variables are named "foo", but are always put into the local list.
2430
     *
2431
     *  We add the list after checking for non-local -> local transition, as
2432
     *  request_attr_local isn't a local attribute.
2433
     *
2434
     *  When the list is forbidden, we're creating a local attribute inside of a local
2435
     *  TLV.
2436
     */
2437
0
    if (is_local && (at_rules->list_presence != TMPL_ATTR_LIST_FORBID)) {
2438
0
      MEM(ar = talloc(vpt, tmpl_attr_t));
2439
0
      *ar = (tmpl_attr_t){
2440
0
        .ar_type = TMPL_ATTR_TYPE_NORMAL,
2441
0
        .ar_da = request_attr_local,
2442
0
        .ar_parent = fr_dict_root(fr_dict_internal())
2443
0
      };
2444
2445
      /*
2446
       *  Prepend the local list ref so it gets evaluated
2447
       *  first.
2448
       */
2449
0
      tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
2450
0
    }
2451
0
  }
2452
2453
  /*
2454
   *  If a list wasn't already specified, then add one now.
2455
   */
2456
0
  if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2457
0
    tmpl_attr_t *ar;
2458
2459
0
    MEM(ar = talloc(vpt, tmpl_attr_t));
2460
0
    *ar = (tmpl_attr_t){
2461
0
      .ar_type = TMPL_ATTR_TYPE_NORMAL,
2462
0
      .ar_parent = fr_dict_root(fr_dict_internal())
2463
0
    };
2464
2465
0
    fr_assert(at_rules->list_def);
2466
0
    ar->ar_da = at_rules->list_def;
2467
2468
    /*
2469
     *  Prepend the list ref so it gets evaluated
2470
     *  first.
2471
     */
2472
0
    tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
2473
0
  }
2474
2475
  /*
2476
   *  If there is a default request (parent, outer, etc.), add it to the ar list.
2477
   *
2478
   *  A NULL request_def pointer is equivalent to the current request.
2479
   */
2480
0
  if (t_rules->attr.request_def) {
2481
0
    tmpl_request_ref_list_acopy(vpt, &vpt->rules.attr.request_def, t_rules->attr.request_def);
2482
0
  }
2483
2484
  /*
2485
   *  Now that all of the lists are set correctly, do some final validation and updates on the
2486
   *  attribute.
2487
   */
2488
0
  if (tmpl_is_attr(vpt)) {
2489
0
    tmpl_attr_t *ar;
2490
2491
    /*
2492
     *  Ensure that the list is set correctly, so that the returned vpt doesn't just
2493
     *  match the input rules, it is also internally consistent.
2494
     */
2495
0
    ar = tmpl_attr_list_head(tmpl_attr(vpt));
2496
0
    fr_assert(ar != NULL);
2497
2498
0
    if (tmpl_attr_is_list_attr(ar)) vpt->rules.attr.list_def = ar->ar_da;
2499
2500
0
    if (tmpl_attr_tail_is_normal(vpt)) {
2501
      /*
2502
       *  Suppress useless casts.
2503
       */
2504
0
      if (tmpl_attr_tail_da(vpt)->type == tmpl_rules_cast(vpt)) {
2505
0
        vpt->rules.cast = FR_TYPE_NULL;
2506
0
      }
2507
2508
      /*
2509
       *  Check if the cast is allowed.  This lets us give better errors at compile time.
2510
       */
2511
0
      if ((tmpl_rules_cast(vpt)!= FR_TYPE_NULL) &&
2512
0
          !fr_type_cast(tmpl_rules_cast(vpt), tmpl_attr_tail_da(vpt)->type)) {
2513
0
        fr_strerror_printf("Cannot cast type '%s' to '%s'",
2514
0
             fr_type_to_str(tmpl_attr_tail_da(vpt)->type), fr_type_to_str(t_rules->cast));
2515
0
        if (err) *err = TMPL_ATTR_ERROR_BAD_CAST;
2516
0
        fr_sbuff_set_to_start(&our_name);
2517
0
        goto error;
2518
0
      }
2519
0
    }
2520
0
  }
2521
2522
0
  TMPL_VERIFY(vpt);  /* Because we want to ensure we produced something sane */
2523
2524
0
  *out = vpt;
2525
0
  FR_SBUFF_SET_RETURN(name, &our_name);
2526
0
}
2527
2528
/** Parse a string into a TMPL_TYPE_ATTR_* type #tmpl_t
2529
 *
2530
 * @param[in,out] ctx   to allocate #tmpl_t in.
2531
 * @param[out] err    May be NULL.  Provides the exact error that the parser hit
2532
 *        when processing the attribute ref.
2533
 * @param[out] out    Where to write pointer to new #tmpl_t.
2534
 * @param[in] name    of attribute including #tmpl_request_ref_t and #fr_pair_list_t qualifiers.
2535
 * @param[in] t_rules   Rules which control parsing.  See tmpl_afrom_attr_substr() for details.
2536
 *
2537
 * @note Unlike #tmpl_afrom_attr_substr this function will error out if the entire
2538
 *  name string isn't parsed.
2539
 */
2540
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
2541
          tmpl_t **out, char const *name, tmpl_rules_t const *t_rules)
2542
0
{
2543
0
  ssize_t slen, name_len;
2544
0
  DEFAULT_RULES;
2545
2546
0
  CHECK_T_RULES;
2547
2548
0
  name_len = strlen(name);
2549
0
  slen = tmpl_afrom_attr_substr(ctx, err, out, &FR_SBUFF_IN(name, name_len), NULL, t_rules);
2550
0
  if (slen <= 0) return slen;
2551
2552
0
  if (!fr_cond_assert(*out)) return -1;
2553
2554
0
  if (slen != name_len) {
2555
    /* This looks wrong, but it produces meaningful errors for unknown attrs */
2556
0
    fr_strerror_printf("Unexpected text after %s",
2557
0
           tmpl_type_to_str((*out)->type));
2558
0
    return -slen;
2559
0
  }
2560
2561
0
  TMPL_VERIFY(*out);
2562
2563
0
  return slen;
2564
0
}
2565
2566
/** Create TMPL_TYPE_DATA from a string
2567
 *
2568
 * @param[in] ctx   to allocate tmpl to.
2569
 * @param[out] out    where to write tmpl.
2570
 * @param[in] in    sbuff to parse.
2571
 * @param[in] quote   surrounding the operand to parse.
2572
 * @param[in] t_rules   specifying the cast and any enumeration values.
2573
 * @param[in] allow_enum  Whether parsing the value as an enum should be allowed.
2574
 * @param[in] p_rules   formatting rules.
2575
 * @return
2576
 *  - <0 on error
2577
 *  - >=0 on success.
2578
 */
2579
static fr_slen_t tmpl_afrom_value_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
2580
           fr_token_t quote,
2581
           tmpl_rules_t const *t_rules, bool allow_enum,
2582
           fr_sbuff_parse_rules_t const *p_rules)
2583
0
{
2584
0
  fr_sbuff_t  our_in = FR_SBUFF(in);
2585
0
  fr_value_box_t  tmp;
2586
0
  tmpl_t    *vpt;
2587
0
  fr_type_t cast = FR_TYPE_STRING;
2588
2589
0
  if (!fr_type_is_null(t_rules->cast)) cast = t_rules->cast;
2590
2591
0
  if (!fr_type_is_leaf(cast)) {
2592
0
    fr_strerror_printf("%s is not a valid cast type",
2593
0
           fr_type_to_str(cast));
2594
0
    FR_SBUFF_ERROR_RETURN(&our_in);
2595
0
  }
2596
2597
0
  vpt = tmpl_alloc_null(ctx);
2598
0
  if (fr_value_box_from_substr(vpt, &tmp,
2599
0
             cast, allow_enum ? t_rules->enumv : NULL,
2600
0
             &our_in, p_rules) < 0) {
2601
0
    talloc_free(vpt);
2602
0
    FR_SBUFF_ERROR_RETURN(&our_in);
2603
0
  }
2604
0
  fr_value_box_mark_safe_for(&tmp, t_rules->literals_safe_for);
2605
2606
0
  tmpl_init(vpt, TMPL_TYPE_DATA, quote, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
2607
2608
0
  fr_value_box_copy_shallow(NULL, tmpl_value(vpt), &tmp);
2609
2610
0
  *out = vpt;
2611
2612
0
  if (cast == tmpl_value_type(vpt)) vpt->rules.cast = FR_TYPE_NULL;
2613
2614
0
  TMPL_VERIFY(vpt);
2615
2616
0
  FR_SBUFF_SET_RETURN(in, &our_in);
2617
0
}
2618
2619
/** Parse a truth value
2620
 *
2621
 * @param[in] ctx to allocate tmpl to.
2622
 * @param[out] out  where to write tmpl.
2623
 * @param[in] in  sbuff to parse.
2624
 * @param[in] p_rules formatting rules.
2625
 * @return
2626
 *  - < 0 sbuff does not contain a boolean value.
2627
 *  - > 0 how many bytes were parsed.
2628
 */
2629
static fr_slen_t tmpl_afrom_bool_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
2630
          fr_sbuff_parse_rules_t const *p_rules)
2631
{
2632
  fr_sbuff_t  our_in = FR_SBUFF(in);
2633
  bool    a_bool;
2634
  tmpl_t    *vpt;
2635
2636
  if (fr_sbuff_out(NULL, &a_bool, &our_in) < 0) {
2637
    fr_strerror_const("Not a boolean value");
2638
    return 0;
2639
  }
2640
2641
  if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2642
    fr_strerror_const("Unexpected text after bool");
2643
    FR_SBUFF_ERROR_RETURN(&our_in);
2644
  }
2645
2646
  MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2647
2648
  fr_value_box_init(&vpt->data.literal, FR_TYPE_BOOL, NULL, false);
2649
  vpt->data.literal.vb_bool = a_bool;
2650
2651
  *out = vpt;
2652
2653
  FR_SBUFF_SET_RETURN(in, &our_in);
2654
}
2655
2656
/** Parse bareword as an octet string
2657
 *
2658
 * @param[in] ctx to allocate tmpl to.
2659
 * @param[out] out  where to write tmpl.
2660
 * @param[in] in  sbuff to parse.
2661
 * @param[in] p_rules formatting rules.
2662
 * @return
2663
 *  - < 0 negative offset where parse error occurred.
2664
 *  - 0 sbuff does not contain a hex string.
2665
 *  - > 0 how many bytes were parsed.
2666
 */
2667
static fr_slen_t tmpl_afrom_octets_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
2668
            fr_sbuff_parse_rules_t const *p_rules)
2669
{
2670
  fr_sbuff_t  our_in = FR_SBUFF(in);
2671
  tmpl_t    *vpt;
2672
  char    *hex;
2673
  size_t    binlen, len;
2674
  uint8_t   *bin;
2675
2676
  if (!fr_sbuff_adv_past_strcase_literal(&our_in, "0x")) return 0;
2677
2678
  MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, NULL, 0));
2679
2680
  /*
2681
   *  This allows stream parsing to work correctly
2682
   *      we could be less lazy and copy hex data in
2683
   *      chunks, but never mind...
2684
   */
2685
  len = fr_sbuff_out_abstrncpy_allowed(vpt, &hex, &our_in, SIZE_MAX, sbuff_char_class_hex);
2686
  if (len & 0x01) {
2687
    fr_strerror_const("Hex string not even length");
2688
  error:
2689
    talloc_free(vpt);
2690
    FR_SBUFF_ERROR_RETURN(&our_in);
2691
  }
2692
  if (len == 0) {
2693
    fr_strerror_const("Zero length hex string is invalid");
2694
    goto error;
2695
  }
2696
2697
  if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2698
    fr_strerror_const("Unexpected text after hex string");
2699
    goto error;
2700
  }
2701
2702
  bin = (uint8_t *)hex;
2703
  binlen = len / 2;
2704
2705
  tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in));
2706
2707
  (void)fr_base16_decode(NULL, &FR_DBUFF_TMP(bin, binlen), &FR_SBUFF_IN(hex, len), false);
2708
  MEM(bin = talloc_realloc_size(vpt, bin, binlen)); /* Realloc to the correct length */
2709
  (void)fr_value_box_memdup_shallow(&vpt->data.literal, NULL, bin, binlen, false);
2710
2711
  *out = vpt;
2712
2713
  FR_SBUFF_SET_RETURN(in, &our_in);
2714
}
2715
2716
/** Parse bareword as an IPv4 address or prefix
2717
 *
2718
 * @param[in] ctx to allocate tmpl to.
2719
 * @param[out] out  where to write tmpl.
2720
 * @param[in] in  sbuff to parse.
2721
 * @param[in] p_rules formatting rules.
2722
 * @return
2723
 *  - < 0 sbuff does not contain an IPv4 address or prefix.
2724
 *  - > 0 how many bytes were parsed.
2725
 */
2726
static fr_slen_t tmpl_afrom_ipv4_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
2727
          fr_sbuff_parse_rules_t const *p_rules)
2728
0
{
2729
0
  tmpl_t    *vpt;
2730
0
  fr_sbuff_t  our_in = FR_SBUFF(in);
2731
0
  fr_type_t type;
2732
0
  int   count;
2733
0
  uint32_t  ipaddr;
2734
0
  uint8_t   addr[4] = {}, prefix = 32;
2735
2736
0
  for (count = 0; count < 4; count++) {
2737
0
    if (!fr_sbuff_out(NULL, &addr[count], &our_in)) FR_SBUFF_ERROR_RETURN(&our_in);
2738
2739
0
    if (count == 3) break;
2740
2741
0
    if (fr_sbuff_next_if_char(&our_in, '.')) continue;
2742
2743
0
    if (!fr_sbuff_is_char(&our_in, '/')) FR_SBUFF_ERROR_RETURN(&our_in);
2744
0
  }
2745
2746
  /*
2747
   *  If it has a trailing '/' then it's an IP prefix.
2748
   */
2749
0
  if (fr_sbuff_next_if_char(&our_in, '/')) {
2750
0
    if (fr_sbuff_out(NULL, &prefix, &our_in) < 0) {
2751
0
      fr_strerror_const("IPv4 CIDR mask malformed");
2752
0
      FR_SBUFF_ERROR_RETURN(&our_in);
2753
0
    }
2754
2755
0
    if (prefix > 32) {
2756
0
      fr_strerror_const("IPv4 CIDR mask too high");
2757
0
      FR_SBUFF_ERROR_RETURN(&our_in);
2758
0
    }
2759
2760
0
    type = FR_TYPE_IPV4_PREFIX;
2761
0
  } else {
2762
0
    type = FR_TYPE_IPV4_ADDR;
2763
0
  }
2764
2765
0
  if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2766
0
    fr_strerror_const("Unexpected text after IPv4 string or prefix");
2767
0
    FR_SBUFF_ERROR_RETURN(&our_in);
2768
0
  }
2769
2770
0
  MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2771
0
  fr_value_box_init(&vpt->data.literal, type, NULL, false);
2772
0
  vpt->data.literal.vb_ip.af = AF_INET;
2773
0
  vpt->data.literal.vb_ip.prefix = prefix;
2774
2775
  /*
2776
   *  Zero out lower bits
2777
   */
2778
0
  ipaddr = (((uint32_t) addr[0]) << 24) | (((uint32_t) addr[1]) << 16) | (((uint32_t) addr[2]) << 8) | addr[3];
2779
0
  if (prefix == 0) {
2780
0
    ipaddr = 0;
2781
2782
0
  } else if (prefix < 32) {
2783
0
    ipaddr &= ~((uint32_t) 0) << (32 - prefix);
2784
0
  }
2785
0
  vpt->data.literal.vb_ipv4addr = htonl(ipaddr);
2786
2787
0
  *out = vpt;
2788
2789
0
  FR_SBUFF_SET_RETURN(in, &our_in);
2790
0
}
2791
2792
/** Parse bareword as an IPv6 address or prefix
2793
 *
2794
 * @param[in] ctx   to allocate tmpl to.
2795
 * @param[out] out    where to write tmpl.
2796
 * @param[in] in    sbuff to parse.
2797
 * @param[in] p_rules   formatting rules.
2798
 * @return
2799
 *  - < 0 sbuff does not contain an IPv6 address or prefix.
2800
 *  - > 0 how many bytes were parsed.
2801
 */
2802
static fr_slen_t tmpl_afrom_ipv6_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
2803
          fr_sbuff_parse_rules_t const *p_rules)
2804
0
{
2805
0
  tmpl_t      *vpt;
2806
0
  fr_sbuff_t    our_in = FR_SBUFF(in);
2807
0
  fr_sbuff_marker_t m;
2808
0
  fr_type_t   type;
2809
0
  size_t      len;
2810
0
  char      *sep_a, *sep_b;
2811
2812
0
  static const bool ipv6_chars[SBUFF_CHAR_CLASS] = {
2813
0
    ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
2814
0
    ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true,
2815
0
    ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
2816
0
    ['f'] = true,
2817
0
    ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true,
2818
0
    ['F'] = true,
2819
0
    [':'] = true, ['.'] = true
2820
0
  };
2821
2822
  /*
2823
   *  Drop a marker to pin the start of the
2824
   *  address in the buffer.
2825
   */
2826
0
  fr_sbuff_marker(&m, &our_in);
2827
2828
  /*
2829
   *  Check for something looking like an IPv6 address
2830
   *
2831
   *  Minimum string is '::'
2832
   */
2833
0
  len = fr_sbuff_adv_past_allowed(&our_in, FR_IPADDR_STRLEN + 1, ipv6_chars, NULL);
2834
0
  if ((len < 2) || (len > FR_IPADDR_STRLEN)) {
2835
0
  error:
2836
0
    FR_SBUFF_ERROR_RETURN(&our_in);
2837
0
  }
2838
2839
  /*
2840
   *  Got ':' after '.', this isn't allowed.
2841
   *
2842
   *  We need this check else IPv4 gets parsed
2843
   *  as blank IPv6 address.
2844
   */
2845
0
  sep_a = memchr(fr_sbuff_current(&m), '.', len);
2846
0
  if (sep_a && (!(sep_b = memchr(fr_sbuff_current(&m), ':', len)) || (sep_b > sep_a))) {
2847
0
    fr_strerror_const("First IPv6 component separator was a '.'");
2848
0
    goto error;
2849
0
  }
2850
2851
  /*
2852
   *  The v6 parse function will happily turn
2853
   *  integers into v6 addresses *sigh*.
2854
   */
2855
0
  sep_a = memchr(fr_sbuff_current(&m), ':', len);
2856
0
  if (!sep_a) {
2857
0
    fr_strerror_const("No IPv6 component separator");
2858
0
    goto error;
2859
0
  }
2860
2861
  /*
2862
   *  Handle scope
2863
   */
2864
0
  if (fr_sbuff_next_if_char(&our_in, '%')) {
2865
0
    len = fr_sbuff_adv_until(&our_in, IFNAMSIZ + 1, p_rules ? p_rules->terminals : NULL, '\0');
2866
0
    if ((len < 1) || (len > IFNAMSIZ)) {
2867
0
      fr_strerror_const("IPv6 scope too long");
2868
0
      goto error;
2869
0
    }
2870
0
  }
2871
2872
  /*
2873
   *  ...and finally the prefix.
2874
   */
2875
0
  if (fr_sbuff_next_if_char(&our_in, '/')) {
2876
0
    uint8_t   mask;
2877
2878
0
    if (fr_sbuff_out(NULL, &mask, &our_in) < 0) {
2879
0
      fr_strerror_const("IPv6 CIDR mask malformed");
2880
0
      goto error;
2881
0
    }
2882
0
    if (mask > 128) {
2883
0
      fr_strerror_const("IPv6 CIDR mask too high");
2884
0
      goto error;
2885
0
    }
2886
2887
0
    type = FR_TYPE_IPV6_PREFIX;
2888
0
  } else {
2889
0
    type = FR_TYPE_IPV6_ADDR;
2890
0
  }
2891
2892
0
  if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2893
0
    fr_strerror_const("Unexpected text after IPv6 string or prefix");
2894
0
    goto error;
2895
0
  }
2896
2897
0
  MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2898
0
  if (fr_value_box_from_substr(vpt, &vpt->data.literal, type, NULL,
2899
0
             &FR_SBUFF_REPARSE(&our_in),
2900
0
             NULL) < 0) {
2901
0
    talloc_free(vpt);
2902
0
    goto error;
2903
0
  }
2904
0
  *out = vpt;
2905
2906
0
  FR_SBUFF_SET_RETURN(in, &our_in);
2907
0
}
2908
2909
2910
/** Try and parse signed or unsigned integers
2911
 *
2912
 * @param[in] ctx to allocate tmpl to.
2913
 * @param[out] out  where to write tmpl.
2914
 * @param[in] in  sbuff to parse.
2915
 * @param[in] p_rules formatting rules.
2916
 * @return
2917
 *  - < 0 sbuff does not contain a mac address.
2918
 *  - > 0 how many bytes were parsed.
2919
 */
2920
static ssize_t tmpl_afrom_ether_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
2921
               fr_sbuff_parse_rules_t const *p_rules)
2922
{
2923
  tmpl_t      *vpt;
2924
  fr_sbuff_t    our_in = FR_SBUFF(in);
2925
  uint8_t     buff[6] = {};
2926
  fr_dbuff_t    dbuff;
2927
  fr_value_box_t    *vb;
2928
  fr_sbuff_parse_error_t  err;
2929
2930
  fr_dbuff_init(&dbuff, buff, sizeof(buff));
2931
2932
  fr_base16_decode(&err, &dbuff, &our_in, true);
2933
  if (err != FR_SBUFF_PARSE_OK) return 0;
2934
2935
  if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2936
2937
  fr_base16_decode(&err, &dbuff, &our_in, true);
2938
  if (err != FR_SBUFF_PARSE_OK) return 0;
2939
2940
  if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2941
2942
  fr_base16_decode(&err, &dbuff, &our_in, true);
2943
  if (err != FR_SBUFF_PARSE_OK) return 0;
2944
2945
  if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2946
2947
  fr_base16_decode(&err, &dbuff, &our_in, true);
2948
  if (err != FR_SBUFF_PARSE_OK) return 0;
2949
2950
  if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2951
2952
  fr_base16_decode(&err, &dbuff, &our_in, true);
2953
  if (err != FR_SBUFF_PARSE_OK) return 0;
2954
2955
  if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2956
2957
  fr_base16_decode(&err, &dbuff, &our_in, true);
2958
  if (err != FR_SBUFF_PARSE_OK) return 0;
2959
2960
  if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2961
    fr_strerror_const("Unexpected text after mac address");
2962
    return 0;
2963
  }
2964
2965
  MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA,
2966
           T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2967
  vb = tmpl_value(vpt);
2968
2969
  fr_value_box_init(vb, FR_TYPE_ETHERNET, NULL, false);
2970
  memcpy(vb->vb_ether, buff, sizeof(vb->vb_ether));
2971
2972
  *out = vpt;
2973
2974
  FR_SBUFF_SET_RETURN(in, &our_in);
2975
}
2976
2977
/** Try and parse signed or unsigned integers
2978
 *
2979
 * @param[in] ctx to allocate tmpl to.
2980
 * @param[out] out  where to write tmpl.
2981
 * @param[in] in  sbuff to parse.
2982
 * @param[in] p_rules formatting rules.
2983
 * @return
2984
 *  - < 0 sbuff does not contain an integer.
2985
 *  - > 0 how many bytes were parsed.
2986
 */
2987
static fr_slen_t tmpl_afrom_integer_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
2988
             fr_sbuff_parse_rules_t const *p_rules)
2989
0
{
2990
0
  tmpl_t    *vpt;
2991
0
  fr_sbuff_t  our_in = FR_SBUFF(in);
2992
0
  ssize_t   slen;
2993
0
  fr_value_box_t  *vb;
2994
2995
  /*
2996
   *  Pick the narrowest signed type
2997
   */
2998
0
  if (fr_sbuff_is_char(&our_in, '-')) {
2999
0
    int64_t   a_int;
3000
3001
0
    slen = fr_sbuff_out(NULL, &a_int, &our_in);
3002
0
    if (slen <= 0) return 0;
3003
3004
0
    if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
3005
0
      fr_strerror_const("Unexpected text after signed integer");
3006
0
    error:
3007
0
      FR_SBUFF_ERROR_RETURN(&our_in);
3008
0
    }
3009
3010
0
    MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA,
3011
0
             T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3012
0
    vb = tmpl_value(vpt);
3013
0
    if (a_int >= INT8_MIN) {
3014
0
      fr_value_box_init(vb, FR_TYPE_INT8, NULL, false);
3015
0
      vb->vb_int8 = (int8_t)a_int;
3016
0
    } else if (a_int >= INT16_MIN) {
3017
0
      fr_value_box_init(vb, FR_TYPE_INT16, NULL, false);
3018
0
      vb->vb_int16 = (int16_t)a_int;
3019
0
    } else if (a_int >= INT32_MIN) {
3020
0
      fr_value_box_init(vb, FR_TYPE_INT32, NULL, false);
3021
0
      vb->vb_int32 = (int32_t)a_int;
3022
0
    } else {
3023
0
      fr_value_box_init(vb, FR_TYPE_INT64, NULL, false);
3024
0
      vb->vb_int64 = (int64_t)a_int;
3025
0
    }
3026
  /*
3027
   *  Pick the narrowest unsigned type
3028
   */
3029
0
  } else {
3030
0
    uint64_t  a_uint;
3031
3032
0
    slen = fr_sbuff_out(NULL, &a_uint, &our_in);
3033
0
    if (slen <= 0) return slen;
3034
3035
0
    if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
3036
0
      fr_strerror_const("Unexpected text after unsigned integer");
3037
0
      goto error;
3038
0
    }
3039
3040
0
    MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA,
3041
0
             T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3042
0
    vb = tmpl_value(vpt);
3043
0
    if (a_uint <= UINT8_MAX) {
3044
0
      fr_value_box_init(vb, FR_TYPE_UINT8, NULL, false);
3045
0
      vb->vb_uint8 = (uint8_t)a_uint;
3046
0
    } else if (a_uint <= UINT16_MAX) {
3047
0
      fr_value_box_init(vb, FR_TYPE_UINT16, NULL, false);
3048
0
      vb->vb_uint16 = (uint16_t)a_uint;
3049
0
    } else if (a_uint <= UINT32_MAX) {
3050
0
      fr_value_box_init(vb, FR_TYPE_UINT32, NULL, false);
3051
0
      vb->vb_uint32 = (uint32_t)a_uint;
3052
0
    } else {
3053
0
      fr_value_box_init(vb, FR_TYPE_UINT64, NULL, false);
3054
0
      vb->vb_uint64 = (uint64_t)a_uint;
3055
0
    }
3056
0
  }
3057
3058
0
  *out = vpt;
3059
3060
0
  FR_SBUFF_SET_RETURN(in, &our_in);
3061
0
}
3062
3063
static ssize_t tmpl_afrom_float_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
3064
               fr_sbuff_parse_rules_t const *p_rules)
3065
{
3066
  tmpl_t    *vpt;
3067
  fr_sbuff_t  our_in = FR_SBUFF(in);
3068
  double    a_float;
3069
  ssize_t   slen;
3070
  fr_value_box_t  *vb;
3071
3072
  slen = fr_sbuff_out(NULL, &a_float, &our_in);
3073
  if (slen <= 0) return 0;
3074
3075
  if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
3076
    fr_strerror_const("Unexpected text after float");
3077
    FR_SBUFF_ERROR_RETURN(&our_in);
3078
  }
3079
3080
  MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA,
3081
           T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3082
  vb = tmpl_value(vpt);
3083
  fr_value_box_init(vb, FR_TYPE_FLOAT64, NULL, false);
3084
  vb->vb_float64 = a_float;
3085
3086
  *out = vpt;
3087
3088
  FR_SBUFF_SET_RETURN(in, &our_in);
3089
}
3090
3091
static ssize_t tmpl_afrom_time_delta(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
3092
             fr_sbuff_parse_rules_t const *p_rules)
3093
{
3094
  tmpl_t    *vpt;
3095
  fr_sbuff_t  our_in = FR_SBUFF(in);
3096
  fr_time_delta_t a_delta;
3097
  fr_slen_t slen;
3098
  fr_value_box_t  *vb;
3099
3100
  slen = fr_time_delta_from_substr(&a_delta, &our_in, FR_TIME_RES_SEC, true, p_rules ? p_rules->terminals : NULL);
3101
  if (slen <= 0) return 0;
3102
3103
  MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA,
3104
           T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3105
  vb = tmpl_value(vpt);
3106
  fr_value_box_init(vb, FR_TYPE_TIME_DELTA, NULL, false);
3107
  vb->vb_time_delta = a_delta;
3108
3109
  *out = vpt;
3110
3111
  FR_SBUFF_SET_RETURN(in, &our_in);
3112
}
3113
3114
/*
3115
 *  ::value
3116
 *
3117
 *  Treated as enum name.  Note that this check MUST be done after the test for IPv6, as
3118
 *  "::1" is an allowed IPv6 address.
3119
 *
3120
 *  @todo - Mark this up as an enum name?  Or do we really care?  Maybe we want to allow
3121
 *
3122
 *    Service-Type == 'Framed-User'
3123
 *
3124
 *  or
3125
 *
3126
 *    Service-Type == "Framed-User'
3127
 *
3128
 *  as the second one allows for xlat expansions of enum names.
3129
 *
3130
 *  We probably do want to forbid the single-quoted form of enums,
3131
 *  as that doesn't seem to make sense.
3132
 *
3133
 *  We also need to distinguish unresolved bare words as enums
3134
 *  (with :: prefix) from unresolved attributes without an & prefix.
3135
 */
3136
static ssize_t tmpl_afrom_enum(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
3137
             fr_sbuff_parse_rules_t const *p_rules,
3138
             tmpl_rules_t const *t_rules)
3139
{
3140
  tmpl_t    *vpt;
3141
  fr_sbuff_parse_error_t  sberr;
3142
  fr_sbuff_t  our_in = FR_SBUFF(in);
3143
  fr_sbuff_t  *enum_buff;
3144
3145
  FR_SBUFF_TALLOC_THREAD_LOCAL(&enum_buff, 1024, SIZE_MAX);
3146
3147
  /*
3148
   *  If there isn't a "::" prefix, then check for migration flags, and enum.
3149
   *
3150
   *  If we require an enum prefix, then the input can't be an enum, and we don't do any more
3151
   *  parsing.
3152
   *
3153
   *  Otherwise if there's no prefix and no enumv, we know this input can't be an enum name.
3154
   */
3155
  if (!fr_sbuff_adv_past_str_literal(&our_in, "::")) {
3156
    return 0;
3157
3158
  } else if (t_rules->enumv &&
3159
       ((t_rules->enumv->type == FR_TYPE_IPV6_ADDR) ||
3160
       ((t_rules->enumv->type == FR_TYPE_IPV6_PREFIX)))) {
3161
3162
    /*
3163
     *  We can't have enumerated names for IPv6 addresses.
3164
     *
3165
     *  @todo - allow them ONLY if the RHS string is a valid enum name.
3166
     */
3167
    return 0;
3168
  }
3169
3170
  /*
3171
   *  Need to store the value with the prefix, because the value box functions
3172
   *  expect it to be there...
3173
   */
3174
  fr_sbuff_in_strcpy_literal(enum_buff, "::");
3175
3176
  vpt = tmpl_alloc_null(ctx);
3177
3178
  /*
3179
   *  If it doesn't match any other type of bareword, parse it as an enum name.
3180
   *
3181
   *  Note that we don't actually try to resolve the enum name.  The caller is responsible
3182
   *  for doing that.
3183
   */
3184
  if (fr_dict_enum_name_from_substr(enum_buff, &sberr, &our_in, p_rules ? p_rules->terminals : NULL) < 0) {
3185
    /*
3186
     *  Produce our own errors which make
3187
     *  more sense in the context of tmpls
3188
     */
3189
    switch (sberr) {
3190
    case FR_SBUFF_PARSE_ERROR_NOT_FOUND:
3191
      fr_strerror_const("No operand found.  Expected &ref, literal, "
3192
            "'quoted literal', \"%{expansion}\", or enum value");
3193
      break;
3194
3195
    case FR_SBUFF_PARSE_ERROR_FORMAT:
3196
      fr_strerror_const("enum values must contain at least one alpha character");
3197
      break;
3198
3199
    default:
3200
      fr_strerror_const("Unexpected text after enum value.");
3201
      break;
3202
    }
3203
3204
    talloc_free(vpt);
3205
    FR_SBUFF_ERROR_RETURN(&our_in);
3206
  }
3207
3208
  /*
3209
   *  If there's a valid enum name, then we use it.  Otherwise we leave name resolution to run time.
3210
   */
3211
  if (t_rules->enumv) {
3212
    fr_dict_enum_value_t const *dv;
3213
3214
    dv = fr_dict_enum_by_name(t_rules->enumv, fr_sbuff_start(enum_buff), fr_sbuff_used(enum_buff));
3215
    if (dv) {
3216
      tmpl_init(vpt, TMPL_TYPE_DATA, T_BARE_WORD,
3217
          fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3218
      if (unlikely(fr_value_box_copy(vpt, &vpt->data.literal, dv->value) < 0)) {
3219
        talloc_free(vpt);
3220
        return -1;
3221
      }
3222
      vpt->data.literal.enumv = t_rules->enumv;
3223
3224
      *out = vpt;
3225
      FR_SBUFF_SET_RETURN(in, &our_in);
3226
    }
3227
  }
3228
3229
  /*
3230
   *  Either there's no enum, or the enum name didn't match one of the listed ones.  There's no
3231
   *  point in waiting for an enum which might be declared later.  That's not possible, so we fall
3232
   *  back to parsing the various data types.
3233
   */
3234
  if (t_rules->at_runtime) return 0;
3235
3236
  tmpl_init(vpt, TMPL_TYPE_DATA_UNRESOLVED, T_BARE_WORD,
3237
      fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3238
  MEM(vpt->data.unescaped = talloc_bstrndup(vpt, fr_sbuff_start(enum_buff), fr_sbuff_used(enum_buff)));
3239
  *out = vpt;
3240
3241
  FR_SBUFF_SET_RETURN(in, &our_in);
3242
}
3243
3244
/** Convert an arbitrary string into a #tmpl_t
3245
 *
3246
 * @note Unlike #tmpl_afrom_attr_str return code 0 doesn't necessarily indicate failure,
3247
 *  may just mean a 0 length string was parsed. Check to see if the function emitted
3248
 *  a #tmpl_t in *out.
3249
 *
3250
 * @note xlats and regexes are left uncompiled.  This is to support the two pass parsing
3251
 *  done by the modcall code.  Compilation on pass1 of that code could fail, as
3252
 *  attributes or xlat functions registered by modules may not be available (yet).
3253
 *
3254
 * @note For details of attribute parsing see #tmpl_afrom_attr_substr.
3255
 *
3256
 * @param[in,out] ctx   To allocate #tmpl_t in.
3257
 * @param[out] out    Where to write the pointer to the new #tmpl_t.
3258
 * @param[in] in    String to parse.
3259
 * @param[in] quote   Quoting around the tmpl.  Determines what we
3260
 *        attempt to parse the string as.
3261
 * @param[in] p_rules   Formatting rules for the tmpl.
3262
 * @param[in] t_rules   Validation rules for attribute references.
3263
 * @return
3264
 *  - < 0 on error (offset as negative integer)
3265
 *  - >= 0 on success (number of bytes parsed).
3266
 *
3267
 * @see REMARKER to produce pretty error markers from the return value.
3268
 *
3269
 * @see tmpl_afrom_attr_substr
3270
 */
3271
fr_slen_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
3272
          fr_sbuff_t *in, fr_token_t quote,
3273
          fr_sbuff_parse_rules_t const *p_rules,
3274
          tmpl_rules_t const *t_rules)
3275
0
{
3276
0
  fr_sbuff_t    our_in = FR_SBUFF(in);
3277
3278
0
  fr_slen_t   slen;
3279
0
  fr_sbuff_parse_error_t  sberr;
3280
0
  char      *str;
3281
3282
0
  tmpl_t      *vpt = NULL;
3283
0
  DEFAULT_RULES;
3284
3285
0
  CHECK_T_RULES;
3286
3287
0
  *out = NULL;
3288
3289
0
  switch (quote) {
3290
0
  case T_BARE_WORD:
3291
    /*
3292
     *  Skip other bareword types if
3293
     *  we find a '&' prefix.
3294
     */
3295
0
    if (fr_sbuff_is_char(&our_in, '&')) return tmpl_afrom_attr_substr(ctx, NULL, out, in,
3296
0
                      p_rules, t_rules);
3297
3298
    /*
3299
     *  Allow bareword xlats if we
3300
     *  find a '%' prefix.
3301
     */
3302
0
    if (fr_sbuff_is_char(&our_in, '%')) {
3303
0
      tmpl_type_t type = TMPL_TYPE_XLAT;
3304
0
      xlat_exp_head_t *head = NULL;
3305
3306
0
      vpt = tmpl_alloc_null(ctx);
3307
0
      slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules);
3308
0
      if (slen <= 0) FR_SBUFF_ERROR_RETURN(&our_in);
3309
3310
0
      if (xlat_needs_resolving(head)) {
3311
0
        UNRESOLVED_SET(&type);
3312
0
        goto set_tmpl;
3313
3314
0
      } else if (fr_dlist_num_elements(&head->dlist) == 1) {
3315
0
        xlat_exp_t *node = xlat_exp_head(head);
3316
0
        tmpl_t *hoisted;
3317
3318
0
        if (node->type != XLAT_TMPL) goto set_tmpl;
3319
3320
        /*
3321
         *  We were asked to parse a tmpl.  But it turned out to be an xlat %{...}
3322
         *
3323
         *  If that xlat is identically a tmpl such as %{User-Name}, then we just
3324
         *  hoist the tmpl to this node.  Otherwise at run time, we will have an
3325
         *  extra bounce through the xlat code, for no real reason.
3326
         */
3327
0
        hoisted = node->vpt;
3328
3329
0
        (void) talloc_steal(ctx, hoisted);
3330
0
        talloc_free(vpt);
3331
0
        vpt = hoisted;
3332
3333
0
      } else {
3334
0
      set_tmpl:
3335
0
        tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3336
0
        vpt->data.xlat.ex = head;
3337
0
      }
3338
3339
0
      *out = vpt;
3340
3341
0
      TMPL_VERIFY(vpt);
3342
3343
0
      FR_SBUFF_SET_RETURN(in, &our_in);
3344
0
    }
3345
3346
    /*
3347
     *  Deal with explicit casts...
3348
     */
3349
0
    if (!fr_type_is_null(t_rules->cast)) {
3350
0
      slen = tmpl_afrom_value_substr(ctx, out, in, quote, t_rules, true, p_rules);
3351
3352
      /*
3353
       *  If the string doesn't cast to the destination type
3354
       *  parse it as an attribute.
3355
       */
3356
0
      if (slen < 0) return tmpl_afrom_attr_substr(ctx, NULL, out, in, p_rules, t_rules);
3357
0
      return slen;
3358
0
    }
3359
3360
    /*
3361
     *  We're at runtime and have a data type.  Just parse it as that data type, without doing
3362
     *  endless "maybe it's this thing" attempts.
3363
     */
3364
0
    if (t_rules->at_runtime && t_rules->enumv) {
3365
0
      tmpl_rules_t my_t_rules = *t_rules;
3366
3367
0
      fr_assert(fr_type_is_leaf(t_rules->enumv->type));
3368
3369
0
      my_t_rules.cast = my_t_rules.enumv->type;
3370
3371
0
      return tmpl_afrom_value_substr(ctx, out, in, quote, &my_t_rules, true, p_rules);
3372
0
    }
3373
3374
    /*
3375
     *  Prefer enum names to IPv6 addresses.
3376
     */
3377
0
    if (t_rules->enumv && fr_sbuff_is_str_literal(&our_in, "::")) {
3378
0
      slen = tmpl_afrom_enum(ctx, out, &our_in, p_rules, t_rules);
3379
0
      if (slen > 0) goto done_bareword;
3380
0
      fr_assert(!*out);
3381
0
    }
3382
3383
    /*
3384
     *  See if it's a boolean value
3385
     */
3386
0
    slen = tmpl_afrom_bool_substr(ctx, out, &our_in, p_rules);
3387
0
    if (slen > 0) {
3388
0
    done_bareword:
3389
0
      TMPL_VERIFY(*out);
3390
3391
0
      FR_SBUFF_SET_RETURN(in, &our_in);
3392
0
    }
3393
0
    fr_assert(!*out);
3394
3395
    /*
3396
     *  See if it's an octets string
3397
     */
3398
0
    slen = tmpl_afrom_octets_substr(ctx, out, &our_in, p_rules);
3399
0
    if (slen > 0) goto done_bareword;
3400
0
    fr_assert(!*out);
3401
3402
    /*
3403
     *  See if it's a mac address
3404
     *
3405
     *  Needs to be before IPv6 as the pton functions
3406
     *  are too greedy, and on macOS will happily
3407
     *  convert a mac address to an IPv6 address.
3408
     */
3409
0
    slen = tmpl_afrom_ether_substr(ctx, out, &our_in, p_rules);
3410
0
    if (slen > 0) goto done_bareword;
3411
0
    fr_assert(!*out);
3412
3413
    /*
3414
     *  See if it's an IPv4 address or prefix
3415
     */
3416
0
    slen = tmpl_afrom_ipv4_substr(ctx, out, &our_in, p_rules);
3417
0
    if (slen > 0) goto done_bareword;
3418
0
    fr_assert(!*out);
3419
3420
    /*
3421
     *  See if it's an IPv6 address or prefix
3422
     */
3423
0
    slen = tmpl_afrom_ipv6_substr(ctx, out, &our_in, p_rules);
3424
0
    if (slen > 0) goto done_bareword;
3425
0
    fr_assert(!*out);
3426
3427
0
    slen = tmpl_afrom_enum(ctx, out, &our_in, p_rules, t_rules);
3428
0
    if (slen > 0) goto done_bareword;
3429
0
    fr_assert(!*out);
3430
3431
    /*
3432
     *  See if it's a integer
3433
     */
3434
0
    slen = tmpl_afrom_integer_substr(ctx, out, &our_in, p_rules);
3435
0
    if (slen > 0) goto done_bareword;
3436
0
    fr_assert(!*out);
3437
3438
    /*
3439
     *  See if it's a float
3440
     */
3441
0
    slen = tmpl_afrom_float_substr(ctx, out, &our_in, p_rules);
3442
0
    if (slen > 0) goto done_bareword;
3443
0
    fr_assert(!*out);
3444
3445
    /*
3446
     *  See if it's a time delta
3447
     *
3448
     *  We do this after floats and integers so that
3449
     *  they get parsed as integer and float types
3450
     *  and not time deltas.
3451
     */
3452
0
    slen = tmpl_afrom_time_delta(ctx, out, &our_in, p_rules);
3453
0
    if (slen > 0) goto done_bareword;
3454
0
    fr_assert(!*out);
3455
3456
    /*
3457
     *  See if it's an attribute reference
3458
     *  without the prefix.
3459
     */
3460
0
    slen = tmpl_afrom_attr_substr(ctx, NULL, out, &our_in, p_rules, t_rules);
3461
0
    if (slen > 0) goto done_bareword;
3462
0
    fr_assert(!*out);
3463
3464
    /*
3465
     *  We can't parse it as anything, that's an error.
3466
     *
3467
     *  But it may be an enumeration value for an
3468
     *  attribute which is loaded later.  In which
3469
     *  case we allow parsing the enumeration.
3470
     */
3471
0
    if (!fr_sbuff_is_str_literal(&our_in, "::")) {
3472
      /*
3473
       *  Return the error string from parsing the attribute!
3474
       */
3475
0
      FR_SBUFF_ERROR_RETURN(&our_in);
3476
0
    }
3477
3478
    /*
3479
     *  Attempt to resolve enumeration values
3480
     */
3481
0
    vpt = tmpl_alloc_null(ctx);
3482
3483
    /*
3484
     *  If it doesn't match any other type of bareword, parse it as an enum name.
3485
     *
3486
     *  Note that we don't actually try to resolve the enum name.  The caller is responsible
3487
     *  for doing that.
3488
     */
3489
0
    if (fr_dict_enum_name_afrom_substr(vpt, &str, &sberr, &our_in, p_rules ? p_rules->terminals : NULL) < 0) {
3490
      /*
3491
       *  Produce our own errors which make
3492
       *  more sense in the context of tmpls
3493
       */
3494
0
      switch (sberr) {
3495
0
      case FR_SBUFF_PARSE_ERROR_NOT_FOUND:
3496
0
        fr_strerror_const("No operand found.  Expected &ref, literal, "
3497
0
              "'quoted literal', \"%{expansion}\", or enum value");
3498
0
        break;
3499
3500
0
      case FR_SBUFF_PARSE_ERROR_FORMAT:
3501
0
        fr_strerror_const("enum values must contain at least one alpha character");
3502
0
        break;
3503
3504
0
      default:
3505
0
        fr_strerror_const("Unexpected text after enum value.");
3506
0
        break;
3507
0
      }
3508
3509
0
      talloc_free(vpt);
3510
0
      FR_SBUFF_ERROR_RETURN(&our_in);
3511
0
    }
3512
3513
0
    tmpl_init(vpt, TMPL_TYPE_DATA_UNRESOLVED, quote,
3514
0
        fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3515
0
    vpt->data.unescaped = str;
3516
0
    *out = vpt;
3517
3518
0
    FR_SBUFF_SET_RETURN(in, &our_in);
3519
3520
0
  case T_SINGLE_QUOTED_STRING:
3521
    /*
3522
     *  Single quoted strings can be cast
3523
     *  to a specific data type immediately
3524
     *  as they cannot contain expansions.
3525
     */
3526
0
    if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
3527
0
                        t_rules, false,
3528
0
                        p_rules);
3529
0
    vpt = tmpl_alloc_null(ctx);
3530
0
    slen = fr_sbuff_out_aunescape_until(vpt, &str, &our_in, SIZE_MAX,
3531
0
                p_rules ? p_rules->terminals : NULL,
3532
0
                p_rules ? p_rules->escapes : NULL);
3533
0
    tmpl_init(vpt, TMPL_TYPE_DATA_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen, t_rules);
3534
0
    vpt->data.unescaped = str;
3535
0
    break;
3536
3537
0
  case T_DOUBLE_QUOTED_STRING:
3538
0
  {
3539
0
    xlat_exp_head_t *head = NULL;
3540
0
    tmpl_type_t type = TMPL_TYPE_XLAT;
3541
3542
0
    vpt = tmpl_alloc_null(ctx);
3543
3544
0
    slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules);
3545
0
    if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
3546
3547
    /*
3548
     *  If the string doesn't contain an xlat,
3549
     *  and we want to cast it as a specific
3550
     *  type, then do the conversion now.
3551
     */
3552
0
    if (xlat_is_literal(head)) {
3553
0
      if (!fr_type_is_null(t_rules->cast)) {
3554
0
        talloc_free(vpt);   /* Also frees any nodes */
3555
3556
0
        return tmpl_afrom_value_substr(ctx, out,
3557
0
                     in, quote,
3558
0
                     t_rules, false, p_rules);
3559
0
      }
3560
3561
      /*
3562
       *  If the string doesn't contain an xlat
3563
       *  and there's no cast, we just store
3564
       *  the string for conversion later.
3565
       */
3566
0
      if (xlat_to_string(vpt, &str, &head)) {
3567
0
        TALLOC_FREE(head);
3568
3569
0
        tmpl_init(vpt, TMPL_TYPE_DATA_UNRESOLVED, quote,
3570
0
                 fr_sbuff_start(&our_in), slen, t_rules);
3571
0
        vpt->data.unescaped = str;  /* Store the unescaped string for parsing later */
3572
0
        break;
3573
0
      }
3574
0
    }
3575
3576
    /*
3577
     *  If the string actually contains an xlat
3578
     *  store the compiled xlat.
3579
     */
3580
0
    if (xlat_needs_resolving(head)) UNRESOLVED_SET(&type);
3581
3582
0
    tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3583
0
    vpt->data.xlat.ex = head;
3584
0
  }
3585
0
    break;
3586
3587
0
  case T_BACK_QUOTED_STRING:
3588
0
  {
3589
0
    tmpl_type_t   type = TMPL_TYPE_EXEC;
3590
0
    xlat_exp_head_t   *head = NULL;
3591
3592
0
    vpt = tmpl_alloc_null(ctx);
3593
3594
    /*
3595
     *  Ensure that we pre-parse the exec string.
3596
     *  This allows us to catch parse errors as early
3597
     *  as possible.
3598
     *
3599
     *  FIXME - We need an ephemeral version of this
3600
     *  too.
3601
     */
3602
0
    slen = xlat_tokenize_argv(vpt, &head, &our_in, NULL, p_rules, t_rules, true);
3603
0
    if ((slen <= 0) || !head) {
3604
0
      talloc_free(vpt);
3605
0
      FR_SBUFF_ERROR_RETURN(&our_in);
3606
0
    }
3607
3608
    /*
3609
     *  Ensure any xlats produced are bootstrapped
3610
     *  so that their instance data will be created.
3611
     */
3612
0
    if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
3613
0
      talloc_free(vpt);
3614
0
      fr_strerror_const("Failed to bootstrap xlat");
3615
0
      FR_SBUFF_ERROR_RETURN(&our_in);
3616
0
    }
3617
3618
0
    if (xlat_needs_resolving(head)) UNRESOLVED_SET(&type);
3619
3620
0
    tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3621
0
    vpt->data.xlat.ex = head;
3622
0
  }
3623
0
    break;
3624
3625
0
  case T_SOLIDUS_QUOTED_STRING:
3626
0
  {
3627
0
    xlat_exp_head_t   *head = NULL;
3628
0
    tmpl_type_t   type = TMPL_TYPE_REGEX_XLAT;
3629
0
    tmpl_rules_t    arg_t_rules = *t_rules;
3630
3631
0
    arg_t_rules.literals_safe_for = FR_REGEX_SAFE_FOR;
3632
3633
0
    if (!fr_type_is_null(t_rules->cast)) {
3634
0
      fr_strerror_const("Casts cannot be used with regular expressions");
3635
0
      fr_sbuff_set_to_start(&our_in); /* Point to the cast */
3636
0
      FR_SBUFF_ERROR_RETURN(&our_in);
3637
0
    }
3638
3639
0
    vpt = tmpl_alloc_null(ctx);
3640
3641
0
    slen = xlat_tokenize(vpt, &head, &our_in, p_rules, &arg_t_rules);
3642
0
    if (slen < 0) {
3643
0
      talloc_free(vpt);
3644
0
      FR_SBUFF_ERROR_RETURN(&our_in);
3645
0
    }
3646
3647
    /*
3648
     *  Check if the string actually contains an xlat
3649
     *  if it doesn't, we unfortunately still
3650
     *  can't compile the regex here, as we don't know if
3651
     *  it should be ephemeral or what flags should be used
3652
     *  during the compilation.
3653
     *
3654
     *  The caller will need to do the compilation after we
3655
     *  return.
3656
     */
3657
0
    if (xlat_to_string(vpt, &str, &head)) {
3658
0
      tmpl_init(vpt, TMPL_TYPE_REGEX_UNCOMPILED, quote,
3659
0
          fr_sbuff_start(&our_in), slen, t_rules);
3660
0
      vpt->data.unescaped = str;  /* Store the unescaped string for compilation later */
3661
0
      break;
3662
0
    }
3663
    /*
3664
     *  Mark the regex up as a regex-xlat which
3665
     *  will need expanding before evaluation, and can never
3666
     *  be pre-compiled.
3667
     */
3668
0
    if (xlat_needs_resolving(head)) UNRESOLVED_SET(&type);
3669
3670
0
    tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3671
0
    vpt->data.xlat.ex = head;
3672
0
  }
3673
0
    break;
3674
3675
0
  default:
3676
0
    fr_assert_msg(0, "Unknown quote type %i", quote);
3677
0
    FR_SBUFF_ERROR_RETURN(&our_in);
3678
0
  }
3679
3680
0
  TMPL_VERIFY(vpt);
3681
0
  *out = vpt;
3682
3683
0
  FR_SBUFF_SET_RETURN(in, &our_in);
3684
0
}
3685
3686
/** Copy a tmpl
3687
 *
3688
 * Fully duplicates the contents of a tmpl including any nested attribute
3689
 * references.
3690
 *
3691
 * @param[in] ctx to perform allocations under.
3692
 * @param[in] in  tmpl to duplicate.
3693
 * @return
3694
 *  - NULL on error.
3695
 *      - A new tmpl on success.
3696
 */
3697
tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
3698
0
{
3699
0
  tmpl_t *vpt;
3700
3701
0
  MEM(vpt = tmpl_alloc(ctx, in->type, in->quote, in->name, in->len));
3702
0
  vpt->rules = in->rules;
3703
3704
  /*
3705
   *  Copy over the unescaped data
3706
   */
3707
0
  if (tmpl_is_data_unresolved(vpt) || tmpl_is_regex_uncompiled(vpt)) {
3708
0
    if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.unescaped)))) {
3709
0
    error:
3710
0
      talloc_free(vpt);
3711
0
      return NULL;
3712
0
    }
3713
0
  }
3714
3715
  /*
3716
   *  Copy attribute references
3717
   */
3718
0
  else if (tmpl_contains_attr(vpt)) {
3719
0
    if (unlikely(tmpl_attr_copy(vpt, in) < 0)) goto error;
3720
3721
  /*
3722
   *  Copy flags for all regex flavours (and possibly recompile the regex)
3723
   */
3724
0
  } else if (tmpl_contains_regex(vpt)) {
3725
0
    vpt->data.reg_flags = in->data.reg_flags;
3726
3727
    /*
3728
     *  If the tmpl contains a _compiled_ regex
3729
     *  then convert it back to an uncompiled
3730
     *  regex and recompile.
3731
     *
3732
     *  Most of the regex libraries don't allow
3733
     *  copying compiled expressions.
3734
     */
3735
0
     if (tmpl_is_regex(vpt)) {
3736
0
      vpt->type = TMPL_TYPE_REGEX_UNCOMPILED;
3737
0
      if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.reg.src)))) goto error;
3738
0
      if (unlikely(tmpl_regex_compile(vpt, in->data.reg.subcaptures) < 0)) goto error;
3739
0
      return vpt;
3740
0
     }
3741
3742
     /*
3743
      * The regex could also be an xlat.
3744
      */
3745
0
     fr_assert(tmpl_contains_xlat(vpt));
3746
3747
0
     goto copy_xlat;
3748
3749
  /*
3750
   *  Copy the xlat component.
3751
   *
3752
   *  @todo - in general we can't copy an xlat, as the instances need resolving!
3753
   *
3754
   *  We add an assertion here because nothing allocates the head, and we need it.
3755
   */
3756
0
  } else if (tmpl_contains_xlat(vpt)) {
3757
0
  copy_xlat:
3758
0
    fr_assert(in->data.xlat.ex != NULL);
3759
3760
0
    vpt->data.xlat.ex = xlat_exp_head_alloc(vpt);
3761
0
    if (!vpt->data.xlat.ex) goto error;
3762
3763
0
    if (unlikely(xlat_copy(vpt, vpt->data.xlat.ex, in->data.xlat.ex) < 0)) goto error;
3764
3765
0
  } else if (tmpl_is_data(vpt)) {
3766
0
    if (unlikely(fr_value_box_copy(vpt, &vpt->data.literal, &in->data.literal) < 0)) goto error;
3767
3768
0
  } else {
3769
0
    fr_assert(0);  /* copy of this type is unimplemented */
3770
0
  }
3771
3772
0
  TMPL_VERIFY(vpt);
3773
3774
0
  return vpt;
3775
0
}
3776
3777
/** Parse a cast specifier
3778
 *
3779
 *  Note that casts are
3780
 *
3781
 *  (foo)
3782
 *
3783
 *  and NOT
3784
 *
3785
 *  ( foo )
3786
 *
3787
 *  Not for any particular reason, but to emphasize a bit that they're
3788
 *  not mathematical expressions.
3789
 *
3790
 * @param[out] rules  to set the cast type in.
3791
 * @param[in] in  String containing the cast marker.
3792
 * @return
3793
 *  - 0 no cast specifier found.
3794
 *  - >0 the number of bytes parsed.
3795
 *  - <0 offset of parse error.
3796
 */
3797
ssize_t tmpl_cast_from_substr(tmpl_rules_t *rules, fr_sbuff_t *in)
3798
0
{
3799
0
  char      close = '\0';
3800
0
  fr_sbuff_t    our_in = FR_SBUFF(in);
3801
0
  fr_sbuff_marker_t m;
3802
0
  fr_type_t   cast;
3803
0
  ssize_t     slen;
3804
3805
0
  if (fr_sbuff_next_if_char(&our_in, '(')) {
3806
0
    close = ')';
3807
3808
0
  } else {
3809
0
    if (rules) rules->cast = FR_TYPE_NULL;
3810
0
    return 0;
3811
0
  }
3812
3813
0
  fr_sbuff_marker(&m, &our_in);
3814
0
  fr_sbuff_out_by_longest_prefix(&slen, &cast, fr_type_table, &our_in, FR_TYPE_NULL);
3815
0
  if (fr_type_is_null(cast)) {
3816
0
    fr_strerror_const("Unknown data type");
3817
0
    FR_SBUFF_ERROR_RETURN(&our_in);
3818
0
  }
3819
0
  if (fr_type_is_non_leaf(cast)) {
3820
0
    fr_strerror_printf("Forbidden data type '%s' in cast", fr_type_to_str(cast));
3821
0
    FR_SBUFF_ERROR_RETURN(&m);
3822
0
  }
3823
3824
0
  if (!fr_sbuff_next_if_char(&our_in, close)) {
3825
0
    fr_strerror_const("Unterminated cast");
3826
0
    FR_SBUFF_ERROR_RETURN(&our_in);
3827
0
  }
3828
0
  fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
3829
3830
0
  if (rules) rules->cast = cast;
3831
3832
0
  FR_SBUFF_SET_RETURN(in, &our_in);
3833
0
}
3834
3835
/** Set a cast for a tmpl
3836
 *
3837
 * @param[in,out] vpt to set cast for.
3838
 * @param[in] dst_type  to set.
3839
 * @return
3840
 *  - 0 on success.
3841
 *  - -1 on failure.
3842
 */
3843
int tmpl_cast_set(tmpl_t *vpt, fr_type_t dst_type)
3844
0
{
3845
0
  fr_type_t src_type;
3846
3847
0
  switch (dst_type) {
3848
0
  default:
3849
0
    fr_strerror_printf("Forbidden data type '%s' in cast",
3850
0
           fr_type_to_str(dst_type));
3851
0
    return -1;
3852
3853
  /*
3854
   *  We can always remove a cast.
3855
   */
3856
0
  case FR_TYPE_NULL:
3857
0
    goto done;
3858
3859
  /*
3860
   *  Only "base" data types are allowed.  Structural types
3861
   *  and horrid WiMAX crap is forbidden.
3862
   */
3863
0
  case FR_TYPE_LEAF:
3864
0
    break;
3865
0
  }
3866
3867
0
  switch (vpt->type) {
3868
  /*
3869
   *  This should have been fixed before we got here.
3870
   */
3871
0
  case TMPL_TYPE_ATTR_UNRESOLVED:
3872
3873
  /*
3874
   *  By default, tmpl types cannot be cast to anything.
3875
   */
3876
0
  default:
3877
0
    fr_strerror_const("Cannot use cast here.");
3878
0
    return -1;
3879
3880
  /*
3881
   *  These tmpl types are effectively of data type
3882
   *  "string", so they can be cast to anything.
3883
   */
3884
0
  case TMPL_TYPE_XLAT:
3885
0
  case TMPL_TYPE_EXEC:
3886
0
  case TMPL_TYPE_DATA_UNRESOLVED:
3887
0
  case TMPL_TYPE_EXEC_UNRESOLVED:
3888
0
  case TMPL_TYPE_XLAT_UNRESOLVED:
3889
0
    break;
3890
3891
0
  case TMPL_TYPE_DATA:
3892
0
    src_type = tmpl_value_type(vpt);
3893
0
    goto check_types;
3894
3895
0
  case TMPL_TYPE_ATTR:
3896
0
    {
3897
0
      fr_dict_attr_t const *da = tmpl_attr_tail_da(vpt);
3898
3899
      /*
3900
       *  If the attribute has an enum, then the cast means "use the raw value, and not
3901
       *  the enum name".
3902
       */
3903
0
      if (da->type == dst_type) {
3904
0
        if (da->flags.has_value) goto done;
3905
0
        return 0;
3906
0
      }
3907
0
      src_type = da->type;
3908
0
    }
3909
3910
    /*
3911
     *  Suppress casts where they are duplicate, unless there's an enumv.  In which case the
3912
     *  cast means "don't print the enumv value, just print the raw data".
3913
     */
3914
0
  check_types:
3915
0
    if (src_type == dst_type) {
3916
      /*
3917
       *  Cast with enumv means "use the raw value, and not the enum name".
3918
       */
3919
0
      if (tmpl_rules_enumv(vpt)) {
3920
0
        tmpl_rules_enumv(vpt) = NULL;
3921
0
        goto done;
3922
0
      }
3923
0
      return 0;
3924
0
    }
3925
3926
0
    if (!fr_type_cast(dst_type, src_type)) {
3927
0
      fr_strerror_printf("Cannot cast type '%s' to '%s'",
3928
0
             fr_type_to_str(src_type),
3929
0
             fr_type_to_str(dst_type));
3930
0
      return -1;
3931
0
    }
3932
0
    break;
3933
0
  }
3934
3935
0
done:
3936
0
  vpt->rules.cast = dst_type;
3937
0
  return 0;
3938
0
}
3939
3940
#ifdef HAVE_REGEX
3941
/** Parse a set of regular expression flags
3942
 *
3943
 * @param[out] vpt  Write the flags to the regex flags field in this #tmpl_t.
3944
 * @param[in] in  Where to parse the flag string from.
3945
 * @param[in] terminals That mark the end of the regex flag string.
3946
 * @return
3947
 *  - 0 no flags found.
3948
 *  - >0 the number of bytes of flags parsed.
3949
 *  - <0 offset of parse error.
3950
 */
3951
ssize_t tmpl_regex_flags_substr(tmpl_t *vpt, fr_sbuff_t *in, fr_sbuff_term_t const *terminals)
3952
0
{
3953
0
  fr_slen_t slen;
3954
0
  int   err = 0;
3955
3956
0
  fr_assert(tmpl_is_regex_uncompiled(vpt) || tmpl_is_regex_xlat(vpt) || tmpl_is_regex_xlat_unresolved(vpt));
3957
3958
0
  slen = regex_flags_parse(&err, &vpt->data.reg_flags, in, terminals, true);
3959
0
  switch (err) {
3960
0
  case 0:
3961
0
    break;
3962
3963
0
  case -1:  /* Non-flag and non-terminal */
3964
0
  case -2:  /* Duplicate flag */
3965
0
    return slen;
3966
0
  }
3967
3968
0
  return slen;
3969
0
}
3970
#endif
3971
3972
/** @name Change a #tmpl_t type, usually by casting or resolving a reference
3973
 *
3974
 * #tmpl_cast_in_place can be used to convert #TMPL_TYPE_DATA_UNRESOLVED to a #TMPL_TYPE_DATA of a
3975
 * specified #fr_type_t.
3976
 *
3977
 * #tmpl_attr_unknown_add converts a #TMPL_TYPE_ATTR with an unknown #fr_dict_attr_t to a
3978
 * #TMPL_TYPE_ATTR with a known #fr_dict_attr_t, by adding the unknown #fr_dict_attr_t to the main
3979
 * dictionary, and updating the ``tmpl_attr_tail_da`` pointer.
3980
 * @{
3981
 */
3982
3983
/** Determine the correct quoting after a cast
3984
 *
3985
 * @param[in] existing_quote  Exiting quotation type.
3986
 * @param[in] type    Cast type.
3987
 * @param[in] enumv   Enumeration values.
3988
 * @param[in] unescaped   The unescaped value of an enumeration.
3989
 * @param[in] unescaped_len Length of unescaped.
3990
 */
3991
static inline CC_HINT(always_inline)
3992
fr_token_t tmpl_cast_quote(fr_token_t existing_quote,
3993
         fr_type_t type, fr_dict_attr_t const *enumv,
3994
         char const *unescaped, size_t unescaped_len)
3995
0
{
3996
0
  if (!fr_type_is_string(type)) return T_BARE_WORD;
3997
3998
0
  if (enumv && fr_dict_enum_by_name(enumv, unescaped, unescaped_len)) return T_BARE_WORD;
3999
4000
  /*
4001
   *  Leave the original quoting if it's
4002
   *  single or double, else default to
4003
   *  single quoting.
4004
   */
4005
0
  switch (existing_quote) {
4006
0
  case T_SINGLE_QUOTED_STRING:
4007
0
  case T_DOUBLE_QUOTED_STRING:
4008
0
    return existing_quote;
4009
4010
0
  default:
4011
0
    return T_SINGLE_QUOTED_STRING;
4012
0
  }
4013
0
}
4014
4015
4016
/** Convert #tmpl_t of type #TMPL_TYPE_DATA_UNRESOLVED or #TMPL_TYPE_DATA to #TMPL_TYPE_DATA of type specified
4017
 *
4018
 * @note Conversion is done in place.
4019
 * @note For #TMPL_TYPE_DATA_UNRESOLVED, the type will be updated to #TMPL_TYPE_DATA
4020
 *
4021
 * @param[in,out] vpt The template to modify. Must be of type #TMPL_TYPE_DATA_UNRESOLVED
4022
 *      or #TMPL_TYPE_DATA, #TMPL_TYPE_ATTR_UNRESOLVED, or #TMPL_TYPE_ATTR
4023
 * @param[in] type  to cast to.
4024
 * @param[in] enumv Enumerated dictionary values associated with a #fr_dict_attr_t.
4025
 * @return
4026
 *  - 0 on success.
4027
 *  - -1 on failure.
4028
 */
4029
int tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv)
4030
0
{
4031
0
  TMPL_VERIFY(vpt);
4032
4033
0
  fr_assert(tmpl_is_data_unresolved(vpt) || tmpl_is_data(vpt) ||
4034
0
      tmpl_is_attr_unresolved(vpt) || tmpl_is_attr(vpt));
4035
4036
0
  switch (vpt->type) {
4037
0
  case TMPL_TYPE_DATA_UNRESOLVED:
4038
0
  {
4039
0
    char *unescaped = vpt->data.unescaped;
4040
4041
    /*
4042
     *  We're trying to convert an unresolved (bareword)
4043
     *  tmpl to octets.
4044
     *
4045
     *  tmpl_afrom_substr uses the 0x prefix as type
4046
     *  inference, so if it was a hex string the tmpl
4047
     *  type would not have fallen through to
4048
     *  unresolved.
4049
     *
4050
     *  That means if we're trying to resolve it here
4051
     *  it's really a printable string, not a sequence
4052
     *  of hexits, so we just want the binary
4053
     *  representation of that string, and not the hex
4054
     *  to bin conversion.
4055
     */
4056
0
    if (fr_type_is_octets(type)) {
4057
0
      if (fr_value_box_memdup(vpt, &vpt->data.literal, enumv,
4058
0
                  (uint8_t const *)unescaped, talloc_strlen(unescaped),
4059
0
                  false) < 0) return -1;
4060
0
    } else {
4061
0
      if (fr_value_box_from_str(vpt, &vpt->data.literal, type,
4062
0
              enumv,
4063
0
              unescaped, talloc_strlen(unescaped),
4064
0
              NULL) < 0) return -1;
4065
0
    }
4066
0
    vpt->type = TMPL_TYPE_DATA;
4067
0
    vpt->quote = tmpl_cast_quote(vpt->quote, type, enumv,
4068
0
               unescaped, talloc_strlen(unescaped));
4069
0
    talloc_free(unescaped);
4070
0
    fr_value_box_mark_safe_for(&vpt->data.literal, vpt->rules.literals_safe_for);
4071
4072
    /*
4073
     *  The data is now of the correct type, so we don't need to keep a cast.
4074
     */
4075
0
    vpt->rules.cast = FR_TYPE_NULL;
4076
0
  }
4077
0
    break;
4078
4079
0
  case TMPL_TYPE_DATA:
4080
0
  {
4081
0
    if (type == tmpl_value_type(vpt)) return 0; /* noop */
4082
4083
    /*
4084
     *  Enumerations aren't used when casting between
4085
     *  data types.  They're only used when processing
4086
     *  unresolved tmpls.
4087
     *
4088
     *  i.e. TMPL_TYPE_DATA_UNRESOLVED != TMPL_TYPE_DATA(FR_TYPE_STRING)
4089
     */
4090
0
    if (fr_value_box_cast_in_place(vpt, &vpt->data.literal, type, NULL) < 0) return -1;
4091
//    fr_value_box_mark_safe_for(&vpt->data.literal, vpt->rules.literals_safe_for); ??? is this necessary?
4092
4093
    /*
4094
     *  Strings get quoted, everything else is a bare
4095
     *  word...
4096
     */
4097
0
    if (fr_type_is_string(type)) {
4098
0
      vpt->quote = T_SINGLE_QUOTED_STRING;
4099
0
    } else {
4100
0
      vpt->quote = T_BARE_WORD;
4101
0
    }
4102
4103
    /*
4104
     *  The data is now of the correct type, so we don't need to keep a cast.
4105
     */
4106
0
    vpt->rules.cast = FR_TYPE_NULL;
4107
0
  }
4108
0
    break;
4109
4110
0
  case TMPL_TYPE_ATTR:
4111
    /*
4112
     *  Suppress casts to the same type.
4113
     */
4114
0
    if (tmpl_attr_tail_da(vpt)->type == type) {
4115
0
      vpt->rules.cast = FR_TYPE_NULL;
4116
0
      break;
4117
0
    }
4118
0
    FALL_THROUGH;
4119
4120
0
  case TMPL_TYPE_ATTR_UNRESOLVED:
4121
0
    vpt->rules.cast = type;
4122
0
    break;
4123
4124
0
  default:
4125
0
    fr_assert(0);
4126
0
  }
4127
0
  TMPL_VERIFY(vpt);
4128
4129
0
  return 0;
4130
0
}
4131
4132
/** Resolve an unresolved attribute
4133
 *
4134
 * Multi-pass parsing fixups for attribute references.
4135
 *
4136
 * @param[in] vpt   to resolve.
4137
 * @param[in] tr_rules  Combined with the original parse rules for
4138
 *        additional resolution passes.
4139
 * @return
4140
 *  - 0 if all references were resolved.
4141
 *  - -1 if there are unknown attributes which need
4142
 *      adding to the global dictionary first.
4143
 *  - -2 if there are attributes we couldn't resolve.
4144
 */
4145
static inline CC_HINT(always_inline) int tmpl_attr_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
4146
0
{
4147
0
  tmpl_attr_t   *ar = NULL, *next, *prev;
4148
0
  fr_dict_attr_t const  *da, *namespace;
4149
0
  fr_dict_t const   *dict_def;
4150
4151
0
  fr_assert(tmpl_is_attr_unresolved(vpt));
4152
4153
0
  TMPL_VERIFY(vpt);
4154
4155
0
  dict_def = vpt->rules.attr.dict_def;
4156
0
  if (!dict_def || tr_rules->force_dict_def) dict_def = tr_rules->dict_def;
4157
4158
  /*
4159
   *  First component is special because we may need
4160
   *  to search for it in multiple dictionaries.
4161
   *
4162
   *  This emulates what's done in the initial
4163
   *  tokenizer function.
4164
   */
4165
0
  ar = tmpl_attr_list_head(tmpl_attr(vpt));
4166
0
  if (ar->type == TMPL_ATTR_TYPE_UNRESOLVED) {
4167
0
    (void)fr_dict_attr_search_by_name_substr(NULL,
4168
0
               &da,
4169
0
               dict_def,
4170
0
               &FR_SBUFF_IN(ar->ar_unresolved,
4171
0
                      talloc_strlen(ar->ar_unresolved)),
4172
0
               NULL,
4173
0
               true,
4174
0
               vpt->rules.attr.allow_foreign);
4175
0
    if (!da) return -2; /* Can't resolve, maybe the caller can resolve later */
4176
4177
0
    ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
4178
0
    ar->ar_da = da;
4179
0
    ar->ar_parent = fr_dict_root(fr_dict_by_da(da));
4180
4181
    /*
4182
     *  Record the dictionary that was
4183
     *  successfully used for resolution.
4184
     */
4185
0
    vpt->rules.attr.dict_def = tr_rules->dict_def;
4186
4187
    /*
4188
     *  Reach into the next reference
4189
     *  and correct its parent and
4190
     *  namespace.
4191
     */
4192
0
    next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4193
0
    if (next) {
4194
0
      next->ar_parent = da;
4195
0
      next->ar_unresolved_namespace = da;
4196
0
    }
4197
0
  }
4198
4199
  /*
4200
   *  Loop, resolving each unresolved attribute in turn
4201
   */
4202
0
  while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4203
0
    switch (ar->type) {
4204
0
    case TMPL_ATTR_TYPE_NORMAL:
4205
0
    case TMPL_ATTR_TYPE_UNSPEC:
4206
0
      continue; /* Don't need to resolve */
4207
4208
0
    case TMPL_ATTR_TYPE_UNKNOWN:
4209
0
      return -1; /* Unknown attributes must be resolved first */
4210
4211
0
    default:
4212
0
      break;
4213
0
    }
4214
4215
0
    prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
4216
4217
    /*
4218
     *  If the parent is a list AR, then use the default dictionary for the namespace
4219
     */
4220
0
    namespace = (prev && dict_def && tmpl_attr_is_list_attr(prev)) ? fr_dict_root(dict_def) : ar->ar_unresolved_namespace;
4221
4222
0
    (void)fr_dict_attr_by_name_substr(NULL,
4223
0
              &da,
4224
0
              namespace,
4225
0
              &FR_SBUFF_IN(ar->ar_unresolved,
4226
0
                       talloc_strlen(ar->ar_unresolved)),
4227
0
              NULL);
4228
    /*
4229
     *  Still can't resolve, check to see if
4230
     *  the last attribute reference was a
4231
     *  group.
4232
     *
4233
     *  If it was, then we may be able to
4234
     *  fall back to resolving the attribute
4235
     *  in the internal dictionary.
4236
     */
4237
0
    if (!da) {
4238
0
      if (prev && (prev->ar_da->type == FR_TYPE_GROUP)) {
4239
0
        (void)fr_dict_attr_by_name_substr(NULL,
4240
0
                  &da,
4241
0
                  fr_dict_root(fr_dict_internal()),
4242
0
                  &FR_SBUFF_IN(ar->ar_unresolved,
4243
0
                         talloc_strlen(ar->ar_unresolved)),
4244
0
                  NULL);
4245
0
      }
4246
0
      if (!da) return -2;
4247
0
    }
4248
4249
    /*
4250
     *  Known attribute, just rewrite.
4251
     */
4252
0
    ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
4253
0
    ar->ar_da = da;
4254
4255
    /*
4256
     *  Parent should have been corrected in
4257
     *  the previous loop iteration.
4258
     */
4259
0
    fr_assert(ar->ar_parent && !ar->ar_parent->flags.is_unknown);
4260
4261
    /*
4262
     *  Reach into the next reference
4263
     *  and correct its parent.
4264
     */
4265
0
    next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4266
0
    if (next) {
4267
0
      next->ar_parent = da;
4268
0
      next->ar_unresolved_namespace = da;
4269
0
    }
4270
4271
    /*
4272
     *  Remove redundant attributes
4273
     *
4274
     *  If it's not a group or does not specify
4275
     *  an index, the ar is redundant and should
4276
     *  be removed.
4277
     */
4278
0
    prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
4279
0
    if (prev && (prev->ar_da->type != FR_TYPE_GROUP) && (prev->ar_num == NUM_UNSPEC)) {
4280
0
      tmpl_attr_list_remove(tmpl_attr(vpt), prev);
4281
0
      ar->ar_parent = prev->ar_parent;
4282
0
      talloc_free(prev);
4283
0
    }
4284
0
  }
4285
4286
0
  RESOLVED_SET(&vpt->type);
4287
0
  TMPL_VERIFY(vpt);
4288
4289
0
  return 0;
4290
0
}
4291
4292
/** Resolve an unresolved xlat, i.e. one containing unresolved attribute references or xlat functions
4293
 *
4294
 * Multi-pass parsing fixups for attribute references.
4295
 *
4296
 * Works for base types:
4297
 * - TMPL_TYPE_XLAT
4298
 * - TMPL_TYPE_EXEC
4299
 * - TMPL_TYPE_REGEX_XLAT
4300
 *
4301
 * @param[in] vpt   Containing the xlat expansion to resolve.
4302
 * @param[in] tr_rules  Combined with the original parse rules for
4303
 *        additional resolution passes.
4304
 * @return
4305
 *  - 0 on success.
4306
 *  - -1 on failure.
4307
 */
4308
static inline CC_HINT(always_inline)
4309
int tmpl_xlat_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
4310
0
{
4311
0
  if (xlat_resolve(vpt->data.xlat.ex,
4312
0
       &(xlat_res_rules_t){
4313
0
        .tr_rules = tr_rules,
4314
0
        .allow_unresolved = false
4315
0
       }) < 0) return -1;
4316
4317
0
  fr_assert(!xlat_needs_resolving(vpt->data.xlat.ex));
4318
4319
0
  RESOLVED_SET(&vpt->type);
4320
0
  TMPL_VERIFY(vpt);
4321
4322
0
  return 0;
4323
0
}
4324
4325
/** Attempt to resolve functions and attributes in xlats and attribute references
4326
 *
4327
 * @note If resolution is successful, the rules->attr.dict_def field will be modified to
4328
 *   reflect the dictionary resolution was successful in.
4329
 *
4330
 * @param[in,out]   vpt   to resolve.  Should be of type TMPL_TYPE_XLAT_UNRESOLVED
4331
 *          or TMPL_TYPE_ATTR_UNRESOLVED.  All other types will be
4332
 *          noops.
4333
 * @param[in]   tr_rules  Combined with the original parse rules for
4334
 *          additional resolution passes.
4335
 * @return
4336
 *  - 0 on success.
4337
 *  - -1 on failure.
4338
 */
4339
int tmpl_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
4340
0
{
4341
0
  static tmpl_res_rules_t const default_tr_rules = {};
4342
4343
0
  int ret = 0;
4344
4345
0
  if (!tmpl_needs_resolving(vpt)) return 0; /* Nothing to do */
4346
4347
0
  if (!tr_rules) tr_rules = &default_tr_rules;
4348
4349
  /*
4350
   *  Sanity check.  There shouldn't be conflicting
4351
   *  enumvs between the original rules and resolution
4352
   *  rules.
4353
   *
4354
   *  Either the enumv was available during parsing
4355
   *  and shouldn't have changed during subsequent
4356
   *  resolution passes, or it wasn't available at
4357
   *  parse-time, but now is.
4358
   */
4359
0
  if (tr_rules->enumv && tmpl_rules_enumv(vpt) && !tmpl_rules_enumv(vpt)->flags.is_unknown &&
4360
0
      (tr_rules->enumv != tmpl_rules_enumv(vpt))) {
4361
0
        fr_strerror_printf("mismatch between parse-time enumv '%s' and resolution-time enumv '%s'",
4362
0
               tmpl_rules_enumv(vpt)->name, tr_rules->enumv->name);
4363
4364
0
        return -1;
4365
0
  }
4366
4367
  /*
4368
   *  The xlat component of the #tmpl_t needs resolving.
4369
   *
4370
   *  This includes exec tmpls, which are largely xlats
4371
   *  "under the hood".
4372
   */
4373
0
  if (tmpl_contains_xlat(vpt)) {
4374
0
    ret = tmpl_xlat_resolve(vpt, tr_rules);
4375
4376
  /*
4377
   *  The attribute reference needs resolving.
4378
   */
4379
0
  } else if (tmpl_contains_attr(vpt)) {
4380
0
    fr_type_t   dst_type = tmpl_rules_cast(vpt);
4381
4382
0
    fr_assert(vpt->quote == T_BARE_WORD); /* 'User-Name' or "User-Name" is not allowed. */
4383
4384
0
    ret = tmpl_attr_resolve(vpt, tr_rules);
4385
0
    if (ret < 0) return ret;
4386
4387
0
    if (dst_type == tmpl_attr_tail_da(vpt)->type) {
4388
0
      vpt->rules.cast = FR_TYPE_NULL;
4389
0
    }
4390
4391
  /*
4392
   *  Convert unresolved tmpls into enumvs, or failing that, string values.
4393
   *
4394
   *  Unresolved tmpls are by definition TMPL_TYPE_DATA.
4395
   */
4396
0
  } else if (tmpl_is_data_unresolved(vpt)) {
4397
0
    fr_type_t   dst_type = tmpl_rules_cast(vpt);
4398
0
    fr_dict_attr_t const  *enumv = tmpl_rules_enumv(vpt);
4399
4400
    /*
4401
     *  If there wasn't an enumv set in the
4402
     *  original rules, and we now have one
4403
     *  (possibly because the other side of a
4404
     *  binary expression has been resolved),
4405
     *  then use the new enumv.
4406
     */
4407
0
    if (!enumv) enumv = tr_rules->enumv;
4408
4409
    /*
4410
     *  We don't have an explicit output type.  Try to
4411
     *  interpret the data os the enumv data type, OR
4412
     *  if all else fails, it's a string.
4413
     */
4414
0
    if (fr_type_is_null(dst_type)) {
4415
      /*
4416
       *  Infer the cast from the enumv type.
4417
       */
4418
0
      if (enumv) {
4419
0
        dst_type = enumv->type;
4420
4421
0
      } else if (vpt->quote != T_BARE_WORD) {
4422
0
        dst_type = FR_TYPE_STRING;  /* quoted strings are strings */
4423
4424
0
      } else if (strncmp(vpt->data.unescaped, "::", 2) != 0) {
4425
        /*
4426
         *  The rest of the code should have errored out before this.
4427
         */
4428
0
        fr_strerror_printf("Failed resolving data '%s' - it is not an attribute name or a quoted string", vpt->data.unescaped);
4429
0
        return -1;
4430
4431
0
      } else {
4432
        /*
4433
         *  It's a valid enum ::NAME which was added _after_ the dictionaries were
4434
         *  loaded.  That's fine.  fr_value_box_from_substr() will skip over the
4435
         *  "::", and parse the enum name.
4436
         */
4437
0
      }
4438
0
    }
4439
4440
    /*
4441
     *  tmpl_cast_in_place first resolves using
4442
     *  the enumv, _then_ casts using the type.
4443
     */
4444
0
    if (tmpl_cast_in_place(vpt, dst_type, enumv) < 0) return -1;
4445
4446
0
    TMPL_VERIFY(vpt);
4447
  /*
4448
   *  Catch any other cases of unresolved things
4449
   *  we need to address.  We put the assert here
4450
   *  so we don't end up running inappropriate
4451
   *  code for non-debug builds.
4452
   */
4453
0
  } else {
4454
0
    fr_assert(0);
4455
0
  }
4456
4457
0
  TMPL_VERIFY(vpt);
4458
4459
0
  return ret;
4460
0
}
4461
4462
/** Reset the tmpl, leaving only the name in place
4463
 *
4464
 * After calling this function, the tmpl type will revert to TMPL_TYPE_DATA_UNRESOLVED
4465
 * and only the name and quoting will be preserved.
4466
 *
4467
 * @param[in] vpt to reset.
4468
 */
4469
void tmpl_unresolve(tmpl_t *vpt)
4470
0
{
4471
0
  tmpl_t  tmp = {
4472
0
      .type = TMPL_TYPE_DATA_UNRESOLVED,
4473
0
      .name = vpt->name,
4474
0
      .len = vpt->len,
4475
0
      .quote = vpt->quote
4476
0
    };
4477
4478
0
  switch (vpt->type) {
4479
0
  case TMPL_TYPE_UNINITIALISED:
4480
0
  case TMPL_TYPE_MAX:
4481
0
    fr_assert(0);
4482
0
    break;
4483
4484
0
  case TMPL_TYPE_DATA_UNRESOLVED:
4485
0
  case TMPL_TYPE_REGEX_UNCOMPILED:
4486
0
    break;
4487
4488
0
  case TMPL_TYPE_DATA:
4489
0
    fr_value_box_clear(&vpt->data.literal);
4490
0
    break;
4491
4492
  /*
4493
   *  These types contain dynamically allocated
4494
   *  attribute and request references.
4495
   */
4496
0
  case TMPL_TYPE_ATTR:
4497
0
  case TMPL_TYPE_ATTR_UNRESOLVED:
4498
0
    tmpl_attr_list_talloc_free(tmpl_attr(vpt));
4499
0
    tmpl_request_list_talloc_free(&vpt->data.attribute.rr);
4500
0
    break;
4501
4502
  /*
4503
   *  These all store an xlat expansion
4504
   */
4505
0
  case TMPL_TYPE_EXEC:
4506
0
  case TMPL_TYPE_XLAT:
4507
0
  case TMPL_TYPE_REGEX_XLAT:
4508
0
  case TMPL_TYPE_EXEC_UNRESOLVED:
4509
0
  case TMPL_TYPE_XLAT_UNRESOLVED:
4510
0
  case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
4511
0
    TALLOC_FREE(vpt->data.xlat.ex);
4512
0
    break;
4513
4514
0
  case TMPL_TYPE_REGEX:
4515
0
    talloc_free(vpt->data.reg.ex);
4516
0
    break;
4517
4518
0
  }
4519
4520
0
  memcpy(vpt, &tmp, sizeof(*vpt));
4521
0
  vpt->data.unescaped = talloc_bstrdup(vpt, vpt->name);
4522
0
  TMPL_VERIFY(vpt);
4523
0
}
4524
4525
/** Add an unknown #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
4526
 *
4527
 * @param vpt to add. ``tmpl_attr_tail_da`` pointer will be updated to point to the
4528
 *  #fr_dict_attr_t inserted into the dictionary.
4529
 * @return
4530
 *  - 1 noop (did nothing) - Not possible to convert tmpl.
4531
 *  - 0 on success.
4532
 *  - -1 on failure.
4533
 */
4534
int tmpl_attr_unknown_add(tmpl_t *vpt)
4535
{
4536
  tmpl_attr_t   *ar = NULL, *next = NULL;
4537
4538
  if (!vpt) return 1;
4539
4540
  /*
4541
   *  Can't do this for expressions parsed at runtime
4542
   */
4543
  if (vpt->rules.at_runtime) return 1;
4544
4545
  tmpl_assert_type(tmpl_is_attr(vpt));
4546
4547
  TMPL_VERIFY(vpt);
4548
4549
  if (!tmpl_attr_tail_is_unknown(vpt)) return 1;  /* Ensure at least the leaf is unknown */
4550
4551
  while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4552
    fr_dict_attr_t const  *unknown, *known;
4553
4554
    switch (ar->type) {
4555
    case TMPL_ATTR_TYPE_NORMAL:   /* Skip */
4556
    case TMPL_ATTR_TYPE_UNSPEC:
4557
      continue;
4558
4559
    case TMPL_ATTR_TYPE_UNRESOLVED:   /* Shouldn't have been called */
4560
      fr_strerror_const("Remaining attributes are unresolved");
4561
      return -1;
4562
4563
    case TMPL_ATTR_TYPE_UNKNOWN:
4564
      break;
4565
    }
4566
4567
    unknown = ar->ar_unknown;
4568
    known = fr_dict_attr_unknown_add(fr_dict_unconst(fr_dict_by_da(unknown)), unknown);
4569
    if (!known) return -1;
4570
4571
    /*
4572
     *  Fixup the parent of the next unknown
4573
     *  now it's known.
4574
     */
4575
    next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4576
    if (next && (next->type == TMPL_ATTR_TYPE_UNKNOWN) &&
4577
        (next->ar_da->parent == unknown)) {
4578
      if (fr_dict_attr_unknown_parent_to_known(fr_dict_attr_unconst(next->ar_da),
4579
                 known) < 0) return -1;
4580
      next->ar_parent = known;
4581
    }
4582
4583
    /*
4584
     *  Convert the ref to a normal type.
4585
     *  At runtime there should be no
4586
     *  "unknown" references as they should
4587
     *  have all been added to a
4588
     *  dictionary.
4589
     */
4590
    ar->type = TMPL_ATTR_TYPE_NORMAL;
4591
4592
    /*
4593
     *  If the attribute is *NOT* raw then
4594
     *  swap the canonical unknown with the
4595
     *  one that was previously associated
4596
     *  with the tmpl.
4597
     *
4598
     *  This establishes the unknown attribute
4599
     *  in the dictionary if it was really
4600
     *  unknown whilst not mucking up the
4601
     *  types for raw attributes.
4602
     */
4603
    if (!ar_is_raw(ar)) {
4604
      fr_dict_attr_unknown_free(&ar->ar_da);
4605
      ar->ar_da = known;
4606
    } else if (!fr_cond_assert(!next)) {
4607
      fr_strerror_const("Only the leaf may be raw");
4608
      return -1;
4609
    }
4610
  }
4611
4612
  return 0;
4613
}
4614
4615
/** Add an unresolved #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
4616
 *
4617
 * @note fr_dict_attr_add will not return an error if the attribute already exists
4618
 *  meaning that multiple #tmpl_t specifying the same attribute can be
4619
 *  passed to this function to be fixed up, so long as the type and flags
4620
 *  are identical.
4621
 *
4622
 * @param[in] dict_def  Default dictionary to use if none is
4623
 *      specified by the tmpl_attr_tail_unresolved.
4624
 * @param[in] vpt specifying unresolved attribute to add.
4625
 *      ``tmpl_attr_tail_da`` pointer will be updated to
4626
 *      point to the #fr_dict_attr_t inserted
4627
 *      into the dictionary. Lists and requests
4628
 *      will be preserved.
4629
 * @param[in] type  to define unresolved attribute as.
4630
 * @param[in] flags to define unresolved attribute with.
4631
 * @return
4632
 *  - 1 noop (did nothing) - Not possible to convert tmpl.
4633
 *  - 0 on success.
4634
 *  - -1 on failure.
4635
 */
4636
int tmpl_attr_tail_unresolved_add(fr_dict_t *dict_def, tmpl_t *vpt,
4637
          fr_type_t type, fr_dict_attr_flags_t const *flags)
4638
0
{
4639
0
  fr_dict_attr_t const *da;
4640
0
  fr_dict_attr_flags_t our_flags = *flags;
4641
4642
0
  our_flags.name_only = true;
4643
4644
0
  if (!vpt) return -1;
4645
4646
0
  TMPL_VERIFY(vpt);
4647
4648
0
  if (!tmpl_is_attr_unresolved(vpt)) return 1;
4649
4650
0
  if (fr_dict_attr_add(dict_def,
4651
0
           fr_dict_root(fr_dict_internal()), tmpl_attr_tail_unresolved(vpt), 0, type, &our_flags) < 0) {
4652
0
    return -1;
4653
0
  }
4654
0
  da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_def), tmpl_attr_tail_unresolved(vpt));
4655
0
  if (!da) return -1;
4656
4657
0
  if (type != da->type) {
4658
0
    fr_strerror_printf("Attribute %s of type %s already defined with type %s",
4659
0
           da->name, fr_type_to_str(type),
4660
0
           fr_type_to_str(da->type));
4661
0
    return -1;
4662
0
  }
4663
4664
0
  if (memcmp(flags, &da->flags, sizeof(*flags)) != 0) {
4665
0
    fr_strerror_printf("Attribute %s already defined with different flags", da->name);
4666
0
    return -1;
4667
0
  }
4668
4669
0
  tmpl_attr_set_da(vpt, da);
4670
0
  vpt->type = TMPL_TYPE_ATTR;
4671
4672
0
  return 0;
4673
0
}
4674
4675
#ifdef HAVE_REGEX
4676
/** Convert a TMPL_TYPE_REGEX_UNCOMPILED into a TMPL_TYPE_REGEX
4677
 *
4678
 * Other regex types become noops.
4679
 */
4680
ssize_t tmpl_regex_compile(tmpl_t *vpt, bool subcaptures)
4681
0
{
4682
0
  ssize_t slen;
4683
0
  char  *unescaped = vpt->data.unescaped;
4684
4685
0
  if (tmpl_is_regex_xlat(vpt) || tmpl_is_regex(vpt)) return 0; /* Don't need compiling */
4686
4687
0
  fr_assert(tmpl_is_regex_uncompiled(vpt));
4688
4689
0
  slen = regex_compile(vpt, &vpt->data.reg.ex,
4690
0
           unescaped, talloc_strlen(unescaped),
4691
0
           &vpt->data.reg_flags, subcaptures, vpt->rules.at_runtime);
4692
0
  if (slen <= 0) return vpt->quote != T_BARE_WORD ? slen - 1 : slen; /* Account for the quoting */
4693
4694
0
  vpt->type = TMPL_TYPE_REGEX;
4695
0
  vpt->data.reg.src = unescaped;      /* Keep this around for debugging and copying */
4696
0
  vpt->data.reg.subcaptures = subcaptures;
4697
4698
0
  TMPL_VERIFY(vpt);
4699
4700
0
  return slen;
4701
0
}
4702
#endif
4703
/** @} */
4704
4705
/** @name Print the contents of a #tmpl_t
4706
 * @{
4707
 */
4708
fr_slen_t tmpl_request_ref_list_print(fr_sbuff_t *out, FR_DLIST_HEAD(tmpl_request_list) const *rql)
4709
0
{
4710
0
  fr_sbuff_t    our_out = FR_SBUFF(out);
4711
0
  tmpl_request_t    *rr = tmpl_request_list_head(rql);
4712
4713
  /*
4714
   *  Print request references
4715
   */
4716
0
  while (rr) {
4717
0
    FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, tmpl_request_ref_print_table, rr->request, "<INVALID>");
4718
0
    rr = tmpl_request_list_next(rql, rr);
4719
0
    if (rr) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4720
0
  }
4721
4722
0
  FR_SBUFF_SET_RETURN(out, &our_out);
4723
0
}
4724
4725
/** Print an attribute or list #tmpl_t to a string
4726
 *
4727
 * This function is the direct counterpart to #tmpl_afrom_attr_substr.
4728
 *
4729
 * @param[in] out   Where to write the presentation format #tmpl_t string.
4730
 * @param[in] vpt   to print.
4731
 * @return
4732
 *  - >0 the number of bytes written to the out buffer.
4733
 *  - 0 invalid argument.
4734
 *  - <0 the number of bytes we would have needed to complete the print.
4735
 */
4736
fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt)
4737
0
{
4738
0
  tmpl_attr_t   *ar = NULL;
4739
0
  fr_da_stack_t   stack;
4740
0
  fr_sbuff_t    our_out = FR_SBUFF(out);
4741
0
  fr_slen_t   slen;
4742
4743
0
  TMPL_VERIFY(vpt);
4744
4745
  /*
4746
   *  Only print things we can print...
4747
   */
4748
0
  switch (vpt->type) {
4749
0
  case TMPL_TYPE_ATTR_UNRESOLVED:
4750
0
  case TMPL_TYPE_ATTR:
4751
0
    break;
4752
4753
0
  default:
4754
0
    fr_assert(0);
4755
0
    return 0;
4756
0
  }
4757
4758
  /*
4759
   *  Print request references
4760
   */
4761
0
  slen = tmpl_request_ref_list_print(&our_out, &vpt->data.attribute.rr);
4762
0
  if (slen > 0) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4763
0
  if (slen < 0) return slen;
4764
4765
  /*
4766
   *
4767
   *  If the leaf attribute is unknown and raw we
4768
   *  add the raw. prefix.
4769
   *
4770
   *  If the leaf attribute is unknown and not raw
4771
   *  we add the .unknown prefix.
4772
   *
4773
   */
4774
0
  if (tmpl_attr_tail_is_raw(vpt)) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "raw.");
4775
4776
  /*
4777
   *  Print attribute identifiers
4778
   */
4779
0
  ar = NULL;
4780
0
  while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4781
0
    switch(ar->type) {
4782
0
    case TMPL_ATTR_TYPE_UNSPEC:
4783
0
      break;
4784
4785
0
    case TMPL_ATTR_TYPE_NORMAL:
4786
0
    case TMPL_ATTR_TYPE_UNKNOWN:
4787
0
    {
4788
0
      int i, depth = 0;
4789
4790
0
      fr_assert(ar->ar_parent);  /* All normal and unknown attributes must have parents */
4791
4792
0
      fr_proto_da_stack_build_partial(&stack, ar->ar_parent, ar->ar_da);
4793
4794
      /*
4795
       *  First component in the list has everything built
4796
       */
4797
0
      if (ar == tmpl_attr_list_head(tmpl_attr(vpt))) {
4798
0
        depth = ar->ar_parent->depth - 1;  /* Adjust for array index */
4799
      /*
4800
       *  Everything else skips the first component
4801
       */
4802
0
      } else {
4803
0
        depth = ar->ar_parent->depth;
4804
0
      }
4805
4806
      /*
4807
       *  Root attributes will be skipped by the build
4808
       *  function, so da[0] contains the attribute
4809
       *  we're looking for.
4810
       */
4811
0
      if (depth < 0) depth = 0;
4812
4813
      /*
4814
       *  Print from our parent depth to the AR we're processing
4815
       *
4816
       *  For refs we skip the attribute pointed to be the ref
4817
       *  and just print its children.
4818
       *
4819
       *  In addition skip printing "request." in most cases.
4820
       */
4821
0
      if ((stack.da[depth] == request_attr_request) && tmpl_attr_list_next(tmpl_attr(vpt), ar) &&
4822
0
          (ar->filter.type == TMPL_ATTR_FILTER_TYPE_NONE)) continue;
4823
4824
0
      for (i = depth; (unsigned int)i < ar->ar_da->depth; i++) {
4825
0
        FR_SBUFF_IN_STRCPY_RETURN(&our_out, stack.da[i]->name);
4826
4827
        /*
4828
         *  Print intermediary separators
4829
         *  if necessary.
4830
         */
4831
0
        if (((unsigned int)i + 1) < ar->ar_da->depth) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4832
0
      }
4833
0
    }
4834
0
      break;
4835
4836
    /*
4837
     *  For unresolved attribute we print the raw identifier we
4838
     *  got when parsing the tmpl.
4839
     */
4840
0
    case TMPL_ATTR_TYPE_UNRESOLVED:
4841
0
    {
4842
0
      unsigned int  i, depth;
4843
4844
      /*
4845
       *  This is the first unresolved component in a potential
4846
       *  chain of unresolved components.  Print the path up to
4847
       *  the last known parent.
4848
       */
4849
0
      if (ar->ar_parent && !ar->ar_parent->flags.is_root) {
4850
0
        fr_proto_da_stack_build_partial(&stack, ar->ar_parent, ar->ar_parent);
4851
0
        if (ar->ar_parent->flags.is_root) {
4852
0
          depth = 0;
4853
0
        } else {
4854
0
          depth = ar->ar_parent->depth - 1;
4855
0
        }
4856
4857
0
        for (i = depth; i < ar->ar_parent->depth; i++) {
4858
0
          FR_SBUFF_IN_STRCPY_RETURN(&our_out, stack.da[i]->name);
4859
0
          FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4860
0
        }
4861
0
      }
4862
      /*
4863
       *  Then print the unresolved component
4864
       */
4865
0
      FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->ar_unresolved);
4866
0
      break;
4867
0
    }
4868
0
    }
4869
4870
0
    if (ar_filter_is_none(ar)) {
4871
      /* do nothing */
4872
4873
0
    } else if (ar_filter_is_num(ar)) {
4874
0
      switch (ar->ar_num) {
4875
0
      case NUM_UNSPEC:
4876
0
        break;
4877
4878
0
      case NUM_ALL:
4879
0
        FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[*]");
4880
0
        break;
4881
4882
0
      case NUM_COUNT:
4883
0
        FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[#]");
4884
0
        break;
4885
4886
0
      case NUM_LAST:
4887
0
        FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[n]");
4888
0
        break;
4889
4890
0
      default:
4891
0
        FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "[%i]", ar->ar_num);
4892
0
        break;
4893
0
      }
4894
4895
0
    } else if (ar_filter_is_cond(ar)) {
4896
0
      FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[");
4897
0
      (void) xlat_print(&our_out, ar->ar_cond, NULL);
4898
0
      FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "]");
4899
4900
0
    } else {
4901
0
      fr_assert(0);
4902
0
    }
4903
4904
0
    if (tmpl_attr_list_next(tmpl_attr(vpt), ar)) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4905
0
  }
4906
0
  FR_SBUFF_SET_RETURN(out, &our_out);
4907
0
}
4908
4909
/** Print a #tmpl_t to a string
4910
 *
4911
 * This function should primarily be used for regenerating vpt->name when the contents
4912
 * of the #tmpl_t is changed programmatically, or when the #tmpl_t is being serialized
4913
 * in some non-standard way, i.e. as a value for a field in a database.
4914
 *
4915
 * This function is the direct counterpart to #tmpl_afrom_substr.
4916
 *
4917
 * @note Does not print flags for regular expressions, as the quoting char is needed
4918
 *   to separate the elements of the expression.
4919
 *   Call regex_flags_print to write the flags values to the output buffer.
4920
 *
4921
 * @param[out] out    Where to write the presentation format #tmpl_t string.
4922
 * @param[in] vpt   to print.
4923
 * @param[in] e_rules   Escaping rules used to print strings.
4924
 * @return
4925
 *  - >0 the number of bytes written to the out buffer.
4926
 *  - 0 invalid argument.
4927
 *  - <0 the number of bytes we would have needed to complete the print.
4928
 */
4929
fr_slen_t tmpl_print(fr_sbuff_t *out, tmpl_t const *vpt,
4930
         fr_sbuff_escape_rules_t const *e_rules)
4931
0
{
4932
0
  fr_sbuff_t  our_out = FR_SBUFF(out);
4933
4934
0
  TMPL_VERIFY(vpt);
4935
4936
0
  switch (vpt->type) {
4937
0
  case TMPL_TYPE_ATTR_UNRESOLVED:
4938
0
  case TMPL_TYPE_ATTR:
4939
0
    FR_SBUFF_RETURN(tmpl_attr_print, &our_out, vpt);
4940
0
    break;
4941
4942
0
  case TMPL_TYPE_DATA:
4943
0
          FR_SBUFF_RETURN(fr_value_box_print, &our_out, tmpl_value(vpt), e_rules);
4944
0
    break;
4945
4946
0
  case TMPL_TYPE_REGEX:
4947
0
    FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, vpt->name, vpt->len);  /* Fixme - double escapes */
4948
0
    break;
4949
4950
0
  case TMPL_TYPE_REGEX_UNCOMPILED:
4951
0
    FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
4952
0
    break;
4953
4954
0
  case TMPL_TYPE_UNINITIALISED:
4955
0
  case TMPL_TYPE_MAX:
4956
0
    fr_sbuff_terminate(out);
4957
0
    break;
4958
4959
  /*
4960
   *  The remaining types will either
4961
   *  be xlat expansions, or need
4962
   *  resolving, in which case the
4963
   *  unescaped string is available
4964
   *  in vpt->unescaped.
4965
   */
4966
0
  default:
4967
0
    if (tmpl_contains_xlat(vpt)) {
4968
0
      FR_SBUFF_RETURN(xlat_print, &our_out, tmpl_xlat(vpt), e_rules);
4969
0
      break;
4970
0
    }
4971
4972
0
    if (tmpl_needs_resolving(vpt)) {
4973
0
      FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
4974
0
      break;
4975
0
    }
4976
4977
0
    fr_assert_fail("Can't print invalid tmpl type %s", tmpl_type_to_str(vpt->type));
4978
4979
    /*
4980
     *  Ensure we do something sane for non-debug builds
4981
     */
4982
0
    fr_sbuff_terminate(out);
4983
0
    return 0;
4984
0
  }
4985
4986
0
  FR_SBUFF_SET_RETURN(out, &our_out);
4987
0
}
4988
4989
/** Print a #tmpl_t to a string with quotes
4990
 *
4991
 * This function should be used when the tmpl is embedded in some other construct
4992
 * in the server's configuration.
4993
 *
4994
 * It adds standard quoting around tmpl's used as operands in expressions and applies
4995
 * the correct escaping rules.
4996
 *
4997
 * @param[out] out    Where to write the presentation format #tmpl_t string.
4998
 * @param[in] vpt   to print.
4999
 * @return
5000
 *  - >0 the number of bytes written to the out buffer.
5001
 *  - 0 invalid argument.
5002
 *  - <0 the number of bytes we would have needed to complete the print.
5003
 */
5004
fr_slen_t tmpl_print_quoted(fr_sbuff_t *out, tmpl_t const *vpt)
5005
0
{
5006
0
  fr_sbuff_t our_out = FR_SBUFF(out);
5007
5008
0
  char quote = fr_token_quote[vpt->quote];
5009
5010
0
  if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
5011
0
  FR_SBUFF_RETURN(tmpl_print, &our_out, vpt,
5012
0
      fr_value_escape_by_quote[vpt->quote]);
5013
0
  if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
5014
5015
  /*
5016
   *  Optionally print the flags
5017
   */
5018
0
  if (vpt->type & TMPL_FLAG_REGEX) FR_SBUFF_RETURN(regex_flags_print, &our_out, tmpl_regex_flags(vpt));
5019
5020
0
  FR_SBUFF_SET_RETURN(out, &our_out);
5021
0
}
5022
/** @} */
5023
5024
5025
#ifdef WITH_VERIFY_PTR
5026
/** Used to check whether areas of a tmpl_t are zeroed out
5027
 *
5028
 * @param ptr Offset to begin checking at.
5029
 * @param len How many bytes to check.
5030
 * @return
5031
 *  - Pointer to the first non-zero byte.
5032
 *  - NULL if all bytes were zero.
5033
 */
5034
static uint8_t const *is_zeroed(uint8_t const *ptr, size_t len)
5035
0
{
5036
0
  size_t i;
5037
5038
0
  for (i = 0; i < len; i++) {
5039
0
    if (ptr[i] != 0x00) return ptr + i;
5040
0
  }
5041
5042
0
  return NULL;
5043
0
}
5044
5045
/** Verify that unused regions of the struct are zeroed out
5046
 *
5047
 */
5048
0
#define CHECK_ZEROED(_vpt, _field) is_zeroed(((uint8_t const *)&(_vpt)->data) + sizeof((_vpt)->data._field), sizeof((_vpt)->data) - sizeof((_vpt)->data._field))
5049
5050
5051
/** Print hex data
5052
 *
5053
 */
5054
0
#define PRINT_NON_ZEROED(_vpt, _field, _nz_ptr) \
5055
0
do { \
5056
0
  DEBUG("Expected live portion %p-%p (0-%zu)", \
5057
0
        _vpt, \
5058
0
        (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
5059
0
        sizeof((_vpt)->data._field)); \
5060
0
  DEBUG("Expected zero portion %p-%p (%zu-%zu)", \
5061
0
        (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
5062
0
        (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data), \
5063
0
        sizeof((_vpt)->data._field), sizeof((_vpt)->data)); \
5064
0
  HEX_MARKER1((uint8_t const *)&vpt->data, sizeof(vpt->data), nz - (uint8_t const *)&vpt->data, "non-zero memory", ""); \
5065
0
} while (0)
5066
5067
5068
/** Verify the attribute reference in a tmpl_t make sense
5069
 *
5070
 * @note If the attribute reference is invalid, causes the server to exit.
5071
 *
5072
 * @param file obtained with __FILE__.
5073
 * @param line obtained with __LINE__.
5074
 * @param vpt to check.
5075
 */
5076
void tmpl_attr_verify(char const *file, int line, tmpl_t const *vpt)
5077
0
{
5078
0
  tmpl_attr_t *ar = NULL;
5079
0
  tmpl_attr_t   *slow = NULL, *fast = NULL;
5080
0
  tmpl_attr_t *seen_unknown = NULL;
5081
0
  tmpl_attr_t *seen_unresolved = NULL;
5082
5083
0
  fr_assert(tmpl_is_attr_unresolved(vpt) || tmpl_is_attr(vpt));
5084
5085
  /*
5086
   *  Loop detection
5087
   */
5088
0
  while ((slow = tmpl_attr_list_next(tmpl_attr(vpt), slow)) &&
5089
0
         (fast = tmpl_attr_list_next(tmpl_attr(vpt), fast))) {
5090
5091
    /*
5092
     *  Advances twice as fast as slow...
5093
     */
5094
0
    fast = tmpl_attr_list_next(tmpl_attr(vpt), fast);
5095
0
    fr_fatal_assert_msg(fast != slow,
5096
0
            "CONSISTENCY CHECK FAILED %s[%u]:  Looping reference list found.  "
5097
0
            "Fast pointer hit slow pointer at \"%s\"",
5098
0
            file, line,
5099
0
            slow->type == TMPL_ATTR_TYPE_UNRESOLVED ? slow->ar_unresolved :
5100
0
            slow->da ? slow->da->name : "(null-attr)");
5101
0
  }
5102
5103
  /*
5104
   *  Lineage type check
5105
   *
5106
   *  Known attribute cannot come after unresolved or unknown attributes
5107
   *  Unknown attributes cannot come after unresolved attributes
5108
   */
5109
0
  if (!tmpl_is_list(vpt)) while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
5110
0
    switch (ar->type) {
5111
0
    case TMPL_ATTR_TYPE_NORMAL:
5112
0
      if (seen_unknown) {
5113
0
        tmpl_attr_debug(stderr, vpt);
5114
0
        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5115
0
                 "TMPL_TYPE_ATTR known attribute \"%s\" "
5116
0
                 "occurred after unknown attribute %s "
5117
0
                 "in attr ref list",
5118
0
                 file, line,
5119
0
                 ar->da->name,
5120
0
                 ar->unknown.da->name);
5121
0
      }
5122
0
      if (seen_unresolved) {
5123
0
        tmpl_attr_debug(stderr, vpt);
5124
0
        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5125
0
                 "TMPL_TYPE_ATTR known attribute \"%s\" "
5126
0
                 "occurred after unresolved attribute \"%s\""
5127
0
                 "in attr ref list",
5128
0
                 file, line,
5129
0
                 ar->da->name,
5130
0
                 ar->ar_unresolved);
5131
0
      }
5132
0
      fr_fatal_assert_msg(ar->ar_parent,
5133
0
              "CONSISTENCY CHECK FAILED %s[%u]: attr ref missing parent",
5134
0
              file, line);
5135
5136
0
      if (ar->ar_parent->type != FR_TYPE_GROUP) {
5137
0
        fr_fatal_assert_msg(ar->ar_parent == ar->ar_da->parent,
5138
0
                "CONSISTENCY CHECK FAILED %s[%u]: attr ref has wrong parent: "
5139
0
                "Expected %s, got %s",
5140
0
                file, line,
5141
0
                ar->ar_da->parent->name,
5142
0
                ar->ar_parent->name);
5143
5144
0
      }
5145
0
      break;
5146
5147
0
    case TMPL_ATTR_TYPE_UNSPEC:
5148
0
      if (seen_unknown) {
5149
0
        tmpl_attr_debug(stderr, vpt);
5150
0
        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5151
0
                 "TMPL_TYPE_ATTR unspecified attribute "
5152
0
                 "occurred after unknown attribute %s "
5153
0
                 "in attr ref list",
5154
0
                 file, line,
5155
0
                 ar->unknown.da->name);
5156
0
      }
5157
0
      if (seen_unresolved) {
5158
0
        tmpl_attr_debug(stderr, vpt);
5159
0
        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5160
0
                 "TMPL_TYPE_ATTR unspecified attribute "
5161
0
                 "occurred after unresolved attribute \"%s\""
5162
0
                 "in attr ref list",
5163
0
                 file, line,
5164
0
                 ar->ar_unresolved);
5165
0
      }
5166
0
      break;
5167
5168
0
    case TMPL_ATTR_TYPE_UNRESOLVED:
5169
0
      seen_unresolved = ar;
5170
0
      fr_fatal_assert_msg(ar->ar_unresolved_namespace,
5171
0
              "CONSISTENCY CHECK FAILED %s[%u]: unresolved attr ref missing namespace",
5172
0
              file, line);
5173
0
      break;
5174
5175
0
    case TMPL_ATTR_TYPE_UNKNOWN:
5176
0
      seen_unknown = ar;
5177
0
      if (seen_unresolved) {
5178
0
        tmpl_attr_debug(stderr, vpt);
5179
0
        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5180
0
                 "TMPL_TYPE_ATTR unknown attribute \"%s\" "
5181
0
                 "occurred after unresolved attribute %s "
5182
0
                 "in attr ref list",
5183
0
                 file, line, ar->da->name,
5184
0
                 ar->ar_unresolved);
5185
0
      }
5186
0
      break;
5187
0
    }
5188
0
  }
5189
0
}
5190
5191
/** Verify fields of a tmpl_t make sense
5192
 *
5193
 * @note If the #tmpl_t is invalid, causes the server to exit.
5194
 *
5195
 * @param file obtained with __FILE__.
5196
 * @param line obtained with __LINE__.
5197
 * @param vpt to check.
5198
 */
5199
void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
5200
0
{
5201
0
  uint8_t const *nz;
5202
5203
0
  fr_assert(vpt);
5204
5205
0
  if (tmpl_is_uninitialised(vpt)) {
5206
0
    fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: tmpl_t type was "
5207
0
             "TMPL_TYPE_UNINITIALISED (uninitialised)", file, line);
5208
0
  }
5209
5210
0
  if (vpt->type >= TMPL_TYPE_MAX) {
5211
0
    fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: tmpl_t type was %i "
5212
0
             "(outside range of tmpl_type_table)", file, line, vpt->type);
5213
0
  }
5214
5215
0
  if (!vpt->name && (vpt->quote != T_INVALID)) {
5216
0
    char quote = vpt->quote >= T_TOKEN_LAST ? '?' : fr_token_quote[vpt->quote];
5217
5218
0
    fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: Quote type '%c' (%i) was set for NULL name",
5219
0
             file, line, quote, vpt->quote);
5220
0
  }
5221
5222
0
  if (vpt->name && (vpt->quote == T_INVALID)) {
5223
0
    fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: No quoting type was set for name \"%.*s\"",
5224
0
             file, line, (int)vpt->len, vpt->name);
5225
0
  }
5226
5227
  /*
5228
   *  Do a memcmp of the bytes after where the space allocated for
5229
   *  the union member should have ended and the end of the union.
5230
   *  These should always be zero if the union has been initialised
5231
   *  properly.
5232
   *
5233
   *  If they're still all zero, do TMPL_TYPE specific checks.
5234
   */
5235
0
  switch (vpt->type) {
5236
0
  case TMPL_TYPE_DATA_UNRESOLVED:
5237
0
    if (!vpt->data.unescaped) {
5238
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA_UNRESOLVED "
5239
0
               "unescaped field is NULL", file, line);
5240
0
     }
5241
0
    break;
5242
5243
0
  case TMPL_TYPE_XLAT_UNRESOLVED:
5244
0
    if (!vpt->data.xlat.ex) {
5245
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5246
0
               "has a NULL xlat.ex field", file, line);
5247
5248
0
    }
5249
5250
0
    if (!xlat_needs_resolving(vpt->data.xlat.ex)) {
5251
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
5252
0
               "does not have 'needs resolving' flag set", file, line);
5253
0
    }
5254
0
    break;
5255
5256
0
  case TMPL_TYPE_XLAT:
5257
0
    if (!vpt->data.xlat.ex) {
5258
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5259
0
               "has a NULL xlat.ex field", file, line);
5260
5261
0
    }
5262
0
    break;
5263
5264
/* @todo When regexes get converted to xlat the flags field of the regex union is used
5265
  case TMPL_TYPE_XLAT_UNRESOLVED:
5266
    if (is_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
5267
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
5268
               "has non-zero bytes in its data union", file, line);
5269
    }
5270
    break;
5271
5272
  case TMPL_TYPE_XLAT:
5273
    if (CHECK_ZEROED(vpt, xlat)) {
5274
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5275
               "has non-zero bytes after the data.xlat pointer in the union", file, line);
5276
    }
5277
    break;
5278
*/
5279
5280
0
  case TMPL_TYPE_EXEC:
5281
0
  case TMPL_TYPE_EXEC_UNRESOLVED:
5282
    /* tmpl_xlat(vpt) can be initialized */
5283
0
    break;
5284
5285
0
  case TMPL_TYPE_ATTR_UNRESOLVED:
5286
0
    if ((tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) &&
5287
0
        ((tmpl_attr_t *)tmpl_attr_list_tail(tmpl_attr(vpt)))->da) {
5288
0
#ifndef NDEBUG
5289
0
      tmpl_attr_debug(stderr, vpt);
5290
0
#endif
5291
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR_UNRESOLVED contains %u "
5292
0
               "references", file, line, tmpl_attr_list_num_elements(tmpl_attr(vpt)));
5293
0
    }
5294
0
    break;
5295
5296
0
  case TMPL_TYPE_ATTR:
5297
0
    if ((nz = CHECK_ZEROED(vpt, attribute))) {
5298
0
      PRINT_NON_ZEROED(vpt, attribute, nz);
5299
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5300
0
               "has non-zero bytes after the data.attribute struct in the union",
5301
0
               file, line);
5302
0
    }
5303
5304
0
    if (tmpl_attr_tail_is_unspecified(vpt)) {
5305
0
      fr_assert(vpt->rules.cast == FR_TYPE_NULL);
5306
0
      break;
5307
0
    }
5308
5309
0
    if (tmpl_attr_tail_is_unknown(vpt)) {
5310
0
      if (tmpl_attr_tail_da(vpt) != tmpl_attr_tail_unknown(vpt)) {
5311
0
        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5312
0
                 "da is marked as unknown, but address is not equal to the template's "
5313
0
                 "unknown da pointer", file, line);
5314
0
      }
5315
    /*
5316
     *  Raw attributes may not have been added to the dictionary yet
5317
     */
5318
0
    } else {
5319
0
      fr_dict_attr_t const  *da;
5320
0
      fr_dict_t const   *dict;
5321
5322
      /*
5323
       *  Attribute may be present with multiple names
5324
       */
5325
0
      dict = fr_dict_by_da(tmpl_attr_tail_da(vpt));
5326
0
      if (!dict) {
5327
0
        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5328
0
                 "attribute \"%s\" (%s) not rooted in a dictionary",
5329
0
                 file, line, tmpl_attr_tail_da(vpt)->name,
5330
0
                 fr_type_to_str(tmpl_attr_tail_da(vpt)->type));
5331
0
      }
5332
5333
0
      da = tmpl_attr_tail_da(vpt);
5334
0
      if (!tmpl_attr_tail_is_raw(vpt) && (da != tmpl_attr_tail_da(vpt))) {
5335
0
        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5336
0
                 "dictionary pointer %p \"%s\" (%s) "
5337
0
                 "and global dictionary pointer %p \"%s\" (%s) differ",
5338
0
                 file, line,
5339
0
                 tmpl_attr_tail_da(vpt), tmpl_attr_tail_da(vpt)->name,
5340
0
                 fr_type_to_str(tmpl_attr_tail_da(vpt)->type),
5341
0
                 da, da->name,
5342
0
                 fr_type_to_str(da->type));
5343
0
      }
5344
5345
0
      tmpl_attr_verify(file, line, vpt);
5346
0
    }
5347
0
    break;
5348
5349
0
  case TMPL_TYPE_DATA:
5350
0
    if ((nz = CHECK_ZEROED(vpt, literal))) {
5351
0
      PRINT_NON_ZEROED(vpt, literal, nz);
5352
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA "
5353
0
               "has non-zero bytes after the data.literal struct in the union",
5354
0
               file, line);
5355
0
    }
5356
5357
0
    if (fr_type_is_null(tmpl_value_type(vpt))) {
5358
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
5359
0
               "FR_TYPE_NULL (uninitialised)", file, line);
5360
0
    }
5361
5362
0
    if (tmpl_value_type(vpt) >= FR_TYPE_MAX) {
5363
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
5364
0
               "%i (outside the range of fr_type_ts)", file, line, tmpl_value_type(vpt));
5365
0
    }
5366
    /*
5367
     *  Unlike fr_pair_ts we can't guarantee that fr_pair_t_TMPL buffers will
5368
     *  be talloced. They may be allocated on the stack or in global variables.
5369
     */
5370
0
    switch (tmpl_value_type(vpt)) {
5371
0
    case FR_TYPE_STRING:
5372
0
      if (tmpl_value(vpt)->vb_strvalue[tmpl_value_length(vpt)] != '\0') {
5373
0
        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA char buffer not \\0 "
5374
0
                 "terminated", file, line);
5375
0
      }
5376
0
      break;
5377
5378
0
    case FR_TYPE_STRUCTURAL:
5379
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA is of type TLV",
5380
0
               file, line);
5381
5382
0
    default:
5383
0
      break;
5384
0
    }
5385
5386
0
    break;
5387
5388
0
  case TMPL_TYPE_REGEX_UNCOMPILED:
5389
0
  case TMPL_TYPE_REGEX_XLAT:
5390
0
  case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
5391
#ifndef HAVE_REGEX
5392
    fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_XLAT_UNRESOLVED - No regex support",
5393
             file, line);
5394
#endif
5395
0
    break;
5396
5397
0
  case TMPL_TYPE_REGEX:
5398
0
#ifdef HAVE_REGEX
5399
0
    if (tmpl_regex(vpt) == NULL) {
5400
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
5401
0
               "reg.ex field was NULL", file, line);
5402
0
    }
5403
#else
5404
    fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX - No regex support",
5405
             file, line);
5406
#endif
5407
0
    break;
5408
5409
0
  case TMPL_TYPE_UNINITIALISED:
5410
0
    fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_UNINITIALISED", file, line);
5411
5412
0
  case TMPL_TYPE_MAX:
5413
0
    fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_MAX", file, line);
5414
0
  }
5415
0
}
5416
#endif
5417
5418
static const bool array_terminal[SBUFF_CHAR_CLASS] = {
5419
  [ ']' ] = true,
5420
};
5421
5422
0
#define return_P(_x) fr_strerror_const(_x);goto return_p
5423
5424
0
#define is_char(_offset, _x) (((p + _offset) < end) && (p[_offset] == _x))
5425
5426
/** Preparse a string in preparation for passing it to tmpl_afrom_substr()
5427
 *
5428
 *  Note that the input string is not modified, which means that the
5429
 *  tmpl_afrom_substr() function MUST un-escape it.
5430
 *
5431
 *  The caller should pass 'out' and 'outlen' to tmpl_afrom_substr()
5432
 *  as 'in' and 'inlen'.  The caller should also pass 'type'.
5433
 *  The caller should also pass do_unescape=true.
5434
 *
5435
 * @param[out] out  start of the string to parse
5436
 * @param[out] outlen length of the string to parse
5437
 * @param      in where we start looking for the string
5438
 * @param      inlen  length of the input string
5439
 * @param[out] type token type of the string.
5440
 * @return
5441
 *  - > 0, amount of parsed string to skip, to get to the next token
5442
 *  - <=0, -offset in 'start' where the parse error was located
5443
 */
5444
ssize_t tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t inlen,
5445
          fr_token_t *type)
5446
0
{
5447
0
  char const *p = in, *end = in + inlen;
5448
0
  char quote;
5449
0
  char close;
5450
0
  int depth;
5451
0
  bool triple;
5452
5453
0
  *type = T_INVALID;
5454
5455
0
  while (isspace((uint8_t) *p) && (p < end)) p++;
5456
0
  if (p >= end) return p - in;
5457
5458
0
  switch (*p) {
5459
    /*
5460
     *  Allow bare xlat's
5461
     */
5462
0
  case '%':
5463
0
    if (p[1] != '{') {
5464
0
      char const *q;
5465
5466
0
      q = p + 1;
5467
5468
      /*
5469
       *  Function syntax: %foo(...)
5470
       */
5471
0
      while ((q < end) && (isalnum((int) *q) || (*q == '.') || (*q == '_') || (*q == '-'))) {
5472
0
        q++;
5473
0
      }
5474
5475
0
      if (*q != '(') {
5476
0
        p++;
5477
0
        fr_strerror_const("Invalid character after '%'");
5478
0
      return_p:
5479
0
        return -(p - in);
5480
0
      }
5481
5482
      /*
5483
       *  Return the whole %foo(...) string.
5484
       */
5485
0
      *out = p;
5486
0
      if (*type == T_INVALID) *type = T_BARE_WORD;
5487
0
      close = ')';
5488
5489
0
      p = q + 1;
5490
0
      depth = 1;
5491
0
      goto loop;
5492
0
    }
5493
5494
    /*
5495
     *  For now, %{...} is treated as a double-quoted
5496
     *  string.  Once we clean other things up, the
5497
     *  xlats will be treated as strongly typed values
5498
     *  / lists on their own.
5499
     */
5500
0
    if (*type == T_INVALID) *type = T_BARE_WORD;
5501
0
    depth = 0;
5502
0
    close = '}';
5503
5504
    /*
5505
     *  Xlat's are quoted by %{...} / %(...) nesting, not by
5506
     *  escapes, so we need to do special escaping.
5507
     */
5508
0
    *out = p;
5509
0
  loop:
5510
0
    while (*p) {
5511
      /*
5512
       *  End of expansion.  Return the entire
5513
       *  expansion, including the enclosing %{}
5514
       *  characters.
5515
       */
5516
0
      if ((*p == '}') || (*p == ')')) {
5517
0
        bool match = (*p == close);
5518
5519
0
        p++;
5520
0
        depth--;
5521
5522
0
        if (depth == 0) {
5523
0
          if (!match) break;
5524
5525
0
          *outlen = p - (*out);
5526
0
          return p - in;
5527
0
        }
5528
0
        continue;
5529
0
      }
5530
5531
0
      if (*p == '\\') {
5532
0
        if (is_char(1, '\0')) {
5533
0
          return_P("End of string after escape");
5534
0
        }
5535
0
        p += 2;
5536
0
        continue;
5537
0
      }
5538
5539
0
      if ((p[0] == '%') && (is_char(1, '{') || is_char(1, '('))) {
5540
0
        if (is_char(2, '\0')) {
5541
0
          return_P("End of string after expansion");
5542
0
        }
5543
5544
0
        p += 2;
5545
0
        depth++;
5546
0
        continue;
5547
0
      }
5548
5549
      /*
5550
       *  Allow (...) and {...}
5551
       */
5552
0
      if ((*p == '{') || (*p == '(')) {
5553
0
        p++;
5554
0
        depth++;
5555
0
        continue;
5556
0
      }
5557
5558
0
      p++;
5559
0
    }
5560
5561
    /*
5562
     *  End of input without end of string.
5563
     *  Point the error to the start of the string.
5564
     */
5565
0
    p = *out;
5566
0
    return_P("Unterminated expansion");
5567
5568
0
  case '/':
5569
0
    goto bare_word;
5570
5571
0
  case '\'':
5572
0
    quote = *(p++);
5573
0
    *type = T_SINGLE_QUOTED_STRING;
5574
0
    goto skip_string;
5575
5576
0
  case '`':
5577
0
    quote = *(p++);
5578
0
    *type = T_BACK_QUOTED_STRING;
5579
0
    goto skip_string;
5580
5581
0
  case '"':
5582
0
    quote = *(p++);
5583
0
    *type = T_DOUBLE_QUOTED_STRING;
5584
5585
    /*
5586
     *  We're not trying to do a *correct* parsing of
5587
     *  every string here.  We're trying to do a
5588
     *  simple parse that isn't wrong.  We therefore
5589
     *  accept most anything that's vaguely well
5590
     *  formed, and rely on the next stage to do a
5591
     *  more rigorous check.
5592
     */
5593
0
  skip_string:
5594
0
    if (is_char(0, quote) && is_char(1, quote)) {
5595
0
      triple = true;
5596
0
      p += 2;
5597
0
    } else {
5598
0
      triple = false;
5599
0
    }
5600
0
    *out = p;
5601
5602
0
    while (*p) {
5603
0
      if (p >= end) goto unterminated;
5604
5605
      /*
5606
       *  End of string.  Tell the caller the
5607
       *  length of the data inside of the
5608
       *  string, and return the number of
5609
       *  characters to skip.
5610
       */
5611
0
      if (*p == quote) {
5612
0
        if (!triple) {
5613
0
          *outlen = p - (*out);
5614
0
          p++;
5615
0
          return p - in;
5616
5617
0
        }
5618
5619
5620
0
        if (is_char(1, quote) && is_char(2, quote)) {
5621
0
          *outlen = p - (*out);
5622
0
          p += 3;
5623
0
          return p - in;
5624
0
        }
5625
5626
0
        p++;
5627
0
        continue;
5628
0
      }
5629
5630
0
      if (*p == '\\') {
5631
0
        if (is_char(1, '\0')) {
5632
0
          return_P("End of string after escape");
5633
0
        }
5634
0
        p++;
5635
0
      }
5636
0
      p++;
5637
0
    }
5638
5639
    /*
5640
     *  End of input without end of string.
5641
     *  Point the error to the start of the string.
5642
     */
5643
0
    unterminated:
5644
0
    p = *out;
5645
0
    return_P("Unterminated string");
5646
5647
0
  case '&':
5648
0
    *out = p; /* the output string starts with '&' */
5649
0
    p++;
5650
0
    quote = '[';
5651
0
    goto skip_word;
5652
5653
0
  default:
5654
0
  bare_word:
5655
0
    *out = p;
5656
0
    quote = '['; /* foo[1] is OK */
5657
5658
0
  skip_word:
5659
0
    *type = T_BARE_WORD;
5660
0
    depth = 0;
5661
5662
    /*
5663
     *  Allow *most* things.  But stop on spaces and special characters.
5664
     */
5665
0
    while (*p) {
5666
0
      if (isspace((uint8_t) *p)) {
5667
0
        break;
5668
0
      }
5669
5670
0
      if (*p == '$') {
5671
0
        if (p[1] == '{') {
5672
0
          p += 2;
5673
0
          depth++;
5674
0
          continue;
5675
5676
0
        } else if ((p[1] == 'E') &&
5677
0
             (p[2] == 'N') &&
5678
0
             (p[3] == 'V') &&
5679
0
             (p[4] == '{')) {
5680
0
          p += 5;
5681
0
          depth++;
5682
0
          continue;
5683
5684
0
        } else {
5685
          /*
5686
           *  Bare '$' is wrong...
5687
           */
5688
0
          break;
5689
0
        }
5690
0
      }
5691
5692
0
      if (*p == '%') {
5693
0
        if (p[1] == '{') {
5694
0
          p += 2;
5695
0
          depth++;
5696
0
          continue;
5697
0
        }
5698
5699
0
        p++;
5700
0
        continue;
5701
0
      }
5702
5703
      /*
5704
       *  If we're inside of a ${...} expansion,
5705
       *  then allow everything until the
5706
       *  closing '}'.  This means that we can
5707
       *  do ${foo[bar].baz}, among other
5708
       *  thingds.
5709
       */
5710
0
      if (depth > 0) {
5711
0
        if (*p == '}') {
5712
0
          depth--;
5713
0
        }
5714
5715
0
        p++;
5716
0
        continue;
5717
0
      }
5718
5719
      /*
5720
       *  '-' is special.  We allow it for
5721
       *  attribute names, BUT it's a
5722
       *  terminating token if the NEXT
5723
       *  character is '='.
5724
       *
5725
       *  We have the same criteria for IPv6
5726
       *  addresses and tagged attributes.  ':'
5727
       *  is allowed, but ':=' is a breaking
5728
       *  token.
5729
       */
5730
0
      if ((*p == '-') || (*p == ':')) {
5731
0
        if (p[1] == '=') break;
5732
0
        p++;
5733
0
        continue;
5734
0
      }
5735
5736
      /*
5737
       *  Allowed in attribute names, and/or
5738
       *  host names and IP addresses, and IPv6 addresses.
5739
       */
5740
0
      if ((*p == '.') || (*p == '/') || (*p == '_') || (*p == '*') ||
5741
0
          (*p == ']') || (*p == '@')) {
5742
0
        p++;
5743
0
        continue;
5744
0
      }
5745
5746
      /*
5747
       *  [...] is an IPv6 address.
5748
       */
5749
0
      if ((p == in) && (*p == '[')) {
5750
0
        p++;
5751
0
        continue;
5752
0
      }
5753
5754
      /*
5755
       *  Allow letters and numbers
5756
       */
5757
0
      if (((*p >= 'a') && (*p <= 'z')) ||
5758
0
          ((*p >= 'A') && (*p <= 'Z')) ||
5759
0
          ((*p >= '0') && (*p <= '9'))) {
5760
0
        p++;
5761
0
        continue;
5762
0
      }
5763
5764
      /*
5765
       *  Allow UTF-8 sequences.
5766
       */
5767
0
      if (*(uint8_t const *)p > 0x80) {
5768
0
        p++;
5769
0
        continue;
5770
0
      }
5771
5772
      /*
5773
       *  If it's an attribute reference, allow
5774
       *  a few more things inside of a "[...]"
5775
       *  block.
5776
       */
5777
0
      if (*p == '[') {
5778
0
        if (quote != '[') {
5779
0
          return_P("Invalid location for '['");
5780
0
        }
5781
5782
0
        p++;
5783
5784
        /*
5785
         *  Allow [#], etc.  But stop
5786
         *  immediately after the ']'.
5787
         */
5788
0
        if ((*p == '#') || (*p == '*') || (*p == 'n')) {
5789
0
          p++;
5790
5791
0
        } else {
5792
0
          ssize_t slen;
5793
0
          bool eol = false;
5794
5795
0
          slen = fr_skip_condition(p, end, array_terminal, &eol);
5796
0
          if (slen < 0) {
5797
0
            p += -slen;
5798
0
            return -(p - in);
5799
0
          }
5800
0
          p += slen;
5801
0
          continue;
5802
0
        }
5803
5804
0
        if (*p == ']') {
5805
0
          p++;
5806
0
          continue;
5807
0
        }
5808
0
      }
5809
5810
      /*
5811
       *  Everything else is a breaking token
5812
       */
5813
0
      break;
5814
0
    }
5815
5816
    /*
5817
     *  Give some slightly better error messages.
5818
     */
5819
0
    if (*p == '\\') {
5820
0
      return_P("Unexpected escape");
5821
0
    }
5822
5823
0
    if ((*p == '"') || (*p == '\'') || (*p == '`')) {
5824
0
      return_P("Unexpected start of string");
5825
0
    }
5826
5827
0
    if (p == *out) {
5828
0
      return_P("Empty string is invalid");
5829
0
    }
5830
5831
0
    *outlen = p - (*out);
5832
0
    break;
5833
0
  }
5834
5835
0
  return p - in;
5836
0
}
5837
5838
/** Return whether or not async is required for this tmpl.
5839
 *
5840
 *  If the tmpl is needs_async, then it is async
5841
 *  If the tmpl is not needs_async, then it will not yield
5842
 *
5843
 *  If the tmpl yields, then async is required.
5844
 */
5845
bool tmpl_async_required(tmpl_t const *vpt)
5846
0
{
5847
0
  switch (vpt->type) {
5848
0
  case TMPL_TYPE_EXEC:  /* we don't have "exec no-wait" here */
5849
0
  case TMPL_TYPE_XLAT_UNRESOLVED: /* we have no idea, so be safe */
5850
#ifndef HAVE_REGEX
5851
  case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
5852
#endif
5853
0
    return true;
5854
5855
#ifndef HAVE_REGEX
5856
  case TMPL_TYPE_REGEX_XLAT:
5857
#endif
5858
0
  case TMPL_TYPE_XLAT:  /* synchronous xlats use unlang_interpret_synchronous() */
5859
0
  default:
5860
0
    return false;
5861
0
  }
5862
0
}
5863
5864
/** Initialize a set of rules from a parent set of rules, and a parsed tmpl_t
5865
 *
5866
 */
5867
void tmpl_rules_child_init(TALLOC_CTX *ctx, tmpl_rules_t *out, tmpl_rules_t const *parent, tmpl_t *vpt)
5868
0
{
5869
0
  fr_dict_attr_t const *da;
5870
0
  fr_dict_attr_t const *ref;
5871
0
  fr_dict_t const *dict, *internal;
5872
5873
0
  *out = *parent;
5874
  /* don't set ->parent=parent, that is only for switching subrequest, etc. */
5875
5876
0
  if (!tmpl_is_attr(vpt)) return;
5877
5878
0
  da = tmpl_attr_tail_da(vpt);
5879
5880
  /*
5881
   *  The input tmpl is a leaf.  We must parse the child as
5882
   *  a normal attribute reference (as with the parent tmpl).
5883
   */
5884
0
  if (!fr_type_structural[da->type]) {
5885
0
    return;
5886
0
  }
5887
5888
0
  if (vpt->rules.attr.request_def) {
5889
0
    tmpl_request_ref_list_acopy(ctx, &out->attr.request_def, vpt->rules.attr.request_def);
5890
0
  }
5891
0
  out->attr.list_def = tmpl_list(vpt);
5892
5893
  /*
5894
   *  Parse the child attributes in the context of the parent struct / tlv / whatever.
5895
   */
5896
0
  if (da->type != FR_TYPE_GROUP) {
5897
0
    out->attr.dict_def = fr_dict_by_da(da);
5898
0
    out->attr.namespace = da;
5899
0
    return;
5900
0
  }
5901
5902
0
  ref = fr_dict_attr_ref(da);
5903
0
  dict = fr_dict_by_da(ref);
5904
0
  internal = fr_dict_internal();
5905
5906
  /*
5907
   *  Groups MAY change dictionaries.  If so, then swap the dictionary and the parent.
5908
   */
5909
0
  if ((dict != internal) && (dict != out->attr.dict_def)) {
5910
0
    out->attr.dict_def = dict;
5911
0
    out->attr.namespace = ref;
5912
0
  }
5913
5914
  /*
5915
   *  Otherwise the reference is swapping FROM a protocol
5916
   *  dictionary TO the internal dictionary, and TO an
5917
   *  internal group.  We fall back to leaving well enough
5918
   *  alone, and leave things as-is.  This allows internal
5919
   *  grouping attributes to appear anywhere.
5920
   */
5921
0
}
5922
5923
static void tmpl_attr_rules_debug(tmpl_attr_rules_t const *at_rules)
5924
0
{
5925
0
  FR_FAULT_LOG("\tdict_def          = %s", at_rules->dict_def ? fr_dict_root(at_rules->dict_def)->name : "");
5926
0
  FR_FAULT_LOG("\tnamespace         = %s", at_rules->namespace ? at_rules->namespace->name : "");
5927
5928
0
  FR_FAULT_LOG("\tlist_def          = %s", at_rules->list_def ? at_rules->list_def->name : "");
5929
5930
0
  FR_FAULT_LOG("\tallow_unknown     = %u", at_rules->allow_unknown);
5931
0
  FR_FAULT_LOG("\tallow_unresolved  = %u", at_rules->allow_unresolved);
5932
0
  FR_FAULT_LOG("\tallow_wildcard    = %u", at_rules->allow_wildcard);
5933
0
  FR_FAULT_LOG("\tallow_foreign     = %u", at_rules->allow_foreign);
5934
0
  FR_FAULT_LOG("\tdisallow_filters  = %u", at_rules->disallow_filters);
5935
0
}
5936
5937
5938
void tmpl_rules_debug(tmpl_rules_t const *rules)
5939
0
{
5940
0
  FR_FAULT_LOG("\tparent     = %p", rules->parent);
5941
0
  FR_FAULT_LOG("    attr {");
5942
0
  tmpl_attr_rules_debug(&rules->attr);
5943
0
  FR_FAULT_LOG("    }");
5944
0
  FR_FAULT_LOG("\tenumv      = %s", rules->enumv ? rules->enumv->name : "");
5945
0
  FR_FAULT_LOG("\tcast       = %s", fr_type_to_str(rules->cast));
5946
0
  FR_FAULT_LOG("\tat_runtime = %u", rules->at_runtime);
5947
0
  FR_FAULT_LOG("\tliterals_safe_for = %lx", rules->literals_safe_for);
5948
5949
0
}