/src/freeradius-server/src/lib/unlang/caller.c
Line | Count | Source |
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: 9ef32bc60d0bd2bb89611ca29ce73ed35271d872 $ |
19 | | * |
20 | | * @file unlang/caller.c |
21 | | * @brief Unlang "caller" keyword evaluation. Used for setting allowed parent protocols |
22 | | * |
23 | | * @copyright 2020 The FreeRADIUS server project |
24 | | * @copyright 2021 Arran Cudbard-Bell <a.cudbardb@freeradius.org> |
25 | | */ |
26 | | RCSID("$Id: 9ef32bc60d0bd2bb89611ca29ce73ed35271d872 $") |
27 | | |
28 | | #include <freeradius-devel/server/state.h> |
29 | | |
30 | | #include "caller_priv.h" |
31 | | #include "group_priv.h" |
32 | | |
33 | | static unlang_action_t unlang_caller(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame) |
34 | 0 | { |
35 | 0 | unlang_group_t *g = unlang_generic_to_group(frame->instruction); |
36 | 0 | unlang_caller_t *gext = unlang_group_to_caller(g); |
37 | |
|
38 | 0 | fr_assert(!unlang_list_empty(&g->children)); /* otherwise the compilation is broken */ |
39 | | |
40 | | /* |
41 | | * No parent, or the dictionaries don't match. Ignore it. |
42 | | */ |
43 | 0 | if (!request->parent || (request->parent->proto_dict != gext->dict)) { |
44 | 0 | RDEBUG2("... parent does not match 'caller %s', ignoring contents ...", fr_dict_root(gext->dict)->name); |
45 | 0 | return UNLANG_ACTION_EXECUTE_NEXT; |
46 | 0 | } |
47 | | |
48 | | /* |
49 | | * The dictionary matches. Go recurse into its children. |
50 | | */ |
51 | 0 | return unlang_group(p_result, request, frame); |
52 | 0 | } |
53 | | |
54 | | |
55 | | static unlang_t *unlang_compile_caller(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci) |
56 | 0 | { |
57 | 0 | CONF_SECTION *cs = cf_item_to_section(ci); |
58 | 0 | unlang_t *c; |
59 | |
|
60 | 0 | unlang_group_t *g; |
61 | 0 | unlang_caller_t *gext; |
62 | |
|
63 | 0 | fr_token_t type; |
64 | 0 | char const *name; |
65 | 0 | fr_dict_t const *dict; |
66 | 0 | unlang_compile_ctx_t unlang_ctx2; |
67 | 0 | tmpl_rules_t parent_rules, t_rules; |
68 | |
|
69 | 0 | fr_dict_autoload_talloc_t *dict_ref = NULL; |
70 | |
|
71 | 0 | name = cf_section_name2(cs); |
72 | 0 | if (!name) { |
73 | 0 | cf_log_err(cs, "You MUST specify a protocol name for 'caller <protocol> { ... }'"); |
74 | 0 | print_url: |
75 | 0 | cf_log_err(ci, DOC_KEYWORD_REF(caller)); |
76 | 0 | return NULL; |
77 | 0 | } |
78 | | |
79 | 0 | type = cf_section_name2_quote(cs); |
80 | 0 | if (type != T_BARE_WORD) { |
81 | 0 | cf_log_err(cs, "The argument to 'caller' cannot be a quoted string or a dynamic value"); |
82 | 0 | goto print_url; |
83 | 0 | } |
84 | | |
85 | 0 | dict = fr_dict_by_protocol_name(name); |
86 | 0 | if (!dict) { |
87 | 0 | dict_ref = fr_dict_autoload_talloc(NULL, &dict, name); |
88 | 0 | if (!dict_ref) { |
89 | 0 | cf_log_perr(cs, "Unknown protocol '%s'", name); |
90 | 0 | goto print_url; |
91 | 0 | } |
92 | 0 | } |
93 | | |
94 | | /* |
95 | | * Create a new parent context with the new dictionary. |
96 | | */ |
97 | 0 | memcpy(&parent_rules, unlang_ctx->rules, sizeof(parent_rules)); |
98 | 0 | memcpy(&t_rules, unlang_ctx->rules, sizeof(t_rules)); |
99 | 0 | parent_rules.attr.dict_def = dict; |
100 | 0 | t_rules.parent = &parent_rules; |
101 | | |
102 | | /* |
103 | | * We don't want to modify the context we were passed, so |
104 | | * we just clone it |
105 | | */ |
106 | 0 | memcpy(&unlang_ctx2, unlang_ctx, sizeof(unlang_ctx2)); |
107 | 0 | unlang_ctx2.rules = &t_rules; |
108 | 0 | unlang_ctx2.section_name1 = "caller"; |
109 | 0 | unlang_ctx2.section_name2 = name; |
110 | |
|
111 | 0 | c = unlang_compile_section(parent, &unlang_ctx2, cs, UNLANG_TYPE_CALLER); |
112 | 0 | if (!c) { |
113 | 0 | talloc_free(dict_ref); |
114 | 0 | return NULL; |
115 | 0 | } |
116 | | |
117 | | /* |
118 | | * Set the virtual server name, which tells unlang_call() |
119 | | * which virtual server to call. |
120 | | */ |
121 | 0 | g = unlang_generic_to_group(c); |
122 | 0 | gext = unlang_group_to_caller(g); |
123 | 0 | gext->dict = dict; |
124 | |
|
125 | 0 | if (dict_ref) { |
126 | | /* |
127 | | * Parent the dictionary reference correctly now that we |
128 | | * have the section with the dependency. This should |
129 | | * be fast as dict_ref has no siblings. |
130 | | */ |
131 | 0 | talloc_steal(gext, dict_ref); |
132 | 0 | } |
133 | |
|
134 | 0 | if (unlang_list_empty(&g->children)) { |
135 | 0 | talloc_free(c); |
136 | 0 | return UNLANG_IGNORE; |
137 | 0 | } |
138 | | |
139 | 0 | return c; |
140 | 0 | } |
141 | | |
142 | | void unlang_caller_init(void) |
143 | 4 | { |
144 | 4 | unlang_register(&(unlang_op_t){ |
145 | 4 | .name = "caller", |
146 | 4 | .type = UNLANG_TYPE_CALLER, |
147 | 4 | .flag = UNLANG_OP_FLAG_DEBUG_BRACES, |
148 | | |
149 | 4 | .compile = unlang_compile_caller, |
150 | 4 | .interpret = unlang_caller, |
151 | | |
152 | 4 | .unlang_size = sizeof(unlang_caller_t), |
153 | 4 | .unlang_name = "unlang_caller_t", |
154 | 4 | }); |
155 | 4 | } |