1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25 #include <scope.h>
26
27 #include <vars.h>
28 #include <expand.h>
29 #include <unix.h>
30 #include <fncall.h>
31 #include <mutex.h>
32 #include <misc_lib.h>
33 #include <rlist.h>
34 #include <conversion.h>
35 #include <syntax.h>
36 #include <policy.h>
37 #include <eval_context.h>
38 #include <audit.h>
39
40 /*******************************************************************/
41
SpecialScopeToString(SpecialScope scope)42 const char *SpecialScopeToString(SpecialScope scope)
43 {
44 switch (scope)
45 {
46 case SPECIAL_SCOPE_CONST:
47 return "const";
48 case SPECIAL_SCOPE_EDIT:
49 return "edit";
50 case SPECIAL_SCOPE_MATCH:
51 return "match";
52 case SPECIAL_SCOPE_MON:
53 return "mon";
54 case SPECIAL_SCOPE_SYS:
55 return "sys";
56 case SPECIAL_SCOPE_DEF:
57 return "def";
58 case SPECIAL_SCOPE_THIS:
59 return "this";
60 case SPECIAL_SCOPE_BODY:
61 return "body";
62 default:
63 ProgrammingError("Unhandled special scope");
64 }
65 }
66
SpecialScopeFromString(const char * scope)67 SpecialScope SpecialScopeFromString(const char *scope)
68 {
69 if (scope == NULL)
70 {
71 return SPECIAL_SCOPE_NONE;
72 }
73 else if (strcmp("const", scope) == 0)
74 {
75 return SPECIAL_SCOPE_CONST;
76 }
77 else if (strcmp("edit", scope) == 0)
78 {
79 return SPECIAL_SCOPE_EDIT;
80 }
81 else if (strcmp("match", scope) == 0)
82 {
83 return SPECIAL_SCOPE_MATCH;
84 }
85 else if (strcmp("mon", scope) == 0)
86 {
87 return SPECIAL_SCOPE_MON;
88 }
89 else if (strcmp("sys", scope) == 0)
90 {
91 return SPECIAL_SCOPE_SYS;
92 }
93 else if (strcmp("def", scope) == 0)
94 {
95 return SPECIAL_SCOPE_DEF;
96 }
97 else if (strcmp("this", scope) == 0)
98 {
99 return SPECIAL_SCOPE_THIS;
100 }
101 else if (strcmp("body", scope) == 0)
102 {
103 return SPECIAL_SCOPE_BODY;
104 }
105
106 /* All other scopes fall here, for example all bundle names. It means that
107 * scope was not special. */
108 return SPECIAL_SCOPE_NONE;
109 }
110
ScopeAugment(EvalContext * ctx,const Bundle * bp,const Promise * pp,const Rlist * arguments)111 void ScopeAugment(EvalContext *ctx, const Bundle *bp, const Promise *pp, const Rlist *arguments)
112 {
113 if (RlistLen(bp->args) != RlistLen(arguments))
114 {
115 Log(LOG_LEVEL_ERR, "While constructing scope '%s'", bp->name);
116 fprintf(stderr, "Formal = ");
117 {
118 Writer *w = FileWriter(stderr);
119 RlistWrite(w, bp->args);
120 FileWriterDetach(w);
121 }
122 fprintf(stderr, ", Actual = ");
123 {
124 Writer *w = FileWriter(stderr);
125 RlistWrite(w, arguments);
126 FileWriterDetach(w);
127 }
128 fprintf(stderr, "\n");
129 FatalError(ctx, "Augment scope, formal and actual parameter mismatch is fatal");
130 }
131
132 const Bundle *pbp = NULL;
133 if (pp != NULL)
134 {
135 pbp = PromiseGetBundle(pp);
136 }
137
138 for (const Rlist *rpl = bp->args, *rpr = arguments; rpl != NULL; rpl = rpl->next, rpr = rpr->next)
139 {
140 const char *lval = RlistScalarValue(rpl);
141
142 Log(LOG_LEVEL_VERBOSE, "V: + Private parameter: '%s' in scope '%s' (type: %c) in pass %d", lval, bp->name, rpr->val.type, EvalContextGetPass(ctx));
143
144 // CheckBundleParameters() already checked that there is no namespace collision
145 // By this stage all functions should have been expanded, so we only have scalars left
146
147 if (rpr->val.type == RVAL_TYPE_SCALAR && IsNakedVar(RlistScalarValue(rpr), '@'))
148 {
149
150 char naked[CF_BUFSIZE];
151
152 GetNaked(naked, RlistScalarValue(rpr));
153
154 DataType value_type;
155 const void *value;
156 if (pbp != NULL)
157 {
158 VarRef *ref = VarRefParseFromBundle(naked, pbp);
159 value = EvalContextVariableGet(ctx, ref, &value_type);
160 VarRefDestroy(ref);
161 }
162 else
163 {
164 VarRef *ref = VarRefParseFromBundle(naked, bp);
165 value = EvalContextVariableGet(ctx, ref, &value_type);
166 VarRefDestroy(ref);
167 }
168
169 switch (value_type)
170 {
171 case CF_DATA_TYPE_STRING_LIST:
172 case CF_DATA_TYPE_INT_LIST:
173 case CF_DATA_TYPE_REAL_LIST:
174 {
175 VarRef *ref = VarRefParseFromBundle(lval, bp);
176 EvalContextVariablePut(ctx, ref, value, CF_DATA_TYPE_STRING_LIST, "source=promise");
177 VarRefDestroy(ref);
178 }
179 break;
180 case CF_DATA_TYPE_CONTAINER:
181 {
182 VarRef *ref = VarRefParseFromBundle(lval, bp);
183 EvalContextVariablePut(ctx, ref, value, CF_DATA_TYPE_CONTAINER, "source=promise");
184 VarRefDestroy(ref);
185 }
186 break;
187 default:
188 {
189 Log(LOG_LEVEL_ERR, "List or container parameter '%s' not found while constructing scope '%s' - use @(scope.variable) in calling reference", naked, bp->name);
190 VarRef *ref = VarRefParseFromBundle(lval, bp);
191 EvalContextVariablePut(ctx, ref, RlistScalarValue(rpr), CF_DATA_TYPE_STRING, "source=promise");
192 VarRefDestroy(ref);
193 }
194 break;
195 }
196 }
197 else
198 {
199 switch(rpr->val.type)
200 {
201 case RVAL_TYPE_SCALAR:
202 {
203 VarRef *ref = VarRefParseFromBundle(lval, bp);
204 EvalContextVariablePut(ctx, ref, RvalScalarValue(rpr->val), CF_DATA_TYPE_STRING, "source=promise");
205 VarRefDestroy(ref);
206 }
207 break;
208
209 case RVAL_TYPE_FNCALL:
210 {
211 FnCall *subfp = RlistFnCallValue(rpr);
212 Rval rval = FnCallEvaluate(ctx, PromiseGetPolicy(pp), subfp, pp).rval;
213 if (rval.type == RVAL_TYPE_SCALAR)
214 {
215 VarRef *ref = VarRefParseFromBundle(lval, bp);
216 EvalContextVariablePut(ctx, ref, RvalScalarValue(rval), CF_DATA_TYPE_STRING, "source=promise");
217 VarRefDestroy(ref);
218 }
219 else
220 {
221 Log(LOG_LEVEL_ERR, "Only functions returning scalars can be used as arguments");
222 }
223 RvalDestroy(rval);
224 }
225 break;
226 default:
227 ProgrammingError("An argument neither a scalar nor a list seemed to appear. Impossible");
228 }
229 }
230 }
231
232 /* Check that there are no danglers left to evaluate in the hash table itself */
233
234 return;
235 }
236
237
ScopeMapBodyArgs(EvalContext * ctx,const Body * body,const Rlist * args)238 void ScopeMapBodyArgs(EvalContext *ctx, const Body *body, const Rlist *args)
239 {
240 const Rlist *arg = NULL;
241 const Rlist *param = NULL;
242
243 for (arg = args, param = body->args; arg != NULL && param != NULL; arg = arg->next, param = param->next)
244 {
245 DataType arg_type = CF_DATA_TYPE_NONE;
246 switch (arg->val.type)
247 {
248 case RVAL_TYPE_SCALAR:
249 arg_type = StringDataType(ctx, RlistScalarValue(arg));
250 break;
251
252 case RVAL_TYPE_FNCALL:
253 {
254 const FnCallType *fn = FnCallTypeGet(RlistFnCallValue(arg)->name);
255 if (!fn)
256 {
257 FatalError(ctx, "Argument '%s' given to body '%s' is not a valid function",
258 RlistFnCallValue(arg)->name, body->name);
259 }
260 arg_type = fn->dtype;
261 }
262 break;
263
264 default:
265 FatalError(ctx, "Cannot derive data type from Rval type %c", arg->val.type);
266 }
267
268 switch (arg->val.type)
269 {
270 case RVAL_TYPE_SCALAR:
271 {
272 const char *lval = RlistScalarValue(param);
273 VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.');
274 EvalContextVariablePut(ctx, ref, RvalScalarValue(arg->val), arg_type, "source=body");
275 VarRefDestroy(ref);
276 }
277 break;
278
279 case RVAL_TYPE_LIST:
280 {
281 const char *lval = RlistScalarValue(param);
282 VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.');
283 EvalContextVariablePut(ctx, ref, RvalRlistValue(arg->val), arg_type, "source=body");
284 VarRefDestroy(ref);
285 }
286 break;
287
288 case RVAL_TYPE_FNCALL:
289 {
290 FnCall *fp = RlistFnCallValue(arg);
291 arg_type = CF_DATA_TYPE_NONE;
292 {
293 const FnCallType *fncall_type = FnCallTypeGet(fp->name);
294 if (fncall_type)
295 {
296 arg_type = fncall_type->dtype;
297 }
298 }
299
300 FnCallResult res = FnCallEvaluate(ctx, body->parent_policy, fp, NULL);
301
302 if (res.status == FNCALL_FAILURE && THIS_AGENT_TYPE != AGENT_TYPE_COMMON)
303 {
304 Log(LOG_LEVEL_VERBOSE, "Embedded function argument does not resolve to a name - probably too many evaluation levels for '%s'",
305 fp->name);
306 }
307 else
308 {
309 const char *lval = RlistScalarValue(param);
310 void *rval = res.rval.item;
311
312 VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.');
313 EvalContextVariablePut(ctx, ref, rval, arg_type, "source=body");
314 VarRefDestroy(ref);
315 }
316
317 RvalDestroy(res.rval);
318 }
319
320 break;
321
322 default:
323 /* Nothing else should happen */
324 ProgrammingError("Software error: something not a scalar/function in argument literal");
325 }
326 }
327 }
328
329 /*******************************************************************/
330 /* Utility functions */
331 /*******************************************************************/
332
JoinScopeName(const char * ns,const char * bundle,char scope_out[CF_MAXVARSIZE])333 void JoinScopeName(const char *ns, const char *bundle, char scope_out[CF_MAXVARSIZE])
334 {
335 assert(bundle);
336
337 if (ns)
338 {
339 snprintf(scope_out, CF_MAXVARSIZE, "%s%c%s", ns, CF_NS, bundle);
340 }
341 else
342 {
343 snprintf(scope_out, CF_MAXVARSIZE, "%s", bundle);
344 }
345 }
346