The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
rlm_rest.c
Go to the documentation of this file.
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 (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: 6bf734c16bda7ed096e4a7f76e2b267c32926087 $
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: 6bf734c16bda7ed096e4a7f76e2b267c32926087 $")
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
114/*
115 * Defaults for the JSON decoder. "do_xlat" defaults to "no", the
116 * REST API must opt in to expansion, either here or with the
117 * per-attribute "do_xlat" flag. Note that this differs from v3,
118 * where "do_xlat" defaulted to "yes".
119 */
121 { FR_CONF_OFFSET("do_xlat", rlm_rest_section_response_json_t, do_xlat), .dflt = "no" },
122 { FR_CONF_OFFSET("is_json", rlm_rest_section_response_json_t, is_json), .dflt = "no" },
124};
125
126/*
127 * Defaults for the POST decoder. As above, "do_xlat" defaults to
128 * "no".
129 */
131 { FR_CONF_OFFSET("do_xlat", rlm_rest_section_response_post_t, do_xlat), .dflt = "no" },
133};
134
142
146
147 /* Transfer configuration */
148 { FR_CONF_OFFSET("timeout", rlm_rest_section_t, timeout), .dflt = "4.0" },
149
150 /* TLS Parameters */
153};
154
159
160static const conf_parser_t xlat_config[] = {
163 { FR_CONF_OFFSET("binary", rlm_rest_section_t, binary) },
164
165 /* Transfer configuration */
166 { FR_CONF_OFFSET("timeout", rlm_rest_section_t, timeout), .dflt = "4.0" },
167
168 /* TLS Parameters */
171};
172
173static const conf_parser_t module_config[] = {
174 { FR_CONF_DEPRECATED("connect_timeout", rlm_rest_t, connect_timeout) },
175 { FR_CONF_OFFSET("connect_proxy", rlm_rest_t, connect_proxy), .func = rest_proxy_parse },
176 { FR_CONF_OFFSET("http_negotiation", rlm_rest_t, http_negotiation),
177 .func = cf_table_parse_int,
179 .dflt = "default" },
180
181 { FR_CONF_OFFSET_SUBSECTION("connection", 0, rlm_rest_t, conn_config, fr_curl_conn_config) },
182
183#ifdef CURLPIPE_MULTIPLEX
184 { FR_CONF_OFFSET("multiplex", rlm_rest_t, multiplex), .dflt = "yes" },
185#endif
186
187#ifndef NDEBUG
188 { FR_CONF_OFFSET("fail_header_decode", rlm_rest_t, fail_header_decode), .dflt = "no" },
189 { FR_CONF_OFFSET("fail_body_decode", rlm_rest_t, fail_body_decode), .dflt = "no" },
190#endif
191
193};
194
195#define REST_CALL_ENV_REQUEST_COMMON(_dflt_username, _dflt_password) \
196 { FR_CALL_ENV_OFFSET("header", FR_TYPE_STRING, CALL_ENV_FLAG_MULTI, rlm_rest_call_env_t, request.header) }, \
197 { FR_CALL_ENV_OFFSET("data", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT, rlm_rest_call_env_t, request.data) }, \
198 { FR_CALL_ENV_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_SINGLE | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE, \
199 rlm_rest_call_env_t, request.username), .pair.dflt_quote = T_BARE_WORD, _dflt_username }, \
200 { 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, \
201 rlm_rest_call_env_t, request.password), .pair.dflt_quote = T_BARE_WORD, _dflt_password }, \
202
203#define REST_CALL_ENV_RESPONSE_COMMON \
204 { FR_CALL_ENV_PARSE_ONLY_OFFSET("header", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE, rlm_rest_call_env_t, response.header) }, \
205
206#define REST_CALL_ENV_SECTION(_var, _dflt_username, _dflt_password) \
207static const call_env_parser_t _var[] = { \
208 { FR_CALL_ENV_SUBSECTION("request", NULL, CALL_ENV_FLAG_REQUIRED, \
209 ((call_env_parser_t[]) { \
210 { FR_CALL_ENV_OFFSET("uri", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_CONCAT, rlm_rest_call_env_t, request.uri), \
211 .pair.escape = { \
212 .box_escape = { \
213 .func = fr_uri_escape, \
214 .safe_for = CURL_URI_SAFE_FOR, \
215 .always_escape = true, /* required! */ \
216 }, \
217 .mode = TMPL_ESCAPE_PRE_CONCAT, \
218 .uctx = { \
219 .func = { \
220 .alloc = rest_uri_part_escape_uctx_alloc, \
221 .uctx = rest_uri_parts \
222 }, \
223 .type = TMPL_ESCAPE_UCTX_ALLOC_FUNC \
224 }, \
225 }, \
226 .pair.literals_safe_for = CURL_URI_SAFE_FOR}, /* Do not concat */ \
227 REST_CALL_ENV_REQUEST_COMMON(_dflt_username, _dflt_password) \
228 CALL_ENV_TERMINATOR \
229 })) }, \
230 { FR_CALL_ENV_SUBSECTION("response", NULL, CALL_ENV_FLAG_NONE, \
231 ((call_env_parser_t[]) { \
232 REST_CALL_ENV_RESPONSE_COMMON \
233 CALL_ENV_TERMINATOR \
234 })) }, \
235 CALL_ENV_TERMINATOR \
236};
237
238REST_CALL_ENV_SECTION(rest_section_common_env,,)
239REST_CALL_ENV_SECTION(rest_section_authenticate_env, .pair.dflt = "User-Name", .pair.dflt = "User-Password")
240
241/*
242 * xlat call env doesn't have the same set of config items as the other sections
243 * because some values come from the xlat call itself.
244 */
246 FR_CALL_ENV_METHOD_OUT(rlm_rest_call_env_t), \
247 .env = (call_env_parser_t[]){ \
249 ((call_env_parser_t[]) { \
250 { FR_CALL_ENV_SUBSECTION("request", NULL, CALL_ENV_FLAG_NONE, \
251 ((call_env_parser_t[]) { \
252 REST_CALL_ENV_REQUEST_COMMON(,) \
254 })) }, \
255 { FR_CALL_ENV_SUBSECTION("response", NULL, CALL_ENV_FLAG_NONE, \
256 ((call_env_parser_t[]) { \
257 REST_CALL_ENV_RESPONSE_COMMON \
258 CALL_ENV_TERMINATOR \
259 })) }, \
261 }) \
262 ) }, \
264 } \
265};
266
268
271 { .out = &dict_freeradius, .proto = "freeradius" },
273};
274
278
281 { .out = &attr_rest_http_body, .name = "REST-HTTP-Body", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
282 { .out = &attr_rest_http_header, .name = "REST-HTTP-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
283 { .out = &attr_rest_http_status_code, .name = "REST-HTTP-Status-Code", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
285};
286
287extern global_lib_autoinst_t const * const rlm_rest_lib[];
292
293static int8_t rest_section_cmp(void const *one, void const *two)
294{
295 rlm_rest_section_conf_t const *a = one, *b = two;
296 return CMP(a->cs, b->cs);
297}
298
299/** Update the status attribute
300 *
301 * @param[in] request The current request.
302 * @param[in] handle rest handle.
303 * @return
304 * - 0 if status was updated successfully.
305 * - -1 if status was not updated successfully.
306 */
307static int rlm_rest_status_update(request_t *request, void *handle)
308{
309 int code;
310 fr_pair_t *vp;
311
312 RDEBUG2("Updating result attribute(s)");
313
314 RINDENT();
315 code = rest_get_handle_code(handle);
316 if (!code) {
318 RDEBUG2("request.REST-HTTP-Status-Code !* ANY");
319 REXDENT();
320 return -1;
321 }
322
323 RDEBUG2("request.REST-HTTP-Status-Code := %i", code);
324
326 vp->vp_uint32 = code;
327 REXDENT();
328
329 return 0;
330}
331
333{
334 return talloc_free(uctx);
335}
336
337/** Allocate an escape uctx to pass to fr_uri_escape
338 *
339 * @param[in] request UNUSED.
340 * @param[in] uctx pointer to the start of the uri_parts array.
341 * @return A new fr_uri_escape_ctx_t.
342 */
343static void *rest_uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
344{
345 static _Thread_local fr_uri_escape_ctx_t *t_ctx;
346
347 if (unlikely(t_ctx == NULL)) {
349
350 MEM(ctx = talloc_zero(NULL, fr_uri_escape_ctx_t));
352 } else {
353 memset(t_ctx, 0, sizeof(*t_ctx));
354 }
355 t_ctx->uri_part = uctx;
356 return t_ctx;
357}
358
359/** URL escape a single box forming part of a URL
360 *
361 * @param[in] vb to escape
362 * @param[in] uctx UNUSED context containing CURL handle
363 * @return
364 * - 0 on success
365 * - -1 on failure
366 */
367static int rest_uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
368{
369 char *escaped, *str;
370
371 escaped = curl_easy_escape(fr_curl_tmp_handle(), vb->vb_strvalue, vb->vb_length);
372 if (!escaped) return -1;
373
374 /*
375 * Returned string the same length - nothing changed
376 */
377 if (strlen(escaped) == vb->vb_length) {
378 curl_free(escaped);
379 return 0;
380 }
381
382 str = talloc_strdup(vb, escaped);
384
385 curl_free(escaped);
386
387 return 0;
388}
389
390static int rlm_rest_perform(module_ctx_t const *mctx,
391 rlm_rest_section_t const *section, fr_curl_io_request_t *randle,
392 request_t *request)
393{
394 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
395 rlm_rest_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
396 int ret;
397
398 RDEBUG2("Sending HTTP %s to \"%pV\"",
399 fr_table_str_by_value(http_method_table, section->request.method, NULL), call_env->request.uri);
400
401 /*
402 * Configure various CURL options, and initialise the read/write
403 * context data.
404 */
405 ret = rest_request_config(mctx, section, request, randle, section->request.method, section->request.body,
406 call_env->request.uri->vb_strvalue,
407 call_env->request.data ? call_env->request.data->vb_strvalue : NULL);
408 if (ret < 0) return -1;
409
410 /*
411 * Send the CURL request, pre-parse headers, aggregate incoming
412 * HTTP body data into a single contiguous buffer.
413 */
414 ret = fr_curl_io_request_enqueue(t->mhandle, request, randle);
415 if (ret < 0) return -1;
416
417 return 0;
418}
419
421 xlat_ctx_t const *xctx,
422 request_t *request, UNUSED fr_value_box_list_t *in)
423{
424 rlm_rest_xlat_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, rlm_rest_xlat_rctx_t);
425 int hcode;
426 ssize_t len;
427 char const *body;
429
430 fr_curl_io_request_t *handle = talloc_get_type_abort(rctx->handle, fr_curl_io_request_t);
431 rlm_rest_section_t *section = &rctx->section;
432
433 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
434
435 if (rlm_rest_status_update(request, handle) < 0) {
436 xa = XLAT_ACTION_FAIL;
437 goto finish;
438 }
439
440 hcode = rest_get_handle_code(handle);
441 switch (hcode) {
442 case 404:
443 case 410:
444 case 403:
445 case 401:
446 {
447 fr_pair_t *vp;
448 xa = XLAT_ACTION_FAIL;
449error:
450 rest_response_error(request, handle);
451
452 /*
453 * When the HTTP status code is a failure, put the
454 * response body in REST-HTTP-Body.
455 */
456 len = rest_get_handle_data(&body, handle);
457 if (len == 0) goto finish;
459 fr_pair_value_bstrndup(vp, body, len, true);
460 goto finish;
461 }
462 case 204:
463 goto finish;
464
465 default:
466 /*
467 * Attempt to parse content if there was any.
468 */
469 if ((hcode >= 200) && (hcode < 300)) {
470 break;
471 } else if (hcode < 500) {
472 xa = XLAT_ACTION_FAIL;
473 goto error;
474 } else {
475 xa = XLAT_ACTION_FAIL;
476 goto error;
477 }
478 }
479
480 /*
481 * Output the xlat data if the HTTP status code is one of the "success" ones.
482 *
483 * The user can check REST-HTTP-Status-Code to figure out what happened.
484 *
485 * Eventually we should just emit two boxes, one with the response code
486 * and one with the body.
487 */
488 len = rest_get_handle_data(&body, handle);
489 if (len > 0) {
490 fr_value_box_t *vb;
491
492 MEM(vb = fr_value_box_alloc_null(ctx));
494 fr_value_box_memdup(vb, vb, NULL, (uint8_t const *)body, len, true);
495 } else {
496 fr_value_box_bstrndup(vb, vb, NULL, body, len, true);
497 }
499 }
500finish:
501
502 rest_slab_release(handle);
503
504 talloc_free(rctx);
505
506 return xa;
507}
508
510 { .required = true, .single = true, .type = FR_TYPE_STRING }, /* HTTP Method */
511 { .required = true, .safe_for = CURL_URI_SAFE_FOR, .type = FR_TYPE_STRING, .will_escape = true }, /* URL */
512 { .concat = true, .type = FR_TYPE_STRING }, /* Data */
513 { .type = FR_TYPE_STRING }, /* Headers */
515};
516
517/** Simple xlat to read text data from a URL
518 *
519 * Example:
520@verbatim
521%rest(POST, http://example.com/, "{ \"key\": \"value\" }", [<headers>])
522@endverbatim
523 *
524 * @ingroup xlat_functions
525 */
527 xlat_ctx_t const *xctx, request_t *request,
528 fr_value_box_list_t *in)
529{
531 rlm_rest_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, rlm_rest_thread_t);
532
533 fr_curl_io_request_t *randle = NULL;
534 int ret;
535 http_method_t method;
536
537 fr_value_box_t *method_vb;
538 fr_value_box_t *uri_vb;
539 fr_value_box_t *data_vb;
540 fr_value_box_t *header_vb;
541
542 /* There are no configurable parameters other than the URI */
544 rlm_rest_section_t *section;
545
546 XLAT_ARGS(in, &method_vb, &uri_vb, &data_vb, &header_vb);
547
548 MEM(rctx = talloc(request, rlm_rest_xlat_rctx_t));
549 section = &rctx->section;
550
551 /*
552 * Section gets modified, so we need our own copy.
553 */
554 memcpy(&rctx->section, &inst->xlat, sizeof(*section));
555
556 /*
557 * Set the HTTP verb
558 */
559 method = fr_table_value_by_substr(http_method_table, method_vb->vb_strvalue, -1, REST_HTTP_METHOD_UNKNOWN);
560 if (method != REST_HTTP_METHOD_UNKNOWN) {
561 section->request.method = method;
562 /*
563 * If the method is unknown, it's a custom verb
564 */
565 } else {
567 MEM(section->request.method_str = talloc_bstrndup(rctx, method_vb->vb_strvalue, method_vb->vb_length));
568 }
569
570 /*
571 * Handle URI component escaping
572 */
573 if (fr_uri_escape_list(&uri_vb->vb_group, rest_uri_parts, NULL) < 0) {
574 RPEDEBUG("Failed escaping URI");
575 error:
576 talloc_free(section);
577 return XLAT_ACTION_FAIL;
578 }
579
580 /*
581 * Smush all the URI components together
582 */
584 uri_vb, &uri_vb->vb_group, FR_TYPE_STRING,
586 SIZE_MAX) < 0) {
587 REDEBUG("Concatenating URI");
588 goto error;
589 }
590
591 /*
592 * We get a connection from the pool here as the CURL object
593 * is needed to use curl_easy_escape() for escaping
594 */
595 randle = rctx->handle = rest_slab_reserve(t->slab);
596 if (!randle) return XLAT_ACTION_FAIL;
597
598 randle->request = request; /* Populate the request pointer for escape callbacks */
599 if (data_vb) section->request.body = REST_HTTP_BODY_CUSTOM;
600
601 RDEBUG2("Sending HTTP %s to \"%pV\"",
604 uri_vb);
605
606 if (header_vb) {
607 fr_value_box_list_foreach(&header_vb->vb_group, header) {
608 if (unlikely(rest_request_config_add_header(request, randle, header->vb_strvalue, true) < 0)) {
609 error_release:
610 rest_slab_release(randle);
611 goto error;
612 }
613 }
614 }
615
616 /*
617 * Configure various CURL options, and initialise the read/write
618 * context data.
619 *
620 * @todo We could extract the User-Name and password from the URL string.
621 */
622 ret = rest_request_config(MODULE_CTX(xctx->mctx->mi, t, xctx->env_data, NULL),
623 section, request, randle, section->request.method,
624 section->request.body,
625 uri_vb->vb_strvalue, data_vb ? data_vb->vb_strvalue : NULL);
626 if (ret < 0) goto error_release;
627
628 /*
629 * Send the CURL request, pre-parse headers, aggregate incoming
630 * HTTP body data into a single contiguous buffer.
631 *
632 * @fixme need to pass in thread to all xlat functions
633 */
634 ret = fr_curl_io_request_enqueue(t->mhandle, request, randle);
635 if (ret < 0) goto error_release;
636
638}
639
641{
643 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
644 rlm_rest_section_t const *section = &env->section->section;
645 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
646
647 int hcode;
649 int ret;
650
651 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
652
653 if (rlm_rest_status_update(request, handle) < 0) {
654 rcode = RLM_MODULE_FAIL;
655 goto finish;
656 }
657
658 hcode = rest_get_handle_code(handle);
659 switch (hcode) {
660 case 404:
661 case 410:
662 rcode = RLM_MODULE_NOTFOUND;
663 break;
664
665 case 403:
666 rcode = RLM_MODULE_DISALLOW;
667 break;
668
669 case 401:
670 /*
671 * Attempt to parse content if there was any.
672 */
673 ret = rest_response_decode(inst, section, request, handle);
674 if (ret < 0) {
675 rcode = RLM_MODULE_FAIL;
676 break;
677 }
678
679 rcode = RLM_MODULE_REJECT;
680 break;
681
682 case 204:
683 rcode = RLM_MODULE_OK;
684 break;
685
686 default:
687 /*
688 * Attempt to parse content if there was any.
689 */
690 if ((hcode >= 200) && (hcode < 300)) {
691 ret = rest_response_decode(inst, section, request, handle);
692 if (ret < 0) rcode = RLM_MODULE_FAIL;
693 else if (ret == 0) rcode = RLM_MODULE_OK;
694 else rcode = RLM_MODULE_UPDATED;
695 break;
696 } else if (hcode < 500) {
697 rcode = RLM_MODULE_INVALID;
698 } else {
699 rcode = RLM_MODULE_FAIL;
700 }
701 }
702
703 switch (rcode) {
705 case RLM_MODULE_FAIL:
707 rest_response_error(request, handle);
708 break;
709
710 default:
711 rest_response_debug(request, handle);
712 break;
713 }
714
715finish:
716 rest_slab_release(handle);
717
718 RETURN_UNLANG_RCODE(rcode);
719}
720
721/*
722 * Find the named user in this modules database. Create the set
723 * of attribute-value pairs to check and reply with for this user
724 * from the database. The authentication code only needs to check
725 * the password, the rest is done here.
726 */
727static unlang_action_t CC_HINT(nonnull) mod_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
728{
729 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
730 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
731 rlm_rest_section_t const *section = &env->section->section;
732
733 void *handle;
734 int ret;
735
736 handle = rest_slab_reserve(t->slab);
737 if (!handle) RETURN_UNLANG_FAIL;
738
739 ret = rlm_rest_perform(mctx, section, handle, request);
740 if (ret < 0) {
741 rest_slab_release(handle);
742
744 }
745
747}
748
750 module_ctx_t const *mctx, request_t *request)
751{
753 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
754 rlm_rest_section_t const *section = &env->section->section;
755 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
756
757 int hcode;
758 int rcode = RLM_MODULE_OK;
759 int ret;
760
761 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
762
763 if (rlm_rest_status_update(request, handle) < 0) {
764 rcode = RLM_MODULE_FAIL;
765 goto finish;
766 }
767
768 hcode = rest_get_handle_code(handle);
769 switch (hcode) {
770 case 404:
771 case 410:
772 rcode = RLM_MODULE_NOTFOUND;
773 break;
774
775 case 403:
776 rcode = RLM_MODULE_DISALLOW;
777 break;
778
779 case 401:
780 /*
781 * Attempt to parse content if there was any.
782 */
783 ret = rest_response_decode(inst, section, request, handle);
784 if (ret < 0) {
785 rcode = RLM_MODULE_FAIL;
786 break;
787 }
788
789 rcode = RLM_MODULE_REJECT;
790 break;
791
792 case 204:
793 rcode = RLM_MODULE_OK;
794 break;
795
796 default:
797 /*
798 * Attempt to parse content if there was any.
799 */
800 if ((hcode >= 200) && (hcode < 300)) {
801 ret = rest_response_decode(inst, section, request, handle);
802 if (ret < 0) rcode = RLM_MODULE_FAIL;
803 else if (ret == 0) rcode = RLM_MODULE_OK;
804 else rcode = RLM_MODULE_UPDATED;
805 break;
806 } else if (hcode < 500) {
807 rcode = RLM_MODULE_INVALID;
808 } else {
809 rcode = RLM_MODULE_FAIL;
810 }
811 }
812
813 switch (rcode) {
815 case RLM_MODULE_FAIL:
817 rest_response_error(request, handle);
818 break;
819
820 default:
821 rest_response_debug(request, handle);
822 break;
823 }
824
825finish:
826 rest_slab_release(handle);
827
828 RETURN_UNLANG_RCODE(rcode);
829}
830
831/*
832 * Authenticate the user with the given password.
833 */
834static unlang_action_t CC_HINT(nonnull) mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
835{
836 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
837 rlm_rest_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
838 rlm_rest_section_t const *section = &call_env->section->section;
839 fr_curl_io_request_t *handle;
840 int ret;
841
842 /*
843 * We can only authenticate user requests which HAVE
844 * a User-Name attribute.
845 */
846 if (!call_env->request.username) {
847 REDEBUG("Attribute \"User-Name\" is required for authentication");
849 }
850
851 if (!call_env->request.password) {
852 REDEBUG("Attribute \"User-Password\" is required for authentication");
854 }
855
856 /*
857 * Make sure the supplied password isn't empty
858 */
859 if (call_env->request.password->vb_length == 0) {
860 REDEBUG("User-Password must not be empty");
862 }
863
864 /*
865 * Log the password
866 */
867 if (RDEBUG_ENABLED3) {
868 RDEBUG("Login attempt with password \"%pV\"", call_env->request.password);
869 } else {
870 RDEBUG2("Login attempt with password");
871 }
872
873 handle = rest_slab_reserve(t->slab);
874 if (!handle) RETURN_UNLANG_FAIL;
875
876 ret = rlm_rest_perform(mctx, section, handle, request);
877 if (ret < 0) {
878 rest_slab_release(handle);
879
881 }
882
884}
885
887{
889 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
890 rlm_rest_section_t const *section = &env->section->section;
891 fr_curl_io_request_t *handle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
892
893 int hcode;
894 int rcode = RLM_MODULE_OK;
895 int ret;
896
897 if (section->tls.extract_cert_attrs) fr_curl_response_certinfo(request, handle);
898
899 if (rlm_rest_status_update(request, handle) < 0) {
900 rcode = RLM_MODULE_FAIL;
901 goto finish;
902 }
903
904 hcode = rest_get_handle_code(handle);
905 if (hcode >= 500) {
906 rcode = RLM_MODULE_FAIL;
907 } else if (hcode == 204) {
908 rcode = RLM_MODULE_OK;
909 } else if ((hcode >= 200) && (hcode < 300)) {
910 ret = rest_response_decode(inst, section, request, handle);
911 if (ret < 0) rcode = RLM_MODULE_FAIL;
912 else if (ret == 0) rcode = RLM_MODULE_OK;
913 else rcode = RLM_MODULE_UPDATED;
914 } else {
915 rcode = RLM_MODULE_INVALID;
916 }
917
918 switch (rcode) {
920 case RLM_MODULE_FAIL:
921 rest_response_error(request, handle);
922 break;
923
924 default:
925 rest_response_debug(request, handle);
926 break;
927 }
928
929finish:
930 rest_slab_release(handle);
931
932 RETURN_UNLANG_RCODE(rcode);
933}
934
935/*
936 * Send accounting info to a REST API endpoint
937 */
938static unlang_action_t CC_HINT(nonnull) mod_accounting(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
939{
940 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
941 rlm_rest_call_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_rest_call_env_t);
942 rlm_rest_section_t const *section = &env->section->section;
943 void *handle;
944 int ret;
945
946 handle = rest_slab_reserve(t->slab);
947 if (!handle) RETURN_UNLANG_FAIL;
948
949 ret = rlm_rest_perform(mctx, section, handle, request);
950 if (ret < 0) {
951 rest_slab_release(handle);
952
954 }
955
957}
958
960 rlm_rest_section_t *config, char const *name, CONF_SECTION *cs)
961{
962 CONF_SECTION *request_cs;
963
964 if (!cs) cs = cf_section_find(parent, name, NULL);
965 if (!cs) {
966 config->name = NULL;
967 return 0;
968 }
969
970 if (cf_section_rules_push(cs, config_items) < 0) return -1;
971 if (cf_section_parse(inst, config, cs) < 0) {
972 config->name = NULL;
973 return -1;
974 }
975
976 /*
977 * Add section name (Maybe add to headers later?).
978 */
979 config->name = name;
980
981 /*
982 * Convert HTTP method auth and body type strings into their integer equivalents.
983 */
984 if ((config->request.auth != REST_HTTP_AUTH_NONE) && !http_curl_auth[config->request.auth]) {
985 cf_log_err(cs, "Unsupported HTTP auth type \"%s\", check libcurl version, OpenSSL build "
986 "configuration, then recompile this module",
987 fr_table_str_by_value(http_auth_table, config->request.auth, "<INVALID>"));
988
989 return -1;
990 }
991 config->request.method = fr_table_value_by_str(http_method_table, config->request.method_str, REST_HTTP_METHOD_CUSTOM);
992
993 /*
994 * Custom hackery to figure out if data was set we can't do it any other way because we can't
995 * parse the tmpl_t except within a call_env.
996 *
997 * We have custom body data so we set REST_HTTP_BODY_CUSTOM, but also need to try and
998 * figure out what content-type to use. So if they've used the canonical form we
999 * need to convert it back into a proper HTTP content_type value.
1000 */
1001 if ((strcmp(name, "xlat") == 0) || ((request_cs = cf_section_find(cs, "request", NULL)) && cf_pair_find(request_cs, "data"))) {
1002 http_body_type_t body;
1003
1004 config->request.body = REST_HTTP_BODY_CUSTOM;
1005
1007 if (body != REST_HTTP_BODY_UNKNOWN) {
1008 config->request.body_str = fr_table_str_by_value(http_content_type_table, body, config->request.body_str);
1009 }
1010 /*
1011 * We don't have any custom user data, so we need to select the right encoder based
1012 * on the body type.
1013 *
1014 * To make this slightly more/less confusing, we accept both canonical body_types,
1015 * and content_types.
1016 */
1017 } else {
1019 if (config->request.body == REST_HTTP_BODY_UNKNOWN) {
1021 }
1022
1023 if (config->request.body == REST_HTTP_BODY_UNKNOWN) {
1024 cf_log_err(cs, "Unknown HTTP body type '%s'", config->request.body_str);
1025 return -1;
1026 }
1027
1028 switch (http_body_type_supported[config->request.body]) {
1030 cf_log_err(cs, "Unsupported HTTP body type \"%s\", please submit patches",
1031 config->request.body_str);
1032 return -1;
1033
1035 cf_log_err(cs, "Invalid HTTP body type. \"%s\" is not a valid web API data "
1036 "markup format", config->request.body_str);
1037 return -1;
1038
1040 cf_log_err(cs, "Unavailable HTTP body type. \"%s\" is not available in this "
1041 "build", config->request.body_str);
1042 return -1;
1043
1044 default:
1045 break;
1046 }
1047 }
1048
1049 if (config->response.force_to_str) {
1050 config->response.force_to = fr_table_value_by_str(http_body_type_table, config->response.force_to_str, REST_HTTP_BODY_UNKNOWN);
1051 if (config->response.force_to == REST_HTTP_BODY_UNKNOWN) {
1052 config->response.force_to = fr_table_value_by_str(http_content_type_table, config->response.force_to_str, REST_HTTP_BODY_UNKNOWN);
1053 }
1054
1055 if (config->response.force_to == REST_HTTP_BODY_UNKNOWN) {
1056 cf_log_err(cs, "Unknown forced response body type '%s'", config->response.force_to_str);
1057 return -1;
1058 }
1059
1060 switch (http_body_type_supported[config->response.force_to]) {
1062 cf_log_err(cs, "Unsupported forced response body type \"%s\", please submit patches",
1063 config->response.force_to_str);
1064 return -1;
1065
1067 cf_log_err(cs, "Invalid HTTP forced response body type. \"%s\" is not a valid web API data "
1068 "markup format", config->response.force_to_str);
1069 return -1;
1070
1071 default:
1072 break;
1073 }
1074 }
1075
1076 return 0;
1077}
1078
1079/** Cleans up after a REST request.
1080 *
1081 * Resets all options associated with a CURL handle, and frees any headers
1082 * associated with it.
1083 *
1084 * @param[in] randle to cleanup.
1085 * @param[in] uctx unused.
1086 */
1088{
1089 rlm_rest_curl_context_t *ctx = talloc_get_type_abort(randle->uctx, rlm_rest_curl_context_t);
1090 CURL *candle = randle->candle;
1091
1092 /*
1093 * Clear any previously configured options
1094 */
1095 curl_easy_reset(candle);
1096
1097 /*
1098 * Free header list
1099 */
1100 if (ctx->headers != NULL) {
1101 curl_slist_free_all(ctx->headers);
1102 ctx->headers = NULL;
1103 }
1104
1105#ifndef NDEBUG
1106 {
1107 CURLcode ret;
1108 /*
1109 * With curl 7.61 when a request in cancelled we get a result
1110 * with a NULL (invalid) pointer to private data. This lets
1111 * us know that the request was returned to the slab.
1112 */
1113 ret = curl_easy_setopt(candle, CURLOPT_PRIVATE, (void *)0xdeadc341);
1114 if (unlikely(ret != CURLE_OK)) {
1115 ERROR("Failed to set private data on curl easy handle %p: %s",
1116 candle, curl_easy_strerror(ret));
1117 }
1118 }
1119#endif
1120
1121 /*
1122 * Free response data
1123 */
1124 TALLOC_FREE(ctx->body);
1125 TALLOC_FREE(ctx->response.buffer);
1126 TALLOC_FREE(ctx->request.encoder);
1127 TALLOC_FREE(ctx->response.decoder);
1128 ctx->response.header = NULL; /* This is owned by the parsed call env and must not be freed */
1129
1130 randle->request = NULL;
1131 return 0;
1132}
1133
1135{
1136 curl_easy_cleanup(randle->candle);
1137 return 0;
1138}
1139
1140static int rest_conn_alloc(fr_curl_io_request_t *randle, void *uctx)
1141{
1142 rlm_rest_t const *inst = talloc_get_type_abort(uctx, rlm_rest_t);
1143 rlm_rest_curl_context_t *curl_ctx = NULL;
1144
1145 randle->candle = curl_easy_init();
1146 if (unlikely(!randle->candle)) {
1147 fr_strerror_printf("Unable to initialise CURL handle");
1148 return -1;
1149 }
1150
1151 MEM(curl_ctx = talloc_zero(randle, rlm_rest_curl_context_t));
1152 curl_ctx->headers = NULL;
1153 curl_ctx->request.instance = inst;
1154 curl_ctx->response.instance = inst;
1155
1156 randle->uctx = curl_ctx;
1157 talloc_set_destructor(randle, _mod_conn_free);
1158
1159 rest_slab_element_set_destructor(randle, _rest_request_cleanup, NULL);
1160
1161 return 0;
1162}
1163
1164/** Create a thread specific multihandle
1165 *
1166 * Easy handles representing requests are added to the curl multihandle
1167 * with the multihandle used for mux/demux.
1168 *
1169 * @param[in] mctx Thread instantiation data.
1170 * @return
1171 * - 0 on success.
1172 * - -1 on failure.
1173 */
1175{
1176 rlm_rest_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1177 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
1178 fr_curl_handle_t *mhandle;
1179
1180 t->inst = inst;
1181
1182 if (!(t->slab = rest_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
1183 rest_conn_alloc, NULL, inst, false, false))) {
1184 ERROR("Connection handle pool instantiation failed");
1185 return -1;
1186 }
1187
1188 mhandle = fr_curl_io_init(t, mctx->el, inst->multiplex);
1189 if (!mhandle) return -1;
1190
1191 t->mhandle = mhandle;
1192
1193 return 0;
1194}
1195
1196/** Cleanup all outstanding requests associated with this thread
1197 *
1198 * Destroys all curl easy handles, and then the multihandle associated
1199 * with this thread.
1200 *
1201 * @param[in] mctx data to destroy.
1202 * @return 0
1203 */
1205{
1206 rlm_rest_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_rest_thread_t);
1207
1208 talloc_free(t->mhandle); /* Ensure this is shutdown before the pool */
1209 talloc_free(t->slab);
1210
1211 return 0;
1212}
1213
1214/*
1215 * Do any per-module initialization that is separate to each
1216 * configured instance of the module. e.g. set up connections
1217 * to external databases, read configuration files, set up
1218 * dictionary entries, etc.
1219 *
1220 * If configuration information is given in the config section
1221 * that must be referenced in later calls, store a handle to it
1222 * in *instance otherwise put a null pointer there.
1223 */
1224static int mod_instantiate(module_inst_ctx_t const *mctx)
1225{
1226 rlm_rest_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1227 CONF_SECTION *conf = mctx->mi->conf;
1228 rlm_rest_section_conf_t *section;
1230
1231 inst->xlat.request.method_str = "GET";
1232 inst->xlat.request.body = REST_HTTP_BODY_NONE;
1233 inst->xlat.request.body_str = "application/x-www-form-urlencoded";
1234 inst->xlat.response.accept_all = true;
1235
1236 if (!inst->sections_init) fr_rb_inline_init(&inst->sections, rlm_rest_section_conf_t, node, rest_section_cmp, NULL);
1237
1238 /*
1239 * Parse xlat config.
1240 */
1241 if ((parse_sub_section(inst, conf, xlat_config, &inst->xlat, "xlat", NULL) < 0)) return -1;
1242
1243 /*
1244 * Parse section configs from calls found by the call_env parser.
1245 */
1246 for (section = fr_rb_iter_init_inorder(&inst->sections, &iter);
1247 section != NULL;
1248 section = fr_rb_iter_next_inorder(&inst->sections, &iter)) {
1250 cf_section_name(section->cs), section->cs) < 0) return -1;
1251 }
1252
1253 inst->conn_config.reuse.num_children = 1;
1254 inst->conn_config.reuse.child_pool_size = sizeof(rlm_rest_curl_context_t);
1255
1256 return 0;
1257}
1258
1259static int mod_bootstrap(module_inst_ctx_t const *mctx)
1260{
1261 xlat_t *xlat;
1262 rlm_rest_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_rest_t);
1263
1264 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, rest_xlat, inst->xlat.binary ? FR_TYPE_OCTETS : FR_TYPE_VOID);
1267
1268 return 0;
1269}
1270
1271static int mod_load(void)
1272{
1273 /* developer sanity */
1275
1276#ifdef HAVE_JSON
1278#endif
1279
1280 return 0;
1281}
1282
1283/*
1284 * Custom call_env parser which looks for a conf section matching the name
1285 * of the section the module is called in and then hands off to the normal
1286 * parsing.
1287 */
1288static int rest_sect_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, UNUSED tmpl_rules_t const *t_rules,
1289 CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1290{
1291 rlm_rest_t *inst = talloc_get_type_abort(cec->mi->data, rlm_rest_t);
1292 CONF_SECTION *cs;
1293 CONF_SECTION *sect = NULL;
1294 call_env_parsed_t *parsed;
1295 void *found;
1296 rlm_rest_section_conf_t *section;
1297 char *p, *name2 = NULL;
1298 size_t i;
1299
1300 /*
1301 * The parent section is the main module conf section
1302 * in which we'll look for a suitable section to parse.
1303 */
1304 cs = cf_item_to_section(cf_parent(ci));
1305
1306 if (cec->asked->name2) {
1307 name2 = talloc_strdup(NULL, cec->asked->name2);
1308 p = name2;
1309 for (i = 0; i < talloc_array_length(name2); i++) {
1310 *p = tolower(*p);
1311 p++;
1312 }
1313 sect = cf_section_find(cs, cec->asked->name1, name2);
1314 }
1315
1316 if (!sect) {
1317 sect = cf_section_find(cs, cec->asked->name1, NULL);
1318 }
1319
1320 if (!inst->sections_init) {
1322 inst->sections_init = true;
1323 }
1324
1325 if (!sect) {
1326 cf_log_err(cs, "%s called in %s %s - requires conf section %s %s%s%s", cec->mi->name,
1327 cec->asked->name1, cec->asked->name2 ? cec->asked->name2 : "",
1328 cec->asked->name1, cec->asked->name2 ? name2 : "",
1329 cec->asked->name2 ? " or " : "",
1330 cec->asked->name2 ? cec->asked->name1 : "");
1331 talloc_free(name2);
1332 return -1;
1333 }
1334 talloc_free(name2);
1335
1336 /*
1337 * "authenticate" sections use a different rules with defaults set for username and password
1338 */
1339 if (strcmp(cec->asked->name1, "authenticate") == 0) {
1340 if (call_env_parse(ctx, out, cec->mi->name, t_rules, sect, cec, rest_section_authenticate_env) < 0) return -1;
1341 } else {
1342 if (call_env_parse(ctx, out, cec->mi->name, t_rules, sect, cec, rest_section_common_env) < 0) return -1;
1343 }
1344 parsed = call_env_parsed_add(ctx, out,
1346 .name = "section",
1347 .flags = CALL_ENV_FLAG_PARSE_ONLY,
1348 .pair = {
1349 .parsed = {
1350 .offset = offsetof(rlm_rest_call_env_t, section),
1352 }
1353 }
1354 });
1355
1356 MEM(section = talloc_zero(inst, rlm_rest_section_conf_t));
1357 section->cs = sect;
1358 if (fr_rb_find_or_insert(&found, &inst->sections, section) < 0) {
1359 talloc_free(section);
1360 return -1;
1361 }
1362 if (found) {
1363 talloc_free(section);
1364 call_env_parsed_set_data(parsed, found);
1365 } else {
1366 call_env_parsed_set_data(parsed, section);
1367 }
1368 return 0;
1369}
1370
1378
1379/*
1380 * The module name should be the only globally exported symbol.
1381 * That is, everything else should be 'static'.
1382 *
1383 * If the module needs to temporarily modify it's instantiation
1384 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
1385 * The server will then take care of ensuring that the module
1386 * is single-threaded.
1387 */
1388extern module_rlm_t rlm_rest;
1390 .common = {
1391 .magic = MODULE_MAGIC_INIT,
1392 .name = "rest",
1393 .inst_size = sizeof(rlm_rest_t),
1394 .thread_inst_size = sizeof(rlm_rest_thread_t),
1395 .config = module_config,
1396 .onload = mod_load,
1397 .bootstrap = mod_bootstrap,
1398 .instantiate = mod_instantiate,
1399 .thread_instantiate = mod_thread_instantiate,
1400 .thread_detach = mod_thread_detach
1401 },
1402 .method_group = {
1403 .bindings = (module_method_binding_t[]){
1404 { .section = SECTION_NAME("recv", "Accounting-Request"), .method = mod_accounting, .method_env = &rest_method_env },
1405 { .section = SECTION_NAME("accounting", CF_IDENT_ANY), .method = mod_accounting, .method_env = &rest_method_env },
1406 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &rest_method_env },
1407 { .section = SECTION_NAME("send", CF_IDENT_ANY), .method = mod_accounting, .method_env = &rest_method_env },
1408 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_common, .method_env = &rest_method_env },
1410 }
1411 }
1412};
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:224
#define RCSID(id)
Definition build.h:512
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#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:407
#define UNUSED
Definition build.h:336
#define NUM_ELEMENTS(_t)
Definition build.h:358
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:460
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:689
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:746
#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:1289
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:1724
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:623
#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:701
#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:606
Common header for all CONF_* types.
Definition cf_priv.h:54
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
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:1201
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:692
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:1594
char const * cf_section_name(CONF_SECTION const *cs)
Return name2 if set, else name1.
Definition cf_util.c:1371
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:672
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1753
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:345
#define cf_parent(_cf)
Definition cf_util.h:118
#define CF_IDENT_ANY
Definition cf_util.h:80
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:36
#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:526
talloc_free(hp)
void fr_json_version_print(void)
Print JSON-C version.
Definition json.c:484
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_VOID
User data.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
@ FR_TYPE_OCTETS
Raw octets.
long int ssize_t
unsigned char uint8_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:234
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:2812
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:165
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:1827
http_body_type_t rest_response_body_type_get(fr_curl_io_request_t *randle)
Return the body type of a HTTP response.
Definition rest.c:1666
fr_table_num_sorted_t const http_body_type_table[]
Conversion table for type config values.
Definition rest.c:149
fr_table_num_sorted_t const http_method_table[]
Conversion table for method config values.
Definition rest.c:130
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:1650
void rest_response_debug(request_t *request, fr_curl_io_request_t *handle)
Print out the response text.
Definition rest.c:1593
fr_table_num_sorted_t const http_content_type_table[]
Conversion table for "Content-Type" header values.
Definition rest.c:193
const unsigned long http_curl_auth[REST_HTTP_AUTH_NUM_ENTRIES]
Definition rest.c:104
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:1747
void rest_response_error(request_t *request, fr_curl_io_request_t *handle)
Print out the response text as error lines.
Definition rest.c:1568
const bool http_body_type_binary[REST_HTTP_BODY_NUM_ENTRIES]
Table of which known body types are expected to give binary data.
Definition rest.c:69
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:2127
Function prototypes and datatypes for the REST (HTTP) transport.
rlm_rest_t const * instance
This instance of rlm_rest.
Definition rest.h:261
struct curl_slist * headers
Any HTTP headers which will be sent with the request.
Definition rest.h:285
tmpl_t * header
Where to create pairs representing HTTP response headers.
Definition rest.h:275
#define rest_get_handle_code(_handle)
Definition rest.h:353
char * buffer
Raw incoming HTTP data.
Definition rest.h:267
fr_curl_handle_t * mhandle
Thread specific multi handle.
Definition rest.h:215
char * body
Pointer to the buffer which contains body data/ Only used when not performing chunked encoding.
Definition rest.h:288
fr_curl_tls_t tls
Definition rest.h:169
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:114
struct rlm_rest_call_env_t::@187 request
rlm_rest_section_t section
Parsed section config.
Definition rest.h:176
rlm_rest_section_conf_t * section
Section config.
Definition rest.h:304
rlm_rest_section_request_t request
Request configuration.
Definition rest.h:166
bool binary
Do we expect binary data - so xlat will output octets.
Definition rest.h:164
rlm_rest_response_t response
Response context data.
Definition rest.h:292
void * decoder
Decoder specific data.
Definition rest.h:278
void * encoder
Encoder specific data.
Definition rest.h:253
rlm_rest_request_t request
Request context data.
Definition rest.h:291
http_method_t method
What HTTP method should be used, GET, POST etc...
Definition rest.h:115
rlm_rest_section_t section
Our mutated section config.
Definition rest.h:299
rlm_rest_t const * inst
Instance of rlm_rest.
Definition rest.h:213
@ REST_HTTP_AUTH_NONE
Definition rest.h:72
rest_slab_list_t * slab
Slab list for connection handles.
Definition rest.h:214
http_body_type_t body
What encoding type should be used.
Definition rest.h:118
fr_curl_io_request_t * handle
curl easy handle servicing our request.
Definition rest.h:300
rlm_rest_t const * instance
This instance of rlm_rest.
Definition rest.h:243
CONF_SECTION * cs
Conf section found for this call.
Definition rest.h:177
Definition rest.h:139
Thread specific rlm_rest instance data.
Definition rest.h:212
Stores the state of a yielded xlat.
Definition rest.h:298
static char const * name
static const call_env_method_t rest_call_env_xlat
Definition rlm_rest.c:245
static int rlm_rest_status_update(request_t *request, void *handle)
Update the status attribute.
Definition rlm_rest.c:307
static int mod_load(void)
Definition rlm_rest.c:1271
static int8_t rest_section_cmp(void const *one, void const *two)
Definition rlm_rest.c:293
static int rest_conn_alloc(fr_curl_io_request_t *randle, void *uctx)
Definition rlm_rest.c:1140
static const conf_parser_t section_response_post_config[]
Definition rlm_rest.c:130
static int _rest_uri_part_escape_uctx_free(void *uctx)
Definition rlm_rest.c:332
static const conf_parser_t xlat_config[]
Definition rlm_rest.c:160
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:420
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:959
static const conf_parser_t section_response_json_config[]
Definition rlm_rest.c:120
static unlang_action_t mod_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:727
fr_dict_t const * dict_freeradius
Definition rlm_rest.c:267
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:390
static const conf_parser_t xlat_request_config[]
Definition rlm_rest.c:155
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:1259
fr_dict_attr_t const * attr_rest_http_header
Definition rlm_rest.c:276
static unlang_action_t mod_accounting(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:938
fr_dict_autoload_t rlm_rest_dict[]
Definition rlm_rest.c:270
static const conf_parser_t section_request_config[]
Definition rlm_rest.c:107
static const conf_parser_t section_config[]
Definition rlm_rest.c:143
static int _rest_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
Cleans up after a REST request.
Definition rlm_rest.c:1087
#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:1371
fr_dict_attr_t const * attr_rest_http_status_code
Definition rlm_rest.c:277
static unlang_action_t mod_accounting_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:886
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:1174
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:834
module_rlm_t rlm_rest
Definition rlm_rest.c:1389
fr_dict_attr_t const * attr_rest_http_body
Definition rlm_rest.c:275
fr_dict_attr_autoload_t rlm_rest_dict_attr[]
Definition rlm_rest.c:280
static const conf_parser_t section_response_config[]
Definition rlm_rest.c:135
#define REST_CALL_ENV_SECTION(_var, _dflt_username, _dflt_password)
Definition rlm_rest.c:206
static const conf_parser_t module_config[]
Definition rlm_rest.c:173
static int _mod_conn_free(fr_curl_io_request_t *randle)
Definition rlm_rest.c:1134
static xlat_arg_parser_t const rest_xlat_args[]
Definition rlm_rest.c:509
static unlang_action_t mod_authenticate_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:749
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Cleanup all outstanding requests associated with this thread.
Definition rlm_rest.c:1204
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1224
static unlang_action_t mod_common_result(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_rest.c:640
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:343
global_lib_autoinst_t const *const rlm_rest_lib[]
Definition rlm_rest.c:288
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:1288
#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:357
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:351
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
void * boot
Data allocated during the boostrap phase.
Definition module.h:296
#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:685
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
#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:725
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:618
#define talloc_get_type_abort_const
Definition talloc.h:117
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
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 consumed 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:4745
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:4838
int fr_value_box_memdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Copy a buffer to a fr_value_box_t.
Definition value.c:5079
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:6604
@ 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:365
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:382