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 #include <platform.h>
27 #include <generic_agent.h>
28 
29 #include <actuator.h>
30 #include <audit.h>
31 #include <cleanup.h>
32 #include <eval_context.h>
33 #include <verify_classes.h>
34 #include <verify_databases.h>
35 #include <verify_environments.h>
36 #include <verify_exec.h>
37 #include <verify_methods.h>
38 #include <verify_processes.h>
39 #include <verify_packages.h>
40 #include <verify_users.h>
41 #include <verify_services.h>
42 #include <verify_storage.h>
43 #include <verify_files.h>
44 #include <verify_files_utils.h>
45 #include <verify_vars.h>
46 #include <addr_lib.h>
47 #include <files_names.h>
48 #include <files_interfaces.h>
49 #include <files_repository.h>
50 #include <files_edit.h>
51 #include <files_properties.h>
52 #include <item_lib.h>
53 #include <vars.h>
54 #include <conversion.h>
55 #include <expand.h>
56 #include <locks.h>
57 #include <scope.h>
58 #include <matching.h>
59 #include <match_scope.h>
60 #include <instrumentation.h>
61 #include <promises.h>
62 #include <unix.h>
63 #include <attributes.h>
64 #include <communication.h>
65 #include <signals.h>
66 #include <nfs.h>
67 #include <processes_select.h>
68 #include <list.h>
69 #include <fncall.h>
70 #include <rlist.h>
71 #include <agent-diagnostics.h>
72 #include <known_dirs.h>
73 #include <cf-agent-enterprise-stubs.h>
74 #include <syslog_client.h>
75 #include <man.h>
76 #include <bootstrap.h>
77 #include <policy_server.h>
78 #include <misc_lib.h>
79 #include <buffer.h>
80 #include <loading.h>
81 #include <conn_cache.h>                 /* ConnCache_Init,ConnCache_Destroy */
82 #include <net.h>
83 #include <package_module.h>
84 #include <string_lib.h>
85 #include <cfnet.h>
86 #include <repair.h>
87 #include <dbm_api.h>                    /* CheckDBRepairFlagFile() */
88 #include <sys/types.h>                  /* checking umask on writing setxid log */
89 #include <sys/stat.h>                   /* checking umask on writing setxid log */
90 #include <simulate_mode.h>              /* ManifestChangedFiles(), DiffChangedFiles() */
91 #include <ip_address.h>
92 
93 #include <syntax.h>                     /* IsBuiltInPromiseType() */
94 #include <mod_common.h>
95 #include <mod_custom.h>                 /* EvaluateCustomPromise(), Intialize/FinalizeCustomPromises() */
96 
97 #ifdef HAVE_AVAHI_CLIENT_CLIENT_H
98 #ifdef HAVE_AVAHI_COMMON_ADDRESS_H
99 #include <findhub.h>
100 #endif
101 #endif
102 
103 #include <ornaments.h>
104 
105 
106 extern int PR_KEPT;
107 extern int PR_REPAIRED;
108 extern int PR_NOTKEPT;
109 
110 static bool ALLCLASSESREPORT = false; /* GLOBAL_P */
111 static bool ALWAYS_VALIDATE = false; /* GLOBAL_P */
112 static bool CFPARANOID = false; /* GLOBAL_P */
113 static bool PERFORM_DB_CHECK = false;
114 
115 static const Rlist *ACCESSLIST = NULL; /* GLOBAL_P */
116 
117 static int CFA_BACKGROUND = 0; /* GLOBAL_X */
118 static int CFA_BACKGROUND_LIMIT = 1; /* GLOBAL_P */
119 
120 static Item *PROCESSREFRESH = NULL; /* GLOBAL_P */
121 
122 static const char *const AGENT_TYPESEQUENCE[] =
123 {
124     "meta",
125     "vars",
126     "defaults",
127     "classes",                  /* Maelstrom order 2 */
128     "users",
129     "files",
130     "packages",
131     "guest_environments",
132     "methods",
133     "processes",
134     "services",
135     "commands",
136     "storage",
137     "databases",
138     "reports",
139     NULL
140 };
141 
142 /*******************************************************************/
143 /* Agent specific variables                                        */
144 /*******************************************************************/
145 
146 static void ThisAgentInit(void);
147 static GenericAgentConfig *CheckOpts(int argc, char **argv);
148 static char **TranslateOldBootstrapOptionsSeparate(int *argc_new, char **argv);
149 static char **TranslateOldBootstrapOptionsConcatenated(int argc, char **argv);
150 static void FreeFixedStringArray(int size, char **array);
151 static void CheckAgentAccess(const Rlist *list, const Policy *policy);
152 static void KeepControlPromises(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config);
153 static PromiseResult KeepAgentPromise(EvalContext *ctx, const Promise *pp, void *param);
154 static void NewTypeContext(TypeSequence type);
155 static void DeleteTypeContext(EvalContext *ctx, TypeSequence type);
156 static PromiseResult ParallelFindAndVerifyFilesPromises(EvalContext *ctx, const Promise *pp);
157 static bool VerifyBootstrap(void);
158 static void KeepPromiseBundles(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config);
159 static void KeepPromises(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config);
160 static int NoteBundleCompliance(const Bundle *bundle, int save_pr_kept, int save_pr_repaired, int save_pr_notkept, struct timespec start);
161 static void AllClassesReport(const EvalContext *ctx);
162 static bool HasAvahiSupport(void);
163 static int AutomaticBootstrap(GenericAgentConfig *config);
164 static void BannerStatus(PromiseResult status, const char *type, char *name);
165 static PromiseResult DefaultVarPromise(EvalContext *ctx, const Promise *pp);
166 static void WaitForBackgroundProcesses();
167 
168 /*******************************************************************/
169 /* Command line options                                            */
170 /*******************************************************************/
171 
172 static const char *const CF_AGENT_SHORT_DESCRIPTION =
173     "evaluate CFEngine policy code and actuate change to the system.";
174 
175 static const char *const CF_AGENT_MANPAGE_LONG_DESCRIPTION =
176         "cf-agent evaluates policy code and makes changes to the system. Policy bundles are evaluated in the order of the "
177         "provided bundlesequence (this is normally specified in the common control body). "
178         "For each bundle, cf-agent groups promise statements according to their type. Promise types are then evaluated in a preset "
179         "order to ensure fast system convergence to policy.\n";
180 
181 static const struct option OPTIONS[] =
182 {
183     {"bootstrap", required_argument, 0, 'B'},
184     {"bundlesequence", required_argument, 0, 'b'},
185     {"workdir", required_argument, 0, 'w'},
186     {"debug", no_argument, 0, 'd'},
187     {"define", required_argument, 0, 'D'},
188     {"self-diagnostics", optional_argument, 0, 'x'},
189     {"dry-run", no_argument, 0, 'n'},
190     {"file", required_argument, 0, 'f'},
191     {"help", no_argument, 0, 'h'},
192     {"inform", no_argument, 0, 'I'},
193     {"log-level", required_argument, 0, 'g'},
194     {"negate", required_argument, 0, 'N'},
195     {"no-lock", no_argument, 0, 'K'},
196     {"verbose", no_argument, 0, 'v'},
197     {"version", no_argument, 0, 'V'},
198     {"timing-output", no_argument, 0, 't'},
199     {"trust-server", optional_argument, 0, 'T'},
200     {"color", optional_argument, 0, 'C'},
201     {"no-extensions", no_argument, 0, 'E'},
202     {"timestamp", no_argument, 0, 'l'},
203     /* Only long option for the rest */
204     {"ignore-preferred-augments", no_argument, 0, 0},
205     {"log-modules", required_argument, 0, 0},
206     {"show-evaluated-classes", optional_argument, 0, 0 },
207     {"show-evaluated-vars", optional_argument, 0, 0 },
208     {"skip-bootstrap-policy-run", no_argument, 0, 0 },
209     {"skip-db-check", optional_argument, 0, 0 },
210     {"simulate", required_argument, 0, 0},
211     {NULL, 0, 0, '\0'}
212 };
213 
214 static const char *const HINTS[] =
215 {
216     "Bootstrap CFEngine to the given policy server IP, hostname or :avahi (automatic detection)",
217     "Set or override bundlesequence from command line",
218     "Override the default /var/cfengine work directory for testing (same as setting CFENGINE_TEST_OVERRIDE_WORKDIR)",
219     "Enable debugging output",
220     "Define a list of comma separated classes to be defined at the start of execution",
221     "Run checks to diagnose a CFEngine agent installation",
222     "All talk and no action mode - make no changes, only inform of promises not kept",
223     "Specify an alternative input file than the default. This option is overridden by FILE if supplied as argument.",
224     "Print the help message",
225     "Print basic information about changes made to the system, i.e. promises repaired",
226     "Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'",
227     "Define a list of comma separated classes to be undefined at the start of execution",
228     "Ignore locking constraints during execution (ifelapsed/expireafter) if \"too soon\" to run",
229     "Output verbose information about the behaviour of the agent",
230     "Output the version of the software",
231     "Output timing information on console when in verbose mode",
232     "Possible values: 'yes' (default, trust the server when bootstrapping), 'no' (server key must already be trusted)",
233     "Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'",
234     "Disable extension loading (used while upgrading)",
235     "Log timestamps on each line of log output",
236     "Ignore def_preferred.json file in favor of def.json",
237     "Enable even more detailed debug logging for specific areas of the implementation. Use together with '-d'. Use --log-modules=help for a list of available modules",
238     "Show *final* evaluated classes, including those defined in common bundles in policy. Optionally can take a regular expression.",
239     "Show *final* evaluated variables, including those defined without dependency to user-defined classes in policy. Optionally can take a regular expression.",
240     "Do not run policy as the last step of the bootstrap process",
241     "Do not run database integrity checks and repairs at startup",
242     "Run in simulate mode, either 'manifest', 'manifest-full' or 'diff'",
243     NULL
244 };
245 
246 /**
247  @brief
248  Wrapper around DefaultVarPromise to silence cast-function-type compiler warning in ScheduleAgentOperations
249  */
DefaultVarPromiseWrapper(EvalContext * ctx,const Promise * pp,void * param)250 static PromiseResult DefaultVarPromiseWrapper(EvalContext *ctx, const Promise *pp, void *param) {
251     UNUSED(param);
252     return DefaultVarPromise(ctx, pp);
253 }
254 
255 /*******************************************************************/
256 
main(int argc,char * argv[])257 int main(int argc, char *argv[])
258 {
259     SetupSignalsForAgent();
260 #ifdef HAVE_LIBXML2
261         xmlInitParser();
262 #endif
263     struct timespec start = BeginMeasure();
264 
265     GenericAgentConfig *config = CheckOpts(argc, argv);
266     bool force_repair = CheckDBRepairFlagFile();
267     if (force_repair || PERFORM_DB_CHECK)
268     {
269         repair_lmdb_default(force_repair);
270     }
271     EvalContext *ctx = EvalContextNew();
272 
273     // Enable only for cf-agent eval context.
274     EvalContextAllClassesLoggingEnable(ctx, true);
275 
276     GenericAgentConfigApply(ctx, config);
277 
278     const char *program_invocation_name = argv[0];
279     const char *last_dir_sep = strrchr(program_invocation_name, FILE_SEPARATOR);
280     const char *program_name = (last_dir_sep != NULL ? last_dir_sep + 1 : program_invocation_name);
281     GenericAgentDiscoverContext(ctx, config, program_name);
282 
283     /* FIXME: (CFE-2709) ALWAYS_VALIDATE will always be false here, since it can
284      *        only change in KeepPromises(), five lines later on. */
285     Policy *policy = SelectAndLoadPolicy(config, ctx, ALWAYS_VALIDATE, true);
286 
287     if (!policy)
288     {
289         Log(LOG_LEVEL_ERR, "Error reading CFEngine policy. Exiting...");
290         DoCleanupAndExit(EXIT_FAILURE);
291     }
292 
293     int ret = 0;
294 
295     GenericAgentPostLoadInit(ctx);
296     ThisAgentInit();
297     ConnCache_Init();
298 
299     BeginAudit();
300 
301     KeepPromises(ctx, policy, config);
302 
303     if (EvalAborted(ctx))
304     {
305         ret = EC_EVAL_ABORTED;
306     }
307 
308     ConnCache_Destroy();
309 
310     if (ALLCLASSESREPORT)
311     {
312         AllClassesReport(ctx);
313     }
314 
315     Nova_TrackExecution(config->input_file);
316 
317     /* Update packages cache. */
318     UpdatePackagesCache(ctx, false);
319 
320     /* Finalize custom promises before waiting for background processes because
321      * they can be background processes and need special handling. */
322     FinalizeCustomPromises();
323 
324     /* Wait for background processes before generating reports because
325      * GenerateReports() does nothing if it detects multiple cf-agent processes
326      * running. */
327     WaitForBackgroundProcesses();
328 
329     GenerateReports(config, ctx);
330 
331     PurgeLocks();
332     BackupLockDatabase();
333 
334     if (config->agent_specific.agent.show_evaluated_classes != NULL)
335     {
336         GenericAgentShowContextsFormatted(ctx, config->agent_specific.agent.show_evaluated_classes);
337         free(config->agent_specific.agent.show_evaluated_classes);
338     }
339 
340     if (config->agent_specific.agent.show_evaluated_variables != NULL)
341     {
342         GenericAgentShowVariablesFormatted(ctx, config->agent_specific.agent.show_evaluated_variables);
343         free(config->agent_specific.agent.show_evaluated_variables);
344     }
345 
346     PolicyDestroy(policy); /* Can we safely do this earlier ? */
347     if (config->agent_specific.agent.bootstrap_argument && !VerifyBootstrap())
348     {
349         PolicyServerRemoveFile(GetWorkDir());
350         WriteAmPolicyHubFile(false);
351         ret = 1;
352     }
353 
354     EndAudit(ctx, CFA_BACKGROUND);
355 
356     Nova_NoteAgentExecutionPerformance(config->input_file, start);
357 
358     GenericAgentFinalize(ctx, config);
359 
360     StringSetDestroy(SINGLE_COPY_CACHE);
361 
362     StringSet *audited_files = NULL;
363     if ((EVAL_MODE == EVAL_MODE_SIMULATE_MANIFEST) ||
364         (EVAL_MODE == EVAL_MODE_SIMULATE_MANIFEST_FULL))
365     {
366         bool success = ManifestChangedFiles(&audited_files);
367         if (!success)
368         {
369             Log(LOG_LEVEL_ERR, "Failed to manifest changed files");
370         }
371         if (EVAL_MODE == EVAL_MODE_SIMULATE_MANIFEST_FULL)
372         {
373             /* Skips the files already manifested above. */
374             success = ManifestAllFiles(&audited_files);
375             if (!success)
376             {
377                 Log(LOG_LEVEL_ERR, "Failed to manifest unmodified files");
378             }
379         }
380         success = ManifestPkgOperations();
381         if (!success)
382         {
383             Log(LOG_LEVEL_ERR, "Failed to manifest present and absent packages");
384         }
385     }
386     else if (EVAL_MODE == EVAL_MODE_SIMULATE_DIFF)
387     {
388         bool success = DiffChangedFiles(&audited_files);
389         if (!success)
390         {
391             Log(LOG_LEVEL_ERR, "Failed to show differences for changed files");
392         }
393         success = DiffPkgOperations();
394         if (!success)
395         {
396             Log(LOG_LEVEL_ERR, "Failed to show differences in installed packages");
397         }
398     }
399     StringSetDestroy(audited_files);
400 
401 #ifdef HAVE_LIBXML2
402         xmlCleanupParser();
403 #endif
404 
405     CallCleanupFunctions();
406 
407     return ret;
408 }
409 
410 /*******************************************************************/
411 /* Level 1                                                         */
412 /*******************************************************************/
413 
ConfigureBootstrap(GenericAgentConfig * config,const char * argument)414 static void ConfigureBootstrap(GenericAgentConfig *config, const char *argument)
415 {
416     assert(config != NULL);
417     if (!BootstrapAllowed())
418     {
419         Log(LOG_LEVEL_ERR, "Not enough privileges to bootstrap CFEngine");
420         DoCleanupAndExit(EXIT_FAILURE);
421     }
422 
423     if(strcmp(optarg, ":avahi") == 0)
424     {
425         if(!HasAvahiSupport())
426         {
427             Log(LOG_LEVEL_ERR, "Avahi support is not built in, please see options to the configure script and rebuild CFEngine");
428             DoCleanupAndExit(EXIT_FAILURE);
429         }
430 
431         int err = AutomaticBootstrap(config);
432         if (err < 0)
433         {
434             Log(LOG_LEVEL_ERR, "Automatic bootstrap failed, error code '%d'", err);
435             DoCleanupAndExit(EXIT_FAILURE);
436         }
437         return;
438     }
439 
440     if(StringEqual(argument, "localhost") || StringIsLocalHostIP(argument))
441     {
442         Log(LOG_LEVEL_WARNING, "Bootstrapping to loopback interface (localhost), other hosts will not be able to bootstrap to this server");
443     }
444 
445     // temporary assure that network functions are working
446     OpenNetwork();
447 
448     config->agent_specific.agent.bootstrap_argument = xstrdup(argument);
449 
450     char *host, *port;
451     ParseHostPort(optarg, &host, &port);
452 
453     char ipaddr[CF_MAX_IP_LEN] = "";
454     if (Hostname2IPString(ipaddr, host,sizeof(ipaddr)) == -1)
455     {
456         Log(LOG_LEVEL_ERR,
457             "Could not resolve hostname '%s', unable to bootstrap",
458             host);
459         DoCleanupAndExit(EXIT_FAILURE);
460     }
461 
462     CloseNetwork();
463 
464     MINUSF = true;
465     config->ignore_locks = true;
466     GenericAgentConfigSetInputFile(config, GetInputDir(), "promises.cf");
467 
468     config->agent_specific.agent.bootstrap_ip = xstrdup(ipaddr);
469     config->agent_specific.agent.bootstrap_host = xstrdup(host);
470 
471     if (port == NULL)
472     {
473         config->agent_specific.agent.bootstrap_port = NULL;
474     }
475     else
476     {
477         config->agent_specific.agent.bootstrap_port = xstrdup(port);
478     }
479 }
480 
CheckOpts(int argc,char ** argv)481 static GenericAgentConfig *CheckOpts(int argc, char **argv)
482 {
483     extern char *optarg;
484     int c;
485 
486     GenericAgentConfig *config = GenericAgentConfigNewDefault(AGENT_TYPE_AGENT, GetTTYInteractive());
487     bool option_trust_server = false;
488 ;
489 /* DEPRECATED:
490    --policy-server (-s) is deprecated in community version 3.5.0.
491    Support rewrite from some common old bootstrap options (until community version 3.6.0?).
492  */
493 
494     int argc_new = argc;
495     char **argv_tmp = TranslateOldBootstrapOptionsSeparate(&argc_new, argv);
496     char **argv_new = TranslateOldBootstrapOptionsConcatenated(argc_new, argv_tmp);
497     FreeFixedStringArray(argc_new, argv_tmp);
498 
499     int longopt_idx;
500     while ((c = getopt_long(argc_new, argv_new, "tdvnKIf:g:w:D:N:VxMB:b:hC::ElT::",
501                             OPTIONS, &longopt_idx))
502            != -1)
503     {
504         switch (c)
505         {
506         case 't':
507             TIMING = true;
508             break;
509 
510         case 'w':
511             Log(LOG_LEVEL_INFO, "Setting workdir to '%s'", optarg);
512             setenv_wrapper("CFENGINE_TEST_OVERRIDE_WORKDIR", optarg, 1);
513             break;
514 
515         case 'f':
516             GenericAgentConfigSetInputFile(config, GetInputDir(), optarg);
517             MINUSF = true;
518             break;
519 
520         case 'b':
521             if (optarg)
522             {
523                 Rlist *bundlesequence = RlistFromSplitString(optarg, ',');
524                 GenericAgentConfigSetBundleSequence(config, bundlesequence);
525                 RlistDestroy(bundlesequence);
526             }
527             break;
528 
529         case 'd':
530             LogSetGlobalLevel(LOG_LEVEL_DEBUG);
531             break;
532 
533         case 'B':
534             {
535                 ConfigureBootstrap(config, optarg);
536             }
537             break;
538 
539         case 'K':
540             config->ignore_locks = true;
541             break;
542 
543         case 'D':
544             {
545                 StringSet *defined_classes = StringSetFromString(optarg, ',');
546                 if (! config->heap_soft)
547                 {
548                     config->heap_soft = defined_classes;
549                 }
550                 else
551                 {
552                     StringSetJoin(config->heap_soft, defined_classes, xstrdup);
553                     StringSetDestroy(defined_classes);
554                 }
555             }
556             break;
557 
558         case 'N':
559             {
560                 StringSet *negated_classes = StringSetFromString(optarg, ',');
561                 if (! config->heap_negated)
562                 {
563                     config->heap_negated = negated_classes;
564                 }
565                 else
566                 {
567                     StringSetJoin(config->heap_negated, negated_classes, xstrdup);
568                     StringSetDestroy(negated_classes);
569                 }
570             }
571             break;
572 
573         case 'I':
574             LogSetGlobalLevel(LOG_LEVEL_INFO);
575             break;
576 
577         case 'v':
578             LogSetGlobalLevel(LOG_LEVEL_VERBOSE);
579             break;
580 
581         case 'g':
582             LogSetGlobalLevelArgOrExit(optarg);
583             break;
584 
585         case 'n':
586             EVAL_MODE = EVAL_MODE_DRY_RUN;
587             config->ignore_locks = true;
588             break;
589 
590         case 'V':
591             {
592                 Writer *w = FileWriter(stdout);
593                 GenericAgentWriteVersion(w);
594                 FileWriterDetach(w);
595             }
596             DoCleanupAndExit(EXIT_SUCCESS);
597 
598         case 'h':
599             {
600                 Writer *w = FileWriter(stdout);
601                 WriterWriteHelp(w, "cf-agent", OPTIONS, HINTS, NULL, false, true);
602                 FileWriterDetach(w);
603             }
604             DoCleanupAndExit(EXIT_SUCCESS);
605 
606         case 'M':
607             {
608                 Writer *out = FileWriter(stdout);
609                 ManPageWrite(out, "cf-agent", time(NULL),
610                              CF_AGENT_SHORT_DESCRIPTION,
611                              CF_AGENT_MANPAGE_LONG_DESCRIPTION,
612                              OPTIONS, HINTS,
613                              NULL, false,
614                              true);
615                 FileWriterDetach(out);
616                 DoCleanupAndExit(EXIT_SUCCESS);
617             }
618 
619         case 'x':
620             {
621                 const char *workdir = GetWorkDir();
622                 const char *inputdir = GetInputDir();
623                 const char *logdir = GetLogDir();
624                 const char *statedir = GetStateDir();
625                 Writer *out = FileWriter(stdout);
626                 WriterWriteF(out, "self-diagnostics for agent using workdir '%s'\n", workdir);
627                 WriterWriteF(out, "self-diagnostics for agent using inputdir '%s'\n", inputdir);
628                 WriterWriteF(out, "self-diagnostics for agent using logdir '%s'\n", logdir);
629                 WriterWriteF(out, "self-diagnostics for agent using statedir '%s'\n", statedir);
630 
631                 AgentDiagnosticsRun(workdir, AgentDiagnosticsAllChecks(), out);
632                 AgentDiagnosticsRunAllChecksNova(workdir, out, &AgentDiagnosticsRun, &AgentDiagnosticsResultNew);
633                 FileWriterDetach(out);
634             }
635             DoCleanupAndExit(EXIT_SUCCESS);
636 
637         case 'C':
638             if (!GenericAgentConfigParseColor(config, optarg))
639             {
640                 DoCleanupAndExit(EXIT_FAILURE);
641             }
642             break;
643 
644         case 'E':
645             extension_libraries_disable();
646             break;
647 
648         case 'l':
649             LoggingEnableTimestamps(true);
650             break;
651 
652         case 'T':
653             option_trust_server = true;
654 
655             /* If the argument is missing, we trust by default. */
656             if (optarg == NULL || strcmp(optarg, "yes") == 0)
657             {
658                 config->agent_specific.agent.bootstrap_trust_server = true;
659             }
660             else
661             {
662                 config->agent_specific.agent.bootstrap_trust_server = false;
663             }
664 
665             break;
666 
667         /* long options only */
668         case 0:
669         {
670             const char *const option_name = OPTIONS[longopt_idx].name;
671             if (StringEqual(option_name, "ignore-preferred-augments"))
672             {
673                 config->ignore_preferred_augments = true;
674             }
675             else if (StringEqual(option_name, "log-modules"))
676             {
677                 bool ret = LogEnableModulesFromString(optarg);
678                 if (!ret)
679                 {
680                     DoCleanupAndExit(EXIT_FAILURE);
681                 }
682             }
683             else if (StringEqual(option_name, "show-evaluated-classes"))
684             {
685                 if (optarg == NULL)
686                 {
687                     optarg = ".*";
688                 }
689                 config->agent_specific.agent.show_evaluated_classes = xstrdup(optarg);
690             }
691             else if (StringEqual(option_name, "show-evaluated-vars"))
692             {
693                 if (optarg == NULL)
694                 {
695                     optarg = ".*";
696                 }
697                 config->agent_specific.agent.show_evaluated_variables = xstrdup(optarg);
698             }
699             else if (StringEqual(option_name, "skip-bootstrap-policy-run"))
700             {
701                 config->agent_specific.agent.bootstrap_trigger_policy = false;
702             }
703             else if (StringEqual(option_name, "skip-db-check"))
704             {
705                 if (optarg == NULL)
706                 {
707                     PERFORM_DB_CHECK = false; // Skip (no arg), check = false
708                 }
709                 else if (StringEqual_IgnoreCase(optarg, "yes"))
710                 {
711                     PERFORM_DB_CHECK = false; // Skip = yes, check = false
712                 }
713                 else if (StringEqual_IgnoreCase(optarg, "no"))
714                 {
715                     PERFORM_DB_CHECK = true; // Skip = no, check = true
716                 }
717                 else
718                 {
719                     Log(LOG_LEVEL_ERR,
720                         "Invalid argument for --skip-db-check(yes/no): '%s'",
721                         optarg);
722                     DoCleanupAndExit(EXIT_FAILURE);
723                 }
724             }
725             else if (StringEqual(option_name, "simulate"))
726             {
727                 if (optarg == NULL)
728                 {
729                     Log(LOG_LEVEL_ERR,
730                         "Missing argument for --simulate, 'manifest', 'manifest-full', or 'diff' required");
731                     DoCleanupAndExit(EXIT_FAILURE);
732                 }
733                 else if (StringEqual_IgnoreCase(optarg, "manifest"))
734                 {
735                     EVAL_MODE = EVAL_MODE_SIMULATE_MANIFEST;
736                 }
737                 else if (StringEqual_IgnoreCase(optarg, "manifest-full"))
738                 {
739                     EVAL_MODE = EVAL_MODE_SIMULATE_MANIFEST_FULL;
740                 }
741                 else if (StringEqual_IgnoreCase(optarg, "diff"))
742                 {
743                     EVAL_MODE = EVAL_MODE_SIMULATE_DIFF;
744                 }
745                 else
746                 {
747                     Log(LOG_LEVEL_ERR,
748                         "Invalid argument for --simulate, 'manifest' or 'diff' required, not '%s'",
749                         optarg);
750                     DoCleanupAndExit(EXIT_FAILURE);
751                 }
752             }
753             break;
754         }
755         default:
756             {
757                 Writer *w = FileWriter(stdout);
758                 WriterWriteHelp(w, "cf-agent", OPTIONS, HINTS, NULL, false, true);
759                 FileWriterDetach(w);
760             }
761             DoCleanupAndExit(EXIT_FAILURE);
762         }
763     }
764 
765     if (!GenericAgentConfigParseArguments(config, argc_new - optind,
766                                           argv_new + optind))
767     {
768         Log(LOG_LEVEL_ERR, "Too many arguments");
769         DoCleanupAndExit(EXIT_FAILURE);
770     }
771 
772     if (option_trust_server &&
773         config->agent_specific.agent.bootstrap_argument == NULL)
774     {
775         Log(LOG_LEVEL_ERR,
776             "Option --trust-server can only be used when bootstrapping");
777         DoCleanupAndExit(EXIT_FAILURE);
778     }
779 
780     FreeFixedStringArray(argc_new, argv_new);
781 
782     return config;
783 }
784 
785 
TranslateOldBootstrapOptionsSeparate(int * argc_new,char ** argv)786 static char **TranslateOldBootstrapOptionsSeparate(int *argc_new, char **argv)
787 {
788     int i;
789     int policy_server_argnum = 0;
790     int server_address_argnum = 0;
791     int bootstrap_argnum = 0;
792     int argc = *argc_new;
793 
794     for(i = 0; i < argc; i++)
795     {
796         if(strcmp(argv[i], "--policy-server") == 0 || strcmp(argv[i], "-s") == 0)
797         {
798             policy_server_argnum = i;
799         }
800 
801         if(strcmp(argv[i], "--bootstrap") == 0 || strcmp(argv[i], "-B") == 0)
802         {
803             bootstrap_argnum = i;
804         }
805     }
806 
807     if(policy_server_argnum > 0)
808     {
809         if(policy_server_argnum + 1 < argc)
810         {
811             server_address_argnum = policy_server_argnum + 1;
812         }
813     }
814 
815     char **argv_new;
816 
817     if(bootstrap_argnum > 0 && server_address_argnum > 0)
818     {
819         Log(LOG_LEVEL_WARNING, "Deprecated bootstrap options detected. The --policy-server (-s) option is deprecated from CFEngine community version 3.5.0."
820             "Please provide the address argument to --bootstrap (-B) instead. Rewriting your arguments now, but you need to adjust them as this support will be removed soon.");
821 
822         *argc_new = argc - 1;  // --policy-server deprecated
823         argv_new = xcalloc(1, sizeof(char *) * (*argc_new + 1));
824 
825         int new_i = 0;
826 
827         for(i = 0; i < argc; i++)
828         {
829             if(i == bootstrap_argnum)
830             {
831                 argv_new[new_i++] = xstrdup(argv[bootstrap_argnum]);
832                 argv_new[new_i++] = xstrdup(argv[server_address_argnum]);
833             }
834             else if(i == server_address_argnum)
835             {
836                 // skip: handled above
837             }
838             else if(i == policy_server_argnum)
839             {
840                 // skip: deprecated
841             }
842             else
843             {
844                 argv_new[new_i++] = xstrdup(argv[i]);
845             }
846         }
847     }
848     else
849     {
850         argv_new = xcalloc(1, sizeof(char *) * (*argc_new + 1));
851 
852         for(i = 0; i < argc; i++)
853         {
854             argv_new[i] = xstrdup(argv[i]);
855         }
856     }
857 
858     return argv_new;
859 }
860 
861 
TranslateOldBootstrapOptionsConcatenated(int argc,char ** argv)862 static char **TranslateOldBootstrapOptionsConcatenated(int argc, char **argv)
863 {
864     char **argv_new = xcalloc(1, sizeof(char *) * (argc + 1));
865 
866     for(int i = 0; i < argc; i++)
867     {
868         if(strcmp(argv[i], "-Bs") == 0)
869         {
870             Log(LOG_LEVEL_WARNING, "Deprecated bootstrap options detected. The --policy-server (-s) option is deprecated from CFEngine community version 3.5.0."
871                 "Please provide the address argument to --bootstrap (-B) instead. Rewriting your arguments now, but you need to adjust them as this support will be removed soon.");
872             argv_new[i] = xstrdup("-B");
873         }
874         else
875         {
876             argv_new[i] = xstrdup(argv[i]);
877         }
878     }
879 
880     return argv_new;
881 }
882 
883 
FreeFixedStringArray(int size,char ** array)884 static void FreeFixedStringArray(int size, char **array)
885 {
886     for(int i = 0; i < size; i++)
887     {
888         free(array[i]);
889     }
890 
891     free(array);
892 }
893 
894 /*******************************************************************/
895 
ThisAgentInit(void)896 static void ThisAgentInit(void)
897 {
898     char filename[CF_BUFSIZE];
899 
900 #ifdef HAVE_SETSID
901     setsid();
902 #endif
903 
904     CFA_MAXTHREADS = 30;
905     EDITFILESIZE = 100000;
906 
907 /*
908   do not set signal(SIGCHLD,SIG_IGN) in agent near
909   popen() - or else pclose will fail to return
910   status which we need for setting returns
911 */
912 
913     snprintf(filename, CF_BUFSIZE, "%s/cfagent.%s.log", GetLogDir(), VSYSNAME.nodename);
914     ToLowerStrInplace(filename);
915     MapName(filename);
916 
917     const mode_t current_umask = umask(0777);  // Gets and changes umask
918     umask(current_umask); // Restores umask
919     Log(LOG_LEVEL_DEBUG, "Current umask is %o", current_umask);
920     FILE *fp = safe_fopen(filename, "a");
921     if (fp != NULL)
922     {
923         fclose(fp);
924     }
925 
926     InitializeCustomPromises();
927 }
928 
929 /*******************************************************************/
930 
KeepPromises(EvalContext * ctx,const Policy * policy,GenericAgentConfig * config)931 static void KeepPromises(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config)
932 {
933     KeepControlPromises(ctx, policy, config);
934     /* Check if 'abortclasses' aborted evaluation or not. */
935     if (EvalAborted(ctx))
936     {
937         return;
938     }
939     KeepPromiseBundles(ctx, policy, config);
940 }
941 
942 /*******************************************************************/
943 /* Level 2                                                         */
944 /*******************************************************************/
945 
KeepControlPromises(EvalContext * ctx,const Policy * policy,GenericAgentConfig * config)946 static void KeepControlPromises(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config)
947 {
948     Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_AGENT);
949     if (constraints)
950     {
951         for (size_t i = 0; i < SeqLength(constraints); i++)
952         {
953             Constraint *cp = SeqAt(constraints, i);
954 
955             if (!IsDefinedClass(ctx, cp->classes))
956             {
957                 continue;
958             }
959 
960             if (CommonControlFromString(cp->lval) != COMMON_CONTROL_MAX)
961             {
962                 /* Already handled in generic_agent */
963                 continue;
964             }
965 
966             VarRef *ref = VarRefParseFromScope(cp->lval, "control_agent");
967             DataType value_type;
968             const void *value = EvalContextVariableGet(ctx, ref, &value_type);
969             VarRefDestroy(ref);
970 
971             /* If var not found */
972             if (value_type == CF_DATA_TYPE_NONE)
973             {
974                 Log(LOG_LEVEL_ERR, "Unknown lval '%s' in agent control body", cp->lval);
975                 continue;
976             }
977 
978             /* 'files_single_copy => { }' is a perfectly valid case. */
979             if (StringEqual(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_FSINGLECOPY].lval))
980             {
981                 assert(value_type == CF_DATA_TYPE_STRING_LIST);
982                 SINGLE_COPY_LIST = value;
983                 SINGLE_COPY_CACHE = StringSetNew();
984                 if (WouldLog(LOG_LEVEL_VERBOSE))
985                 {
986                     char *rlist_str = RlistToString(SINGLE_COPY_LIST);
987                     Log(LOG_LEVEL_VERBOSE, "Setting file single copy list to: %s", rlist_str);
988                     free(rlist_str);
989                 }
990                 continue;
991             }
992 
993             /* Empty list is not supported for the other constraints/attributes. */
994             if (value == NULL)
995             {
996                 Log(LOG_LEVEL_ERR,
997                     "Empty list is not a valid value for '%s' attribute in agent control body",
998                     cp->lval);
999                 continue;
1000             }
1001 
1002             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_MAXCONNECTIONS].lval) == 0)
1003             {
1004                 CFA_MAXTHREADS = (int) IntFromString(value);
1005                 Log(LOG_LEVEL_VERBOSE, "Setting maxconnections to %d", CFA_MAXTHREADS);
1006                 continue;
1007             }
1008 
1009             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_CHECKSUM_ALERT_TIME].lval) == 0)
1010             {
1011                 CF_PERSISTENCE = (int) IntFromString(value);
1012                 Log(LOG_LEVEL_VERBOSE, "Setting checksum_alert_time to %d", CF_PERSISTENCE);
1013                 continue;
1014             }
1015 
1016             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_AGENTFACILITY].lval) == 0)
1017             {
1018                 SetFacility(value);
1019                 continue;
1020             }
1021 
1022             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_AGENTACCESS].lval) == 0)
1023             {
1024                 ACCESSLIST = value;
1025                 CheckAgentAccess(ACCESSLIST, policy);
1026                 continue;
1027             }
1028 
1029             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_REFRESH_PROCESSES].lval) == 0)
1030             {
1031                 Log(LOG_LEVEL_VERBOSE, "Setting refresh_processes when starting to...");
1032                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
1033                 {
1034                     Log(LOG_LEVEL_VERBOSE, "%s", RlistScalarValue(rp));
1035                     // TODO: why is this only done in verbose mode?
1036                     // original commit says 'optimization'.
1037                     if (LogGetGlobalLevel() >= LOG_LEVEL_VERBOSE)
1038                     {
1039                         PrependItem(&PROCESSREFRESH, RlistScalarValue(rp), NULL);
1040                     }
1041                 }
1042                 continue;
1043             }
1044 
1045             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ABORTCLASSES].lval) == 0)
1046             {
1047                 Log(LOG_LEVEL_VERBOSE, "Setting abort classes from ...");
1048 
1049                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
1050                 {
1051                     char name[CF_MAXVARSIZE] = "";
1052 
1053                     strlcpy(name, RlistScalarValue(rp), CF_MAXVARSIZE);
1054 
1055                     EvalContextHeapAddAbort(ctx, name, cp->classes);
1056                 }
1057 
1058                 continue;
1059             }
1060 
1061             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ABORTBUNDLECLASSES].lval) == 0)
1062             {
1063                 Log(LOG_LEVEL_VERBOSE, "Setting abort bundle classes from ...");
1064 
1065                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
1066                 {
1067                     char name[CF_MAXVARSIZE] = "";
1068                     strlcpy(name, RlistScalarValue(rp), CF_MAXVARSIZE);
1069 
1070                     EvalContextHeapAddAbortCurrentBundle(ctx, name, cp->classes);
1071                 }
1072 
1073                 continue;
1074             }
1075 
1076             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ADDCLASSES].lval) == 0)
1077             {
1078                 Log(LOG_LEVEL_VERBOSE, "Add classes ...");
1079 
1080                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
1081                 {
1082                     Log(LOG_LEVEL_VERBOSE, "... %s", RlistScalarValue(rp));
1083                     EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=environment");
1084                 }
1085 
1086                 continue;
1087             }
1088 
1089             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ALWAYSVALIDATE].lval) == 0)
1090             {
1091                 ALWAYS_VALIDATE = BooleanFromString(value);
1092                 Log(LOG_LEVEL_VERBOSE, "Setting alwaysvalidate to '%s'", ALWAYS_VALIDATE ? "true" : "false");
1093                 continue;
1094             }
1095 
1096             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ALLCLASSESREPORT].lval) == 0)
1097             {
1098                 ALLCLASSESREPORT = BooleanFromString(value);
1099                 Log(LOG_LEVEL_VERBOSE, "Setting allclassesreport to '%s'", ALLCLASSESREPORT ? "true" : "false");
1100             }
1101 
1102             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_SECUREINPUT].lval) == 0)
1103             {
1104                 CFPARANOID = BooleanFromString(value);
1105                 Log(LOG_LEVEL_VERBOSE, "Setting secure input to '%s'", CFPARANOID ? "true" : "false");
1106                 continue;
1107             }
1108 
1109             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_BINDTOINTERFACE].lval) == 0)
1110             {
1111                 SetBindInterface(value);
1112                 continue;
1113             }
1114 
1115             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_HASHUPDATES].lval) == 0)
1116             {
1117                 bool enabled = BooleanFromString(value);
1118 
1119                 SetChecksumUpdatesDefault(ctx, enabled);
1120                 Log(LOG_LEVEL_VERBOSE, "Setting checksum updates to '%s'", enabled ? "true" : "false");
1121                 continue;
1122             }
1123 
1124             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_CHILDLIBPATH].lval) == 0)
1125             {
1126                 Log(LOG_LEVEL_VERBOSE, "Setting 'LD_LIBRARY_PATH=%s'", (const char *)value);
1127                 setenv_wrapper("LD_LIBRARY_PATH", value, 1);
1128                 continue;
1129             }
1130 
1131             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_DEFAULTCOPYTYPE].lval) == 0)
1132             {
1133                 DEFAULT_COPYTYPE = value;
1134                 Log(LOG_LEVEL_VERBOSE, "Setting defaultcopytype to '%s'", DEFAULT_COPYTYPE);
1135                 continue;
1136             }
1137 
1138             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_FAUTODEFINE].lval) == 0)
1139             {
1140                 SetFileAutoDefineList(value);
1141                 Log(LOG_LEVEL_VERBOSE, "Setting file auto define list");
1142                 continue;
1143             }
1144 
1145             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_DRYRUN].lval) == 0)
1146             {
1147                 EVAL_MODE = BooleanFromString(value) ? EVAL_MODE_DRY_RUN : EVAL_MODE_NORMAL;
1148                 Log(LOG_LEVEL_VERBOSE, "Setting dryrun to %d", DONTDO);
1149                 continue;
1150             }
1151 
1152             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_INFORM].lval) == 0)
1153             {
1154                 bool inform = BooleanFromString(value);
1155                 if (inform)
1156                 {
1157                     LogSetGlobalLevel(MAX(LOG_LEVEL_INFO, LogGetGlobalLevel()));
1158                 }
1159                 else
1160                 {
1161                     if (LogGetGlobalLevel() >= LOG_LEVEL_INFO)
1162                     {
1163                         LogSetGlobalLevel(LOG_LEVEL_NOTICE);
1164                     }
1165                 }
1166                 Log(LOG_LEVEL_VERBOSE, "body agent control, inform => '%s', sets new log level to '%s'",
1167                     inform ? "true" : "false", LogLevelToString(LogGetGlobalLevel()));
1168                 continue;
1169             }
1170 
1171             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_VERBOSE].lval) == 0)
1172             {
1173                 bool verbose = BooleanFromString(value);
1174                 if (verbose)
1175                 {
1176                     LogSetGlobalLevel(MAX(LOG_LEVEL_VERBOSE, LogGetGlobalLevel()));
1177                 }
1178                 else
1179                 {
1180                     if (LogGetGlobalLevel() >= LOG_LEVEL_VERBOSE)
1181                     {
1182                         LogSetGlobalLevel(LOG_LEVEL_INFO);
1183                     }
1184                 }
1185                 Log(LOG_LEVEL_VERBOSE, "body agent control, verbose => '%s', sets new log level to '%s'",
1186                     verbose ? "true" : "false", LogLevelToString(LogGetGlobalLevel()));
1187                 continue;
1188             }
1189 
1190             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_REPOSITORY].lval) == 0)
1191             {
1192                 SetRepositoryLocation(value);
1193                 Log(LOG_LEVEL_VERBOSE, "Setting repository to '%s'", (const char *)value);
1194                 continue;
1195             }
1196 
1197             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_SKIPIDENTIFY].lval) == 0)
1198             {
1199                 bool enabled = BooleanFromString(value);
1200 
1201                 SetSkipIdentify(enabled);
1202                 Log(LOG_LEVEL_VERBOSE, "Setting skipidentify to '%s'", enabled ? "true" : "false");
1203                 continue;
1204             }
1205 
1206             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_SUSPICIOUSNAMES].lval) == 0)
1207             {
1208                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
1209                 {
1210                     AddFilenameToListOfSuspicious(RlistScalarValue(rp));
1211                     Log(LOG_LEVEL_VERBOSE, "Considering '%s' as suspicious file", RlistScalarValue(rp));
1212                 }
1213 
1214                 continue;
1215             }
1216 
1217             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_REPCHAR].lval) == 0)
1218             {
1219                 char c = *(char *)value;
1220 
1221                 SetRepositoryChar(c);
1222                 Log(LOG_LEVEL_VERBOSE, "Setting repchar to '%c'", c);
1223                 continue;
1224             }
1225 
1226             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_MOUNTFILESYSTEMS].lval) == 0)
1227             {
1228                 CF_MOUNTALL = BooleanFromString(value);
1229                 Log(LOG_LEVEL_VERBOSE, "Setting mountfilesystems to '%s'", CF_MOUNTALL ? "true" : "false");
1230                 continue;
1231             }
1232 
1233             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_EDITFILESIZE].lval) == 0)
1234             {
1235                 EDITFILESIZE = IntFromString(value);
1236                 Log(LOG_LEVEL_VERBOSE, "Setting edit file size to %d", EDITFILESIZE);
1237                 continue;
1238             }
1239 
1240             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_IFELAPSED].lval) == 0)
1241             {
1242                 VIFELAPSED = IntFromString(value);
1243                 Log(LOG_LEVEL_VERBOSE, "Setting ifelapsed to %d", VIFELAPSED);
1244                 continue;
1245             }
1246 
1247             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_EXPIREAFTER].lval) == 0)
1248             {
1249                 VEXPIREAFTER = IntFromString(value);
1250                 Log(LOG_LEVEL_VERBOSE, "Setting expireafter to %d", VEXPIREAFTER);
1251                 continue;
1252             }
1253 
1254             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_TIMEOUT].lval) == 0)
1255             {
1256                 CONNTIMEOUT = IntFromString(value);
1257                 Log(LOG_LEVEL_VERBOSE, "Setting timeout to %jd", (intmax_t) CONNTIMEOUT);
1258                 continue;
1259             }
1260 
1261             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_MAX_CHILDREN].lval) == 0)
1262             {
1263                 CFA_BACKGROUND_LIMIT = IntFromString(value);
1264                 Log(LOG_LEVEL_VERBOSE, "Setting max_children to %d", CFA_BACKGROUND_LIMIT);
1265                 if (CFA_BACKGROUND_LIMIT > 10)
1266                 {
1267                     Log(LOG_LEVEL_ERR, "Silly value for max_children in agent control promise (%d > 10)",
1268                           CFA_BACKGROUND_LIMIT);
1269                     CFA_BACKGROUND_LIMIT = 1;
1270                 }
1271                 continue;
1272             }
1273 
1274             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ENVIRONMENT].lval) == 0)
1275             {
1276                 Log(LOG_LEVEL_VERBOSE, "Setting environment variables from ...");
1277 
1278                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
1279                 {
1280                     assert(strchr(RlistScalarValue(rp), '=')); /* Valid for putenv() */
1281                     if (putenv_wrapper(RlistScalarValue(rp)) != 0)
1282                     {
1283                         Log(LOG_LEVEL_ERR, "Failed to set environment variable '%s'. (putenv: %s)",
1284                             RlistScalarValue(rp), GetErrorStr());
1285                     }
1286                 }
1287 
1288                 continue;
1289             }
1290 
1291             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_SELECT_END_MATCH_EOF].lval) == 0)
1292             {
1293                 Log(LOG_LEVEL_VERBOSE, "SET select_end_match_eof %s", (char *) value);
1294                 EvalContextSetSelectEndMatchEof(ctx, BooleanFromString(value));
1295             }
1296 
1297             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_REPORTCLASSLOG].lval) == 0)
1298             {
1299                 config->agent_specific.agent.report_class_log = BooleanFromString(value);
1300 
1301                 Log(LOG_LEVEL_VERBOSE, "Setting report_class_log to %s",
1302                     config->agent_specific.agent.report_class_log? "true" : "false");
1303                 continue;
1304             }
1305         }
1306     }
1307 
1308     const void *value = NULL;
1309     if ((value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_LASTSEEN_EXPIRE_AFTER)))
1310     {
1311         LASTSEENEXPIREAFTER = IntFromString(value) * 60;
1312     }
1313 
1314     if ((value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_FIPS_MODE)))
1315     {
1316         FIPS_MODE = BooleanFromString(value);
1317         Log(LOG_LEVEL_VERBOSE, "Setting FIPS mode to '%s'", FIPS_MODE ? "true" : "false");
1318     }
1319 
1320     if ((value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_SYSLOG_PORT)))
1321     {
1322         SetSyslogPort(IntFromString(value));
1323         Log(LOG_LEVEL_VERBOSE, "Setting syslog_port to '%s'", (const char *)value);
1324     }
1325 
1326     if ((value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_SYSLOG_HOST)))
1327     {
1328         /* Don't resolve syslog_host now, better do it per log request. */
1329         if (!SetSyslogHost(value))
1330         {
1331             Log(LOG_LEVEL_ERR,
1332                   "Failed to set syslog_host to '%s', too long", (const char *)value);
1333         }
1334         else
1335         {
1336             Log(LOG_LEVEL_VERBOSE, "Setting syslog_host to '%s'", (const char *)value);
1337         }
1338     }
1339 
1340     if ((value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_BWLIMIT)))
1341     {
1342         double bval;
1343         if (DoubleFromString(value, &bval))
1344         {
1345             bwlimit_kbytes = (uint32_t) ( bval / 1000.0);
1346             Log(LOG_LEVEL_VERBOSE, "Setting rate limit to %d kBytes/sec", bwlimit_kbytes);
1347         }
1348     }
1349     Nova_Initialize(ctx);
1350 
1351     // If not have been enabled above then should be disabled.
1352     // By default it's enabled to catch all set classes on startup stage
1353     // before this part of the policy is processed.
1354     if (!config->agent_specific.agent.report_class_log)
1355     {
1356         EvalContextAllClassesLoggingEnable(ctx, false);
1357     }
1358 }
1359 
1360 /*********************************************************************/
1361 
KeepPromiseBundles(EvalContext * ctx,const Policy * policy,GenericAgentConfig * config)1362 static void KeepPromiseBundles(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config)
1363 {
1364     Rlist *bundlesequence = NULL;
1365 
1366     Banner("Begin policy/promise evaluation");
1367 
1368     if (config->bundlesequence != NULL)
1369     {
1370         Log(LOG_LEVEL_INFO, "Using command line specified bundlesequence");
1371         bundlesequence = RlistCopy(config->bundlesequence);
1372     }
1373     else
1374     {
1375         bundlesequence = RlistCopy((Rlist *) EvalContextVariableControlCommonGet(
1376                                        ctx, COMMON_CONTROL_BUNDLESEQUENCE));
1377 
1378         if (bundlesequence == NULL)
1379         {
1380             RlistAppendScalar(&bundlesequence, "main");
1381         }
1382     }
1383 
1384     bool ok = true;
1385     for (const Rlist *rp = bundlesequence; rp; rp = rp->next)
1386     {
1387         const char *name = NULL;
1388 
1389         switch (rp->val.type)
1390         {
1391         case RVAL_TYPE_SCALAR:
1392             name = RlistScalarValue(rp);
1393             break;
1394         case RVAL_TYPE_FNCALL:
1395             name = RlistFnCallValue(rp)->name;
1396             break;
1397 
1398         default:
1399             name = NULL;
1400             {
1401                 Writer *w = StringWriter();
1402                 WriterWrite(w, "Illegal item found in bundlesequence: ");
1403                 RvalWrite(w, rp->val);
1404                 Log(LOG_LEVEL_ERR, "%s", StringWriterData(w));
1405                 WriterClose(w);
1406             }
1407             ok = false;
1408             break;
1409         }
1410 
1411         if (!config->ignore_missing_bundles)
1412         {
1413             const Bundle *bp = EvalContextResolveBundleExpression(ctx, policy, name, "agent");
1414             if (!bp)
1415             {
1416                 bp = EvalContextResolveBundleExpression(ctx, policy, name, "common");
1417             }
1418 
1419             if (!bp)
1420             {
1421                 Log(LOG_LEVEL_ERR, "Bundle '%s' listed in the bundlesequence was not found", name);
1422                 ok = false;
1423             }
1424         }
1425     }
1426 
1427     if (!ok)
1428     {
1429         FatalError(ctx, "Errors in agent bundles");
1430     }
1431 
1432     Writer *w = StringWriter();
1433     WriterWrite(w, "Using bundlesequence => ");
1434     RlistWrite(w, bundlesequence);
1435     Log(LOG_LEVEL_VERBOSE, "%s", StringWriterData(w));
1436     WriterClose(w);
1437 
1438 /* If all is okay, go ahead and evaluate */
1439 
1440     for (const Rlist *rp = bundlesequence; rp; rp = rp->next)
1441     {
1442         const char *name = NULL;
1443         const Rlist *args = NULL;
1444 
1445         if (rp->val.type == RVAL_TYPE_FNCALL)
1446         {
1447             name = RlistFnCallValue(rp)->name;
1448             args = RlistFnCallValue(rp)->args;
1449         }
1450         else
1451         {
1452             name = RlistScalarValue(rp);
1453             args = NULL;
1454         }
1455 
1456         EvalContextSetBundleArgs(ctx, args);
1457 
1458         const Bundle *bp = EvalContextResolveBundleExpression(ctx, policy, name, "agent");
1459         if (!bp)
1460         {
1461             bp = EvalContextResolveBundleExpression(ctx, policy, name, "common");
1462         }
1463 
1464         if (bp)
1465         {
1466             BundleBanner(bp,args);
1467             EvalContextStackPushBundleFrame(ctx, bp, args, false);
1468             ScheduleAgentOperations(ctx, bp);
1469             EvalContextStackPopFrame(ctx);
1470             EndBundleBanner(bp);
1471             if (EvalAborted(ctx))
1472             {
1473                 break;
1474             }
1475         }
1476         else
1477         {
1478             if (config->ignore_missing_bundles)
1479             {
1480                 Log(LOG_LEVEL_VERBOSE, "Ignoring missing bundle '%s'", name);
1481             }
1482             else
1483             {
1484                 FatalError(ctx, "Bundlesequence contained unknown bundle reference '%s'", name);
1485             }
1486         }
1487     }
1488 
1489     RlistDestroy(bundlesequence);
1490 }
1491 
AllClassesReport(const EvalContext * ctx)1492 static void AllClassesReport(const EvalContext *ctx)
1493 {
1494     char context_report_file[CF_BUFSIZE];
1495     snprintf(context_report_file, CF_BUFSIZE, "%s%callclasses.txt", GetStateDir(), FILE_SEPARATOR);
1496 
1497     FILE *fp = safe_fopen(context_report_file, "w");
1498     if (fp == NULL)
1499     {
1500         Log(LOG_LEVEL_INFO, "Could not open allclasses cache file '%s' (fopen: %s)", context_report_file, GetErrorStr());
1501     }
1502     else
1503     {
1504         Writer *writer = FileWriter(fp);
1505         ClassTableIterator *iter = EvalContextClassTableIteratorNewGlobal(ctx, NULL, true, true);
1506         Class *cls = NULL;
1507         while ((cls = ClassTableIteratorNext(iter)))
1508         {
1509             char *expr = ClassRefToString(cls->ns, cls->name);
1510             WriterWriteF(writer, "%s\n", expr);
1511             free(expr);
1512         }
1513         ClassTableIteratorDestroy(iter);
1514         WriterClose(writer);
1515     }
1516 }
1517 
ScheduleAgentOperations(EvalContext * ctx,const Bundle * bp)1518 PromiseResult ScheduleAgentOperations(EvalContext *ctx, const Bundle *bp)
1519 // NB - this function can be called recursively through "methods"
1520 {
1521     assert(bp != NULL);
1522 
1523     int save_pr_kept = PR_KEPT;
1524     int save_pr_repaired = PR_REPAIRED;
1525     int save_pr_notkept = PR_NOTKEPT;
1526     struct timespec start = BeginMeasure();
1527 
1528     if (PROCESSREFRESH == NULL || (PROCESSREFRESH && IsRegexItemIn(ctx, PROCESSREFRESH, bp->name)))
1529     {
1530         ClearProcessTable();
1531     }
1532 
1533     PromiseResult result = PROMISE_RESULT_SKIPPED;
1534 
1535     for (int pass = 1; pass < CF_DONEPASSES; pass++)
1536     {
1537         // Evaluate built-in (non-custom) promise types, according to type sequence (normal order):
1538         for (TypeSequence type = 0; AGENT_TYPESEQUENCE[type] != NULL; type++)
1539         {
1540             const BundleSection *sp = BundleGetSection((Bundle *)bp, AGENT_TYPESEQUENCE[type]);
1541 
1542             if (!sp || SeqLength(sp->promises) == 0)
1543             {
1544                 continue;
1545             }
1546 
1547             NewTypeContext(type);
1548 
1549             SpecialTypeBanner(type, pass);
1550             EvalContextStackPushBundleSectionFrame(ctx, sp);
1551 
1552             for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++)
1553             {
1554                 Promise *pp = SeqAt(sp->promises, ppi);
1555 
1556                 EvalContextSetPass(ctx, pass);
1557 
1558                 PromiseResult promise_result = ExpandPromise(ctx, pp, KeepAgentPromise, NULL);
1559                 result = PromiseResultUpdate(result, promise_result);
1560 
1561                 if (EvalAborted(ctx) || BundleAbort(ctx))
1562                 {
1563                     DeleteTypeContext(ctx, type);
1564                     EvalContextStackPopFrame(ctx);
1565                     NoteBundleCompliance(bp, save_pr_kept, save_pr_repaired, save_pr_notkept, start);
1566                     return result;
1567                 }
1568             }
1569 
1570             DeleteTypeContext(ctx, type);
1571             EvalContextStackPopFrame(ctx);
1572 
1573             if (type == TYPE_SEQUENCE_CONTEXTS)
1574             {
1575                 BundleResolve(ctx, bp);
1576                 BundleResolvePromiseType(ctx, bp, "defaults", DefaultVarPromiseWrapper);
1577             }
1578         }
1579 
1580         // Custom promises are evaluated at the end of an evaluation pass:
1581         const size_t sections = SeqLength(bp->custom_sections);
1582         for (size_t i = 0; i < sections; ++i)
1583         {
1584             BundleSection *section = SeqAt(bp->custom_sections, i);
1585 
1586             EvalContextStackPushBundleSectionFrame(ctx, section);
1587 
1588             const size_t promises = SeqLength(section->promises);
1589             for (size_t ppi = 0; ppi < promises; ppi++)
1590             {
1591                 Promise *pp = SeqAt(section->promises, ppi);
1592 
1593                 EvalContextSetPass(ctx, pass);
1594 
1595                 PromiseResult promise_result = ExpandPromise(ctx, pp, KeepAgentPromise, NULL);
1596                 result = PromiseResultUpdate(result, promise_result);
1597 
1598                 if (EvalAborted(ctx) || BundleAbort(ctx))
1599                 {
1600                     EvalContextStackPopFrame(ctx);
1601                     NoteBundleCompliance(bp, save_pr_kept, save_pr_repaired, save_pr_notkept, start);
1602                     return result;
1603                 }
1604             }
1605             EvalContextStackPopFrame(ctx);
1606         }
1607     }
1608 
1609     NoteBundleCompliance(bp, save_pr_kept, save_pr_repaired, save_pr_notkept, start);
1610     return result;
1611 }
1612 
1613 /*********************************************************************/
1614 
1615 #ifdef __MINGW32__
1616 
CheckAgentAccess(const Rlist * list,const Policy * policy)1617 static void CheckAgentAccess(const Rlist *list, const Policy *policy)
1618 {
1619 }
1620 
1621 #else
1622 
CheckAgentAccess(const Rlist * list,const Policy * policy)1623 static void CheckAgentAccess(const Rlist *list, const Policy *policy)
1624 {
1625     uid_t uid = getuid();
1626 
1627     for (const Rlist *rp = list; rp != NULL; rp = rp->next)
1628     {
1629         if (Str2Uid(RlistScalarValue(rp), NULL, NULL) == uid)
1630         {
1631             return;
1632         }
1633     }
1634 
1635     {
1636         StringSet *input_files = PolicySourceFiles(policy);
1637         StringSetIterator iter = StringSetIteratorInit(input_files);
1638         const char *input_file = NULL;
1639         while ((input_file = StringSetIteratorNext(&iter)))
1640         {
1641             struct stat sb;
1642             stat(input_file, &sb);
1643 
1644             if (ACCESSLIST)
1645             {
1646                 bool access = false;
1647                 for (const Rlist *rp2 = ACCESSLIST; rp2 != NULL; rp2 = rp2->next)
1648                 {
1649                     if (Str2Uid(RlistScalarValue(rp2), NULL, NULL) == sb.st_uid)
1650                     {
1651                         access = true;
1652                         break;
1653                     }
1654                 }
1655 
1656                 if (!access)
1657                 {
1658                     Log(LOG_LEVEL_ERR, "File '%s' is not owned by an authorized user (security exception)", input_file);
1659                     DoCleanupAndExit(EXIT_FAILURE);
1660                 }
1661             }
1662             else if (CFPARANOID && IsPrivileged())
1663             {
1664                 if (sb.st_uid != getuid())
1665                 {
1666                     Log(LOG_LEVEL_ERR, "File '%s' is not owned by uid %ju (security exception)", input_file,
1667                           (uintmax_t)getuid());
1668                     DoCleanupAndExit(EXIT_FAILURE);
1669                 }
1670             }
1671         }
1672 
1673         StringSetDestroy(input_files);
1674     }
1675 
1676     Log(LOG_LEVEL_ERR, "You are denied access to run this policy");
1677     DoCleanupAndExit(EXIT_FAILURE);
1678 }
1679 #endif /* !__MINGW32__ */
1680 
1681 /*********************************************************************/
1682 
DefaultVarPromise(EvalContext * ctx,const Promise * pp)1683 static PromiseResult DefaultVarPromise(EvalContext *ctx, const Promise *pp)
1684 {
1685     char *regex = PromiseGetConstraintAsRval(pp, "if_match_regex", RVAL_TYPE_SCALAR);
1686     bool okay = true;
1687 
1688 
1689     DataType value_type = CF_DATA_TYPE_NONE;
1690     const void *value = NULL;
1691     {
1692         VarRef *ref = VarRefParseFromScope(pp->promiser, "this");
1693         value = EvalContextVariableGet(ctx, ref, &value_type);
1694         VarRefDestroy(ref);
1695     }
1696 
1697     switch (value_type)
1698     {
1699     case CF_DATA_TYPE_STRING:
1700     case CF_DATA_TYPE_INT:
1701     case CF_DATA_TYPE_REAL:
1702         if (regex && !FullTextMatch(ctx, regex, value))
1703         {
1704             return PROMISE_RESULT_NOOP;
1705         }
1706 
1707         if (regex == NULL)
1708         {
1709             return PROMISE_RESULT_NOOP;
1710         }
1711         break;
1712 
1713     case CF_DATA_TYPE_STRING_LIST:
1714     case CF_DATA_TYPE_INT_LIST:
1715     case CF_DATA_TYPE_REAL_LIST:
1716         if (regex)
1717         {
1718             for (const Rlist *rp = value; rp != NULL; rp = rp->next)
1719             {
1720                 if (FullTextMatch(ctx, regex, RlistScalarValue(rp)))
1721                 {
1722                     okay = false;
1723                     break;
1724                 }
1725             }
1726 
1727             if (okay)
1728             {
1729                 return PROMISE_RESULT_NOOP;
1730             }
1731         }
1732         break;
1733 
1734     default:
1735         break;
1736     }
1737 
1738     {
1739         VarRef *ref = VarRefParseFromBundle(pp->promiser, PromiseGetBundle(pp));
1740         EvalContextVariableRemove(ctx, ref);
1741         VarRefDestroy(ref);
1742     }
1743 
1744     return VerifyVarPromise(ctx, pp, NULL);
1745 }
1746 
LogVariableValue(const EvalContext * ctx,const Promise * pp)1747 static void LogVariableValue(const EvalContext *ctx, const Promise *pp)
1748 {
1749     VarRef *ref = VarRefParseFromBundle(pp->promiser, PromiseGetBundle(pp));
1750     char *out = NULL;
1751 
1752     DataType type;
1753     const void *var = EvalContextVariableGet(ctx, ref, &type);
1754     switch (type)
1755     {
1756         case CF_DATA_TYPE_INT:
1757         case CF_DATA_TYPE_REAL:
1758         case CF_DATA_TYPE_STRING:
1759             out = xstrdup((char *) var);
1760             break;
1761         case CF_DATA_TYPE_INT_LIST:
1762         case CF_DATA_TYPE_REAL_LIST:
1763         case CF_DATA_TYPE_STRING_LIST:
1764         {
1765             size_t siz = CF_BUFSIZE;
1766             size_t len = 0;
1767             out = xcalloc(1, CF_BUFSIZE);
1768 
1769             for (Rlist *rp = (Rlist *) var; rp != NULL; rp = rp->next)
1770             {
1771                 const char *s = (char *) rp->val.item;
1772 
1773                 if (strlen(s) + len + 3  >= siz)                // ", " + NULL
1774                 {
1775                     out = xrealloc(out, siz + CF_BUFSIZE);
1776                     siz += CF_BUFSIZE;
1777                 }
1778 
1779                 if (len > 0)
1780                 {
1781                     len += strlcat(out, ", ", siz);
1782                 }
1783 
1784                 len += strlcat(out, s, siz);
1785             }
1786             break;
1787         }
1788         case CF_DATA_TYPE_CONTAINER:
1789         {
1790             Writer *w = StringWriter();
1791             JsonWriteCompact(w, (JsonElement *) var);
1792             out = StringWriterClose(w);
1793             break;
1794         }
1795         default:
1796             /* TODO is CF_DATA_TYPE_NONE acceptable? Today all meta variables
1797              * are of this type. */
1798             /* UnexpectedError("Variable '%s' is of unknown type %d", */
1799             /*                 pp->promiser, type); */
1800             out = xstrdup("NONE");
1801             break;
1802     }
1803 
1804     Log(LOG_LEVEL_DEBUG, "V: '%s' => '%s'", pp->promiser, out);
1805     free(out);
1806     VarRefDestroy(ref);
1807 }
1808 
KeepAgentPromise(EvalContext * ctx,const Promise * pp,ARG_UNUSED void * param)1809 static PromiseResult KeepAgentPromise(EvalContext *ctx, const Promise *pp, ARG_UNUSED void *param)
1810 {
1811     assert(param == NULL);
1812     assert(pp != NULL);
1813 
1814     struct timespec start = BeginMeasure();
1815     PromiseResult result = PROMISE_RESULT_NOOP;
1816 
1817     if (strcmp("meta", PromiseGetPromiseType(pp)) == 0 ||
1818         strcmp("vars", PromiseGetPromiseType(pp)) == 0)
1819     {
1820         Log(LOG_LEVEL_VERBOSE, "V:     Computing value of '%s'", pp->promiser);
1821 
1822         result = VerifyVarPromise(ctx, pp, NULL);
1823         if (result != PROMISE_RESULT_FAIL)
1824         {
1825             if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG)
1826             {
1827                 LogVariableValue(ctx, pp);
1828             }
1829         }
1830     }
1831     else if (strcmp("defaults", PromiseGetPromiseType(pp)) == 0)
1832     {
1833         result = DefaultVarPromise(ctx, pp);
1834     }
1835     else if (strcmp("classes", PromiseGetPromiseType(pp)) == 0)
1836     {
1837         result = VerifyClassPromise(ctx, pp, NULL);
1838     }
1839     else if (strcmp("processes", PromiseGetPromiseType(pp)) == 0)
1840     {
1841         if (!LoadProcessTable())
1842         {
1843             Log(LOG_LEVEL_ERR, "Unable to read the process table - cannot keep processes: type promises");
1844             return PROMISE_RESULT_FAIL;
1845         }
1846         result = VerifyProcessesPromise(ctx, pp);
1847         if (result != PROMISE_RESULT_SKIPPED)
1848         {
1849             EndMeasurePromise(start, pp);
1850         }
1851     }
1852     else if (strcmp("storage", PromiseGetPromiseType(pp)) == 0)
1853     {
1854         result = FindAndVerifyStoragePromises(ctx, pp);
1855         if (result != PROMISE_RESULT_SKIPPED)
1856         {
1857             EndMeasurePromise(start, pp);
1858         }
1859     }
1860     else if (strcmp("packages", PromiseGetPromiseType(pp)) == 0)
1861     {
1862         result = VerifyPackagesPromise(ctx, pp);
1863         if (result != PROMISE_RESULT_SKIPPED)
1864         {
1865             EndMeasurePromise(start, pp);
1866         }
1867     }
1868     else if (strcmp("users", PromiseGetPromiseType(pp)) == 0)
1869     {
1870         result = VerifyUsersPromise(ctx, pp);
1871         if (result != PROMISE_RESULT_SKIPPED)
1872         {
1873             EndMeasurePromise(start, pp);
1874         }
1875     }
1876 
1877     else if (strcmp("files", PromiseGetPromiseType(pp)) == 0)
1878     {
1879         result = ParallelFindAndVerifyFilesPromises(ctx, pp);
1880         if (result != PROMISE_RESULT_SKIPPED)
1881         {
1882             EndMeasurePromise(start, pp);
1883         }
1884     }
1885     else if (strcmp("commands", PromiseGetPromiseType(pp)) == 0)
1886     {
1887         result = VerifyExecPromise(ctx, pp);
1888         if (result != PROMISE_RESULT_SKIPPED)
1889         {
1890             EndMeasurePromise(start, pp);
1891         }
1892     }
1893     else if (strcmp("databases", PromiseGetPromiseType(pp)) == 0)
1894     {
1895         result = VerifyDatabasePromises(ctx, pp);
1896         if (result != PROMISE_RESULT_SKIPPED)
1897         {
1898             EndMeasurePromise(start, pp);
1899         }
1900     }
1901     else if (strcmp("methods", PromiseGetPromiseType(pp)) == 0)
1902     {
1903         result = VerifyMethodsPromise(ctx, pp);
1904         if (result != PROMISE_RESULT_SKIPPED)
1905         {
1906             EndMeasurePromise(start, pp);
1907         }
1908     }
1909     else if (strcmp("services", PromiseGetPromiseType(pp)) == 0)
1910     {
1911         result = VerifyServicesPromise(ctx, pp);
1912         if (result != PROMISE_RESULT_SKIPPED)
1913         {
1914             EndMeasurePromise(start, pp);
1915         }
1916     }
1917     else if (strcmp("guest_environments", PromiseGetPromiseType(pp)) == 0)
1918     {
1919         result = VerifyEnvironmentsPromise(ctx, pp);
1920         if (result != PROMISE_RESULT_SKIPPED)
1921         {
1922             EndMeasurePromise(start, pp);
1923         }
1924     }
1925     else if (strcmp("reports", PromiseGetPromiseType(pp)) == 0)
1926     {
1927         result = VerifyReportPromise(ctx, pp);
1928     }
1929     else if (!IsBuiltInPromiseType(PromiseGetPromiseType(pp)))
1930     {
1931         result = EvaluateCustomPromise(ctx, pp);
1932     }
1933     else
1934     {
1935         result = PROMISE_RESULT_NOOP;
1936     }
1937 
1938     BannerStatus(result, PromiseGetPromiseType(pp), pp->promiser);
1939     EvalContextLogPromiseIterationOutcome(ctx, pp, result);
1940     return result;
1941 }
1942 
1943 
BannerStatus(PromiseResult status,const char * type,char * name)1944 static void BannerStatus(PromiseResult status, const char *type, char *name)
1945 {
1946     if ((strcmp(type, "vars") == 0) || (strcmp(type, "classes") == 0))
1947     {
1948         return;
1949     }
1950 
1951     switch (status)
1952     {
1953     case PROMISE_RESULT_CHANGE:
1954         Log(LOG_LEVEL_VERBOSE, "A: Promise REPAIRED");
1955         break;
1956 
1957     case PROMISE_RESULT_TIMEOUT:
1958         Log(LOG_LEVEL_VERBOSE, "A: Promise TIMED-OUT");
1959         break;
1960 
1961     case PROMISE_RESULT_WARN:
1962     case PROMISE_RESULT_FAIL:
1963     case PROMISE_RESULT_INTERRUPTED:
1964         Log(LOG_LEVEL_VERBOSE, "A: Promise NOT KEPT!");
1965         break;
1966 
1967     case PROMISE_RESULT_DENIED:
1968         Log(LOG_LEVEL_VERBOSE, "A: Promise NOT KEPT - denied");
1969         break;
1970 
1971     case PROMISE_RESULT_NOOP:
1972         Log(LOG_LEVEL_VERBOSE, "A: Promise was KEPT");
1973         break;
1974     default:
1975         return;
1976         break;
1977     }
1978 
1979     Log(LOG_LEVEL_VERBOSE, "P: END %s promise (%.30s%s)",
1980         type, name,
1981         (strlen(name) > 30) ? "..." : "");
1982 }
1983 
1984 /*********************************************************************/
1985 /* Type context                                                      */
1986 /*********************************************************************/
1987 
NewTypeContext(TypeSequence type)1988 static void NewTypeContext(TypeSequence type)
1989 {
1990 // get maxconnections
1991 
1992     switch (type)
1993     {
1994     case TYPE_SEQUENCE_ENVIRONMENTS:
1995         NewEnvironmentsContext();
1996         break;
1997 
1998     case TYPE_SEQUENCE_FILES:
1999         break;
2000 
2001     case TYPE_SEQUENCE_PROCESSES:
2002         break;
2003 
2004     case TYPE_SEQUENCE_STORAGE:
2005 #ifndef __MINGW32__                   // TODO: Run if implemented on Windows
2006         if (SeqLength(GetGlobalMountedFSList()))
2007         {
2008             DeleteMountInfo(GetGlobalMountedFSList());
2009             SeqClear(GetGlobalMountedFSList());
2010         }
2011 #endif /* !__MINGW32__ */
2012         break;
2013 
2014     default:
2015         break;
2016     }
2017 
2018     return;
2019 }
2020 
2021 /*********************************************************************/
2022 
DeleteTypeContext(EvalContext * ctx,TypeSequence type)2023 static void DeleteTypeContext(EvalContext *ctx, TypeSequence type)
2024 {
2025     switch (type)
2026     {
2027     case TYPE_SEQUENCE_ENVIRONMENTS:
2028         DeleteEnvironmentsContext();
2029         break;
2030 
2031     case TYPE_SEQUENCE_FILES:
2032         break;
2033 
2034     case TYPE_SEQUENCE_PROCESSES:
2035         break;
2036 
2037     case TYPE_SEQUENCE_STORAGE:
2038         DeleteStorageContext();
2039         break;
2040 
2041     case TYPE_SEQUENCE_PACKAGES:
2042         ExecuteScheduledPackages(ctx);
2043         CleanScheduledPackages();
2044         break;
2045 
2046     default:
2047         break;
2048     }
2049 }
2050 
2051 /**************************************************************/
2052 /* Thread context                                             */
2053 /**************************************************************/
2054 
2055 #ifdef __MINGW32__
2056 
ParallelFindAndVerifyFilesPromises(EvalContext * ctx,const Promise * pp)2057 static PromiseResult ParallelFindAndVerifyFilesPromises(EvalContext *ctx, const Promise *pp)
2058 {
2059     int background = PromiseGetConstraintAsBoolean(ctx, "background", pp);
2060 
2061     if (background)
2062     {
2063         Log(LOG_LEVEL_VERBOSE, "Background processing of files promises is not supported on Windows");
2064     }
2065 
2066     return FindAndVerifyFilesPromises(ctx, pp);
2067 }
2068 
2069 #else /* !__MINGW32__ */
2070 
ParallelFindAndVerifyFilesPromises(EvalContext * ctx,const Promise * pp)2071 static PromiseResult ParallelFindAndVerifyFilesPromises(EvalContext *ctx, const Promise *pp)
2072 {
2073     int background = PromiseGetConstraintAsBoolean(ctx, "background", pp);
2074     pid_t child = 1;
2075     PromiseResult result = PROMISE_RESULT_SKIPPED;
2076 
2077     if (background)
2078     {
2079         if (CFA_BACKGROUND < CFA_BACKGROUND_LIMIT)
2080         {
2081             CFA_BACKGROUND++;
2082             Log(LOG_LEVEL_VERBOSE, "Spawning new process...");
2083             child = fork();
2084 
2085             if (child == 0)
2086             {
2087                 ALARM_PID = -1;
2088 
2089                 result = PromiseResultUpdate(result, FindAndVerifyFilesPromises(ctx, pp));
2090 
2091                 Log(LOG_LEVEL_VERBOSE, "Exiting backgrounded promise");
2092                 PromiseRef(LOG_LEVEL_VERBOSE, pp);
2093                 _exit(EXIT_SUCCESS);
2094                 // TODO: need to solve this
2095             }
2096         }
2097         else
2098         {
2099             Log(LOG_LEVEL_VERBOSE, "Promised parallel execution promised but exceeded the max number of promised background tasks, so serializing");
2100             background = 0;
2101         }
2102     }
2103     else
2104     {
2105         result = PromiseResultUpdate(result, FindAndVerifyFilesPromises(ctx, pp));
2106     }
2107 
2108     return result;
2109 }
2110 
2111 #endif /* !__MINGW32__ */
2112 
2113 /**************************************************************/
2114 
VerifyBootstrap(void)2115 static bool VerifyBootstrap(void)
2116 {
2117     const char *policy_server = PolicyServerGet();
2118     if (NULL_OR_EMPTY(policy_server))
2119     {
2120         Log(LOG_LEVEL_ERR, "Bootstrapping failed, no policy server is specified");
2121         return false;
2122     }
2123 
2124     // we should at least have gotten promises.cf from the policy hub
2125     {
2126         char filename[CF_MAXVARSIZE];
2127         snprintf(filename, sizeof(filename), "%s/promises.cf", GetInputDir());
2128         MapName(filename);
2129 
2130         struct stat sb;
2131         if (stat(filename, &sb) == -1)
2132         {
2133             Log(LOG_LEVEL_ERR, "Bootstrapping failed, no input file at '%s' after bootstrap", filename);
2134             return false;
2135         }
2136     }
2137 
2138     // embedded failsafe.cf (bootstrap.c) contains a promise to start cf-execd (executed while running this cf-agent)
2139     ClearProcessTable();
2140     LoadProcessTable();
2141 
2142     if (!IsProcessNameRunning(".*cf-execd.*"))
2143     {
2144         Log(LOG_LEVEL_ERR, "Bootstrapping failed, cf-execd is not running");
2145         return false;
2146     }
2147 
2148 
2149     Log(LOG_LEVEL_NOTICE, "Bootstrap to '%s' completed successfully!", policy_server);
2150     return true;
2151 }
2152 
2153 /**************************************************************/
2154 /* Compliance comp                                            */
2155 /**************************************************************/
2156 
NoteBundleCompliance(const Bundle * bundle,int save_pr_kept,int save_pr_repaired,int save_pr_notkept,struct timespec start)2157 static int NoteBundleCompliance(const Bundle *bundle, int save_pr_kept, int save_pr_repaired, int save_pr_notkept, struct timespec start)
2158 {
2159     double delta_pr_kept, delta_pr_repaired, delta_pr_notkept;
2160     double bundle_compliance = 0.0;
2161 
2162     delta_pr_kept = (double) (PR_KEPT - save_pr_kept);
2163     delta_pr_notkept = (double) (PR_NOTKEPT - save_pr_notkept);
2164     delta_pr_repaired = (double) (PR_REPAIRED - save_pr_repaired);
2165 
2166     Log(LOG_LEVEL_VERBOSE, "A: ...................................................");
2167     Log(LOG_LEVEL_VERBOSE, "A: Bundle Accounting Summary for '%s' in namespace %s", bundle->name, bundle->ns);
2168 
2169     if (delta_pr_kept + delta_pr_notkept + delta_pr_repaired <= 0)
2170     {
2171         Log(LOG_LEVEL_VERBOSE, "A: Zero promises executed for bundle '%s'", bundle->name);
2172         Log(LOG_LEVEL_VERBOSE, "A: ...................................................");
2173         return PROMISE_RESULT_NOOP;
2174     }
2175     else
2176     {
2177         Log(LOG_LEVEL_VERBOSE, "A: Promises kept in '%s' = %.0lf", bundle->name, delta_pr_kept);
2178         Log(LOG_LEVEL_VERBOSE, "A: Promises not kept in '%s' = %.0lf", bundle->name, delta_pr_notkept);
2179         Log(LOG_LEVEL_VERBOSE, "A: Promises repaired in '%s' = %.0lf", bundle->name, delta_pr_repaired);
2180 
2181         bundle_compliance = (delta_pr_kept + delta_pr_repaired) / (delta_pr_kept + delta_pr_notkept + delta_pr_repaired);
2182 
2183         Log(LOG_LEVEL_VERBOSE, "A: Aggregate compliance (promises kept/repaired) for bundle '%s' = %.1lf%%",
2184           bundle->name, bundle_compliance * 100.0);
2185 
2186         if (LogGetGlobalLevel() >= LOG_LEVEL_INFO)
2187         {
2188             char name[CF_MAXVARSIZE];
2189             snprintf(name, CF_MAXVARSIZE, "%s:%s", bundle->ns, bundle->name);
2190             EndMeasure(name, start);
2191         }
2192         else
2193         {
2194             EndMeasure(NULL, start);
2195         }
2196         Log(LOG_LEVEL_VERBOSE, "A: ...................................................");
2197     }
2198 
2199     // return the worst case for the bundle status
2200 
2201     if (delta_pr_notkept > 0)
2202     {
2203         return PROMISE_RESULT_FAIL;
2204     }
2205 
2206     if (delta_pr_repaired > 0)
2207     {
2208         return PROMISE_RESULT_CHANGE;
2209     }
2210 
2211     return PROMISE_RESULT_NOOP;
2212 }
2213 
2214 #if defined(HAVE_AVAHI_CLIENT_CLIENT_H) && defined(HAVE_AVAHI_COMMON_ADDRESS_H)
2215 
HasAvahiSupport(void)2216 static bool HasAvahiSupport(void)
2217 {
2218     return true;
2219 }
2220 
2221 
AutomaticBootstrap(GenericAgentConfig * config)2222 static int AutomaticBootstrap(GenericAgentConfig *config)
2223 {
2224     List *foundhubs = NULL;
2225     int hubcount = ListHubs(&foundhubs);
2226     int ret;
2227 
2228     switch(hubcount)
2229     {
2230     case -1:
2231         Log(LOG_LEVEL_ERR, "Error while trying to find a Policy Server");
2232         ret = -1;
2233         break;
2234     case 0:
2235         Log(LOG_LEVEL_ERR, "No hubs were found. Exiting.");
2236         ret = -1;
2237         break;
2238     case 1:
2239     {
2240         char *hostname = ((HostProperties*)foundhubs)->Hostname;
2241         char *ipaddr = ((HostProperties*)foundhubs)->IPAddress;
2242         Log(LOG_LEVEL_NOTICE, "Autodiscovered hub installed on hostname '%s', IP address '%s'",
2243             hostname, ipaddr);
2244 
2245         // TODO: This is a very bad way to check for valid IP(?)
2246         if (strlen(ipaddr) < CF_MAX_IP_LEN)
2247         {
2248             config->agent_specific.agent.bootstrap_argument = xstrdup(ipaddr);
2249             config->agent_specific.agent.bootstrap_ip       = xstrdup(ipaddr);
2250             config->agent_specific.agent.bootstrap_host     = xstrdup(ipaddr);
2251             ret = 0;
2252         }
2253         else
2254         {
2255             Log(LOG_LEVEL_ERR,  "Invalid autodiscovered hub IP address '%s'", ipaddr);
2256             ret = -1;
2257         }
2258         break;
2259     }
2260     default:
2261         Log(LOG_LEVEL_ERR, "Found more than one hub registered in the network. Please bootstrap manually using IP from the list below.");
2262         PrintList(foundhubs);
2263         ret = -1;
2264     };
2265 
2266     if (avahi_handle)
2267     {
2268         /*
2269          * This case happens when dlopen does not manage to open the library.
2270          */
2271         dlclose(avahi_handle);
2272     }
2273     ListDestroy(&foundhubs);
2274 
2275     return ret;
2276 }
2277 #else
2278 
HasAvahiSupport(void)2279 static bool HasAvahiSupport(void)
2280 {
2281     return false;
2282 }
2283 
AutomaticBootstrap(ARG_UNUSED GenericAgentConfig * config)2284 static int AutomaticBootstrap(ARG_UNUSED GenericAgentConfig *config)
2285 {
2286     ProgrammingError("Attempted automated bootstrap on a non-avahi build of CFEngine");
2287 }
2288 
2289 #endif // Avahi
2290 
WaitForBackgroundProcesses()2291 static void WaitForBackgroundProcesses()
2292 {
2293 #ifdef __MINGW32__
2294     /* no fork() on Windows */
2295     return;
2296 #else
2297     Log(LOG_LEVEL_VERBOSE, "Waiting for background processes");
2298     bool have_children = true;
2299     while (have_children)
2300     {
2301         pid_t child = wait(NULL);
2302         if (child >= 0)
2303         {
2304             Log(LOG_LEVEL_VERBOSE, "Background process %ju terminated", (uintmax_t) child);
2305         }
2306         have_children = !((child == -1) && (errno == ECHILD));
2307     }
2308     Log(LOG_LEVEL_VERBOSE, "No more background processes to wait for");
2309     return;
2310 #endif  /* __MINGW32__ */
2311 }
2312