The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
base.c
Go to the documentation of this file.
1 /*
2  * This program is 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 (at
5  * 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: a700bb2c0ceb17ae09040d3f3656b1eb0c479862 $
19  * @file curl/base.c
20  * @brief Curl global initialisation
21  *
22  * @copyright 2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  */
24 #include <freeradius-devel/curl/base.h>
25 #include <freeradius-devel/curl/xlat.h>
26 #ifdef WITH_TLS
27 #include <freeradius-devel/tls/base.h>
28 #endif
29 
30 #include <freeradius-devel/util/talloc.h>
31 #include <freeradius-devel/util/syserror.h>
32 #include <freeradius-devel/unlang/xlat_func.h>
33 
34 #include "attrs.h"
35 
37 static fr_dict_t const *dict_freeradius; /*internal dictionary for server*/
38 
41  { .out = &attr_tls_certificate, .name = "TLS-Certificate", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
42  { NULL }
43 };
44 
46  { .out = &dict_freeradius, .proto = "freeradius" },
47  { NULL }
48 };
49 
51  {L("allow"), CURLUSESSL_TRY },
52  {L("demand"), CURLUSESSL_ALL },
53  {L("never"), CURLUSESSL_NONE },
54 };
56 
58 {
59  char const *ca_path = NULL;
60 #if CURL_AT_LEAST_VERSION(7,70,0)
61  ca_path = curl_version_info(CURLVERSION_NOW)->capath;
62 #endif
63  if (!ca_path) return 0;
64  MEM(*out = cf_pair_alloc(cs, rule->name1, ca_path, T_OP_EQ, T_BARE_WORD, quote));
65  return 0;
66 }
67 
70  { FR_CONF_OFFSET_FLAGS("ca_issuer_file", CONF_FLAG_FILE_INPUT, fr_curl_tls_t, ca_issuer_file) },
71  { FR_CONF_OFFSET_FLAGS("ca_path", CONF_FLAG_FILE_INPUT, fr_curl_tls_t, ca_path), .dflt_func = tls_config_dflt_capath },
72  { FR_CONF_OFFSET_FLAGS("certificate_file", CONF_FLAG_FILE_INPUT, fr_curl_tls_t, certificate_file) },
73  { FR_CONF_OFFSET_FLAGS("private_key_file", CONF_FLAG_FILE_INPUT, fr_curl_tls_t, private_key_file) },
74  { FR_CONF_OFFSET_FLAGS("private_key_password", CONF_FLAG_SECRET, fr_curl_tls_t, private_key_password) },
75  { FR_CONF_OFFSET("random_file", fr_curl_tls_t, random_file) },
76  { FR_CONF_OFFSET_TYPE_FLAGS("require_cert", FR_TYPE_VOID, 0, fr_curl_tls_t, require_cert),
77  .func = cf_table_parse_int,
78  .uctx = &(cf_table_parse_ctx_t){
79  .table = fr_curl_sslcode_table,
81  },
82  .dflt = "allow" },
83  { FR_CONF_OFFSET("check_cert", fr_curl_tls_t, check_cert), .dflt = "yes" },
84  { FR_CONF_OFFSET("check_cert_cn", fr_curl_tls_t, check_cert_cn), .dflt = "yes" },
85  { FR_CONF_OFFSET("extract_cert_attrs", fr_curl_tls_t, extract_cert_attrs), .dflt = "no" },
86 #ifdef WITH_TLS
87  { FR_CONF_OFFSET_FLAGS("keylog_file", CONF_FLAG_FILE_OUTPUT, fr_curl_tls_t, keylog_file) },
88 #endif
90 };
91 
93  { FR_CONF_OFFSET("min", fr_slab_config_t, min_elements), .dflt = "10" },
94  { FR_CONF_OFFSET("max", fr_slab_config_t, max_elements), .dflt = "100" },
95  { FR_CONF_OFFSET("cleanup_interval", fr_slab_config_t, interval), .dflt = "30s" },
97 };
98 
101  { FR_CONF_OFFSET("connect_timeout", fr_curl_conn_config_t, connect_timeout), .dflt = "3.0" },
103 };
104 
105 #ifdef WITH_TLS
106 static void _curl_easy_tls_keylog(const SSL *ssl, const char *line)
107 {
108  fr_curl_tls_t const *conf = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl), FR_TLS_EX_INDEX_CURL_CONF);
109 
110  FILE *fp = fopen(conf->keylog_file, "a");
111  if (!fp) {
112  RATE_LIMIT_GLOBAL(WARN, "Failed opening keylog file \"%s\" - %s", conf->keylog_file, fr_syserror(errno));
113  return;
114  }
115 
116  /*
117  * POSIX states fprintf calls must not intermingle
118  * data being written to the same file, and as all
119  * keying material is written on the same line, this
120  * should be safe.
121  */
122  fprintf(fp, "%s\n", line);
123  fclose(fp);
124 }
125 
126 static CURLcode _curl_easy_ssl_ctx_conf(UNUSED CURL *curl, void *ssl_ctx, void *clientp)
127 {
128  SSL_CTX *ctx = ssl_ctx;
129  fr_curl_tls_t const *conf = clientp; /* May not be talloced */
130 
131  SSL_CTX_set_ex_data(ctx, FR_TLS_EX_INDEX_CURL_CONF, UNCONST(void *, conf));
132 
133  if (conf->keylog_file) {
134  SSL_CTX_set_keylog_callback(ctx, _curl_easy_tls_keylog);
135  }
136 
137  return CURLE_OK;
138 }
139 #endif
140 
142 {
143  request_t *request = randle->request;
144 
145  if (conf->certificate_file) FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_SSLCERT, conf->certificate_file);
146  if (conf->private_key_file) FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_SSLKEY, conf->private_key_file);
147  if (conf->private_key_password) FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_KEYPASSWD, conf->private_key_password);
148  if (conf->ca_file) FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_CAINFO, conf->ca_file);
149  if (conf->ca_issuer_file) FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_ISSUERCERT, conf->ca_issuer_file);
150  if (conf->ca_path) FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_CAPATH, conf->ca_path);
151 #if !CURL_AT_LEAST_VERSION(7,84,0)
152  if (conf->random_file) FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_RANDOM_FILE, conf->random_file);
153 #endif
154  FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_USE_SSL, conf->require_cert);
155 
156  FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_SSL_VERIFYPEER, (conf->check_cert == true) ? 1L : 0L);
157  FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_SSL_VERIFYHOST, (conf->check_cert_cn == true) ? 2L : 0L);
158  if (conf->extract_cert_attrs) FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_CERTINFO, 1L);
159 
160 #ifdef WITH_TLS
161  if (conf->keylog_file) {
162  FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_SSL_CTX_FUNCTION, _curl_easy_ssl_ctx_conf);
163  FR_CURL_ROPTIONAL_SET_OPTION(CURLOPT_SSL_CTX_DATA, conf);
164  }
165 #endif
166 
167  return 0;
168 error:
169  return -1;
170 }
171 
173 {
174  CURL *candle = randle->candle;
175  CURLcode ret;
176  int i;
177  char buffer[265];
178  char *p , *q;
179  fr_pair_list_t cert_vps;
180 
181  /*
182  * Examples and documentation show cert_info being
183  * a struct curl_certinfo *, but CPP checks require
184  * it to be a struct curl_slist *.
185  *
186  * https://curl.haxx.se/libcurl/c/certinfo.html
187  */
188  union {
189  struct curl_slist *to_info;
190  struct curl_certinfo *to_certinfo;
191  } ptr;
192  ptr.to_info = NULL;
193 
194  fr_pair_list_init(&cert_vps);
195 
196  ret = curl_easy_getinfo(candle, CURLINFO_CERTINFO, &ptr.to_info);
197  if (ret != CURLE_OK) {
198  REDEBUG("Getting certificate info failed: %i - %s", ret, curl_easy_strerror(ret));
199 
200  return -1;
201  }
202 
203  /*
204  * There doesn't seem to be any way to determine if
205  * the session uses ssl or not, so if no certs are
206  * returned, we assume it's not an ssl session.
207  */
208  if (ptr.to_certinfo->num_of_certs == 0) return 0;
209 
210  RDEBUG2("Chain has %i certificate(s)", ptr.to_certinfo->num_of_certs);
211  for (i = 0; i < ptr.to_certinfo->num_of_certs; i++) {
212  struct curl_slist *cert_attrs;
213  fr_pair_t *container;
214 
215  MEM(container = fr_pair_afrom_da(request->request_ctx, attr_tls_certificate));
216  fr_pair_append(&cert_vps, container);
217 
218  RDEBUG2("Processing certificate %i",i);
219 
220  for (cert_attrs = ptr.to_certinfo->certinfo[i];
221  cert_attrs;
222  cert_attrs = cert_attrs->next) {
223  fr_pair_t *vp;
224  fr_dict_attr_t const *da;
225 
226  q = strchr(cert_attrs->data, ':');
227  if (!q) {
228  RWDEBUG("Malformed certinfo from libcurl: %s", cert_attrs->data);
229  continue;
230  }
231 
232  strlcpy(buffer, cert_attrs->data, (q - cert_attrs->data) + 1);
233  for (p = buffer; *p != '\0'; p++) if (*p == ' ') *p = '-';
234 
236  if (!da) {
237  RDEBUG3("Skipping %s += '%s'", buffer, q + 1);
238  RDEBUG3("If this value is required, define attribute \"%s\"", buffer);
239  continue;
240  }
241  MEM(vp = fr_pair_afrom_da(container, da));
242  fr_pair_value_from_str(vp, q + 1, strlen(q + 1), NULL, true);
243 
244  fr_pair_append(&container->vp_group, vp);
245  }
246  /*
247  * Add a copy of the cert_vps to the request list.
248  */
249  if (!fr_pair_list_empty(&cert_vps)) {
250  /*
251  * Print out all the pairs we have so far
252  */
253  log_request_pair_list(L_DBG_LVL_2, request, NULL, &cert_vps, NULL);
254  fr_pair_list_append(&request->request_pairs, &cert_vps);
255  }
256  }
257  return 0;
258 }
259 
260 /** Free the curl easy handle
261  *
262  * @param[in] arg curl easy handle to free.
263  */
264 static int _curl_tmpl_handle(void *arg)
265 {
266  curl_easy_cleanup(arg);
267  return 0;
268 }
269 
270 /** Return a thread local curl easy handle
271  *
272  * This should only be used for calls into libcurl functions
273  * which don't operate on an active request, like the
274  * escape/unescape functions.
275  *
276  * @return
277  * - A thread local curl easy handle.
278  * - NULL on failure.
279  */
281 {
282  static _Thread_local CURL *t_candle;
283 
284  if (unlikely(t_candle == NULL)) {
285  CURL *candle;
286 
287  MEM(candle = curl_easy_init());
288  fr_atexit_thread_local(t_candle, _curl_tmpl_handle, candle);
289  }
290 
291  return t_candle;
292 }
293 
294 /** Initialise global curl options
295  *
296  * libcurl is meant to performa reference counting, but still seems to
297  * leak lots of memory if we call curl_global_init many times.
298  */
299 static int fr_curl_init(void)
300 {
301  CURLcode ret;
302  curl_version_info_data *curlversion;
303 
304 #ifdef WITH_TLS
305  /*
306  * Use our OpenSSL init with the hope that
307  * the free function will also free the
308  * memory allocated during SSL init.
309  */
310  if (fr_openssl_init() < 0) return -1;
311 #endif
312 
313  if (fr_dict_autoload(curl_dict) < 0) {
314  PERROR("Failed loading dictionaries for curl");
315  return -1;
316  }
317 
318  if (fr_dict_attr_autoload(curl_attr) < 0) {
319  PERROR("Failed loading dictionaries for curl");
320  return -1;
321  }
322 
323  ret = curl_global_init(CURL_GLOBAL_ALL);
324  if (ret != CURLE_OK) {
325  ERROR("CURL init returned error: %i - %s", ret, curl_easy_strerror(ret));
326  error:
328  return -1;
329  }
330 
331  curlversion = curl_version_info(CURLVERSION_NOW);
332  if (strcmp(LIBCURL_VERSION, curlversion->version) != 0) {
333  WARN("libcurl version changed since the server was built");
334  WARN("linked: %s built: %s", curlversion->version, LIBCURL_VERSION);
335  }
336 
337  INFO("libcurl version: %s", curl_version());
338 
339  {
340  xlat_t *xlat;
341 
342  /*
343  * Generic escape function for all CURL based modules
344  * Use CURL_URI_SAFE_FOR within the module.
345  */
346  xlat = xlat_func_register(NULL, "uri.escape", fr_curl_xlat_uri_escape, FR_TYPE_STRING);
347  if (unlikely(!xlat)) {
348  ERROR("Failed registering \"uri.escape\" xlat");
349  goto error;
350  }
354 
355  /*
356  * Generic safe function for all CURL based modules
357  * Use CURL_URI_SAFE_FOR within the module.
358  */
359  xlat = xlat_func_register(NULL, "uri.safe", xlat_transparent, FR_TYPE_STRING);
360  if (unlikely(!xlat)) {
361  ERROR("Failed registering \"uri.safe\" xlat");
362  goto error;
363  }
367 
368  /*
369  * Generic unescape function for all CURL based modules
370  */
371  xlat = xlat_func_register(NULL, "uri.unescape", fr_curl_xlat_uri_unescape, FR_TYPE_STRING);
372  if (unlikely(!xlat)) {
373  ERROR("Failed registering \"uri.unescape\" xlat");
374  goto error;
375  }
378  }
379 
380  return 0;
381 }
382 
383 static void fr_curl_free(void)
384 {
386 
387 #ifdef WITH_TLS
388  fr_openssl_free();
389 #endif
390  curl_global_cleanup();
391 
392  xlat_func_unregister("uri.escape");
393  xlat_func_unregister("uri.safe");
394  xlat_func_unregister("uri.unescape");
395 }
396 
397 /*
398  * Public symbol modules can reference to auto instantiate libcurl
399  */
401  .name = "curl",
402  .init = fr_curl_init,
403  .free = fr_curl_free
404 };
static int const char char buffer[256]
Definition: acutest.h:574
int const char int line
Definition: acutest.h:702
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition: atexit.h:221
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define unlikely(_x)
Definition: build.h:379
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
int cf_table_parse_int(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Generic function for parsing conf pair values as int.
Definition: cf_parse.c:1474
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
char const * name1
Name of the CONF_ITEM to parse.
Definition: cf_parse.h:565
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:256
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition: cf_parse.h:297
@ CONF_FLAG_FILE_OUTPUT
File matching value must exist, and must be writable.
Definition: cf_parse.h:413
@ CONF_FLAG_SECRET
Only print value if debug level >= 3.
Definition: cf_parse.h:409
@ CONF_FLAG_FILE_INPUT
File matching value must exist, and must be readable.
Definition: cf_parse.h:411
#define FR_CONF_OFFSET_TYPE_FLAGS(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:241
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
Configuration AVP similar to a fr_pair_t.
Definition: cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:101
CONF_PAIR * cf_pair_alloc(CONF_SECTION *parent, char const *attr, char const *value, fr_token_t op, fr_token_t lhs_quote, fr_token_t rhs_quote)
Allocate a CONF_PAIR.
Definition: cf_util.c:1279
#define FR_CURL_ROPTIONAL_SET_OPTION(_x, _y)
Definition: base.h:56
request_t * request
Current request.
Definition: base.h:104
CURL * candle
Request specific handle.
Definition: base.h:102
Structure representing an individual request being passed to curl for processing.
Definition: base.h:101
xlat_action_t fr_curl_xlat_uri_escape(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
xlat function to escape URI encoded strings
Definition: xlat.c:45
xlat_arg_parser_t const fr_curl_xlat_uri_args[]
Definition: xlat.c:32
xlat_action_t fr_curl_xlat_uri_unescape(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
xlat function to unescape URI encoded strings
Definition: xlat.c:75
xlat_arg_parser_t const fr_curl_xlat_safe_args[]
Definition: xlat.c:37
#define CURL_URI_SAFE_FOR
safe for value suitable for all users of the curl library
Definition: xlat.h:36
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define fr_dict_autofree(_to_free)
Definition: dict.h:850
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition: dict_util.c:3263
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition: dict_util.c:4090
#define fr_dict_autoload(_to_load)
Definition: dict.h:847
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
char const * name
Name of library and section within global config.
Definition: global_lib.h:39
Structure to define how to initialise libraries with global configuration.
Definition: global_lib.h:38
static fr_table_num_sorted_t const fr_curl_sslcode_table[]
Definition: base.c:50
static fr_dict_t const * dict_freeradius
Definition: base.c:37
int fr_curl_response_certinfo(request_t *request, fr_curl_io_request_t *randle)
Definition: base.c:172
static size_t fr_curl_sslcode_table_len
Definition: base.c:55
static int fr_curl_init(void)
Initialise global curl options.
Definition: base.c:299
static conf_parser_t reuse_curl_conn_config[]
Definition: base.c:92
int fr_curl_easy_tls_init(fr_curl_io_request_t *randle, fr_curl_tls_t const *conf)
Definition: base.c:141
static int tls_config_dflt_capath(CONF_PAIR **out, UNUSED void *parent, CONF_SECTION *cs, fr_token_t quote, conf_parser_t const *rule)
Definition: base.c:57
fr_dict_attr_t const * attr_tls_certificate
Attribute definitions for lib curl.
Definition: base.c:36
global_lib_autoinst_t fr_curl_autoinst
Definition: base.c:400
static void fr_curl_free(void)
Definition: base.c:383
conf_parser_t fr_curl_conn_config[]
Definition: base.c:99
fr_dict_attr_autoload_t curl_attr[]
Definition: base.c:40
CURL * fr_curl_tmp_handle(void)
Return a thread local curl easy handle.
Definition: base.c:280
conf_parser_t fr_curl_tls_config[]
Definition: base.c:68
static fr_dict_autoload_t curl_dict[]
Definition: base.c:45
static int _curl_tmpl_handle(void *arg)
Free the curl easy handle.
Definition: base.c:264
void log_request_pair_list(fr_log_lvl_t lvl, request_t *request, fr_pair_t const *parent, fr_pair_list_t const *vps, char const *prefix)
Print a fr_pair_list_t.
Definition: log.c:830
#define PERROR(_fmt,...)
Definition: log.h:228
#define RWDEBUG(fmt,...)
Definition: log.h:361
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RATE_LIMIT_GLOBAL(_log, _fmt,...)
Rate limit messages using a global limiting entry.
Definition: log.h:641
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
Definition: log.h:71
@ FR_TYPE_TLV
Contains nested attributes.
Definition: merged_model.c:118
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_VOID
User data.
Definition: merged_model.c:127
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:283
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1345
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, bool tainted)
Convert string value to native attribute value.
Definition: pair.c:2589
VQP attributes.
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define WARN(fmt,...)
Definition: radclient.h:47
#define INFO(fmt,...)
Definition: radict.c:54
static rs_t * conf
Definition: radsniff.c:53
Tuneable parameters for slabs.
Definition: slab.h:42
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_pair_t * vp
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:34
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:49
enum fr_token fr_token_t
@ T_BARE_WORD
Definition: token.h:120
@ T_OP_EQ
Definition: token.h:83
xlat_action_t xlat_transparent(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
Definition: pair_inline.c:125
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
Definition: pair_inline.c:182
static fr_slen_t parent
Definition: pair.h:851
static size_t char ** out
Definition: value.h:997
void xlat_func_flags_set(xlat_t *x, xlat_func_flags_t flags)
Specify flags that alter the xlat's behaviour.
Definition: xlat_func.c:402
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition: xlat_func.c:365
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
Definition: xlat_func.c:218
void xlat_func_unregister(char const *name)
Unregister an xlat function.
Definition: xlat_func.c:519
#define xlat_func_safe_for_set(_xlat, _escaped)
Set the escaped values for output boxes.
Definition: xlat_func.h:82
@ XLAT_FUNC_FLAG_PURE
Definition: xlat_func.h:38