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