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