The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
subrequest.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: 35c8e18c71d17cfa0644fa5f01de83c1c76ed96a $
19  *
20  * @file unlang/subrequest.c
21  * @brief Unlang "subrequest" and "detach" keyword evaluation.
22  *
23  * @copyright 2006-2019 The FreeRADIUS server project
24  */
25 RCSID("$Id: 35c8e18c71d17cfa0644fa5f01de83c1c76ed96a $")
26 
27 #include <freeradius-devel/server/state.h>
28 #include <freeradius-devel/server/tmpl_dcursor.h>
29 #include <freeradius-devel/unlang/action.h>
30 #include "unlang_priv.h"
31 #include "interpret_priv.h"
32 #include "subrequest_priv.h"
33 #include "subrequest_child_priv.h"
34 
35 /** Send a signal from parent request to subrequest
36  *
37  */
39  fr_signal_t action)
40 {
41  unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
42  request_t *child = talloc_get_type_abort(state->child, request_t);
43 
44  /*
45  * Parent should never receive a detach
46  * signal whilst the child is running.
47  *
48  * Only the child receives a detach
49  * signal when the detach keyword is used.
50  */
51  fr_assert(action != FR_SIGNAL_DETACH);
52 
53  /*
54  * If the server is stopped, inside a breakpoint,
55  * whilst processing a child, on resumption both
56  * requests (parent and child) may need to be
57  * cancelled as they've both hit max request_time.
58  *
59  * Sometimes the child will run to completion before
60  * the cancellation is processed, but the parent
61  * will still be cancelled.
62  *
63  * When the parent is cancelled this function is
64  * executed, which will signal an already stopped
65  * child to cancel itself.
66  *
67  * This triggers asserts in the time tracking code.
68  *
69  * ...so we check to see if the child is done before
70  * sending a signal.
71  */
72  if (unlang_request_is_done(child)) return;
73 
74  /*
75  * Forward other signals to the child
76  */
77  unlang_interpret_signal(child, action);
78 }
79 
80 /** Parent being resumed after a child completes
81  *
82  */
84  unlang_stack_frame_t *frame)
85 {
87  unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state,
89  request_t *child = state->child;
90  unlang_subrequest_t *gext;
91 
92  /*
93  * Child detached
94  */
95  if (!state->child) {
96  RDEBUG3("subrequest detached during its execution - Not updating rcode or reply attributes");
97 
98  /*
99  * If the child detached the subrequest section
100  * should become entirely transparent, and
101  * should not update the section rcode.
102  */
104  }
105 
106  RDEBUG3("subrequest complete");
107 
108  /*
109  * If there's a no destination tmpl, we're done.
110  */
111  if (!child->reply) {
114  }
115 
116  /*
117  * Otherwise... copy reply attributes into the
118  * specified destination.
119  */
120  gext = unlang_group_to_subrequest(g);
121  if (gext->dst) {
122  fr_pair_t *vp = NULL;
124  fr_dcursor_t cursor;
125 
126  /*
127  * Use callback to build missing destination container.
128  */
129  vp = tmpl_dcursor_build_init(NULL, request, &cc, &cursor, request, gext->dst, tmpl_dcursor_pair_build, NULL);
130  if (!vp) {
131  RPDEBUG("Discarding subrequest attributes - Failed allocating groups");
132  *p_result = RLM_MODULE_FAIL;
133  tmpl_dcursor_clear(&cc);
135  }
136 
137  MEM(fr_pair_list_copy(vp, &vp->vp_group, &child->reply_pairs) >= 0);
138 
139  tmpl_dcursor_clear(&cc);
140  }
141 
144 }
145 
147  unlang_stack_frame_t *frame)
148 {
149  unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
150  request_t *child;
151  rlm_rcode_t rcode;
152  fr_pair_t *vp;
153 
154  unlang_group_t *g;
155  unlang_subrequest_t *gext;
156 
157  /*
158  * Initialize the state
159  */
161  if (!g->num_children) {
162  *p_result = RLM_MODULE_NOOP;
164  }
165 
166  gext = unlang_group_to_subrequest(g);
167  child = state->child = unlang_io_subrequest_alloc(request, gext->dict, UNLANG_DETACHABLE);
168  if (!child) {
169  fail:
170  rcode = RLM_MODULE_FAIL;
171 
172  /*
173  * Pass the result back to the module
174  * that created the subrequest, or
175  * use it to modify the current section
176  * rcode.
177  */
178  if (state->p_result) *state->p_result = rcode;
179 
180  *p_result = rcode;
182  }
183  /*
184  * Set the packet type.
185  */
186  MEM(vp = fr_pair_afrom_da(child->request_ctx, gext->attr_packet_type));
187  if (gext->type_enum) {
188  child->packet->code = vp->vp_uint32 = gext->type_enum->value->vb_uint32;
189  } else {
190  fr_dict_enum_value_t const *type_enum;
191  fr_pair_t *attr;
192 
193  if (tmpl_find_vp(&attr, request, gext->vpt) < 0) {
194  RDEBUG("Failed finding attribute %s", gext->vpt->name);
195  goto fail;
196  }
197 
198  if (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_STRING) {
199  type_enum = fr_dict_enum_by_name(gext->attr_packet_type, attr->vp_strvalue, attr->vp_length);
200  if (!type_enum) {
201  RDEBUG("Unknown Packet-Type %pV", &attr->data);
202  goto fail;
203  }
204 
205  child->packet->code = vp->vp_uint32 = type_enum->value->vb_uint32;
206  } else {
207  fr_value_box_t box;
208 
209  fr_value_box_init(&box, FR_TYPE_UINT32, NULL, false);
210  if (fr_value_box_cast(request, &box, FR_TYPE_UINT32, NULL, &attr->data) < 0) {
211  RDEBUG("Failed casting value from %pV to data type uint32", &attr->data);
212  goto fail;
213  }
214 
215  /*
216  * Check that the value is known to the server.
217  *
218  * If it isn't known, then there's no
219  * "recv foo" section for it and we can't
220  * do anything with this packet.
221  */
222  type_enum = fr_dict_enum_by_value(gext->attr_packet_type, &box);
223  if (!type_enum) {
224  RDEBUG("Invalid value %pV for Packet-Type", &box);
225  goto fail;
226  }
227 
228  child->packet->code = vp->vp_uint32 = box.vb_uint32;
229  }
230 
231  }
232  fr_pair_append(&child->request_pairs, vp);
233 
234  if ((gext->src) && (tmpl_copy_pair_children(child->request_ctx, &child->request_pairs, request, gext->src) < -1)) {
235  RPEDEBUG("Failed copying source attributes into subrequest");
236  goto fail;
237  }
238 
239  /*
240  * Setup the child so it'll inform us when
241  * it resumes, or if it detaches.
242  */
243  if (unlang_subrequest_child_push_resume(child, state) < 0) goto fail;
244 
245  /*
246  * Push the first instruction the child's
247  * going to run.
248  */
249  if (unlang_interpret_push(child, g->children, frame->result,
250  UNLANG_NEXT_SIBLING, UNLANG_SUB_FRAME) < 0) goto fail;
251 
252  state->p_result = p_result;
253  state->detachable = true;
254 
255  /*
256  * Store/restore session information in the subrequest
257  * keyed off the exact subrequest keyword.
258  */
259  state->session.enable = true;
260  state->session.unique_ptr = frame->instruction;
261  state->session.unique_int = 0;
262 
264 
265  return unlang_subrequest_child_run(p_result, request, frame); /* returns UNLANG_ACTION_YIELD */
266 }
267 
268 /** Free a child request, detaching it from its parent and freeing allocated memory
269  *
270  * @param[in] child to free.
271  */
273 {
274  request_detach(*child);
275  talloc_free(*child);
276  *child = NULL;
277 }
278 
279 /** Allocate a subrequest to run through a virtual server at some point in the future
280  *
281  * @param[in] parent to hang sub request off of.
282  * @param[in] namespace the child will operate in.
283  * @return
284  * - A new child request.
285  * - NULL on failure.
286  */
288 {
290 }
291 
292 /** Initialise subrequest ops
293  *
294  */
296 {
298  &(unlang_op_t){
299  .name = "subrequest",
300  .interpret = unlang_subrequest_parent_init,
302  .rcode_set = true,
303  .debug_braces = true,
304  .frame_state_size = sizeof(unlang_frame_state_subrequest_t),
305  .frame_state_type = "unlang_frame_state_subrequest_t",
306  });
307 
308  if (unlang_subrequest_child_op_init() < 0) return -1;
309 
310  return 0;
311 }
312 
314 {
316 }
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_EXECUTE_NEXT
Execute the next unlang_t.
Definition: action.h:38
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition: action.h:37
#define RCSID(id)
Definition: build.h:481
#define UNUSED
Definition: build.h:313
fr_dict_enum_value_t * fr_dict_enum_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
Lookup the structure representing an enum value in a fr_dict_attr_t.
Definition: dict_util.c:3349
fr_value_box_t const * value
Enum value (what name maps to).
Definition: dict.h:230
fr_dict_enum_value_t * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition: dict_util.c:3395
Value of an enumerated attribute.
Definition: dict.h:226
bool unlang_request_is_done(request_t const *request)
Return whether a request has been marked done.
Definition: interpret.c:1329
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
void unlang_interpret_signal(request_t *request, fr_signal_t action)
Send a signal (usually stop) to a request.
Definition: interpret.c:1196
#define UNLANG_SUB_FRAME
Definition: interpret.h:36
Private declarations for the unlang interpreter.
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RPDEBUG(fmt,...)
Definition: log.h:346
#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:63
request_t * unlang_io_subrequest_alloc(request_t *parent, fr_dict_t const *namespace, bool detachable)
Allocate a child request based on the parent.
Definition: io.c:39
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:283
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition: pair.c:2319
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1345
#define RDEBUG(fmt,...)
Definition: radclient.h:53
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition: rcode.h:48
int request_detach(request_t *child)
Unlink a subrequest from its parent.
Definition: request.c:668
int tmpl_find_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt))
Returns the first VP matching a tmpl_t.
Definition: tmpl_eval.c:889
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:812
int tmpl_copy_pair_children(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, tmpl_t const *vpt))
Copy children of pairs matching a tmpl_t in the current request_t.
Definition: tmpl_eval.c:841
fr_signal_t
Definition: signal.h:48
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
static unlang_action_t unlang_subrequest_parent_resume(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Parent being resumed after a child completes.
Definition: subrequest.c:83
request_t * unlang_subrequest_alloc(request_t *parent, fr_dict_t const *namespace)
Allocate a subrequest to run through a virtual server at some point in the future.
Definition: subrequest.c:287
int unlang_subrequest_op_init(void)
Initialise subrequest ops.
Definition: subrequest.c:295
static void unlang_subrequest_signal_child(UNUSED request_t *request, unlang_stack_frame_t *frame, fr_signal_t action)
Send a signal from parent request to subrequest.
Definition: subrequest.c:38
void unlang_subrequest_detach_and_free(request_t **child)
Free a child request, detaching it from its parent and freeing allocated memory.
Definition: subrequest.c:272
static unlang_action_t unlang_subrequest_parent_init(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition: subrequest.c:146
void unlang_subrequest_op_free(void)
Definition: subrequest.c:313
int unique_int
Session unique int identifier.
Definition: subrequest.h:37
void const * unique_ptr
Session unique ptr identifier.
Definition: subrequest.h:36
bool enable
Whether we should store/restore sessions.
Definition: subrequest.h:35
void unlang_subrequest_child_op_free(void)
int unlang_subrequest_child_push_resume(request_t *child, unlang_frame_state_subrequest_t *state)
Push a resumption frame onto a child's stack.
int unlang_subrequest_child_op_init(void)
unlang_action_t unlang_subrequest_child_run(UNUSED rlm_rcode_t *p_result, UNUSED request_t *request, unlang_stack_frame_t *frame)
Function called by the unlang interpreter to start the child running.
tmpl_t * vpt
Value to expand to find the value to place into the packet-type attribute.
unlang_subrequest_session_t session
Session configuration.
fr_dict_attr_t const * attr_packet_type
Packet-type attribute in the subrequest protocol.
request_t * child
Pre-allocated child request.
tmpl_t * src
Pairs to copy into the subrequest request list.
fr_dict_t const * dict
Dictionary of the subrequest protocol.
static unlang_subrequest_t * unlang_group_to_subrequest(unlang_group_t *g)
Cast a group structure to the subrequest keyword extension.
tmpl_t * dst
Where to copy pairs from the reply list in the subrequest to.
fr_dict_enum_value_t const * type_enum
Static enumeration value for attr_packet_type.
rlm_rcode_t * p_result
Where to store the result.
bool detachable
Whether the request can be detached.
Parameters for initialising the subrequest (parent's frame state)
fr_pair_t * tmpl_dcursor_pair_build(fr_pair_t *parent, fr_dcursor_t *cursor, fr_dict_attr_t const *da, UNUSED void *uctx)
Simple pair building callback for use with tmpl_dcursors.
Definition: tmpl_dcursor.c:523
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
Definition: tmpl_dcursor.c:495
#define tmpl_dcursor_build_init(_err, _ctx, _cc, _cursor, _request, _vpt, _build, _uctx)
Definition: tmpl_dcursor.h:103
Maintains state between cursor calls.
Definition: tmpl_dcursor.h:62
Private interpreter structures and functions.
#define UNLANG_NORMAL_CHILD
Definition: unlang_priv.h:96
#define UNLANG_DETACHABLE
Definition: unlang_priv.h:95
#define UNLANG_NEXT_SIBLING
Definition: unlang_priv.h:93
void * state
Stack frame specialisations.
Definition: unlang_priv.h:296
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
Definition: unlang_priv.h:531
@ UNLANG_TYPE_SUBREQUEST
create a child subrequest
Definition: unlang_priv.h:64
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:518
unlang_t const * instruction
The unlang node we're evaluating.
Definition: unlang_priv.h:281
rlm_rcode_t result
The result from executing the instruction.
Definition: unlang_priv.h:300
unlang_t * children
Children beneath this group.
Definition: unlang_priv.h:147
Generic representation of a grouping.
Definition: unlang_priv.h:145
An unlang operation.
Definition: unlang_priv.h:204
Our interpreter stack, as distinct from the C stack.
Definition: unlang_priv.h:280
static fr_slen_t parent
Definition: pair.h:851
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition: value.c:3352
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition: value.h:587