static void execd_config_full_cb(const EvalContext *ctx, const Policy *policy) { ExecdConfig *config = ExecdConfigNew(ctx, policy); assert_int_equal(2, StringSetSize(config->schedule)); assert_int_equal(true, StringSetContains(config->schedule, "Min00_05")); assert_int_equal(true, StringSetContains(config->schedule, "Min05_10")); /* Splay calculation uses FQNAME and getuid(), so can't predict actual splay value */ assert_int_equal(true, config->splay_time>=0 && config->splay_time<60); assert_string_equal("LOG_LOCAL6", config->log_facility); ExecdConfigDestroy(config); }
int MissingDependencies(EvalContext *ctx, const Promise *pp) { if (pp == NULL) { return false; } char name[CF_BUFSIZE], *d; Rlist *rp, *deps = PromiseGetConstraintAsList(ctx, "depends_on", pp); for (rp = deps; rp != NULL; rp = rp->next) { if (strchr(rp->item, ':')) { d = (char *)rp->item; } else { snprintf(name, CF_BUFSIZE, "%s:%s", PromiseGetNamespace(pp), (char *)rp->item); d = name; } if (!StringSetContains(ctx->dependency_handles, d)) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "\n"); CfOut(OUTPUT_LEVEL_VERBOSE, "", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . \n"); CfOut(OUTPUT_LEVEL_VERBOSE, "", "Skipping whole next promise (%s), as promise dependency %s has not yet been kept\n", pp->promiser, d); CfOut(OUTPUT_LEVEL_VERBOSE, "", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . \n"); return true; } } return false; }
/** * @param #tags is a comma separated string of words. * Both "" or NULL are equivalent. */ static void ClassInit(Class *cls, const char *ns, const char *name, bool is_soft, ContextScope scope, const char *tags) { if (ns == NULL || strcmp(ns, "default") == 0) { cls->ns = NULL; } else { cls->ns = xstrdup(ns); } cls->name = xstrdup(name); CanonifyNameInPlace(cls->name); cls->is_soft = is_soft; cls->scope = scope; cls->tags = StringSetFromString(tags, ','); if (!is_soft && !StringSetContains(cls->tags, "hardclass")) { StringSetAdd(cls->tags, xstrdup("hardclass")); } }
static bool EvalContextStackFrameContainsNegated(const EvalContext *ctx, const char *context) { if (SeqLength(ctx->stack) == 0) { return false; } return StringSetContains(EvalContextStackFrame(ctx)->contexts_negated, context); }
bool StackFrameContainsNegatedRecursive(const EvalContext *ctx, const char *context, size_t stack_index) { StackFrame *frame = SeqAt(ctx->stack, stack_index); if (frame->type == STACK_FRAME_TYPE_BUNDLE && StringSetContains(frame->data.bundle.contexts_negated, context)) { return true; } else if (stack_index > 0 && frame->inherits_previous) { return StackFrameContainsNegatedRecursive(ctx, context, stack_index - 1); } else { return false; } }
bool StackFrameContainsSoftRecursive(const EvalContext *ctx, const char *context, size_t stack_index) { StackFrame *frame = SeqAt(ctx->stack, stack_index); if (StringSetContains(frame->contexts, context)) { return true; } else if (stack_index > 0 && frame->inherits_previous) { return StackFrameContainsSoftRecursive(ctx, context, stack_index - 1); } else { return false; } }
static ExpressionValue EvalTokenFromList(ARG_UNUSED const EvalContext *ctx, const char *token, void *param) { StringSet *set = param; return StringSetContains(set, token); }
bool EvalContextHeapContainsNegated(const EvalContext *ctx, const char *context) { return StringSetContains(ctx->heap_negated, context); }
bool EvalContextHeapContainsHard(const EvalContext *ctx, const char *context) { return StringSetContains(ctx->heap_hard, context); }
bool EvalContextHeapContainsSoft(const EvalContext *ctx, const char *context) { return StringSetContains(ctx->heap_soft, context); }
static ExpressionValue EvalTokenFromList(const char *token, void *param) { StringSet *set = param; return StringSetContains(set, token); }
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 execd_config_empty_cb(const EvalContext *ctx, const Policy *policy) { ExecdConfig *config = ExecdConfigNew(ctx, policy); assert_int_equal(12, StringSetSize(config->schedule)); assert_int_equal(true, StringSetContains(config->schedule, "Min00")); assert_int_equal(true, StringSetContains(config->schedule, "Min05")); assert_int_equal(true, StringSetContains(config->schedule, "Min10")); assert_int_equal(true, StringSetContains(config->schedule, "Min15")); assert_int_equal(true, StringSetContains(config->schedule, "Min20")); assert_int_equal(true, StringSetContains(config->schedule, "Min25")); assert_int_equal(true, StringSetContains(config->schedule, "Min30")); assert_int_equal(true, StringSetContains(config->schedule, "Min35")); assert_int_equal(true, StringSetContains(config->schedule, "Min40")); assert_int_equal(true, StringSetContains(config->schedule, "Min45")); assert_int_equal(true, StringSetContains(config->schedule, "Min50")); assert_int_equal(true, StringSetContains(config->schedule, "Min55")); assert_int_equal(0, config->splay_time); assert_string_equal("LOG_USER", config->log_facility); ExecdConfigDestroy(config); }
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); }
static void test_class_persistence(void) { EvalContext *ctx = EvalContextNew(); // simulate old version { CF_DB *dbp; PersistentClassInfo i; assert_true(OpenDB(&dbp, dbid_state)); i.expires = UINT_MAX; i.policy = CONTEXT_STATE_POLICY_RESET; WriteDB(dbp, "old", &i, sizeof(PersistentClassInfo)); CloseDB(dbp); } // e.g. by monitoring EvalContextHeapPersistentSave(ctx, "class1", 3, CONTEXT_STATE_POLICY_PRESERVE, "a,b"); // e.g. by a class promise in a bundle with a namespace { Policy *p = PolicyNew(); Bundle *bp = PolicyAppendBundle(p, "ns1", "bundle1", "agent", NULL, NULL); EvalContextStackPushBundleFrame(ctx, bp, NULL, false); EvalContextHeapPersistentSave(ctx, "class2", 5, CONTEXT_STATE_POLICY_PRESERVE, "x"); EvalContextStackPopFrame(ctx); PolicyDestroy(p); } EvalContextHeapPersistentLoadAll(ctx); { const Class *cls = EvalContextClassGet(ctx, "default", "old"); assert_true(cls != NULL); assert_string_equal("old", cls->name); assert_true(cls->tags != NULL); assert_int_equal(1, StringSetSize(cls->tags)); assert_true(StringSetContains(cls->tags, "source=persistent")); } { const Class *cls = EvalContextClassGet(ctx, "default", "class1"); assert_true(cls != NULL); assert_string_equal("class1", cls->name); assert_true(cls->tags != NULL); assert_int_equal(3, StringSetSize(cls->tags)); assert_true(StringSetContains(cls->tags, "source=persistent")); assert_true(StringSetContains(cls->tags, "a")); assert_true(StringSetContains(cls->tags, "b")); } { const Class *cls = EvalContextClassGet(ctx, "ns1", "class2"); assert_true(cls != NULL); assert_string_equal("ns1", cls->ns); assert_string_equal("class2", cls->name); assert_true(cls->tags != NULL); assert_int_equal(2, StringSetSize(cls->tags)); assert_true(StringSetContains(cls->tags, "source=persistent")); assert_true(StringSetContains(cls->tags, "x")); } EvalContextDestroy(ctx); }