void VerifyExecPromise(EvalContext *ctx, Promise *pp) { Attributes a = { {0} }; a = GetExecAttributes(ctx, pp); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, DATA_TYPE_STRING); if (!SyntaxCheckExec(a, pp)) { // cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, ""); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return; } if (PromiseKeptExec(a, pp)) { // cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_NOOP, pp, a, ""); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return; } char *lock_name = GetLockNameExec(a, pp); CfLock thislock = AcquireLock(ctx, lock_name, VUQNAME, CFSTARTTIME, a.transaction, pp, false); free(lock_name); if (thislock.lock == NULL) { // cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, ""); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return; } PromiseBanner(pp); switch (RepairExec(ctx, a, pp)) { case ACTION_RESULT_OK: // cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, ""); break; case ACTION_RESULT_TIMEOUT: // cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_TIMEOUT, pp, a, ""); break; case ACTION_RESULT_FAILED: // cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, ""); break; default: ProgrammingError("Unexpected ActionResult value"); } YieldCurrentLock(thislock); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); }
PromiseResult VerifyExecPromise(EvalContext *ctx, const Promise *pp) { Attributes a = GetExecAttributes(ctx, pp); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, DATA_TYPE_STRING, "source=promise"); if (!SyntaxCheckExec(a, pp)) { EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return PROMISE_RESULT_FAIL; } if (PromiseKeptExec(a, pp)) { EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return PROMISE_RESULT_NOOP; } char *lock_name = GetLockNameExec(a, pp); CfLock thislock = AcquireLock(ctx, lock_name, VUQNAME, CFSTARTTIME, a.transaction, pp, false); free(lock_name); if (thislock.lock == NULL) { EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return PROMISE_RESULT_SKIPPED; } PromiseBanner(pp); PromiseResult result = PROMISE_RESULT_NOOP; switch (RepairExec(ctx, a, pp, &result)) { case ACTION_RESULT_OK: result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE); break; case ACTION_RESULT_TIMEOUT: result = PromiseResultUpdate(result, PROMISE_RESULT_TIMEOUT); break; case ACTION_RESULT_FAILED: result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); break; default: ProgrammingError("Unexpected ActionResult value"); } YieldCurrentLock(thislock); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return result; }
static void VerifyProcesses(EvalContext *ctx, Attributes a, Promise *pp) { CfLock thislock; char lockname[CF_BUFSIZE]; if (a.restart_class) { snprintf(lockname, CF_BUFSIZE - 1, "proc-%s-%s", pp->promiser, a.restart_class); } else { snprintf(lockname, CF_BUFSIZE - 1, "proc-%s-norestart", pp->promiser); } thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return; } EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, DATA_TYPE_STRING); PromiseBanner(pp); VerifyProcessOp(ctx, PROCESSTABLE, a, pp); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); YieldCurrentLock(thislock); }
static PromiseResult VerifyServices(EvalContext *ctx, Attributes a, const Promise *pp) { CfLock thislock; thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, CF_DATA_TYPE_STRING, "source=promise"); PromiseBanner(pp); PromiseResult result = PROMISE_RESULT_NOOP; if (strcmp(a.service.service_type, "windows") == 0) { #ifdef __MINGW32__ result = PromiseResultUpdate(result, VerifyWindowsService(ctx, a, pp)); #else Log(LOG_LEVEL_INFO, "Service type windows not supported on this platform."); #endif } else { result = PromiseResultUpdate(result, DoVerifyServices(ctx, a, pp)); } EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); YieldCurrentLock(thislock); return result; }
static PromiseResult VerifyServices(EvalContext *ctx, Attributes a, Promise *pp) { CfLock thislock; thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, DATA_TYPE_STRING, "goal=state,source=promise"); PromiseBanner(pp); PromiseResult result = PROMISE_RESULT_NOOP; if (strcmp(a.service.service_type, "windows") == 0) { result = PromiseResultUpdate(result, VerifyWindowsService(ctx, a, pp)); } else { result = PromiseResultUpdate(result, DoVerifyServices(ctx, a, pp)); } EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); YieldCurrentLock(thislock); return result; }
/** * @brief Sets both internal C variables as well as policy sys variables. * * Called at bootstrap and after reading policy_server.dat. * Changes sys.policy_hub and sys.policy_hub_port. * NULL is an acceptable value for new_policy_server. Could happen when an * already bootstrapped server re-parses its policies, and the * policy_server.dat file has been removed. Then this function will be called * with NULL as new_policy_server, and cf-serverd will keep running even * without a policy server set. * * @param ctx EvalContext is used to set related variables * @param new_policy_server can be 'host:port', same as policy_server.dat */ void EvalContextSetPolicyServer(EvalContext *ctx, const char *new_policy_server) { // Remove variables if undefined policy server: if ( NULL_OR_EMPTY(new_policy_server) ) { EvalContextVariableRemoveSpecial( ctx, SPECIAL_SCOPE_SYS, "policy_hub" ); EvalContextVariableRemoveSpecial( ctx, SPECIAL_SCOPE_SYS, "policy_hub_port" ); return; } PolicyServerSet(new_policy_server); const char *ip = PolicyServerGetIP(); // Set the sys.policy_hub variable: if ( ip != NULL ) { EvalContextVariablePutSpecial( ctx, SPECIAL_SCOPE_SYS, "policy_hub", ip, CF_DATA_TYPE_STRING, "source=bootstrap" ); } else { EvalContextVariableRemoveSpecial( ctx, SPECIAL_SCOPE_SYS, "policy_hub" ); } // Set the sys.policy_hub_port variable: if (PolicyServerGetPort() != NULL) { EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "policy_hub_port", PolicyServerGetPort(), CF_DATA_TYPE_STRING, "source=bootstrap" ); } else // Default value (CFENGINE_PORT_STR = "5308") is set { EvalContextVariablePutSpecial( ctx, SPECIAL_SCOPE_SYS, "policy_hub_port", CFENGINE_PORT_STR, CF_DATA_TYPE_STRING, "source=bootstrap" ); } }
PromiseResult VerifyMethodsPromise(EvalContext *ctx, const Promise *pp) { Attributes a = GetMethodAttributes(ctx, pp); PromiseResult result = VerifyMethod(ctx, "usebundle", a, pp); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return result; }
/* NULL is an acceptable value for new_policy_server. Could happen when an * already bootstrapped server re-parses its policies, and the * policy_server.dat file has been removed. Then this function will be called * with NULL as new_policy_server, and cf-serverd will keep running even * without a policy server set." */ void SetPolicyServer(EvalContext *ctx, const char *new_policy_server) { if (new_policy_server) { snprintf(POLICY_SERVER, CF_MAX_IP_LEN, "%s", new_policy_server); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "policy_hub", new_policy_server, DATA_TYPE_STRING, "source=bootstrap"); } else { strcpy(POLICY_SERVER, ""); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_SYS, "policy_hub"); } }
static void ResolveControlBody(EvalContext *ctx, GenericAgentConfig *config, const Body *control_body) { const ConstraintSyntax *body_syntax = NULL; Rval returnval; assert(strcmp(control_body->name, "control") == 0); for (int i = 0; CONTROL_BODIES[i].constraints != NULL; i++) { body_syntax = CONTROL_BODIES[i].constraints; if (strcmp(control_body->type, CONTROL_BODIES[i].body_type) == 0) { break; } } if (body_syntax == NULL) { FatalError(ctx, "Unknown agent"); } char scope[CF_BUFSIZE]; snprintf(scope, CF_BUFSIZE, "%s_%s", control_body->name, control_body->type); Log(LOG_LEVEL_DEBUG, "Initiate control variable convergence for scope '%s'", scope); EvalContextStackPushBodyFrame(ctx, NULL, control_body, NULL); for (size_t i = 0; i < SeqLength(control_body->conlist); i++) { Constraint *cp = SeqAt(control_body->conlist, i); if (!IsDefinedClass(ctx, cp->classes)) { continue; } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_BUNDLESEQUENCE].lval) == 0) { returnval = ExpandPrivateRval(ctx, NULL, scope, cp->rval.item, cp->rval.type); } else { returnval = EvaluateFinalRval(ctx, control_body->parent_policy, NULL, scope, cp->rval, true, NULL); } VarRef *ref = VarRefParseFromScope(cp->lval, scope); EvalContextVariableRemove(ctx, ref); if (!EvalContextVariablePut(ctx, ref, returnval.item, ConstraintSyntaxGetDataType(body_syntax, cp->lval), "source=promise")) { Log(LOG_LEVEL_ERR, "Rule from %s at/before line %zu", control_body->source_path, cp->offset.line); } VarRefDestroy(ref); if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_OUTPUT_PREFIX].lval) == 0) { strncpy(VPREFIX, returnval.item, CF_MAXVARSIZE); } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_DOMAIN].lval) == 0) { strcpy(VDOMAIN, cp->rval.item); Log(LOG_LEVEL_VERBOSE, "SET domain = %s", VDOMAIN); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_SYS, "domain"); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_SYS, "fqhost"); snprintf(VFQNAME, CF_MAXVARSIZE, "%s.%s", VUQNAME, VDOMAIN); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "fqhost", VFQNAME, DATA_TYPE_STRING, "inventory,source=agent,group=Host name"); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "domain", VDOMAIN, DATA_TYPE_STRING, "source=agent"); EvalContextClassPutHard(ctx, VDOMAIN, "source=agent"); } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_IGNORE_MISSING_INPUTS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET ignore_missing_inputs %s", RvalScalarValue(cp->rval)); config->ignore_missing_inputs = BooleanFromString(cp->rval.item); } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_IGNORE_MISSING_BUNDLES].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET ignore_missing_bundles %s", RvalScalarValue(cp->rval)); config->ignore_missing_bundles = BooleanFromString(cp->rval.item); } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_CACHE_SYSTEM_FUNCTIONS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET cache_system_functions %s", RvalScalarValue(cp->rval)); bool cache_system_functions = BooleanFromString(RvalScalarValue(cp->rval)); EvalContextSetEvalOption(ctx, EVAL_OPTION_CACHE_SYSTEM_FUNCTIONS, cache_system_functions); } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_GOALPATTERNS].lval) == 0) { /* Ignored */ continue; } RvalDestroy(returnval); } EvalContextStackPopFrame(ctx); }
/** * Evaluate the relevant control body, and set the * relevant fields in #ctx and #config. */ static void ResolveControlBody(EvalContext *ctx, GenericAgentConfig *config, const Body *control_body) { const char *filename = control_body->source_path; assert(CFG_CONTROLBODY[COMMON_CONTROL_MAX].lval == NULL); const ConstraintSyntax *body_syntax = NULL; for (int i = 0; CONTROL_BODIES[i].constraints != NULL; i++) { body_syntax = CONTROL_BODIES[i].constraints; if (strcmp(control_body->type, CONTROL_BODIES[i].body_type) == 0) { break; } } if (body_syntax == NULL) { FatalError(ctx, "Unknown control body: %s", control_body->type); } char *scope; assert(strcmp(control_body->name, "control") == 0); xasprintf(&scope, "control_%s", control_body->type); Log(LOG_LEVEL_DEBUG, "Initiate control variable convergence for scope '%s'", scope); EvalContextStackPushBodyFrame(ctx, NULL, control_body, NULL); for (size_t i = 0; i < SeqLength(control_body->conlist); i++) { const char *lval; Rval evaluated_rval; size_t lineno; /* Use nested scope to constrain cp. */ { Constraint *cp = SeqAt(control_body->conlist, i); lval = cp->lval; lineno = cp->offset.line; if (!IsDefinedClass(ctx, cp->classes)) { continue; } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_BUNDLESEQUENCE].lval) == 0) { evaluated_rval = ExpandPrivateRval(ctx, NULL, scope, cp->rval.item, cp->rval.type); } else { evaluated_rval = EvaluateFinalRval(ctx, control_body->parent_policy, NULL, scope, cp->rval, true, NULL); } } /* Close scope: assert we only use evaluated_rval, not cp->rval. */ VarRef *ref = VarRefParseFromScope(lval, scope); EvalContextVariableRemove(ctx, ref); DataType rval_proper_datatype = ConstraintSyntaxGetDataType(body_syntax, lval); if (evaluated_rval.type != DataTypeToRvalType(rval_proper_datatype)) { Log(LOG_LEVEL_ERR, "Attribute '%s' in %s:%zu is of wrong type, skipping", lval, filename, lineno); VarRefDestroy(ref); RvalDestroy(evaluated_rval); continue; } bool success = EvalContextVariablePut( ctx, ref, evaluated_rval.item, rval_proper_datatype, "source=promise"); if (!success) { Log(LOG_LEVEL_ERR, "Attribute '%s' in %s:%zu can't be added, skipping", lval, filename, lineno); VarRefDestroy(ref); RvalDestroy(evaluated_rval); continue; } VarRefDestroy(ref); if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_OUTPUT_PREFIX].lval) == 0) { strlcpy(VPREFIX, RvalScalarValue(evaluated_rval), sizeof(VPREFIX)); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_DOMAIN].lval) == 0) { strlcpy(VDOMAIN, RvalScalarValue(evaluated_rval), sizeof(VDOMAIN)); Log(LOG_LEVEL_VERBOSE, "SET domain = %s", VDOMAIN); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_SYS, "domain"); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_SYS, "fqhost"); snprintf(VFQNAME, CF_MAXVARSIZE, "%s.%s", VUQNAME, VDOMAIN); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "fqhost", VFQNAME, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=Host name"); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "domain", VDOMAIN, CF_DATA_TYPE_STRING, "source=agent"); EvalContextClassPutHard(ctx, VDOMAIN, "source=agent"); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_IGNORE_MISSING_INPUTS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET ignore_missing_inputs %s", RvalScalarValue(evaluated_rval)); config->ignore_missing_inputs = BooleanFromString( RvalScalarValue(evaluated_rval)); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_IGNORE_MISSING_BUNDLES].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET ignore_missing_bundles %s", RvalScalarValue(evaluated_rval)); config->ignore_missing_bundles = BooleanFromString( RvalScalarValue(evaluated_rval)); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_CACHE_SYSTEM_FUNCTIONS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET cache_system_functions %s", RvalScalarValue(evaluated_rval)); bool cache_system_functions = BooleanFromString( RvalScalarValue(evaluated_rval)); EvalContextSetEvalOption(ctx, EVAL_OPTION_CACHE_SYSTEM_FUNCTIONS, cache_system_functions); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_PROTOCOL_VERSION].lval) == 0) { config->protocol_version = ProtocolVersionParse( RvalScalarValue(evaluated_rval)); Log(LOG_LEVEL_VERBOSE, "SET common protocol_version: %s", PROTOCOL_VERSION_STRING[config->protocol_version]); } /* Those are package_inventory and package_module common control body options */ if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_PACKAGE_INVENTORY].lval) == 0) { AddDefaultInventoryToContext(ctx, RvalRlistValue(evaluated_rval)); Log(LOG_LEVEL_VERBOSE, "SET common package_inventory list"); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_PACKAGE_MODULE].lval) == 0) { AddDefaultPackageModuleToContext(ctx, RvalScalarValue(evaluated_rval)); Log(LOG_LEVEL_VERBOSE, "SET common package_module: %s", RvalScalarValue(evaluated_rval)); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_GOALPATTERNS].lval) == 0) { /* Ignored */ } RvalDestroy(evaluated_rval); } EvalContextStackPopFrame(ctx); free(scope); }
static bool DoCreateUser(const char *puser, User u, enum cfopaction action, EvalContext *ctx, const Attributes *a, const Promise *pp) { char cmd[CF_BUFSIZE]; if (puser == NULL || !strcmp (puser, "")) { return false; } strcpy (cmd, USERADD); if (u.uid != NULL && strcmp (u.uid, "")) { StringAppend(cmd, " -u \"", sizeof(cmd)); StringAppend(cmd, u.uid, sizeof(cmd)); StringAppend(cmd, "\"", sizeof(cmd)); } if (u.description != NULL) { StringAppend(cmd, " -c \"", sizeof(cmd)); StringAppend(cmd, u.description, sizeof(cmd)); StringAppend(cmd, "\"", sizeof(cmd)); } if (u.group_primary != NULL && strcmp (u.group_primary, "")) { // TODO: Should check that group exists StringAppend(cmd, " -g \"", sizeof(cmd)); StringAppend(cmd, u.group_primary, sizeof(cmd)); StringAppend(cmd, "\"", sizeof(cmd)); } if (u.groups_secondary != NULL) { // TODO: Should check that groups exist StringAppend(cmd, " -G \"", sizeof(cmd)); char sep[2] = { '\0', '\0' }; for (Rlist *i = u.groups_secondary; i; i = i->next) { if (strcmp(RvalScalarValue(i->val), CF_NULL_VALUE) != 0) { StringAppend(cmd, sep, sizeof(cmd)); StringAppend(cmd, RvalScalarValue(i->val), sizeof(cmd)); sep[0] = ','; } } StringAppend(cmd, "\"", sizeof(cmd)); } if (u.home_dir != NULL && strcmp (u.home_dir, "")) { StringAppend(cmd, " -d \"", sizeof(cmd)); StringAppend(cmd, u.home_dir, sizeof(cmd)); StringAppend(cmd, "\"", sizeof(cmd)); } if (u.shell != NULL && strcmp (u.shell, "")) { StringAppend(cmd, " -s \"", sizeof(cmd)); StringAppend(cmd, u.shell, sizeof(cmd)); StringAppend(cmd, "\"", sizeof(cmd)); } StringAppend(cmd, " ", sizeof(cmd)); StringAppend(cmd, puser, sizeof(cmd)); if (action == cfa_warn || DONTDO) { Log(LOG_LEVEL_NOTICE, "Need to create user '%s'.", puser); return false; } else { if (strlen(cmd) >= sizeof(cmd) - 1) { // Instead of checking every string call above, assume that a maxed out // string length overflowed the string. Log(LOG_LEVEL_ERR, "Command line too long while creating user '%s'", puser); return false; } Log(LOG_LEVEL_VERBOSE, "Creating user '%s'. (command: '%s')", puser, cmd); int status; status = system(cmd); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { Log(LOG_LEVEL_ERR, "Command returned error while creating user '%s'. (Command line: '%s')", puser, cmd); return false; } // Initially, "useradd" may set the password to '!', which confuses our detection for // locked accounts. So reset it to 'x' hash instead, which will never match anything. if (!ChangePassword(puser, "x", PASSWORD_FORMAT_HASH)) { return false; } if (u.policy == USER_STATE_LOCKED) { if (!SetAccountLocked(puser, "", true)) { return false; } } if (a->havebundle) { VerifyMethod(ctx, "home_bundle", *a, pp); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); } if (u.policy != USER_STATE_LOCKED && u.password != NULL && strcmp (u.password, "")) { if (!ChangePassword(puser, u.password, u.password_format)) { return false; } } } return true; }