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: abcd7687f12df54427a3b1aeb85f7bc057d366a3 $
19 *
20 * @brief #VALUE_PAIR template functions
21 * @file main/tmpl.c
22 *
23 * @ingroup AVP
24 *
25 * @copyright 2014-2015 The FreeRADIUS server project
26 */
27 RCSID("$Id: abcd7687f12df54427a3b1aeb85f7bc057d366a3 $")
28
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/rad_assert.h>
31
32 #include <ctype.h>
33
34 /** Map #tmpl_type_t values to descriptive strings
35 */
36 FR_NAME_NUMBER const tmpl_names[] = {
37 { "literal", TMPL_TYPE_LITERAL },
38 { "xlat", TMPL_TYPE_XLAT },
39 { "attr", TMPL_TYPE_ATTR },
40 { "unknown attr", TMPL_TYPE_ATTR_UNDEFINED },
41 { "list", TMPL_TYPE_LIST },
42 { "regex", TMPL_TYPE_REGEX },
43 { "exec", TMPL_TYPE_EXEC },
44 { "data", TMPL_TYPE_DATA },
45 { "parsed xlat", TMPL_TYPE_XLAT_STRUCT },
46 { "parsed regex", TMPL_TYPE_REGEX_STRUCT },
47 { "null", TMPL_TYPE_NULL },
48 { NULL, 0 }
49 };
50
51 /** Map keywords to #pair_lists_t values
52 */
53 const FR_NAME_NUMBER pair_lists[] = {
54 { "request", PAIR_LIST_REQUEST },
55 { "reply", PAIR_LIST_REPLY },
56 { "control", PAIR_LIST_CONTROL }, /* New name should have priority */
57 { "config", PAIR_LIST_CONTROL },
58 { "session-state", PAIR_LIST_STATE },
59 #ifdef WITH_PROXY
60 { "proxy-request", PAIR_LIST_PROXY_REQUEST },
61 { "proxy-reply", PAIR_LIST_PROXY_REPLY },
62 #endif
63 #ifdef WITH_COA
64 { "coa", PAIR_LIST_COA },
65 { "coa-reply", PAIR_LIST_COA_REPLY },
66 { "disconnect", PAIR_LIST_DM },
67 { "disconnect-reply", PAIR_LIST_DM_REPLY },
68 #endif
69 { NULL , -1 }
70 };
71
72 /** Map keywords to #request_refs_t values
73 */
74 const FR_NAME_NUMBER request_refs[] = {
75 { "outer", REQUEST_OUTER },
76 { "current", REQUEST_CURRENT },
77 { "parent", REQUEST_PARENT },
78 { NULL , -1 }
79 };
80
81 /** @name Parse list and request qualifiers to #pair_lists_t and #request_refs_t values
82 *
83 * These functions also resolve #pair_lists_t and #request_refs_t values to #REQUEST
84 * structs and the head of #VALUE_PAIR lists in those structs.
85 *
86 * For adding new #VALUE_PAIR to the lists, the #radius_list_ctx function can be used
87 * to obtain the appropriate TALLOC_CTX pointer.
88 *
89 * @note These don't really have much to do with #vp_tmpl_t. They're in the same
90 * file as they're used almost exclusively by the tmpl_* functions.
91 * @{
92 */
93
94 /** Resolve attribute name to a #pair_lists_t value.
95 *
96 * Check the name string for #pair_lists qualifiers and write a #pair_lists_t value
97 * for that list to out. This value may be passed to #radius_list, along with the current
98 * #REQUEST, to get a pointer to the actual list in the #REQUEST.
99 *
100 * If we're sure we've definitely found a list qualifier token delimiter (``:``) but the
101 * string doesn't match a #radius_list qualifier, return 0 and write #PAIR_LIST_UNKNOWN
102 * to out.
103 *
104 * If we can't find a string that looks like a request qualifier, set out to def, and
105 * return 0.
106 *
107 * @note #radius_list_name should be called before passing a name string that may
108 * contain qualifiers to #dict_attrbyname.
109 *
110 * @param[out] out Where to write the list qualifier.
111 * @param[in] name String containing list qualifiers to parse.
112 * @param[in] def the list to return if no qualifiers were found.
113 * @return 0 if no valid list qualifier could be found, else the number of bytes consumed.
114 * The caller may then advanced the name pointer by the value returned, to get the
115 * start of the attribute name (if any).
116 *
117 * @see pair_list
118 * @see radius_list
119 */
radius_list_name(pair_lists_t * out,char const * name,pair_lists_t def)120 size_t radius_list_name(pair_lists_t *out, char const *name, pair_lists_t def)
121 {
122 char const *p = name;
123 char const *q;
124
125 /* This should never be a NULL pointer */
126 rad_assert(name);
127
128 /*
129 * Try and determine the end of the token
130 */
131 for (q = p; dict_attr_allowed_chars[(uint8_t) *q]; q++);
132
133 switch (*q) {
134 /*
135 * It's a bareword made up entirely of dictionary chars
136 * check and see if it's a list qualifier, and if it's
137 * not, return the def and say we couldn't parse
138 * anything.
139 */
140 case '\0':
141 *out = fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
142 if (*out != PAIR_LIST_UNKNOWN) return q - p;
143 *out = def;
144 return 0;
145
146 /*
147 * It may be a list qualifier delimiter. Because of tags
148 * We need to check that it doesn't look like a tag suffix.
149 * We do this by looking at the chars between ':' and the
150 * next token delimiter, and seeing if they're all digits.
151 */
152 case ':':
153 {
154 char const *d = q + 1;
155
156 if (isdigit((int) *d)) {
157 while (isdigit((int) *d)) d++;
158
159 /*
160 * Char after the number string
161 * was a token delimiter, so this is a
162 * tag, not a list qualifier.
163 */
164 if (!dict_attr_allowed_chars[(uint8_t) *d]) {
165 *out = def;
166 return 0;
167 }
168 }
169
170 *out = fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
171 if (*out == PAIR_LIST_UNKNOWN) return 0;
172
173 return (q + 1) - name; /* Consume the list and delimiter */
174 }
175
176 default:
177 *out = def;
178 return 0;
179 }
180 }
181
182 /** Resolve attribute #pair_lists_t value to an attribute list.
183 *
184 * The value returned is a pointer to the pointer of the HEAD of a #VALUE_PAIR list in the
185 * #REQUEST. If the head of the list changes, the pointer will still be valid.
186 *
187 * @param[in] request containing the target lists.
188 * @param[in] list #pair_lists_t value to resolve to #VALUE_PAIR list. Will be NULL if list
189 * name couldn't be resolved.
190 * @return a pointer to the HEAD of a list in the #REQUEST.
191 *
192 * @see tmpl_cursor_init
193 * @see fr_cursor_init
194 */
radius_list(REQUEST * request,pair_lists_t list)195 VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
196 {
197 if (!request) return NULL;
198
199 switch (list) {
200 /* Don't add default */
201 case PAIR_LIST_UNKNOWN:
202 break;
203
204 case PAIR_LIST_REQUEST:
205 if (!request->packet) return NULL;
206 return &request->packet->vps;
207
208 case PAIR_LIST_REPLY:
209 if (!request->reply) return NULL;
210 return &request->reply->vps;
211
212 case PAIR_LIST_CONTROL:
213 return &request->config;
214
215 case PAIR_LIST_STATE:
216 return &request->state;
217
218 #ifdef WITH_PROXY
219 case PAIR_LIST_PROXY_REQUEST:
220 if (!request->proxy) break;
221 return &request->proxy->vps;
222
223 case PAIR_LIST_PROXY_REPLY:
224 if (!request->proxy_reply) break;
225 return &request->proxy_reply->vps;
226 #endif
227 #ifdef WITH_COA
228 case PAIR_LIST_COA:
229 if (request->coa &&
230 (request->coa->proxy->code == PW_CODE_COA_REQUEST)) {
231 return &request->coa->proxy->vps;
232 }
233 break;
234
235 case PAIR_LIST_COA_REPLY:
236 if (request->coa && /* match reply with request */
237 (request->coa->proxy->code == PW_CODE_COA_REQUEST) &&
238 request->coa->proxy_reply) {
239 return &request->coa->proxy_reply->vps;
240 }
241 break;
242
243 case PAIR_LIST_DM:
244 if (request->coa &&
245 (request->coa->proxy->code == PW_CODE_DISCONNECT_REQUEST)) {
246 return &request->coa->proxy->vps;
247 }
248 break;
249
250 case PAIR_LIST_DM_REPLY:
251 if (request->coa && /* match reply with request */
252 (request->coa->proxy->code == PW_CODE_DISCONNECT_REQUEST) &&
253 request->coa->proxy_reply) {
254 return &request->coa->proxy_reply->vps;
255 }
256 break;
257 #endif
258 }
259
260 RWDEBUG2("List \"%s\" is not available",
261 fr_int2str(pair_lists, list, "<INVALID>"));
262
263 return NULL;
264 }
265
266 /** Resolve a list to the #RADIUS_PACKET holding the HEAD pointer for a #VALUE_PAIR list
267 *
268 * Returns a pointer to the #RADIUS_PACKET that holds the HEAD pointer of a given list,
269 * for the current #REQUEST.
270 *
271 * @param[in] request To resolve list in.
272 * @param[in] list #pair_lists_t value to resolve to #RADIUS_PACKET.
273 * @return a #RADIUS_PACKET on success, else NULL.
274 *
275 * @see radius_list
276 */
radius_packet(REQUEST * request,pair_lists_t list)277 RADIUS_PACKET *radius_packet(REQUEST *request, pair_lists_t list)
278 {
279 switch (list) {
280 /* Don't add default */
281 case PAIR_LIST_STATE:
282 case PAIR_LIST_CONTROL:
283 case PAIR_LIST_UNKNOWN:
284 return NULL;
285
286 case PAIR_LIST_REQUEST:
287 return request->packet;
288
289 case PAIR_LIST_REPLY:
290 return request->reply;
291
292 #ifdef WITH_PROXY
293 case PAIR_LIST_PROXY_REQUEST:
294 return request->proxy;
295
296 case PAIR_LIST_PROXY_REPLY:
297 return request->proxy_reply;
298 #endif
299
300 #ifdef WITH_COA
301 case PAIR_LIST_COA:
302 case PAIR_LIST_DM:
303 return request->coa->proxy;
304
305 case PAIR_LIST_COA_REPLY:
306 case PAIR_LIST_DM_REPLY:
307 return request->coa->proxy_reply;
308 #endif
309 }
310
311 return NULL;
312 }
313
314 /** Return the correct TALLOC_CTX to alloc #VALUE_PAIR in, for a list
315 *
316 * Allocating new #VALUE_PAIR in the context of a #REQUEST is usually wrong.
317 * #VALUE_PAIR should be allocated in the context of a #RADIUS_PACKET, so that if the
318 * #RADIUS_PACKET is freed before the #REQUEST, the associated #VALUE_PAIR lists are
319 * freed too.
320 *
321 * @param[in] request containing the target lists.
322 * @param[in] list #pair_lists_t value to resolve to TALLOC_CTX.
323 * @return a TALLOC_CTX on success, else NULL.
324 *
325 * @see radius_list
326 */
radius_list_ctx(REQUEST * request,pair_lists_t list)327 TALLOC_CTX *radius_list_ctx(REQUEST *request, pair_lists_t list)
328 {
329 if (!request) return NULL;
330
331 switch (list) {
332 case PAIR_LIST_REQUEST:
333 return request->packet;
334
335 case PAIR_LIST_REPLY:
336 return request->reply;
337
338 case PAIR_LIST_CONTROL:
339 return request;
340
341 case PAIR_LIST_STATE:
342 return request->state_ctx;
343
344 #ifdef WITH_PROXY
345 case PAIR_LIST_PROXY_REQUEST:
346 return request->proxy;
347
348 case PAIR_LIST_PROXY_REPLY:
349 return request->proxy_reply;
350 #endif
351
352 #ifdef WITH_COA
353 case PAIR_LIST_COA:
354 if (!request->coa) return NULL;
355 rad_assert(request->coa->proxy != NULL);
356 if (request->coa->proxy->code != PW_CODE_COA_REQUEST) return NULL;
357 return request->coa->proxy;
358
359 case PAIR_LIST_COA_REPLY:
360 if (!request->coa) return NULL;
361 rad_assert(request->coa->proxy != NULL);
362 if (request->coa->proxy->code != PW_CODE_COA_REQUEST) return NULL;
363 return request->coa->proxy_reply;
364
365 case PAIR_LIST_DM:
366 if (!request->coa) return NULL;
367 rad_assert(request->coa->proxy != NULL);
368 if (request->coa->proxy->code != PW_CODE_DISCONNECT_REQUEST) return NULL;
369 return request->coa->proxy;
370
371 case PAIR_LIST_DM_REPLY:
372 if (!request->coa) return NULL;
373 rad_assert(request->coa->proxy != NULL);
374 if (request->coa->proxy->code != PW_CODE_DISCONNECT_REQUEST) return NULL;
375 return request->coa->proxy_reply;
376 #endif
377 /* Don't add default */
378 case PAIR_LIST_UNKNOWN:
379 break;
380 }
381
382 return NULL;
383 }
384
385 /** Resolve attribute name to a #request_refs_t value.
386 *
387 * Check the name string for qualifiers that reference a parent #REQUEST.
388 *
389 * If we find a string that matches a #request_refs qualifier, return the number of chars
390 * we consumed.
391 *
392 * If we're sure we've definitely found a list qualifier token delimiter (``*``) but the
393 * qualifier doesn't match one of the #request_refs qualifiers, return 0 and set out to
394 * #REQUEST_UNKNOWN.
395 *
396 * If we can't find a string that looks like a request qualifier, set out to def, and
397 * return 0.
398 *
399 * @param[out] out The #request_refs_t value the name resolved to (or #REQUEST_UNKNOWN).
400 * @param[in] name of attribute.
401 * @param[in] def default request ref to return if no request qualifier is present.
402 * @return 0 if no valid request qualifier could be found, else the number of bytes consumed.
403 * The caller may then advanced the name pointer by the value returned, to get the
404 * start of the attribute list or attribute name(if any).
405 *
406 * @see radius_list_name
407 * @see request_refs
408 */
radius_request_name(request_refs_t * out,char const * name,request_refs_t def)409 size_t radius_request_name(request_refs_t *out, char const *name, request_refs_t def)
410 {
411 char const *p, *q;
412
413 p = name;
414 /*
415 * Try and determine the end of the token
416 */
417 for (q = p; dict_attr_allowed_chars[(uint8_t) *q] && (*q != '.') && (*q != '-'); q++);
418
419 /*
420 * First token delimiter wasn't a '.'
421 */
422 if (*q != '.') {
423 *out = def;
424 return 0;
425 }
426
427 *out = fr_substr2int(request_refs, name, REQUEST_UNKNOWN, q - p);
428 if (*out == REQUEST_UNKNOWN) return 0;
429
430 return (q + 1) - p;
431 }
432
433 /** Resolve a #request_refs_t to a #REQUEST.
434 *
435 * Sometimes #REQUEST structs may be chained to each other, as is the case
436 * when internally proxying EAP. This function resolves a #request_refs_t
437 * to a #REQUEST higher in the chain than the current #REQUEST.
438 *
439 * @see radius_list
440 * @param[in,out] context #REQUEST to start resolving from, and where to write
441 * a pointer to the resolved #REQUEST back to.
442 * @param[in] name (request) to resolve.
443 * @return 0 if request is valid in this context, else -1.
444 */
radius_request(REQUEST ** context,request_refs_t name)445 int radius_request(REQUEST **context, request_refs_t name)
446 {
447 REQUEST *request = *context;
448
449 switch (name) {
450 case REQUEST_CURRENT:
451 return 0;
452
453 case REQUEST_PARENT: /* for future use in request chaining */
454 case REQUEST_OUTER:
455 if (!request->parent) {
456 return -1;
457 }
458 *context = request->parent;
459 break;
460
461 case REQUEST_UNKNOWN:
462 default:
463 rad_assert(0);
464 return -1;
465 }
466
467 return 0;
468 }
469 /** @} */
470
471 /** @name Alloc or initialise #vp_tmpl_t
472 *
473 * @note Should not usually be called outside of tmpl_* functions, use one of
474 * the tmpl_*from_* functions instead.
475 * @{
476 */
477
478 /** Initialise stack allocated #vp_tmpl_t
479 *
480 * @note Name is not strdupe'd or memcpy'd so must be available, and must not change
481 * for the lifetime of the #vp_tmpl_t.
482 *
483 * @param[out] vpt to initialise.
484 * @param[in] type to set in the #vp_tmpl_t.
485 * @param[in] name of the #vp_tmpl_t.
486 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
487 * If < 0 strlen will be used to determine the length.
488 * @return a pointer to the initialised #vp_tmpl_t. The same value as
489 * vpt.
490 */
tmpl_init(vp_tmpl_t * vpt,tmpl_type_t type,char const * name,ssize_t len)491 vp_tmpl_t *tmpl_init(vp_tmpl_t *vpt, tmpl_type_t type, char const *name, ssize_t len)
492 {
493 rad_assert(vpt);
494 rad_assert(type != TMPL_TYPE_UNKNOWN);
495 rad_assert(type <= TMPL_TYPE_NULL);
496
497 memset(vpt, 0, sizeof(vp_tmpl_t));
498 vpt->type = type;
499
500 if (name) {
501 vpt->name = name;
502 vpt->len = len < 0 ? strlen(name) :
503 (size_t) len;
504 }
505 return vpt;
506 }
507
508 /** Create a new heap allocated #vp_tmpl_t
509 *
510 * @param[in,out] ctx to allocate in.
511 * @param[in] type to set in the #vp_tmpl_t.
512 * @param[in] name of the #vp_tmpl_t (will be copied to a new talloc buffer parented
513 * by the #vp_tmpl_t).
514 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
515 * If < 0 strlen will be used to determine the length.
516 * @return the newly allocated #vp_tmpl_t.
517 */
tmpl_alloc(TALLOC_CTX * ctx,tmpl_type_t type,char const * name,ssize_t len)518 vp_tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, char const *name, ssize_t len)
519 {
520 vp_tmpl_t *vpt;
521
522 rad_assert(type != TMPL_TYPE_UNKNOWN);
523 rad_assert(type <= TMPL_TYPE_NULL);
524
525 vpt = talloc_zero(ctx, vp_tmpl_t);
526 if (!vpt) return NULL;
527 vpt->type = type;
528 if (name) {
529 vpt->name = talloc_bstrndup(vpt, name, len < 0 ? strlen(name) : (size_t)len);
530 vpt->len = talloc_array_length(vpt->name) - 1;
531 }
532
533 return vpt;
534 }
535 /* @} **/
536
537 /** @name Create new #vp_tmpl_t from a string
538 *
539 * @{
540 */
541 /** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #vp_tmpl_t
542 *
543 * @note The name field is just a copy of the input pointer, if you know that string might be
544 * freed before you're done with the #vp_tmpl_t use #tmpl_afrom_attr_str
545 * instead.
546 *
547 * @param[out] vpt to modify.
548 * @param[in] name of attribute including #request_refs and #pair_lists qualifiers.
549 * If only #request_refs and #pair_lists qualifiers are found, a #TMPL_TYPE_LIST
550 * #vp_tmpl_t will be produced.
551 * @param[in] request_def The default #REQUEST to set if no #request_refs qualifiers are
552 * found in name.
553 * @param[in] list_def The default list to set if no #pair_lists qualifiers are found in
554 * name.
555 * @param[in] allow_unknown If true attributes in the format accepted by
556 * #dict_unknown_from_substr will be allowed, even if they're not in the main
557 * dictionaries.
558 * If an unknown attribute is found a #TMPL_TYPE_ATTR #vp_tmpl_t will be
559 * produced with the unknown #DICT_ATTR stored in the ``unknown.da`` buffer.
560 * This #DICT_ATTR will have its ``flags.is_unknown`` field set to true.
561 * If #tmpl_from_attr_substr is being called on startup, the #vp_tmpl_t may be
562 * passed to #tmpl_define_unknown_attr to add the unknown attribute to the main
563 * dictionary.
564 * If the unknown attribute is not added to the main dictionary the #vp_tmpl_t
565 * cannot be used to search for a #VALUE_PAIR in a #REQUEST.
566 * @param[in] allow_undefined If true, we don't generate a parse error on unknown attributes.
567 * If an unknown attribute is found a #TMPL_TYPE_ATTR_UNDEFINED #vp_tmpl_t
568 * will be produced.
569 * @return <= 0 on error (offset as negative integer), > 0 on success
570 * (number of bytes parsed).
571 *
572 * @see REMARKER to produce pretty error markers from the return value.
573 */
tmpl_from_attr_substr(vp_tmpl_t * vpt,char const * name,request_refs_t request_def,pair_lists_t list_def,bool allow_unknown,bool allow_undefined)574 ssize_t tmpl_from_attr_substr(vp_tmpl_t *vpt, char const *name,
575 request_refs_t request_def, pair_lists_t list_def,
576 bool allow_unknown, bool allow_undefined)
577 {
578 char const *p;
579 long num;
580 char *q;
581 tmpl_type_t type = TMPL_TYPE_ATTR;
582
583 value_pair_tmpl_attr_t attr; /* So we don't fill the tmpl with junk and then error out */
584
585 memset(vpt, 0, sizeof(*vpt));
586 memset(&attr, 0, sizeof(attr));
587
588 p = name;
589
590 if (*p == '&') p++;
591
592 p += radius_request_name(&attr.request, p, request_def);
593 if (attr.request == REQUEST_UNKNOWN) {
594 fr_strerror_printf("Invalid request qualifier");
595 return -(p - name);
596 }
597
598 /*
599 * Finding a list qualifier is optional
600 */
601 p += radius_list_name(&attr.list, p, list_def);
602 if (attr.list == PAIR_LIST_UNKNOWN) {
603 fr_strerror_printf("Invalid list qualifier");
604 return -(p - name);
605 }
606
607 attr.tag = TAG_ANY;
608 attr.num = NUM_ANY;
609
610 /*
611 * This may be just a bare list, but it can still
612 * have instance selectors and tag selectors.
613 */
614 switch (*p) {
615 case '\0':
616 type = TMPL_TYPE_LIST;
617 attr.num = NUM_ALL; /* Hack - Should be removed once tests are updated */
618 goto finish;
619
620 case '[':
621 type = TMPL_TYPE_LIST;
622 attr.num = NUM_ALL; /* Hack - Should be removed once tests are updated */
623 goto do_num;
624
625 default:
626 break;
627 }
628
629 attr.da = dict_attrbyname_substr(&p);
630 if (!attr.da) {
631 char const *a;
632
633 /*
634 * Record start of attribute in case we need to error out.
635 */
636 a = p;
637
638 fr_strerror(); /* Clear out any existing errors */
639
640 /*
641 * Attr-1.2.3.4 is OK.
642 */
643 if (dict_unknown_from_substr((DICT_ATTR *)&attr.unknown.da, &p) == 0) {
644 /*
645 * Check what we just parsed really hasn't been defined
646 * in the main dictionaries.
647 *
648 * If it has, parsing is the same as if the attribute
649 * name had been used instead of its OID.
650 */
651 attr.da = dict_attrbyvalue(((DICT_ATTR *)&attr.unknown.da)->attr,
652 ((DICT_ATTR *)&attr.unknown.da)->vendor);
653 if (attr.da) {
654 vpt->auto_converted = true;
655 goto do_num;
656 }
657
658 if (!allow_unknown) {
659 fr_strerror_printf("Unknown attribute");
660 return -(a - name);
661 }
662
663 /*
664 * Unknown attributes can't be encoded, as we don't
665 * know how to encode them!
666 */
667 attr.da = (DICT_ATTR *)&attr.unknown.da;
668
669 goto do_num; /* unknown attributes can't have tags */
670 }
671
672 /*
673 * Can't parse it as an attribute, might be a literal string
674 * let the caller decide.
675 *
676 * Don't alter the fr_strerror buffer, should contain the parse
677 * error from dict_unknown_from_substr.
678 */
679 if (!allow_undefined) return -(a - name);
680
681 /*
682 * Copy the name to a field for later resolution
683 */
684 type = TMPL_TYPE_ATTR_UNDEFINED;
685 for (q = attr.unknown.name; dict_attr_allowed_chars[(int) *p]; *q++ = *p++) {
686 if (q >= (attr.unknown.name + sizeof(attr.unknown.name) - 1)) {
687 fr_strerror_printf("Attribute name is too long");
688 return -(p - name);
689 }
690 }
691 *q = '\0';
692
693 goto do_num;
694 }
695
696 /*
697 * The string MIGHT have a tag.
698 */
699 if (*p == ':') {
700 if (attr.da && !attr.da->flags.has_tag) { /* Lists don't have a da */
701 fr_strerror_printf("Attribute '%s' cannot have a tag", attr.da->name);
702 return -(p - name);
703 }
704
705 num = strtol(p + 1, &q, 10);
706 if ((num > 0x1f) || (num < 0)) {
707 fr_strerror_printf("Invalid tag value '%li' (should be between 0-31)", num);
708 return -((p + 1)- name);
709 }
710
711 attr.tag = num;
712 p = q;
713 }
714
715 do_num:
716 if (*p == '\0') goto finish;
717
718 if (*p == '[') {
719 p++;
720
721 switch (*p) {
722 case '#':
723 attr.num = NUM_COUNT;
724 p++;
725 break;
726
727 case '*':
728 attr.num = NUM_ALL;
729 p++;
730 break;
731
732 case 'n':
733 attr.num = NUM_LAST;
734 p++;
735 break;
736
737 default:
738 num = strtol(p, &q, 10);
739 if (p == q) {
740 fr_strerror_printf("Array index is not an integer");
741 return -(p - name);
742 }
743
744 if ((num > 1000) || (num < 0)) {
745 fr_strerror_printf("Invalid array reference '%li' (should be between 0-1000)", num);
746 return -(p - name);
747 }
748 attr.num = num;
749 p = q;
750 break;
751 }
752
753 if (*p != ']') {
754 fr_strerror_printf("No closing ']' for array index");
755 return -(p - name);
756 }
757 p++;
758 }
759
760 finish:
761 vpt->type = type;
762 vpt->name = name;
763 vpt->len = p - name;
764
765 /*
766 * Copy over the attribute definition, now we're
767 * sure what we were passed is valid.
768 */
769 memcpy(&vpt->data.attribute, &attr, sizeof(vpt->data.attribute));
770 if ((vpt->type == TMPL_TYPE_ATTR) && attr.da->flags.is_unknown) {
771 vpt->tmpl_da = (DICT_ATTR *)&vpt->data.attribute.unknown.da;
772 }
773
774 VERIFY_TMPL(vpt);
775
776 return vpt->len;
777 }
778
779 /** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #vp_tmpl_t
780 *
781 * @note Unlike #tmpl_from_attr_substr this function will error out if the entire
782 * name string isn't parsed.
783 *
784 * @copydetails tmpl_from_attr_substr
785 */
tmpl_from_attr_str(vp_tmpl_t * vpt,char const * name,request_refs_t request_def,pair_lists_t list_def,bool allow_unknown,bool allow_undefined)786 ssize_t tmpl_from_attr_str(vp_tmpl_t *vpt, char const *name,
787 request_refs_t request_def, pair_lists_t list_def,
788 bool allow_unknown, bool allow_undefined)
789 {
790 ssize_t slen;
791
792 slen = tmpl_from_attr_substr(vpt, name, request_def, list_def, allow_unknown, allow_undefined);
793 if (slen <= 0) return slen;
794 if (name[slen] != '\0') {
795 /* This looks wrong, but it produces meaningful errors for unknown attrs with tags */
796 fr_strerror_printf("Unexpected text after %s", fr_int2str(tmpl_names, vpt->type, "<INVALID>"));
797 return -slen;
798 }
799
800 VERIFY_TMPL(vpt);
801
802 return slen;
803 }
804
805 /** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #vp_tmpl_t
806 *
807 * @param[in,out] ctx to allocate #vp_tmpl_t in.
808 * @param[out] out Where to write pointer to new #vp_tmpl_t.
809 * @param[in] name of attribute including #request_refs and #pair_lists qualifiers.
810 * If only #request_refs #pair_lists qualifiers are found, a #TMPL_TYPE_LIST
811 * #vp_tmpl_t will be produced.
812 * @param[in] request_def The default #REQUEST to set if no #request_refs qualifiers are
813 * found in name.
814 * @param[in] list_def The default list to set if no #pair_lists qualifiers are found in
815 * name.
816 * @param[in] allow_unknown If true attributes in the format accepted by
817 * #dict_unknown_from_substr will be allowed, even if they're not in the main
818 * dictionaries.
819 * If an unknown attribute is found a #TMPL_TYPE_ATTR #vp_tmpl_t will be
820 * produced with the unknown #DICT_ATTR stored in the ``unknown.da`` buffer.
821 * This #DICT_ATTR will have its ``flags.is_unknown`` field set to true.
822 * If #tmpl_from_attr_substr is being called on startup, the #vp_tmpl_t may be
823 * passed to #tmpl_define_unknown_attr to add the unknown attribute to the main
824 * dictionary.
825 * If the unknown attribute is not added to the main dictionary the #vp_tmpl_t
826 * cannot be used to search for a #VALUE_PAIR in a #REQUEST.
827 * @param[in] allow_undefined If true, we don't generate a parse error on unknown attributes.
828 * If an unknown attribute is found a #TMPL_TYPE_ATTR_UNDEFINED #vp_tmpl_t
829 * will be produced.
830 * @return <= 0 on error (offset as negative integer), > 0 on success
831 * (number of bytes parsed).
832 *
833 * @see REMARKER to produce pretty error markers from the return value.
834 */
tmpl_afrom_attr_substr(TALLOC_CTX * ctx,vp_tmpl_t ** out,char const * name,request_refs_t request_def,pair_lists_t list_def,bool allow_unknown,bool allow_undefined)835 ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name,
836 request_refs_t request_def, pair_lists_t list_def,
837 bool allow_unknown, bool allow_undefined)
838 {
839 ssize_t slen;
840 vp_tmpl_t *vpt;
841
842 MEM(vpt = talloc(ctx, vp_tmpl_t)); /* tmpl_from_attr_substr zeros it */
843
844 slen = tmpl_from_attr_substr(vpt, name, request_def, list_def, allow_unknown, allow_undefined);
845 if (slen <= 0) {
846 TALLOC_FREE(vpt);
847 return slen;
848 }
849 vpt->name = talloc_strndup(vpt, vpt->name, slen);
850
851 VERIFY_TMPL(vpt);
852
853 *out = vpt;
854
855 return slen;
856 }
857
858 /** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #vp_tmpl_t
859 *
860 * @note Unlike #tmpl_afrom_attr_substr this function will error out if the entire
861 * name string isn't parsed.
862 *
863 * @copydetails tmpl_afrom_attr_substr
864 */
tmpl_afrom_attr_str(TALLOC_CTX * ctx,vp_tmpl_t ** out,char const * name,request_refs_t request_def,pair_lists_t list_def,bool allow_unknown,bool allow_undefined)865 ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name,
866 request_refs_t request_def, pair_lists_t list_def,
867 bool allow_unknown, bool allow_undefined)
868 {
869 ssize_t slen;
870 vp_tmpl_t *vpt;
871
872 MEM(vpt = talloc(ctx, vp_tmpl_t)); /* tmpl_from_attr_substr zeros it */
873
874 slen = tmpl_from_attr_substr(vpt, name, request_def, list_def, allow_unknown, allow_undefined);
875 if (slen <= 0) {
876 TALLOC_FREE(vpt);
877 return slen;
878 }
879 if (name[slen] != '\0') {
880 /* This looks wrong, but it produces meaningful errors for unknown attrs with tags */
881 fr_strerror_printf("Unexpected text after %s", fr_int2str(tmpl_names, vpt->type, "<INVALID>"));
882 TALLOC_FREE(vpt);
883 return -slen;
884 }
885 vpt->name = talloc_strndup(vpt, vpt->name, vpt->len);
886
887 VERIFY_TMPL(vpt);
888
889 *out = vpt;
890
891 return slen;
892 }
893
894 /** Convert an arbitrary string into a #vp_tmpl_t
895 *
896 * @note Unlike #tmpl_afrom_attr_str return code 0 doesn't necessarily indicate failure,
897 * may just mean a 0 length string was parsed.
898 *
899 * @note xlats and regexes are left uncompiled. This is to support the two pass parsing
900 * done by the modcall code. Compilation on pass1 of that code could fail, as
901 * attributes or xlat functions registered by modules may not be available (yet).
902 *
903 * @note For details of attribute parsing see #tmpl_from_attr_substr.
904 *
905 * @param[in,out] ctx To allocate #vp_tmpl_t in.
906 * @param[out] out Where to write the pointer to the new #vp_tmpl_t.
907 * @param[in] in String to convert to a #vp_tmpl_t.
908 * @param[in] inlen length of string to convert.
909 * @param[in] type of quoting around value. May be one of:
910 * - #T_BARE_WORD - If string begins with ``&`` produces #TMPL_TYPE_ATTR,
911 * #TMPL_TYPE_ATTR_UNDEFINED, #TMPL_TYPE_LIST or error.
912 * If string does not begin with ``&`` produces #TMPL_TYPE_LITERAL,
913 * #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
914 * - #T_SINGLE_QUOTED_STRING - Produces #TMPL_TYPE_LITERAL
915 * - #T_DOUBLE_QUOTED_STRING - Produces #TMPL_TYPE_XLAT or #TMPL_TYPE_LITERAL (if
916 * string doesn't contain ``%``).
917 * - #T_BACK_QUOTED_STRING - Produces #TMPL_TYPE_EXEC
918 * - #T_OP_REG_EQ - Produces #TMPL_TYPE_REGEX
919 * @param[in] request_def The default #REQUEST to set if no #request_refs qualifiers are
920 * found in name.
921 * @param[in] list_def The default list to set if no #pair_lists qualifiers are found in
922 * name.
923 * @param[in] do_unescape whether or not we should do unescaping. Should be false if the
924 * caller already did it.
925 * @return <= 0 on error (offset as negative integer), > 0 on success
926 * (number of bytes parsed).
927 * @see REMARKER to produce pretty error markers from the return value.
928 *
929 * @see tmpl_from_attr_substr
930 */
tmpl_afrom_str(TALLOC_CTX * ctx,vp_tmpl_t ** out,char const * in,size_t inlen,FR_TOKEN type,request_refs_t request_def,pair_lists_t list_def,bool do_unescape)931 ssize_t tmpl_afrom_str(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *in, size_t inlen, FR_TOKEN type,
932 request_refs_t request_def, pair_lists_t list_def, bool do_unescape)
933 {
934 bool do_xlat;
935 char quote;
936 char const *p;
937 ssize_t slen;
938 PW_TYPE data_type = PW_TYPE_STRING;
939 vp_tmpl_t *vpt = NULL;
940 value_data_t data;
941
942 switch (type) {
943 case T_BARE_WORD:
944 /*
945 * If we can parse it as an attribute, it's an attribute.
946 * Otherwise, treat it as a literal.
947 */
948 quote = '\0';
949
950 slen = tmpl_afrom_attr_str(ctx, &vpt, in, request_def, list_def, true, (in[0] == '&'));
951 if ((in[0] == '&') && (slen <= 0)) return slen;
952 if (slen > 0) break;
953 goto parse;
954
955 case T_SINGLE_QUOTED_STRING:
956 quote = '\'';
957
958 parse:
959 if (cf_new_escape && do_unescape) {
960 slen = value_data_from_str(ctx, &data, &data_type, NULL, in, inlen, quote);
961 if (slen < 0) return 0;
962
963 vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, data.strvalue, talloc_array_length(data.strvalue) - 1);
964 talloc_free(data.ptr);
965 } else {
966 vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, in, inlen);
967 }
968 vpt->quote = quote;
969 slen = vpt->len;
970 break;
971
972 case T_DOUBLE_QUOTED_STRING:
973 do_xlat = false;
974
975 p = in;
976 while (*p) {
977 if (do_unescape) { /* otherwise \ is just another character */
978 if (*p == '\\') {
979 if (!p[1]) break;
980 p += 2;
981 continue;
982 }
983 }
984
985 if (*p == '%') {
986 do_xlat = true;
987 break;
988 }
989
990 p++;
991 }
992
993 /*
994 * If the double quoted string needs to be
995 * expanded at run time, make it an xlat
996 * expansion. Otherwise, convert it to be a
997 * literal.
998 */
999 if (cf_new_escape && do_unescape) {
1000 slen = value_data_from_str(ctx, &data, &data_type, NULL, in, inlen, '"');
1001 if (slen < 0) return slen;
1002
1003 if (do_xlat) {
1004 vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, data.strvalue,
1005 talloc_array_length(data.strvalue) - 1);
1006 } else {
1007 vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, data.strvalue,
1008 talloc_array_length(data.strvalue) - 1);
1009 vpt->quote = '"';
1010 }
1011 talloc_free(data.ptr);
1012 } else {
1013 if (do_xlat) {
1014 vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, in, inlen);
1015 } else {
1016 vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, in, inlen);
1017 vpt->quote = '"';
1018 }
1019 }
1020 slen = vpt->len;
1021 break;
1022
1023 case T_BACK_QUOTED_STRING:
1024 if (cf_new_escape && do_unescape) {
1025 slen = value_data_from_str(ctx, &data, &data_type, NULL, in, inlen, '`');
1026 if (slen < 0) return slen;
1027
1028 vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, data.strvalue, talloc_array_length(data.strvalue) - 1);
1029 talloc_free(data.ptr);
1030 } else {
1031 vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, in, inlen);
1032 }
1033 slen = vpt->len;
1034 break;
1035
1036 case T_OP_REG_EQ: /* hack */
1037 vpt = tmpl_alloc(ctx, TMPL_TYPE_REGEX, in, inlen);
1038 slen = vpt->len;
1039 break;
1040
1041 default:
1042 rad_assert(0);
1043 return 0; /* 0 is an error here too */
1044 }
1045
1046 rad_assert((slen >= 0) && (vpt != NULL));
1047
1048 VERIFY_TMPL(vpt);
1049
1050 *out = vpt;
1051
1052 return slen;
1053 }
1054 /* @} **/
1055
1056 /** @name Cast or convert #vp_tmpl_t
1057 *
1058 * #tmpl_cast_in_place can be used to convert #TMPL_TYPE_LITERAL to a #TMPL_TYPE_DATA of a
1059 * specified #PW_TYPE.
1060 *
1061 * #tmpl_cast_in_place_str does the same as #tmpl_cast_in_place, but will always convert to
1062 * #PW_TYPE #PW_TYPE_STRING.
1063 *
1064 * #tmpl_cast_to_vp does the same as #tmpl_cast_in_place, but outputs a #VALUE_PAIR.
1065 *
1066 * #tmpl_define_unknown_attr converts a #TMPL_TYPE_ATTR with an unknown #DICT_ATTR to a
1067 * #TMPL_TYPE_ATTR with a known #DICT_ATTR, by adding the unknown #DICT_ATTR to the main
1068 * dictionary, and updating the ``tmpl_da`` pointer.
1069 * @{
1070 */
1071
1072 /** Convert #vp_tmpl_t of type #TMPL_TYPE_LITERAL or #TMPL_TYPE_DATA to #TMPL_TYPE_DATA of type specified
1073 *
1074 * @note Conversion is done in place.
1075 * @note Irrespective of whether the #vp_tmpl_t was #TMPL_TYPE_LITERAL or #TMPL_TYPE_DATA,
1076 * on successful cast it will be #TMPL_TYPE_DATA.
1077 *
1078 * @param[in,out] vpt The template to modify. Must be of type #TMPL_TYPE_LITERAL
1079 * or #TMPL_TYPE_DATA.
1080 * @param[in] type to cast to.
1081 * @param[in] enumv Enumerated dictionary values associated with a #DICT_ATTR.
1082 * @return 0 on success, -1 on failure.
1083 */
tmpl_cast_in_place(vp_tmpl_t * vpt,PW_TYPE type,DICT_ATTR const * enumv)1084 int tmpl_cast_in_place(vp_tmpl_t *vpt, PW_TYPE type, DICT_ATTR const *enumv)
1085 {
1086 ssize_t ret;
1087
1088 VERIFY_TMPL(vpt);
1089
1090 rad_assert(vpt != NULL);
1091 rad_assert((vpt->type == TMPL_TYPE_LITERAL) || (vpt->type == TMPL_TYPE_DATA));
1092
1093 switch (vpt->type) {
1094 case TMPL_TYPE_LITERAL:
1095 /*
1096 * Why do we pass a pointer to the tmpl type? Goddamn WiMAX.
1097 */
1098 ret = value_data_from_str(vpt, &vpt->tmpl_data_value, &type,
1099 enumv, vpt->name, vpt->len, '\0');
1100 if (ret < 0) {
1101 VERIFY_TMPL(vpt);
1102 return -1;
1103 }
1104
1105 vpt->tmpl_data_type = type;
1106 vpt->type = TMPL_TYPE_DATA;
1107 vpt->tmpl_data_length = (size_t) ret;
1108 break;
1109
1110 case TMPL_TYPE_DATA:
1111 {
1112 value_data_t new;
1113
1114 if (type == vpt->tmpl_data_type) return 0; /* noop */
1115
1116 ret = value_data_cast(vpt, &new, type, enumv, vpt->tmpl_data_type,
1117 NULL, &vpt->tmpl_data_value, vpt->tmpl_data_length);
1118 if (ret < 0) return -1;
1119
1120 /*
1121 * Free old value buffers
1122 */
1123 switch (vpt->tmpl_data_type) {
1124 case PW_TYPE_STRING:
1125 case PW_TYPE_OCTETS:
1126 talloc_free(vpt->tmpl_data_value.ptr);
1127 break;
1128
1129 default:
1130 break;
1131 }
1132
1133 memcpy(&vpt->tmpl_data_value, &new, sizeof(vpt->tmpl_data_value));
1134 vpt->tmpl_data_type = type;
1135 vpt->tmpl_data_length = (size_t) ret;
1136 }
1137 break;
1138
1139 default:
1140 rad_assert(0);
1141 }
1142
1143 VERIFY_TMPL(vpt);
1144
1145 return 0;
1146 }
1147
1148 /** Convert #vp_tmpl_t of type #TMPL_TYPE_LITERAL to #TMPL_TYPE_DATA of type #PW_TYPE_STRING
1149 *
1150 * @note Conversion is done in place.
1151 *
1152 * @param[in,out] vpt The template to modify. Must be of type #TMPL_TYPE_LITERAL.
1153 */
tmpl_cast_in_place_str(vp_tmpl_t * vpt)1154 void tmpl_cast_in_place_str(vp_tmpl_t *vpt)
1155 {
1156 rad_assert(vpt != NULL);
1157 rad_assert(vpt->type == TMPL_TYPE_LITERAL);
1158
1159 vpt->tmpl_data.vp_strvalue = talloc_typed_strdup(vpt, vpt->name);
1160 rad_assert(vpt->tmpl_data.vp_strvalue != NULL);
1161
1162 vpt->type = TMPL_TYPE_DATA;
1163 vpt->tmpl_data_type = PW_TYPE_STRING;
1164 vpt->tmpl_data_length = talloc_array_length(vpt->tmpl_data.vp_strvalue) - 1;
1165 }
1166
1167 /** Expand a #vp_tmpl_t to a string, parse it as an attribute of type cast, create a #VALUE_PAIR from the result
1168 *
1169 * @note Like #tmpl_expand, but produces a #VALUE_PAIR.
1170 *
1171 * @param out Where to write pointer to the new #VALUE_PAIR.
1172 * @param request The current #REQUEST.
1173 * @param vpt to cast. Must be one of the following types:
1174 * - #TMPL_TYPE_LITERAL
1175 * - #TMPL_TYPE_EXEC
1176 * - #TMPL_TYPE_XLAT
1177 * - #TMPL_TYPE_XLAT_STRUCT
1178 * - #TMPL_TYPE_ATTR
1179 * - #TMPL_TYPE_DATA
1180 * @param cast type of #VALUE_PAIR to create.
1181 * @return 0 on success, -1 on failure.
1182 */
tmpl_cast_to_vp(VALUE_PAIR ** out,REQUEST * request,vp_tmpl_t const * vpt,DICT_ATTR const * cast)1183 int tmpl_cast_to_vp(VALUE_PAIR **out, REQUEST *request,
1184 vp_tmpl_t const *vpt, DICT_ATTR const *cast)
1185 {
1186 int rcode;
1187 VALUE_PAIR *vp;
1188 value_data_t data;
1189 char *p;
1190
1191 VERIFY_TMPL(vpt);
1192
1193 *out = NULL;
1194
1195 vp = fr_pair_afrom_da(request, cast);
1196 if (!vp) return -1;
1197
1198 if (vpt->type == TMPL_TYPE_DATA) {
1199 VERIFY_VP(vp);
1200 rad_assert(vp->da->type == vpt->tmpl_data_type);
1201
1202 value_data_copy(vp, &vp->data, vpt->tmpl_data_type, &vpt->tmpl_data_value, vpt->tmpl_data_length);
1203 *out = vp;
1204 return 0;
1205 }
1206
1207 rcode = tmpl_aexpand(vp, &p, request, vpt, NULL, NULL);
1208 if (rcode < 0) {
1209 fr_pair_list_free(&vp);
1210 return rcode;
1211 }
1212 data.strvalue = p;
1213
1214 /*
1215 * New escapes: strings are in binary form.
1216 */
1217 if (cf_new_escape && (vp->da->type == PW_TYPE_STRING)) {
1218 vp->data.ptr = talloc_steal(vp, data.ptr);
1219 vp->vp_length = rcode;
1220
1221 } else if (fr_pair_value_from_str(vp, data.strvalue, rcode) < 0) {
1222 talloc_free(data.ptr);
1223 fr_pair_list_free(&vp);
1224 return -1;
1225 }
1226
1227 /*
1228 * Copy over any additional fields needed...
1229 */
1230 if ((vpt->type == TMPL_TYPE_ATTR) && vp->da->flags.has_tag) {
1231 vp->tag = vpt->tmpl_tag;
1232 }
1233
1234 *out = vp;
1235 return 0;
1236 }
1237
1238 /** Add an unknown #DICT_ATTR specified by a #vp_tmpl_t to the main dictionary
1239 *
1240 * @param vpt to add. ``tmpl_da`` pointer will be updated to point to the
1241 * #DICT_ATTR inserted into the dictionary.
1242 * @return 0 on success, -1 on failure.
1243 */
tmpl_define_unknown_attr(vp_tmpl_t * vpt)1244 int tmpl_define_unknown_attr(vp_tmpl_t *vpt)
1245 {
1246 DICT_ATTR const *da;
1247
1248 if (!vpt) return -1;
1249
1250 VERIFY_TMPL(vpt);
1251
1252 if (vpt->type != TMPL_TYPE_ATTR) return 0;
1253
1254 if (!vpt->tmpl_da->flags.is_unknown) return 0;
1255
1256 da = dict_unknown_add(vpt->tmpl_da);
1257 if (!da) return -1;
1258 vpt->tmpl_da = da;
1259 return 0;
1260 }
1261 /* @} **/
1262
1263 /** @name Resolve a #vp_tmpl_t outputting the result in various formats
1264 *
1265 * @{
1266 */
1267
1268 /** Expand a #vp_tmpl_t to a string writing the result to a buffer
1269 *
1270 * The intended use of #tmpl_expand and #tmpl_aexpand is for modules to easily convert a #vp_tmpl_t
1271 * provided by the conf parser, into a usable value.
1272 * The value returned should be raw and undoctored for #PW_TYPE_STRING and #PW_TYPE_OCTETS types,
1273 * and the printable (string) version of the data for all others.
1274 *
1275 * Depending what arguments are passed, either copies the value to buff, or writes a pointer
1276 * to a string buffer to out. This allows the most efficient access to the value resolved by
1277 * the #vp_tmpl_t, avoiding unecessary string copies.
1278 *
1279 * @note This function is used where raw string values are needed, which may mean the string
1280 * returned may be binary data or contain unprintable chars. #fr_prints or #fr_aprints should
1281 * be used before using these values in debug statements. #is_printable can be used to check
1282 * if the string only contains printable chars.
1283 *
1284 * @param out Where to write a pointer to the string buffer. On return may point to buff if
1285 * buff was used to store the value. Otherwise will point to a #value_data_t buffer,
1286 * or the name of the template. To force copying to buff, out should be NULL.
1287 * @param buff Expansion buffer, may be NULL if out is not NULL, and processing #TMPL_TYPE_LITERAL
1288 * or string types.
1289 * @param bufflen Length of expansion buffer.
1290 * @param request Current request.
1291 * @param vpt to expand. Must be one of the following types:
1292 * - #TMPL_TYPE_LITERAL
1293 * - #TMPL_TYPE_EXEC
1294 * - #TMPL_TYPE_XLAT
1295 * - #TMPL_TYPE_XLAT_STRUCT
1296 * - #TMPL_TYPE_ATTR
1297 * - #TMPL_TYPE_DATA
1298 * @param escape xlat escape function (only used for xlat types).
1299 * @param escape_ctx xlat escape function data.
1300 * @return -1 on error, else the length of data written to buff, or pointed to by out.
1301 */
tmpl_expand(char const ** out,char * buff,size_t bufflen,REQUEST * request,vp_tmpl_t const * vpt,xlat_escape_t escape,void * escape_ctx)1302 ssize_t tmpl_expand(char const **out, char *buff, size_t bufflen, REQUEST *request,
1303 vp_tmpl_t const *vpt, xlat_escape_t escape, void *escape_ctx)
1304 {
1305 VALUE_PAIR *vp;
1306 ssize_t slen = -1; /* quiet compiler */
1307
1308 VERIFY_TMPL(vpt);
1309
1310 rad_assert(vpt->type != TMPL_TYPE_LIST);
1311
1312 if (out) *out = NULL;
1313
1314 switch (vpt->type) {
1315 case TMPL_TYPE_LITERAL:
1316 RDEBUG4("EXPAND TMPL LITERAL");
1317
1318 if (!out) {
1319 rad_assert(buff);
1320 memcpy(buff, vpt->name, vpt->len >= bufflen ? bufflen : vpt->len + 1);
1321 } else {
1322 *out = vpt->name;
1323 }
1324 return vpt->len;
1325
1326 case TMPL_TYPE_EXEC:
1327 {
1328 RDEBUG4("EXPAND TMPL EXEC");
1329 rad_assert(buff);
1330 if (radius_exec_program(request, buff, bufflen, NULL, request, vpt->name, NULL,
1331 true, false, EXEC_TIMEOUT) != 0) {
1332 return -1;
1333 }
1334 slen = strlen(buff);
1335 if (out) *out = buff;
1336 }
1337 break;
1338
1339 case TMPL_TYPE_XLAT:
1340 RDEBUG4("EXPAND TMPL XLAT");
1341 rad_assert(buff);
1342 /* Error in expansion, this is distinct from zero length expansion */
1343 slen = radius_xlat(buff, bufflen, request, vpt->name, escape, escape_ctx);
1344 if (slen < 0) return slen;
1345 if (out) *out = buff;
1346 break;
1347
1348 case TMPL_TYPE_XLAT_STRUCT:
1349 RDEBUG4("EXPAND TMPL XLAT STRUCT");
1350 rad_assert(buff);
1351 /* Error in expansion, this is distinct from zero length expansion */
1352 slen = radius_xlat_struct(buff, bufflen, request, vpt->tmpl_xlat, escape, escape_ctx);
1353 if (slen < 0) {
1354 return slen;
1355 }
1356 slen = strlen(buff);
1357 if (out) *out = buff;
1358 break;
1359
1360 case TMPL_TYPE_ATTR:
1361 {
1362 int ret;
1363
1364 RDEBUG4("EXPAND TMPL ATTR");
1365 rad_assert(buff);
1366 ret = tmpl_find_vp(&vp, request, vpt);
1367 if (ret < 0) return -2;
1368
1369 if (out && ((vp->da->type == PW_TYPE_STRING) || (vp->da->type == PW_TYPE_OCTETS))) {
1370 *out = vp->data.ptr;
1371 slen = vp->vp_length;
1372 } else {
1373 if (out) *out = buff;
1374 slen = vp_prints_value(buff, bufflen, vp, '\0');
1375 }
1376 }
1377 break;
1378
1379 case TMPL_TYPE_DATA:
1380 {
1381 RDEBUG4("EXPAND TMPL DATA");
1382
1383 if (out && ((vpt->tmpl_data_type == PW_TYPE_STRING) || (vpt->tmpl_data_type == PW_TYPE_OCTETS))) {
1384 *out = vpt->tmpl_data_value.ptr;
1385 slen = vpt->tmpl_data_length;
1386 } else {
1387 if (out) *out = buff;
1388 /**
1389 * @todo tmpl_expand should accept an enumv da from the lhs of the map.
1390 */
1391 slen = value_data_prints(buff, bufflen, vpt->tmpl_data_type, NULL, &vpt->tmpl_data_value, vpt->tmpl_data_length, '\0');
1392 }
1393 }
1394 break;
1395
1396 /*
1397 * We should never be expanding these.
1398 */
1399 case TMPL_TYPE_UNKNOWN:
1400 case TMPL_TYPE_NULL:
1401 case TMPL_TYPE_LIST:
1402 case TMPL_TYPE_REGEX:
1403 case TMPL_TYPE_ATTR_UNDEFINED:
1404 case TMPL_TYPE_REGEX_STRUCT:
1405 rad_assert(0 == 1);
1406 slen = -1;
1407 break;
1408 }
1409
1410 if (slen < 0) return slen;
1411
1412
1413 #if 0
1414 /*
1415 * If we're doing correct escapes, we may have to re-parse the string.
1416 * If the string is from another expansion, it needs re-parsing.
1417 * Or, if it's from a "string" attribute, it needs re-parsing.
1418 * Integers, IP addresses, etc. don't need re-parsing.
1419 */
1420 if (cf_new_escape && (vpt->type != TMPL_TYPE_ATTR)) {
1421 value_data_t vd;
1422 int ret;
1423
1424 PW_TYPE type = PW_TYPE_STRING;
1425
1426 slen = value_data_from_str(ctx, &vd, &type, NULL, *out, slen, '"');
1427 talloc_free(*out); /* free the old value */
1428 *out = vd.ptr;
1429 }
1430 #endif
1431
1432 if (vpt->type == TMPL_TYPE_XLAT_STRUCT) {
1433 RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
1434 RDEBUG2(" --> %s", buff);
1435 }
1436
1437 return slen;
1438 }
1439
1440 /** Expand a template to a string, allocing a new buffer to hold the string
1441 *
1442 * The intended use of #tmpl_expand and #tmpl_aexpand is for modules to easily convert a #vp_tmpl_t
1443 * provided by the conf parser, into a usable value.
1444 * The value returned should be raw and undoctored for #PW_TYPE_STRING and #PW_TYPE_OCTETS types,
1445 * and the printable (string) version of the data for all others.
1446 *
1447 * This function will always duplicate values, whereas #tmpl_expand may return a pointer to an
1448 * existing buffer.
1449 *
1450 * @note This function is used where raw string values are needed, which may mean the string
1451 * returned may be binary data or contain unprintable chars. #fr_prints or #fr_aprints should
1452 * be used before using these values in debug statements. #is_printable can be used to check
1453 * if the string only contains printable chars.
1454 *
1455 * @note The type (char or uint8_t) can be obtained with talloc_get_type, and may be used as a
1456 * hint as to how to process or print the data.
1457 *
1458 * @param ctx to allocate new buffer in.
1459 * @param out Where to write pointer to the new buffer.
1460 * @param request Current request.
1461 * @param vpt to expand. Must be one of the following types:
1462 * - #TMPL_TYPE_LITERAL
1463 * - #TMPL_TYPE_EXEC
1464 * - #TMPL_TYPE_XLAT
1465 * - #TMPL_TYPE_XLAT_STRUCT
1466 * - #TMPL_TYPE_ATTR
1467 * - #TMPL_TYPE_DATA
1468 * @param escape xlat escape function (only used for xlat types).
1469 * @param escape_ctx xlat escape function data (only used for xlat types).
1470 * @return
1471 * - -1 on failure.
1472 * - The length of data written to buff, or pointed to by out.
1473 */
tmpl_aexpand(TALLOC_CTX * ctx,char ** out,REQUEST * request,vp_tmpl_t const * vpt,xlat_escape_t escape,void * escape_ctx)1474 ssize_t tmpl_aexpand(TALLOC_CTX *ctx, char **out, REQUEST *request, vp_tmpl_t const *vpt,
1475 xlat_escape_t escape, void *escape_ctx)
1476 {
1477 VALUE_PAIR *vp;
1478 ssize_t slen = -1; /* quiet compiler */
1479
1480 rad_assert(vpt->type != TMPL_TYPE_LIST);
1481
1482 VERIFY_TMPL(vpt);
1483
1484 *out = NULL;
1485
1486 switch (vpt->type) {
1487 case TMPL_TYPE_LITERAL:
1488 RDEBUG4("EXPAND TMPL LITERAL");
1489 *out = talloc_bstrndup(ctx, vpt->name, vpt->len);
1490 return vpt->len;
1491
1492 case TMPL_TYPE_EXEC:
1493 {
1494 char *buff = NULL;
1495
1496 RDEBUG4("EXPAND TMPL EXEC");
1497 buff = talloc_array(ctx, char, 1024);
1498 if (radius_exec_program(request, buff, 1024, NULL, request, vpt->name, NULL,
1499 true, false, EXEC_TIMEOUT) != 0) {
1500 TALLOC_FREE(buff);
1501 return -1;
1502 }
1503 slen = strlen(buff);
1504 *out = buff;
1505 }
1506 break;
1507
1508 case TMPL_TYPE_XLAT:
1509 RDEBUG4("EXPAND TMPL XLAT");
1510 /* Error in expansion, this is distinct from zero length expansion */
1511 slen = radius_axlat(out, request, vpt->name, escape, escape_ctx);
1512 if (slen < 0) {
1513 rad_assert(!*out);
1514 return slen;
1515 }
1516 rad_assert(*out);
1517 slen = strlen(*out);
1518 break;
1519
1520 case TMPL_TYPE_XLAT_STRUCT:
1521 RDEBUG4("EXPAND TMPL XLAT STRUCT");
1522 /* Error in expansion, this is distinct from zero length expansion */
1523 slen = radius_axlat_struct(out, request, vpt->tmpl_xlat, escape, escape_ctx);
1524 if (slen < 0) {
1525 rad_assert(!*out);
1526 return slen;
1527 }
1528 slen = strlen(*out);
1529 break;
1530
1531 case TMPL_TYPE_ATTR:
1532 {
1533 int ret;
1534
1535 RDEBUG4("EXPAND TMPL ATTR");
1536 ret = tmpl_find_vp(&vp, request, vpt);
1537 if (ret < 0) return -2;
1538
1539 switch (vpt->tmpl_da->type) {
1540 case PW_TYPE_STRING:
1541 *out = talloc_bstrndup(ctx, vp->vp_strvalue, vp->vp_length);
1542 if (!*out) return -1;
1543 slen = vp->vp_length;
1544 break;
1545
1546 case PW_TYPE_OCTETS:
1547 *out = talloc_memdup(ctx, vp->vp_octets, vp->vp_length);
1548 if (!*out) return -1;
1549 slen = vp->vp_length;
1550 break;
1551
1552 default:
1553 *out = vp_aprints_value(ctx, vp, '\0');
1554 if (!*out) return -1;
1555 slen = talloc_array_length(*out) - 1;
1556 break;
1557 }
1558 }
1559 break;
1560
1561 case TMPL_TYPE_DATA:
1562 {
1563 RDEBUG4("EXPAND TMPL DATA");
1564
1565 switch (vpt->tmpl_data_type) {
1566 case PW_TYPE_STRING:
1567 *out = talloc_bstrndup(ctx, vpt->tmpl_data_value.strvalue, vpt->tmpl_data_length);
1568 if (!*out) return -1;
1569 slen = vpt->tmpl_data_length;
1570 break;
1571
1572 case PW_TYPE_OCTETS:
1573 *out = talloc_memdup(ctx, vpt->tmpl_data_value.octets, vpt->tmpl_data_length);
1574 if (!*out) return -1;
1575 slen = vpt->tmpl_data_length;
1576 break;
1577
1578 default:
1579 *out = value_data_aprints(ctx, vpt->tmpl_data_type, NULL, &vpt->tmpl_data_value, vpt->tmpl_data_length, '\0');
1580 if (!*out) return -1;
1581 slen = talloc_array_length(*out) - 1;
1582 break;
1583 }
1584 }
1585 break;
1586
1587 /*
1588 * We should never be expanding these.
1589 */
1590 case TMPL_TYPE_UNKNOWN:
1591 case TMPL_TYPE_NULL:
1592 case TMPL_TYPE_LIST:
1593 case TMPL_TYPE_REGEX:
1594 case TMPL_TYPE_ATTR_UNDEFINED:
1595 case TMPL_TYPE_REGEX_STRUCT:
1596 rad_assert(0 == 1);
1597 slen = -1;
1598 break;
1599 }
1600
1601 if (slen < 0) return slen;
1602
1603 /*
1604 * If we're doing correct escapes, we may have to re-parse the string.
1605 * If the string is from another expansion, it needs re-parsing.
1606 * Or, if it's from a "string" attribute, it needs re-parsing.
1607 * Integers, IP addresses, etc. don't need re-parsing.
1608 */
1609 if (cf_new_escape && (vpt->type != TMPL_TYPE_ATTR)) {
1610 value_data_t vd;
1611
1612 PW_TYPE type = PW_TYPE_STRING;
1613
1614 slen = value_data_from_str(ctx, &vd, &type, NULL, *out, slen, '"');
1615 talloc_free(*out); /* free the old value */
1616 *out = vd.ptr;
1617 }
1618
1619 if (vpt->type == TMPL_TYPE_XLAT_STRUCT) {
1620 RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
1621 RDEBUG2(" --> %s", *out);
1622 }
1623
1624 return slen;
1625 }
1626
1627 /** Print a #vp_tmpl_t to a string
1628 *
1629 * @param[out] out Where to write the presentation format #vp_tmpl_t string.
1630 * @param[in] outlen Size of output buffer.
1631 * @param[in] vpt to print
1632 * @param[in] values Used for integer attributes only. #DICT_ATTR to use when mapping integer
1633 * values to strings.
1634 * @return the size of the string written to the output buffer.
1635 */
tmpl_prints(char * out,size_t outlen,vp_tmpl_t const * vpt,DICT_ATTR const * values)1636 size_t tmpl_prints(char *out, size_t outlen, vp_tmpl_t const *vpt, DICT_ATTR const *values)
1637 {
1638 size_t len;
1639 char c;
1640 char const *p;
1641 char *q = out;
1642
1643 if (!vpt) {
1644 *out = '\0';
1645 return 0;
1646 }
1647
1648 VERIFY_TMPL(vpt);
1649
1650 switch (vpt->type) {
1651 default:
1652 return 0;
1653
1654 case TMPL_TYPE_REGEX:
1655 case TMPL_TYPE_REGEX_STRUCT:
1656 c = '/';
1657 break;
1658
1659 case TMPL_TYPE_XLAT:
1660 case TMPL_TYPE_XLAT_STRUCT:
1661 c = '"';
1662 break;
1663 case TMPL_TYPE_LITERAL: /* single-quoted or bare word */
1664 /*
1665 * Hack
1666 */
1667 for (p = vpt->name; *p != '\0'; p++) {
1668 if (*p == ' ') break;
1669 if (*p == '\'') break;
1670 if (!dict_attr_allowed_chars[(int) *p]) break;
1671 }
1672
1673 if (!*p) {
1674 strlcpy(out, vpt->name, outlen);
1675 return strlen(out);
1676 }
1677
1678 c = vpt->quote;
1679 break;
1680
1681 case TMPL_TYPE_EXEC:
1682 c = '`';
1683 break;
1684
1685 case TMPL_TYPE_LIST:
1686 out[0] = '&';
1687 if (vpt->tmpl_request == REQUEST_CURRENT) {
1688 snprintf(out + 1, outlen - 1, "%s:",
1689 fr_int2str(pair_lists, vpt->tmpl_list, ""));
1690 } else {
1691 snprintf(out + 1, outlen - 1, "%s.%s:",
1692 fr_int2str(request_refs, vpt->tmpl_request, ""),
1693 fr_int2str(pair_lists, vpt->tmpl_list, ""));
1694 }
1695 len = strlen(out);
1696 goto attr_inst_tag;
1697
1698 case TMPL_TYPE_ATTR:
1699 out[0] = '&';
1700 if (vpt->tmpl_request == REQUEST_CURRENT) {
1701 if (vpt->tmpl_list == PAIR_LIST_REQUEST) {
1702 strlcpy(out + 1, vpt->tmpl_da->name, outlen - 1);
1703 } else {
1704 snprintf(out + 1, outlen - 1, "%s:%s",
1705 fr_int2str(pair_lists, vpt->tmpl_list, ""),
1706 vpt->tmpl_da->name);
1707 }
1708
1709 } else {
1710 snprintf(out + 1, outlen - 1, "%s.%s:%s",
1711 fr_int2str(request_refs, vpt->tmpl_request, ""),
1712 fr_int2str(pair_lists, vpt->tmpl_list, ""),
1713 vpt->tmpl_da->name);
1714 }
1715
1716 len = strlen(out);
1717
1718 attr_inst_tag:
1719 if ((vpt->tmpl_tag == TAG_ANY) && (vpt->tmpl_num == NUM_ANY)) return len;
1720
1721 q = out + len;
1722 outlen -= len;
1723
1724 if (vpt->tmpl_tag != TAG_ANY) {
1725 snprintf(q, outlen, ":%d", vpt->tmpl_tag);
1726 len = strlen(q);
1727 q += len;
1728 outlen -= len;
1729 }
1730
1731 switch (vpt->tmpl_num) {
1732 case NUM_ANY:
1733 break;
1734
1735 case NUM_ALL:
1736 snprintf(q, outlen, "[*]");
1737 len = strlen(q);
1738 q += len;
1739 break;
1740
1741 case NUM_COUNT:
1742 snprintf(q, outlen, "[#]");
1743 len = strlen(q);
1744 q += len;
1745 break;
1746
1747 case NUM_LAST:
1748 snprintf(q, outlen, "[n]");
1749 len = strlen(q);
1750 q += len;
1751 break;
1752
1753 default:
1754 snprintf(q, outlen, "[%i]", vpt->tmpl_num);
1755 len = strlen(q);
1756 q += len;
1757 break;
1758 }
1759
1760 return (q - out);
1761
1762 case TMPL_TYPE_ATTR_UNDEFINED:
1763 out[0] = '&';
1764 if (vpt->tmpl_request == REQUEST_CURRENT) {
1765 if (vpt->tmpl_list == PAIR_LIST_REQUEST) {
1766 strlcpy(out + 1, vpt->tmpl_unknown_name, outlen - 1);
1767 } else {
1768 snprintf(out + 1, outlen - 1, "%s:%s",
1769 fr_int2str(pair_lists, vpt->tmpl_list, ""),
1770 vpt->tmpl_unknown_name);
1771 }
1772
1773 } else {
1774 snprintf(out + 1, outlen - 1, "%s.%s:%s",
1775 fr_int2str(request_refs, vpt->tmpl_request, ""),
1776 fr_int2str(pair_lists, vpt->tmpl_list, ""),
1777 vpt->tmpl_unknown_name);
1778 }
1779
1780 len = strlen(out);
1781
1782 if (vpt->tmpl_num == NUM_ANY) {
1783 return len;
1784 }
1785
1786 q = out + len;
1787 outlen -= len;
1788
1789 if (vpt->tmpl_num != NUM_ANY) {
1790 snprintf(q, outlen, "[%i]", vpt->tmpl_num);
1791 len = strlen(q);
1792 q += len;
1793 }
1794
1795 return (q - out);
1796
1797 case TMPL_TYPE_DATA:
1798 return value_data_prints(out, outlen, vpt->tmpl_data_type, values, &vpt->tmpl_data_value,
1799 vpt->tmpl_data_length, vpt->quote);
1800 }
1801
1802 if (outlen <= 3) {
1803 *out = '\0';
1804 return 0;
1805 }
1806
1807 *(q++) = c;
1808
1809 /*
1810 * Print it with appropriate escaping
1811 */
1812 if (cf_new_escape && (c == '/')) {
1813 len = fr_prints(q, outlen - 3, vpt->name, vpt->len, '\0');
1814 } else {
1815 len = fr_prints(q, outlen - 3, vpt->name, vpt->len, c);
1816 }
1817
1818 q += len;
1819 *(q++) = c;
1820 *q = '\0';
1821
1822 return q - out;
1823 }
1824
1825 /** Initialise a #vp_cursor_t to the #VALUE_PAIR specified by a #vp_tmpl_t
1826 *
1827 * This makes iterating over the one or more #VALUE_PAIR specified by a #vp_tmpl_t
1828 * significantly easier.
1829 *
1830 * @param err May be NULL if no error code is required. Will be set to:
1831 * - 0 on success.
1832 * - -1 if no matching #VALUE_PAIR could be found.
1833 * - -2 if list could not be found (doesn't exist in current #REQUEST).
1834 * - -3 if context could not be found (no parent #REQUEST available).
1835 * @param cursor to store iterator state.
1836 * @param request The current #REQUEST.
1837 * @param vpt specifying the #VALUE_PAIR type/tag or list to iterate over.
1838 * @return the first #VALUE_PAIR specified by the #vp_tmpl_t, or NULL if no matching
1839 * #VALUE_PAIR found, and NULL on error.
1840 *
1841 * @see tmpl_cursor_next
1842 */
tmpl_cursor_init(int * err,vp_cursor_t * cursor,REQUEST * request,vp_tmpl_t const * vpt)1843 VALUE_PAIR *tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request, vp_tmpl_t const *vpt)
1844 {
1845 VALUE_PAIR **vps, *vp = NULL;
1846 int num;
1847
1848 VERIFY_TMPL(vpt);
1849
1850 rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST));
1851
1852 if (err) *err = 0;
1853
1854 if (radius_request(&request, vpt->tmpl_request) < 0) {
1855 if (err) *err = -3;
1856 return NULL;
1857 }
1858 vps = radius_list(request, vpt->tmpl_list);
1859 if (!vps) {
1860 if (err) *err = -2;
1861 return NULL;
1862 }
1863 (void) fr_cursor_init(cursor, vps);
1864
1865 switch (vpt->type) {
1866 /*
1867 * May not may not be found, but it *is* a known name.
1868 */
1869 case TMPL_TYPE_ATTR:
1870 switch (vpt->tmpl_num) {
1871 case NUM_ANY:
1872 vp = fr_cursor_next_by_da(cursor, vpt->tmpl_da, vpt->tmpl_tag);
1873 if (!vp) {
1874 if (err) *err = -1;
1875 return NULL;
1876 }
1877 VERIFY_VP(vp);
1878 return vp;
1879
1880 /*
1881 * Get the last instance of a VALUE_PAIR.
1882 */
1883 case NUM_LAST:
1884 {
1885 VALUE_PAIR *last = NULL;
1886
1887 while ((vp = fr_cursor_next_by_da(cursor, vpt->tmpl_da, vpt->tmpl_tag))) {
1888 VERIFY_VP(vp);
1889 last = vp;
1890 }
1891 VERIFY_VP(last);
1892 if (!last) break;
1893 return last;
1894 }
1895
1896 /*
1897 * Callers expect NUM_COUNT to setup the cursor to point
1898 * to the first attribute in the list we're meant to be
1899 * counting.
1900 *
1901 * It does not produce a virtual attribute containing the
1902 * total number of attributes.
1903 */
1904 case NUM_COUNT:
1905 return fr_cursor_next_by_da(cursor, vpt->tmpl_da, vpt->tmpl_tag);
1906
1907 default:
1908 num = vpt->tmpl_num;
1909 while ((vp = fr_cursor_next_by_da(cursor, vpt->tmpl_da, vpt->tmpl_tag))) {
1910 VERIFY_VP(vp);
1911 if (num-- <= 0) return vp;
1912 }
1913 break;
1914 }
1915
1916 if (err) *err = -1;
1917 return NULL;
1918
1919 case TMPL_TYPE_LIST:
1920 switch (vpt->tmpl_num) {
1921 case NUM_COUNT:
1922 case NUM_ANY:
1923 case NUM_ALL:
1924 vp = fr_cursor_init(cursor, vps);
1925 if (!vp) {
1926 if (err) *err = -1;
1927 return NULL;
1928 }
1929 VERIFY_VP(vp);
1930 return vp;
1931
1932 /*
1933 * Get the last instance of a VALUE_PAIR.
1934 */
1935 case NUM_LAST:
1936 {
1937 VALUE_PAIR *last = NULL;
1938
1939 for (vp = fr_cursor_init(cursor, vps);
1940 vp;
1941 vp = fr_cursor_next(cursor)) {
1942 VERIFY_VP(vp);
1943 last = vp;
1944 }
1945 if (!last) break;
1946 VERIFY_VP(last);
1947 return last;
1948 }
1949
1950 default:
1951 num = vpt->tmpl_num;
1952 for (vp = fr_cursor_init(cursor, vps);
1953 vp;
1954 vp = fr_cursor_next(cursor)) {
1955 VERIFY_VP(vp);
1956 if (num-- <= 0) return vp;
1957 }
1958 break;
1959 }
1960
1961 break;
1962
1963 default:
1964 rad_assert(0);
1965 }
1966
1967 return vp;
1968 }
1969
1970 /** Returns the next #VALUE_PAIR specified by vpt
1971 *
1972 * @param cursor initialised with #tmpl_cursor_init.
1973 * @param vpt specifying the #VALUE_PAIR type/tag to iterate over.
1974 * Must be one of the following types:
1975 * - #TMPL_TYPE_LIST
1976 * - #TMPL_TYPE_ATTR
1977 * @return NULL if no more matching #VALUE_PAIR of the specified type/tag are found.
1978 */
tmpl_cursor_next(vp_cursor_t * cursor,vp_tmpl_t const * vpt)1979 VALUE_PAIR *tmpl_cursor_next(vp_cursor_t *cursor, vp_tmpl_t const *vpt)
1980 {
1981 rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST));
1982
1983 VERIFY_TMPL(vpt);
1984
1985 switch (vpt->type) {
1986 /*
1987 * May not may not be found, but it *is* a known name.
1988 */
1989 case TMPL_TYPE_ATTR:
1990 switch (vpt->tmpl_num) {
1991 default:
1992 return NULL;
1993
1994 case NUM_ALL:
1995 case NUM_COUNT: /* This cursor is being used to count matching attrs */
1996 break;
1997 }
1998 return fr_cursor_next_by_da(cursor, vpt->tmpl_da, vpt->tmpl_tag);
1999
2000 case TMPL_TYPE_LIST:
2001 switch (vpt->tmpl_num) {
2002 default:
2003 return NULL;
2004
2005 case NUM_ALL:
2006 case NUM_COUNT: /* This cursor is being used to count matching attrs */
2007 break;
2008 }
2009 return fr_cursor_next(cursor);
2010
2011 default:
2012 rad_assert(0);
2013 return NULL; /* Older versions of GCC flag the lack of return as an error */
2014 }
2015 }
2016
2017 /** Copy pairs matching a #vp_tmpl_t in the current #REQUEST
2018 *
2019 * @param ctx to allocate new #VALUE_PAIR in.
2020 * @param out Where to write the copied #VALUE_PAIR (s).
2021 * @param request The current #REQUEST.
2022 * @param vpt specifying the #VALUE_PAIR type/tag or list to copy.
2023 * Must be one of the following types:
2024 * - #TMPL_TYPE_LIST
2025 * - #TMPL_TYPE_ATTR
2026 * @return
2027 * - -1 if no matching #VALUE_PAIR could be found.
2028 * - -2 if list could not be found (doesn't exist in current #REQUEST).
2029 * - -3 if context could not be found (no parent #REQUEST available).
2030 * - -4 on memory allocation error.
2031 */
tmpl_copy_vps(TALLOC_CTX * ctx,VALUE_PAIR ** out,REQUEST * request,vp_tmpl_t const * vpt)2032 int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_tmpl_t const *vpt)
2033 {
2034 VALUE_PAIR *vp;
2035 vp_cursor_t from, to;
2036
2037 VERIFY_TMPL(vpt);
2038
2039 int err;
2040
2041 rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST));
2042
2043 *out = NULL;
2044
2045 fr_cursor_init(&to, out);
2046
2047 for (vp = tmpl_cursor_init(&err, &from, request, vpt);
2048 vp;
2049 vp = tmpl_cursor_next(&from, vpt)) {
2050 vp = fr_pair_copy(ctx, vp);
2051 if (!vp) {
2052 fr_pair_list_free(out);
2053 return -4;
2054 }
2055 fr_cursor_insert(&to, vp);
2056 }
2057
2058 return err;
2059 }
2060
2061 /** Returns the first VP matching a #vp_tmpl_t
2062 *
2063 * @param out where to write the retrieved vp.
2064 * @param request The current #REQUEST.
2065 * @param vpt specifying the #VALUE_PAIR type/tag to find.
2066 * Must be one of the following types:
2067 * - #TMPL_TYPE_LIST
2068 * - #TMPL_TYPE_ATTR
2069 * @return
2070 * - -1 if no matching #VALUE_PAIR could be found.
2071 * - -2 if list could not be found (doesn't exist in current #REQUEST).
2072 * - -3 if context could not be found (no parent #REQUEST available).
2073 */
tmpl_find_vp(VALUE_PAIR ** out,REQUEST * request,vp_tmpl_t const * vpt)2074 int tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, vp_tmpl_t const *vpt)
2075 {
2076 vp_cursor_t cursor;
2077 VALUE_PAIR *vp;
2078
2079 VERIFY_TMPL(vpt);
2080
2081 int err;
2082
2083 vp = tmpl_cursor_init(&err, &cursor, request, vpt);
2084 if (out) *out = vp;
2085
2086 return err;
2087 }
2088 /* @} **/
2089
2090 #ifdef WITH_VERIFY_PTR
2091 /** Used to check whether areas of a vp_tmpl_t are zeroed out
2092 *
2093 * @param ptr Offset to begin checking at.
2094 * @param len How many bytes to check.
2095 * @return pointer to the first non-zero byte, or NULL if all bytes were zero.
2096 */
not_zeroed(uint8_t const * ptr,size_t len)2097 static uint8_t const *not_zeroed(uint8_t const *ptr, size_t len)
2098 {
2099 size_t i;
2100
2101 for (i = 0; i < len; i++) {
2102 if (ptr[i] != 0x00) return ptr + i;
2103 }
2104
2105 return NULL;
2106 }
2107 #define CHECK_ZEROED(_x) not_zeroed((uint8_t const *)&_x + sizeof(_x), sizeof(vpt->data) - sizeof(_x))
2108
2109 /** Verify fields of a vp_tmpl_t make sense
2110 *
2111 * @note If the #vp_tmpl_t is invalid, causes the server to exit.
2112 *
2113 * @param file obtained with __FILE__.
2114 * @param line obtained with __LINE__.
2115 * @param vpt to check.
2116 */
tmpl_verify(char const * file,int line,vp_tmpl_t const * vpt)2117 void tmpl_verify(char const *file, int line, vp_tmpl_t const *vpt)
2118 {
2119 rad_assert(vpt);
2120
2121 if (vpt->type == TMPL_TYPE_UNKNOWN) {
2122 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: vp_tmpl_t type was "
2123 "TMPL_TYPE_UNKNOWN (uninitialised)", file, line);
2124 fr_assert(0);
2125 fr_exit_now(1);
2126 }
2127
2128 if (vpt->type > TMPL_TYPE_NULL) {
2129 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: vp_tmpl_t type was %i "
2130 "(outside range of tmpl_names)", file, line, vpt->type);
2131 fr_assert(0);
2132 fr_exit_now(1);
2133 }
2134
2135 /*
2136 * Do a memcmp of the bytes after where the space allocated for
2137 * the union member should have ended and the end of the union.
2138 * These should always be zero if the union has been initialised
2139 * properly.
2140 *
2141 * If they're still all zero, do TMPL_TYPE specific checks.
2142 */
2143 switch (vpt->type) {
2144 case TMPL_TYPE_NULL:
2145 if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
2146 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_NULL "
2147 "has non-zero bytes in its data union", file, line);
2148 fr_assert(0);
2149 fr_exit_now(1);
2150 }
2151 break;
2152
2153 case TMPL_TYPE_LITERAL:
2154 if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
2155 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LITERAL "
2156 "has non-zero bytes in its data union", file, line);
2157 fr_assert(0);
2158 fr_exit_now(1);
2159 }
2160 break;
2161
2162 case TMPL_TYPE_XLAT:
2163 case TMPL_TYPE_XLAT_STRUCT:
2164 break;
2165
2166 /* @todo When regexes get converted to xlat the flags field of the regex union is used
2167 case TMPL_TYPE_XLAT:
2168 if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
2169 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
2170 "has non-zero bytes in its data union", file, line);
2171 fr_assert(0);
2172 fr_exit_now(1);
2173 }
2174 break;
2175
2176 case TMPL_TYPE_XLAT_STRUCT:
2177 if (CHECK_ZEROED(vpt->data.xlat)) {
2178 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_STRUCT "
2179 "has non-zero bytes after the data.xlat pointer in the union", file, line);
2180 fr_assert(0);
2181 fr_exit_now(1);
2182 }
2183 break;
2184 */
2185
2186 case TMPL_TYPE_EXEC:
2187 if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
2188 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_EXEC "
2189 "has non-zero bytes in its data union", file, line);
2190 fr_assert(0);
2191 fr_exit_now(1);
2192 }
2193 break;
2194
2195 case TMPL_TYPE_ATTR_UNDEFINED:
2196 rad_assert(vpt->tmpl_da == NULL);
2197 break;
2198
2199 case TMPL_TYPE_ATTR:
2200 if (CHECK_ZEROED(vpt->data.attribute)) {
2201 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
2202 "has non-zero bytes after the data.attribute struct in the union",
2203 file, line);
2204 fr_assert(0);
2205 fr_exit_now(1);
2206 }
2207
2208 if (vpt->tmpl_da->flags.is_unknown) {
2209 if (vpt->tmpl_da != (DICT_ATTR const *)&vpt->data.attribute.unknown.da) {
2210 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
2211 "da is marked as unknown, but does not point to the template's "
2212 "unknown da buffer", file, line);
2213 fr_assert(0);
2214 fr_exit_now(1);
2215 }
2216
2217 } else {
2218 DICT_ATTR const *da;
2219
2220 /*
2221 * Attribute may be present with multiple names
2222 */
2223 da = dict_attrbyname(vpt->tmpl_da->name);
2224 if (!da) {
2225 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
2226 "attribute \"%s\" (%s) not found in global dictionary",
2227 file, line, vpt->tmpl_da->name,
2228 fr_int2str(dict_attr_types, vpt->tmpl_da->type, "<INVALID>"));
2229 fr_assert(0);
2230 fr_exit_now(1);
2231 }
2232
2233 if ((da->type == PW_TYPE_COMBO_IP_ADDR) && (da->type != vpt->tmpl_da->type)) {
2234 da = dict_attrbytype(vpt->tmpl_da->attr, vpt->tmpl_da->vendor, vpt->tmpl_da->type);
2235 if (!da) {
2236 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
2237 "attribute \"%s\" variant (%s) not found in global dictionary",
2238 file, line, vpt->tmpl_da->name,
2239 fr_int2str(dict_attr_types, vpt->tmpl_da->type, "<INVALID>"));
2240 fr_assert(0);
2241 fr_exit_now(1);
2242 }
2243 }
2244
2245 if (da != vpt->tmpl_da) {
2246 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
2247 "dictionary pointer %p \"%s\" (%s) "
2248 "and global dictionary pointer %p \"%s\" (%s) differ",
2249 file, line,
2250 vpt->tmpl_da, vpt->tmpl_da->name,
2251 fr_int2str(dict_attr_types, vpt->tmpl_da->type, "<INVALID>"),
2252 da, da->name,
2253 fr_int2str(dict_attr_types, da->type, "<INVALID>"));
2254 fr_assert(0);
2255 fr_exit_now(1);
2256 }
2257 }
2258 break;
2259
2260 case TMPL_TYPE_LIST:
2261 if (CHECK_ZEROED(vpt->data.attribute)) {
2262 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LIST"
2263 "has non-zero bytes after the data.attribute struct in the union", file, line);
2264 fr_assert(0);
2265 fr_exit_now(1);
2266 }
2267
2268 if (vpt->tmpl_da != NULL) {
2269 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LIST da pointer was NULL", file, line);
2270 fr_assert(0);
2271 fr_exit_now(1);
2272 }
2273 break;
2274
2275 case TMPL_TYPE_DATA:
2276 if (CHECK_ZEROED(vpt->data.literal)) {
2277 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA "
2278 "has non-zero bytes after the data.literal struct in the union",
2279 file, line);
2280 fr_assert(0);
2281 fr_exit_now(1);
2282 }
2283
2284 if (vpt->tmpl_data_type == PW_TYPE_INVALID) {
2285 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
2286 "PW_TYPE_INVALID (uninitialised)", file, line);
2287 fr_assert(0);
2288 fr_exit_now(1);
2289 }
2290
2291 if (vpt->tmpl_data_type >= PW_TYPE_MAX) {
2292 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
2293 "%i (outside the range of PW_TYPEs)", file, line, vpt->tmpl_data_type);
2294 fr_assert(0);
2295 fr_exit_now(1);
2296 }
2297 /*
2298 * Unlike VALUE_PAIRs we can't guarantee that VALUE_PAIR_TMPL buffers will
2299 * be talloced. They may be allocated on the stack or in global variables.
2300 */
2301 switch (vpt->tmpl_data_type) {
2302 case PW_TYPE_STRING:
2303 if (vpt->tmpl_data.vp_strvalue[vpt->tmpl_data_length] != '\0') {
2304 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA char buffer not \\0 "
2305 "terminated", file, line);
2306 fr_assert(0);
2307 fr_exit_now(1);
2308 }
2309 break;
2310
2311 case PW_TYPE_TLV:
2312 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA is of type TLV",
2313 file, line);
2314 fr_assert(0);
2315 fr_exit_now(1);
2316
2317 case PW_TYPE_OCTETS:
2318 break;
2319
2320 default:
2321 if (vpt->tmpl_data_length == 0) {
2322 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA data pointer not NULL "
2323 "but len field is zero", file, line);
2324 fr_assert(0);
2325 fr_exit_now(1);
2326 }
2327 }
2328
2329 break;
2330
2331 case TMPL_TYPE_REGEX:
2332 /*
2333 * iflag field is used for non compiled regexes too.
2334 */
2335 if (CHECK_ZEROED(vpt->data.preg)) {
2336 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
2337 "has non-zero bytes after the data.preg struct in the union", file, line);
2338 fr_assert(0);
2339 fr_exit_now(1);
2340 }
2341
2342 if (vpt->tmpl_preg != NULL) {
2343 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
2344 "preg field was not nULL", file, line);
2345 fr_assert(0);
2346 fr_exit_now(1);
2347 }
2348
2349 if ((vpt->tmpl_iflag != true) && (vpt->tmpl_iflag != false)) {
2350 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
2351 "iflag field was neither true or false", file, line);
2352 fr_assert(0);
2353 fr_exit_now(1);
2354 }
2355
2356 if ((vpt->tmpl_mflag != true) && (vpt->tmpl_mflag != false)) {
2357 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
2358 "mflag field was neither true or false", file, line);
2359 fr_assert(0);
2360 fr_exit_now(1);
2361 }
2362
2363 break;
2364
2365 case TMPL_TYPE_REGEX_STRUCT:
2366 if (CHECK_ZEROED(vpt->data.preg)) {
2367 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_STRUCT "
2368 "has non-zero bytes after the data.preg struct in the union", file, line);
2369 fr_assert(0);
2370 fr_exit_now(1);
2371 }
2372
2373 if (vpt->tmpl_preg == NULL) {
2374 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_STRUCT "
2375 "comp field was NULL", file, line);
2376 fr_assert(0);
2377 fr_exit_now(1);
2378 }
2379
2380 if ((vpt->tmpl_iflag != true) && (vpt->tmpl_iflag != false)) {
2381 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_STRUCT "
2382 "iflag field was neither true or false", file, line);
2383 fr_assert(0);
2384 fr_exit_now(1);
2385 }
2386
2387 if ((vpt->tmpl_mflag != true) && (vpt->tmpl_mflag != false)) {
2388 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
2389 "mflag field was neither true or false", file, line);
2390 fr_assert(0);
2391 fr_exit_now(1);
2392 }
2393 break;
2394
2395 case TMPL_TYPE_UNKNOWN:
2396 rad_assert(0);
2397 }
2398 }
2399 #endif
2400