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 /*
26 This file is an attempt to clean up cf3parse.y, moving as much
27 C code (logic) as possible out of it, so the actual grammar
28 is more readable. It should only be included from cf3parse.y (!).
29
30 The advantages of moving the C code out of the grammar are:
31 * Separate overall grammar from noisy details
32 * Less crazy indentation
33 * Better editor support for auto complete / syntax highlighting
34 */
35
36 #ifndef CF3_PARSE_LOGIC_H
37 #define CF3_PARSE_LOGIC_H
38
39 #include "cf3.defs.h"
40 #include "parser.h"
41 #include "parser_helpers.h"
42 #include "parser_state.h"
43
44 #include "logging.h"
45 #include "fncall.h"
46 #include "rlist.h"
47 #include "item_lib.h"
48 #include "policy.h"
49 #include "mod_files.h"
50 #include "string_lib.h"
51 #include "logic_expressions.h"
52 #include "json-yaml.h"
53 #include "cleanup.h"
54
55 // FIX: remove
56 #include "syntax.h"
57
58 #include <assert.h>
59
60 int yylex(void);
61 extern char *yytext;
62
63 static bool RelevantBundle(const char *agent, const char *blocktype);
64 static bool LvalWantsBody(char *stype, char *lval);
65 static SyntaxTypeMatch CheckSelection(
66 ParserBlock block,
67 const char *type,
68 const char *name,
69 const char *lval,
70 Rval rval);
71 static SyntaxTypeMatch CheckConstraint(
72 const char *type,
73 const char *lval,
74 Rval rval,
75 const PromiseTypeSyntax *ss);
76 static void fatal_yyerror(const char *s);
77
78 static void ParseErrorColumnOffset(int column_offset, const char *s, ...)
79 FUNC_ATTR_PRINTF(2, 3);
80 static void ParseError(const char *s, ...) FUNC_ATTR_PRINTF(1, 2);
81 static void ParseWarning(unsigned int warning, const char *s, ...)
82 FUNC_ATTR_PRINTF(2, 3);
83
84 static void ValidateClassLiteral(const char *class_literal);
85
86 static bool INSTALL_SKIP = false;
87 static size_t CURRENT_BLOCKID_LINE = 0;
88 static size_t CURRENT_PROMISER_LINE = 0;
89
90 #define YYMALLOC xmalloc
91 #define P PARSER_STATE
92
93 #define ParserDebug(...) LogDebug(LOG_MOD_PARSER, __VA_ARGS__)
94
95
96 /*****************************************************************/
97
ParseErrorVColumnOffset(int column_offset,const char * s,va_list ap)98 static void ParseErrorVColumnOffset(
99 int column_offset, const char *s, va_list ap)
100 {
101 char *errmsg = StringVFormat(s, ap);
102 fprintf(
103 stderr,
104 "%s:%d:%d: error: %s\n",
105 P.filename,
106 P.line_no,
107 P.line_pos + column_offset,
108 errmsg);
109 free(errmsg);
110
111 P.error_count++;
112
113 /* Current line is not set when syntax error in first line */
114 if (P.current_line)
115 {
116 fprintf(stderr, "%s\n", P.current_line);
117 fprintf(stderr, "%*s\n", P.line_pos + column_offset, "^");
118 }
119
120 if (P.error_count > 12)
121 {
122 fprintf(stderr, "Too many errors\n");
123 DoCleanupAndExit(EXIT_FAILURE);
124 }
125 }
126
ParseErrorColumnOffset(int column_offset,const char * s,...)127 static void ParseErrorColumnOffset(int column_offset, const char *s, ...)
128 {
129 va_list ap;
130 va_start(ap, s);
131 ParseErrorVColumnOffset(column_offset, s, ap);
132 va_end(ap);
133 }
134
ParseErrorV(const char * s,va_list ap)135 static void ParseErrorV(const char *s, va_list ap)
136 {
137 ParseErrorVColumnOffset(0, s, ap);
138 }
139
ParseError(const char * s,...)140 static void ParseError(const char *s, ...)
141 {
142 va_list ap;
143 va_start(ap, s);
144 ParseErrorV(s, ap);
145 va_end(ap);
146 }
147
ParseWarningV(unsigned int warning,const char * s,va_list ap)148 static void ParseWarningV(unsigned int warning, const char *s, va_list ap)
149 {
150 if (((P.warnings | P.warnings_error) & warning) == 0)
151 {
152 return;
153 }
154
155 char *errmsg = StringVFormat(s, ap);
156 const char *warning_str = ParserWarningToString(warning);
157
158 fprintf(
159 stderr,
160 "%s:%d:%d: warning: %s [-W%s]\n",
161 P.filename,
162 P.line_no,
163 P.line_pos,
164 errmsg,
165 warning_str);
166 fprintf(stderr, "%s\n", P.current_line);
167 fprintf(stderr, "%*s\n", P.line_pos, "^");
168
169 free(errmsg);
170
171 P.warning_count++;
172
173 if ((P.warnings_error & warning) != 0)
174 {
175 P.error_count++;
176 }
177
178 if (P.error_count > 12)
179 {
180 fprintf(stderr, "Too many errors\n");
181 DoCleanupAndExit(EXIT_FAILURE);
182 }
183 }
184
ParseWarning(unsigned int warning,const char * s,...)185 static void ParseWarning(unsigned int warning, const char *s, ...)
186 {
187 va_list ap;
188 va_start(ap, s);
189 ParseWarningV(warning, s, ap);
190 va_end(ap);
191 }
192
yyerror(const char * str)193 void yyerror(const char *str)
194 {
195 ParseError("%s", str);
196 }
197
fatal_yyerror(const char * s)198 static void fatal_yyerror(const char *s)
199 {
200 char *sp = yytext;
201 /* Skip quotation mark */
202 if (sp && *sp == '\"' && sp[1])
203 {
204 sp++;
205 }
206
207 fprintf(
208 stderr,
209 "%s: %d,%d: Fatal error during parsing: %s, near token \'%.20s\'\n",
210 P.filename,
211 P.line_no,
212 P.line_pos,
213 s,
214 sp ? sp : "NULL");
215 DoCleanupAndExit(EXIT_FAILURE);
216 }
217
RelevantBundle(const char * agent,const char * blocktype)218 static bool RelevantBundle(const char *agent, const char *blocktype)
219 {
220 if ((strcmp(agent, CF_AGENTTYPES[AGENT_TYPE_COMMON]) == 0)
221 || (strcmp(CF_COMMONC, blocktype) == 0))
222 {
223 return true;
224 }
225
226 /* Here are some additional bundle types handled by cfAgent */
227
228 Item *ip = SplitString("edit_line,edit_xml", ',');
229
230 if (strcmp(agent, CF_AGENTTYPES[AGENT_TYPE_AGENT]) == 0)
231 {
232 if (IsItemIn(ip, blocktype))
233 {
234 DeleteItemList(ip);
235 return true;
236 }
237 }
238
239 DeleteItemList(ip);
240 return false;
241 }
242
LvalWantsBody(char * stype,char * lval)243 static bool LvalWantsBody(char *stype, char *lval)
244 {
245 for (int i = 0; i < CF3_MODULES; i++)
246 {
247 const PromiseTypeSyntax *promise_type_syntax = CF_ALL_PROMISE_TYPES[i];
248 if (!promise_type_syntax)
249 {
250 continue;
251 }
252
253 for (int j = 0; promise_type_syntax[j].promise_type != NULL; j++)
254 {
255 const ConstraintSyntax *bs = promise_type_syntax[j].constraints;
256 if (!bs)
257 {
258 continue;
259 }
260
261 if (strcmp(promise_type_syntax[j].promise_type, stype) != 0)
262 {
263 continue;
264 }
265
266 for (int l = 0; bs[l].lval != NULL; l++)
267 {
268 if (strcmp(bs[l].lval, lval) == 0)
269 {
270 if (bs[l].dtype == CF_DATA_TYPE_BODY)
271 {
272 return true;
273 }
274 else
275 {
276 return false;
277 }
278 }
279 }
280 }
281 }
282
283 return false;
284 }
285
CheckSelection(ParserBlock block,const char * type,const char * name,const char * lval,Rval rval)286 static SyntaxTypeMatch CheckSelection(
287 ParserBlock block,
288 const char *type,
289 const char *name,
290 const char *lval,
291 Rval rval)
292 {
293 if (block == PARSER_BLOCK_PROMISE)
294 {
295 const BodySyntax *body_syntax = BodySyntaxGet(block, type);
296 const ConstraintSyntax *constraints = body_syntax->constraints;
297 for (int i = 0; constraints[i].lval != NULL; i++)
298 {
299 if (StringEqual(lval, constraints[i].lval))
300 {
301 return CheckConstraintTypeMatch(
302 lval,
303 rval,
304 constraints[i].dtype,
305 constraints[i].range.validation_string,
306 0);
307 }
308 }
309 // Parser should ensure that lval is a valid
310 // attribute, and so we should never get here
311 debug_abort_if_reached();
312 }
313
314 assert(block == PARSER_BLOCK_BODY);
315
316 // Check internal control bodies etc
317 if (strcmp("control", name) == 0)
318 {
319 for (int i = 0; CONTROL_BODIES[i].body_type != NULL; i++)
320 {
321 if (strcmp(type, CONTROL_BODIES[i].body_type) == 0)
322 {
323 const ConstraintSyntax *bs = CONTROL_BODIES[i].constraints;
324
325 for (int l = 0; bs[l].lval != NULL; l++)
326 {
327 if (strcmp(lval, bs[l].lval) == 0)
328 {
329 if (bs[l].dtype == CF_DATA_TYPE_BODY)
330 {
331 return SYNTAX_TYPE_MATCH_OK;
332 }
333 else if (bs[l].dtype == CF_DATA_TYPE_BUNDLE)
334 {
335 return SYNTAX_TYPE_MATCH_OK;
336 }
337 else
338 {
339 return CheckConstraintTypeMatch(
340 lval,
341 rval,
342 bs[l].dtype,
343 bs[l].range.validation_string,
344 0);
345 }
346 }
347 }
348 }
349 }
350 }
351
352 // Now check the functional modules - extra level of indirection
353 for (int i = 0; i < CF3_MODULES; i++)
354 {
355 const PromiseTypeSyntax *promise_type_syntax = CF_ALL_PROMISE_TYPES[i];
356 if (!promise_type_syntax)
357 {
358 continue;
359 }
360
361 for (int j = 0; promise_type_syntax[j].promise_type != NULL; j++)
362 {
363 const ConstraintSyntax *bs = promise_type_syntax[j].constraints;
364
365 if (!bs)
366 {
367 continue;
368 }
369
370 for (int l = 0; bs[l].lval != NULL; l++)
371 {
372 if (bs[l].dtype == CF_DATA_TYPE_BODY)
373 {
374 const ConstraintSyntax *bs2 =
375 bs[l].range.body_type_syntax->constraints;
376
377 if (bs2 == NULL || bs2 == (void *) CF_BUNDLE)
378 {
379 continue;
380 }
381
382 for (int k = 0; bs2[k].dtype != CF_DATA_TYPE_NONE; k++)
383 {
384 /* Either module defined or common */
385
386 if (strcmp(promise_type_syntax[j].promise_type, type)
387 == 0
388 && strcmp(promise_type_syntax[j].promise_type, "*")
389 != 0)
390 {
391 char output[CF_BUFSIZE];
392 snprintf(
393 output,
394 CF_BUFSIZE,
395 "lval %s belongs to promise type '%s': but this is '%s'\n",
396 lval,
397 promise_type_syntax[j].promise_type,
398 type);
399 yyerror(output);
400 return SYNTAX_TYPE_MATCH_OK;
401 }
402
403 if (strcmp(lval, bs2[k].lval) == 0)
404 {
405 /* Body definitions will be checked later. */
406 if (bs2[k].dtype != CF_DATA_TYPE_BODY)
407 {
408 return CheckConstraintTypeMatch(
409 lval,
410 rval,
411 bs2[k].dtype,
412 bs2[k].range.validation_string,
413 0);
414 }
415 else
416 {
417 return SYNTAX_TYPE_MATCH_OK;
418 }
419 }
420 }
421 }
422 }
423 }
424 }
425
426 char output[CF_BUFSIZE];
427 snprintf(
428 output,
429 CF_BUFSIZE,
430 "Constraint lvalue \"%s\" is not allowed in \'%s\' constraint body",
431 lval,
432 type);
433 yyerror(output);
434
435 return SYNTAX_TYPE_MATCH_OK; // TODO: OK?
436 }
437
CheckConstraint(const char * type,const char * lval,Rval rval,const PromiseTypeSyntax * promise_type_syntax)438 static SyntaxTypeMatch CheckConstraint(
439 const char *type,
440 const char *lval,
441 Rval rval,
442 const PromiseTypeSyntax *promise_type_syntax)
443 {
444 assert(promise_type_syntax);
445
446 if (promise_type_syntax->promise_type != NULL) /* In a bundle */
447 {
448 if (strcmp(promise_type_syntax->promise_type, type) == 0)
449 {
450 const ConstraintSyntax *bs = promise_type_syntax->constraints;
451
452 for (int l = 0; bs[l].lval != NULL; l++)
453 {
454 if (strcmp(lval, bs[l].lval) == 0)
455 {
456 /* If we get here we have found the lval and it is valid
457 for this promise_type */
458
459 /* For bodies and bundles definitions can be elsewhere, so
460 they are checked in PolicyCheckRunnable(). */
461 if (bs[l].dtype != CF_DATA_TYPE_BODY
462 && bs[l].dtype != CF_DATA_TYPE_BUNDLE)
463 {
464 return CheckConstraintTypeMatch(
465 lval,
466 rval,
467 bs[l].dtype,
468 bs[l].range.validation_string,
469 0);
470 }
471 }
472 }
473 }
474 }
475
476 return SYNTAX_TYPE_MATCH_OK;
477 }
478
ValidateClassLiteral(const char * class_literal)479 static void ValidateClassLiteral(const char *class_literal)
480 {
481 ParseResult res = ParseExpression(class_literal, 0, strlen(class_literal));
482
483 if (!res.result)
484 {
485 ParseErrorColumnOffset(
486 res.position - strlen(class_literal),
487 "Syntax error in context string");
488 }
489
490 FreeExpression(res.result);
491 }
492
ParserEndCurrentBlock()493 static inline void ParserEndCurrentBlock()
494 {
495 P.offsets.last_id = -1;
496 P.offsets.last_string = -1;
497 P.offsets.last_class_id = -1;
498 if (P.block != PARSER_BLOCK_BUNDLE && P.currentbody != NULL)
499 {
500 P.currentbody->offset.end = P.offsets.current;
501 }
502
503 if (P.block == PARSER_BLOCK_BUNDLE && P.currentbundle != NULL)
504 {
505 P.currentbundle->offset.end = P.offsets.current;
506 }
507 }
508
509 // This part of the parser is interesting, to say the least.
510 // While parsing, we try to, opportunistically, do a bunch of
511 // transformations on the right-hand side values (rval) of
512 // promise attributes. For example converting some patterns
513 // to function calls:
514 // @(x)
515 // mergedata(x)
516 // data => "{}"
517 // data => parsejson("{}")
518 //
519 // In some cases, we even try to evaluate the function call,
520 // like parsejson, and if it succeeds (if there are no
521 // unresolved variables) we just insert the resulting data
522 // container directly.
523
MagicRvalTransformations(const PromiseTypeSyntax * promise_type_syntax)524 static inline void MagicRvalTransformations(
525 const PromiseTypeSyntax *promise_type_syntax)
526 {
527 const char *item = P.rval.item;
528 // convert @(x) to mergedata(x)
529 if (P.rval.type == RVAL_TYPE_SCALAR
530 && (strcmp(P.lval, "data") == 0
531 || strcmp(P.lval, "template_data") == 0)
532 && strlen(item) > 3 && item[0] == '@'
533 && (item[1] == '(' || item[1] == '{'))
534 {
535 Rlist *synthetic_args = NULL;
536 char *tmp = xstrndup(P.rval.item + 2, strlen(P.rval.item) - 3);
537 RlistAppendScalar(&synthetic_args, tmp);
538 free(tmp);
539 RvalDestroy(P.rval);
540
541 P.rval =
542 (Rval){FnCallNew("mergedata", synthetic_args), RVAL_TYPE_FNCALL};
543 }
544 // convert 'json or yaml' to direct container or parsejson(x)
545 // or parseyaml(x)
546 else if (
547 P.rval.type == RVAL_TYPE_SCALAR
548 && (strcmp(P.lval, "data") == 0
549 || strcmp(P.lval, "template_data") == 0))
550 {
551 JsonElement *json = NULL;
552 JsonParseError res;
553 bool json_parse_attempted = false;
554 Buffer *copy = BufferNewFrom(P.rval.item, strlen(P.rval.item));
555
556 const char *fname = NULL;
557 if (strlen(P.rval.item) > 3 && strncmp("---", P.rval.item, 3) == 0)
558 {
559 fname = "parseyaml";
560
561 // look for unexpanded variables
562 if (strstr(P.rval.item, "$(") == NULL
563 && strstr(P.rval.item, "${") == NULL)
564 {
565 const char *copy_data = BufferData(copy);
566 res = JsonParseYamlString(©_data, &json);
567 json_parse_attempted = true;
568 }
569 }
570 else
571 {
572 fname = "parsejson";
573 // look for unexpanded variables
574 if (strstr(P.rval.item, "$(") == NULL
575 && strstr(P.rval.item, "${") == NULL)
576 {
577 const char *copy_data = BufferData(copy);
578 res = JsonParse(©_data, &json);
579 json_parse_attempted = true;
580 }
581 }
582
583 BufferDestroy(copy);
584
585 if (json_parse_attempted && res != JSON_PARSE_OK)
586 {
587 // Parsing failed, insert fncall so it can be retried
588 // during evaluation
589 }
590 else if (
591 json != NULL
592 && JsonGetElementType(json) == JSON_ELEMENT_TYPE_PRIMITIVE)
593 {
594 // Parsing failed, insert fncall so it can be retried
595 // during evaluation
596 JsonDestroy(json);
597 json = NULL;
598 }
599
600 if (fname != NULL)
601 {
602 if (json == NULL)
603 {
604 Rlist *synthetic_args = NULL;
605 RlistAppendScalar(&synthetic_args, P.rval.item);
606 RvalDestroy(P.rval);
607
608 P.rval =
609 (Rval){FnCallNew(fname, synthetic_args), RVAL_TYPE_FNCALL};
610 }
611 else
612 {
613 RvalDestroy(P.rval);
614 P.rval = (Rval){json, RVAL_TYPE_CONTAINER};
615 }
616 }
617 }
618
619 if (promise_type_syntax != NULL) // NULL for custom promise types
620 {
621 SyntaxTypeMatch err = CheckConstraint(
622 P.currenttype, P.lval, P.rval, promise_type_syntax);
623 if (err != SYNTAX_TYPE_MATCH_OK
624 && err != SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED)
625 {
626 yyerror(SyntaxTypeMatchToString(err));
627 }
628 }
629
630 if (P.rval.type == RVAL_TYPE_SCALAR
631 && (strcmp(P.lval, "ifvarclass") == 0 || strcmp(P.lval, "if") == 0))
632 {
633 ValidateClassLiteral(P.rval.item);
634 }
635 }
636
ParserAppendCurrentConstraint()637 static inline void ParserAppendCurrentConstraint()
638 {
639 Constraint *cp = PromiseAppendConstraint(
640 P.currentpromise, P.lval, RvalCopy(P.rval), P.references_body);
641 cp->offset.line = P.line_no;
642 cp->offset.start = P.offsets.last_id;
643 cp->offset.end = P.offsets.current;
644 cp->offset.context = P.offsets.last_class_id;
645 P.currentstype->offset.end = P.offsets.current;
646 }
647
648 // This function is called for every rval (right hand side value) of every
649 // promise while parsing. The reason why it is so big is because it does a lot
650 // of transformation, for example transforming strings into function calls like
651 // parsejson, and then attempts to resolve those function calls.
ParserHandleBundlePromiseRval()652 static inline void ParserHandleBundlePromiseRval()
653 {
654 if (INSTALL_SKIP)
655 {
656 RvalDestroy(P.rval);
657 P.rval = RvalNew(NULL, RVAL_TYPE_NOPROMISEE);
658 return;
659 }
660
661 if (PolicyHasCustomPromiseType(P.policy, P.currenttype))
662 {
663 // Definitely custom promise type, just add the constraint and move on
664 MagicRvalTransformations(NULL);
665 ParserAppendCurrentConstraint();
666 goto cleanup;
667 }
668
669 const ConstraintSyntax *constraint_syntax = NULL;
670 const PromiseTypeSyntax *promise_type_syntax =
671 PromiseTypeSyntaxGet(P.blocktype, P.currenttype);
672 if (promise_type_syntax != NULL)
673 {
674 constraint_syntax =
675 PromiseTypeSyntaxGetConstraintSyntax(promise_type_syntax, P.lval);
676 }
677
678 if (promise_type_syntax == NULL)
679 {
680 // Assume custom promise type, but defined in another policy file
681 MagicRvalTransformations(NULL);
682 ParserAppendCurrentConstraint();
683 goto cleanup;
684 }
685 else if (constraint_syntax == NULL)
686 {
687 ParseError(
688 "Unknown constraint '%s' in promise type '%s'",
689 P.lval,
690 promise_type_syntax->promise_type);
691 }
692 else
693 {
694 switch (constraint_syntax->status)
695 {
696 case SYNTAX_STATUS_DEPRECATED:
697 ParseWarning(
698 PARSER_WARNING_DEPRECATED,
699 "Deprecated constraint '%s' in promise type '%s'",
700 constraint_syntax->lval,
701 promise_type_syntax->promise_type);
702 // fall through
703 case SYNTAX_STATUS_NORMAL:
704 {
705 MagicRvalTransformations(promise_type_syntax);
706 ParserAppendCurrentConstraint();
707 }
708 break;
709 case SYNTAX_STATUS_REMOVED:
710 ParseWarning(
711 PARSER_WARNING_REMOVED,
712 "Removed constraint '%s' in promise type '%s'",
713 constraint_syntax->lval,
714 promise_type_syntax->promise_type);
715 break;
716 }
717 }
718
719 cleanup:
720
721 RvalDestroy(P.rval);
722 P.rval = RvalNew(NULL, RVAL_TYPE_NOPROMISEE);
723 strcpy(P.lval, "no lval");
724 RlistDestroy(P.currentRlist);
725 P.currentRlist = NULL;
726 }
727
ParserBeginBlock(ParserBlock b)728 static inline void ParserBeginBlock(ParserBlock b)
729 {
730 ParserDebug("P:%s:%s\n", ParserBlockString(b), P.blocktype);
731 P.block = b;
732
733 if (b == PARSER_BLOCK_BUNDLE)
734 {
735 RvalDestroy(P.rval);
736 P.rval = RvalNew(NULL, RVAL_TYPE_NOPROMISEE);
737 }
738
739 RlistDestroy(P.currentRlist);
740 P.currentRlist = NULL;
741
742 if (P.currentstring)
743 {
744 free(P.currentstring);
745 }
746 P.currentstring = NULL;
747
748 strcpy(P.blockid, "");
749 }
750
751 // The promise "guard" is a promise type followed by a single colon,
752 // found in bundles. It is called guard because it resembles the
753 // class guards, and all other names I could think of were confusing.
754 // (It doesn't really "guard" anything).
ParserHandlePromiseGuard()755 static inline void ParserHandlePromiseGuard()
756 {
757 ParserDebug(
758 "\tP:%s:%s:%s promise_type = %s\n",
759 ParserBlockString(P.block),
760 P.blocktype,
761 P.blockid,
762 P.currenttype);
763
764 const PromiseTypeSyntax *promise_type_syntax =
765 PromiseTypeSyntaxGet(P.blocktype, P.currenttype);
766
767 if (promise_type_syntax)
768 {
769 switch (promise_type_syntax->status)
770 {
771 case SYNTAX_STATUS_DEPRECATED:
772 ParseWarning(
773 PARSER_WARNING_DEPRECATED,
774 "Deprecated promise type '%s' in bundle type '%s'",
775 promise_type_syntax->promise_type,
776 promise_type_syntax->bundle_type);
777 // fall through
778 case SYNTAX_STATUS_NORMAL:
779 if (P.block == PARSER_BLOCK_BUNDLE)
780 {
781 if (!INSTALL_SKIP)
782 {
783 P.currentstype =
784 BundleAppendSection(P.currentbundle, P.currenttype);
785 P.currentstype->offset.line = P.line_no;
786 P.currentstype->offset.start =
787 P.offsets.last_promise_guard_id;
788 }
789 else
790 {
791 P.currentstype = NULL;
792 }
793 }
794 break;
795 case SYNTAX_STATUS_REMOVED:
796 ParseWarning(
797 PARSER_WARNING_REMOVED,
798 "Removed promise type '%s' in bundle type '%s'",
799 promise_type_syntax->promise_type,
800 promise_type_syntax->bundle_type);
801 INSTALL_SKIP = true;
802 break;
803 }
804 }
805 else
806 {
807 // Unrecognized promise type, assume it is custom
808 // no way to know while parsing, let's check later:
809 if (!INSTALL_SKIP)
810 {
811 P.currentstype =
812 BundleAppendSection(P.currentbundle, P.currenttype);
813 P.currentstype->offset.line = P.line_no;
814 P.currentstype->offset.start = P.offsets.last_promise_guard_id;
815 }
816 else
817 {
818 P.currentstype = NULL;
819 }
820 }
821 }
822
823 // Called at the beginning of the body of the block, i.e. the opening '{'
ParserBeginBlockBody()824 static inline void ParserBeginBlockBody()
825 {
826 const BodySyntax *body_syntax = BodySyntaxGet(P.block, P.blocktype);
827
828 if (P.block == PARSER_BLOCK_PROMISE && body_syntax != NULL)
829 {
830 P.currentbody = PolicyAppendPromiseBlock(
831 P.policy,
832 P.current_namespace,
833 P.blockid,
834 P.blocktype,
835 P.useargs,
836 P.filename);
837 P.currentbody->offset.line = CURRENT_BLOCKID_LINE;
838 P.currentbody->offset.start = P.offsets.last_block_id;
839 }
840 else if (body_syntax)
841 {
842 INSTALL_SKIP = false;
843
844 switch (body_syntax->status)
845 {
846 case SYNTAX_STATUS_DEPRECATED:
847 ParseWarning(
848 PARSER_WARNING_DEPRECATED,
849 "Deprecated body '%s' of type '%s'",
850 P.blockid,
851 body_syntax->body_type);
852 // fall through
853 case SYNTAX_STATUS_NORMAL:
854 P.currentbody = PolicyAppendBody(
855 P.policy,
856 P.current_namespace,
857 P.blockid,
858 P.blocktype,
859 P.useargs,
860 P.filename);
861 P.currentbody->offset.line = CURRENT_BLOCKID_LINE;
862 P.currentbody->offset.start = P.offsets.last_block_id;
863 break;
864
865 case SYNTAX_STATUS_REMOVED:
866 ParseWarning(
867 PARSER_WARNING_REMOVED,
868 "Removed body '%s' of type '%s'",
869 P.blockid,
870 body_syntax->body_type);
871 INSTALL_SKIP = true;
872 break;
873 }
874 }
875 else
876 {
877 ParseError("Invalid body type '%s'", P.blocktype);
878 INSTALL_SKIP = true;
879 }
880
881 RlistDestroy(P.useargs);
882 P.useargs = NULL;
883
884 strcpy(P.currentid, "");
885 }
886
887 // Called for every Rval (Right hand side value) of attributes
888 // in body blocks
ParserHandleBlockAttributeRval()889 static inline void ParserHandleBlockAttributeRval()
890 {
891 assert(P.block == PARSER_BLOCK_BODY || P.block == PARSER_BLOCK_PROMISE);
892
893 if (!INSTALL_SKIP)
894 {
895 const BodySyntax *body_syntax = BodySyntaxGet(P.block, P.blocktype);
896 assert(body_syntax != NULL);
897
898 const ConstraintSyntax *constraint_syntax =
899 BodySyntaxGetConstraintSyntax(body_syntax->constraints, P.lval);
900 if (constraint_syntax)
901 {
902 switch (constraint_syntax->status)
903 {
904 case SYNTAX_STATUS_DEPRECATED:
905 ParseWarning(
906 PARSER_WARNING_DEPRECATED,
907 "Deprecated constraint '%s' in body type '%s'",
908 constraint_syntax->lval,
909 body_syntax->body_type);
910 // fall through
911 case SYNTAX_STATUS_NORMAL:
912 {
913 SyntaxTypeMatch err = CheckSelection(
914 P.block, P.blocktype, P.blockid, P.lval, P.rval);
915 if (err != SYNTAX_TYPE_MATCH_OK
916 && err != SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED)
917 {
918 yyerror(SyntaxTypeMatchToString(err));
919 }
920
921 if (P.rval.type == RVAL_TYPE_SCALAR
922 && (strcmp(P.lval, "ifvarclass") == 0
923 || strcmp(P.lval, "if") == 0))
924 {
925 ValidateClassLiteral(P.rval.item);
926 }
927
928 Constraint *cp = NULL;
929 if (P.currentclasses == NULL)
930 {
931 cp = BodyAppendConstraint(
932 P.currentbody,
933 P.lval,
934 RvalCopy(P.rval),
935 "any",
936 P.references_body);
937 }
938 else
939 {
940 cp = BodyAppendConstraint(
941 P.currentbody,
942 P.lval,
943 RvalCopy(P.rval),
944 P.currentclasses,
945 P.references_body);
946 }
947
948 if (P.currentvarclasses != NULL)
949 {
950 ParseError(
951 "Body attributes can't be put under a variable class '%s'",
952 P.currentvarclasses);
953 }
954
955 cp->offset.line = P.line_no;
956 cp->offset.start = P.offsets.last_id;
957 cp->offset.end = P.offsets.current;
958 cp->offset.context = P.offsets.last_class_id;
959 break;
960 }
961 case SYNTAX_STATUS_REMOVED:
962 ParseWarning(
963 PARSER_WARNING_REMOVED,
964 "Removed constraint '%s' in promise type '%s'",
965 constraint_syntax->lval,
966 body_syntax->body_type);
967 break;
968 }
969 }
970 }
971 else
972 {
973 RvalDestroy(P.rval);
974 P.rval = RvalNew(NULL, RVAL_TYPE_NOPROMISEE);
975 }
976
977 if (strcmp(P.blockid, "control") == 0 && strcmp(P.blocktype, "file") == 0)
978 {
979 if (strcmp(P.lval, "namespace") == 0)
980 {
981 if (P.rval.type != RVAL_TYPE_SCALAR)
982 {
983 yyerror("namespace must be a constant scalar string");
984 }
985 else
986 {
987 free(P.current_namespace);
988 P.current_namespace = xstrdup(P.rval.item);
989 }
990 }
991 }
992
993 RvalDestroy(P.rval);
994 P.rval = RvalNew(NULL, RVAL_TYPE_NOPROMISEE);
995 }
996
ParserBeginBundleBody()997 static inline void ParserBeginBundleBody()
998 {
999 assert(P.block == PARSER_BLOCK_BUNDLE);
1000
1001 if (RelevantBundle(CF_AGENTTYPES[P.agent_type], P.blocktype))
1002 {
1003 INSTALL_SKIP = false;
1004 }
1005 else if (strcmp(CF_AGENTTYPES[P.agent_type], P.blocktype) != 0)
1006 {
1007 INSTALL_SKIP = true;
1008 }
1009
1010 if (!INSTALL_SKIP)
1011 {
1012 P.currentbundle = PolicyAppendBundle(
1013 P.policy,
1014 P.current_namespace,
1015 P.blockid,
1016 P.blocktype,
1017 P.useargs,
1018 P.filename);
1019 P.currentbundle->offset.line = CURRENT_BLOCKID_LINE;
1020 P.currentbundle->offset.start = P.offsets.last_block_id;
1021 }
1022 else
1023 {
1024 P.currentbundle = NULL;
1025 }
1026
1027 RlistDestroy(P.useargs);
1028 P.useargs = NULL;
1029 }
1030
ParserHandleQuotedListItem()1031 static inline void ParserHandleQuotedListItem()
1032 {
1033 RlistAppendScalar((Rlist **) &P.currentRlist,
1034 (void *) P.currentstring);
1035 FREE_AND_NULL(P.currentstring);
1036 }
1037
1038 #endif // CF3_PARSE_LOGIC_H
1039