int VerifyMount(EvalContext *ctx, char *name, Attributes a, Promise *pp) { char comm[CF_BUFSIZE], line[CF_BUFSIZE]; FILE *pfp; char *host, *rmountpt, *mountpt, *opts=NULL; host = a.mount.mount_server; rmountpt = a.mount.mount_source; mountpt = name; /* Check for options required for this mount - i.e., -o ro,rsize, etc. */ if (a.mount.mount_options) { opts = Rlist2String(a.mount.mount_options, ","); } else { opts = xstrdup(VMOUNTOPTS[VSYSTEMHARDCLASS]); } if (!DONTDO) { snprintf(comm, CF_BUFSIZE, "%s -o %s %s:%s %s", CommandArg0(VMOUNTCOMM[VSYSTEMHARDCLASS]), opts, host, rmountpt, mountpt); if ((pfp = cf_popen(comm, "r", true)) == NULL) { Log(LOG_LEVEL_ERR, "Failed to open pipe from %s", CommandArg0(VMOUNTCOMM[VSYSTEMHARDCLASS])); return 0; } ssize_t res = CfReadLine(line, CF_BUFSIZE, pfp); if (res == -1) { Log(LOG_LEVEL_ERR, "Unable to read output of mount command. (fread: %s)", GetErrorStr()); cf_pclose(pfp); return 0; } if (res != 0 && ((strstr(line, "busy")) || (strstr(line, "Busy")))) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_INTERRUPTED, pp, a, "The device under %s cannot be mounted\n", mountpt); cf_pclose(pfp); return 1; } cf_pclose(pfp); } /* Since opts is either Rlist2String or xstrdup'd, we need to always free it */ free(opts); cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Mounting %s to keep promise\n", mountpt); return 0; }
const char *RealPackageManager(const char *manager) { assert(manager); const char *pos = strchr(manager, ' '); if (strncmp(manager, "env ", 4) != 0 && (!pos || pos - manager < 4 || strncmp(pos - 4, "/env", 4) != 0)) { return CommandArg0(manager); } // Look for variable assignments. const char *last_pos; bool eq_sign_found = false; while (true) { if (eq_sign_found) { last_pos = pos + 1; } else { last_pos = pos + strspn(pos, " "); // Skip over consecutive spaces. } pos = strpbrk(last_pos, "= "); if (!pos) { break; } if (*pos == '=') { eq_sign_found = true; } else if (eq_sign_found) { eq_sign_found = false; } else { return CommandArg0(last_pos); } } // Reached the end? Weird. Must be env command with no real command. return CommandArg0(manager); }
static void VerifyProcessOp(EvalContext *ctx, Item *procdata, Attributes a, Promise *pp) { int matches = 0, do_signals = true, out_of_range, killed = 0, need_to_restart = true; Item *killlist = NULL; matches = FindPidMatches(ctx, procdata, &killlist, a, pp->promiser); /* promise based on number of matches */ if (a.process_count.min_range != CF_NOINT) /* if a range is specified */ { if ((matches < a.process_count.min_range) || (matches > a.process_count.max_range)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Process count for '%s' was out of promised range (%d found)", pp->promiser, matches); for (const Rlist *rp = a.process_count.out_of_range_define; rp != NULL; rp = rp->next) { if (!EvalContextHeapContainsSoft(ctx, rp->item)) { EvalContextHeapAddSoft(ctx, rp->item, PromiseGetNamespace(pp)); } } out_of_range = true; } else { for (const Rlist *rp = a.process_count.in_range_define; rp != NULL; rp = rp->next) { if (!EvalContextHeapContainsSoft(ctx, rp->item)) { EvalContextHeapAddSoft(ctx, rp->item, PromiseGetNamespace(pp)); } } cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Process promise for '%s' is kept", pp->promiser); out_of_range = false; } } else { out_of_range = true; } if (!out_of_range) { return; } if (a.transaction.action == cfa_warn) { do_signals = false; } else { do_signals = true; } /* signal/kill promises for existing matches */ if (do_signals && (matches > 0)) { if (a.process_stop != NULL) { if (DONTDO) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "Need to keep process-stop promise for '%s', but only a warning is promised", pp->promiser); } else { if (IsExecutable(CommandArg0(a.process_stop))) { ShellCommandReturnsZero(a.process_stop, SHELL_TYPE_NONE); } else { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_FAIL, pp, a, "Process promise to stop '%s' could not be kept because '%s' the stop operator failed", pp->promiser, a.process_stop); DeleteItemList(killlist); return; } } } killed = DoAllSignals(ctx, killlist, a, pp); } /* delegated promise to restart killed or non-existent entries */ need_to_restart = (a.restart_class != NULL) && (killed || (matches == 0)); DeleteItemList(killlist); if (!need_to_restart) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "No restart promised for %s", pp->promiser); return; } else { if (a.transaction.action == cfa_warn) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "Need to keep restart promise for '%s', but only a warning is promised", pp->promiser); } else { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Making a one-time restart promise for '%s'", pp->promiser); EvalContextHeapAddSoft(ctx, a.restart_class, PromiseGetNamespace(pp)); } } }
static PromiseResult VerifyProcessOp(EvalContext *ctx, Item *procdata, Attributes a, const Promise *pp) { bool do_signals = true; int out_of_range; int killed = 0; bool need_to_restart = true; Item *killlist = NULL; int matches = FindPidMatches(procdata, &killlist, a, pp->promiser); /* promise based on number of matches */ PromiseResult result = PROMISE_RESULT_NOOP; if (a.process_count.min_range != CF_NOINT) /* if a range is specified */ { if ((matches < a.process_count.min_range) || (matches > a.process_count.max_range)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Process count for '%s' was out of promised range (%d found)", pp->promiser, matches); result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE); for (const Rlist *rp = a.process_count.out_of_range_define; rp != NULL; rp = rp->next) { ClassRef ref = ClassRefParse(RlistScalarValue(rp)); EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=promise"); ClassRefDestroy(ref); } out_of_range = true; } else { for (const Rlist *rp = a.process_count.in_range_define; rp != NULL; rp = rp->next) { ClassRef ref = ClassRefParse(RlistScalarValue(rp)); EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=promise"); ClassRefDestroy(ref); } cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Process promise for '%s' is kept", pp->promiser); out_of_range = false; } } else { out_of_range = true; } if (!out_of_range) { DeleteItemList(killlist); return result; } if (a.transaction.action == cfa_warn) { do_signals = false; result = PromiseResultUpdate(result, PROMISE_RESULT_WARN); } else { do_signals = true; } /* signal/kill promises for existing matches */ if (do_signals && (matches > 0)) { if (a.process_stop != NULL) { if (DONTDO) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "Need to keep process-stop promise for '%s', but only a warning is promised", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_WARN); } else { if (IsExecutable(CommandArg0(a.process_stop))) { ShellCommandReturnsZero(a.process_stop, SHELL_TYPE_NONE); } else { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Process promise to stop '%s' could not be kept because '%s' the stop operator failed", pp->promiser, a.process_stop); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); DeleteItemList(killlist); return result; } } } killed = DoAllSignals(ctx, killlist, a, pp, &result); } /* delegated promise to restart killed or non-existent entries */ need_to_restart = (a.restart_class != NULL) && (killed || (matches == 0)); DeleteItemList(killlist); if (!need_to_restart) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "No restart promised for %s", pp->promiser); return result; } else { if (a.transaction.action == cfa_warn) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "Need to keep restart promise for '%s', but only a warning is promised", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_WARN); } else { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Making a one-time restart promise for '%s'", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE); EvalContextClassPutSoft(ctx, a.restart_class, CONTEXT_SCOPE_NAMESPACE, "source=promise"); } } return result; }
static ActionResult RepairExec(EvalContext *ctx, Attributes a, Promise *pp) { char line[CF_BUFSIZE], eventname[CF_BUFSIZE]; char cmdline[CF_BUFSIZE]; char comm[20]; int outsourced, count = 0; #if !defined(__MINGW32__) mode_t maskval = 0; #endif FILE *pfp; char cmdOutBuf[CF_BUFSIZE]; int cmdOutBufPos = 0; int lineOutLen; if (IsAbsoluteFileName(CommandArg0(pp->promiser)) || a.contain.shelltype == SHELL_TYPE_NONE) { if (!IsExecutable(CommandArg0(pp->promiser))) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "'%s' promises to be executable but isn't", pp->promiser); if (strchr(pp->promiser, ' ')) { Log(LOG_LEVEL_VERBOSE, "Paths with spaces must be inside escaped quoutes (e.g. \\\"%s\\\")", pp->promiser); } return ACTION_RESULT_FAILED; } else { Log(LOG_LEVEL_VERBOSE, "Promiser string contains a valid executable '%s' - ok", CommandArg0(pp->promiser)); } } char timeout_str[CF_BUFSIZE]; if (a.contain.timeout == CF_NOINT) { snprintf(timeout_str, CF_BUFSIZE, "no timeout"); } else { snprintf(timeout_str, CF_BUFSIZE, "timeout=%ds", a.contain.timeout); } char owner_str[CF_BUFSIZE] = ""; if (a.contain.owner != -1) { snprintf(owner_str, CF_BUFSIZE, ",uid=%ju", (uintmax_t)a.contain.owner); } char group_str[CF_BUFSIZE] = ""; if (a.contain.group != -1) { snprintf(group_str, CF_BUFSIZE, ",gid=%ju", (uintmax_t)a.contain.group); } snprintf(cmdline, CF_BUFSIZE, "%s%s%s", pp->promiser, a.args ? " " : "", a.args ? a.args : ""); Log(LOG_LEVEL_INFO, "Executing '%s%s%s' ... '%s'", timeout_str, owner_str, group_str, cmdline); BeginMeasure(); if (DONTDO && (!a.contain.preview)) { Log(LOG_LEVEL_ERR, "Would execute script '%s'", cmdline); return ACTION_RESULT_OK; } if (a.transaction.action != cfa_fix) { Log(LOG_LEVEL_ERR, "Command '%s' needs to be executed, but only warning was promised", cmdline); return ACTION_RESULT_OK; } CommandPrefix(cmdline, comm); if (a.transaction.background) { #ifdef __MINGW32__ outsourced = true; #else Log(LOG_LEVEL_VERBOSE, "Backgrounding job '%s'", cmdline); outsourced = fork(); #endif } else { outsourced = false; } if (outsourced || (!a.transaction.background)) // work done here: either by child or non-background parent { if (a.contain.timeout != CF_NOINT) { SetTimeOut(a.contain.timeout); } #ifndef __MINGW32__ Log(LOG_LEVEL_VERBOSE, "(Setting umask to %jo)", (uintmax_t)a.contain.umask); maskval = umask(a.contain.umask); if (a.contain.umask == 0) { Log(LOG_LEVEL_VERBOSE, "Programming '%s' running with umask 0! Use umask= to set", cmdline); } #endif /* !__MINGW32__ */ if (a.contain.shelltype == SHELL_TYPE_POWERSHELL) { #ifdef __MINGW32__ pfp = cf_popen_powershell_setuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); #else // !__MINGW32__ Log(LOG_LEVEL_ERR, "Powershell is only supported on Windows"); return ACTION_RESULT_FAILED; #endif // !__MINGW32__ } else if (a.contain.shelltype == SHELL_TYPE_USE) { pfp = cf_popen_shsetuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); } else { pfp = cf_popensetuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); } if (pfp == NULL) { Log(LOG_LEVEL_ERR, "Couldn't open pipe to command '%s'. (cf_popen: %s)", cmdline, GetErrorStr()); return ACTION_RESULT_FAILED; } for (;;) { ssize_t res = CfReadLine(line, CF_BUFSIZE, pfp); if (res == 0) { break; } if (res == -1) { Log(LOG_LEVEL_ERR, "Unable to read output from command '%s'. (fread: %s)", cmdline, GetErrorStr()); cf_pclose(pfp); return ACTION_RESULT_FAILED; } if (strstr(line, "cfengine-die")) { break; } if (a.contain.preview) { PreviewProtocolLine(line, cmdline); } if (a.module) { ModuleProtocol(ctx, cmdline, line, !a.contain.nooutput, PromiseGetNamespace(pp)); } else if ((!a.contain.nooutput) && (!EmptyString(line))) { lineOutLen = strlen(comm) + strlen(line) + 12; // if buffer is to small for this line, output it directly if (lineOutLen > sizeof(cmdOutBuf)) { Log(LOG_LEVEL_NOTICE, "Q: '%s': %s", comm, line); } else { if (cmdOutBufPos + lineOutLen > sizeof(cmdOutBuf)) { Log(LOG_LEVEL_NOTICE, "%s", cmdOutBuf); cmdOutBufPos = 0; } sprintf(cmdOutBuf + cmdOutBufPos, "Q: \"...%s\": %s\n", comm, line); cmdOutBufPos += (lineOutLen - 1); } count++; } } #ifdef __MINGW32__ if (outsourced) // only get return value if we waited for command execution { cf_pclose(pfp); } else #endif /* __MINGW32__ */ { int ret = cf_pclose(pfp); if (ret == -1) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Finished script '%s' - failed (abnormal termination)", pp->promiser); } else { VerifyCommandRetcode(ctx, ret, true, a, pp); } } } if (count) { if (cmdOutBufPos) { Log(LOG_LEVEL_NOTICE, "%s", cmdOutBuf); } Log(LOG_LEVEL_INFO, "Last %d quoted lines were generated by promiser '%s'", count, cmdline); } if (a.contain.timeout != CF_NOINT) { alarm(0); signal(SIGALRM, SIG_DFL); } Log(LOG_LEVEL_INFO, "Completed execution of '%s'", cmdline); #ifndef __MINGW32__ umask(maskval); #endif snprintf(eventname, CF_BUFSIZE - 1, "Exec(%s)", cmdline); #ifndef __MINGW32__ if ((a.transaction.background) && outsourced) { Log(LOG_LEVEL_VERBOSE, "Backgrounded command '%s' is done - exiting", cmdline); exit(0); } #endif /* !__MINGW32__ */ return ACTION_RESULT_OK; }
int BusyWithClassicConnection(EvalContext *ctx, ServerConnectionState *conn) { time_t tloc, trem = 0; char recvbuffer[CF_BUFSIZE + CF_BUFEXT], check[CF_BUFSIZE]; char sendbuffer[CF_BUFSIZE] = { 0 }; char filename[CF_BUFSIZE], buffer[CF_BUFSIZE], args[CF_BUFSIZE], out[CF_BUFSIZE]; long time_no_see = 0; unsigned int len = 0; int drift, plainlen, received, encrypted = 0; ServerFileGetState get_args; Item *classes; memset(recvbuffer, 0, CF_BUFSIZE + CF_BUFEXT); memset(&get_args, 0, sizeof(get_args)); received = ReceiveTransaction(conn->conn_info, recvbuffer, NULL); if (received == -1 || received == 0) { return false; } if (strlen(recvbuffer) == 0) { Log(LOG_LEVEL_WARNING, "Got NULL transmission, skipping!"); return true; } /* Don't process request if we're signalled to exit. */ if (IsPendingTermination()) { return false; } ProtocolCommandClassic command = GetCommandClassic(recvbuffer); switch (command) { /* Plain text authentication; this MUST be the first command client using classic protocol is sending. */ case PROTOCOL_COMMAND_AUTH_PLAIN: SetConnectionData(conn, (char *) (recvbuffer + strlen("CAUTH "))); if (conn->username == NULL || IsUserNameValid(conn->username) == false) { Log(LOG_LEVEL_INFO, "Client is sending wrong username: '******'", conn->username); RefuseAccess(conn, recvbuffer); return false; } /* This is used only for forcing correct state of state machine while connecting and authenticating user using classic protocol. */ conn->user_data_set = true; return true; /* This MUST be exactly second command client using classic protocol is sending. This is where key agreement takes place. */ case PROTOCOL_COMMAND_AUTH_SECURE: /* First command was ommited by client; this is protocol violation. */ if (!conn->user_data_set) { Log(LOG_LEVEL_INFO, "Client is not verified; rejecting connection"); RefuseAccess(conn, recvbuffer); return false; } conn->rsa_auth = AuthenticationDialogue(conn, recvbuffer, received); if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Auth dialogue error"); RefuseAccess(conn, recvbuffer); return false; } return true; default: break; } /* At this point we should have both user_data_set and rsa_auth set to perform any operation. We can check only for second one as without first it won't be set up. */ if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Server refusal due to no RSA authentication [command: %d]", command); RefuseAccess(conn, recvbuffer); return false; } /* We have to have key at this point. */ assert(conn->session_key); /* At this point we can safely do next switch and make sure user is authenticated. */ switch (command) { case PROTOCOL_COMMAND_EXEC: memset(args, 0, CF_BUFSIZE); sscanf(recvbuffer, "EXEC %255[^\n]", args); if (!AllowedUser(conn->username)) { Log(LOG_LEVEL_INFO, "Server refusal due to non-allowed user"); RefuseAccess(conn, recvbuffer); return false; } if (!AccessControl(ctx, CommandArg0(CFRUNCOMMAND), conn, false)) { Log(LOG_LEVEL_INFO, "Server refusal due to denied access to requested object"); RefuseAccess(conn, recvbuffer); return false; } if (!MatchClasses(ctx, conn)) { Log(LOG_LEVEL_INFO, "Server refusal due to failed class/context match"); Terminate(conn->conn_info); return false; } DoExec(ctx, conn, args); Terminate(conn->conn_info); return false; case PROTOCOL_COMMAND_VERSION: snprintf(sendbuffer, sizeof(sendbuffer), "OK: %s", Version()); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); return conn->user_data_set; case PROTOCOL_COMMAND_GET: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "GET %d %[^\n]", &(get_args.buf_size), filename); if ((get_args.buf_size < 0) || (get_args.buf_size > CF_BUFSIZE)) { Log(LOG_LEVEL_INFO, "GET buffer out of bounds"); RefuseAccess(conn, recvbuffer); return false; } if (!AccessControl(ctx, filename, conn, false)) { Log(LOG_LEVEL_INFO, "Access denied to get object"); RefuseAccess(conn, recvbuffer); return true; } memset(sendbuffer, 0, sizeof(sendbuffer)); if (get_args.buf_size >= CF_BUFSIZE) { get_args.buf_size = 2048; } get_args.connect = conn; get_args.encrypt = false; get_args.replybuff = sendbuffer; get_args.replyfile = filename; CfGetFile(&get_args); return true; case PROTOCOL_COMMAND_GET_SECURE: memset(buffer, 0, CF_BUFSIZE); sscanf(recvbuffer, "SGET %u %d", &len, &(get_args.buf_size)); if (received != len + CF_PROTO_OFFSET) { Log(LOG_LEVEL_VERBOSE, "Protocol error SGET"); RefuseAccess(conn, recvbuffer); return false; } plainlen = DecryptString(conn->encryption_type, recvbuffer + CF_PROTO_OFFSET, buffer, conn->session_key, len); cfscanf(buffer, strlen("GET"), strlen("dummykey"), check, sendbuffer, filename); if (strcmp(check, "GET") != 0) { Log(LOG_LEVEL_INFO, "SGET/GET problem"); RefuseAccess(conn, recvbuffer); return true; } if ((get_args.buf_size < 0) || (get_args.buf_size > 8192)) { Log(LOG_LEVEL_INFO, "SGET bounding error"); RefuseAccess(conn, recvbuffer); return false; } if (get_args.buf_size >= CF_BUFSIZE) { get_args.buf_size = 2048; } Log(LOG_LEVEL_DEBUG, "Confirm decryption, and thus validity of caller"); Log(LOG_LEVEL_DEBUG, "SGET '%s' with blocksize %d", filename, get_args.buf_size); if (!AccessControl(ctx, filename, conn, true)) { Log(LOG_LEVEL_INFO, "Access control error"); RefuseAccess(conn, recvbuffer); return false; } memset(sendbuffer, 0, sizeof(sendbuffer)); get_args.connect = conn; get_args.encrypt = true; get_args.replybuff = sendbuffer; get_args.replyfile = filename; CfEncryptGetFile(&get_args); return true; case PROTOCOL_COMMAND_OPENDIR_SECURE: memset(buffer, 0, CF_BUFSIZE); sscanf(recvbuffer, "SOPENDIR %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_VERBOSE, "Protocol error OPENDIR: %d", len); RefuseAccess(conn, recvbuffer); return false; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "OPENDIR", 7) != 0) { Log(LOG_LEVEL_INFO, "Opendir failed to decrypt"); RefuseAccess(conn, recvbuffer); return true; } memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "OPENDIR %[^\n]", filename); if (!AccessControl(ctx, filename, conn, true)) /* opendir don't care about privacy */ { Log(LOG_LEVEL_INFO, "Access error"); RefuseAccess(conn, recvbuffer); return false; } CfSecOpenDirectory(conn, sendbuffer, filename); return true; case PROTOCOL_COMMAND_OPENDIR: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "OPENDIR %[^\n]", filename); if (!AccessControl(ctx, filename, conn, true)) /* opendir don't care about privacy */ { Log(LOG_LEVEL_INFO, "DIR access error"); RefuseAccess(conn, recvbuffer); return false; } CfOpenDirectory(conn, sendbuffer, filename); return true; case PROTOCOL_COMMAND_SYNC_SECURE: memset(buffer, 0, CF_BUFSIZE); sscanf(recvbuffer, "SSYNCH %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_VERBOSE, "Protocol error SSYNCH: %d", len); RefuseAccess(conn, recvbuffer); return false; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (plainlen < 0) { DebugBinOut((char *) conn->session_key, 32, "Session key"); Log(LOG_LEVEL_ERR, "Bad decrypt (%d)", len); } if (strncmp(recvbuffer, "SYNCH", 5) != 0) { Log(LOG_LEVEL_INFO, "No synch"); RefuseAccess(conn, recvbuffer); return true; } /* roll through, no break */ case PROTOCOL_COMMAND_SYNC: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]", &time_no_see, filename); trem = (time_t) time_no_see; if ((time_no_see == 0) || (filename[0] == '\0')) { break; } if ((tloc = time((time_t *) NULL)) == -1) { Log(LOG_LEVEL_INFO, "Couldn't read system clock. (time: %s)", GetErrorStr()); SendTransaction(conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE); return true; } drift = (int) (tloc - trem); if (!AccessControl(ctx, filename, conn, true)) { Log(LOG_LEVEL_INFO, "Access control in sync"); RefuseAccess(conn, recvbuffer); return true; } if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT)) { snprintf(sendbuffer, sizeof(sendbuffer), "BAD: Clocks are too far unsynchronized %ld/%ld", (long) tloc, (long) trem); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); return true; } else { Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld", (long) tloc - (long) trem); StatFile(conn, sendbuffer, filename); } return true; case PROTOCOL_COMMAND_MD5_SECURE: sscanf(recvbuffer, "SMD5 %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decryption error"); RefuseAccess(conn, recvbuffer); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "MD5", 3) != 0) { Log(LOG_LEVEL_INFO, "MD5 protocol error"); RefuseAccess(conn, recvbuffer); return false; } /* roll through, no break */ case PROTOCOL_COMMAND_MD5: CompareLocalHash(conn, sendbuffer, recvbuffer); return true; case PROTOCOL_COMMAND_VAR_SECURE: sscanf(recvbuffer, "SVAR %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error SVAR"); RefuseAccess(conn, "decrypt error SVAR"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); encrypted = true; if (strncmp(recvbuffer, "VAR", 3) != 0) { Log(LOG_LEVEL_INFO, "VAR protocol defect"); RefuseAccess(conn, "decryption failure"); return false; } /* roll through, no break */ case PROTOCOL_COMMAND_VAR: if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted)) { Log(LOG_LEVEL_INFO, "Literal access failure"); RefuseAccess(conn, recvbuffer); return false; } GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted); return true; case PROTOCOL_COMMAND_CONTEXT_SECURE: sscanf(recvbuffer, "SCONTEXT %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error SCONTEXT, len,received = %d,%d", len, received); RefuseAccess(conn, "decrypt error SCONTEXT"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); encrypted = true; if (strncmp(recvbuffer, "CONTEXT", 7) != 0) { Log(LOG_LEVEL_INFO, "CONTEXT protocol defect..."); RefuseAccess(conn, "Decryption failed?"); return false; } /* roll through, no break */ case PROTOCOL_COMMAND_CONTEXT: if ((classes = ContextAccessControl(ctx, recvbuffer, conn, encrypted)) == NULL) { Log(LOG_LEVEL_INFO, "Context access failure on %s", recvbuffer); RefuseAccess(conn, recvbuffer); return false; } ReplyServerContext(conn, encrypted, classes); return true; case PROTOCOL_COMMAND_QUERY_SECURE: sscanf(recvbuffer, "SQUERY %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error SQUERY"); RefuseAccess(conn, "decrypt error SQUERY"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "QUERY", 5) != 0) { Log(LOG_LEVEL_INFO, "QUERY protocol defect"); RefuseAccess(conn, "decryption failure"); return false; } if (!LiteralAccessControl(ctx, recvbuffer, conn, true)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, recvbuffer); return false; } if (GetServerQuery(conn, recvbuffer, true)) /* always encrypt */ { return true; } break; case PROTOCOL_COMMAND_CALL_ME_BACK: sscanf(recvbuffer, "SCALLBACK %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error CALL_ME_BACK"); RefuseAccess(conn, "decrypt error CALL_ME_BACK"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "CALL_ME_BACK collect_calls", strlen("CALL_ME_BACK collect_calls")) != 0) { Log(LOG_LEVEL_INFO, "CALL_ME_BACK protocol defect"); RefuseAccess(conn, "decryption failure"); return false; } if (!LiteralAccessControl(ctx, recvbuffer, conn, true)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, recvbuffer); return false; } if (ReceiveCollectCall(conn)) { return true; } case PROTOCOL_COMMAND_AUTH_PLAIN: case PROTOCOL_COMMAND_AUTH_SECURE: case PROTOCOL_COMMAND_AUTH: case PROTOCOL_COMMAND_CONTEXTS: case PROTOCOL_COMMAND_BAD: Log(LOG_LEVEL_WARNING, "Unexpected protocol command"); } strcpy(sendbuffer, "BAD: Request denied"); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); Log(LOG_LEVEL_INFO, "Closing connection, due to request: '%s'", recvbuffer); return false; }
PromiseResult VerifyMount(EvalContext *ctx, char *name, const Attributes *a, const Promise *pp) { char comm[CF_BUFSIZE]; FILE *pfp; char *host, *rmountpt, *mountpt, *opts=NULL; host = a->mount.mount_server; rmountpt = a->mount.mount_source; mountpt = name; /* Check for options required for this mount - i.e., -o ro,rsize, etc. */ if (a->mount.mount_options) { opts = Rlist2String(a->mount.mount_options, ","); } else { opts = xstrdup(VMOUNTOPTS[VSYSTEMHARDCLASS]); } PromiseResult result = PROMISE_RESULT_NOOP; if (!DONTDO) { snprintf(comm, CF_BUFSIZE, "%s -o %s %s:%s %s", CommandArg0(VMOUNTCOMM[VSYSTEMHARDCLASS]), opts, host, rmountpt, mountpt); if ((pfp = cf_popen(comm, "r", true)) == NULL) { Log(LOG_LEVEL_ERR, "Failed to open pipe from '%s'", CommandArg0(VMOUNTCOMM[VSYSTEMHARDCLASS])); return PROMISE_RESULT_FAIL; } size_t line_size = CF_BUFSIZE; char *line = xmalloc(line_size); ssize_t res = CfReadLine(&line, &line_size, pfp); if (res == -1) { if (!feof(pfp)) { Log(LOG_LEVEL_ERR, "Unable to read output of mount command. (fread: %s)", GetErrorStr()); cf_pclose(pfp); free(line); return PROMISE_RESULT_FAIL; } } else if ((strstr(line, "busy")) || (strstr(line, "Busy"))) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_INTERRUPTED, pp, a, "The device under '%s' cannot be mounted", mountpt); result = PromiseResultUpdate(result, PROMISE_RESULT_INTERRUPTED); cf_pclose(pfp); free(line); return 1; } free(line); cf_pclose(pfp); } /* Since opts is either Rlist2String or xstrdup'd, we need to always free it */ free(opts); cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Mounting '%s' to keep promise", mountpt); result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE); return result; }
static void test_command_promiser(void **state) { char *t1 = "/bin/echo"; assert_string_equal(CommandArg0(t1), "/bin/echo"); char *t2 = "/bin/rpm -qa --queryformat \"i | repos | %{name} | %{version}-%{release} | %{arch}\n\""; assert_string_equal(CommandArg0(t2), "/bin/rpm"); char *t3 = "/bin/mount -va"; assert_string_equal(CommandArg0(t3), "/bin/mount"); char *t4 = "\"/bin/echo\""; assert_string_equal(CommandArg0(t4), "/bin/echo"); char *t5 = "\"/bin/echo\" 123"; assert_string_equal(CommandArg0(t5), "/bin/echo"); char *t6 = "\"/bin/echo with space\" 123"; assert_string_equal(CommandArg0(t6), "/bin/echo with space"); char *t7 = "c:\\Windows\\System32\\cmd.exe"; assert_string_equal(CommandArg0(t7), "c:\\Windows\\System32\\cmd.exe"); char *t8 = "\"c:\\Windows\\System32\\cmd.exe\""; assert_string_equal(CommandArg0(t8), "c:\\Windows\\System32\\cmd.exe"); char *t9 = "\"c:\\Windows\\System32\\cmd.exe\" /some args here"; assert_string_equal(CommandArg0(t9), "c:\\Windows\\System32\\cmd.exe"); char *t10 = "\"c:\\Windows\\System32 with space\\cmd.exe\""; assert_string_equal(CommandArg0(t10), "c:\\Windows\\System32 with space\\cmd.exe"); char *t11 = "\"c:\\Windows\\System32 with space\\cmd.exe\" /some args here"; assert_string_equal(CommandArg0(t11), "c:\\Windows\\System32 with space\\cmd.exe"); char *t12 = "\"c:\\Windows\\System32 with space\\cmd.exe\" /some \"args here\""; assert_string_equal(CommandArg0(t12), "c:\\Windows\\System32 with space\\cmd.exe"); char *t13 = "\\\\mycommand"; assert_string_equal(CommandArg0(t13), "\\\\mycommand"); char *t14 = "\\\\myhost\\share\\command.exe"; assert_string_equal(CommandArg0(t14), "\\\\myhost\\share\\command.exe"); char *t15 = "\"\\\\myhost\\share\\command.exe\""; assert_string_equal(CommandArg0(t15), "\\\\myhost\\share\\command.exe"); /* bad input */ char *b1 = "\"/bin/echo 123"; assert_string_equal(CommandArg0(b1), "/bin/echo 123"); char *b2 = "/bin/echo\" 123"; assert_string_equal(CommandArg0(b2), "/bin/echo\""); char *b3 = ""; assert_string_equal(CommandArg0(b3), ""); }
bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) { time_t tloc, trem = 0; char recvbuffer[CF_BUFSIZE + CF_BUFEXT], sendbuffer[CF_BUFSIZE]; char filename[CF_BUFSIZE], args[CF_BUFSIZE], out[CF_BUFSIZE]; long time_no_see = 0; unsigned int len = 0; int drift, received; ServerFileGetState get_args; Item *classes; /* We never double encrypt within the TLS layer */ const int encrypted = 0; memset(recvbuffer, 0, CF_BUFSIZE + CF_BUFEXT); memset(&get_args, 0, sizeof(get_args)); received = ReceiveTransaction(&conn->conn_info, recvbuffer, NULL); if (received == -1 || received == 0) { return false; } if (strlen(recvbuffer) == 0) { Log(LOG_LEVEL_WARNING, "Got NULL transmission, skipping!"); return true; } /* Don't process request if we're signalled to exit. */ if (IsPendingTermination()) { return false; } switch (GetCommandNew(recvbuffer)) { case PROTOCOL_COMMAND_EXEC: memset(args, 0, CF_BUFSIZE); sscanf(recvbuffer, "EXEC %255[^\n]", args); if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "Server refusal due to incorrect identity"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AllowedUser(conn->username)) { Log(LOG_LEVEL_INFO, "Server refusal due to non-allowed user"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Server refusal due to no RSA authentication"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AccessControl(ctx, CommandArg0(CFRUNCOMMAND), conn, false)) { Log(LOG_LEVEL_INFO, "Server refusal due to denied access to requested object"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!MatchClasses(ctx, conn)) { Log(LOG_LEVEL_INFO, "Server refusal due to failed class/context match"); Terminate(&conn->conn_info); return false; } DoExec(ctx, conn, args); Terminate(&conn->conn_info); return false; case PROTOCOL_COMMAND_VERSION: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); } snprintf(conn->output, CF_BUFSIZE, "OK: %s", Version()); SendTransaction(&conn->conn_info, conn->output, 0, CF_DONE); return conn->id_verified; case PROTOCOL_COMMAND_GET: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "GET %d %[^\n]", &(get_args.buf_size), filename); if ((get_args.buf_size < 0) || (get_args.buf_size > CF_BUFSIZE)) { Log(LOG_LEVEL_INFO, "GET buffer out of bounds"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AccessControl(ctx, filename, conn, false)) { Log(LOG_LEVEL_INFO, "Access denied to get object"); RefuseAccess(conn, 0, recvbuffer); return true; } memset(sendbuffer, 0, CF_BUFSIZE); if (get_args.buf_size >= CF_BUFSIZE) { get_args.buf_size = 2048; } get_args.connect = conn; get_args.encrypt = false; get_args.replybuff = sendbuffer; get_args.replyfile = filename; CfGetFile(&get_args); return true; case PROTOCOL_COMMAND_OPENDIR: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "OPENDIR %[^\n]", filename); if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AccessControl(ctx, filename, conn, true)) /* opendir don't care about privacy */ { Log(LOG_LEVEL_INFO, "DIR access error"); RefuseAccess(conn, 0, recvbuffer); return false; } CfOpenDirectory(conn, sendbuffer, filename); return true; case PROTOCOL_COMMAND_SYNC: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return false; } memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]", &time_no_see, filename); trem = (time_t) time_no_see; if ((time_no_see == 0) || (filename[0] == '\0')) { break; } if ((tloc = time((time_t *) NULL)) == -1) { sprintf(conn->output, "Couldn't read system clock\n"); Log(LOG_LEVEL_INFO, "Couldn't read system clock. (time: %s)", GetErrorStr()); SendTransaction(&conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE); return true; } drift = (int) (tloc - trem); if (!AccessControl(ctx, filename, conn, true)) { Log(LOG_LEVEL_VERBOSE, "AccessControl: access denied"); RefuseAccess(conn, 0, recvbuffer); return true; } if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT)) { snprintf(conn->output, CF_BUFSIZE - 1, "BAD: Clocks are too far unsynchronized %ld/%ld\n", (long) tloc, (long) trem); SendTransaction(&conn->conn_info, conn->output, 0, CF_DONE); return true; } else { Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld", (long) tloc - (long) trem); StatFile(conn, sendbuffer, filename); } return true; case PROTOCOL_COMMAND_MD5: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } CompareLocalHash(conn, sendbuffer, recvbuffer); return true; case PROTOCOL_COMMAND_VAR: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted)) { Log(LOG_LEVEL_INFO, "Literal access failure"); RefuseAccess(conn, 0, recvbuffer); return false; } GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted); return true; case PROTOCOL_COMMAND_CONTEXT: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, "Context probe"); return true; } if ((classes = ContextAccessControl(ctx, recvbuffer, conn, encrypted)) == NULL) { Log(LOG_LEVEL_INFO, "Context access failure on %s", recvbuffer); RefuseAccess(conn, 0, recvbuffer); return false; } ReplyServerContext(conn, encrypted, classes); return true; case PROTOCOL_COMMAND_QUERY: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, 0, recvbuffer); return false; } if (GetServerQuery(conn, recvbuffer, encrypted)) { return true; } break; case PROTOCOL_COMMAND_CALL_ME_BACK: sscanf(recvbuffer, "SCALLBACK %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error CALL_ME_BACK"); RefuseAccess(conn, 0, "decrypt error CALL_ME_BACK"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "CALL_ME_BACK collect_calls", strlen("CALL_ME_BACK collect_calls")) != 0) { Log(LOG_LEVEL_INFO, "CALL_ME_BACK protocol defect"); RefuseAccess(conn, 0, "decryption failure"); return false; } if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } if (!LiteralAccessControl(ctx, recvbuffer, conn, true)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, 0, recvbuffer); return false; } return ReceiveCollectCall(conn); case PROTOCOL_COMMAND_BAD: Log(LOG_LEVEL_WARNING, "Unexpected protocol command: %s", recvbuffer); } sprintf(sendbuffer, "BAD: Request denied\n"); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); Log(LOG_LEVEL_INFO, "Closing connection, due to request: '%s'", recvbuffer); return false; }
static Item *MonReSample(EvalContext *ctx, int slot, Attributes a, const Promise *pp, PromiseResult *result) { CfLock thislock; char eventname[CF_BUFSIZE]; char comm[20]; struct timespec start; FILE *fin = NULL; mode_t maskval = 0; if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0) { if (!IsExecutable(CommandArg0(pp->promiser))) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "%s promises to be executable but isn't\n", pp->promiser); *result = PromiseResultUpdate(*result, PROMISE_RESULT_FAIL); return NULL; } else { Log(LOG_LEVEL_VERBOSE, "Promiser string contains a valid executable (%s) - ok", CommandArg0(pp->promiser)); } } TransactionContext tc = { .expireafter = a.transaction.expireafter, .ifelapsed = MONITOR_RESTARTED ? 0 : a.transaction.ifelapsed, // Force a measurement if restarted }; CFSTARTTIME = time(NULL); thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, tc, pp, false); if (thislock.lock == NULL) { if (a.measure.history_type && (strcmp(a.measure.history_type, "log") == 0)) { DeleteItemList(ENTERPRISE_DATA[slot].output); ENTERPRISE_DATA[slot].output = NULL; } else { /* If static or time-series, and too soon or busy then use a cached value to avoid artificial gaps in the history */ } MONITOR_RESTARTED = false; return ENTERPRISE_DATA[slot].output; } else { DeleteItemList(ENTERPRISE_DATA[slot].output); ENTERPRISE_DATA[slot].output = NULL; Log(LOG_LEVEL_INFO, "Sampling \'%s\' ...(timeout=%d,owner=%ju,group=%ju)", pp->promiser, a.contain.timeout, (uintmax_t)a.contain.owner, (uintmax_t)a.contain.group); start = BeginMeasure(); CommandPrefix(pp->promiser, comm); if (a.contain.timeout != 0) { SetTimeOut(a.contain.timeout); } /* Stream types */ if (a.measure.stream_type && strcmp(a.measure.stream_type, "file") == 0) { long filepos = 0; struct stat sb; Log(LOG_LEVEL_VERBOSE, "Stream \"%s\" is a plain file", pp->promiser); if (stat(pp->promiser, &sb) == -1) { Log(LOG_LEVEL_INFO, "Unable to find stream '%s'. (stat: %s)", pp->promiser, GetErrorStr()); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; return NULL; } fin = safe_fopen(pp->promiser, "r"); if (a.measure.growing) { filepos = Mon_RestoreFilePosition(pp->promiser); if (sb.st_size >= filepos) { fseek(fin, filepos, SEEK_SET); } } } else if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0) { Log(LOG_LEVEL_VERBOSE, "(Setting pipe umask to %jo)", (uintmax_t)a.contain.umask); maskval = umask(a.contain.umask); if (a.contain.umask == 0) { Log(LOG_LEVEL_VERBOSE, "Programming %s running with umask 0! Use umask= to set", pp->promiser); } // Mark: This is strange that we used these wrappers. Currently no way of setting these a.contain.owner = -1; a.contain.group = -1; a.contain.chdir = NULL; a.contain.chroot = NULL; // Mark: they were unset, and would fail for non-root(!) if (a.contain.shelltype == SHELL_TYPE_POWERSHELL) { #ifdef __MINGW32__ fin = cf_popen_powershell_setuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, false); #else // !__MINGW32__ Log(LOG_LEVEL_ERR, "Powershell is only supported on Windows"); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; return NULL; #endif // !__MINGW32__ } else if (a.contain.shelltype == SHELL_TYPE_USE) { fin = cf_popen_shsetuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, false); } else { fin = cf_popensetuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, false); } } /* generic file stream */ if (fin == NULL) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Couldn't open pipe to command '%s'. (cf_popen: %s)", pp->promiser, GetErrorStr()); *result = PromiseResultUpdate(*result, PROMISE_RESULT_FAIL); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; return ENTERPRISE_DATA[slot].output; } size_t line_size = CF_BUFSIZE; char *line = xmalloc(line_size); for (;;) { ssize_t res = CfReadLine(&line, &line_size, fin); if (res == -1) { if (!feof(fin)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_TIMEOUT, pp, a, "Sample stream '%s'. (fread: %s)", pp->promiser, GetErrorStr()); *result = PromiseResultUpdate(*result, PROMISE_RESULT_TIMEOUT); YieldCurrentLock(thislock); free(line); return ENTERPRISE_DATA[slot].output; } else { break; } } AppendItem(&(ENTERPRISE_DATA[slot].output), line, NULL); } free(line); if (a.measure.stream_type && strcmp(a.measure.stream_type, "file") == 0) { long fileptr = ftell(fin); fclose(fin); Mon_SaveFilePosition(pp->promiser, fileptr); } else if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0) { cf_pclose(fin); } } if (a.contain.timeout != 0) { alarm(0); signal(SIGALRM, SIG_DFL); } Log(LOG_LEVEL_INFO, "Collected sample of %s", pp->promiser); umask(maskval); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; snprintf(eventname, CF_BUFSIZE - 1, "Sample(%s)", pp->promiser); EndMeasure(eventname, start); return ENTERPRISE_DATA[slot].output; } /************************************************************************************/ void HistoryUpdate(EvalContext *ctx, Averages newvals) { CfLock thislock; time_t now = time(NULL); /* We do this only once per hour - this should not be changed */ Log(LOG_LEVEL_VERBOSE, "(Updating long-term history database)"); Policy *history_db_policy = PolicyNew(); Promise *pp = NULL; Bundle *bp = PolicyAppendBundle(history_db_policy, NamespaceDefault(), "history_db_bundle", "agent", NULL, NULL); PromiseType *tp = BundleAppendPromiseType(bp, "history_db"); pp = PromiseTypeAppendPromise(tp, "the long term memory", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL); assert(pp); TransactionContext tc = { .expireafter = 0, .ifelapsed = 59 }; thislock = AcquireLock(ctx, pp->promiser, VUQNAME, now, tc, pp, false); if (thislock.lock == NULL) { PolicyDestroy(history_db_policy); return; } /* Refresh the class context of the agent */ EvalContextClear(ctx); DetectEnvironment(ctx); time_t t = SetReferenceTime(); UpdateTimeClasses(ctx, t); EvalContextHeapPersistentLoadAll(ctx); LoadSystemConstants(ctx); YieldCurrentLock(thislock); PolicyDestroy(history_db_policy); Mon_HistoryUpdate(CFSTARTTIME, &newvals); Mon_DumpSlowlyVaryingObservations(); } /************************************************************************************/ static Item *MonGetMeasurementStream(EvalContext *ctx, Attributes a, const Promise *pp, PromiseResult *result) { int i; for (i = 0; i < CF_DUNBAR_WORK; i++) { if (ENTERPRISE_DATA[i].path == NULL) { break; } if (strcmp(ENTERPRISE_DATA[i].path, pp->promiser) == 0) { ENTERPRISE_DATA[i].output = MonReSample(ctx, i, a, pp, result); return ENTERPRISE_DATA[i].output; } } ENTERPRISE_DATA[i].path = xstrdup(pp->promiser); ENTERPRISE_DATA[i].output = MonReSample(ctx, i, a, pp, result); return ENTERPRISE_DATA[i].output; }
static void VerifyProcessOp(Item *procdata, Attributes a, Promise *pp) { int matches = 0, do_signals = true, out_of_range, killed = 0, need_to_restart = true; Item *killlist = NULL; CfDebug("VerifyProcessOp\n"); matches = FindPidMatches(procdata, &killlist, a, pp); /* promise based on number of matches */ if (a.process_count.min_range != CF_NOINT) /* if a range is specified */ { if ((matches < a.process_count.min_range) || (matches > a.process_count.max_range)) { cfPS(OUTPUT_LEVEL_VERBOSE, CF_CHG, "", pp, a, " !! Process count for \'%s\' was out of promised range (%d found)\n", pp->promiser, matches); AddEphemeralClasses(a.process_count.out_of_range_define, pp->ns); out_of_range = true; } else { AddEphemeralClasses(a.process_count.in_range_define, pp->ns); cfPS(OUTPUT_LEVEL_VERBOSE, CF_NOP, "", pp, a, " -> Process promise for %s is kept", pp->promiser); out_of_range = false; } } else { out_of_range = true; } if (!out_of_range) { return; } if (a.transaction.action == cfa_warn) { do_signals = false; } else { do_signals = true; } /* signal/kill promises for existing matches */ if (do_signals && (matches > 0)) { if (a.process_stop != NULL) { if (DONTDO) { cfPS(OUTPUT_LEVEL_ERROR, CF_WARN, "", pp, a, " -- Need to keep process-stop promise for %s, but only a warning is promised", pp->promiser); } else { if (IsExecutable(CommandArg0(a.process_stop))) { ShellCommandReturnsZero(a.process_stop, false); } else { cfPS(OUTPUT_LEVEL_VERBOSE, CF_FAIL, "", pp, a, "Process promise to stop %s could not be kept because %s the stop operator failed", pp->promiser, a.process_stop); DeleteItemList(killlist); return; } } } killed = DoAllSignals(killlist, a, pp); } /* delegated promise to restart killed or non-existent entries */ need_to_restart = (a.restart_class != NULL) && (killed || (matches == 0)); DeleteItemList(killlist); if (!need_to_restart) { cfPS(OUTPUT_LEVEL_VERBOSE, CF_NOP, "", pp, a, " -> No restart promised for %s\n", pp->promiser); return; } else { if (a.transaction.action == cfa_warn) { cfPS(OUTPUT_LEVEL_ERROR, CF_WARN, "", pp, a, " -- Need to keep restart promise for %s, but only a warning is promised", pp->promiser); } else { cfPS(OUTPUT_LEVEL_INFORM, CF_CHG, "", pp, a, " -> Making a one-time restart promise for %s", pp->promiser); NewClass(a.restart_class, pp->ns); } } }