char *PromiseLoggingPromiseFinish(const EvalContext *eval_context, const Promise *pp) { LoggingPrivContext *pctx = LoggingPrivGetContext(); if (pctx == NULL) { ProgrammingError("Promise logging: Unable to finish promise, not bound to EvalContext"); } PromiseLoggingContext *plctx = pctx->param; if (plctx->eval_context != eval_context) { ProgrammingError("Promise logging: Unable to finish promise, bound to EvalContext different from passed one"); } if (EvalContextStackGetTopPromise(eval_context) != pp) { /* * FIXME: There are still cases where promise passed here is not on top of stack */ /* ProgrammingError("Logging: Attempt to finish promise not on top of stack"); */ } char *last_message = plctx->last_message; plctx->promise_level--; plctx->last_message = NULL; free(plctx->stack_path); LoggingPrivSetLevels(LogGetGlobalLevel(), LogGetGlobalLevel()); return last_message; }
void GenericAgentConfigApply(EvalContext *ctx, const GenericAgentConfig *config) { if (config->heap_soft) { StringSetIterator it = StringSetIteratorInit(config->heap_soft); const char *context = NULL; while ((context = StringSetIteratorNext(&it))) { if (EvalContextHeapContainsHard(ctx, context)) { FatalError(ctx, "cfengine: You cannot use -D to define a reserved class!"); } EvalContextHeapAddSoft(ctx, context, NULL); } } if (config->heap_negated) { StringSetIterator it = StringSetIteratorInit(config->heap_negated); const char *context = NULL; while ((context = StringSetIteratorNext(&it))) { if (EvalContextHeapContainsHard(ctx, context)) { FatalError(ctx, "Cannot negate the reserved class [%s]\n", context); } EvalContextHeapAddNegated(ctx, context); } } switch (LogGetGlobalLevel()) { case LOG_LEVEL_DEBUG: EvalContextHeapAddHard(ctx, "debug_mode"); EvalContextHeapAddHard(ctx, "opt_debug"); // intentional fall case LOG_LEVEL_VERBOSE: EvalContextHeapAddHard(ctx, "verbose_mode"); // intentional fall case LOG_LEVEL_INFO: EvalContextHeapAddHard(ctx, "inform_mode"); break; default: break; } if (config->agent_specific.agent.bootstrap_policy_server) { EvalContextHeapAddHard(ctx, "bootstrap_mode"); } if (config->color) { LoggingSetColor(config->color); } }
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; }
static LogLevel CalculateReportLevel(const EvalContext *ctx, const Promise *pp) { LogLevel report_level = LogGetGlobalLevel(); if (pp) { report_level = AdjustLogLevel(report_level, GetLevelForPromise(ctx, pp, "report_level")); } return report_level; }
static LogLevel CalculateLogLevel(const EvalContext *ctx, const Promise *pp) { LogLevel log_level = LogGetGlobalLevel(); if (pp) { log_level = AdjustLogLevel(log_level, GetLevelForPromise(ctx, pp, "log_level")); } /* Disable system log for dry-runs */ if (DONTDO) { log_level = LOG_LEVEL_NOTHING; } return log_level; }
static bool GetSysUsers( int *userListSz, int *numRootProcs, int *numOtherProcs) { FILE *fp; char user[CF_BUFSIZE]; char vbuff[CF_BUFSIZE]; char cbuff[CF_BUFSIZE]; /* * The best would be to ask only "user" field from ps, but we are asking * for "user,pid". The reason is that we try to mimic cf-monitord's * behaviour, else a different number of users might be detected by the * test, as printing "user,pid" truncates the user column. TODO fix the * ps command to use only "-o user" in both mon_processes.c and this test. */ #if defined(__sun) xsnprintf(cbuff, CF_BUFSIZE, "/bin/ps -eo user,pid > %s/users.txt", CFWORKDIR); #elif defined(_AIX) xsnprintf(cbuff, CF_BUFSIZE, "/bin/ps -N -eo user,pid > %s/users.txt", CFWORKDIR); #elif defined(__hpux) xsnprintf(cbuff, CF_BUFSIZE, "UNIX95=1 /bin/ps -eo user,pid > %s/users.txt", CFWORKDIR); /* SKIP on HP-UX since cf-monitord doesn't count processes correctly! */ return false; #else xsnprintf(cbuff, CF_BUFSIZE, "ps -eo user:30,pid > %s/users.txt", CFWORKDIR); #endif Item *userList = NULL; system(cbuff); xsnprintf(cbuff, CF_BUFSIZE, "%s/users.txt", CFWORKDIR); if ((fp = fopen(cbuff, "r")) == NULL) { return false; } while (fgets(vbuff, CF_BUFSIZE, fp) != NULL) { int ret = sscanf(vbuff, " %s ", user); if (ret != 1 || strcmp(user, "") == 0 || strcmp(user, "USER") == 0 || isdigit(user[0])) { continue; } if (!IsItemIn(userList, user)) { PrependItem(&userList, user, NULL); (*userListSz)++; } if (strcmp(user, "root") == 0) { (*numRootProcs)++; } else { (*numOtherProcs)++; } } fclose(fp); if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { char *s = ItemList2CSV(userList); Log(LOG_LEVEL_DEBUG, "Users in the process table detected from the test: (%s)", s); free(s); } DeleteItemList(userList); return true; }
Policy *LoadPolicy(EvalContext *ctx, GenericAgentConfig *config) { StringSet *parsed_files_and_checksums = StringSetNew(); StringSet *failed_files = StringSetNew(); Policy *policy = LoadPolicyFile(ctx, config, config->input_file, parsed_files_and_checksums, failed_files); if (StringSetSize(failed_files) > 0) { Log(LOG_LEVEL_ERR, "There are syntax errors in policy files"); exit(EXIT_FAILURE); } StringSetDestroy(parsed_files_and_checksums); StringSetDestroy(failed_files); { Seq *errors = SeqNew(100, PolicyErrorDestroy); if (PolicyCheckPartial(policy, errors)) { if (!config->bundlesequence && (PolicyIsRunnable(policy) || config->check_runnable)) { Log(LOG_LEVEL_VERBOSE, "Running full policy integrity checks"); PolicyCheckRunnable(ctx, policy, errors, config->ignore_missing_bundles); } } if (SeqLength(errors) > 0) { Writer *writer = FileWriter(stderr); for (size_t i = 0; i < errors->length; i++) { PolicyErrorWrite(writer, errors->data[i]); } WriterClose(writer); exit(EXIT_FAILURE); // TODO: do not exit } SeqDestroy(errors); } if (LogGetGlobalLevel() >= LOG_LEVEL_VERBOSE) { ShowContext(ctx); } if (policy) { for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bp = SeqAt(policy->bundles, i); EvalContextStackPushBundleFrame(ctx, bp, NULL, false); for (size_t j = 0; j < SeqLength(bp->promise_types); j++) { PromiseType *sp = SeqAt(bp->promise_types, j); EvalContextStackPushPromiseTypeFrame(ctx, sp); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, CommonEvalPromise, NULL); } EvalContextStackPopFrame(ctx); } EvalContextStackPopFrame(ctx); } PolicyResolve(ctx, policy, config); // TODO: need to move this inside PolicyCheckRunnable eventually. if (!config->bundlesequence && config->check_runnable) { // only verify policy-defined bundlesequence for cf-agent, cf-promises if ((config->agent_type == AGENT_TYPE_AGENT) || (config->agent_type == AGENT_TYPE_COMMON)) { if (!VerifyBundleSequence(ctx, policy, config)) { FatalError(ctx, "Errors in promise bundles: could not verify bundlesequence"); } } } } JsonElement *validated_doc = ReadReleaseIdFileFromInputs(); if (validated_doc) { const char *release_id = JsonObjectGetAsString(validated_doc, "releaseId"); if (release_id) { policy->release_id = xstrdup(release_id); } JsonDestroy(validated_doc); } return policy; }
static void GetProcessColumnNames(const char *proc, char **names, int *start, int *end) { char title[16]; int col, offset = 0; if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Parsing ps line: '%s'", proc); // Makes the entry line up with the line above. PrintStringIndexLine(18, strlen(proc)); } for (col = 0; col < CF_PROCCOLS; col++) { start[col] = end[col] = -1; names[col] = NULL; } col = 0; for (const char *sp = proc; *sp != '\0'; sp++) { offset = sp - proc; if (isspace((unsigned char) *sp)) { if (start[col] != -1) { Log(LOG_LEVEL_DEBUG, "End of '%s' is %d", title, offset - 1); end[col++] = offset - 1; if (col >= CF_PROCCOLS) /* No space for more columns. */ { size_t blank = strspn(sp, " \t\r\n\f\v"); if (sp[blank]) /* i.e. that wasn't everything. */ { /* If this happens, we have more columns in * our ps output than space to store them. * Update the #define CF_PROCCOLS (last seen * in libpromises/cf3.defs.h) to a bigger * number ! */ Log(LOG_LEVEL_ERR, "Process table lacks space for last columns: %s", sp + blank); } break; } } } else if (start[col] == -1) { if (col == 0) { // The first column always extends all the way to the left. start[col] = 0; } else { start[col] = offset; } sscanf(sp, "%15s", title); Log(LOG_LEVEL_DEBUG, "Start of '%s' is %d", title, offset); names[col] = xstrdup(title); Log(LOG_LEVEL_DEBUG, "Col[%d] = '%s'", col, names[col]); } } if (end[col] == -1) { Log(LOG_LEVEL_DEBUG, "End of '%s' is %d", title, offset); end[col] = offset; } }
static bool SplitProcLine(const char *line, time_t pstime, char **names, int *start, int *end, PsColumnAlgorithm pca, char **fields) { if (line == NULL || line[0] == '\0') { return false; } size_t linelen = strlen(line); if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Parsing ps line: '%s'", line); // Makes the entry line up with the line above. PrintStringIndexLine(18, linelen); } /* All platforms have been verified to not produce overlapping fields with currently used ps tools, and hence we can parse based on space separation (with some caveats, see below). Dates may have spaces in them, like "May 4", or not, like "May4". Prefer to match a date without spaces as long as it contains a number, but fall back to parsing letters followed by space(s) and a date. Commands will also have extra spaces, but it is always the last field, so we just include spaces at this point in the parsing. An additional complication is that some platforms (only AIX is known at the time of writing) can have empty columns when a process is a zombie. The plan is to match for this by checking the range between start and end directly below the header (same byte position). If the whole range is whitespace we consider the entry missing. The columns are (presumably) guaranteed to line up correctly for this, since zombie processes do not have memory usage which can produce large, potentially alignment-altering numbers. However, we cannot do this whitespace check in general, because non-zombie processes may shift columns in a way that leaves some columns apparently (but not actually) empty. Take these two examples: AIX: USER PID PPID PGID %CPU %MEM VSZ NI ST STIME TIME COMMAND jenkins 1036484 643150 1036484 0.0 0.0 584 20 A 09:29:20 00:00:00 bash 254232 729146 729146 20 Z 00:00:00 <defunct> Solaris 9: USER PID %CPU %MEM SZ RSS TT S STIME TIME COMMAND jenkins 29769 0.0 0.0 810 2976 pts/1 S 07:22:43 0:00 /usr/bin/perl ../../ps.pl jenkins 29835 - - 0 0 ? Z - 0:00 <defunct> jenkins 10026 0.0 0.3 30927 143632 ? S Jan_21 01:18:58 /usr/jdk/jdk1.6.0_45/bin/java -jar slave.jar Due to how the process state 'S' is shifted under the 'S' header in the second example, it is not possible to separate between this and a missing column. Counting spaces is no good, because commands can contain an arbitrary number of spaces, and there is no way to know the byte position where a command begins. Hence the only way is to base this algorithm on platform and only do the "empty column detection" when: * The platform is known to produce empty columns for zombie processes (see PCA_ZombieSkipEmptyColumns) * The platform is known to not shift columns when the process is a zombie. * The process is a zombie. */ bool zombie = false; if (pca == PCA_ZombieSkipEmptyColumns) { // Find out if the process is a zombie. for (int field = 0; names[field]; field++) { if (strcmp(names[field], "S") == 0 || strcmp(names[field], "ST") == 0) { // Check for zombie state. for (int pos = start[field]; pos <= end[field] && pos < linelen; pos++) { // 'Z' letter with word boundary on each side. if (isspace(line[pos - 1]) && line[pos] == 'Z' && (isspace(line[pos + 1]) || line[pos + 1] == '\0')) { Log(LOG_LEVEL_DEBUG, "Detected zombie process, " "skipping parsing of empty ps fields."); zombie = true; } } break; } } } int field = 0; int pos = 0; while (names[field]) { // Some sanity checks. if (pos >= linelen) { if (pca == PCA_ZombieSkipEmptyColumns && zombie) { Log(LOG_LEVEL_DEBUG, "Assuming '%s' field is empty, " "since ps line '%s' is not long enough to reach under its " "header.", names[field], line); fields[field] = xstrdup(""); field++; continue; } else { Log(LOG_LEVEL_ERR, "ps output line '%s' is shorter than its " "associated header.", line); return false; } } bool cmd = (strcmp(names[field], "CMD") == 0 || strcmp(names[field], "COMMAND") == 0); bool stime = !cmd && (strcmp(names[field], "STIME") == 0); // Equal boolean results, either both must be true, or both must be // false. IOW we must either both be at the last field, and it must be // CMD, or none of those. | // v if ((names[field + 1] != NULL) == cmd) { Log(LOG_LEVEL_ERR, "Last field of ps output '%s' is not " "CMD/COMMAND.", line); return false; } // If zombie, check if field is empty. if (pca == PCA_ZombieSkipEmptyColumns && zombie) { int empty_pos = start[field]; bool empty = true; while (empty_pos <= end[field]) { if (!isspace(line[empty_pos])) { empty = false; break; } empty_pos++; } if (empty) { Log(LOG_LEVEL_DEBUG, "Detected empty '%s' field between " "positions %d and %d\n", names[field], start[field], end[field]); fields[field] = xstrdup(""); pos = end[field] + 1; field++; continue; } else { Log(LOG_LEVEL_DEBUG, "Detected non-empty '%s' field between " "positions %d and %d\n", names[field], start[field], end[field]); } } // Preceding space. while (isspace(line[pos])) { pos++; } // Field. int last = pos; if (cmd) { // Last field, slurp up the rest, but discard trailing whitespace. last = linelen; while (isspace(line[last - 1])) { last--; } } else if (stime) { while (isalpha(line[last])) { last++; } if (isspace(line[last])) { // In this case we expect spaces followed by a number. // It means what we first read was the month, now is the date. do { last++; } while (isspace(line[last])); if (!isdigit(line[last])) { char fmt[200]; xsnprintf(fmt, sizeof(fmt), "Unable to parse STIME entry in ps " "output line '%%s': Expected day number after " "'%%.%ds'", (last - 1) - pos); Log(LOG_LEVEL_ERR, fmt, line, line + pos); return false; } } while (line[last] && !isspace(line[last])) { last++; } } else { // Generic fields while (line[last] && !isspace(line[last])) { last++; } } // Make a copy and store in fields. fields[field] = xstrndup(line + pos, last - pos); Log(LOG_LEVEL_DEBUG, "'%s' field '%s' extracted from between positions " "%d and %d", names[field], fields[field], pos, last - 1); pos = last; field++; } MaybeFixStartTime(line, pstime, names, fields); return true; }
static bool SplitProcLine(const char *line, time_t pstime, char **names, int *start, int *end, PsColumnAlgorithm pca, char **fields) { if (line == NULL || line[0] == '\0') { return false; } size_t linelen = strlen(line); if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { LogDebug(LOG_MOD_PS, "Parsing ps line: '%s'", line); // Makes the entry line up with the line above. PrintStringIndexLine(18, linelen); } /* All platforms have been verified to not produce overlapping fields with currently used ps tools, and hence we can parse based on space separation (with some caveats, see below). Dates may have spaces in them, like "May 4", or not, like "May4". Prefer to match a date without spaces as long as it contains a number, but fall back to parsing letters followed by space(s) and a date. Commands will also have extra spaces, but it is always the last field, so we just include spaces at this point in the parsing. An additional complication is that some platforms (only AIX is known at the time of writing) can have empty columns when a process is a zombie. The plan is to match for this by checking the range between start and end directly below the header (same byte position). If the whole range is whitespace we consider the entry missing. The columns are (presumably) guaranteed to line up correctly for this, since zombie processes do not have memory usage which can produce large, potentially alignment-altering numbers. However, we cannot do this whitespace check in general, because non-zombie processes may shift columns in a way that leaves some columns apparently (but not actually) empty. Zombie processes have state Z and command <defunct> on AIX. Similarly processes marked with command <exiting> also have missing columns and need to be skipped. (AIX only). Take these two examples: AIX: USER PID PPID PGID %CPU %MEM VSZ NI S STIME TIME COMMAND root 1 0 0 0.0 0.0 784 20 A Nov 28 00:00:00 /etc/init root 1835344 1 1835344 0.0 0.0 944 20 A Nov 28 00:00:00 /usr/lib/errdemon root 2097594 1 1638802 0.0 0.0 596 20 A Nov 28 00:00:05 /usr/sbin/syncd 60 root 3408328 1 3408328 0.0 0.0 888 20 A Nov 28 00:00:00 /usr/sbin/srcmstr root 4325852 3408328 4325852 0.0 0.0 728 20 A Nov 28 00:00:00 /usr/sbin/syslogd root 4784534 3408328 4784534 0.0 0.0 1212 20 A Nov 28 00:00:00 sendmail: accepting connections root 5898690 1 5898690 0.0 0.0 1040 20 A Nov 28 00:00:00 /usr/sbin/cron 6095244 8913268 8913268 20 Z 00:00:00 <defunct> root 6160866 3408328 6160866 0.0 0.0 1612 20 A Nov 28 00:00:00 /opt/rsct/bin/IBM.ServiceRMd 6750680 17826152 17826152 20 Z 00:00:00 <defunct> root 7143692 3408328 7143692 0.0 0.0 476 20 A Nov 28 00:00:00 /var/perf/pm/bin/pmperfrec root 7340384 8651136 8651136 0.0 0.0 500 20 A Nov 28 00:00:00 [trspoolm] root 7602560 8978714 7602560 0.0 0.0 636 20 A Nov 28 00:00:00 sshd: u0013628 [priv] 7733720 - - - A - <exiting> Solaris 9: USER PID %CPU %MEM SZ RSS TT S STIME TIME COMMAND jenkins 29769 0.0 0.0 810 2976 pts/1 S 07:22:43 0:00 /usr/bin/perl ../../ps.pl jenkins 29835 - - 0 0 ? Z - 0:00 <defunct> jenkins 10026 0.0 0.3 30927 143632 ? S Jan_21 01:18:58 /usr/jdk/jdk1.6.0_45/bin/java -jar slave.jar Due to how the process state 'S' is shifted under the 'S' header in the second example, it is not possible to separate between this and a missing column. Counting spaces is no good, because commands can contain an arbitrary number of spaces, and there is no way to know the byte position where a command begins. Hence the only way is to base this algorithm on platform and only do the "empty column detection" when: * The platform is known to produce empty columns for zombie processes (see PCA_ZombieSkipEmptyColumns) * The platform is known to not shift columns when the process is a zombie. * It is a zombie / exiting / idle process (These states provide almost no useful info in ps output) */ bool skip = false; if (pca == PCA_ZombieSkipEmptyColumns) { // Find out if the process is a zombie. for (int field = 0; names[field] && !skip; field++) { if (strcmp(names[field], "S") == 0 || strcmp(names[field], "ST") == 0) { // Check for zombie state. for (int pos = start[field]; pos <= end[field] && pos < linelen && !skip; pos++) { // 'Z' letter with word boundary on each side. if (isspace(line[pos - 1]) && line[pos] == 'Z' && (isspace(line[pos + 1]) || line[pos + 1] == '\0')) { LogDebug(LOG_MOD_PS, "Detected zombie process, " "skipping parsing of empty ps fields."); skip = true; } } } else if (strcmp(names[field], "COMMAND") == 0) { // Check for exiting or idle state. for (int pos = start[field]; pos <= end[field] && pos < linelen && !skip; pos++) { if (!isspace(line[pos])) // Skip spaces { if (strncmp(line + pos, "<exiting>", 9) == 0 || strncmp(line + pos, "<idle>", 6) == 0) { LogDebug(LOG_MOD_PS, "Detected exiting/idle process, " "skipping parsing of empty ps fields."); skip = true; } else { break; } } } } } } int field = 0; int pos = 0; while (names[field]) { // Some sanity checks. if (pos >= linelen) { if (pca == PCA_ZombieSkipEmptyColumns && skip) { LogDebug(LOG_MOD_PS, "Assuming '%s' field is empty, " "since ps line '%s' is not long enough to reach under its " "header.", names[field], line); fields[field] = xstrdup(""); field++; continue; } else { Log(LOG_LEVEL_ERR, "ps output line '%s' is shorter than its " "associated header.", line); return false; } } bool cmd = (strcmp(names[field], "CMD") == 0 || strcmp(names[field], "COMMAND") == 0); bool stime = !cmd && (strcmp(names[field], "STIME") == 0); // Equal boolean results, either both must be true, or both must be // false. IOW we must either both be at the last field, and it must be // CMD, or none of those. | // v if ((names[field + 1] != NULL) == cmd) { Log(LOG_LEVEL_ERR, "Last field of ps output '%s' is not " "CMD/COMMAND.", line); return false; } // If zombie/exiting, check if field is empty. if (pca == PCA_ZombieSkipEmptyColumns && skip) { int empty_pos = start[field]; bool empty = true; while (empty_pos <= end[field]) { if (!isspace(line[empty_pos])) { empty = false; break; } empty_pos++; } if (empty) { LogDebug(LOG_MOD_PS, "Detected empty" " '%s' field between positions %d and %d", names[field], start[field], end[field]); fields[field] = xstrdup(""); pos = end[field] + 1; field++; continue; } else { LogDebug(LOG_MOD_PS, "Detected non-empty " "'%s' field between positions %d and %d", names[field], start[field], end[field]); } } // Preceding space. while (isspace(line[pos])) { pos++; } // Field. int last = pos; if (cmd) { // Last field, slurp up the rest, but discard trailing whitespace. last = linelen; while (isspace(line[last - 1])) { last--; } } else if (stime) { while (isalpha(line[last])) { last++; } if (isspace(line[last])) { // In this case we expect spaces followed by a number. // It means what we first read was the month, now is the date. do { last++; } while (isspace(line[last])); if (!isdigit(line[last])) { char fmt[200]; xsnprintf(fmt, sizeof(fmt), "Unable to parse STIME entry in ps " "output line '%%s': Expected day number after " "'%%.%ds'", (last - 1) - pos); Log(LOG_LEVEL_ERR, fmt, line, line + pos); return false; } } while (line[last] && !isspace(line[last])) { last++; } } else { // Generic fields while (line[last] && !isspace(line[last])) { last++; } } // Make a copy and store in fields. fields[field] = xstrndup(line + pos, last - pos); LogDebug(LOG_MOD_PS, "'%s' field '%s'" " extracted from between positions %d and %d", names[field], fields[field], pos, last - 1); pos = last; field++; } MaybeFixStartTime(line, pstime, names, fields); return true; }
int OpenReceiverChannel(void) { struct addrinfo *response, *ap; struct addrinfo query = { .ai_flags = AI_PASSIVE, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; /* Listen to INADDR(6)_ANY if BINDINTERFACE unset. */ char *ptr = NULL; if (BINDINTERFACE[0] != '\0') { ptr = BINDINTERFACE; } char servname[10]; snprintf(servname, 10, "%d", CFENGINE_PORT); /* Resolve listening interface. */ if (getaddrinfo(ptr, servname, &query, &response) != 0) { Log(LOG_LEVEL_ERR, "DNS/service lookup failure. (getaddrinfo: %s)", GetErrorStr()); return -1; } int sd = -1; for (ap = response; ap != NULL; ap = ap->ai_next) { if ((sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol)) == -1) { continue; } #ifdef IPV6_V6ONLY /* Some platforms don't listen to both address families (windows) for the IPv6 loopback address and need this flag. Some other platforms won't even honour this flag (openbsd). */ if (BINDINTERFACE[0] == '\0') { int no = 0; if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no)) == -1) { Log(LOG_LEVEL_VERBOSE, "Failed to clear IPv6-only flag on listening socket" " (setsockopt: %s)", GetErrorStr()); } } #endif int yes = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { Log(LOG_LEVEL_WARNING, "Socket option SO_REUSEADDR was not accepted. (setsockopt: %s)", GetErrorStr()); } struct linger cflinger = { .l_onoff = 1, .l_linger = 60 }; if (setsockopt(sd, SOL_SOCKET, SO_LINGER, &cflinger, sizeof(cflinger)) == -1) { Log(LOG_LEVEL_ERR, "Socket option SO_LINGER was not accepted. (setsockopt: %s)", GetErrorStr()); exit(EXIT_FAILURE); } if (bind(sd, ap->ai_addr, ap->ai_addrlen) != -1) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { /* Convert IP address to string, no DNS lookup performed. */ char txtaddr[CF_MAX_IP_LEN] = ""; getnameinfo(ap->ai_addr, ap->ai_addrlen, txtaddr, sizeof(txtaddr), NULL, 0, NI_NUMERICHOST); Log(LOG_LEVEL_DEBUG, "Bound to address '%s' on '%s' = %d", txtaddr, CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS); } break; } else { Log(LOG_LEVEL_ERR, "Could not bind server address. (bind: %s)", GetErrorStr()); cf_closesocket(sd); } } if (sd < 0) { Log(LOG_LEVEL_ERR, "Couldn't open/bind a socket"); exit(EXIT_FAILURE); } freeaddrinfo(response); return sd; } /*********************************************************************/ /* Level 3 */ /*********************************************************************/ static void DeleteAuthList(Auth **list, Auth **list_tail) { Auth *ap = *list; while (ap != NULL) { Auth *ap_next = ap->next; DeleteItemList(ap->accesslist); DeleteItemList(ap->maproot); free(ap->path); free(ap); /* Just make sure the tail was consistent. */ if (ap_next == NULL) assert(ap == *list_tail); ap = ap_next; } *list = NULL; *list_tail = NULL; }
int OpenReceiverChannel(void) { struct addrinfo *response, *ap; struct addrinfo query = { .ai_flags = AI_PASSIVE, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; /* Listen to INADDR(6)_ANY if BINDINTERFACE unset. */ char *ptr = NULL; if (BINDINTERFACE[0] != '\0') { ptr = BINDINTERFACE; } /* Resolve listening interface. */ if (getaddrinfo(ptr, STR_CFENGINEPORT, &query, &response) != 0) { Log(LOG_LEVEL_ERR, "DNS/service lookup failure. (getaddrinfo: %s)", GetErrorStr()); return -1; } int sd = -1; for (ap = response; ap != NULL; ap = ap->ai_next) { if ((sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol)) == -1) { continue; } int yes = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { Log(LOG_LEVEL_ERR, "Socket option SO_REUSEADDR was not accepted. (setsockopt: %s)", GetErrorStr()); exit(1); } struct linger cflinger = { .l_onoff = 1, .l_linger = 60 }; if (setsockopt(sd, SOL_SOCKET, SO_LINGER, &cflinger, sizeof(cflinger)) == -1) { Log(LOG_LEVEL_ERR, "Socket option SO_LINGER was not accepted. (setsockopt: %s)", GetErrorStr()); exit(1); } if (bind(sd, ap->ai_addr, ap->ai_addrlen) != -1) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { /* Convert IP address to string, no DNS lookup performed. */ char txtaddr[CF_MAX_IP_LEN] = ""; getnameinfo(ap->ai_addr, ap->ai_addrlen, txtaddr, sizeof(txtaddr), NULL, 0, NI_NUMERICHOST); Log(LOG_LEVEL_DEBUG, "Bound to address '%s' on '%s' = %d", txtaddr, CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS); } break; } else { Log(LOG_LEVEL_ERR, "Could not bind server address. (bind: %s)", GetErrorStr()); cf_closesocket(sd); } } if (sd < 0) { Log(LOG_LEVEL_ERR, "Couldn't open/bind a socket"); exit(1); } freeaddrinfo(response); return sd; } /*********************************************************************/ /* Level 3 */ /*********************************************************************/ void CheckFileChanges(EvalContext *ctx, Policy **policy, GenericAgentConfig *config) { Log(LOG_LEVEL_DEBUG, "Checking file updates for input file '%s'", config->input_file); if (NewPromiseProposals(ctx, config, InputFiles(ctx, *policy))) { Log(LOG_LEVEL_VERBOSE, "New promises detected..."); if (CheckPromises(config)) { Log(LOG_LEVEL_INFO, "Rereading policy file '%s'", config->input_file); /* Free & reload -- lock this to avoid access errors during reload */ EvalContextHeapClear(ctx); DeleteItemList(IPADDRESSES); IPADDRESSES = NULL; DeleteItemList(SV.trustkeylist); DeleteItemList(SV.skipverify); DeleteItemList(SV.attackerlist); DeleteItemList(SV.nonattackerlist); DeleteItemList(SV.multiconnlist); DeleteAuthList(SV.admit); DeleteAuthList(SV.deny); DeleteAuthList(SV.varadmit); DeleteAuthList(SV.vardeny); DeleteAuthList(SV.roles); //DeleteRlist(VINPUTLIST); This is just a pointer, cannot free it ScopeDeleteAll(); strcpy(VDOMAIN, "undefined.domain"); POLICY_SERVER[0] = '\0'; SV.admit = NULL; SV.admittop = NULL; SV.varadmit = NULL; SV.varadmittop = NULL; SV.deny = NULL; SV.denytop = NULL; SV.vardeny = NULL; SV.vardenytop = NULL; SV.roles = NULL; SV.rolestop = NULL; SV.trustkeylist = NULL; SV.skipverify = NULL; SV.attackerlist = NULL; SV.nonattackerlist = NULL; SV.multiconnlist = NULL; PolicyDestroy(*policy); *policy = NULL; { char *existing_policy_server = ReadPolicyServerFile(GetWorkDir()); SetPolicyServer(ctx, existing_policy_server); free(existing_policy_server); } GetNameInfo3(ctx, AGENT_TYPE_SERVER); GetInterfacesInfo(ctx, AGENT_TYPE_SERVER); Get3Environment(ctx, AGENT_TYPE_SERVER); BuiltinClasses(ctx); OSClasses(ctx); KeepHardClasses(ctx); EvalContextHeapAddHard(ctx, CF_AGENTTYPES[config->agent_type]); SetReferenceTime(ctx, true); *policy = GenericAgentLoadPolicy(ctx, config); KeepPromises(ctx, *policy, config); Summarize(); } else { Log(LOG_LEVEL_INFO, "File changes contain errors -- ignoring"); PROMISETIME = time(NULL); } } else { Log(LOG_LEVEL_DEBUG, "No new promises found"); } }
static bool ConsiderFile(const char *nodename, const char *path, struct stat *stat) { int i; const char *sp; if (strlen(nodename) < 1) { Log(LOG_LEVEL_ERR, "Empty (null) filename detected in %s", path); return true; } if (IsItemIn(SUSPICIOUSLIST, nodename)) { if (stat && (S_ISREG(stat->st_mode) || S_ISLNK(stat->st_mode))) { Log(LOG_LEVEL_ERR, "Suspicious file %s found in %s", nodename, path); return false; } } if (strcmp(nodename, "...") == 0) { Log(LOG_LEVEL_VERBOSE, "Possible DFS/FS cell node detected in %s...", path); return true; } for (i = 0; SKIPFILES[i] != NULL; i++) { if (strcmp(nodename, SKIPFILES[i]) == 0) { Log(LOG_LEVEL_DEBUG, "Filename '%s/%s' is classified as ignorable", path, nodename); return false; } } if ((strcmp("[", nodename) == 0) && (strcmp("/usr/bin", path) == 0)) { #if defined(__linux__) return true; #endif } for (sp = nodename; *sp != '\0'; sp++) { if ((*sp > 31) && (*sp < 127)) { break; } } for (sp = nodename; *sp != '\0'; sp++) /* Check for files like ".. ." */ { if ((*sp != '.') && (!isspace((int)*sp))) { return true; } } if (stat == NULL) { Log(LOG_LEVEL_VERBOSE, "Couldn't stat '%s/%s'. (cf_lstat: %s)", path, nodename, GetErrorStr()); return true; } if ((stat->st_size == 0) && LogGetGlobalLevel() < LOG_LEVEL_INFO) /* No sense in warning about empty files */ { return false; } Log(LOG_LEVEL_ERR, "Suspicious looking file object '%s' masquerading as hidden file in '%s'", nodename, path); if (S_ISLNK(stat->st_mode)) { Log(LOG_LEVEL_INFO, " %s is a symbolic link", nodename); } else if (S_ISDIR(stat->st_mode)) { Log(LOG_LEVEL_INFO, " %s is a directory", nodename); } Log(LOG_LEVEL_VERBOSE, "[%s] has size %ld and full mode %o", nodename, (unsigned long) (stat->st_size), (unsigned int) (stat->st_mode)); return true; }
static bool GatherProcessUsers(Item **userList, int *userListSz, int *numRootProcs, int *numOtherProcs) { char pscomm[CF_BUFSIZE]; xsnprintf(pscomm, sizeof(pscomm), "%s %s", VPSCOMM[VPSHARDCLASS], VPSOPTS[VPSHARDCLASS]); FILE *pp; if ((pp = cf_popen(pscomm, "r", true)) == NULL) { /* FIXME: no logging */ return false; } size_t vbuff_size = CF_BUFSIZE; char *vbuff = xmalloc(vbuff_size); /* Ignore first line -- header */ ssize_t res = CfReadLine(&vbuff, &vbuff_size, pp); if (res <= 0) { /* FIXME: no logging */ cf_pclose(pp); free(vbuff); return false; } for (;;) { res = CfReadLine(&vbuff, &vbuff_size, pp); if (res == -1) { if (!feof(pp)) { /* FIXME: no logging */ cf_pclose(pp); free(vbuff); return false; } else { break; } } char user[64]; int ret = sscanf(vbuff, " %63s ", user); /* CFE-1560: Skip the username if it starts with a digit, this means * that we are reading the PID! Happens on some platforms (e.g. AIX) * where zombie processes have an empty username field in ps. */ if (ret != 1 || user[0] == '\0' || isdigit(user[0])) { continue; } if (!IsItemIn(*userList, user)) { PrependItem(userList, user, NULL); (*userListSz)++; } if (strcmp(user, "root") == 0) { (*numRootProcs)++; } else { (*numOtherProcs)++; } } if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { char *s = ItemList2CSV(*userList); Log(LOG_LEVEL_DEBUG, "Users in the process table: (%s)", s); free(s); } cf_pclose(pp); free(vbuff); return true; }
static int OpenReceiverChannel(void) { struct addrinfo *response = NULL, *ap; struct addrinfo query = { .ai_flags = AI_PASSIVE, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; /* Listen to INADDR(6)_ANY if BINDINTERFACE unset. */ char *ptr = NULL; if (BINDINTERFACE[0] != '\0') { ptr = BINDINTERFACE; } char servname[PRINTSIZE(CFENGINE_PORT)]; xsnprintf(servname, sizeof(servname), "%d", CFENGINE_PORT); /* Resolve listening interface. */ int gres; if ((gres = getaddrinfo(ptr, servname, &query, &response)) != 0) { Log(LOG_LEVEL_ERR, "DNS/service lookup failure. (getaddrinfo: %s)", gai_strerror(gres)); if (response) { freeaddrinfo(response); } return -1; } int sd = -1; for (ap = response; ap != NULL; ap = ap->ai_next) { sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol); if (sd == -1) { continue; } #ifdef IPV6_V6ONLY /* Properly implemented getaddrinfo(AI_PASSIVE) should return the IPV6 loopback address first. Some platforms (notably Windows) don't listen to both address families when binding to it and need this flag. Some other platforms won't even honour this flag (openbsd). */ if (BINDINTERFACE[0] == '\0' && ap->ai_family == AF_INET6) { int no = 0; if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no)) == -1) { Log(LOG_LEVEL_VERBOSE, "Failed to clear IPv6-only flag on listening socket" " (setsockopt: %s)", GetErrorStr()); } } #endif int yes = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { Log(LOG_LEVEL_VERBOSE, "Socket option SO_REUSEADDR was not accepted. (setsockopt: %s)", GetErrorStr()); } struct linger cflinger = { .l_onoff = 1, .l_linger = 60 }; if (setsockopt(sd, SOL_SOCKET, SO_LINGER, &cflinger, sizeof(cflinger)) == -1) { Log(LOG_LEVEL_INFO, "Socket option SO_LINGER was not accepted. (setsockopt: %s)", GetErrorStr()); } if (bind(sd, ap->ai_addr, ap->ai_addrlen) != -1) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { /* Convert IP address to string, no DNS lookup performed. */ char txtaddr[CF_MAX_IP_LEN] = ""; getnameinfo(ap->ai_addr, ap->ai_addrlen, txtaddr, sizeof(txtaddr), NULL, 0, NI_NUMERICHOST); Log(LOG_LEVEL_DEBUG, "Bound to address '%s' on '%s' = %d", txtaddr, CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS); } break; } Log(LOG_LEVEL_INFO, "Could not bind server address. (bind: %s)", GetErrorStr()); cf_closesocket(sd); sd = -1; } assert(response != NULL); /* getaddrinfo() was successful */ freeaddrinfo(response); return sd; } static int InitServer(size_t queue_size) { int sd = OpenReceiverChannel(); if (sd == -1) { Log(LOG_LEVEL_ERR, "Unable to start server"); } else if (listen(sd, queue_size) == -1) { Log(LOG_LEVEL_ERR, "listen failed. (listen: %s)", GetErrorStr()); cf_closesocket(sd); } else { return sd; } exit(EXIT_FAILURE); }
FnCallResult FnCallEvaluate(EvalContext *ctx, const Policy *policy, FnCall *fp, const Promise *caller) { assert(ctx); assert(policy); assert(fp); fp->caller = caller; if (!EvalContextGetEvalOption(ctx, EVAL_OPTION_EVAL_FUNCTIONS)) { Log(LOG_LEVEL_VERBOSE, "Skipping function '%s', because evaluation was turned off in the evaluator", fp->name); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } const FnCallType *fp_type = FnCallTypeGet(fp->name); if (!fp_type) { if (caller) { Log(LOG_LEVEL_ERR, "No such FnCall '%s' in promise '%s' near line %zd", fp->name, PromiseGetBundle(caller)->source_path, caller->offset.line); } else { Log(LOG_LEVEL_ERR, "No such FnCall '%s', context info unavailable", fp->name); } return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } Rlist *expargs = NewExpArgs(ctx, policy, fp); Writer *fncall_writer; const char *fncall_string; if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { fncall_writer = StringWriter(); FnCallWrite(fncall_writer, fp); fncall_string = StringWriterData(fncall_writer); } if (RlistIsUnresolved(expargs)) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Skipping function evaluation for now," " arguments contain unresolved variables: %s", fncall_string); WriterClose(fncall_writer); } RlistDestroy(expargs); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } Rval cached_rval; if ((fp_type->options & FNCALL_OPTION_CACHED) && EvalContextFunctionCacheGet(ctx, fp, expargs, &cached_rval)) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Using previously cached result for function: %s", fncall_string); WriterClose(fncall_writer); } Writer *w = StringWriter(); FnCallWrite(w, fp); WriterClose(w); RlistDestroy(expargs); return (FnCallResult) { FNCALL_SUCCESS, RvalCopy(cached_rval) }; } if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Evaluating function: %s", fncall_string); WriterClose(fncall_writer); } FnCallResult result = CallFunction(ctx, policy, fp, expargs); if (result.status == FNCALL_FAILURE) { RlistDestroy(expargs); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } else if (result.rval.type == RVAL_TYPE_LIST && !result.rval.item) { Rlist *seq = NULL; // don't pass NULL items to evaluator RlistPrepend(&seq, CF_NULL_VALUE, RVAL_TYPE_SCALAR); result.rval.item = seq; } if (fp_type->options & FNCALL_OPTION_CACHED) { Writer *w = StringWriter(); FnCallWrite(w, fp); Log(LOG_LEVEL_VERBOSE, "Caching result for function '%s'", StringWriterData(w)); WriterClose(w); EvalContextFunctionCachePut(ctx, fp, expargs, &result.rval); } RlistDestroy(expargs); return result; }
FnCallResult FnCallEvaluate(EvalContext *ctx, const Policy *policy, FnCall *fp, const Promise *caller) { assert(ctx); assert(policy); assert(fp); fp->caller = caller; if (!EvalContextGetEvalOption(ctx, EVAL_OPTION_EVAL_FUNCTIONS)) { Log(LOG_LEVEL_VERBOSE, "Skipping function '%s', because evaluation was turned off in the evaluator", fp->name); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } const FnCallType *fp_type = FnCallTypeGet(fp->name); if (!fp_type) { if (caller) { Log(LOG_LEVEL_ERR, "No such FnCall '%s' in promise '%s' near line %zd", fp->name, PromiseGetBundle(caller)->source_path, caller->offset.line); } else { Log(LOG_LEVEL_ERR, "No such FnCall '%s', context info unavailable", fp->name); } return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } Rlist *expargs = NewExpArgs(ctx, policy, fp, fp_type); Writer *fncall_writer = NULL; const char *fncall_string = ""; if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { fncall_writer = StringWriter(); FnCallWrite(fncall_writer, fp); fncall_string = StringWriterData(fncall_writer); } // Check if arguments are resolved, except for delayed evaluation functions if ( ! (fp_type->options & FNCALL_OPTION_DELAYED_EVALUATION) && RlistIsUnresolved(expargs)) { // Special case: ifelse(isvariable("x"), $(x), "default") // (the first argument will come down expanded as "!any") if (strcmp(fp->name, "ifelse") == 0 && RlistLen(expargs) == 3 && strcmp("!any", RlistScalarValueSafe(expargs)) == 0 && !RlistIsUnresolved(expargs->next->next)) { Log(LOG_LEVEL_DEBUG, "Allowing ifelse() function evaluation even" " though its arguments contain unresolved variables: %s", fncall_string); } else { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Skipping function evaluation for now," " arguments contain unresolved variables: %s", fncall_string); WriterClose(fncall_writer); } RlistDestroy(expargs); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } } Rval cached_rval; if ((fp_type->options & FNCALL_OPTION_CACHED) && EvalContextFunctionCacheGet(ctx, fp, expargs, &cached_rval)) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Using previously cached result for function: %s", fncall_string); WriterClose(fncall_writer); } Writer *w = StringWriter(); FnCallWrite(w, fp); WriterClose(w); RlistDestroy(expargs); return (FnCallResult) { FNCALL_SUCCESS, RvalCopy(cached_rval) }; } if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Evaluating function: %s", fncall_string); WriterClose(fncall_writer); } FnCallResult result = CallFunction(ctx, policy, fp, expargs); if (result.status == FNCALL_FAILURE) { RlistDestroy(expargs); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } if (fp_type->options & FNCALL_OPTION_CACHED) { Writer *w = StringWriter(); FnCallWrite(w, fp); Log(LOG_LEVEL_VERBOSE, "Caching result for function '%s'", StringWriterData(w)); WriterClose(w); EvalContextFunctionCachePut(ctx, fp, expargs, &result.rval); } RlistDestroy(expargs); return result; }
int OpenReceiverChannel(void) { struct addrinfo *response, *ap; struct addrinfo query = { .ai_flags = AI_PASSIVE, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; /* Listen to INADDR(6)_ANY if BINDINTERFACE unset. */ char *ptr = NULL; if (BINDINTERFACE[0] != '\0') { ptr = BINDINTERFACE; } char servname[10]; snprintf(servname, 10, "%d", CFENGINE_PORT); /* Resolve listening interface. */ if (getaddrinfo(ptr, servname, &query, &response) != 0) { Log(LOG_LEVEL_ERR, "DNS/service lookup failure. (getaddrinfo: %s)", GetErrorStr()); return -1; } int sd = -1; for (ap = response; ap != NULL; ap = ap->ai_next) { if ((sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol)) == -1) { continue; } int yes = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { Log(LOG_LEVEL_ERR, "Socket option SO_REUSEADDR was not accepted. (setsockopt: %s)", GetErrorStr()); exit(1); } struct linger cflinger = { .l_onoff = 1, .l_linger = 60 }; if (setsockopt(sd, SOL_SOCKET, SO_LINGER, &cflinger, sizeof(cflinger)) == -1) { Log(LOG_LEVEL_ERR, "Socket option SO_LINGER was not accepted. (setsockopt: %s)", GetErrorStr()); exit(1); } if (bind(sd, ap->ai_addr, ap->ai_addrlen) != -1) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { /* Convert IP address to string, no DNS lookup performed. */ char txtaddr[CF_MAX_IP_LEN] = ""; getnameinfo(ap->ai_addr, ap->ai_addrlen, txtaddr, sizeof(txtaddr), NULL, 0, NI_NUMERICHOST); Log(LOG_LEVEL_DEBUG, "Bound to address '%s' on '%s' = %d", txtaddr, CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS); } break; } else { Log(LOG_LEVEL_ERR, "Could not bind server address. (bind: %s)", GetErrorStr()); cf_closesocket(sd); } } if (sd < 0) { Log(LOG_LEVEL_ERR, "Couldn't open/bind a socket"); exit(1); } freeaddrinfo(response); return sd; } /*********************************************************************/ /* Level 3 */ /*********************************************************************/ void CheckFileChanges(EvalContext *ctx, Policy **policy, GenericAgentConfig *config, time_t *last_policy_reload) { time_t validated_at; Log(LOG_LEVEL_DEBUG, "Checking file updates for input file '%s'", config->input_file); validated_at = ReadTimestampFromPolicyValidatedMasterfiles(config); if (*last_policy_reload < validated_at) { *last_policy_reload = validated_at; Log(LOG_LEVEL_VERBOSE, "New promises detected..."); if (GenericAgentArePromisesValid(config)) { Log(LOG_LEVEL_INFO, "Rereading policy file '%s'", config->input_file); /* Free & reload -- lock this to avoid access errors during reload */ EvalContextClear(ctx); free(SV.allowciphers); SV.allowciphers = NULL; DeleteItemList(SV.trustkeylist); DeleteItemList(SV.attackerlist); DeleteItemList(SV.nonattackerlist); DeleteItemList(SV.multiconnlist); DeleteAuthList(SV.admit); DeleteAuthList(SV.deny); DeleteAuthList(SV.varadmit); DeleteAuthList(SV.vardeny); DeleteAuthList(SV.roles); strcpy(VDOMAIN, "undefined.domain"); SV.admit = NULL; SV.admittop = NULL; SV.varadmit = NULL; SV.varadmittop = NULL; SV.deny = NULL; SV.denytop = NULL; SV.vardeny = NULL; SV.vardenytop = NULL; SV.roles = NULL; SV.rolestop = NULL; SV.trustkeylist = NULL; SV.attackerlist = NULL; SV.nonattackerlist = NULL; SV.multiconnlist = NULL; PolicyDestroy(*policy); *policy = NULL; { char *existing_policy_server = ReadPolicyServerFile(GetWorkDir()); SetPolicyServer(ctx, existing_policy_server); free(existing_policy_server); } UpdateLastPolicyUpdateTime(ctx); DetectEnvironment(ctx); KeepHardClasses(ctx); EvalContextClassPutHard(ctx, CF_AGENTTYPES[config->agent_type], "cfe_internal,source=agent"); time_t t = SetReferenceTime(); UpdateTimeClasses(ctx, t); *policy = GenericAgentLoadPolicy(ctx, config); KeepPromises(ctx, *policy, config); Summarize(); } else { Log(LOG_LEVEL_INFO, "File changes contain errors -- ignoring"); } } else { Log(LOG_LEVEL_DEBUG, "No new promises found"); } }