static Policy *LoadPolicyFile(EvalContext *ctx, GenericAgentConfig *config, const char *policy_file, StringSet *parsed_files, StringSet *failed_files) { Policy *policy = Cf3ParseFile(config, policy_file); StringSetAdd(parsed_files, xstrdup(policy_file)); if (!policy) { StringSetAdd(failed_files, xstrdup(policy_file)); return NULL; } PolicyResolve(ctx, policy, config); Body *body_common_control = PolicyGetBody(policy, NULL, "common", "control"); Body *body_file_control = PolicyGetBody(policy, NULL, "file", "control"); if (body_common_control) { Seq *potential_inputs = BodyGetConstraint(body_common_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } PolicyResolve(ctx, policy, config); if (body_file_control) { Seq *potential_inputs = BodyGetConstraint(body_file_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } return policy; }
static Policy *LoadPolicyInputFiles(EvalContext *ctx, GenericAgentConfig *config, const Rlist *inputs, StringSet *parsed_files_and_checksums, StringSet *failed_files) { Policy *policy = PolicyNew(); for (const Rlist *rp = inputs; rp; rp = rp->next) { if (rp->val.type != RVAL_TYPE_SCALAR) { Log(LOG_LEVEL_ERR, "Non-file object in inputs list"); continue; } const char *unresolved_input = RlistScalarValue(rp); if (strcmp(CF_NULL_VALUE, unresolved_input) == 0) { continue; } if (IsExpandable(unresolved_input)) { PolicyResolve(ctx, policy, config); } Rval resolved_input = EvaluateFinalRval(ctx, policy, NULL, "sys", rp->val, true, NULL); Policy *aux_policy = NULL; switch (resolved_input.type) { case RVAL_TYPE_SCALAR: if (IsCf3VarString(RvalScalarValue(resolved_input))) { Log(LOG_LEVEL_ERR, "Unresolved variable '%s' in input list, cannot parse", RvalScalarValue(resolved_input)); break; } aux_policy = LoadPolicyFile(ctx, config, GenericAgentResolveInputPath(config, RvalScalarValue(resolved_input)), parsed_files_and_checksums, failed_files); break; case RVAL_TYPE_LIST: aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(resolved_input), parsed_files_and_checksums, failed_files); break; default: ProgrammingError("Unknown type in input list for parsing: %d", resolved_input.type); break; } if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } RvalDestroy(resolved_input); } return policy; }
static void run_test_in_policy(const char *policy_filename, TestFn fn) { GenericAgentConfig *agent_config = GenericAgentConfigNewDefault( AGENT_TYPE_EXECUTOR); EvalContext *ctx = EvalContextNew(); Policy *policy = TestParsePolicy(policy_filename); PolicyResolve(ctx, policy, agent_config); /* Setup global environment */ strcpy(VFQNAME, "localhost.localdomain"); strcpy(VIPADDRESS, "127.0.0.100"); EvalContextAddIpAddress(ctx, "127.0.0.100"); EvalContextAddIpAddress(ctx, "127.0.0.101"); fn(ctx, policy); PolicyDestroy(policy); GenericAgentFinalize(ctx, agent_config); }
static void VerifyPromises(EvalContext *ctx, Policy *policy, GenericAgentConfig *config) { /* Now look once through ALL the bundles themselves */ for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bp = SeqAt(policy->bundles, i); EvalContextStackPushBundleFrame(ctx, bp, NULL, false); for (size_t j = 0; j < SeqLength(bp->promise_types); j++) { PromiseType *sp = SeqAt(bp->promise_types, j); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, CommonEvalPromise, NULL); } } EvalContextStackPopFrame(ctx); } PolicyResolve(ctx, policy, config); // TODO: need to move this inside PolicyCheckRunnable eventually. if (!config->bundlesequence && config->check_runnable) { // only verify policy-defined bundlesequence for cf-agent, cf-promises if ((config->agent_type == AGENT_TYPE_AGENT) || (config->agent_type == AGENT_TYPE_COMMON)) { if (!VerifyBundleSequence(ctx, policy, config)) { FatalError(ctx, "Errors in promise bundles"); } } } }
static void KeepControlPromises(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config) { CFD_MAXPROCESSES = 30; MAXTRIES = 5; DENYBADCLOCKS = true; CFRUNCOMMAND[0] = '\0'; SetChecksumUpdatesDefault(ctx, true); /* Keep promised agent behaviour - control bodies */ Banner("Server control promises.."); PolicyResolve(ctx, policy, config); /* Now expand */ Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_SERVER); if (constraints) { for (size_t i = 0; i < SeqLength(constraints); i++) { Constraint *cp = SeqAt(constraints, i); #define IsControlBody(e) (strcmp(cp->lval, CFS_CONTROLBODY[e].lval) == 0) if (!IsDefinedClass(ctx, cp->classes)) { continue; } VarRef *ref = VarRefParseFromScope(cp->lval, "control_server"); const void *value = EvalContextVariableGet(ctx, ref, NULL); VarRefDestroy(ref); if (!value) { Log(LOG_LEVEL_ERR, "Unknown lval '%s' in server control body", cp->lval); } else if (IsControlBody(SERVER_CONTROL_SERVER_FACILITY)) { SetFacility(value); } else if (IsControlBody(SERVER_CONTROL_DENY_BAD_CLOCKS)) { DENYBADCLOCKS = BooleanFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting denybadclocks to '%s'", DENYBADCLOCKS ? "true" : "false"); } else if (IsControlBody(SERVER_CONTROL_LOG_ENCRYPTED_TRANSFERS)) { LOGENCRYPT = BooleanFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting logencrypt to '%s'", LOGENCRYPT ? "true" : "false"); } else if (IsControlBody(SERVER_CONTROL_LOG_ALL_CONNECTIONS)) { SV.logconns = BooleanFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting logconns to %d", SV.logconns); } else if (IsControlBody(SERVER_CONTROL_MAX_CONNECTIONS)) { CFD_MAXPROCESSES = (int) IntFromString(value); MAXTRIES = CFD_MAXPROCESSES / 3; Log(LOG_LEVEL_VERBOSE, "Setting maxconnections to %d", CFD_MAXPROCESSES); /* The handling of max_readers in LMDB is not ideal, but * here is how it is right now: We know that both cf-serverd and * cf-hub will access the lastseen database. Worst case every * single thread and process will do it at the same time, and * this has in fact been observed. So we add the maximum of * those two values together to provide a safe ceiling. In * addition, cf-agent can access the database occasionally as * well, so add a few extra for that too. */ DBSetMaximumConcurrentTransactions(CFD_MAXPROCESSES + EnterpriseGetMaxCfHubProcesses() + 10); continue; } else if (IsControlBody(SERVER_CONTROL_CALL_COLLECT_INTERVAL)) { COLLECT_INTERVAL = (int) 60 * IntFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting call_collect_interval to %d (seconds)", COLLECT_INTERVAL); } else if (IsControlBody(SERVER_CONTROL_LISTEN)) { SERVER_LISTEN = BooleanFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting server listen to '%s' ", SERVER_LISTEN ? "true" : "false"); } else if (IsControlBody(SERVER_CONTROL_CALL_COLLECT_WINDOW)) { COLLECT_WINDOW = (int) IntFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting collect_window to %d (seconds)", COLLECT_INTERVAL); } else if (IsControlBody(SERVER_CONTROL_CF_RUN_COMMAND)) { strlcpy(CFRUNCOMMAND, value, sizeof(CFRUNCOMMAND)); Log(LOG_LEVEL_VERBOSE, "Setting cfruncommand to '%s'", CFRUNCOMMAND); } else if (IsControlBody(SERVER_CONTROL_ALLOW_CONNECTS)) { Log(LOG_LEVEL_VERBOSE, "Setting allowing connections from ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.nonattackerlist, RlistScalarValue(rp))) { PrependItem(&SV.nonattackerlist, RlistScalarValue(rp), cp->classes); } } } else if (IsControlBody(SERVER_CONTROL_DENY_CONNECTS)) { Log(LOG_LEVEL_VERBOSE, "Setting denying connections from ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.attackerlist, RlistScalarValue(rp))) { PrependItem(&SV.attackerlist, RlistScalarValue(rp), cp->classes); } } } else if (IsControlBody(SERVER_CONTROL_SKIP_VERIFY)) { /* Skip. */ } else if (IsControlBody(SERVER_CONTROL_ALLOW_ALL_CONNECTS)) { Log(LOG_LEVEL_VERBOSE, "Setting allowing multiple connections from ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.multiconnlist, RlistScalarValue(rp))) { PrependItem(&SV.multiconnlist, RlistScalarValue(rp), cp->classes); } } } else if (IsControlBody(SERVER_CONTROL_ALLOW_USERS)) { Log(LOG_LEVEL_VERBOSE, "SET Allowing users ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.allowuserlist, RlistScalarValue(rp))) { PrependItem(&SV.allowuserlist, RlistScalarValue(rp), cp->classes); } } } else if (IsControlBody(SERVER_CONTROL_TRUST_KEYS_FROM)) { Log(LOG_LEVEL_VERBOSE, "Setting trust keys from ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.trustkeylist, RlistScalarValue(rp))) { PrependItem(&SV.trustkeylist, RlistScalarValue(rp), cp->classes); } } } else if (IsControlBody(SERVER_CONTROL_ALLOWLEGACYCONNECTS)) { Log(LOG_LEVEL_VERBOSE, "Setting allowing legacy connections from ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.allowlegacyconnects, RlistScalarValue(rp))) { PrependItem(&SV.allowlegacyconnects, RlistScalarValue(rp), cp->classes); } } } else if (IsControlBody(SERVER_CONTROL_PORT_NUMBER)) { CFENGINE_PORT = IntFromString(value); strlcpy(CFENGINE_PORT_STR, value, sizeof(CFENGINE_PORT_STR)); Log(LOG_LEVEL_VERBOSE, "Setting default port number to %d", CFENGINE_PORT); } else if (IsControlBody(SERVER_CONTROL_BIND_TO_INTERFACE)) { strlcpy(BINDINTERFACE, value, sizeof(BINDINTERFACE)); Log(LOG_LEVEL_VERBOSE, "Setting bindtointerface to '%s'", BINDINTERFACE); } else if (IsControlBody(SERVER_CONTROL_ALLOWCIPHERS)) { SV.allowciphers = xstrdup(value); Log(LOG_LEVEL_VERBOSE, "Setting allowciphers to '%s'", SV.allowciphers); } #undef IsControlBody } } const void *value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_SYSLOG_HOST); if (value) { /* Don't resolve syslog_host now, better do it per log request. */ if (!SetSyslogHost(value)) { Log(LOG_LEVEL_ERR, "Failed to set syslog_host, '%s' too long", (const char *)value); } else { Log(LOG_LEVEL_VERBOSE, "Setting syslog_host to '%s'", (const char *)value); } } value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_SYSLOG_PORT); if (value) { SetSyslogPort(IntFromString(value)); } value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_FIPS_MODE); if (value) { FIPS_MODE = BooleanFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting FIPS mode to to '%s'", FIPS_MODE ? "true" : "false"); } value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_LASTSEEN_EXPIRE_AFTER); if (value) { LASTSEENEXPIREAFTER = IntFromString(value) * 60; } value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_BWLIMIT); if (value) { double bval; if (DoubleFromString(value, &bval)) { bwlimit_kbytes = (uint32_t) ( bval / 1000.0); Log(LOG_LEVEL_VERBOSE, "Setting rate limit to %d kBytes/sec", bwlimit_kbytes); } } }
static void KeepControlPromises(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config) { CFD_MAXPROCESSES = 30; MAXTRIES = 5; DENYBADCLOCKS = true; CFRUNCOMMAND[0] = '\0'; SetChecksumUpdatesDefault(ctx, true); /* Keep promised agent behaviour - control bodies */ Banner("Server control promises.."); PolicyResolve(ctx, policy, config); /* Now expand */ Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_SERVER); if (constraints) { for (size_t i = 0; i < SeqLength(constraints); i++) { Constraint *cp = SeqAt(constraints, i); if (!IsDefinedClass(ctx, cp->classes)) { continue; } VarRef *ref = VarRefParseFromScope(cp->lval, "control_server"); const void *value = EvalContextVariableGet(ctx, ref, NULL); VarRefDestroy(ref); if (!value) { Log(LOG_LEVEL_ERR, "Unknown lval '%s' in server control body", cp->lval); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_SERVER_FACILITY].lval) == 0) { SetFacility(value); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_DENY_BAD_CLOCKS].lval) == 0) { DENYBADCLOCKS = BooleanFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting denybadclocks to '%s'", DENYBADCLOCKS ? "true" : "false"); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_LOG_ENCRYPTED_TRANSFERS].lval) == 0) { LOGENCRYPT = BooleanFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting logencrypt to '%s'", LOGENCRYPT ? "true" : "false"); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_LOG_ALL_CONNECTIONS].lval) == 0) { SV.logconns = BooleanFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting logconns to %d", SV.logconns); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_MAX_CONNECTIONS].lval) == 0) { CFD_MAXPROCESSES = (int) IntFromString(value); MAXTRIES = CFD_MAXPROCESSES / 3; Log(LOG_LEVEL_VERBOSE, "Setting maxconnections to %d", CFD_MAXPROCESSES); #ifdef LMDB static int LSD_MAXREADERS = 0; if (LSD_MAXREADERS < CFD_MAXPROCESSES) { int rc = UpdateLastSeenMaxReaders(CFD_MAXPROCESSES); if (rc == 0) { LSD_MAXREADERS = CFD_MAXPROCESSES; } } #endif continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_CALL_COLLECT_INTERVAL].lval) == 0) { COLLECT_INTERVAL = (int) 60 * IntFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting call_collect_interval to %d (seconds)", COLLECT_INTERVAL); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_LISTEN].lval) == 0) { SERVER_LISTEN = BooleanFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting server listen to '%s' ", (SERVER_LISTEN)? "true":"false"); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_CALL_COLLECT_WINDOW].lval) == 0) { COLLECT_WINDOW = (int) IntFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting collect_window to %d (seconds)", COLLECT_INTERVAL); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_CF_RUN_COMMAND].lval) == 0) { strlcpy(CFRUNCOMMAND, value, sizeof(CFRUNCOMMAND)); Log(LOG_LEVEL_VERBOSE, "Setting cfruncommand to '%s'", CFRUNCOMMAND); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_ALLOW_CONNECTS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "Setting allowing connections from ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.nonattackerlist, RlistScalarValue(rp))) { AppendItem(&SV.nonattackerlist, RlistScalarValue(rp), cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_DENY_CONNECTS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "Setting denying connections from ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.attackerlist, RlistScalarValue(rp))) { AppendItem(&SV.attackerlist, RlistScalarValue(rp), cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_SKIP_VERIFY].lval) == 0) { continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_ALLOW_ALL_CONNECTS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "Setting allowing multiple connections from ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.multiconnlist, RlistScalarValue(rp))) { AppendItem(&SV.multiconnlist, RlistScalarValue(rp), cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_ALLOW_USERS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET Allowing users ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.allowuserlist, RlistScalarValue(rp))) { AppendItem(&SV.allowuserlist, RlistScalarValue(rp), cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_TRUST_KEYS_FROM].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "Setting trust keys from ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.trustkeylist, RlistScalarValue(rp))) { AppendItem(&SV.trustkeylist, RlistScalarValue(rp), cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_ALLOWLEGACYCONNECTS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "Setting allowing legacy connections from ..."); for (const Rlist *rp = value; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.allowlegacyconnects, RlistScalarValue(rp))) { AppendItem(&SV.allowlegacyconnects, RlistScalarValue(rp), cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_PORT_NUMBER].lval) == 0) { CFENGINE_PORT = IntFromString(value); strlcpy(CFENGINE_PORT_STR, value, sizeof(CFENGINE_PORT_STR)); Log(LOG_LEVEL_VERBOSE, "Setting default port number to %d", CFENGINE_PORT); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_BIND_TO_INTERFACE].lval) == 0) { strlcpy(BINDINTERFACE, value, sizeof(BINDINTERFACE)); Log(LOG_LEVEL_VERBOSE, "Setting bindtointerface to '%s'", BINDINTERFACE); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_ALLOWCIPHERS].lval) == 0) { SV.allowciphers = xstrdup(value); Log(LOG_LEVEL_VERBOSE, "Setting allowciphers to '%s'", SV.allowciphers); continue; } } } const void *value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_SYSLOG_HOST); if (value) { /* Don't resolve syslog_host now, better do it per log request. */ if (!SetSyslogHost(value)) { Log(LOG_LEVEL_ERR, "Failed to set syslog_host, '%s' too long", (const char *)value); } else { Log(LOG_LEVEL_VERBOSE, "Setting syslog_host to '%s'", (const char *)value); } } value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_SYSLOG_PORT); if (value) { SetSyslogPort(IntFromString(value)); } value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_FIPS_MODE); if (value) { FIPS_MODE = BooleanFromString(value); Log(LOG_LEVEL_VERBOSE, "Setting FIPS mode to to '%s'", FIPS_MODE ? "true" : "false"); } value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_LASTSEEN_EXPIRE_AFTER); if (value) { LASTSEENEXPIREAFTER = IntFromString(value) * 60; } }
Policy *LoadPolicy(EvalContext *ctx, GenericAgentConfig *config) { StringSet *parsed_files_and_checksums = StringSetNew(); StringSet *failed_files = StringSetNew(); Policy *policy = LoadPolicyFile(ctx, config, config->input_file, parsed_files_and_checksums, failed_files); if (StringSetSize(failed_files) > 0) { Log(LOG_LEVEL_ERR, "There are syntax errors in policy files"); exit(EXIT_FAILURE); } StringSetDestroy(parsed_files_and_checksums); StringSetDestroy(failed_files); { Seq *errors = SeqNew(100, PolicyErrorDestroy); if (PolicyCheckPartial(policy, errors)) { if (!config->bundlesequence && (PolicyIsRunnable(policy) || config->check_runnable)) { Log(LOG_LEVEL_VERBOSE, "Running full policy integrity checks"); PolicyCheckRunnable(ctx, policy, errors, config->ignore_missing_bundles); } } if (SeqLength(errors) > 0) { Writer *writer = FileWriter(stderr); for (size_t i = 0; i < errors->length; i++) { PolicyErrorWrite(writer, errors->data[i]); } WriterClose(writer); exit(EXIT_FAILURE); // TODO: do not exit } SeqDestroy(errors); } if (LogGetGlobalLevel() >= LOG_LEVEL_VERBOSE) { ShowContext(ctx); } if (policy) { for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bp = SeqAt(policy->bundles, i); EvalContextStackPushBundleFrame(ctx, bp, NULL, false); for (size_t j = 0; j < SeqLength(bp->promise_types); j++) { PromiseType *sp = SeqAt(bp->promise_types, j); EvalContextStackPushPromiseTypeFrame(ctx, sp); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, CommonEvalPromise, NULL); } EvalContextStackPopFrame(ctx); } EvalContextStackPopFrame(ctx); } PolicyResolve(ctx, policy, config); // TODO: need to move this inside PolicyCheckRunnable eventually. if (!config->bundlesequence && config->check_runnable) { // only verify policy-defined bundlesequence for cf-agent, cf-promises if ((config->agent_type == AGENT_TYPE_AGENT) || (config->agent_type == AGENT_TYPE_COMMON)) { if (!VerifyBundleSequence(ctx, policy, config)) { FatalError(ctx, "Errors in promise bundles: could not verify bundlesequence"); } } } } JsonElement *validated_doc = ReadReleaseIdFileFromInputs(); if (validated_doc) { const char *release_id = JsonObjectGetAsString(validated_doc, "releaseId"); if (release_id) { policy->release_id = xstrdup(release_id); } JsonDestroy(validated_doc); } return policy; }
static Policy *LoadPolicyFile(EvalContext *ctx, GenericAgentConfig *config, const char *policy_file, StringSet *parsed_files_and_checksums, StringSet *failed_files) { Policy *policy = NULL; unsigned char digest[EVP_MAX_MD_SIZE + 1] = { 0 }; char hashbuffer[EVP_MAX_MD_SIZE * 4] = { 0 }; char hashprintbuffer[CF_BUFSIZE] = { 0 }; HashFile(policy_file, digest, CF_DEFAULT_DIGEST); snprintf(hashprintbuffer, CF_BUFSIZE - 1, "{checksum}%s", HashPrintSafe(CF_DEFAULT_DIGEST, true, digest, hashbuffer)); Log(LOG_LEVEL_DEBUG, "Hashed policy file %s to %s", policy_file, hashprintbuffer); if (StringSetContains(parsed_files_and_checksums, policy_file)) { Log(LOG_LEVEL_VERBOSE, "Skipping loading of duplicate policy file %s", policy_file); return NULL; } else if (StringSetContains(parsed_files_and_checksums, hashprintbuffer)) { Log(LOG_LEVEL_VERBOSE, "Skipping loading of duplicate (detected by hash) policy file %s", policy_file); return NULL; } else { Log(LOG_LEVEL_DEBUG, "Loading policy file %s", policy_file); } policy = Cf3ParseFile(config, policy_file); // we keep the checksum and the policy file name to help debugging StringSetAdd(parsed_files_and_checksums, xstrdup(policy_file)); StringSetAdd(parsed_files_and_checksums, xstrdup(hashprintbuffer)); if (policy) { Seq *errors = SeqNew(10, free); if (!PolicyCheckPartial(policy, errors)) { Writer *writer = FileWriter(stderr); for (size_t i = 0; i < errors->length; i++) { PolicyErrorWrite(writer, errors->data[i]); } WriterClose(writer); SeqDestroy(errors); StringSetAdd(failed_files, xstrdup(policy_file)); return NULL; } SeqDestroy(errors); } else { StringSetAdd(failed_files, xstrdup(policy_file)); return NULL; } PolicyResolve(ctx, policy, config); Body *body_common_control = PolicyGetBody(policy, NULL, "common", "control"); Body *body_file_control = PolicyGetBody(policy, NULL, "file", "control"); if (body_common_control) { Seq *potential_inputs = BodyGetConstraint(body_common_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files_and_checksums, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } if (body_file_control) { Seq *potential_inputs = BodyGetConstraint(body_file_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files_and_checksums, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } return policy; }
static void KeepControlPromises(EvalContext *ctx, Policy *policy, GenericAgentConfig *config) { Rval retval; CFD_MAXPROCESSES = 30; MAXTRIES = 5; DENYBADCLOCKS = true; CFRUNCOMMAND[0] = '\0'; SetChecksumUpdates(true); /* Keep promised agent behaviour - control bodies */ Banner("Server control promises.."); PolicyResolve(ctx, policy, config); /* Now expand */ Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_SERVER); if (constraints) { for (size_t i = 0; i < SeqLength(constraints); i++) { Constraint *cp = SeqAt(constraints, i); if (!IsDefinedClass(ctx, cp->classes, NULL)) { continue; } VarRef *ref = VarRefParseFromScope(cp->lval, "control_server"); if (!EvalContextVariableGet(ctx, ref, &retval, NULL)) { Log(LOG_LEVEL_ERR, "Unknown lval '%s' in server control body", cp->lval); VarRefDestroy(ref); continue; } VarRefDestroy(ref); if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_SERVER_FACILITY].lval) == 0) { SetFacility(retval.item); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_DENY_BAD_CLOCKS].lval) == 0) { DENYBADCLOCKS = BooleanFromString(retval.item); Log(LOG_LEVEL_VERBOSE, "Setting denybadclocks to '%s'", DENYBADCLOCKS ? "true" : "false"); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_LOG_ENCRYPTED_TRANSFERS].lval) == 0) { LOGENCRYPT = BooleanFromString(retval.item); Log(LOG_LEVEL_VERBOSE, "Setting logencrypt to '%s'", LOGENCRYPT ? "true" : "false"); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_LOG_ALL_CONNECTIONS].lval) == 0) { SV.logconns = BooleanFromString(retval.item); Log(LOG_LEVEL_VERBOSE, "Setting logconns to %d", SV.logconns); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_MAX_CONNECTIONS].lval) == 0) { CFD_MAXPROCESSES = (int) IntFromString(retval.item); MAXTRIES = CFD_MAXPROCESSES / 3; Log(LOG_LEVEL_VERBOSE, "Setting maxconnections to %d", CFD_MAXPROCESSES); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_CALL_COLLECT_INTERVAL].lval) == 0) { COLLECT_INTERVAL = (int) 60 * IntFromString(retval.item); Log(LOG_LEVEL_VERBOSE, "Setting call_collect_interval to %d (seconds)", COLLECT_INTERVAL); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_LISTEN].lval) == 0) { SERVER_LISTEN = BooleanFromString(retval.item); Log(LOG_LEVEL_VERBOSE, "Setting server listen to '%s' ", (SERVER_LISTEN)? "true":"false"); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_CALL_COLLECT_WINDOW].lval) == 0) { COLLECT_WINDOW = (int) IntFromString(retval.item); Log(LOG_LEVEL_VERBOSE, "Setting collect_window to %d (seconds)", COLLECT_INTERVAL); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_CF_RUN_COMMAND].lval) == 0) { strncpy(CFRUNCOMMAND, retval.item, CF_BUFSIZE - 1); Log(LOG_LEVEL_VERBOSE, "Setting cfruncommand to '%s'", CFRUNCOMMAND); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_ALLOW_CONNECTS].lval) == 0) { Rlist *rp; Log(LOG_LEVEL_VERBOSE, "Setting allowing connections from ..."); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.nonattackerlist, rp->item)) { AppendItem(&SV.nonattackerlist, rp->item, cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_DENY_CONNECTS].lval) == 0) { Rlist *rp; Log(LOG_LEVEL_VERBOSE, "Setting denying connections from ..."); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.attackerlist, rp->item)) { AppendItem(&SV.attackerlist, rp->item, cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_SKIP_VERIFY].lval) == 0) { Rlist *rp; Log(LOG_LEVEL_VERBOSE, "Setting skip verify connections from ..."); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.skipverify, rp->item)) { AppendItem(&SV.skipverify, rp->item, cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_ALLOW_ALL_CONNECTS].lval) == 0) { Rlist *rp; Log(LOG_LEVEL_VERBOSE, "Setting allowing multiple connections from ..."); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.multiconnlist, rp->item)) { AppendItem(&SV.multiconnlist, rp->item, cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_ALLOW_USERS].lval) == 0) { Rlist *rp; Log(LOG_LEVEL_VERBOSE, "SET Allowing users ..."); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.allowuserlist, rp->item)) { AppendItem(&SV.allowuserlist, rp->item, cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_TRUST_KEYS_FROM].lval) == 0) { Rlist *rp; Log(LOG_LEVEL_VERBOSE, "Setting trust keys from ..."); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { if (!IsItemIn(SV.trustkeylist, rp->item)) { AppendItem(&SV.trustkeylist, rp->item, cp->classes); } } continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_PORT_NUMBER].lval) == 0) { SHORT_CFENGINEPORT = (short) IntFromString(retval.item); strncpy(STR_CFENGINEPORT, retval.item, 15); Log(LOG_LEVEL_VERBOSE, "Setting default portnumber to %u = %s = %s", (int) SHORT_CFENGINEPORT, STR_CFENGINEPORT, RvalScalarValue(retval)); SHORT_CFENGINEPORT = htons((short) IntFromString(retval.item)); continue; } if (strcmp(cp->lval, CFS_CONTROLBODY[SERVER_CONTROL_BIND_TO_INTERFACE].lval) == 0) { strncpy(BINDINTERFACE, retval.item, CF_BUFSIZE - 1); Log(LOG_LEVEL_VERBOSE, "Setting bindtointerface to '%s'", BINDINTERFACE); continue; } } } if (EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_SYSLOG_HOST, &retval)) { /* Don't resolve syslog_host now, better do it per log request. */ if (!SetSyslogHost(retval.item)) { Log(LOG_LEVEL_ERR, "Failed to set syslog_host, '%s' too long", (char *) retval.item); } else { Log(LOG_LEVEL_VERBOSE, "Setting syslog_host to '%s'", (char *) retval.item); } } if (EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_SYSLOG_PORT, &retval)) { SetSyslogPort(IntFromString(retval.item)); } if (EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_FIPS_MODE, &retval)) { FIPS_MODE = BooleanFromString(retval.item); Log(LOG_LEVEL_VERBOSE, "Setting FIPS mode to to '%s'", FIPS_MODE ? "true" : "false"); } if (EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_LASTSEEN_EXPIRE_AFTER, &retval)) { LASTSEENEXPIREAFTER = IntFromString(retval.item) * 60; } }
static Policy *LoadPolicyFile(EvalContext *ctx, GenericAgentConfig *config, const char *policy_file, StringSet *parsed_files_and_checksums, StringSet *failed_files) { unsigned char digest[EVP_MAX_MD_SIZE + 1] = { 0 }; char hashbuffer[CF_HOSTKEY_STRING_SIZE] = { 0 }; char hashprintbuffer[CF_BUFSIZE] = { 0 }; HashFile(policy_file, digest, CF_DEFAULT_DIGEST); snprintf(hashprintbuffer, CF_BUFSIZE - 1, "{checksum}%s", HashPrintSafe(hashbuffer, sizeof(hashbuffer), digest, CF_DEFAULT_DIGEST, true)); Log(LOG_LEVEL_DEBUG, "Hashed policy file %s to %s", policy_file, hashprintbuffer); if (StringSetContains(parsed_files_and_checksums, policy_file)) { Log(LOG_LEVEL_VERBOSE, "Skipping loading of duplicate policy file %s", policy_file); return NULL; } else if (StringSetContains(parsed_files_and_checksums, hashprintbuffer)) { Log(LOG_LEVEL_VERBOSE, "Skipping loading of duplicate (detected by hash) policy file %s", policy_file); return NULL; } else { Log(LOG_LEVEL_DEBUG, "Loading policy file %s", policy_file); } Policy *policy = Cf3ParseFile(config, policy_file); // we keep the checksum and the policy file name to help debugging StringSetAdd(parsed_files_and_checksums, xstrdup(policy_file)); StringSetAdd(parsed_files_and_checksums, xstrdup(hashprintbuffer)); if (policy) { Seq *errors = SeqNew(10, free); if (!PolicyCheckPartial(policy, errors)) { Writer *writer = FileWriter(stderr); for (size_t i = 0; i < errors->length; i++) { PolicyErrorWrite(writer, errors->data[i]); } WriterClose(writer); SeqDestroy(errors); StringSetAdd(failed_files, xstrdup(policy_file)); PolicyDestroy(policy); return NULL; } SeqDestroy(errors); } else { StringSetAdd(failed_files, xstrdup(policy_file)); return NULL; } PolicyResolve(ctx, policy, config); DataType def_inputs_type = CF_DATA_TYPE_NONE; VarRef *inputs_ref = VarRefParse("def.augment_inputs"); const void *def_inputs = EvalContextVariableGet(ctx, inputs_ref, &def_inputs_type); VarRefDestroy(inputs_ref); if (RVAL_TYPE_CONTAINER == DataTypeToRvalType(def_inputs_type) && NULL != def_inputs) { const JsonElement *el; JsonIterator iter = JsonIteratorInit((JsonElement*) def_inputs); while ((el = JsonIteratorNextValueByType(&iter, JSON_ELEMENT_TYPE_PRIMITIVE, true))) { char *input = JsonPrimitiveToString(el); Log(LOG_LEVEL_VERBOSE, "Loading augments from def.augment_inputs: %s", input); Rlist* inputs_rlist = NULL; RlistAppendScalar(&inputs_rlist, input); Policy *aux_policy = LoadPolicyInputFiles(ctx, config, inputs_rlist, parsed_files_and_checksums, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } RlistDestroy(inputs_rlist); free(input); } } Body *body_common_control = PolicyGetBody(policy, NULL, "common", "control"); Body *body_file_control = PolicyGetBody(policy, NULL, "file", "control"); if (body_common_control) { Seq *potential_inputs = BodyGetConstraint(body_common_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files_and_checksums, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } if (body_file_control) { Seq *potential_inputs = BodyGetConstraint(body_file_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files_and_checksums, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } return policy; }
static void test_load(void) { GenericAgentConfig *agent_config = GenericAgentConfigNewDefault(AGENT_TYPE_EXECUTOR); ExecConfig *c = ExecConfigNewDefault(true, "host", "ip"); assert_true(c->scheduled_run); assert_string_equal("host", c->fq_name); assert_string_equal("ip", c->ip_address); TestCheckConfigIsDefault(c); EvalContext *ctx = EvalContextNew(); { VarRef *lval = VarRefParse("g.host"); EvalContextVariablePut(ctx, lval, "snookie", DATA_TYPE_STRING, NULL); VarRefDestroy(lval); } // provide a full body executor control and check that all options are collected { Policy *p = LoadPolicy("body_executor_control_full.cf"); PolicyResolve(ctx, p, agent_config); ExecConfigUpdate(ctx, p, c); assert_true(c->scheduled_run); assert_string_equal("host", c->fq_name); assert_string_equal("ip", c->ip_address); assert_int_equal(120, c->agent_expireafter); assert_string_equal("/bin/echo", c->exec_command); assert_string_equal("LOG_LOCAL6",c->log_facility); assert_string_equal("*****@*****.**",c->mail_from_address); assert_int_equal(50, c->mail_max_lines); assert_string_equal("localhost", c->mail_server); assert_string_equal("*****@*****.**",c->mail_to_address); assert_string_equal("Test [localhost/127.0.0.1]",c->mail_subject); // splay time hard to test (pseudo random) assert_int_equal(2, StringSetSize(c->schedule)); assert_true(StringSetContains(c->schedule, "Min00_05")); assert_true(StringSetContains(c->schedule, "Min05_10")); PolicyDestroy(p); } // provide a small policy and check that missing settings are being reverted to default { { Policy *p = LoadPolicy("body_executor_control_agent_expireafter_only.cf"); PolicyResolve(ctx, p, agent_config); ExecConfigUpdate(ctx, p, c); assert_true(c->scheduled_run); assert_string_equal("host", c->fq_name); assert_string_equal("ip", c->ip_address); assert_int_equal(121, c->agent_expireafter); // rest should be default assert_string_equal("", c->exec_command); assert_string_equal("LOG_USER",c->log_facility); assert_string_equal("",c->mail_from_address); assert_int_equal(30, c->mail_max_lines); assert_string_equal("", c->mail_server); assert_string_equal("",c->mail_to_address); assert_string_equal("",c->mail_subject); assert_int_equal(0, c->splay_time); assert_int_equal(12, StringSetSize(c->schedule)); PolicyDestroy(p); } } EvalContextDestroy(ctx); GenericAgentConfigDestroy(agent_config); }