コード例 #1
0
ファイル: promise_logging.c プロジェクト: JarleB/core
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;
}
コード例 #2
0
ファイル: generic_agent.c プロジェクト: nperron/core
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);
    }
}
コード例 #3
0
ファイル: generic_agent.c プロジェクト: nperron/core
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;
}
コード例 #4
0
ファイル: promise_logging.c プロジェクト: JarleB/core
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;
}
コード例 #5
0
ファイル: promise_logging.c プロジェクト: JarleB/core
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;
}
コード例 #6
0
ファイル: mon_processes_test.c プロジェクト: cfengine/core
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;
}
コード例 #7
0
ファイル: loading.c プロジェクト: lra/core
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;
}
コード例 #8
0
ファイル: processes_select.c プロジェクト: atsaloli/core
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;
    }
}
コード例 #9
0
ファイル: processes_select.c プロジェクト: atsaloli/core
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;
}
コード例 #10
0
ファイル: processes_select.c プロジェクト: nickanderson/core
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;
}
コード例 #11
0
ファイル: cf-serverd-functions.c プロジェクト: tzz/core
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;
}
コード例 #12
0
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");
    }
}
コード例 #13
0
ファイル: files_properties.c プロジェクト: baptr/core
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;
}
コード例 #14
0
ファイル: mon_processes.c プロジェクト: dstam/core
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;
}
コード例 #15
0
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);
}
コード例 #16
0
ファイル: fncall.c プロジェクト: bahamat/cfengine-core
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;
}
コード例 #17
0
ファイル: fncall.c プロジェクト: dstam/core
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;
}
コード例 #18
0
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");
    }
}