The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_rest.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: 480345d8950c11d65bea5bbdf28e9d3487ed7635 $
19 * @file rlm_rest.c
20 * @brief Integrate FreeRADIUS with RESTfull APIs
21 *
22 * @copyright 2012-2019,2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24RCSID("$Id: 480345d8950c11d65bea5bbdf28e9d3487ed7635 $")
25
26#include <freeradius-devel/curl/base.h>
27#include <freeradius-devel/curl/xlat.h>
28#include <freeradius-devel/server/base.h>
29
30#include <freeradius-devel/util/atexit.h>
31#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/util/uri.h>
33#include <freeradius-devel/unlang/xlat_func.h>
34
35#include "rest.h"
36
37static int rest_uri_part_escape(fr_value_box_t *vb, void *uctx);
38static void *rest_uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx);
39
40static fr_uri_part_t const rest_uri_parts[] = {
41 { .name = "scheme", .safe_for = CURL_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":")), .part_adv = { [':'] = 1 }, .extra_skip = 2 },
42 { .name = "host", .safe_for = CURL_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":"), L("/")), .part_adv = { [':'] = 1, ['/'] = 2 }, .func = rest_uri_part_escape },
43 { .name = "port", .safe_for = CURL_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("/")), .part_adv = { ['/'] = 1 } },
44 { .name = "method", .safe_for = CURL_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }, .func = rest_uri_part_escape },
45 { .name = "param", .safe_for = CURL_URI_SAFE_FOR, .func = rest_uri_part_escape },
47};
48
50
51 { L("1.0"), CURL_HTTP_VERSION_1_0 }, //!< Enforce HTTP 1.0 requests.
52 { L("1.1"), CURL_HTTP_VERSION_1_1 }, //!< Enforce HTTP 1.1 requests.
53/*
54 * These are all enum values
55 */
56#if CURL_AT_LEAST_VERSION(7,49,0)
57 { L("2.0"), CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE }, //!< Enforce HTTP 2.0 requests.
58#endif
59#if CURL_AT_LEAST_VERSION(7,33,0)
60 { L("2.0+auto"), CURL_HTTP_VERSION_2_0 }, //!< Attempt HTTP 2 requests. libcurl will fall back
61 ///< to HTTP 1.1 if HTTP 2 can't be negotiated with the
62 ///< server. (Added in 7.33.0)
63#endif
64#if CURL_AT_LEAST_VERSION(7,47,0)
65 { L("2.0+tls"), CURL_HTTP_VERSION_2TLS }, //!< Attempt HTTP 2 over TLS (HTTPS) only.
66 ///< libcurl will fall back to HTTP 1.1 if HTTP 2
67 ///< can't be negotiated with the HTTPS server.
68 ///< For clear text HTTP servers, libcurl will use 1.1.
69#endif
70 { L("default"), CURL_HTTP_VERSION_NONE } //!< We don't care about what version the library uses.
71 ///< libcurl will use whatever it thinks fit.
72};
74
75/** Unique pointer used to determine if we should explicitly disable proxying
76 *
77 */
78char const *rest_no_proxy = "*";
79
80static int rest_proxy_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
81 CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
82{
83 static fr_table_num_sorted_t const disable_proxy_table[] = {
84 { L("no"), 1 },
85 { L("false"), 1 },
86 { L("none"), 1 }
87 };
88 static size_t disable_proxy_table_len = NUM_ELEMENTS(disable_proxy_table);
89 char const *value = cf_pair_value(cf_item_to_pair(ci));
90
91 if (fr_table_value_by_str(disable_proxy_table, value, 0) == 1) {
92 *((char const **)out) = rest_no_proxy;
93 } else {
94 *((char const **)out) = value;
95 }
96 return 0;
97}
98
99#define SECTION_REQUEST_COMMON \
100 { FR_CONF_OFFSET("body", rlm_rest_section_request_t, body_str), .dflt = "none" }, \
101 /* User authentication */ \
102 { FR_CONF_OFFSET_IS_SET("auth", FR_TYPE_VOID, 0, rlm_rest_section_request_t, auth), \
103 .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = http_auth_table, .len = &http_auth_table_len }, .dflt = "none" }, \
104 { FR_CONF_OFFSET("require_auth", rlm_rest_section_request_t, require_auth), .dflt = "no" }, \
105 { FR_CONF_OFFSET("chunk", rlm_rest_section_request_t, chunk), .dflt = "0" } \
106
113
115 { FR_CONF_OFFSET("force_to", rlm_rest_section_response_t, force_to_str) }, \
116 { FR_CONF_OFFSET_TYPE_FLAGS("max_body_in", FR_TYPE_SIZE, 0, rlm_rest_section_response_t, max_body_in), .dflt = "16k" },
118};
119
123
124 /* Transfer configuration */
125 { FR_CONF_OFFSET("timeout", rlm_rest_section_t, timeout), .dflt = "4.0" },
126
127 /* TLS Parameters */
130};
131
136
137static const conf_parser_t xlat_config[] = {
140
141 /* Transfer configuration */
142 { FR_CONF_OFFSET("timeout", rlm_rest_section_t, timeout), .dflt = "4.0" },
143
144 /* TLS Parameters */
147};
148
149static const conf_parser_t module_config[] = {
150 { FR_CONF_DEPRECATED("connect_timeout", rlm_rest_t, connect_timeout) },
151 { FR_CONF_OFFSET("connect_proxy", rlm_rest_t, connect_proxy), .func = rest_proxy_parse },
152 { FR_CONF_OFFSET("http_negotiation", rlm_rest_t, http_negotiation),
153 .func = cf_table_parse_int,
155 .dflt = "default" },
156
157 { FR_CONF_OFFSET_SUBSECTION("connection", 0, rlm_rest_t, conn_config, fr_curl_conn_config) },
158
159#ifdef CURLPIPE_MULTIPLEX
160 { FR_CONF_OFFSET("multiplex", rlm_rest_t, multiplex), .dflt = "yes" },
161#endif
162
163#ifndef NDEBUG
164 { FR_CONF_OFFSET("fail_header_decode", rlm_rest_t, fail_header_decode), .dflt = "no" },
165 { FR_CONF_OFFSET("fail_body_decode", rlm_rest_t, fail_body_decode), .dflt = "no" },
166#endif
167
169};
170
171#define REST_CALL_ENV_REQUEST_COMMON(_dflt_username, _dflt_password) \
172 { FR_CALL_ENV_OFFSET("header", FR_TYPE_STRING, CALL_ENV_FLAG_MULTI, rlm_rest_call_env_t, request.header) }, \
173 { FR_CALL_ENV_OFFSET("data", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT, rlm_rest_call_env_t, request.data) }, \
174 { FR_CALL_ENV_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_SINGLE | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE, \
175 rlm_rest_call_env_t, request.username), .pair.dflt_quote = T_BARE_WORD, _dflt_username }, \
176 { FR_CALL_ENV_OFFSET("password", FR_TYPE_STRING, CALL_ENV_FLAG_SINGLE | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_SECRET | CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE, \
177 rlm_rest_call_env_t, request.password), .pair.dflt_quote = T_BARE_WORD, _dflt_password }, \
178
179#define REST_CALL_ENV_RESPONSE_COMMON \
180 { FR_CALL_ENV_PARSE_ONLY_OFFSET("header", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE, rlm_rest_call_env_t, response.header) }, \
181
182#define REST_CALL_ENV_SECTION(_var, _dflt_username, _dflt_password) \
183static const call_env_parser_t _var[] = { \
184 { FR_CALL_ENV_SUBSECTION("request", NULL, CALL_ENV_FLAG_REQUIRED, \
185 ((call_env_parser_t[]) { \
186 { FR_CALL_ENV_OFFSET("uri", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_CONCAT, rlm_rest_call_env_t, request.uri), \
187 .pair.escape = { \
188 .box_escape = { \
189 .func = fr_uri_escape, \
190 .safe_for = CURL_URI_SAFE_FOR, \
191 .always_escape = true, /* required! */ \
192 }, \
193 .mode = TMPL_ESCAPE_PRE_CONCAT, \
194 .uctx = { \
195 .func = { \
196 .alloc = rest_uri_part_escape_uctx_alloc, \
197 .uctx = rest_uri_parts \
198 }, \
199 .type = TMPL_ESCAPE_UCTX_ALLOC_FUNC \
200 }, \
201 }, \
202 .pair.literals_safe_for = CURL_URI_SAFE_FOR}, /* Do not concat */ \
203 REST_CALL_ENV_REQUEST_COMMON(_dflt_username, _dflt_password) \
204 CALL_ENV_TERMINATOR \
205 })) }, \
206 { FR_CALL_ENV_SUBSECTION("response", NULL, CALL_ENV_FLAG_NONE, \
207 ((call_env_parser_t[]) { \
208 REST_CALL_ENV_RESPONSE_COMMON \
209 CALL_ENV_TERMINATOR \
210 })) }, \
211 CALL_ENV_TERMINATOR \
212};
213
214REST_CALL_ENV_SECTION(rest_section_common_env,,)
215REST_CALL_ENV_SECTION(rest_section_authenticate_env, .pair.dflt = "User-Name", .pair.dflt = "User-Password")
216
217/*
218 * xlat call env doesn't have the same set of config items as the other sections
219 * because some values come from the xlat call itself.
220 */
222 FR_CALL_ENV_METHOD_OUT(rlm_rest_call_env_t), \
223 .env = (call_env_parser_t[]){ \
225 ((call_env_parser_t[]) { \
226 { FR_CALL_ENV_SUBSECTION("request", NULL, CALL_ENV_FLAG_NONE, \
227 ((call_env_parser_t[]) { \
228 REST_CALL_ENV_REQUEST_COMMON(,) \
230 })) }, \
231 { FR_CALL_ENV_SUBSECTION("response", NULL, CALL_ENV_FLAG_NONE, \
232 ((call_env_parser_t[]) { \
233 REST_CALL_ENV_RESPONSE_COMMON \
234 CALL_ENV_TERMINATOR \
235 })) }, \
237 }) \
238 ) }, \
240 } \
241};
242
244
247 { .out = &dict_freeradius, .proto = "freeradius" },
249};
250
254
257 { .out = &attr_rest_http_body, .name = "REST-HTTP-Body", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
258 { .out = &attr_rest_http_header, .name = "REST-HTTP-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
259 { .out = &attr_rest_http_status_code, .name = "REST-HTTP-Status-Code", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
261};
262
263extern global_lib_autoinst_t const * const rlm_rest_lib[];
268
269static int8_t rest_section_cmp(void const *one, void const *two)
270{
271 rlm_rest_section_conf_t const *a = one, *b = two;
272 return CMP(a->cs, b->cs);
273}
274
275/** Update the status attribute
276 *
277 * @param[in] request The current request.
278 * @param[in] handle rest handle.
279 * @return
280 * - 0 if status was updated successfully.
281 * - -1 if status was not updated successfully.
282 */
283static int rlm_rest_status_update(request_t *request, void *handle)
284{
285 int code;
286 fr_pair_t *vp;
287
288 RDEBUG2("Updating result attribute(s)");
289
290 RINDENT();
291 code = rest_get_handle_code(handle);
292 if (!code) {
294 RDEBUG2("request.REST-HTTP-Status-Code !* ANY");
295 REXDENT();
296 return -1;
297 }
298
299 RDEBUG2("request.REST-HTTP-Status-Code := %i", code);
300
302 vp->vp_uint32 = code;
303 REXDENT();
304
305 return 0;
306}
307
309{
310 return talloc_free(uctx);
311}
312
313/** Allocate an escape uctx to pass to fr_uri_escape
314 *
315 * @param[in] request UNUSED.
316 * @param[in] uctx pointer to the start of the uri_parts array.
317 * @return A new fr_uri_escape_ctx_t.
318 */
319static void *rest_uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
320{
321 static _Thread_local fr_uri_escape_ctx_t *t_ctx;
322
323 if (unlikely(t_ctx == NULL)) {
325
326 MEM(ctx = talloc_zero(NULL, fr_uri_escape_ctx_t));
328 } else {
329 memset(t_ctx, 0, sizeof(*t_ctx));
330 }
331 t_ctx->uri_part = uctx;
332 return t_ctx;
333}
334
335/** URL escape a single box forming part of a URL
336 *
337 * @param[in] vb to escape
338 * @param[in] uctx UNUSED context containing CURL handle
339 * @return
340 * - 0 on success
341 * - -1 on failure
342 */
343static int rest_uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
344{
345 char *escaped, *str;
346
347 escaped = curl_easy_escape(fr_curl_tmp_handle(), vb->vb_strvalue, vb->vb_length);
348 if (!escaped) return -1;
349
350 /*
351 * Returned string the same length - nothing changed
352 */
353 if (strlen(escaped) == vb->vb_length) {
354 curl_free(escaped);
355 return 0;
356 }
357
358 str = talloc_strdup(vb, escaped);
360
361 curl_free(escaped);
362
363 return 0;
364}
365
366static int rlm_rest_perform(module_ctx_t const *mctx,
367 rlm_rest_section_t const *section, fr_curl_io_request_t *randle,
368 request_t *request)
369{
370 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
371 rlm_rest_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
372 int ret;
373
374 RDEBUG2("Sending HTTP %s to \"%pV\"",
375 fr_table_str_by_value(http_method_table, section->request.method, NULL), call_env->request.uri);
376
377 /*
378 * Configure various CURL options, and initialise the read/write
379 * context data.
380 */
381 ret = rest_request_config(mctx, section, request, randle, section->request.method, section->request.body,
382 call_env->request.uri->vb_strvalue,
383 call_env->request.data ? call_env->request.data->vb_strvalue : NULL);
384 if (ret < 0) return -1;
385
386 /*
387 * Send the CURL request, pre-parse headers, aggregate incoming
388 * HTTP body data into a single contiguous buffer.
389 */
390 ret = fr_curl_io_request_enqueue(t->mhandle, request, randle);
391 if (ret < 0) return -1;
392
393 return 0;
394}
395
397 xlat_ctx_t const *xctx,
398 request_t *request, UNUSED fr_value_box_list_t *in)
399{
400 rlm_rest_xlat_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, rlm_rest_xlat_rctx_t);
401 int hcode;
402 ssize_t len;
403 char const *body;
405
406 fr_curl_io_request_t *handle = talloc_get_type_abort(rctx->handle, fr_curl_io_request_t);
407 rlm_rest_section_t *section = &rctx->section;
408
409 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
410
411 if (rlm_rest_status_update(request, handle) < 0) {
412 xa = XLAT_ACTION_FAIL;
413 goto finish;
414 }
415
416 hcode = rest_get_handle_code(handle);
417 switch (hcode) {
418 case 404:
419 case 410:
420 case 403:
421 case 401:
422 {
423 fr_pair_t *vp;
424 xa = XLAT_ACTION_FAIL;
425error:
426 rest_response_error(request, handle);
427
428 /*
429 * When the HTTP status code is a failure, put the
430 * response body in REST-HTTP-Body.
431 */
432 len = rest_get_handle_data(&body, handle);
433 if (len == 0) goto finish;
435 fr_pair_value_bstrndup(vp, body, len, true);
436 goto finish;
437 }
438 case 204:
439 goto finish;
440
441 default:
442 /*
443 * Attempt to parse content if there was any.
444 */
445 if ((hcode >= 200) && (hcode < 300)) {
446 break;
447 } else if (hcode < 500) {
448 xa = XLAT_ACTION_FAIL;
449 goto error;
450 } else {
451 xa = XLAT_ACTION_FAIL;
452 goto error;
453 }
454 }
455
456 /*
457 * Output the xlat data if the HTTP status code is one of the "success" ones.
458 *
459 * The user can check REST-HTTP-Status-Code to figure out what happened.
460 *
461 * Eventually we should just emit two boxes, one with the response code
462 * and one with the body.
463 */
464 len = rest_get_handle_data(&body, handle);
465 if (len > 0) {
466 fr_value_box_t *vb;
467
468 MEM(vb = fr_value_box_alloc_null(ctx));
469 fr_value_box_bstrndup(vb, vb, NULL, body, len, true);
471 }
472finish:
473
474 rest_slab_release(handle);
475
476 talloc_free(rctx);
477
478 return xa;
479}
480
482 { .required = true, .single = true, .type = FR_TYPE_STRING }, /* HTTP Method */
483 { .required = true, .safe_for = CURL_URI_SAFE_FOR, .type = FR_TYPE_STRING, .will_escape = true }, /* URL */
484 { .concat = true, .type = FR_TYPE_STRING }, /* Data */
485 { .type = FR_TYPE_STRING }, /* Headers */
487};
488
489/** Simple xlat to read text data from a URL
490 *
491 * Example:
492@verbatim
493%rest(POST, http://example.com/, "{ \"key\": \"value\" }", [<headers>])
494@endverbatim
495 *
496 * @ingroup xlat_functions
497 */
499 xlat_ctx_t const *xctx, request_t *request,
500 fr_value_box_list_t *in)
501{
503 rlm_rest_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, rlm_rest_thread_t);
504
505 fr_curl_io_request_t *randle = NULL;
506 int ret;
507 http_method_t method;
508
509 fr_value_box_t *method_vb;
510 fr_value_box_t *uri_vb;
511 fr_value_box_t *data_vb;
512 fr_value_box_t *header_vb;
513
514 /* There are no configurable parameters other than the URI */
516 rlm_rest_section_t *section;
517
518 XLAT_ARGS(in, &method_vb, &uri_vb, &data_vb, &header_vb);
519
520 MEM(rctx = talloc(request, rlm_rest_xlat_rctx_t));
521 section = &rctx->section;
522
523 /*
524 * Section gets modified, so we need our own copy.
525 */
526 memcpy(&rctx->section, &inst->xlat, sizeof(*section));
527
528 /*
529 * Set the HTTP verb
530 */
531 method = fr_table_value_by_substr(http_method_table, method_vb->vb_strvalue, -1, REST_HTTP_METHOD_UNKNOWN);
532 if (method != REST_HTTP_METHOD_UNKNOWN) {
533 section->request.method = method;
534 /*
535 * If the method is unknown, it's a custom verb
536 */
537 } else {
539 MEM(section->request.method_str = talloc_bstrndup(rctx, method_vb->vb_strvalue, method_vb->vb_length));
540 }
541
542 /*
543 * Handle URI component escaping
544 */
545 if (fr_uri_escape_list(&uri_vb->vb_group, rest_uri_parts, NULL) < 0) {
546 RPEDEBUG("Failed escaping URI");
547 error:
548 talloc_free(section);
549 return XLAT_ACTION_FAIL;
550 }
551
552 /*
553 * Smush all the URI components together
554 */
556 uri_vb, &uri_vb->vb_group, FR_TYPE_STRING,
558 SIZE_MAX) < 0) {
559 REDEBUG("Concatenating URI");
560 goto error;
561 }
562
563 /*
564 * We get a connection from the pool here as the CURL object
565 * is needed to use curl_easy_escape() for escaping
566 */
567 randle = rctx->handle = rest_slab_reserve(t->slab);
568 if (!randle) return XLAT_ACTION_FAIL;
569
570 randle->request = request; /* Populate the request pointer for escape callbacks */
571 if (data_vb) section->request.body = REST_HTTP_BODY_CUSTOM;
572
573 RDEBUG2("Sending HTTP %s to \"%pV\"",
576 uri_vb);
577
578 if (header_vb) {
579 fr_value_box_list_foreach(&header_vb->vb_group, header) {
580 if (unlikely(rest_request_config_add_header(request, randle, header->vb_strvalue, true) < 0)) {
581 error_release:
582 rest_slab_release(randle);
583 goto error;
584 }
585 }
586 }
587
588 /*
589 * Configure various CURL options, and initialise the read/write
590 * context data.
591 *
592 * @todo We could extract the User-Name and password from the URL string.
593 */
594 ret = rest_request_config(MODULE_CTX(xctx->mctx->mi, t, xctx->env_data, NULL),
595 section, request, randle, section->request.method,
596 section->request.body,
597 uri_vb->vb_strvalue, data_vb ? data_vb->vb_strvalue : NULL);
598 if (ret < 0) goto error_release;
599
600 /*
601 * Send the CURL request, pre-parse headers, aggregate incoming
602 * HTTP body data into a single contiguous buffer.
603 *
604 * @fixme need to pass in thread to all xlat functions
605 */
606 ret = fr_curl_io_request_enqueue(t->mhandle, request, randle);
607 if (ret < 0) goto error_release;
608
610}
611
613{
615 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
616 rlm_rest_section_t const *section = &env->section->section;
617 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
618
619 int hcode;
621 int ret;
622
623 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
624
625 if (rlm_rest_status_update(request, handle) < 0) {
626 rcode = RLM_MODULE_FAIL;
627 goto finish;
628 }
629
630 hcode = rest_get_handle_code(handle);
631 switch (hcode) {
632 case 404:
633 case 410:
634 rcode = RLM_MODULE_NOTFOUND;
635 break;
636
637 case 403:
638 rcode = RLM_MODULE_DISALLOW;
639 break;
640
641 case 401:
642 /*
643 * Attempt to parse content if there was any.
644 */
645 ret = rest_response_decode(inst, section, request, handle);
646 if (ret < 0) {
647 rcode = RLM_MODULE_FAIL;
648 break;
649 }
650
651 rcode = RLM_MODULE_REJECT;
652 break;
653
654 case 204:
655 rcode = RLM_MODULE_OK;
656 break;
657
658 default:
659 /*
660 * Attempt to parse content if there was any.
661 */
662 if ((hcode >= 200) && (hcode < 300)) {
663 ret = rest_response_decode(inst, section, request, handle);
664 if (ret < 0) rcode = RLM_MODULE_FAIL;
665 else if (ret == 0) rcode = RLM_MODULE_OK;
666 else rcode = RLM_MODULE_UPDATED;
667 break;
668 } else if (hcode < 500) {
669 rcode = RLM_MODULE_INVALID;
670 } else {
671 rcode = RLM_MODULE_FAIL;
672 }
673 }
674
675 switch (rcode) {
677 case RLM_MODULE_FAIL:
679 rest_response_error(request, handle);
680 break;
681
682 default:
683 rest_response_debug(request, handle);
684 break;
685 }
686
687finish:
688 rest_slab_release(handle);
689
690 RETURN_UNLANG_RCODE(rcode);
691}
692
693/*
694 * Find the named user in this modules database. Create the set
695 * of attribute-value pairs to check and reply with for this user
696 * from the database. The authentication code only needs to check
697 * the password, the rest is done here.
698 */
699static unlang_action_t CC_HINT(nonnull) mod_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
700{
701 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
702 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
703 rlm_rest_section_t const *section = &env->section->section;
704
705 void *handle;
706 int ret;
707
708 handle = rest_slab_reserve(t->slab);
709 if (!handle) RETURN_UNLANG_FAIL;
710
711 ret = rlm_rest_perform(mctx, section, handle, request);
712 if (ret < 0) {
713 rest_slab_release(handle);
714
716 }
717
719}
720
722 module_ctx_t const *mctx, request_t *request)
723{
725 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
726 rlm_rest_section_t const *section = &env->section->section;
727 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
728
729 int hcode;
730 int rcode = RLM_MODULE_OK;
731 int ret;
732
733 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
734
735 if (rlm_rest_status_update(request, handle) < 0) {
736 rcode = RLM_MODULE_FAIL;
737 goto finish;
738 }
739
740 hcode = rest_get_handle_code(handle);
741 switch (hcode) {
742 case 404:
743 case 410:
744 rcode = RLM_MODULE_NOTFOUND;
745 break;
746
747 case 403:
748 rcode = RLM_MODULE_DISALLOW;
749 break;
750
751 case 401:
752 /*
753 * Attempt to parse content if there was any.
754 */
755 ret = rest_response_decode(inst, section, request, handle);
756 if (ret < 0) {
757 rcode = RLM_MODULE_FAIL;
758 break;
759 }
760
761 rcode = RLM_MODULE_REJECT;
762 break;
763
764 case 204:
765 rcode = RLM_MODULE_OK;
766 break;
767
768 default:
769 /*
770 * Attempt to parse content if there was any.
771 */
772 if ((hcode >= 200) && (hcode < 300)) {
773 ret = rest_response_decode(inst, section, request, handle);
774 if (ret < 0) rcode = RLM_MODULE_FAIL;
775 else if (ret == 0) rcode = RLM_MODULE_OK;
776 else rcode = RLM_MODULE_UPDATED;
777 break;
778 } else if (hcode < 500) {
779 rcode = RLM_MODULE_INVALID;
780 } else {
781 rcode = RLM_MODULE_FAIL;
782 }
783 }
784
785 switch (rcode) {
787 case RLM_MODULE_FAIL:
789 rest_response_error(request, handle);
790 break;
791
792 default:
793 rest_response_debug(request, handle);
794 break;
795 }
796
797finish:
798 rest_slab_release(handle);
799
800 RETURN_UNLANG_RCODE(rcode);
801}
802
803/*
804 * Authenticate the user with the given password.
805 */
806static unlang_action_t CC_HINT(nonnull) mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
807{
808 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
809 rlm_rest_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
810 rlm_rest_section_t const *section = &call_env->section->section;
811 fr_curl_io_request_t *handle;
812 int ret;
813
814 /*
815 * We can only authenticate user requests which HAVE
816 * a User-Name attribute.
817 */
818 if (!call_env->request.username) {
819 REDEBUG("Attribute \"User-Name\" is required for authentication");
821 }
822
823 if (!call_env->request.password) {
824 REDEBUG("Attribute \"User-Password\" is required for authentication");
826 }
827
828 /*
829 * Make sure the supplied password isn't empty
830 */
831 if (call_env->request.password->vb_length == 0) {
832 REDEBUG("User-Password must not be empty");
834 }
835
836 /*
837 * Log the password
838 */
839 if (RDEBUG_ENABLED3) {
840 RDEBUG("Login attempt with password \"%pV\"", call_env->request.password);
841 } else {
842 RDEBUG2("Login attempt with password");
843 }
844
845 handle = rest_slab_reserve(t->slab);
846 if (!handle) RETURN_UNLANG_FAIL;
847
848 ret = rlm_rest_perform(mctx, section, handle, request);
849 if (ret < 0) {
850 rest_slab_release(handle);
851
853 }
854
856}
857
859{
861 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
862 rlm_rest_section_t const *section = &env->section->section;
863 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
864
865 int hcode;
866 int rcode = RLM_MODULE_OK;
867 int ret;
868
869 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
870
871 if (rlm_rest_status_update(request, handle) < 0) {
872 rcode = RLM_MODULE_FAIL;
873 goto finish;
874 }
875
876 hcode = rest_get_handle_code(handle);
877 if (hcode >= 500) {
878 rcode = RLM_MODULE_FAIL;
879 } else if (hcode == 204) {
880 rcode = RLM_MODULE_OK;
881 } else if ((hcode >= 200) && (hcode < 300)) {
882 ret = rest_response_decode(inst, section, request, handle);
883 if (ret < 0) rcode = RLM_MODULE_FAIL;
884 else if (ret == 0) rcode = RLM_MODULE_OK;
885 else rcode = RLM_MODULE_UPDATED;
886 } else {
887 rcode = RLM_MODULE_INVALID;
888 }
889
890 switch (rcode) {
892 case RLM_MODULE_FAIL:
893 rest_response_error(request, handle);
894 break;
895
896 default:
897 rest_response_debug(request, handle);
898 break;
899 }
900
901finish:
902 rest_slab_release(handle);
903
904 RETURN_UNLANG_RCODE(rcode);
905}
906
907/*
908 * Send accounting info to a REST API endpoint
909 */
910static unlang_action_t CC_HINT(nonnull) mod_accounting(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
911{
912 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
913 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
914 rlm_rest_section_t const *section = &env->section->section;
915 void *handle;
916 int ret;
917
918 handle = rest_slab_reserve(t->slab);
919 if (!handle) RETURN_UNLANG_FAIL;
920
921 ret = rlm_rest_perform(mctx, section, handle, request);
922 if (ret < 0) {
923 rest_slab_release(handle);
924
926 }
927
929}
930
932 rlm_rest_section_t *config, char const *name, CONF_SECTION *cs)
933{
934 CONF_SECTION *request_cs;
935
936 if (!cs) cs = cf_section_find(parent, name, NULL);
937 if (!cs) {
938 config->name = NULL;
939 return 0;
940 }
941
942 if (cf_section_rules_push(cs, config_items) < 0) return -1;
943 if (cf_section_parse(inst, config, cs) < 0) {
944 config->name = NULL;
945 return -1;
946 }
947
948 /*
949 * Add section name (Maybe add to headers later?).
950 */
951 config->name = name;
952
953 /*
954 * Convert HTTP method auth and body type strings into their integer equivalents.
955 */
956 if ((config->request.auth != REST_HTTP_AUTH_NONE) && !http_curl_auth[config->request.auth]) {
957 cf_log_err(cs, "Unsupported HTTP auth type \"%s\", check libcurl version, OpenSSL build "
958 "configuration, then recompile this module",
959 fr_table_str_by_value(http_auth_table, config->request.auth, "<INVALID>"));
960
961 return -1;
962 }
963 config->request.method = fr_table_value_by_str(http_method_table, config->request.method_str, REST_HTTP_METHOD_CUSTOM);
964
965 /*
966 * Custom hackery to figure out if data was set we can't do it any other way because we can't
967 * parse the tmpl_t except within a call_env.
968 *
969 * We have custom body data so we set REST_HTTP_BODY_CUSTOM, but also need to try and
970 * figure out what content-type to use. So if they've used the canonical form we
971 * need to convert it back into a proper HTTP content_type value.
972 */
973 if ((strcmp(name, "xlat") == 0) || ((request_cs = cf_section_find(cs, "request", NULL)) && cf_pair_find(request_cs, "data"))) {
974 http_body_type_t body;
975
976 config->request.body = REST_HTTP_BODY_CUSTOM;
977
979 if (body != REST_HTTP_BODY_UNKNOWN) {
980 config->request.body_str = fr_table_str_by_value(http_content_type_table, body, config->request.body_str);
981 }
982 /*
983 * We don't have any custom user data, so we need to select the right encoder based
984 * on the body type.
985 *
986 * To make this slightly more/less confusing, we accept both canonical body_types,
987 * and content_types.
988 */
989 } else {
991 if (config->request.body == REST_HTTP_BODY_UNKNOWN) {
993 }
994
995 if (config->request.body == REST_HTTP_BODY_UNKNOWN) {
996 cf_log_err(cs, "Unknown HTTP body type '%s'", config->request.body_str);
997 return -1;
998 }
999
1000 switch (http_body_type_supported[config->request.body]) {
1002 cf_log_err(cs, "Unsupported HTTP body type \"%s\", please submit patches",
1003 config->request.body_str);
1004 return -1;
1005
1007 cf_log_err(cs, "Invalid HTTP body type. \"%s\" is not a valid web API data "
1008 "markup format", config->request.body_str);
1009 return -1;
1010
1012 cf_log_err(cs, "Unavailable HTTP body type. \"%s\" is not available in this "
1013 "build", config->request.body_str);
1014 return -1;
1015
1016 default:
1017 break;
1018 }
1019 }
1020
1021 if (config->response.force_to_str) {
1022 config->response.force_to = fr_table_value_by_str(http_body_type_table, config->response.force_to_str, REST_HTTP_BODY_UNKNOWN);
1023 if (config->response.force_to == REST_HTTP_BODY_UNKNOWN) {
1024 config->response.force_to = fr_table_value_by_str(http_content_type_table, config->response.force_to_str, REST_HTTP_BODY_UNKNOWN);
1025 }
1026
1027 if (config->response.force_to == REST_HTTP_BODY_UNKNOWN) {
1028 cf_log_err(cs, "Unknown forced response body type '%s'", config->response.force_to_str);
1029 return -1;
1030 }
1031
1032 switch (http_body_type_supported[config->response.force_to]) {
1034 cf_log_err(cs, "Unsupported forced response body type \"%s\", please submit patches",
1035 config->response.force_to_str);
1036 return -1;
1037
1039 cf_log_err(cs, "Invalid HTTP forced response body type. \"%s\" is not a valid web API data "
1040 "markup format", config->response.force_to_str);
1041 return -1;
1042
1043 default:
1044 break;
1045 }
1046 }
1047
1048 return 0;
1049}
1050
1051/** Cleans up after a REST request.
1052 *
1053 * Resets all options associated with a CURL handle, and frees any headers
1054 * associated with it.
1055 *
1056 * @param[in] randle to cleanup.
1057 * @param[in] uctx unused.
1058 */
1060{
1061 rlm_rest_curl_context_t *ctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
1062 CURL *candle = randle->candle;
1063
1064 /*
1065 * Clear any previously configured options
1066 */
1067 curl_easy_reset(candle);
1068
1069 /*
1070 * Free header list
1071 */
1072 if (ctx->headers != NULL) {
1073 curl_slist_free_all(ctx->headers);
1074 ctx->headers = NULL;
1075 }
1076
1077#ifndef NDEBUG
1078 {
1079 CURLcode ret;
1080 /*
1081 * With curl 7.61 when a request in cancelled we get a result
1082 * with a NULL (invalid) pointer to private data. This lets
1083 * us know that the request was returned to the slab.
1084 */
1085 ret = curl_easy_setopt(candle, CURLOPT_PRIVATE, (void *)0xdeadc341);
1086 if (unlikely(ret != CURLE_OK)) {
1087 ERROR("Failed to set private data on curl easy handle %p: %s",
1088 candle, curl_easy_strerror(ret));
1089 }
1090 }
1091#endif
1092
1093 /*
1094 * Free response data
1095 */
1096 TALLOC_FREE(ctx->body);
1097 TALLOC_FREE(ctx->response.buffer);
1098 TALLOC_FREE(ctx->request.encoder);
1099 TALLOC_FREE(ctx->response.decoder);
1100 ctx->response.header = NULL; /* This is owned by the parsed call env and must not be freed */
1101
1102 randle->request = NULL;
1103 return 0;
1104}
1105
1107{
1108 curl_easy_cleanup(randle->candle);
1109 return 0;
1110}
1111
1112static int rest_conn_alloc(fr_curl_io_request_t *randle, void *uctx)
1113{
1114 rlm_rest_t const *inst = talloc_get_type_abort(uctx, rlm_rest_t);
1115 rlm_rest_curl_context_t *curl_ctx = NULL;
1116
1117 randle->candle = curl_easy_init();
1118 if (unlikely(!randle->candle)) {
1119 fr_strerror_printf("Unable to initialise CURL handle");
1120 return -1;
1121 }
1122
1123 MEM(curl_ctx = talloc_zero(randle, rlm_rest_curl_context_t));
1124 curl_ctx->headers = NULL;
1125 curl_ctx->request.instance = inst;
1126 curl_ctx->response.instance = inst;
1127
1128 randle->uctx = curl_ctx;
1129 talloc_set_destructor(randle, _mod_conn_free);
1130
1131 rest_slab_element_set_destructor(randle, _rest_request_cleanup, NULL);
1132
1133 return 0;
1134}
1135
1136/** Create a thread specific multihandle
1137 *
1138 * Easy handles representing requests are added to the curl multihandle
1139 * with the multihandle used for mux/demux.
1140 *
1141 * @param[in] mctx Thread instantiation data.
1142 * @return
1143 * - 0 on success.
1144 * - -1 on failure.
1145 */
1147{
1148 rlm_rest_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1149 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
1150 fr_curl_handle_t *mhandle;
1151
1152 t->inst = inst;
1153
1154 if (!(t->slab = rest_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
1155 rest_conn_alloc, NULL, inst, false, false))) {
1156 ERROR("Connection handle pool instantiation failed");
1157 return -1;
1158 }
1159
1160 mhandle = fr_curl_io_init(t, mctx->el, inst->multiplex);
1161 if (!mhandle) return -1;
1162
1163 t->mhandle = mhandle;
1164
1165 return 0;
1166}
1167
1168/** Cleanup all outstanding requests associated with this thread
1169 *
1170 * Destroys all curl easy handles, and then the multihandle associated
1171 * with this thread.
1172 *
1173 * @param[in] mctx data to destroy.
1174 * @return 0
1175 */
1177{
1178 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
1179
1180 talloc_free(t->mhandle); /* Ensure this is shutdown before the pool */
1181 talloc_free(t->slab);
1182
1183 return 0;
1184}
1185
1186/*
1187 * Do any per-module initialization that is separate to each
1188 * configured instance of the module. e.g. set up connections
1189 * to external databases, read configuration files, set up
1190 * dictionary entries, etc.
1191 *
1192 * If configuration information is given in the config section
1193 * that must be referenced in later calls, store a handle to it
1194 * in *instance otherwise put a null pointer there.
1195 */
1196static int mod_instantiate(module_inst_ctx_t const *mctx)
1197{
1198 rlm_rest_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1199 CONF_SECTION *conf = mctx->mi->conf;
1200 rlm_rest_section_conf_t *section;
1202
1203 inst->xlat.request.method_str = "GET";
1204 inst->xlat.request.body = REST_HTTP_BODY_NONE;
1205 inst->xlat.request.body_str = "application/x-www-form-urlencoded";
1206 inst->xlat.response.accept_all = true;
1207
1208 if (!inst->sections_init) fr_rb_inline_init(&inst->sections, rlm_rest_section_conf_t, node, rest_section_cmp, NULL);
1209
1210 /*
1211 * Parse xlat config.
1212 */
1213 if ((parse_sub_section(inst, conf, xlat_config, &inst->xlat, "xlat", NULL) < 0)) return -1;
1214
1215 /*
1216 * Parse section configs from calls found by the call_env parser.
1217 */
1218 for (section = fr_rb_iter_init_inorder(&inst->sections, &iter);
1219 section != NULL;
1220 section = fr_rb_iter_next_inorder(&inst->sections, &iter)) {
1222 cf_section_name(section->cs), section->cs) < 0) return -1;
1223 }
1224
1225 inst->conn_config.reuse.num_children = 1;
1226 inst->conn_config.reuse.child_pool_size = sizeof(rlm_rest_curl_context_t);
1227
1228 return 0;
1229}
1230
1231static int mod_bootstrap(module_inst_ctx_t const *mctx)
1232{
1233 xlat_t *xlat;
1234
1235 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, rest_xlat, FR_TYPE_STRING);
1238
1239 return 0;
1240}
1241
1242static int mod_load(void)
1243{
1244 /* developer sanity */
1246
1247#ifdef HAVE_JSON
1249#endif
1250
1251 return 0;
1252}
1253
1254/*
1255 * Custom call_env parser which looks for a conf section matching the name
1256 * of the section the module is called in and then hands off to the normal
1257 * parsing.
1258 */
1259static int rest_sect_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, UNUSED tmpl_rules_t const *t_rules,
1260 CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1261{
1262 rlm_rest_t *inst = talloc_get_type_abort(cec->mi->data, rlm_rest_t);
1263 CONF_SECTION *cs;
1264 CONF_SECTION *sect = NULL;
1265 call_env_parsed_t *parsed;
1266 void *found;
1267 rlm_rest_section_conf_t *section;
1268 char *p, *name2 = NULL;
1269 size_t i;
1270
1271 /*
1272 * The parent section is the main module conf section
1273 * in which we'll look for a suitable section to parse.
1274 */
1275 cs = cf_item_to_section(cf_parent(ci));
1276
1277 if (cec->asked->name2) {
1278 name2 = talloc_strdup(NULL, cec->asked->name2);
1279 p = name2;
1280 for (i = 0; i < talloc_array_length(name2); i++) {
1281 *p = tolower(*p);
1282 p++;
1283 }
1284 sect = cf_section_find(cs, cec->asked->name1, name2);
1285 }
1286
1287 if (!sect) {
1288 sect = cf_section_find(cs, cec->asked->name1, NULL);
1289 }
1290
1291 if (!inst->sections_init) {
1293 inst->sections_init = true;
1294 }
1295
1296 if (!sect) {
1297 cf_log_err(cs, "%s called in %s %s - requires conf section %s %s%s%s", cec->mi->name,
1298 cec->asked->name1, cec->asked->name2 ? cec->asked->name2 : "",
1299 cec->asked->name1, cec->asked->name2 ? name2 : "",
1300 cec->asked->name2 ? " or " : "",
1301 cec->asked->name2 ? cec->asked->name1 : "");
1302 talloc_free(name2);
1303 return -1;
1304 }
1305 talloc_free(name2);
1306
1307 /*
1308 * "authenticate" sections use a different rules with defaults set for username and password
1309 */
1310 if (strcmp(cec->asked->name1, "authenticate") == 0) {
1311 call_env_parse(ctx, out, cec->mi->name, t_rules, sect, cec, rest_section_authenticate_env);
1312 } else {
1313 call_env_parse(ctx, out, cec->mi->name, t_rules, sect, cec, rest_section_common_env);
1314 }
1315 parsed = call_env_parsed_add(ctx, out,
1317 .name = "section",
1318 .flags = CALL_ENV_FLAG_PARSE_ONLY,
1319 .pair = {
1320 .parsed = {
1321 .offset = offsetof(rlm_rest_call_env_t, section),
1323 }
1324 }
1325 });
1326
1327 MEM(section = talloc_zero(inst, rlm_rest_section_conf_t));
1328 section->cs = sect;
1329 if (fr_rb_find_or_insert(&found, &inst->sections, section) < 0) {
1330 talloc_free(section);
1331 return -1;
1332 }
1333 if (found) {
1334 talloc_free(section);
1335 call_env_parsed_set_data(parsed, found);
1336 } else {
1337 call_env_parsed_set_data(parsed, section);
1338 }
1339 return 0;
1340}
1341
1349
1350/*
1351 * The module name should be the only globally exported symbol.
1352 * That is, everything else should be 'static'.
1353 *
1354 * If the module needs to temporarily modify it's instantiation
1355 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
1356 * The server will then take care of ensuring that the module
1357 * is single-threaded.
1358 */
1359extern module_rlm_t rlm_rest;
1361 .common = {
1362 .magic = MODULE_MAGIC_INIT,
1363 .name = "rest",
1364 .inst_size = sizeof(rlm_rest_t),
1365 .thread_inst_size = sizeof(rlm_rest_thread_t),
1366 .config = module_config,
1367 .onload = mod_load,
1368 .bootstrap = mod_bootstrap,
1369 .instantiate = mod_instantiate,
1370 .thread_instantiate = mod_thread_instantiate,
1371 .thread_detach = mod_thread_detach
1372 },
1373 .method_group = {
1374 .bindings = (module_method_binding_t[]){
1375 { .section = SECTION_NAME("recv", "Accounting-Request"), .method = mod_accounting, .method_env = &rest_method_env },
1376 { .section = SECTION_NAME("accounting", CF_IDENT_ANY), .method = mod_accounting, .method_env = &rest_method_env },
1377 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &rest_method_env },
1378 { .section = SECTION_NAME("send", CF_IDENT_ANY), .method = mod_accounting, .method_env = &rest_method_env },
1379 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_common, .method_env = &rest_method_env },
1381 }
1382 }
1383};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:220
#define RCSID(id)
Definition build.h:488
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:210
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:113
#define unlikely(_x)
Definition build.h:384
#define UNUSED
Definition build.h:318
#define NUM_ELEMENTS(_t)
Definition build.h:340
int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char const *name, tmpl_rules_t const *t_rules, CONF_SECTION const *cs, call_env_ctx_t const *cec, call_env_parser_t const *rule)
Parse per call env.
Definition call_env.c:461
call_env_parsed_t * call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs.
Definition call_env.c:690
void call_env_parsed_set_data(call_env_parsed_t *parsed, void const *data)
Assign data to a call_env_parsed_t.
Definition call_env.c:747
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition call_env.h:240
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:247
section_name_t const * asked
The actual name1/name2 that resolved to a module_method_binding_t.
Definition call_env.h:232
#define FR_CALL_ENV_SUBSECTION(_name, _name2, _flags, _subcs)
Specify a call_env_parser_t which defines a nested subsection.
Definition call_env.h:402
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition call_env.h:85
@ CALL_ENV_FLAG_NONE
Definition call_env.h:74
@ CALL_ENV_FLAG_PARSE_MISSING
If this subsection is missing, still parse it.
Definition call_env.h:88
@ CALL_ENV_PARSE_TYPE_VOID
Output of the parsing phase is undefined (a custom structure).
Definition call_env.h:62
module_instance_t const * mi
Module instance that the callenv is registered to.
Definition call_env.h:229
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
Definition call_env.h:412
Per method call config.
Definition call_env.h:180
int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs)
Parse a configuration section into user-supplied variables.
Definition cf_parse.c:1207
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:1635
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:657
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:611
#define FR_CONF_DEPRECATED(_name, _struct, _field)
conf_parser_t entry which raises an error if a matching CONF_PAIR is found
Definition cf_parse.h:409
#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:280
#define cf_section_rules_push(_cs, _rule)
Definition cf_parse.h:689
#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:309
#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:238
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:594
Common header for all CONF_* types.
Definition cf_priv.h:49
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1027
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition cf_util.c:1419
char const * cf_section_name(CONF_SECTION const *cs)
Return name2 if set, else name1.
Definition cf_util.c:1196
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1574
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:285
#define cf_parent(_cf)
Definition cf_util.h:98
#define CF_IDENT_ANY
Definition cf_util.h:75
fr_curl_handle_t * fr_curl_io_init(TALLOC_CTX *ctx, fr_event_list_t *el, bool multiplex)
bool extract_cert_attrs
Definition base.h:119
request_t * request
Current request.
Definition base.h:104
void * uctx
Private data for the module using the API.
Definition base.h:105
int fr_curl_io_request_enqueue(fr_curl_handle_t *mhandle, request_t *request, fr_curl_io_request_t *creq)
Sends a request using libcurl.
Definition io.c:478
CURL * candle
Request specific handle.
Definition base.h:102
Uctx data for timer and I/O functions.
Definition base.h:91
Structure representing an individual request being passed to curl for processing.
Definition base.h:101
#define CURL_URI_SAFE_FOR
safe for value suitable for all users of the curl library
Definition xlat.h:36
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition dcursor.h:435
#define MEM(x)
Definition debug.h:46
#define ERROR(fmt,...)
Definition dhcpclient.c:40
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
static fr_slen_t in
Definition dict.h:882
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
Test enumeration values.
Definition dict_test.h:92
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define GLOBAL_LIB_TERMINATOR
Definition global_lib.h:51
Structure to define how to initialise libraries with global configuration.
Definition global_lib.h:38
static xlat_action_t rest_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Simple xlat to read text data from a URL.
Definition rlm_rest.c:498
talloc_free(hp)
void fr_json_version_print(void)
Print JSON-C version.
Definition json.c:475
int fr_curl_response_certinfo(request_t *request, fr_curl_io_request_t *randle)
Definition base.c:170
global_lib_autoinst_t fr_curl_autoinst
Definition base.c:396
CURL * fr_curl_tmp_handle(void)
Return a thread local curl easy handle.
Definition base.c:276
conf_parser_t fr_curl_conn_config[]
Definition base.c:97
conf_parser_t fr_curl_tls_config[]
Definition base.c:68
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:455
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:347
#define RPEDEBUG(fmt,...)
Definition log.h:388
#define RINDENT()
Indent R* messages by one level.
Definition log.h:442
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
long int ssize_t
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * thread
Thread specific instance data.
Definition module_ctx.h:43
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition module_ctx.h:68
void * thread
Thread instance data.
Definition module_ctx.h:67
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:64
#define MODULE_CTX(_mi, _thread, _env_data, _rctx)
Wrapper to create a module_ctx_t as a compound literal.
Definition module_ctx.h:128
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition module_ctx.h:63
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition module_rlm.c:247
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
void rest_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Handle asynchronous cancellation of a request.
Definition io.c:33
void rest_io_xlat_signal(xlat_ctx_t const *xctx, request_t *request, fr_signal_t action)
Handle asynchronous cancellation of a request.
Definition io.c:56
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
Definition pair.c:2816
static const conf_parser_t config[]
Definition base.c:163
#define fr_assert(_expr)
Definition rad_assert.h:37
#define pair_update_request(_attr, _da)
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define RDEBUG(fmt,...)
static rs_t * conf
Definition radsniff.c:52
void * fr_rb_iter_init_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Initialise an in-order iterator.
Definition rb.c:824
int fr_rb_find_or_insert(void **found, fr_rb_tree_t *tree, void const *data)
Attempt to find current data in the tree, if it does not exist insert it.
Definition rb.c:598
void * fr_rb_iter_next_inorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:850
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
Definition rb.h:178
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:319
#define RETURN_UNLANG_INVALID
Definition rcode.h:66
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:61
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:52
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:53
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:55
fr_table_num_sorted_t const http_auth_table[]
Definition rest.c:158
int rest_request_config(module_ctx_t const *mctx, rlm_rest_section_t const *section, request_t *request, fr_curl_io_request_t *randle, http_method_t method, http_body_type_t type, char const *uri, char const *body_data)
Configures request curlopts.
Definition rest.c:1790
fr_table_num_sorted_t const http_body_type_table[]
Conversion table for type config values.
Definition rest.c:142
fr_table_num_sorted_t const http_method_table[]
Conversion table for method config values.
Definition rest.c:123
size_t rest_get_handle_data(char const **out, fr_curl_io_request_t *randle)
Extracts pointer to buffer containing response data.
Definition rest.c:1626
void rest_response_debug(request_t *request, fr_curl_io_request_t *handle)
Print out the response text.
Definition rest.c:1569
fr_table_num_sorted_t const http_content_type_table[]
Conversion table for "Content-Type" header values.
Definition rest.c:186
const unsigned long http_curl_auth[REST_HTTP_AUTH_NUM_ENTRIES]
Definition rest.c:97
const http_body_type_t http_body_type_supported[REST_HTTP_BODY_NUM_ENTRIES]
Table of encoder/decoder support.
Definition rest.c:47
int rest_request_config_add_header(request_t *request, fr_curl_io_request_t *randle, char const *header, bool validate)
Adds an additional header to a handle to use in the next reques.
Definition rest.c:1710
void rest_response_error(request_t *request, fr_curl_io_request_t *handle)
Print out the response text as error lines.
Definition rest.c:1544
int rest_response_decode(rlm_rest_t const *instance, rlm_rest_section_t const *section, request_t *request, fr_curl_io_request_t *randle)
Sends the response to the correct decode function.
Definition rest.c:2090
Function prototypes and datatypes for the REST (HTTP) transport.
rlm_rest_t const * instance
This instance of rlm_rest.
Definition rest.h:239
struct curl_slist * headers
Any HTTP headers which will be sent with the request.
Definition rest.h:263
tmpl_t * header
Where to create pairs representing HTTP response headers.
Definition rest.h:253
#define rest_get_handle_code(_handle)
Definition rest.h:331
char * buffer
Raw incoming HTTP data.
Definition rest.h:245
fr_curl_handle_t * mhandle
Thread specific multi handle.
Definition rest.h:193
char * body
Pointer to the buffer which contains body data/ Only used when not performing chunked encoding.
Definition rest.h:266
fr_curl_tls_t tls
Definition rest.h:147
http_method_t
Definition rest.h:43
@ REST_HTTP_METHOD_UNKNOWN
Definition rest.h:44
@ REST_HTTP_METHOD_CUSTOM
Must always come last, should not be in method table.
Definition rest.h:50
http_body_type_t
Definition rest.h:53
@ REST_HTTP_BODY_INVALID
Definition rest.h:57
@ REST_HTTP_BODY_UNSUPPORTED
Definition rest.h:55
@ REST_HTTP_BODY_CUSTOM
Definition rest.h:59
@ REST_HTTP_BODY_NUM_ENTRIES
Definition rest.h:67
@ REST_HTTP_BODY_UNKNOWN
Definition rest.h:54
@ REST_HTTP_BODY_NONE
Definition rest.h:58
@ REST_HTTP_BODY_UNAVAILABLE
Definition rest.h:56
char const * method_str
The string version of the HTTP method.
Definition rest.h:112
rlm_rest_section_t section
Parsed section config.
Definition rest.h:154
rlm_rest_section_conf_t * section
Section config.
Definition rest.h:282
rlm_rest_section_request_t request
Request configuration.
Definition rest.h:144
rlm_rest_response_t response
Response context data.
Definition rest.h:270
void * decoder
Decoder specific data.
Definition rest.h:256
struct rlm_rest_call_env_t::@189 request
void * encoder
Encoder specific data.
Definition rest.h:231
rlm_rest_request_t request
Request context data.
Definition rest.h:269
http_method_t method
What HTTP method should be used, GET, POST etc...
Definition rest.h:113
rlm_rest_section_t section
Our mutated section config.
Definition rest.h:277
rlm_rest_t const * inst
Instance of rlm_rest.
Definition rest.h:191
@ REST_HTTP_AUTH_NONE
Definition rest.h:72
rest_slab_list_t * slab
Slab list for connection handles.
Definition rest.h:192
http_body_type_t body
What encoding type should be used.
Definition rest.h:116
fr_curl_io_request_t * handle
curl easy handle servicing our request.
Definition rest.h:278
rlm_rest_t const * instance
This instance of rlm_rest.
Definition rest.h:221
CONF_SECTION * cs
Conf section found for this call.
Definition rest.h:155
Thread specific rlm_rest instance data.
Definition rest.h:190
Stores the state of a yielded xlat.
Definition rest.h:276
static char const * name
static const call_env_method_t rest_call_env_xlat
Definition rlm_rest.c:221
static int rlm_rest_status_update(request_t *request, void *handle)
Update the status attribute.
Definition rlm_rest.c:283
static int mod_load(void)
Definition rlm_rest.c:1242
static int8_t rest_section_cmp(void const *one, void const *two)
Definition rlm_rest.c:269
static int rest_conn_alloc(fr_curl_io_request_t *randle, void *uctx)
Definition rlm_rest.c:1112
static int _rest_uri_part_escape_uctx_free(void *uctx)
Definition rlm_rest.c:308
static const conf_parser_t xlat_config[]
Definition rlm_rest.c:137
static xlat_action_t rest_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_rest.c:396
static int parse_sub_section(rlm_rest_t *inst, CONF_SECTION *parent, conf_parser_t const *config_items, rlm_rest_section_t *config, char const *name, CONF_SECTION *cs)
Definition rlm_rest.c:931
static unlang_action_t mod_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:699
fr_dict_t const * dict_freeradius
Definition rlm_rest.c:243
static int rlm_rest_perform(module_ctx_t const *mctx, rlm_rest_section_t const *section, fr_curl_io_request_t *randle, request_t *request)
Definition rlm_rest.c:366
static const conf_parser_t xlat_request_config[]
Definition rlm_rest.c:132
static fr_table_num_sorted_t const http_negotiation_table[]
Definition rlm_rest.c:49
static fr_uri_part_t const rest_uri_parts[]
Definition rlm_rest.c:40
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1231
fr_dict_attr_t const * attr_rest_http_header
Definition rlm_rest.c:252
static unlang_action_t mod_accounting(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:910
fr_dict_autoload_t rlm_rest_dict[]
Definition rlm_rest.c:246
static const conf_parser_t section_request_config[]
Definition rlm_rest.c:107
static const conf_parser_t section_config[]
Definition rlm_rest.c:120
static int _rest_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
Cleans up after a REST request.
Definition rlm_rest.c:1059
#define SECTION_REQUEST_COMMON
Definition rlm_rest.c:99
static int rest_uri_part_escape(fr_value_box_t *vb, void *uctx)
static int rest_proxy_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
Definition rlm_rest.c:80
static const call_env_method_t rest_method_env
Definition rlm_rest.c:1342
fr_dict_attr_t const * attr_rest_http_status_code
Definition rlm_rest.c:253
static unlang_action_t mod_accounting_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:858
static size_t http_negotiation_table_len
Definition rlm_rest.c:73
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Create a thread specific multihandle.
Definition rlm_rest.c:1146
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:806
module_rlm_t rlm_rest
Definition rlm_rest.c:1360
fr_dict_attr_t const * attr_rest_http_body
Definition rlm_rest.c:251
fr_dict_attr_autoload_t rlm_rest_dict_attr[]
Definition rlm_rest.c:256
static const conf_parser_t section_response_config[]
Definition rlm_rest.c:114
#define REST_CALL_ENV_SECTION(_var, _dflt_username, _dflt_password)
Definition rlm_rest.c:182
static const conf_parser_t module_config[]
Definition rlm_rest.c:149
static int _mod_conn_free(fr_curl_io_request_t *randle)
Definition rlm_rest.c:1106
static xlat_arg_parser_t const rest_xlat_args[]
Definition rlm_rest.c:481
static unlang_action_t mod_authenticate_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:721
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Cleanup all outstanding requests associated with this thread.
Definition rlm_rest.c:1176
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1196
static unlang_action_t mod_common_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:612
static void * rest_uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
Allocate an escape uctx to pass to fr_uri_escape.
Definition rlm_rest.c:319
global_lib_autoinst_t const *const rlm_rest_lib[]
Definition rlm_rest.c:264
char const * rest_no_proxy
Unique pointer used to determine if we should explicitly disable proxying.
Definition rlm_rest.c:78
static int rest_sect_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, UNUSED tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
Definition rlm_rest.c:1259
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:190
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
char const * name2
Second section name. Usually a packet type like 'access-request', 'access-accept',...
Definition section.h:45
char const * name1
First section name. Usually a verb like 'recv', 'send', etc...
Definition section.h:44
char const * name
Instance name e.g. user_database.
Definition module.h:355
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:349
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:291
void * boot
Data allocated during the boostrap phase.
Definition module.h:294
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
#define pair_delete_request(_pair_or_da)
Delete a fr_pair_t in the request list.
Definition pair.h:172
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:431
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
#define fr_table_value_by_substr(_table, _name, _name_len, _def)
Convert a partial string to a value using an ordered or sorted table.
Definition table.h:693
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:617
#define talloc_get_type_abort_const
Definition talloc.h:110
#define talloc_strdup(_ctx, _str)
Definition talloc.h:142
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:136
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:543
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:383
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:146
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumend by an xlat function.
Definition xlat.h:145
int fr_uri_escape_list(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts, void *uctx)
Parse a list of value boxes representing a URI.
Definition uri.c:140
#define XLAT_URI_PART_TERMINATOR
Definition uri.h:66
char const * name
Name of this part of the URI.
Definition uri.h:47
uctx to pass to fr_uri_escape
Definition uri.h:60
Definition for a single part of a URI.
Definition uri.h:46
static fr_slen_t parent
Definition pair.h:858
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
void fr_value_box_strdup_shallow_replace(fr_value_box_t *vb, char const *src, ssize_t len)
Free the existing buffer (if talloced) associated with the valuebox, and replace it with a new one.
Definition value.c:4759
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4852
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
Definition value.c:6614
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:238
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:655
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:224
static size_t char ** out
Definition value.h:1030
void * rctx
Resume context.
Definition xlat_ctx.h:54
void * env_data
Expanded call env data.
Definition xlat_ctx.h:53
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition xlat_ctx.h:52
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:363
void xlat_func_call_env_set(xlat_t *x, call_env_method_t const *env_method)
Register call environment of an xlat.
Definition xlat_func.c:389