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: bc1a93851ce38ba754e48e0480f4198306411ea1 $
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: bc1a93851ce38ba754e48e0480f4198306411ea1 $")
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  * Forward other signals to the child
55  */
56  unlang_interpret_signal(child, action);
57 }
58 
59 /** Parent being resumed after a child completes
60  *
61  */
63  unlang_stack_frame_t *frame)
64 {
66  unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state,
68  request_t *child = state->child;
69  unlang_subrequest_t *gext;
70 
71  /*
72  * Child detached
73  */
74  if (!state->child) {
75  RDEBUG3("subrequest detached during its execution - Not updating rcode or reply attributes");
76 
77  /*
78  * If the child detached the subrequest section
79  * should become entirely transparent, and
80  * should not update the section rcode.
81  */
83  }
84 
85  RDEBUG3("subrequest complete");
86 
87  /*
88  * If there's a no destination tmpl, we're done.
89  */
90  if (!child->reply) {
93  }
94 
95  /*
96  * Otherwise... copy reply attributes into the
97  * specified destination.
98  */
100  if (gext->dst) {
101  fr_pair_t *vp = NULL;
103  fr_dcursor_t cursor;
104 
105  /*
106  * Use callback to build missing destination container.
107  */
108  vp = tmpl_dcursor_build_init(NULL, request, &cc, &cursor, request, gext->dst, tmpl_dcursor_pair_build, NULL);
109  if (!vp) {
110  RPDEBUG("Discarding subrequest attributes - Failed allocating groups");
111  *p_result = RLM_MODULE_FAIL;
112  tmpl_dcursor_clear(&cc);
114  }
115 
116  MEM(fr_pair_list_copy(vp, &vp->vp_group, &child->reply_pairs) >= 0);
117 
118  tmpl_dcursor_clear(&cc);
119  }
120 
123 }
124 
126  unlang_stack_frame_t *frame)
127 {
128  unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
129  request_t *child;
130  rlm_rcode_t rcode;
131  fr_pair_t *vp;
132 
133  unlang_group_t *g;
134  unlang_subrequest_t *gext;
135 
136  /*
137  * Initialize the state
138  */
140  if (!g->num_children) {
141  *p_result = RLM_MODULE_NOOP;
143  }
144 
145  gext = unlang_group_to_subrequest(g);
146  child = state->child = unlang_io_subrequest_alloc(request, gext->dict, UNLANG_DETACHABLE);
147  if (!child) {
148  fail:
149  rcode = RLM_MODULE_FAIL;
150 
151  /*
152  * Pass the result back to the module
153  * that created the subrequest, or
154  * use it to modify the current section
155  * rcode.
156  */
157  if (state->p_result) *state->p_result = rcode;
158 
159  *p_result = rcode;
161  }
162  /*
163  * Set the packet type.
164  */
165  MEM(vp = fr_pair_afrom_da(child->request_ctx, gext->attr_packet_type));
166  if (gext->type_enum) {
167  child->packet->code = vp->vp_uint32 = gext->type_enum->value->vb_uint32;
168  } else {
169  fr_dict_enum_value_t const *type_enum;
170  fr_pair_t *attr;
171 
172  if (tmpl_find_vp(&attr, request, gext->vpt) < 0) {
173  RDEBUG("Failed finding attribute %s", gext->vpt->name);
174  goto fail;
175  }
176 
177  if (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_STRING) {
178  type_enum = fr_dict_enum_by_name(gext->attr_packet_type, attr->vp_strvalue, attr->vp_length);
179  if (!type_enum) {
180  RDEBUG("Unknown Packet-Type %pV", &attr->data);
181  goto fail;
182  }
183 
184  child->packet->code = vp->vp_uint32 = type_enum->value->vb_uint32;
185  } else {
186  fr_value_box_t box;
187 
188  fr_value_box_init(&box, FR_TYPE_UINT32, NULL, false);
189  if (fr_value_box_cast(request, &box, FR_TYPE_UINT32, NULL, &attr->data) < 0) {
190  RDEBUG("Failed casting value from %pV to data type uint32", &attr->data);
191  goto fail;
192  }
193 
194  /*
195  * Check that the value is known to the server.
196  *
197  * If it isn't known, then there's no
198  * "recv foo" section for it and we can't
199  * do anything with this packet.
200  */
201  type_enum = fr_dict_enum_by_value(gext->attr_packet_type, &box);
202  if (!type_enum) {
203  RDEBUG("Invalid value %pV for Packet-Type", &box);
204  goto fail;
205  }
206 
207  child->packet->code = vp->vp_uint32 = box.vb_uint32;
208  }
209 
210  }
211  fr_pair_append(&child->request_pairs, vp);
212 
213  if ((gext->src) && (tmpl_copy_pair_children(child->request_ctx, &child->request_pairs, request, gext->src) < -1)) {
214  RPEDEBUG("Failed copying source attributes into subrequest");
215  goto fail;
216  }
217 
218  /*
219  * Setup the child so it'll inform us when
220  * it resumes, or if it detaches.
221  */
222  if (unlang_subrequest_child_push_resume(child, state) < 0) goto fail;
223 
224  /*
225  * Push the first instruction the child's
226  * going to run.
227  */
228  if (unlang_interpret_push(child, g->children, frame->result,
229  UNLANG_NEXT_SIBLING, UNLANG_SUB_FRAME) < 0) goto fail;
230 
231  state->p_result = p_result;
232  state->detachable = true;
233 
234  /*
235  * Store/restore session information in the subrequest
236  * keyed off the exact subrequest keyword.
237  */
238  state->session.enable = true;
239  state->session.unique_ptr = frame->instruction;
240  state->session.unique_int = 0;
241 
243 
244  return unlang_subrequest_child_run(p_result, request, frame); /* returns UNLANG_ACTION_YIELD */
245 }
246 
247 /** Free a child request, detaching it from its parent and freeing allocated memory
248  *
249  * @param[in] child to free.
250  */
252 {
253  request_detach(*child);
254  talloc_free(*child);
255  *child = NULL;
256 }
257 
258 /** Allocate a subrequest to run through a virtual server at some point in the future
259  *
260  * @param[in] parent to hang sub request off of.
261  * @param[in] namespace the child will operate in.
262  * @return
263  * - A new child request.
264  * - NULL on failure.
265  */
267 {
269 }
270 
271 /** Initialise subrequest ops
272  *
273  */
275 {
277  &(unlang_op_t){
278  .name = "subrequest",
279  .interpret = unlang_subrequest_parent_init,
281  .debug_braces = true,
282  .frame_state_size = sizeof(unlang_frame_state_subrequest_t),
283  .frame_state_type = "unlang_frame_state_subrequest_t",
284  });
285 
286  if (unlang_subrequest_child_op_init() < 0) return -1;
287 
288  return 0;
289 }
290 
292 {
294 }
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:444
#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:2946
fr_value_box_t const * value
Enum value (what name maps to).
Definition: dict.h:213
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:2992
Value of an enumerated attribute.
Definition: dict.h:209
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:1184
#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:65
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:278
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:2316
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:1340
#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:664
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:887
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:796
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:839
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:62
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:266
int unlang_subrequest_op_init(void)
Initialise subrequest ops.
Definition: subrequest.c:274
static void unlang_subrequest_parent_signal(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:251
static unlang_action_t unlang_subrequest_parent_init(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition: subrequest.c:125
void unlang_subrequest_op_free(void)
Definition: subrequest.c:291
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:447
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
Definition: tmpl_dcursor.c:419
#define tmpl_dcursor_build_init(_err, _ctx, _cc, _cursor, _request, _vpt, _build, _uctx)
Definition: tmpl_dcursor.h:102
Maintains state between cursor calls.
Definition: tmpl_dcursor.h:61
Private interpreter structures and functions.
#define UNLANG_NORMAL_CHILD
Definition: unlang_priv.h:106
#define UNLANG_DETACHABLE
Definition: unlang_priv.h:105
#define UNLANG_NEXT_SIBLING
Definition: unlang_priv.h:103
void * state
Stack frame specialisations.
Definition: unlang_priv.h:304
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
Definition: unlang_priv.h:539
@ UNLANG_TYPE_SUBREQUEST
create a child subrequest
Definition: unlang_priv.h:74
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
rlm_rcode_t result
The result from executing the instruction.
Definition: unlang_priv.h:308
unlang_t * children
Children beneath this group.
Definition: unlang_priv.h:157
Generic representation of a grouping.
Definition: unlang_priv.h:155
An unlang operation.
Definition: unlang_priv.h:214
Our interpreter stack, as distinct from the C stack.
Definition: unlang_priv.h:288
static fr_slen_t parent
Definition: pair.h:844
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:3301
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition: value.h:574