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