// TODO: should be replaced by something not complected with loading static void ShowContext(EvalContext *ctx) { Seq *hard_contexts = SeqNew(1000, NULL); Seq *soft_contexts = SeqNew(1000, NULL); { ClassTableIterator *iter = EvalContextClassTableIteratorNewGlobal(ctx, NULL, true, true); Class *cls = NULL; while ((cls = ClassTableIteratorNext(iter))) { if (cls->is_soft) { SeqAppend(soft_contexts, cls->name); } else { SeqAppend(hard_contexts, cls->name); } } ClassTableIteratorDestroy(iter); } SeqSort(soft_contexts, (SeqItemComparator)strcmp, NULL); SeqSort(hard_contexts, (SeqItemComparator)strcmp, NULL); Log(LOG_LEVEL_VERBOSE, "----------------------------------------------------------------"); { Log(LOG_LEVEL_VERBOSE, "BEGIN Discovered hard classes:"); for (size_t i = 0; i < SeqLength(hard_contexts); i++) { const char *context = SeqAt(hard_contexts, i); Log(LOG_LEVEL_VERBOSE, "C: discovered hard class %s", context); } Log(LOG_LEVEL_VERBOSE, "END Discovered hard classes"); } Log(LOG_LEVEL_VERBOSE, "----------------------------------------------------------------"); if (SeqLength(soft_contexts)) { Log(LOG_LEVEL_VERBOSE, "BEGIN initial soft classes:"); for (size_t i = 0; i < SeqLength(soft_contexts); i++) { const char *context = SeqAt(soft_contexts, i); Log(LOG_LEVEL_VERBOSE, "C: added soft class %s", context); } Log(LOG_LEVEL_VERBOSE, "END initial soft classes"); } SeqDestroy(hard_contexts); SeqDestroy(soft_contexts); }
PromiseIterator *PromiseIteratorNew(EvalContext *ctx, const Promise *pp, const Rlist *lists, const Rlist *containers) { PromiseIterator *iter = xmalloc(sizeof(PromiseIterator)); iter->vars = SeqNew(RlistLen(lists), DeleteAssoc); iter->var_states = SeqNew(RlistLen(lists), NULL); iter->has_null_list = false; for (const Rlist *rp = lists; rp != NULL; rp = rp->next) { VarRef *ref = VarRefParseFromBundle(RlistScalarValue(rp), PromiseGetBundle(pp)); DataType dtype = CF_DATA_TYPE_NONE; const void *value = EvalContextVariableGet(ctx, ref, &dtype); if (!value) { Log(LOG_LEVEL_ERR, "Couldn't locate variable '%s' apparently in '%s'", RlistScalarValue(rp), PromiseGetBundle(pp)->name); VarRefDestroy(ref); continue; } VarRefDestroy(ref); CfAssoc *new_var = NewAssoc(RlistScalarValue(rp), (Rval) { (void *)value, DataTypeToRvalType(dtype) }, dtype); iter->has_null_list |= !AppendIterationVariable(iter, new_var); } for (const Rlist *rp = containers; rp; rp = rp->next) { VarRef *ref = VarRefParseFromBundle(RlistScalarValue(rp), PromiseGetBundle(pp)); DataType dtype = CF_DATA_TYPE_NONE; const JsonElement *value = EvalContextVariableGet(ctx, ref, &dtype); if (!value) { Log(LOG_LEVEL_ERR, "Couldn't locate variable '%s' apparently in '%s'", RlistScalarValue(rp), PromiseGetBundle(pp)->name); VarRefDestroy(ref); continue; } VarRefDestroy(ref); assert(dtype == CF_DATA_TYPE_CONTAINER); /* Mimics NewAssoc() but bypassing extra copying of ->rval: */ CfAssoc *new_var = xmalloc(sizeof(CfAssoc)); new_var->lval = xstrdup(RlistScalarValue(rp)); new_var->rval = (Rval) { ContainerToRlist(value), RVAL_TYPE_LIST }; new_var->dtype = CF_DATA_TYPE_STRING_LIST; iter->has_null_list |= !AppendIterationVariable(iter, new_var); } // We now have a control list of list-variables, with internal state in state_ptr return iter; }
static void SeqMailFilterFill(const Seq *input, Seq **output, Seq **output_regex, const char *filter_type) { int len = SeqLength(input); *output = SeqNew(len, &free); *output_regex = SeqNew(len, &RegexFree); for (int i = 0; i < len; i++) { MailFilterFill(SeqAt(input, i), output, output_regex, filter_type); } }
static void test_failsafe(void) { char *tmp = tempnam(NULL, "cfengine_test"); WriteBuiltinFailsafePolicyToPath(tmp); Policy *failsafe = ParserParseFile(tmp, PARSER_WARNING_ALL, PARSER_WARNING_ALL); unlink(tmp); free(tmp); assert_true(failsafe); Seq *errs = SeqNew(10, PolicyErrorDestroy); PolicyCheckPartial(failsafe, errs); DumpErrors(errs); assert_int_equal(0, SeqLength(errs)); { EvalContext *ctx = EvalContextNew(); PolicyCheckRunnable(ctx, failsafe, errs, false); DumpErrors(errs); assert_int_equal(0, SeqLength(errs)); EvalContextDestroy(ctx); } assert_int_equal(0, (SeqLength(errs))); SeqDestroy(errs); PolicyDestroy(failsafe); }
static void ShowVariablesFormatted(EvalContext *ctx) { VariableTableIterator *iter = EvalContextVariableTableIteratorNew(ctx, NULL, NULL, NULL); Variable *v = NULL; Seq *seq = SeqNew(2000, free); while ((v = VariableTableIteratorNext(iter))) { char *varname = VarRefToString(v->ref, true); Writer *w = StringWriter(); switch (DataTypeToRvalType(v->type)) { case RVAL_TYPE_CONTAINER: JsonWriteCompact(w, RvalContainerValue(v->rval)); break; default: RvalWrite(w, v->rval); } const char *var_value; if (StringIsPrintable(StringWriterData(w))) { var_value = StringWriterData(w); } else { var_value = "<non-printable>"; } StringSet *tagset = EvalContextVariableTags(ctx, v->ref); Buffer *tagbuf = StringSetToBuffer(tagset, ','); char *line; xasprintf(&line, "%-40s %-60s %-40s", varname, var_value, BufferData(tagbuf)); SeqAppend(seq, line); BufferDestroy(tagbuf); WriterClose(w); free(varname); } SeqSort(seq, (SeqItemComparator)strcmp, NULL); printf("%-40s %-60s %-40s\n", "Variable name", "Variable value", "Meta tags"); for (size_t i = 0; i < SeqLength(seq); i++) { const char *variable = SeqAt(seq, i); printf("%s\n", variable); } SeqDestroy(seq); VariableTableIteratorDestroy(iter); }
static void ShowContextsFormatted(EvalContext *ctx) { ClassTableIterator *iter = EvalContextClassTableIteratorNewGlobal(ctx, NULL, true, true); Class *cls = NULL; Seq *seq = SeqNew(1000, free); while ((cls = ClassTableIteratorNext(iter))) { char *class_name = ClassRefToString(cls->ns, cls->name); StringSet *tagset = EvalContextClassTags(ctx, cls->ns, cls->name); Buffer *tagbuf = StringSetToBuffer(tagset, ','); char *line; xasprintf(&line, "%-60s %-40s", class_name, BufferData(tagbuf)); SeqAppend(seq, line); BufferDestroy(tagbuf); free(class_name); } SeqSort(seq, (SeqItemComparator)strcmp, NULL); printf("%-60s %-40s\n", "Class name", "Meta tags"); for (size_t i = 0; i < SeqLength(seq); i++) { const char *context = SeqAt(seq, i); printf("%s\n", context); } SeqDestroy(seq); ClassTableIteratorDestroy(iter); }
static void test_sort(void) { Seq *seq = SeqNew(5, NULL); size_t one = 1; size_t two = 2; size_t three = 3; size_t four = 4; size_t five = 5; SeqAppend(seq, &three); SeqAppend(seq, &two); SeqAppend(seq, &five); SeqAppend(seq, &one); SeqAppend(seq, &four); SeqSort(seq, CompareNumbers, NULL); assert_int_equal(seq->data[0], &one); assert_int_equal(seq->data[1], &two); assert_int_equal(seq->data[2], &three); assert_int_equal(seq->data[3], &four); assert_int_equal(seq->data[4], &five); SeqDestroy(seq); }
static void RlistMailFilterFill(const Rlist *input, Seq **output, Seq **output_regex, const char *filter_type) { int len = RlistLen(input); *output = SeqNew(len, &free); *output_regex = SeqNew(len, &RegexFree); const Rlist *ptr = input; for (int i = 0; i < len; i++) { const char *str = ptr->val.item; MailFilterFill(str, output, output_regex, filter_type); ptr = ptr->next; } }
Policy *GenericAgentLoadPolicy(EvalContext *ctx, GenericAgentConfig *config) { config->policy_last_read_attempt = time(NULL); StringSet *parsed_files = StringSetNew(); StringSet *failed_files = StringSetNew(); Policy *policy = LoadPolicyFile(ctx, config, config->input_file, parsed_files, failed_files); if (StringSetSize(failed_files) > 0) { Log(LOG_LEVEL_ERR, "There are syntax errors in policy files"); exit(EXIT_FAILURE); } StringSetDestroy(parsed_files); 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) { VerifyPromises(ctx, policy, config); } return policy; }
Seq *GetGlobalMountedFSList(void) { static Seq *mounted_fs_list = NULL; if (!mounted_fs_list) { mounted_fs_list = SeqNew(100, free); } return mounted_fs_list; }
Promise *ExpandDeRefPromise(EvalContext *ctx, const char *scopeid, const Promise *pp) { Promise *pcopy; Rval returnval, final; CfDebug("ExpandDerefPromise()\n"); pcopy = xcalloc(1, sizeof(Promise)); returnval = ExpandPrivateRval(ctx, "this", (Rval) {pp->promiser, RVAL_TYPE_SCALAR }); pcopy->promiser = (char *) returnval.item; if (pp->promisee.item) { pcopy->promisee = EvaluateFinalRval(ctx, scopeid, pp->promisee, true, pp); } else { pcopy->promisee = (Rval) {NULL, RVAL_TYPE_NOPROMISEE }; } if (pp->classes) { pcopy->classes = xstrdup(pp->classes); } else { pcopy->classes = xstrdup("any"); } if (pcopy->promiser == NULL) { ProgrammingError("ExpandPromise returned NULL"); } pcopy->parent_promise_type = pp->parent_promise_type; pcopy->offset.line = pp->offset.line; pcopy->comment = pp->comment ? xstrdup(pp->comment) : NULL; pcopy->conlist = SeqNew(10, ConstraintDestroy); pcopy->org_pp = pp->org_pp; /* No further type checking should be necessary here, already done by CheckConstraintTypeMatch */ for (size_t i = 0; i < SeqLength(pp->conlist); i++) { Constraint *cp = SeqAt(pp->conlist, i); Rval returnval; if (ExpectedDataType(cp->lval) == DATA_TYPE_BUNDLE) { final = ExpandBundleReference(ctx, scopeid, cp->rval); } else {
Seq *SeqParseCsvString(const char *string) { Seq *newlist = SeqNew(16, free); if (LaunchCsvAutomata(string, &newlist) != CSV_ERR_OK) { return NULL; } return newlist; }
static Seq *LoadAndCheck(const char *filename) { Policy *p = LoadPolicy(filename); Seq *errs = SeqNew(10, PolicyErrorDestroy); PolicyCheckPartial(p, errs); DumpErrors(errs); return errs; }
static Seq *SequenceCreateRange(size_t initialCapacity, size_t start, size_t end) { Seq *seq = SeqNew(initialCapacity, free); for (size_t i = start; i <= end; i++) { size_t *item = xmalloc(sizeof(size_t)); *item = i; SeqAppend(seq, item); } return seq; }
static void RemoveRemotelyInjectedVars(const EvalContext *ctx, const Bundle *bundle) { const Seq *remote_var_promises = EvalContextGetRemoteVarPromises(ctx, bundle->name); if ((remote_var_promises == NULL) || SeqLength(remote_var_promises) == 0) { /* nothing to do here */ return; } size_t promises_length = SeqLength(remote_var_promises); Seq *remove_vars = SeqNew(promises_length, NULL); /* remove variables that have been attempted to be inserted into this * bundle */ /* TODO: this is expensive and should be removed! */ for (size_t i = 0; i < promises_length; i++) { const Promise *pp = (Promise *) SeqAt(remote_var_promises, i); VariableTableIterator *iter = EvalContextVariableTableIteratorNew(ctx, NULL, bundle->name, NULL); const Variable *var = VariableTableIteratorNext(iter); while (var != NULL) { /* variables are stored together with their original promises (org_pp) */ if (var->promise && var->promise->org_pp == pp) { Log(LOG_LEVEL_ERR, "Ignoring remotely-injected variable '%s'", var->ref->lval); /* avoid modifications of the variable table being iterated * over and avoid trying to remove the same variable twice */ SeqAppendOnce(remove_vars, (void *) var, PointerCmp); } var = VariableTableIteratorNext(iter); } VariableTableIteratorDestroy(iter); } /* iteration over the variable table done, time to remove the variables */ size_t remove_vars_length = SeqLength(remove_vars); for (size_t i = 0; i < remove_vars_length; i++) { Variable *var = (Variable *) SeqAt(remove_vars, i); if (var->ref != NULL) { EvalContextVariableRemove(ctx, var->ref); } } SeqDestroy(remove_vars); }
EvalContext *EvalContextNew(void) { EvalContext *ctx = xmalloc(sizeof(EvalContext)); ctx->heap_soft = StringSetNew(); ctx->heap_hard = StringSetNew(); ctx->heap_negated = StringSetNew(); ctx->heap_abort = NULL; ctx->heap_abort_current_bundle = NULL; ctx->stack = SeqNew(10, StackFrameDestroy); ctx->dependency_handles = StringSetNew(); return ctx; }
static Seq *FindPackages(const char *database_filename, PackagePattern *pattern) { Seq *db = ReadPackageEntries(database_filename); Seq *matching = SeqNew(1000, NULL); for (size_t i = 0; i < SeqLength(db); i++) { Package *package = SeqAt(db, i); if (MatchPackage(pattern, package)) { SeqAppend(matching, package); } } return matching; }
static void test_append(void) { Seq *seq = SeqNew(2, free); for (size_t i = 0; i < 1000; i++) { SeqAppend(seq, xstrdup("snookie")); } assert_int_equal(seq->length, 1000); for (size_t i = 0; i < 1000; i++) { assert_string_equal(seq->data[i], "snookie"); } SeqDestroy(seq); }
static void test_len(void) { Seq *seq = SeqNew(5, NULL); size_t one = 1; size_t two = 2; size_t three = 3; size_t four = 4; size_t five = 5; SeqAppend(seq, &three); SeqAppend(seq, &two); SeqAppend(seq, &five); SeqAppend(seq, &one); SeqAppend(seq, &four); assert_int_equal(SeqLength(seq),5); SeqDestroy(seq); }
static Seq *LoadAndCheckString(const char *policy_code) { const char *tmp = tempnam(NULL, "cfengine_test"); { FILE *out = fopen(tmp, "w"); Writer *w = FileWriter(out); WriterWrite(w, policy_code); WriterClose(w); } Policy *p = ParserParseFile(tmp, PARSER_WARNING_ALL, PARSER_WARNING_ALL); assert_true(p); Seq *errs = SeqNew(10, PolicyErrorDestroy); PolicyCheckPartial(p, errs); unlink(tmp); return errs; }
static Seq *ReadPackageEntries(const char *database_filename) { FILE *packages_file = fopen(database_filename, "r"); Seq *packages = SeqNew(1000, NULL); if (packages_file != NULL) { char serialized_package[MAX_PACKAGE_ENTRY_LENGTH]; while (fscanf(packages_file, "%s\n", serialized_package) != EOF) { Package *package = DeserializePackage(serialized_package); SeqAppend(packages, package); } fclose(packages_file); } return packages; }
bool MustacheRender(Buffer *out, const char *input, const JsonElement *hash) { char delim_start[MUSTACHE_MAX_DELIM_SIZE] = "{{"; size_t delim_start_len = strlen(delim_start); char delim_end[MUSTACHE_MAX_DELIM_SIZE] = "}}"; size_t delim_end_len = strlen(delim_end); Seq *hash_stack = SeqNew(10, NULL); SeqAppend(hash_stack, (JsonElement*)hash); bool success = Render(out, input, input, hash_stack, delim_start, &delim_start_len, delim_end, &delim_end_len, false, NULL, NULL); SeqDestroy(hash_stack); return success; }
Promise *DeRefCopyPromise(EvalContext *ctx, const Promise *pp) { Promise *pcopy; Rval returnval; pcopy = xcalloc(1, sizeof(Promise)); if (pp->promiser) { pcopy->promiser = xstrdup(pp->promiser); } if (pp->promisee.item) { pcopy->promisee = RvalCopy(pp->promisee); if (pcopy->promisee.type == RVAL_TYPE_LIST) { Rlist *rval_list = RvalRlistValue(pcopy->promisee); RlistFlatten(ctx, &rval_list); pcopy->promisee.item = rval_list; } } if (pp->classes) { pcopy->classes = xstrdup(pp->classes); } /* FIXME: may it happen? */ if ((pp->promisee.item != NULL && pcopy->promisee.item == NULL)) { ProgrammingError("Unable to copy promise"); } pcopy->parent_promise_type = pp->parent_promise_type; pcopy->offset.line = pp->offset.line; pcopy->comment = pp->comment ? xstrdup(pp->comment) : NULL; pcopy->has_subbundles = pp->has_subbundles; pcopy->conlist = SeqNew(10, ConstraintDestroy); pcopy->org_pp = pp->org_pp; pcopy->offset = pp->offset; /* No further type checking should be necessary here, already done by CheckConstraintTypeMatch */ for (size_t i = 0; i < SeqLength(pp->conlist); i++) { Constraint *cp = SeqAt(pp->conlist, i); Body *bp = NULL; FnCall *fp = NULL; /* A body template reference could look like a scalar or fn to the parser w/w () */ const Policy *policy = PolicyFromPromise(pp); Seq *bodies = policy ? policy->bodies : NULL; char body_ns[CF_MAXVARSIZE] = ""; char body_name[CF_MAXVARSIZE] = ""; switch (cp->rval.type) { case RVAL_TYPE_SCALAR: if (cp->references_body) { SplitScopeName(RvalScalarValue(cp->rval), body_ns, body_name); if (EmptyString(body_ns)) { strncpy(body_ns, PromiseGetNamespace(pp), CF_MAXVARSIZE); } bp = IsBody(bodies, body_ns, body_name); } fp = NULL; break; case RVAL_TYPE_FNCALL: fp = RvalFnCallValue(cp->rval); SplitScopeName(fp->name, body_ns, body_name); if (EmptyString(body_ns)) { strncpy(body_ns, PromiseGetNamespace(pp), CF_MAXVARSIZE); } bp = IsBody(bodies, body_ns, body_name); break; default: bp = NULL; fp = NULL; break; } /* First case is: we have a body template to expand lval = body(args), .. */ if (bp) { EvalContextStackPushBodyFrame(ctx, pcopy, bp, fp ? fp->args : NULL); if (strcmp(bp->type, cp->lval) != 0) { Log(LOG_LEVEL_ERR, "Body type mismatch for body reference '%s' in promise at line %zu of file '%s', '%s' does not equal '%s'", body_name, pp->offset.line, PromiseGetBundle(pp)->source_path, bp->type, cp->lval); } /* Keep the referent body type as a boolean for convenience when checking later */ if (IsDefinedClass(ctx, cp->classes, PromiseGetNamespace(pcopy))) { Constraint *cp_copy = PromiseAppendConstraint(pcopy, cp->lval, (Rval) {xstrdup("true"), RVAL_TYPE_SCALAR }, false); cp_copy->offset = cp->offset; } if (bp->args != NULL) { /* There are arguments to insert */ if (fp == NULL || fp->args == NULL) { Log(LOG_LEVEL_ERR, "Argument mismatch for body reference '%s' in promise at line %zu of file '%s'", body_name, pp->offset.line, PromiseGetBundle(pp)->source_path); } for (size_t k = 0; k < SeqLength(bp->conlist); k++) { Constraint *scp = SeqAt(bp->conlist, k); returnval = ExpandPrivateRval(ctx, NULL, "body", scp->rval.item, scp->rval.type); if (IsDefinedClass(ctx, scp->classes, PromiseGetNamespace(pcopy))) { Constraint *scp_copy = PromiseAppendConstraint(pcopy, scp->lval, returnval, false); scp_copy->offset = scp->offset; } } } else { /* No arguments to deal with or body undeclared */ if (fp != NULL) { Log(LOG_LEVEL_ERR, "An apparent body \"%s()\" was undeclared or could have incorrect args, but used in a promise near line %zu of %s (possible unquoted literal value)", body_name, pp->offset.line, PromiseGetBundle(pp)->source_path); } else { for (size_t k = 0; k < SeqLength(bp->conlist); k++) { Constraint *scp = SeqAt(bp->conlist, k); Rval newrv = RvalCopy(scp->rval); if (newrv.type == RVAL_TYPE_LIST) { Rlist *new_list = RvalRlistValue(newrv); RlistFlatten(ctx, &new_list); newrv.item = new_list; } if (IsDefinedClass(ctx, scp->classes, PromiseGetNamespace(pcopy))) { Constraint *scp_copy = PromiseAppendConstraint(pcopy, scp->lval, newrv, false); scp_copy->offset = scp->offset; } } } } EvalContextStackPopFrame(ctx); } else { const Policy *policy = PolicyFromPromise(pp); if (cp->references_body && !IsBundle(policy->bundles, EmptyString(body_ns) ? NULL : body_ns, body_name)) { Log(LOG_LEVEL_ERR, "Apparent body \"%s()\" was undeclared, but used in a promise near line %zu of %s (possible unquoted literal value)", body_name, pp->offset.line, PromiseGetBundle(pp)->source_path); } Rval newrv = RvalCopy(cp->rval); if (newrv.type == RVAL_TYPE_LIST) { Rlist *new_list = RvalRlistValue(newrv); RlistFlatten(ctx, &new_list); newrv.item = new_list; } if (IsDefinedClass(ctx, cp->classes, PromiseGetNamespace(pcopy))) { Constraint *cp_copy = PromiseAppendConstraint(pcopy, cp->lval, newrv, false); cp_copy->offset = cp->offset; } } } return pcopy; }
static void Apoptosis() { Promise pp = { 0 }; Rlist *signals = NULL, *owners = NULL; char mypid[32]; static char promiser_buf[CF_SMALLBUF]; #if defined(_WIN32) return; #endif CfOut(OUTPUT_LEVEL_VERBOSE, "", " !! Programmed pruning of the scheduler cluster"); #ifdef __MINGW32__ snprintf(promiser_buf, sizeof(promiser_buf), "cf-execd"); // using '\' causes regexp problems #else snprintf(promiser_buf, sizeof(promiser_buf), "%s/bin/cf-execd", CFWORKDIR); #endif pp.promiser = promiser_buf; pp.promisee = (Rval) {"cfengine", RVAL_TYPE_SCALAR}; pp.classes = "any"; pp.offset.line = 0; pp.audit = NULL; pp.conlist = SeqNew(100, ConstraintDestroy); pp.bundletype = "agent"; pp.bundle = "exec_apoptosis"; pp.ref = "Programmed death"; pp.agentsubtype = "processes"; pp.done = false; pp.cache = NULL; pp.inode_cache = NULL; pp.this_server = NULL; pp.donep = &(pp.done); pp.conn = NULL; GetCurrentUserName(mypid, 31); RlistPrepend(&signals, "term", RVAL_TYPE_SCALAR); RlistPrepend(&owners, mypid, RVAL_TYPE_SCALAR); PromiseAppendConstraint(&pp, "signals", (Rval) {signals, RVAL_TYPE_LIST }, "any", false); PromiseAppendConstraint(&pp, "process_select", (Rval) {xstrdup("true"), RVAL_TYPE_SCALAR}, "any", false); PromiseAppendConstraint(&pp, "process_owner", (Rval) {owners, RVAL_TYPE_LIST }, "any", false); PromiseAppendConstraint(&pp, "ifelapsed", (Rval) {xstrdup("0"), RVAL_TYPE_SCALAR}, "any", false); PromiseAppendConstraint(&pp, "process_count", (Rval) {xstrdup("true"), RVAL_TYPE_SCALAR}, "any", false); PromiseAppendConstraint(&pp, "match_range", (Rval) {xstrdup("0,2"), RVAL_TYPE_SCALAR}, "any", false); PromiseAppendConstraint(&pp, "process_result", (Rval) {xstrdup("process_owner.process_count"), RVAL_TYPE_SCALAR}, "any", false); CfOut(OUTPUT_LEVEL_VERBOSE, "", " -> Looking for cf-execd processes owned by %s", mypid); if (LoadProcessTable(&PROCESSTABLE)) { VerifyProcessesPromise(&pp); } DeleteItemList(PROCESSTABLE); SeqDestroy(pp.conlist); CfOut(OUTPUT_LEVEL_VERBOSE, "", " !! Pruning complete"); }
static void *CFTestD_ServeReport(void *config_arg) { CFTestD_Config *config = (CFTestD_Config *) config_arg; /* Set prefix for all Log()ging: */ LoggingPrivContext *prior = LoggingPrivGetContext(); LoggingPrivContext log_ctx = { .log_hook = LogAddPrefix, .param = config->address }; LoggingPrivSetContext(&log_ctx); char *priv_key_path = NULL; char *pub_key_path = NULL; if (config->key_file != NULL) { priv_key_path = config->key_file; pub_key_path = xstrdup(priv_key_path); StringReplace(pub_key_path, strlen(pub_key_path) + 1, "priv", "pub"); } LoadSecretKeys(priv_key_path, pub_key_path, &(config->priv_key), &(config->pub_key)); free(pub_key_path); char *report_file = config->report_file; if (report_file != NULL) { Log(LOG_LEVEL_NOTICE, "Got file argument: '%s'", report_file); if (!FileCanOpen(report_file, "r")) { Log(LOG_LEVEL_ERR, "Can't open file '%s' for reading", report_file); exit(EXIT_FAILURE); } Writer *contents = FileRead(report_file, SIZE_MAX, NULL); if (!contents) { Log(LOG_LEVEL_ERR, "Error reading report file '%s'", report_file); exit(EXIT_FAILURE); } size_t report_data_len = StringWriterLength(contents); config->report_data = StringWriterClose(contents); Seq *report = SeqNew(64, NULL); size_t report_len = 0; StringRef ts_ref = StringGetToken(config->report_data, report_data_len, 0, "\n"); char *ts = (char *) ts_ref.data; *(ts + ts_ref.len) = '\0'; SeqAppend(report, ts); /* start right after the newline after the timestamp header */ char *position = ts + ts_ref.len + 1; char *report_line; size_t report_line_len; while (CFTestD_GetReportLine(position, &report_line, &report_line_len)) { *(report_line + report_line_len) = '\0'; SeqAppend(report, report_line); report_len += report_line_len; position = report_line + report_line_len + 1; /* there's an extra newline after each report_line */ } config->report = report; config->report_len = report_len; Log(LOG_LEVEL_NOTICE, "Read %d bytes for report contents", config->report_len); if (config->report_len <= 0) { Log(LOG_LEVEL_ERR, "Report file contained no bytes"); exit(EXIT_FAILURE); } } Log(LOG_LEVEL_INFO, "Starting server at %s...", config->address); fflush(stdout); // for debugging startup config->ret = CFTestD_StartServer(config); free(config->report_data); /* we don't really need to do this here because the process is about the * terminate, but it's a good way the cleanup actually works and doesn't * cause a segfault or something */ ServerTLSDeInitialize(&(config->priv_key), &(config->pub_key), &(config->ssl_ctx)); LoggingPrivSetContext(prior); return NULL; } static void HandleSignal(int signum) { switch (signum) { case SIGTERM: case SIGINT: // flush all logging before process ends. fflush(stdout); fprintf(stderr, "Terminating...\n"); TERMINATE = true; break; default: break; } } /** * @param ip_str string representation of an IPv4 address (the usual one, with * 4 octets separated by dots) * @return a new string representing the incremented IP address (HAS TO BE FREED) */ static char *IncrementIPaddress(const char *ip_str) { uint32_t ip = (uint32_t) inet_addr(ip_str); if (ip == INADDR_NONE) { Log(LOG_LEVEL_ERR, "Failed to parse address: '%s'", ip_str); return NULL; } int step = 1; char *last_dot = strrchr(ip_str, '.'); assert(last_dot != NULL); /* the doc comment says there must be dots! */ if (StringSafeEqual(last_dot + 1, "255")) { /* avoid the network address (ending with 0) */ step = 2; } else if (StringSafeEqual(last_dot + 1, "254")) { /* avoid the broadcast address and the network address */ step = 3; } uint32_t ip_num = ntohl(ip); ip_num += step; ip = htonl(ip_num); struct in_addr ip_struct; ip_struct.s_addr = ip; return xstrdup(inet_ntoa(ip_struct)); }
// If return_names is not set, only the captured data is returned (so // for N captures you can expect N elements in the Sequence). Seq *StringMatchCapturesWithPrecompiledRegex(const pcre *pattern, const char *str, const bool return_names) { int captures; int res = pcre_fullinfo(pattern, NULL, PCRE_INFO_CAPTURECOUNT, &captures); if (res != 0) { return NULL; } // Get the table of named captures. unsigned char *name_table = NULL; // Doesn't have to be freed as per docs. int namecount = 0; int name_entry_size = 0; unsigned char *tabptr; pcre_fullinfo(pattern, NULL, PCRE_INFO_NAMECOUNT, &namecount); const bool have_named_captures = (namecount > 0 && return_names); if (have_named_captures) { pcre_fullinfo(pattern, NULL, PCRE_INFO_NAMETABLE, &name_table); pcre_fullinfo(pattern, NULL, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size); } int *ovector = xmalloc(sizeof(int) * (captures + 1) * 3); int result = pcre_exec(pattern, NULL, str, strlen(str), 0, 0, ovector, (captures + 1) * 3); if (result <= 0) { free(ovector); return NULL; } Seq *ret = SeqNew(captures + 1, BufferDestroy); for (int i = 0; i <= captures; ++i) { Buffer *capture = NULL; if (have_named_captures) { // The overhead of doing a nested name scan is negligible. tabptr = name_table; for (int namepos = 0; namepos < namecount; namepos++) { int n = (tabptr[0] << 8) | tabptr[1]; if (n == i) // We found the position { capture = BufferNewFrom(tabptr + 2, name_entry_size - 3); break; } tabptr += name_entry_size; } } if (return_names) { if (NULL == capture) { capture = BufferNew(); BufferAppendF(capture, "%zd", i); } SeqAppend(ret, capture); } Buffer *data = BufferNewFrom(str + ovector[2*i], ovector[2*i + 1] - ovector[2 * i]); Log(LOG_LEVEL_DEBUG, "StringMatchCaptures: return_names = %d, have_named_captures = %d, offset %d, name '%s', data '%s'", return_names, have_named_captures, i, capture == NULL ? "no_name" : BufferData(capture), BufferData(data)); SeqAppend(ret, data); } free(ovector); return ret; }
static JsonElement *BundleTypesToJson(void) { JsonElement *bundle_types = JsonObjectCreate(50); Seq *common_promise_types = SeqNew(50, free); for (int module_index = 0; module_index < CF3_MODULES; module_index++) { for (int promise_type_index = 0; CF_ALL_PROMISE_TYPES[module_index][promise_type_index].promise_type; promise_type_index++) { const PromiseTypeSyntax *promise_type_syntax = &CF_ALL_PROMISE_TYPES[module_index][promise_type_index]; // skip global constraints if (strcmp("*", promise_type_syntax->promise_type) == 0) { continue; } // collect common promise types to be appended at the end if (strcmp("*", promise_type_syntax->bundle_type) == 0) { SeqAppend(common_promise_types, xstrdup(promise_type_syntax->promise_type)); continue; } if (promise_type_syntax->status == SYNTAX_STATUS_REMOVED) { continue; } JsonElement *bundle_type = JsonObjectGet(bundle_types, promise_type_syntax->bundle_type); if (!bundle_type) { bundle_type = JsonBundleTypeNew(); JsonObjectAppendObject(bundle_types, promise_type_syntax->bundle_type, bundle_type); } assert(bundle_type); JsonElement *promise_types = JsonObjectGet(bundle_type, "promiseTypes"); assert(promise_types); JsonArrayAppendString(promise_types, promise_type_syntax->promise_type); } } // Append the common bundle, which has only common promise types, but is not declared in syntax { JsonElement *bundle_type = JsonBundleTypeNew(); JsonObjectAppendObject(bundle_types, "common", bundle_type); } JsonIterator it = JsonIteratorInit(bundle_types); const char *bundle_type = NULL; while ((bundle_type = JsonIteratorNextKey(&it))) { JsonElement *promise_types = JsonObjectGetAsArray(JsonObjectGetAsObject(bundle_types, bundle_type), "promiseTypes"); for (int i = 0; i < SeqLength(common_promise_types); i++) { const char *common_promise_type = SeqAt(common_promise_types, i); JsonArrayAppendString(promise_types, common_promise_type); } } SeqDestroy(common_promise_types); return bundle_types; }
bool ScanLastSeenQuality(LastSeenQualityCallback callback, void *ctx) { StringMap *lastseen_db = LoadDatabaseToStringMap(dbid_lastseen); if (!lastseen_db) { return false; } MapIterator it = MapIteratorInit(lastseen_db->impl); MapKeyValue *item; Seq *hostkeys = SeqNew(100, free); while ((item = MapIteratorNext(&it)) != NULL) { char *key = item->key; /* Only look for "keyhost" entries */ if (key[0] != 'k') { continue; } SeqAppend(hostkeys, xstrdup(key + 1)); } for (int i = 0; i < SeqLength(hostkeys); ++i) { const char *hostkey = SeqAt(hostkeys, i); char keyhost_key[CF_BUFSIZE]; snprintf(keyhost_key, CF_BUFSIZE, "k%s", hostkey); char *address = NULL; address = (char*)StringMapGet(lastseen_db, keyhost_key); if (!address) { Log(LOG_LEVEL_ERR, "Failed to read address for key '%s'.", hostkey); continue; } char incoming_key[CF_BUFSIZE]; snprintf(incoming_key, CF_BUFSIZE, "qi%s", hostkey); KeyHostSeen *incoming = NULL; incoming = (KeyHostSeen*)StringMapGet(lastseen_db, incoming_key); if (incoming) { if (!(*callback)(hostkey, address, true, incoming, ctx)) { break; } } char outgoing_key[CF_BUFSIZE]; snprintf(outgoing_key, CF_BUFSIZE, "qo%s", hostkey); KeyHostSeen *outgoing = NULL; outgoing = (KeyHostSeen*)StringMapGet(lastseen_db, outgoing_key); if (outgoing) { if (!(*callback)(hostkey, address, false, outgoing, ctx)) { break; } } } StringMapDestroy(lastseen_db); SeqDestroy(hostkeys); return true; }
static void ShowContext(EvalContext *ctx) { { Writer *w = NULL; if (LEGACY_OUTPUT) { w = FileWriter(stdout); WriterWriteF(w, "%s> -> Hard classes = {", VPREFIX); } else { w = StringWriter(); WriterWrite(w, "Discovered hard classes:"); } Seq *hard_contexts = SeqNew(1000, NULL); SetIterator it = EvalContextHeapIteratorHard(ctx); char *context = NULL; while ((context = SetIteratorNext(&it))) { if (!EvalContextHeapContainsNegated(ctx, context)) { SeqAppend(hard_contexts, context); } } SeqSort(hard_contexts, (SeqItemComparator)strcmp, NULL); for (size_t i = 0; i < SeqLength(hard_contexts); i++) { const char *context = SeqAt(hard_contexts, i); WriterWriteF(w, " %s", context); } if (LEGACY_OUTPUT) { WriterWrite(w, "}\n"); FileWriterDetach(w); } else { Log(LOG_LEVEL_VERBOSE, "%s", StringWriterData(w)); WriterClose(w); } SeqDestroy(hard_contexts); } { Writer *w = NULL; if (LEGACY_OUTPUT) { w = FileWriter(stdout); WriterWriteF(w, "%s> -> Additional classes = {", VPREFIX); } else { w = StringWriter(); WriterWrite(w, "Additional classes:"); } Seq *soft_contexts = SeqNew(1000, NULL); SetIterator it = EvalContextHeapIteratorSoft(ctx); char *context = NULL; while ((context = SetIteratorNext(&it))) { if (!EvalContextHeapContainsNegated(ctx, context)) { SeqAppend(soft_contexts, context); } } SeqSort(soft_contexts, (SeqItemComparator)strcmp, NULL); for (size_t i = 0; i < SeqLength(soft_contexts); i++) { const char *context = SeqAt(soft_contexts, i); WriterWriteF(w, " %s", context); } if (LEGACY_OUTPUT) { WriterWrite(w, "}\n"); FileWriterDetach(w); } else { if (SeqLength(soft_contexts) > 0) { Log(LOG_LEVEL_VERBOSE, "%s", StringWriterData(w)); } WriterClose(w); } SeqDestroy(soft_contexts); } { bool have_negated_classes = false; Writer *w = NULL; if (LEGACY_OUTPUT) { w = FileWriter(stdout); WriterWriteF(w, "%s> -> Negated classes = {", VPREFIX); } else { w = StringWriter(); WriterWrite(w, "Negated classes:"); } StringSetIterator it = EvalContextHeapIteratorNegated(ctx); const char *context = NULL; while ((context = StringSetIteratorNext(&it))) { WriterWriteF(w, " %s", context); have_negated_classes = true; } if (LEGACY_OUTPUT) { WriterWrite(w, "}\n"); FileWriterDetach(w); } else { if (have_negated_classes) { Log(LOG_LEVEL_VERBOSE, "%s", StringWriterData(w)); } WriterClose(w); } } }
ExecConfig *ExecConfigNew(bool scheduled_run, const EvalContext *ctx, const Policy *policy) { ExecConfig *exec_config = xcalloc(1, sizeof(ExecConfig)); exec_config->scheduled_run = scheduled_run; exec_config->exec_command = xstrdup(""); exec_config->agent_expireafter = 2 * 60; /* two hours */ exec_config->mail_server = xstrdup(""); exec_config->mail_from_address = xstrdup(""); exec_config->mail_to_address = xstrdup(""); exec_config->mail_subject = xstrdup(""); exec_config->mail_max_lines = 30; exec_config->mailfilter_include = SeqNew(0, &free); exec_config->mailfilter_include_regex = SeqNew(0, &RegexFree); exec_config->mailfilter_exclude = SeqNew(0, &free); exec_config->mailfilter_exclude_regex = SeqNew(0, &RegexFree); exec_config->fq_name = xstrdup(VFQNAME); exec_config->ip_address = xstrdup(VIPADDRESS); exec_config->ip_addresses = GetIpAddresses(ctx); Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_EXECUTOR); 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_executor"); DataType t; const void *value = EvalContextVariableGet(ctx, ref, &t); VarRefDestroy(ref); if (t == CF_DATA_TYPE_NONE) { ProgrammingError("Unknown attribute '%s' in control body," " should have already been stopped by the parser", cp->lval); } if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILFROM].lval) == 0) { free(exec_config->mail_from_address); exec_config->mail_from_address = xstrdup(value); Log(LOG_LEVEL_DEBUG, "mailfrom '%s'", exec_config->mail_from_address); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILTO].lval) == 0) { free(exec_config->mail_to_address); exec_config->mail_to_address = xstrdup(value); Log(LOG_LEVEL_DEBUG, "mailto '%s'", exec_config->mail_to_address); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILSUBJECT].lval) == 0) { free(exec_config->mail_subject); exec_config->mail_subject = xstrdup(value); Log(LOG_LEVEL_DEBUG, "mailsubject '%s'", exec_config->mail_subject); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_SMTPSERVER].lval) == 0) { free(exec_config->mail_server); exec_config->mail_server = xstrdup(value); Log(LOG_LEVEL_DEBUG, "smtpserver '%s'", exec_config->mail_server); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_EXECCOMMAND].lval) == 0) { free(exec_config->exec_command); exec_config->exec_command = xstrdup(value); Log(LOG_LEVEL_DEBUG, "exec_command '%s'", exec_config->exec_command); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_AGENT_EXPIREAFTER].lval) == 0) { exec_config->agent_expireafter = IntFromString(value); Log(LOG_LEVEL_DEBUG, "agent_expireafter %d", exec_config->agent_expireafter); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILMAXLINES].lval) == 0) { exec_config->mail_max_lines = IntFromString(value); Log(LOG_LEVEL_DEBUG, "maxlines %d", exec_config->mail_max_lines); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILFILTER_INCLUDE].lval) == 0) { SeqDestroy(exec_config->mailfilter_include); SeqDestroy(exec_config->mailfilter_include_regex); RlistMailFilterFill(value, &exec_config->mailfilter_include, &exec_config->mailfilter_include_regex, "include"); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILFILTER_EXCLUDE].lval) == 0) { SeqDestroy(exec_config->mailfilter_exclude); SeqDestroy(exec_config->mailfilter_exclude_regex); RlistMailFilterFill(value, &exec_config->mailfilter_exclude, &exec_config->mailfilter_exclude_regex, "exclude"); } } } return exec_config; }