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 <var_expressions.h>
26 
27 #include <cf3.defs.h>
28 #include <buffer.h>
29 #include <misc_lib.h>
30 #include <string_lib.h>
31 #include <scope.h>
32 
33 // This is not allowed to be the part of VarRef.indices so looks safe
34 // to be used as multi array indices separator while hashing.
35 #define ARRAY_SEPARATOR_HASH ']'
36 
VarRefHash(const VarRef * ref)37 static unsigned VarRefHash(const VarRef *ref)
38 {
39     unsigned int h = 0;
40 
41     if (VarRefIsQualified(ref))
42     {
43         if (ref->ns)
44         {
45             for (int i = 0; ref->ns[i] != '\0'; i++)
46             {
47                 h += ref->ns[i];
48                 h += (h << 10);
49                 h ^= (h >> 6);
50             }
51         }
52         else
53         {
54             h = 1195645448; // hash of "default"
55         }
56 
57         int len = strlen(ref->scope);
58         for (int i = 0; i < len; i++)
59         {
60             h += ref->scope[i];
61             h += (h << 10);
62             h ^= (h >> 6);
63         }
64     }
65 
66     for (int i = 0; ref->lval[i] != '\0'; i++)
67     {
68         h += ref->lval[i];
69         h += (h << 10);
70         h ^= (h >> 6);
71     }
72 
73     for (size_t k = 0; k < ref->num_indices; k++)
74     {
75         // Fixing multi index arrays hashing collisions - Redmine 6674
76         // Multi index arrays with indexes expanded to the same string
77         // (e.g. v[te][st], v[t][e][s][t]) will not be hashed to the same value.
78         h += ARRAY_SEPARATOR_HASH;
79         h += (h << 10);
80         h ^= (h >> 6);
81 
82         for (int i = 0; ref->indices[k][i] != '\0'; i++)
83         {
84             h += ref->indices[k][i];
85             h += (h << 10);
86             h ^= (h >> 6);
87         }
88     }
89 
90     h += (h << 3);
91     h ^= (h >> 11);
92     h += (h << 15);
93 
94     return h;
95 }
96 
VarRefHash_untyped(const void * ref,unsigned int seed ARG_UNUSED)97 unsigned int VarRefHash_untyped(const void *ref,
98                                 unsigned int seed ARG_UNUSED)
99 {
100     return VarRefHash(ref);
101 }
102 
VarRefConst(const char * ns,const char * scope,const char * lval)103 VarRef VarRefConst(const char *ns, const char *scope, const char *lval)
104 {
105     VarRef ref;
106 
107     ref.ns = (char *)ns;
108     ref.scope = (char *)scope;
109     ref.lval = (char *)lval;
110     ref.num_indices = 0;
111     ref.indices = NULL;
112 
113     return ref;
114 }
115 
VarRefCopy(const VarRef * ref)116 VarRef *VarRefCopy(const VarRef *ref)
117 {
118     VarRef *copy = xmalloc(sizeof(VarRef));
119 
120     copy->ns = ref->ns ? xstrdup(ref->ns) : NULL;
121     copy->scope = ref->scope ? xstrdup(ref->scope) : NULL;
122     copy->lval = ref->lval ? xstrdup(ref->lval) : NULL;
123 
124     copy->num_indices = ref->num_indices;
125     if (ref->num_indices > 0)
126     {
127         copy->indices = xmalloc(ref->num_indices * sizeof(char*));
128         for (size_t i = 0; i < ref->num_indices; i++)
129         {
130             copy->indices[i] = xstrdup(ref->indices[i]);
131         }
132     }
133     else
134     {
135         copy->indices = NULL;
136     }
137 
138     return copy;
139 }
140 
VarRefCopyLocalized(const VarRef * ref)141 VarRef *VarRefCopyLocalized(const VarRef *ref)
142 {
143     VarRef *copy = xmalloc(sizeof(VarRef));
144 
145     copy->ns = NULL;
146     copy->scope = xstrdup("this");
147     copy->lval = ref->lval ? xstrdup(ref->lval) : NULL;
148 
149     copy->num_indices = ref->num_indices;
150     if (ref->num_indices > 0)
151     {
152         copy->indices = xmalloc(ref->num_indices * sizeof(char*));
153         for (size_t i = 0; i < ref->num_indices; i++)
154         {
155             copy->indices[i] = xstrdup(ref->indices[i]);
156         }
157     }
158     else
159     {
160         copy->indices = NULL;
161     }
162 
163     return copy;
164 }
165 
VarRefCopyIndexless(const VarRef * ref)166 VarRef *VarRefCopyIndexless(const VarRef *ref)
167 {
168     VarRef *copy = xmalloc(sizeof(VarRef));
169 
170     copy->ns = ref->ns ? xstrdup(ref->ns) : NULL;
171     copy->scope = ref->scope ? xstrdup(ref->scope) : NULL;
172     copy->lval = ref->lval ? xstrdup(ref->lval) : NULL;
173     copy->num_indices = 0;
174     copy->indices = NULL;
175 
176     return copy;
177 }
178 
179 
180 
IndexBracketsBalance(const char * var_string)181 static bool IndexBracketsBalance(const char *var_string)
182 {
183     int count = 0;
184     for (const char *c = var_string; *c != '\0'; c++)
185     {
186         if (*c == '[')
187         {
188             count++;
189         }
190         if (*c == ']')
191         {
192             count--;
193         }
194     }
195 
196     return count == 0;
197 }
198 
199 
IndexCount(const char * var_string)200 static size_t IndexCount(const char *var_string)
201 {
202     size_t count = 0;
203     size_t level = 0;
204 
205     for (const char *c = var_string; *c != '\0'; c++)
206     {
207         if (*c == '[')
208         {
209             if (level == 0)
210             {
211                 count++;
212             }
213             level++;
214         }
215         if (*c == ']')
216         {
217             level--;
218         }
219     }
220 
221     return count;
222 }
223 
VarRefParseFromNamespaceAndScope(const char * qualified_name,const char * _ns,const char * _scope,char ns_separator,char scope_separator)224 VarRef *VarRefParseFromNamespaceAndScope(const char *qualified_name,
225                                          const char *_ns, const char *_scope,
226                                          char ns_separator, char scope_separator)
227 {
228     assert(qualified_name);
229     char *ns = NULL;
230 
231     const char *indices_start = strchr(qualified_name, '[');
232 
233     const char *scope_start = strchr(qualified_name, ns_separator);
234     if (scope_start && (!indices_start || scope_start < indices_start))
235     {
236         ns = xstrndup(qualified_name, scope_start - qualified_name);
237         scope_start++;
238     }
239     else
240     {
241         scope_start = qualified_name;
242     }
243 
244     char *scope = NULL;
245 
246     const char *lval_start = strchr(scope_start, scope_separator);
247 
248     if (lval_start && (!indices_start || lval_start < indices_start))
249     {
250         lval_start++;
251         scope = xstrndup(scope_start, lval_start - scope_start - 1);
252     }
253     else
254     {
255         lval_start = scope_start;
256     }
257 
258     char *lval = NULL;
259     char **indices = NULL;
260     size_t num_indices = 0;
261 
262     if (indices_start)
263     {
264         indices_start++;
265         lval = xstrndup(lval_start, indices_start - lval_start - 1);
266 
267         if (!IndexBracketsBalance(indices_start - 1))
268         {
269             Log(LOG_LEVEL_ERR, "Broken variable expression, index brackets do not balance, in '%s'", qualified_name);
270         }
271         else
272         {
273             num_indices = IndexCount(indices_start - 1);
274             indices = xmalloc(num_indices * sizeof(char *));
275 
276             Buffer *buf = BufferNew();
277             size_t cur_index = 0;
278             size_t open_count = 1;
279 
280             for (const char *c = indices_start; *c != '\0'; c++)
281             {
282                 if (*c == '[')
283                 {
284                     if (open_count++ == 0)
285                     {
286                         cur_index++;
287                         continue;
288                     }
289                 }
290                 else if (*c == ']')
291                 {
292                     if (open_count-- == 1)
293                     {
294                         indices[cur_index] = xstrdup(BufferData(buf));
295                         BufferClear(buf);
296                         continue;
297                     }
298                 }
299 
300                 BufferAppend(buf, c, sizeof(char));
301             }
302             BufferDestroy(buf);
303         }
304     }
305     else
306     {
307         lval = xstrdup(lval_start);
308     }
309 
310     assert(lval);
311 
312     if (scope)
313     {
314         if (SpecialScopeFromString(scope) != SPECIAL_SCOPE_NONE)
315         {
316             _ns = NULL;
317         }
318 
319         /*
320          * Force considering non-special "this." variables as unqualified.
321          * This allows qualifying bundle parameters passed as reference with a "this" scope
322          * in the calling bundle.
323          */
324         if (is_this_not_special(scope, lval)) {
325             free(scope);
326             scope = NULL;
327         }
328     }
329 
330     VarRef *ref = xmalloc(sizeof(VarRef));
331 
332     ref->ns = ns ? ns : (_ns ? xstrdup(_ns) : NULL);
333     ref->scope = scope ? scope : (_scope ? xstrdup(_scope) : NULL);
334     ref->lval = lval;
335     ref->indices = indices;
336     ref->num_indices = num_indices;
337 
338     return ref;
339 }
340 
341 /*
342  * This function will return true if the given variable is
343  * a this.something variable that is an alias to a non-special local variable.
344  */
is_this_not_special(const char * scope,const char * lval)345 bool is_this_not_special(const char *scope, const char *lval) {
346     // TODO: better way to get this list?
347     const char *special_this_variables[] = {"v","k","this","service_policy","promiser","promiser_uid","promiser_gid","promiser_pid","promiser_ppid","bundle","handle","namespace","promise_filename","promise_dirname","promise_linenumber", NULL};
348 
349     if (!scope) {
350         return false;
351     }
352 
353     if (SpecialScopeFromString(scope) != SPECIAL_SCOPE_THIS) {
354         return false;
355     }
356 
357     if (IsStrIn(lval, special_this_variables)) {
358         return false;
359     }
360 
361     return true;
362 }
363 
VarRefParse(const char * var_ref_string)364 VarRef *VarRefParse(const char *var_ref_string)
365 {
366     return VarRefParseFromNamespaceAndScope(var_ref_string, NULL, NULL, CF_NS, '.');
367 }
368 
VarRefParseFromScope(const char * var_ref_string,const char * scope)369 VarRef *VarRefParseFromScope(const char *var_ref_string, const char *scope)
370 {
371     if (!scope)
372     {
373         return VarRefParseFromNamespaceAndScope(var_ref_string, NULL, NULL, CF_NS, '.');
374     }
375 
376     const char *scope_start = strchr(scope, CF_NS);
377     if (scope_start)
378     {
379         char *ns = xstrndup(scope, scope_start - scope);
380         VarRef *ref = VarRefParseFromNamespaceAndScope(var_ref_string, ns, scope_start + 1, CF_NS, '.');
381         free(ns);
382         return ref;
383     }
384     else
385     {
386         return VarRefParseFromNamespaceAndScope(var_ref_string, NULL, scope, CF_NS, '.');
387     }
388 }
389 
390 /**
391  * @brief Parse the variable reference in the context of a bundle. This means
392  *        that the VarRef will inherit scope and namespace of the bundle if
393  *        these are not specified explicitly in the string.
394  */
VarRefParseFromBundle(const char * var_ref_string,const Bundle * bundle)395 VarRef *VarRefParseFromBundle(const char *var_ref_string, const Bundle *bundle)
396 {
397     if (bundle)
398     {
399         return VarRefParseFromNamespaceAndScope(var_ref_string,
400                                                 bundle->ns, bundle->name,
401                                                 CF_NS, '.');
402     }
403     else
404     {
405         return VarRefParse(var_ref_string);
406     }
407 }
408 
VarRefDestroy(VarRef * ref)409 void VarRefDestroy(VarRef *ref)
410 {
411     if (ref)
412     {
413         free(ref->ns);
414         free(ref->scope);
415         free(ref->lval);
416         if (ref->num_indices > 0)
417         {
418             for (size_t i = 0; i < ref->num_indices; ++i)
419             {
420                 free(ref->indices[i]);
421             }
422             free(ref->indices);
423         }
424 
425         free(ref);
426     }
427 
428 }
429 
VarRefDestroy_untyped(void * ref)430 void VarRefDestroy_untyped(void *ref)
431 {
432     VarRefDestroy(ref);
433 }
434 
VarRefToString(const VarRef * ref,bool qualified)435 char *VarRefToString(const VarRef *ref, bool qualified)
436 {
437     assert(ref->lval);
438 
439     Buffer *buf = BufferNew();
440     if (qualified && VarRefIsQualified(ref))
441     {
442         const char *ns = ref->ns ? ref->ns : "default";
443 
444         BufferAppend(buf, ns, strlen(ns));
445         BufferAppend(buf, ":", sizeof(char));
446         BufferAppend(buf, ref->scope, strlen(ref->scope));
447         BufferAppend(buf, ".", sizeof(char));
448     }
449 
450     BufferAppend(buf, ref->lval, strlen(ref->lval));
451 
452     for (size_t i = 0; i < ref->num_indices; i++)
453     {
454         BufferAppend(buf, "[", sizeof(char));
455         BufferAppend(buf, ref->indices[i], strlen(ref->indices[i]));
456         BufferAppend(buf, "]", sizeof(char));
457     }
458 
459     return BufferClose(buf);
460 }
461 
VarRefMangle(const VarRef * ref)462 char *VarRefMangle(const VarRef *ref)
463 {
464     char *suffix = VarRefToString(ref, false);
465 
466     if (!ref->scope)
467     {
468         return suffix;
469     }
470     else
471     {
472         if (ref->ns)
473         {
474             char *mangled = StringFormat("%s*%s#%s", ref->ns, ref->scope, suffix);
475             free(suffix);
476             return mangled;
477         }
478         else
479         {
480             char *mangled = StringFormat("%s#%s", ref->scope, suffix);
481             free(suffix);
482             return mangled;
483         }
484     }
485 }
486 
VarRefDeMangle(const char * mangled_var_ref)487 VarRef *VarRefDeMangle(const char *mangled_var_ref)
488 {
489     return VarRefParseFromNamespaceAndScope(mangled_var_ref, NULL, NULL,
490                                             CF_MANGLED_NS, CF_MANGLED_SCOPE);
491 }
492 
VarRefIsMeta(VarRef * ref)493 static bool VarRefIsMeta(VarRef *ref)
494 {
495     return StringEndsWith(ref->scope, "_meta");
496 }
497 
VarRefSetMeta(VarRef * ref,bool enabled)498 void VarRefSetMeta(VarRef *ref, bool enabled)
499 {
500     if (enabled)
501     {
502         if (!VarRefIsMeta(ref))
503         {
504             char *tmp = StringConcatenate(2, ref->scope, "_meta");
505             free(ref->scope);
506             ref->scope = tmp;
507         }
508     }
509     else
510     {
511         if (VarRefIsMeta(ref))
512         {
513             char *tmp = ref->scope;
514             size_t len = strlen(ref->scope);
515             memcpy(ref->scope, StringSubstring(ref->scope, len, 0, len - strlen("_meta")), len - strlen("_meta"));
516             free(tmp);
517         }
518     }
519 }
520 
VarRefIsQualified(const VarRef * ref)521 bool VarRefIsQualified(const VarRef *ref)
522 {
523     return ref->scope != NULL;
524 }
525 
VarRefQualify(VarRef * ref,const char * ns,const char * scope)526 void VarRefQualify(VarRef *ref, const char *ns, const char *scope)
527 {
528     assert(scope);
529 
530     free(ref->ns);
531     ref->ns = NULL;
532 
533     free(ref->scope);
534     ref->scope = NULL;
535 
536     ref->ns = ns ? xstrdup(ns) : NULL;
537     ref->scope = xstrdup(scope);
538 }
539 
VarRefAddIndex(VarRef * ref,const char * index)540 void VarRefAddIndex(VarRef *ref, const char *index)
541 {
542     if (ref->indices)
543     {
544         assert(ref->num_indices > 0);
545         ref->indices = xrealloc(ref->indices, sizeof(char *) * (ref->num_indices + 1));
546     }
547     else
548     {
549         assert(ref->num_indices == 0);
550         ref->indices = xmalloc(sizeof(char *));
551     }
552 
553     ref->indices[ref->num_indices] = xstrdup(index);
554     ref->num_indices++;
555 }
556 
VarRefCompare(const VarRef * a,const VarRef * b)557 int VarRefCompare(const VarRef *a, const VarRef *b)
558 {
559     int ret = strcmp(a->lval, b->lval);
560     if (ret != 0)
561     {
562         return ret;
563     }
564 
565     ret = strcmp(NULLStringToEmpty(a->scope), NULLStringToEmpty(b->scope));
566     if (ret != 0)
567     {
568         return ret;
569     }
570 
571     const char *a_ns = a->ns ? a->ns : "default";
572     const char *b_ns = b->ns ? b->ns : "default";
573 
574     ret = strcmp(a_ns, b_ns);
575     if (ret != 0)
576     {
577         return ret;
578     }
579 
580     ret = a->num_indices - b->num_indices;
581     if (ret != 0)
582     {
583         return ret;
584     }
585 
586     for (size_t i = 0; i < a->num_indices; i++)
587     {
588         ret = strcmp(a->indices[i], b->indices[i]);
589         if (ret != 0)
590         {
591             return ret;
592         }
593     }
594 
595     return 0;
596 }
597 
VarRefEqual_untyped(const void * a,const void * b)598 bool VarRefEqual_untyped(const void *a, const void *b)
599 {
600     return (VarRefCompare(a, b) == 0);
601 }
602