The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
tmpl.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
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: fe96e217544536ddee8dae8895e2475d132c559d $
19  *
20  * @file unlang/tmpl.c
21  * @brief Defines functions for calling tmpl__t asynchronously
22  *
23  * @copyright 2021 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
24  * @copyright 2020 Network RADIUS SAS (legal@networkradius.com)
25  */
26 RCSID("$Id: fe96e217544536ddee8dae8895e2475d132c559d $")
27 
28 #include <freeradius-devel/unlang/tmpl.h>
29 #include <freeradius-devel/server/exec.h>
30 #include <freeradius-devel/util/syserror.h>
31 #include <freeradius-devel/util/value.h>
32 #include "tmpl_priv.h"
33 #include <signal.h>
34 
35 #if defined(__linux__) || defined(__FreeBSD__)
36 #include <sys/wait.h>
37 #endif
38 
39 /** Send a signal (usually stop) to a request
40  *
41  * This is typically called via an "async" action, i.e. an action
42  * outside of the normal processing of the request.
43  *
44  * If there is no #fr_unlang_tmpl_signal_t callback defined, the action is ignored.
45  *
46  * @param[in] request The current request.
47  * @param[in] frame being signalled.
48  * @param[in] action to signal.
49  */
50 static void unlang_tmpl_signal(request_t *request, unlang_stack_frame_t *frame, fr_signal_t action)
51 {
52  unlang_frame_state_tmpl_t *state = talloc_get_type_abort(frame->state,
54 
55  /*
56  * If we're cancelled, then kill any child processes
57  */
58  if ((action == FR_SIGNAL_CANCEL) && state->exec.request) fr_exec_oneshot_cleanup(&state->exec, SIGKILL);
59 
60  if (!state->signal) return;
61 
62  state->signal(request, state->rctx, action);
63 
64  /*
65  * If we're cancelled then disable this signal handler.
66  * fr_exec_oneshot_cleanup should handle being called spuriously.
67  */
68  if (action == FR_SIGNAL_CANCEL) state->signal = NULL;
69 }
70 
71 /** Wrapper to call a resumption function after a tmpl has been expanded
72  *
73  * If the resumption function returns YIELD, then this function is
74  * called repeatedly until the resumption function returns a final
75  * value.
76  */
78 {
79  unlang_frame_state_tmpl_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_tmpl_t);
81 
82  if (tmpl_eval_cast_in_place(&state->list, request, ut->tmpl) < 0) {
83  RPEDEBUG("Failed casting expansion");
84  *p_result = RLM_MODULE_FAIL;
86  }
87 
88  if (state->out) fr_value_box_list_move(state->out, &state->list);
89 
90  if (state->resume) return state->resume(p_result, request, state->rctx);
91 
92  *p_result = RLM_MODULE_OK;
93 
95 }
96 
97 /** Wrapper to call exec after the program has finished executing
98  *
99  */
101  unlang_stack_frame_t *frame)
102 {
103  unlang_frame_state_tmpl_t *state = talloc_get_type_abort(frame->state,
105 
106  /*
107  * The exec failed for some internal reason. We don't
108  * care about output, and we don't care about the programs exit status.
109  */
110  if (state->exec.failed) {
111  fr_value_box_list_talloc_free(&state->list);
112  goto resume;
113  }
114 
115  fr_assert(state->exec.pid < 0); /* Assert this has been cleaned up */
116 
117  if (!state->args.exec.stdout_on_error && (state->exec.status != 0)) {
118  fr_assert(fr_value_box_list_empty(&state->list));
119  goto resume;
120  }
121 
122  /*
123  * We might want to just get the status of the program,
124  * and not care about the output.
125  *
126  * If we do care about the output, it's unquoted, and tainted.
127  *
128  * FIXME - It would be much more efficient to just reparent
129  * the string buffer into the context of the box... but we'd
130  * need to fix talloc first.
131  */
132  if (state->out) {
134  fr_value_box_t *box;
135 
136  /*
137  * Remove any trailing LF / CR
138  */
139  fr_sbuff_trim(&state->exec.stdout_buff, sbuff_char_line_endings);
140 
141  fr_value_box_list_init(&state->list);
142  MEM(box = fr_value_box_alloc(state->ctx, FR_TYPE_STRING, NULL));
143  if (fr_value_box_from_str(state->ctx, box, type, NULL,
144  fr_sbuff_start(&state->exec.stdout_buff),
145  fr_sbuff_used(&state->exec.stdout_buff),
146  NULL, true) < 0) {
147  talloc_free(box);
148  *p_result = RLM_MODULE_FAIL;
150  }
151  fr_value_box_list_insert_head(&state->list, box);
152  }
153 
154 resume:
155  /*
156  * Inform the caller of the status if it asked for it
157  */
158  if (state->args.exec.status_out) *state->args.exec.status_out = state->exec.status;
159 
160  /*
161  * Ensure that the callers resume function is called.
162  */
163  frame->process = unlang_tmpl_resume;
164  return unlang_tmpl_resume(p_result, request, frame);
165 }
166 
167 
168 /** Wrapper to call exec after a tmpl has been expanded
169  *
170  */
172  unlang_stack_frame_t *frame)
173 {
174  unlang_frame_state_tmpl_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_tmpl_t);
175 
176  if (fr_exec_oneshot(state->ctx, &state->exec, request,
177  &state->list,
178  state->args.exec.env, false, false,
179  false,
180  (state->out != NULL), state,
181  state->args.exec.timeout) < 0) {
182  RPEDEBUG("Failed executing program");
183  *p_result = RLM_MODULE_FAIL;
185  }
186 
187  fr_value_box_list_talloc_free(&state->list); /* this is the xlat expansion, and not the output string we want */
189 
190  return UNLANG_ACTION_YIELD;
191 }
192 
193 
195 {
196  unlang_frame_state_tmpl_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_tmpl_t);
198 
199  /*
200  * If we're not called from unlang_tmpl_push(), then
201  * ensure that we clean up the resulting value boxes
202  * and that the list to write the boxes in is initialised.
203  */
204  if (!state->ctx) {
205  state->ctx = state;
206  fr_value_box_list_init(&state->list);
207  }
208 
209  /*
210  * Synchronous tmpls can just be resolved immediately, and directly to the output list.
211  *
212  * However, xlat expansions (including fully synchronous function calls!) need to be expanded by
213  * the xlat framework.
214  */
215  if (!tmpl_async_required(ut->tmpl) && !tmpl_contains_xlat(ut->tmpl)) {
216  if (tmpl_eval(state->ctx, state->out, request, ut->tmpl) < 0) {
217  RPEDEBUG("Failed evaluating expansion");
218  goto fail;
219  }
220 
221  *p_result = RLM_MODULE_OK;
223  }
224 
225  /*
226  * XLAT structs are allowed.
227  */
228  if (tmpl_is_xlat(ut->tmpl)) {
230  goto push;
231  }
232 
234 
235  /*
236  * Expand the arguments to the program we're executing.
237  */
239 push:
240  if (unlang_xlat_push(state->ctx, NULL, &state->list, request, tmpl_xlat(ut->tmpl), false) < 0) {
241  fail:
242  *p_result = RLM_MODULE_FAIL;
244  }
245 
247 }
248 
249 /** Push a tmpl onto the stack for evaluation
250  *
251  * @param[in] ctx To allocate value boxes and values in.
252  * @param[out] out The value_box created from the tmpl. May be NULL,
253  * in which case the result is discarded.
254  * @param[in] request The current request.
255  * @param[in] tmpl the tmpl to expand
256  * @param[in] args additional controls for expanding #TMPL_TYPE_EXEC,
257  * and where the status of exited programs will be stored.
258  */
259 int unlang_tmpl_push(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request,
260  tmpl_t const *tmpl, unlang_tmpl_args_t *args)
261 {
262  unlang_stack_t *stack = request->stack;
263  unlang_stack_frame_t *frame;
265 
266  unlang_tmpl_t *ut;
267 
268  static unlang_t tmpl_instruction = {
270  .name = "tmpl",
271  .debug_name = "tmpl",
272  .actions = {
273  .actions = {
274  [RLM_MODULE_REJECT] = 0,
275  [RLM_MODULE_FAIL] = 0,
276  [RLM_MODULE_OK] = 0,
277  [RLM_MODULE_HANDLED] = 0,
278  [RLM_MODULE_INVALID] = 0,
279  [RLM_MODULE_DISALLOW] = 0,
280  [RLM_MODULE_NOTFOUND] = 0,
281  [RLM_MODULE_NOOP] = 0,
282  [RLM_MODULE_UPDATED] = 0
283  },
284  .retry = RETRY_INIT,
285  },
286  };
287 
288  if (tmpl_needs_resolving(tmpl)) {
289  REDEBUG("Expansion \"%pV\" needs to be resolved before it is used", fr_box_strvalue_len(tmpl->name, tmpl->len));
290  return -1;
291  }
292 
294 
295  MEM(ut = talloc(stack, unlang_tmpl_t));
296  *ut = (unlang_tmpl_t){
297  .self = tmpl_instruction,
298  .tmpl = tmpl
299  };
300 
301  /*
302  * Push a new tmpl frame onto the stack
303  */
305  RLM_MODULE_NOT_SET, UNLANG_NEXT_STOP, false) < 0) return -1;
306 
307  frame = &stack->frame[stack->depth];
308  state = talloc_get_type_abort(frame->state, unlang_frame_state_tmpl_t);
309 
310  *state = (unlang_frame_state_tmpl_t) {
311  .out = out,
312  .ctx = ctx,
313  };
314  if (args) state->args = *args; /* Copy these because they're usually ephemeral/initialised as compound literal */
315 
316  /*
317  * Default to something sensible
318  * instead of locking the same indefinitely.
319  */
320  if (!fr_time_delta_ispos(state->args.exec.timeout)) state->args.exec.timeout = fr_time_delta_from_sec(EXEC_TIMEOUT);
321 
322  fr_value_box_list_init(&state->list);
323 
324  return 0;
325 }
326 
328 {
330  &(unlang_op_t){
331  .name = "tmpl",
332  .interpret = unlang_tmpl,
333  .signal = unlang_tmpl_signal,
334  .frame_state_size = sizeof(unlang_frame_state_tmpl_t),
335  .frame_state_type = "unlang_frame_state_tmpl_t",
336  });
337 }
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition: action.h:39
@ UNLANG_ACTION_STOP_PROCESSING
Break out of processing the current request (unwind).
Definition: action.h:43
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition: action.h:37
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition: action.h:42
va_list args
Definition: acutest.h:770
#define RCSID(id)
Definition: build.h:444
int fr_exec_oneshot(TALLOC_CTX *ctx, fr_exec_state_t *exec, request_t *request, fr_value_box_list_t *args, fr_pair_list_t *env_pairs, bool env_escape, bool env_inherit, bool need_stdin, bool store_stdout, TALLOC_CTX *stdout_ctx, fr_time_delta_t timeout)
Call an child program, optionally reading it's output.
Definition: exec.c:984
void fr_exec_oneshot_cleanup(fr_exec_state_t *exec, int signal)
Cleans up an exec'd process on error.
Definition: exec.c:666
#define EXEC_TIMEOUT
Default wait time for exec calls (in seconds).
Definition: exec.h:32
int unlang_interpret_push(request_t *request, unlang_t const *instruction, rlm_rcode_t default_rcode, bool do_next_sibling, bool top_frame)
Push a new frame onto the stack.
Definition: interpret.c:161
#define RPEDEBUG(fmt,...)
Definition: log.h:376
void unlang_register(int type, unlang_op_t *op)
Register an operation with the interpreter.
Definition: base.c:65
talloc_free(reap)
static char * stack[MAX_STACK]
Definition: radmin.c:158
fr_type_t
Definition: merged_model.c:80
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
#define REDEBUG(fmt,...)
Definition: radclient.h:52
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition: rcode.h:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition: rcode.h:46
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition: rcode.h:41
@ RLM_MODULE_NOTFOUND
User not found.
Definition: rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition: rcode.h:49
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition: rcode.h:51
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition: rcode.h:48
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition: rcode.h:44
bool const sbuff_char_line_endings[UINT8_MAX+1]
Definition: sbuff.c:96
size_t fr_sbuff_trim(fr_sbuff_t *sbuff, bool const to_trim[static UINT8_MAX+1])
Trim trailing characters from a string we're composing.
Definition: sbuff.c:2087
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_used(_sbuff_or_marker)
#define tmpl_contains_xlat(vpt)
Definition: tmpl.h:231
#define tmpl_is_xlat(vpt)
Definition: tmpl.h:215
#define tmpl_contains_regex(vpt)
Definition: tmpl.h:230
#define tmpl_is_exec(vpt)
Definition: tmpl.h:216
bool tmpl_async_required(tmpl_t const *vpt)
Return whether or not async is required for this tmpl.
#define tmpl_xlat(_tmpl)
Definition: tmpl.h:925
int tmpl_eval_cast_in_place(fr_value_box_list_t *out, request_t *request, tmpl_t const *vpt)
Casts a value or list of values according to the tmpl.
Definition: tmpl_eval.c:1344
int tmpl_eval(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *vpt)
Gets the value of a tmpl.
Definition: tmpl_eval.c:1216
#define tmpl_needs_resolving(vpt)
Definition: tmpl.h:228
Signals that can be sent to a request.
fr_signal_t
Definition: signal.h:48
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_aka_sim_id_type_t type
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition: time.h:588
#define fr_time_delta_ispos(_a)
Definition: time.h:288
static unlang_action_t unlang_tmpl_exec_wait_final(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Wrapper to call exec after the program has finished executing.
Definition: tmpl.c:100
void unlang_tmpl_init(void)
Definition: tmpl.c:327
static void unlang_tmpl_signal(request_t *request, unlang_stack_frame_t *frame, fr_signal_t action)
Send a signal (usually stop) to a request.
Definition: tmpl.c:50
static unlang_action_t unlang_tmpl_resume(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Wrapper to call a resumption function after a tmpl has been expanded.
Definition: tmpl.c:77
int unlang_tmpl_push(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args)
Push a tmpl onto the stack for evaluation.
Definition: tmpl.c:259
static unlang_action_t unlang_tmpl(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition: tmpl.c:194
static unlang_action_t unlang_tmpl_exec_wait_resume(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Wrapper to call exec after a tmpl has been expanded.
Definition: tmpl.c:171
Declarations for the unlang tmpl interface.
fr_value_box_list_t * out
output list if the exec succeeds
Definition: tmpl_priv.h:39
unlang_tmpl_args_t args
Arguments that control how the tmpl is evaluated.
Definition: tmpl_priv.h:50
TALLOC_CTX * ctx
for allocating value boxes
Definition: tmpl_priv.h:38
fr_unlang_tmpl_resume_t resume
resumption handler
Definition: tmpl_priv.h:43
fr_value_box_list_t list
our intermediate working list
Definition: tmpl_priv.h:40
void * rctx
for resume
Definition: tmpl_priv.h:42
fr_unlang_tmpl_signal_t signal
signal handler
Definition: tmpl_priv.h:44
A tmpl stack entry.
Definition: tmpl_priv.h:37
struct unlang_tmpl_args_t::@95 exec
Exec specific arguments.
Arguments for evaluating different types of tmpls.
Definition: tmpl.h:47
int unlang_xlat_push(TALLOC_CTX *ctx, bool *p_success, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
Definition: xlat.c:274
void * state
Stack frame specialisations.
Definition: unlang_priv.h:304
#define UNLANG_NEXT_STOP
Definition: unlang_priv.h:102
unlang_t self
Definition: unlang_priv.h:171
@ UNLANG_TYPE_TMPL
asynchronously expand a tmpl_t
Definition: unlang_priv.h:85
static void frame_repeat(unlang_stack_frame_t *frame, unlang_process_t process)
Mark the current stack frame up for repeat, and set a new process function.
Definition: unlang_priv.h:526
unlang_t const * instruction
The unlang node we're evaluating.
Definition: unlang_priv.h:289
tmpl_t const * tmpl
Definition: unlang_priv.h:172
static unlang_t * unlang_tmpl_to_generic(unlang_tmpl_t const *p)
Definition: unlang_priv.h:557
unlang_process_t process
function to call for interpreting this stack frame
Definition: unlang_priv.h:292
unlang_type_t type
The specialisation of this node.
Definition: unlang_priv.h:127
static unlang_tmpl_t * unlang_generic_to_tmpl(unlang_t const *p)
Definition: unlang_priv.h:551
An unlang operation.
Definition: unlang_priv.h:214
A node in a graph of unlang_op_t (s) that we execute.
Definition: unlang_priv.h:122
Our interpreter stack, as distinct from the C stack.
Definition: unlang_priv.h:288
An unlang stack associated with a request.
Definition: unlang_priv.h:322
A naked xlat.
Definition: unlang_priv.h:170
#define RETRY_INIT
Definition: retry.h:39
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules, bool tainted)
Definition: value.c:5264
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition: value.h:608
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:279
static size_t char ** out
Definition: value.h:984