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